diff --git a/.idea/artifacts/jfx_1_3_2.xml b/.idea/artifacts/jfx_1_4_0.xml similarity index 67% rename from .idea/artifacts/jfx_1_3_2.xml rename to .idea/artifacts/jfx_1_4_0.xml index c7f3557..b63085e 100644 --- a/.idea/artifacts/jfx_1_3_2.xml +++ b/.idea/artifacts/jfx_1_4_0.xml @@ -1,7 +1,7 @@ - + $PROJECT_DIR$/build/ - + diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml new file mode 100644 index 0000000..879e1e4 --- /dev/null +++ b/.idea/codeStyleSettings.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 96cc43e..0fc6280 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -2,21 +2,11 @@ - - - - - - - - - - - + \ No newline at end of file diff --git a/.idea/dictionaries/ronn.xml b/.idea/dictionaries/ronn.xml new file mode 100644 index 0000000..a1ba98d --- /dev/null +++ b/.idea/dictionaries/ronn.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/libraries/rlib.xml b/.idea/libraries/rlib.xml deleted file mode 100644 index f882bbc..0000000 --- a/.idea/libraries/rlib.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 3a99fa2..dee66b1 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,17 +3,7 @@ - - - - - - - - - - - + \ No newline at end of file diff --git a/README.md b/README.md index bbf83d0..a7e4d98 100755 --- a/README.md +++ b/README.md @@ -5,3 +5,71 @@ JFX Gui bridge for JME with usefull utilities for common usecases License is the New BSD License (same as JME3) http://opensource.org/licenses/BSD-3-Clause + +How to use: +* 1. You need to create FXContainer inside the method simpleInitApp() of SimpleApplication. + +```` +final ProtonCursorProvider cursorProvider = new ProtonCursorProvider(this, assetManager, inputManager); + +for (final CursorType type : CursorType.values()) { + cursorProvider.setup(type); +} + +fxContainer = JmeFxContainer.install(this, guiNode, cursorProvider); +```` + +if you want to use simple default arrow cursor you should use this: +```` +fxContainer = JmeFxContainer.install(this, guiNode, null); +```` + +* 2. Then you must create javaFX scene and set it on the fx container to build your UI. +You can use FXML if you prefer it like this: +```` +FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/hud.fxml")); +BorderPane root = fxmlLoader.load(); +int width = app.getContext().getSettings().getWidth(); +int height = app.getContext().getSettings().getHeight(); +root.setPrefSize(width, height); +Hud hud = fxmlLoader.getController(); +Group group = new Group(root); +Scene scene = new Scene(group, Color.TRANSPARENT); +fxContainer.setScene(scene, group); +```` + +where Hud is controller class for example + +```` +public class Hud implements Initializable { + @FXML + public BorderPane root; + + @Override + public void initialize(URL location, ResourceBundle resources) { + } +} +```` +Ofc hud.fxml should be accessible in resources, valid and points to Hud class. +If you do not want to use FXML your code can be even simpler: + +```` +BorderPane root = new BorderPane(); +int width = app.getContext().getSettings().getWidth(); +int height = app.getContext().getSettings().getHeight(); +root.setPrefSize(width, height); +Group group = new Group(root); +Scene scene = new Scene(group, Color.TRANSPARENT); +fxContainer.setScene(scene, group); +```` + +* 3. From now you are able to get transparent JavaFX scene and use it as in classic JavaFX application. +```` +fxContainer.getScene(); +```` + +* 3. Also you need to add calling a draw method in the main loop(the method update() of your application) + +```` +if (fxContainer.isNeedWriteToJME()) fxContainer.writeToJME(); +```` diff --git a/build/jfx-1.2.jar b/build/jfx-1.2.jar deleted file mode 100644 index 033ed0c..0000000 Binary files a/build/jfx-1.2.jar and /dev/null differ diff --git a/build/jfx-1.3.1.jar b/build/jfx-1.3.1.jar deleted file mode 100644 index a8795d7..0000000 Binary files a/build/jfx-1.3.1.jar and /dev/null differ diff --git a/build/jfx-1.3.2.jar b/build/jfx-1.3.2.jar deleted file mode 100644 index bd58fd9..0000000 Binary files a/build/jfx-1.3.2.jar and /dev/null differ diff --git a/build/jfx-1.3.jar b/build/jfx-1.3.jar deleted file mode 100644 index 8c9d2bb..0000000 Binary files a/build/jfx-1.3.jar and /dev/null differ diff --git a/build/jfx-1.4.0.jar b/build/jfx-1.4.0.jar new file mode 100644 index 0000000..2b35877 Binary files /dev/null and b/build/jfx-1.4.0.jar differ diff --git a/lib/rlib-4.0.0.jar b/lib/rlib-4.0.0.jar new file mode 100644 index 0000000..4d54c78 Binary files /dev/null and b/lib/rlib-4.0.0.jar differ diff --git a/lib/rlib-sources-4.0.0.jar b/lib/rlib-sources-4.0.0.jar new file mode 100644 index 0000000..ce2c2d0 Binary files /dev/null and b/lib/rlib-sources-4.0.0.jar differ diff --git a/src/com/jme3x/jfx/cursor/CursorDisplayProvider.java b/src/com/jme3x/jfx/cursor/CursorDisplayProvider.java new file mode 100644 index 0000000..30cca69 --- /dev/null +++ b/src/com/jme3x/jfx/cursor/CursorDisplayProvider.java @@ -0,0 +1,20 @@ +package com.jme3x.jfx.cursor; + + +import java.awt.Cursor; + +/** + * The interface for implementing the provider of cursors. + */ +public interface CursorDisplayProvider { + + /** + * Setups the type of cursor. + */ + void setupCursor(Cursor normal); + + /** + * Shows ths cursor. + */ + void showCursor(Cursor cursorFrame); +} diff --git a/src/com/jme3x/jfx/cursor/proton/ProtonCursorProvider.java b/src/com/jme3x/jfx/cursor/proton/ProtonCursorProvider.java new file mode 100644 index 0000000..726d45b --- /dev/null +++ b/src/com/jme3x/jfx/cursor/proton/ProtonCursorProvider.java @@ -0,0 +1,83 @@ +package com.jme3x.jfx.cursor.proton; + +import com.jme3.app.Application; +import com.jme3.asset.AssetManager; +import com.jme3.asset.plugins.ClasspathLocator; +import com.jme3.cursors.plugins.JmeCursor; +import com.jme3.input.InputManager; +import com.jme3x.jfx.cursor.CursorDisplayProvider; + +import java.awt.Cursor; +import java.util.concurrent.ConcurrentHashMap; + +/** + * http://www.rw-designer.com/cursor-set/proton by juanello
A cursorProvider that simulates the + * native JFX one and tries to behave similar,
using native cursors and 2D surface logic. + * + * @author empire + */ +public class ProtonCursorProvider implements CursorDisplayProvider { + + private ConcurrentHashMap cache = new ConcurrentHashMap<>(); + + private AssetManager assetManager; + private InputManager inputManager; + private Application app; + + public ProtonCursorProvider(final Application app, final AssetManager assetManager, final InputManager inputManager) { + this.assetManager = assetManager; + this.inputManager = inputManager; + this.app = app; + assetManager.registerLocator("", ClasspathLocator.class); + } + + @Override + public synchronized void showCursor(final Cursor cursor) { + + if (cache.get(cursor) == null) { + setupCursor(cursor); + } + + final JmeCursor toDisplay = cache.get(cursor); + if (toDisplay == null) return; + + app.enqueue(() -> { + inputManager.setMouseCursor(toDisplay); + return null; + }); + } + + @Override + public void setupCursor(final Cursor cursor) { + + JmeCursor loaded = null; + + switch (cursor.getType()) { + case Cursor.CROSSHAIR_CURSOR: + loaded = (JmeCursor) assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_cross.cur"); + break; + case Cursor.DEFAULT_CURSOR: + loaded = (JmeCursor) assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_arrow.cur"); + break; + case Cursor.MOVE_CURSOR: + loaded = (JmeCursor) assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_move.cur"); + break; + case Cursor.HAND_CURSOR: + break; + case Cursor.TEXT_CURSOR: + loaded = (JmeCursor) assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_text.cur"); + break; + case Cursor.WAIT_CURSOR: + loaded = (JmeCursor) assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_busy.ani"); + break; + case Cursor.W_RESIZE_CURSOR: + loaded = (JmeCursor) assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_ew.cur"); + break; + default: { + loaded = (JmeCursor) assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_arrow.cur"); + } + } + + if (loaded != null) cache.put(cursor, loaded); + } +} diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_arrow.cur b/src/com/jme3x/jfx/cursor/proton/aero_arrow.cur old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_arrow.cur rename to src/com/jme3x/jfx/cursor/proton/aero_arrow.cur diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_busy.ani b/src/com/jme3x/jfx/cursor/proton/aero_busy.ani old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_busy.ani rename to src/com/jme3x/jfx/cursor/proton/aero_busy.ani diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_cross.cur b/src/com/jme3x/jfx/cursor/proton/aero_cross.cur old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_cross.cur rename to src/com/jme3x/jfx/cursor/proton/aero_cross.cur diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_ew.cur b/src/com/jme3x/jfx/cursor/proton/aero_ew.cur old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_ew.cur rename to src/com/jme3x/jfx/cursor/proton/aero_ew.cur diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_helpsel.cur b/src/com/jme3x/jfx/cursor/proton/aero_helpsel.cur old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_helpsel.cur rename to src/com/jme3x/jfx/cursor/proton/aero_helpsel.cur diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_link.cur b/src/com/jme3x/jfx/cursor/proton/aero_link.cur old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_link.cur rename to src/com/jme3x/jfx/cursor/proton/aero_link.cur diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_move.cur b/src/com/jme3x/jfx/cursor/proton/aero_move.cur old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_move.cur rename to src/com/jme3x/jfx/cursor/proton/aero_move.cur diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_nesw.cur b/src/com/jme3x/jfx/cursor/proton/aero_nesw.cur old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_nesw.cur rename to src/com/jme3x/jfx/cursor/proton/aero_nesw.cur diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_ns.cur b/src/com/jme3x/jfx/cursor/proton/aero_ns.cur old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_ns.cur rename to src/com/jme3x/jfx/cursor/proton/aero_ns.cur diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_nwse.cur b/src/com/jme3x/jfx/cursor/proton/aero_nwse.cur old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_nwse.cur rename to src/com/jme3x/jfx/cursor/proton/aero_nwse.cur diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_pen.cur b/src/com/jme3x/jfx/cursor/proton/aero_pen.cur old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_pen.cur rename to src/com/jme3x/jfx/cursor/proton/aero_pen.cur diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_text.cur b/src/com/jme3x/jfx/cursor/proton/aero_text.cur old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_text.cur rename to src/com/jme3x/jfx/cursor/proton/aero_text.cur diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_unavail.cur b/src/com/jme3x/jfx/cursor/proton/aero_unavail.cur old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_unavail.cur rename to src/com/jme3x/jfx/cursor/proton/aero_unavail.cur diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_up.cur b/src/com/jme3x/jfx/cursor/proton/aero_up.cur old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_up.cur rename to src/com/jme3x/jfx/cursor/proton/aero_up.cur diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/aero_working.ani b/src/com/jme3x/jfx/cursor/proton/aero_working.ani old mode 100755 new mode 100644 similarity index 100% rename from src/main/java/com/jme3x/jfx/cursor/proton/aero_working.ani rename to src/com/jme3x/jfx/cursor/proton/aero_working.ani diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/readme.txt b/src/com/jme3x/jfx/cursor/proton/readme.txt old mode 100755 new mode 100644 similarity index 93% rename from src/main/java/com/jme3x/jfx/cursor/proton/readme.txt rename to src/com/jme3x/jfx/cursor/proton/readme.txt index c8e2af6..c829538 --- a/src/main/java/com/jme3x/jfx/cursor/proton/readme.txt +++ b/src/com/jme3x/jfx/cursor/proton/readme.txt @@ -1,17 +1,17 @@ -=== Proton Cursor Set === - -By: juanello - -Download: http://www.rw-designer.com/cursor-set/proton - -Author's decription: - - - -========== - -License: Released to Public Domain - -You are free: - +=== Proton Cursor Set === + +By: juanello + +Download: http://www.rw-designer.com/cursor-set/proton + +Author's decription: + + + +========== + +License: Released to Public Domain + +You are free: + * To use this work for any legal purpose. \ No newline at end of file diff --git a/src/com/jme3x/jfx/injfx/ApplicationThreadExecutor.java b/src/com/jme3x/jfx/injfx/ApplicationThreadExecutor.java new file mode 100644 index 0000000..1f2306e --- /dev/null +++ b/src/com/jme3x/jfx/injfx/ApplicationThreadExecutor.java @@ -0,0 +1,62 @@ +package com.jme3x.jfx.injfx; + +import com.sun.istack.internal.NotNull; + +import rlib.util.ArrayUtils; +import rlib.util.array.Array; +import rlib.util.array.ArrayFactory; +import rlib.util.array.ConcurrentArray; + +/** + * The executor for executing tasks in application thread. + * + * @author JavaSaBr + */ +public class ApplicationThreadExecutor { + + private static final ApplicationThreadExecutor INSTANCE = new ApplicationThreadExecutor(); + + @NotNull + public static ApplicationThreadExecutor getInstance() { + return INSTANCE; + } + + /** + * The list of waiting tasks. + */ + @NotNull + private final ConcurrentArray waitTasks; + + /** + * THe list of tasks to execute. + */ + @NotNull + private final Array execute; + + public ApplicationThreadExecutor() { + this.waitTasks = ArrayFactory.newConcurrentAtomicARSWLockArray(Runnable.class); + this.execute = ArrayFactory.newArray(Runnable.class); + } + + /** + * Add the task to execute. + * + * @param task the new task. + */ + public void addToExecute(@NotNull final Runnable task) { + ArrayUtils.runInWriteLock(waitTasks, task, Array::add); + } + + /** + * Execute the waiting tasks. + */ + public void execute() { + if (waitTasks.isEmpty()) return; + ArrayUtils.runInWriteLock(waitTasks, execute, ArrayUtils::move); + try { + execute.forEach(Runnable::run); + } finally { + execute.clear(); + } + } +} diff --git a/src/com/jme3x/jfx/injfx/JmeOffscreenSurfaceContext.java b/src/com/jme3x/jfx/injfx/JmeOffscreenSurfaceContext.java new file mode 100644 index 0000000..7f75546 --- /dev/null +++ b/src/com/jme3x/jfx/injfx/JmeOffscreenSurfaceContext.java @@ -0,0 +1,214 @@ +package com.jme3x.jfx.injfx; + +import com.jme3.input.JoyInput; +import com.jme3.input.TouchInput; +import com.jme3.opencl.Context; +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 com.jme3x.jfx.injfx.input.JFXKeyInput; +import com.jme3x.jfx.injfx.input.JFXMouseInput; +import com.sun.istack.internal.NotNull; + +import javax.annotation.Nullable; + +/** + * The implementation of the {@link JmeContext} for integrating to JavaFX. + * + * @author empirephoenix + */ +public class JmeOffscreenSurfaceContext implements JmeContext { + + /** + * The settings. + */ + protected final AppSettings settings; + + /** + * The key input. + */ + @NotNull + protected final JFXKeyInput keyInput; + + /** + * The mouse input. + */ + @NotNull + protected final JFXMouseInput mouseInput; + + /** + * The current width. + */ + private volatile int width; + + /** + * The current height. + */ + private volatile int height; + + /** + * The background context. + */ + protected JmeContext backgroundContext; + + public JmeOffscreenSurfaceContext() { + this.keyInput = new JFXKeyInput(this); + this.mouseInput = new JFXMouseInput(this); + this.settings = createSettings(); + this.backgroundContext = createBackgroundContext(); + this.height = 1; + this.width = 1; + } + + /** + * @return the current height. + */ + public int getHeight() { + return height; + } + + /** + * @param height the current height. + */ + public void setHeight(final int height) { + this.height = height; + } + + /** + * @return the current width. + */ + public int getWidth() { + return width; + } + + /** + * @param width the current width. + */ + public void setWidth(final int width) { + this.width = width; + } + + /** + * @return new settings. + */ + @NotNull + protected AppSettings createSettings() { + final AppSettings settings = new AppSettings(true); + settings.setRenderer(AppSettings.LWJGL_OPENGL3); + return settings; + } + + /** + * @return new context/ + */ + @NotNull + protected JmeContext createBackgroundContext() { + return JmeSystem.newContext(settings, Type.OffscreenSurface); + } + + @NotNull + @Override + public Type getType() { + return Type.OffscreenSurface; + } + + @Override + public void setSettings(@NotNull final AppSettings settings) { + this.settings.copyFrom(settings); + this.settings.setRenderer(AppSettings.LWJGL_OPENGL3); + this.backgroundContext.setSettings(settings); + } + + @Override + public void setSystemListener(@NotNull final SystemListener listener) { + backgroundContext.setSystemListener(listener); + } + + @NotNull + @Override + public AppSettings getSettings() { + return settings; + } + + @NotNull + @Override + public Renderer getRenderer() { + return backgroundContext.getRenderer(); + } + + @Nullable + @Override + public Context getOpenCLContext() { + return null; + } + + @NotNull + @Override + public JFXMouseInput getMouseInput() { + return mouseInput; + } + + @NotNull + @Override + public JFXKeyInput getKeyInput() { + return keyInput; + } + + @Nullable + @Override + public JoyInput getJoyInput() { + return null; + } + + @Nullable + @Override + public TouchInput getTouchInput() { + return null; + } + + @NotNull + @Override + public Timer getTimer() { + return backgroundContext.getTimer(); + } + + @Override + public void setTitle(@NotNull final String title) { + } + + @Override + public boolean isCreated() { + return backgroundContext != null && backgroundContext.isCreated(); + } + + @Override + public boolean isRenderable() { + return backgroundContext != null && backgroundContext.isRenderable(); + } + + @Override + public void setAutoFlushFrames(final boolean enabled) { + // TODO Auto-generated method stub + } + + @Override + public void create(final boolean waitFor) { + final String render = System.getProperty("jfx.background.render", AppSettings.LWJGL_OPENGL3); + backgroundContext.getSettings().setRenderer(render); + backgroundContext.create(waitFor); + } + + @Override + public void restart() { + } + + @Override + public void destroy(final boolean waitFor) { + if (backgroundContext == null) throw new IllegalStateException("Not created"); + // destroy wrapped context + backgroundContext.destroy(waitFor); + } +} \ No newline at end of file diff --git a/src/com/jme3x/jfx/injfx/JmeToJFXApplication.java b/src/com/jme3x/jfx/injfx/JmeToJFXApplication.java new file mode 100644 index 0000000..b594ee1 --- /dev/null +++ b/src/com/jme3x/jfx/injfx/JmeToJFXApplication.java @@ -0,0 +1,27 @@ +package com.jme3x.jfx.injfx; + +import com.jme3.app.Application; +import com.jme3.app.SimpleApplication; + +/** + * The base implementation of {@link Application} for using in the JavaFX. + * + * @author JavaSaBr. + */ +public class JmeToJFXApplication extends SimpleApplication { + + private static final ApplicationThreadExecutor EXECUTOR = ApplicationThreadExecutor.getInstance(); + + public JmeToJFXApplication() { + } + + @Override + public void update() { + EXECUTOR.execute(); + super.update(); + } + + @Override + public void simpleInitApp() { + } +} diff --git a/src/com/jme3x/jfx/injfx/JmeToJFXIntegrator.java b/src/com/jme3x/jfx/injfx/JmeToJFXIntegrator.java new file mode 100644 index 0000000..3fdce45 --- /dev/null +++ b/src/com/jme3x/jfx/injfx/JmeToJFXIntegrator.java @@ -0,0 +1,114 @@ +package com.jme3x.jfx.injfx; + +import static java.lang.Math.max; +import static java.lang.Math.min; + +import com.jme3.renderer.ViewPort; +import com.jme3.system.AppSettings; +import com.jme3x.jfx.injfx.processor.CanvasFrameTransferSceneProcessor; +import com.jme3x.jfx.injfx.processor.FrameTransferSceneProcessor; +import com.jme3x.jfx.injfx.processor.ImageViewFrameTransferSceneProcessor; +import com.sun.istack.internal.NotNull; + +import java.util.function.Function; + +import javafx.application.Platform; +import javafx.scene.Node; +import javafx.scene.canvas.Canvas; +import javafx.scene.image.ImageView; + +/** + * @author JavaSaBr + */ +public class JmeToJFXIntegrator { + + public static void prepareSettings(@NotNull final AppSettings settings, final int frameRate) { + settings.setFullscreen(false); + settings.setFrameRate(max(1, min(100, frameRate))); + settings.setCustomRenderer(JmeOffscreenSurfaceContext.class); + } + + @NotNull + public static FrameTransferSceneProcessor startAndBind(@NotNull final JmeToJFXApplication application, + @NotNull final ImageView imageView, @NotNull final Function factory) { + factory.apply(application::start).start(); + final ImageViewFrameTransferSceneProcessor processor = new ImageViewFrameTransferSceneProcessor(); + Platform.runLater(() -> application.enqueue(() -> processor.bind(imageView, application))); + return processor; + } + + @NotNull + public static FrameTransferSceneProcessor startAndBind(@NotNull final JmeToJFXApplication application, + @NotNull final Canvas canvas, @NotNull final Function factory) { + factory.apply(application::start).start(); + final CanvasFrameTransferSceneProcessor processor = new CanvasFrameTransferSceneProcessor(); + Platform.runLater(() -> application.enqueue(() -> processor.bind(canvas, application))); + return processor; + } + + @NotNull + public static FrameTransferSceneProcessor bind(@NotNull final JmeToJFXApplication application, @NotNull final ImageView imageView) { + final ImageViewFrameTransferSceneProcessor processor = new ImageViewFrameTransferSceneProcessor(); + processor.bind(imageView, application); + return processor; + } + + @NotNull + public static FrameTransferSceneProcessor bind(@NotNull final JmeToJFXApplication application, @NotNull final Canvas canvas) { + final CanvasFrameTransferSceneProcessor processor = new CanvasFrameTransferSceneProcessor(); + processor.bind(canvas, application); + return processor; + } + + @NotNull + public static FrameTransferSceneProcessor bind(@NotNull final JmeToJFXApplication application, + @NotNull final ImageView imageView, @NotNull final ViewPort viewPort) { + final ImageViewFrameTransferSceneProcessor processor = new ImageViewFrameTransferSceneProcessor(); + processor.bind(imageView, application, viewPort); + return processor; + } + + @NotNull + public static FrameTransferSceneProcessor bind(@NotNull final JmeToJFXApplication application, + @NotNull final Canvas canvas, @NotNull final ViewPort viewPort) { + final CanvasFrameTransferSceneProcessor processor = new CanvasFrameTransferSceneProcessor(); + processor.bind(canvas, application, viewPort); + return processor; + } + + @NotNull + public static FrameTransferSceneProcessor bind(@NotNull final JmeToJFXApplication application, + @NotNull final ImageView imageView, @NotNull final Node inputNode) { + final ImageViewFrameTransferSceneProcessor processor = new ImageViewFrameTransferSceneProcessor(); + processor.bind(imageView, application, inputNode); + return processor; + } + + @NotNull + public static FrameTransferSceneProcessor bind(@NotNull final JmeToJFXApplication application, + @NotNull final Canvas canvas, @NotNull final Node inputNode) { + final CanvasFrameTransferSceneProcessor processor = new CanvasFrameTransferSceneProcessor(); + processor.bind(canvas, application, inputNode); + return processor; + } + + @NotNull + public static FrameTransferSceneProcessor bind(@NotNull final JmeToJFXApplication application, + @NotNull final ImageView imageView, + @NotNull final Node inputNode, + @NotNull final ViewPort viewPort, final boolean main) { + final ImageViewFrameTransferSceneProcessor processor = new ImageViewFrameTransferSceneProcessor(); + processor.bind(imageView, application, inputNode, viewPort, main); + return processor; + } + + @NotNull + public static FrameTransferSceneProcessor bind(@NotNull final JmeToJFXApplication application, + @NotNull final Canvas canvas, + @NotNull final Node inputNode, + @NotNull final ViewPort viewPort, final boolean main) { + final CanvasFrameTransferSceneProcessor processor = new CanvasFrameTransferSceneProcessor(); + processor.bind(canvas, application, inputNode, viewPort, main); + return processor; + } +} diff --git a/src/com/jme3x/jfx/injfx/input/JFXInput.java b/src/com/jme3x/jfx/injfx/input/JFXInput.java new file mode 100644 index 0000000..75ebb68 --- /dev/null +++ b/src/com/jme3x/jfx/injfx/input/JFXInput.java @@ -0,0 +1,102 @@ +package com.jme3x.jfx.injfx.input; + +import com.jme3.input.Input; +import com.jme3.input.RawInputListener; +import com.jme3x.jfx.injfx.ApplicationThreadExecutor; +import com.jme3x.jfx.injfx.JmeOffscreenSurfaceContext; +import com.sun.istack.internal.NotNull; + +import java.util.Objects; + +import javafx.scene.Node; +import javafx.scene.Scene; + +/** + * The base implementation of the {@link Input} for using in the ImageView. + * + * @author JavaSaBr. + */ +public class JFXInput implements Input { + + protected static final ApplicationThreadExecutor EXECUTOR = ApplicationThreadExecutor.getInstance(); + + /** + * The context. + */ + protected final JmeOffscreenSurfaceContext context; + + /** + * The raw listener. + */ + protected RawInputListener listener; + + /** + * The input node. + */ + protected Node node; + + /** + * The scene. + */ + protected Scene scene; + + /** + * Initializes is it. + */ + protected boolean initialized; + + public JFXInput(@NotNull final JmeOffscreenSurfaceContext context) { + this.context = context; + } + + public void bind(@NotNull final Node node) { + this.node = node; + this.scene = node.getScene(); + Objects.requireNonNull(this.node, "ImageView can' be null"); + Objects.requireNonNull(this.scene, "The scene of the ImageView can' be null"); + } + + public void unbind() { + this.node = null; + this.scene = null; + } + + @Override + public void initialize() { + if (isInitialized()) return; + initializeImpl(); + initialized = true; + } + + protected void initializeImpl() { + } + + @Override + public void update() { + if (!context.isRenderable()) return; + updateImpl(); + } + + protected void updateImpl() { + } + + @Override + public void destroy() { + unbind(); + } + + @Override + public boolean isInitialized() { + return initialized; + } + + @Override + public void setInputListener(@NotNull final RawInputListener listener) { + this.listener = listener; + } + + @Override + public long getInputTimeNanos() { + return System.nanoTime(); + } +} diff --git a/src/com/jme3x/jfx/injfx/input/JFXKeyInput.java b/src/com/jme3x/jfx/injfx/input/JFXKeyInput.java new file mode 100644 index 0000000..707d9b3 --- /dev/null +++ b/src/com/jme3x/jfx/injfx/input/JFXKeyInput.java @@ -0,0 +1,202 @@ +package com.jme3x.jfx.injfx.input; + +import static rlib.util.linkedlist.LinkedListFactory.newLinkedList; + +import com.jme3.input.KeyInput; +import com.jme3.input.event.KeyInputEvent; +import com.jme3x.jfx.injfx.JmeOffscreenSurfaceContext; +import com.sun.istack.internal.NotNull; + +import java.util.HashMap; +import java.util.Map; + +import javafx.event.EventHandler; +import javafx.scene.Node; +import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import rlib.util.linkedlist.LinkedList; + +/** + * The implementation of the {@link KeyInput} for using in the {@link ImageView}. + * + * @author JavaSaBr. + */ +public class JFXKeyInput extends JFXInput implements KeyInput { + + @NotNull + private static final Map KEY_CODE_TO_JME = new HashMap<>(); + + static { + KEY_CODE_TO_JME.put(KeyCode.ESCAPE, KEY_ESCAPE); + KEY_CODE_TO_JME.put(KeyCode.DIGIT0, KEY_0); + KEY_CODE_TO_JME.put(KeyCode.DIGIT1, KEY_1); + KEY_CODE_TO_JME.put(KeyCode.DIGIT2, KEY_2); + KEY_CODE_TO_JME.put(KeyCode.DIGIT3, KEY_3); + KEY_CODE_TO_JME.put(KeyCode.DIGIT4, KEY_4); + KEY_CODE_TO_JME.put(KeyCode.DIGIT5, KEY_5); + KEY_CODE_TO_JME.put(KeyCode.DIGIT6, KEY_6); + KEY_CODE_TO_JME.put(KeyCode.DIGIT7, KEY_7); + KEY_CODE_TO_JME.put(KeyCode.DIGIT8, KEY_8); + KEY_CODE_TO_JME.put(KeyCode.DIGIT9, KEY_9); + KEY_CODE_TO_JME.put(KeyCode.MINUS, KEY_MINUS); + KEY_CODE_TO_JME.put(KeyCode.EQUALS, KEY_EQUALS); + KEY_CODE_TO_JME.put(KeyCode.BACK_SPACE, KEY_BACK); + KEY_CODE_TO_JME.put(KeyCode.TAB, KEY_TAB); + KEY_CODE_TO_JME.put(KeyCode.Q, KEY_Q); + KEY_CODE_TO_JME.put(KeyCode.W, KEY_W); + KEY_CODE_TO_JME.put(KeyCode.E, KEY_E); + KEY_CODE_TO_JME.put(KeyCode.R, KEY_R); + KEY_CODE_TO_JME.put(KeyCode.T, KEY_T); + KEY_CODE_TO_JME.put(KeyCode.U, KEY_U); + KEY_CODE_TO_JME.put(KeyCode.I, KEY_I); + KEY_CODE_TO_JME.put(KeyCode.O, KEY_O); + KEY_CODE_TO_JME.put(KeyCode.P, KEY_P); + KEY_CODE_TO_JME.put(KeyCode.OPEN_BRACKET, KEY_LBRACKET); + KEY_CODE_TO_JME.put(KeyCode.CLOSE_BRACKET, KEY_RBRACKET); + KEY_CODE_TO_JME.put(KeyCode.ENTER, KEY_RETURN); + KEY_CODE_TO_JME.put(KeyCode.CONTROL, KEY_LCONTROL); + KEY_CODE_TO_JME.put(KeyCode.A, KEY_A); + KEY_CODE_TO_JME.put(KeyCode.S, KEY_S); + KEY_CODE_TO_JME.put(KeyCode.D, KEY_D); + KEY_CODE_TO_JME.put(KeyCode.F, KEY_F); + KEY_CODE_TO_JME.put(KeyCode.G, KEY_G); + KEY_CODE_TO_JME.put(KeyCode.H, KEY_H); + KEY_CODE_TO_JME.put(KeyCode.J, KEY_J); + KEY_CODE_TO_JME.put(KeyCode.Y, KEY_Y); + KEY_CODE_TO_JME.put(KeyCode.K, KEY_K); + KEY_CODE_TO_JME.put(KeyCode.L, KEY_L); + KEY_CODE_TO_JME.put(KeyCode.SEMICOLON, KEY_SEMICOLON); + KEY_CODE_TO_JME.put(KeyCode.QUOTE, KEY_APOSTROPHE); + KEY_CODE_TO_JME.put(KeyCode.DEAD_GRAVE, KEY_GRAVE); + KEY_CODE_TO_JME.put(KeyCode.SHIFT, KEY_LSHIFT); + KEY_CODE_TO_JME.put(KeyCode.BACK_SLASH, KEY_BACKSLASH); + KEY_CODE_TO_JME.put(KeyCode.Z, KEY_Z); + KEY_CODE_TO_JME.put(KeyCode.X, KEY_X); + KEY_CODE_TO_JME.put(KeyCode.C, KEY_C); + KEY_CODE_TO_JME.put(KeyCode.V, KEY_V); + KEY_CODE_TO_JME.put(KeyCode.B, KEY_B); + KEY_CODE_TO_JME.put(KeyCode.N, KEY_N); + KEY_CODE_TO_JME.put(KeyCode.M, KEY_M); + KEY_CODE_TO_JME.put(KeyCode.COMMA, KEY_COMMA); + KEY_CODE_TO_JME.put(KeyCode.PERIOD, KEY_PERIOD); + KEY_CODE_TO_JME.put(KeyCode.SLASH, KEY_SLASH); + KEY_CODE_TO_JME.put(KeyCode.MULTIPLY, KEY_MULTIPLY); + KEY_CODE_TO_JME.put(KeyCode.SPACE, KEY_SPACE); + KEY_CODE_TO_JME.put(KeyCode.CAPS, KEY_CAPITAL); + KEY_CODE_TO_JME.put(KeyCode.F1, KEY_F1); + KEY_CODE_TO_JME.put(KeyCode.F2, KEY_F2); + KEY_CODE_TO_JME.put(KeyCode.F3, KEY_F3); + KEY_CODE_TO_JME.put(KeyCode.F4, KEY_F4); + KEY_CODE_TO_JME.put(KeyCode.F5, KEY_F5); + KEY_CODE_TO_JME.put(KeyCode.F6, KEY_F6); + KEY_CODE_TO_JME.put(KeyCode.F7, KEY_F7); + KEY_CODE_TO_JME.put(KeyCode.F8, KEY_F8); + KEY_CODE_TO_JME.put(KeyCode.F9, KEY_F9); + KEY_CODE_TO_JME.put(KeyCode.F10, KEY_F10); + KEY_CODE_TO_JME.put(KeyCode.NUM_LOCK, KEY_NUMLOCK); + KEY_CODE_TO_JME.put(KeyCode.SCROLL_LOCK, KEY_SCROLL); + KEY_CODE_TO_JME.put(KeyCode.NUMPAD7, KEY_NUMPAD7); + KEY_CODE_TO_JME.put(KeyCode.NUMPAD8, KEY_NUMPAD8); + KEY_CODE_TO_JME.put(KeyCode.NUMPAD9, KEY_NUMPAD9); + KEY_CODE_TO_JME.put(KeyCode.SUBTRACT, KEY_SUBTRACT); + KEY_CODE_TO_JME.put(KeyCode.NUMPAD4, KEY_NUMPAD4); + KEY_CODE_TO_JME.put(KeyCode.NUMPAD5, KEY_NUMPAD5); + KEY_CODE_TO_JME.put(KeyCode.NUMPAD6, KEY_NUMPAD6); + KEY_CODE_TO_JME.put(KeyCode.ADD, KEY_ADD); + KEY_CODE_TO_JME.put(KeyCode.NUMPAD1, KEY_NUMPAD1); + KEY_CODE_TO_JME.put(KeyCode.NUMPAD2, KEY_NUMPAD2); + KEY_CODE_TO_JME.put(KeyCode.NUMPAD3, KEY_NUMPAD3); + KEY_CODE_TO_JME.put(KeyCode.NUMPAD0, KEY_NUMPAD0); + KEY_CODE_TO_JME.put(KeyCode.DECIMAL, KEY_DECIMAL); + KEY_CODE_TO_JME.put(KeyCode.F11, KEY_F11); + KEY_CODE_TO_JME.put(KeyCode.F12, KEY_F12); + KEY_CODE_TO_JME.put(KeyCode.F13, KEY_F13); + KEY_CODE_TO_JME.put(KeyCode.F14, KEY_F14); + KEY_CODE_TO_JME.put(KeyCode.F15, KEY_F15); + KEY_CODE_TO_JME.put(KeyCode.KANA, KEY_KANA); + KEY_CODE_TO_JME.put(KeyCode.CONVERT, KEY_CONVERT); + KEY_CODE_TO_JME.put(KeyCode.NONCONVERT, KEY_NOCONVERT); + KEY_CODE_TO_JME.put(KeyCode.CIRCUMFLEX, KEY_CIRCUMFLEX); + KEY_CODE_TO_JME.put(KeyCode.AT, KEY_AT); + KEY_CODE_TO_JME.put(KeyCode.COLON, KEY_COLON); + KEY_CODE_TO_JME.put(KeyCode.UNDERSCORE, KEY_UNDERLINE); + KEY_CODE_TO_JME.put(KeyCode.STOP, KEY_STOP); + KEY_CODE_TO_JME.put(KeyCode.DIVIDE, KEY_DIVIDE); + KEY_CODE_TO_JME.put(KeyCode.PAUSE, KEY_PAUSE); + KEY_CODE_TO_JME.put(KeyCode.HOME, KEY_HOME); + KEY_CODE_TO_JME.put(KeyCode.UP, KEY_UP); + KEY_CODE_TO_JME.put(KeyCode.PAGE_UP, KEY_PRIOR); + KEY_CODE_TO_JME.put(KeyCode.LEFT, KEY_LEFT); + KEY_CODE_TO_JME.put(KeyCode.RIGHT, KEY_RIGHT); + KEY_CODE_TO_JME.put(KeyCode.END, KEY_END); + KEY_CODE_TO_JME.put(KeyCode.DOWN, KEY_DOWN); + KEY_CODE_TO_JME.put(KeyCode.PAGE_DOWN, KEY_NEXT); + KEY_CODE_TO_JME.put(KeyCode.INSERT, KEY_INSERT); + KEY_CODE_TO_JME.put(KeyCode.DELETE, KEY_DELETE); + KEY_CODE_TO_JME.put(KeyCode.ALT, KEY_LMENU); + KEY_CODE_TO_JME.put(KeyCode.META, KEY_RCONTROL); + } + + @NotNull + private final EventHandler processKeyPressed = this::processKeyPressed; + + @NotNull + private final EventHandler processKeyReleased = this::processKeyReleased; + + @NotNull + private final LinkedList keyInputEvents; + + public JFXKeyInput(final JmeOffscreenSurfaceContext context) { + super(context); + keyInputEvents = newLinkedList(KeyInputEvent.class); + } + + @Override + public void bind(@NotNull final Node node) { + super.bind(node); + node.addEventHandler(KeyEvent.KEY_PRESSED, processKeyPressed); + node.addEventHandler(KeyEvent.KEY_RELEASED, processKeyReleased); + } + + @Override + public void unbind() { + if (node != null) { + node.removeEventHandler(KeyEvent.KEY_PRESSED, processKeyPressed); + node.removeEventHandler(KeyEvent.KEY_RELEASED, processKeyReleased); + } + super.unbind(); + } + + private void processKeyReleased(@NotNull final KeyEvent keyEvent) { + onKeyEvent(keyEvent, false); + } + + private void processKeyPressed(@NotNull final KeyEvent keyEvent) { + onKeyEvent(keyEvent, true); + } + + private void onKeyEvent(@NotNull final KeyEvent keyEvent, final boolean pressed) { + + final int code = convertKeyCode(keyEvent.getCode()); + final String character = keyEvent.getText(); + final char keyChar = character.isEmpty() ? '\0' : character.charAt(0); + + final KeyInputEvent event = new KeyInputEvent(code, keyChar, pressed, false); + event.setTime(getInputTimeNanos()); + + EXECUTOR.addToExecute(() -> keyInputEvents.add(event)); + } + + @Override + protected void updateImpl() { + while (!keyInputEvents.isEmpty()) { + listener.onKeyEvent(keyInputEvents.poll()); + } + } + + private int convertKeyCode(@NotNull final KeyCode keyCode) { + final Integer code = KEY_CODE_TO_JME.get(keyCode); + return code == null ? KEY_UNKNOWN : code; + } +} diff --git a/src/com/jme3x/jfx/injfx/input/JFXMouseInput.java b/src/com/jme3x/jfx/injfx/input/JFXMouseInput.java new file mode 100644 index 0000000..86c70a8 --- /dev/null +++ b/src/com/jme3x/jfx/injfx/input/JFXMouseInput.java @@ -0,0 +1,191 @@ +package com.jme3x.jfx.injfx.input; + +import static rlib.util.linkedlist.LinkedListFactory.newLinkedList; + +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.jme3x.jfx.injfx.JmeOffscreenSurfaceContext; +import com.sun.istack.internal.NotNull; + +import java.util.HashMap; +import java.util.Map; + +import javafx.event.EventHandler; +import javafx.scene.Node; +import javafx.scene.image.ImageView; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.scene.input.ScrollEvent; +import rlib.util.linkedlist.LinkedList; + +/** + * The implementation of the {@link MouseInput} for using in the {@link ImageView}. + * + * @author JavaSaBr. + */ +public class JFXMouseInput extends JFXInput implements MouseInput { + + @NotNull + private static final Map MOUSE_BUTTON_TO_JME = new HashMap<>(); + + static { + MOUSE_BUTTON_TO_JME.put(MouseButton.PRIMARY, BUTTON_LEFT); + MOUSE_BUTTON_TO_JME.put(MouseButton.SECONDARY, BUTTON_RIGHT); + MOUSE_BUTTON_TO_JME.put(MouseButton.MIDDLE, BUTTON_MIDDLE); + } + + /** + * The scale factor for scrolling. + */ + private static final int WHEEL_SCALE = 10; + + @NotNull + private final EventHandler processMotion = this::processMotion; + + @NotNull + private final EventHandler processPressed = this::processPressed; + + @NotNull + private final EventHandler processReleased = this::processReleased; + + @NotNull + private final EventHandler processScroll = this::processScroll; + + @NotNull + private final LinkedList mouseMotionEvents; + + @NotNull + private final LinkedList mouseButtonEvents; + + private int mouseX; + private int mouseY; + private int mouseWheel; + + public JFXMouseInput(@NotNull final JmeOffscreenSurfaceContext context) { + super(context); + mouseMotionEvents = newLinkedList(MouseMotionEvent.class); + mouseButtonEvents = newLinkedList(MouseButtonEvent.class); + } + + @Override + public void bind(@NotNull final Node node) { + super.bind(node); + node.addEventHandler(MouseEvent.MOUSE_MOVED, processMotion); + node.addEventHandler(MouseEvent.MOUSE_PRESSED, processPressed); + node.addEventHandler(MouseEvent.MOUSE_RELEASED, processReleased); + node.addEventHandler(MouseEvent.MOUSE_DRAGGED, processMotion); + node.addEventHandler(ScrollEvent.ANY, processScroll); + } + + @Override + public void unbind() { + if (node != null) { + node.removeEventHandler(MouseEvent.MOUSE_MOVED, processMotion); + node.removeEventHandler(MouseEvent.MOUSE_DRAGGED, processMotion); + node.removeEventHandler(MouseEvent.MOUSE_PRESSED, processPressed); + node.removeEventHandler(MouseEvent.MOUSE_RELEASED, processReleased); + node.removeEventHandler(ScrollEvent.ANY, processScroll); + } + super.unbind(); + } + + @Override + protected void updateImpl() { + while (!mouseMotionEvents.isEmpty()) { + listener.onMouseMotionEvent(mouseMotionEvents.poll()); + } + while (!mouseButtonEvents.isEmpty()) { + listener.onMouseButtonEvent(mouseButtonEvents.poll()); + } + } + + /** + * Handle the scroll event. + */ + private void processScroll(@NotNull final ScrollEvent mouseEvent) { + onWheelScroll(mouseEvent.getDeltaX() * WHEEL_SCALE, mouseEvent.getDeltaY() * WHEEL_SCALE); + } + + /** + * Handle the mouse released event. + */ + private void processReleased(@NotNull final MouseEvent mouseEvent) { + onMouseButton(mouseEvent.getButton(), false); + } + + /** + * Handle the mouse pressed event. + */ + private void processPressed(@NotNull final MouseEvent mouseEvent) { + onMouseButton(mouseEvent.getButton(), true); + } + + /** + * Handle the mouse motion event. + */ + private void processMotion(@NotNull final MouseEvent mouseEvent) { + onCursorPos(mouseEvent.getSceneX(), mouseEvent.getSceneY()); + } + + private void onWheelScroll(final double xOffset, final double yOffset) { + + mouseWheel += yOffset; + + final MouseMotionEvent mouseMotionEvent = new MouseMotionEvent(mouseX, mouseY, 0, 0, mouseWheel, (int) Math.round(yOffset)); + mouseMotionEvent.setTime(getInputTimeNanos()); + + EXECUTOR.addToExecute(() -> mouseMotionEvents.add(mouseMotionEvent)); + } + + private void onCursorPos(final double xpos, final double ypos) { + + int xDelta; + int yDelta; + int x = (int) Math.round(xpos); + int y = context.getHeight() - (int) Math.round(ypos); + + if (mouseX == 0) mouseX = x; + if (mouseY == 0) mouseY = y; + + xDelta = x - mouseX; + yDelta = y - mouseY; + + mouseX = x; + mouseY = y; + + if (xDelta == 0 && yDelta == 0) return; + + final MouseMotionEvent mouseMotionEvent = new MouseMotionEvent(x, y, xDelta, yDelta, mouseWheel, 0); + mouseMotionEvent.setTime(getInputTimeNanos()); + + EXECUTOR.addToExecute(() -> mouseMotionEvents.add(mouseMotionEvent)); + } + + private void onMouseButton(@NotNull final MouseButton button, final boolean pressed) { + + final MouseButtonEvent mouseButtonEvent = new MouseButtonEvent(convertButton(button), pressed, mouseX, mouseY); + mouseButtonEvent.setTime(getInputTimeNanos()); + + EXECUTOR.addToExecute(() -> mouseButtonEvents.add(mouseButtonEvent)); + } + + private int convertButton(@NotNull final MouseButton button) { + final Integer result = MOUSE_BUTTON_TO_JME.get(button); + return result == null ? 0 : result; + } + + @Override + public void setCursorVisible(final boolean visible) { + } + + @Override + public int getButtonCount() { + return 3; + } + + @Override + public void setNativeCursor(@NotNull final JmeCursor cursor) { + } +} diff --git a/src/com/jme3x/jfx/injfx/processor/AbstractFrameTransferSceneProcessor.java b/src/com/jme3x/jfx/injfx/processor/AbstractFrameTransferSceneProcessor.java new file mode 100644 index 0000000..438afa9 --- /dev/null +++ b/src/com/jme3x/jfx/injfx/processor/AbstractFrameTransferSceneProcessor.java @@ -0,0 +1,392 @@ +package com.jme3x.jfx.injfx.processor; + +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.texture.FrameBuffer; +import com.jme3.texture.Image; +import com.jme3x.jfx.injfx.JmeOffscreenSurfaceContext; +import com.jme3x.jfx.injfx.JmeToJFXApplication; +import com.jme3x.jfx.injfx.input.JFXKeyInput; +import com.jme3x.jfx.injfx.input.JFXMouseInput; +import com.jme3x.jfx.injfx.transfer.FrameTransfer; +import com.jme3x.jfx.util.JFXPlatform; +import com.sun.istack.internal.NotNull; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; + +import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.scene.Node; +import javafx.scene.image.ImageView; + +/** + * The base implementation of scene processor for transferring frames. + * + * @author JavaSaBr. + */ +public abstract class AbstractFrameTransferSceneProcessor implements FrameTransferSceneProcessor { + + /** + * The listeners. + */ + protected final ChangeListener widthListener; + protected final ChangeListener heightListener; + protected final ChangeListener rationListener; + + private final AtomicBoolean reshapeNeeded; + + private RenderManager renderManager; + private ViewPort viewPort; + private FrameTransfer frameTransfer; + + /** + * THe JME application. + */ + private volatile JmeToJFXApplication application; + + /** + * The {@link ImageView} for showing the content of jME. + */ + protected volatile T destination; + + /** + * The main processor is it. + */ + private volatile boolean main; + + private int askWidth; + private int askHeight; + + private boolean askFixAspect; + private boolean enabled; + + public AbstractFrameTransferSceneProcessor() { + askWidth = 1; + askHeight = 1; + main = true; + reshapeNeeded = new AtomicBoolean(true); + widthListener = (view, oldValue, newValue) -> notifyChangedWidth(newValue); + heightListener = (view, oldValue, newValue) -> notifyChangedHeight(newValue); + rationListener = (view, oldValue, newValue) -> notifyChangedRatio(newValue); + } + + /** + * Notify about that the ratio was changed. + * + * @param newValue the new value of the ratio. + */ + protected void notifyChangedRatio(final Boolean newValue) { + notifyComponentResized(getDestinationWidth(), getDestinationHeight(), newValue); + } + + /** + * Notify about that the height was changed. + * + * @param newValue the new value of the height. + */ + protected void notifyChangedHeight(@NotNull final Number newValue) { + notifyComponentResized(getDestinationWidth(), newValue.intValue(), isPreserveRatio()); + } + + /** + * Notify about that the width was changed. + * + * @param newValue the new value of the width. + */ + protected void notifyChangedWidth(@NotNull final Number newValue) { + notifyComponentResized(newValue.intValue(), getDestinationHeight(), isPreserveRatio()); + } + + /** + * Handle resizing. + */ + protected void notifyComponentResized(int newWidth, int newHeight, boolean fixAspect) { + newWidth = Math.max(newWidth, 1); + newHeight = Math.max(newHeight, 1); + if (askWidth == newWidth && askWidth == newHeight && askFixAspect == fixAspect) return; + askWidth = newWidth; + askHeight = newHeight; + askFixAspect = fixAspect; + reshapeNeeded.set(true); + } + + /** + * @return is preserve ratio. + */ + protected boolean isPreserveRatio() { + throw new UnsupportedOperationException(); + } + + /** + * @return the destination width. + */ + protected int getDestinationWidth() { + throw new UnsupportedOperationException(); + } + + /** + * @return the destination height. + */ + protected int getDestinationHeight() { + throw new UnsupportedOperationException(); + } + + public void bind(@NotNull final T destination, @NotNull final JmeToJFXApplication application) { + bind(destination, application, destination); + } + + /** + * Bind this processor to the destination. + * + * @param destination the destination. + * @param application the application. + * @param viewPort the view port. + */ + public void bind(@NotNull final T destination, @NotNull final JmeToJFXApplication application, + @NotNull final ViewPort viewPort) { + bind(destination, application, destination, viewPort, true); + } + + + /** + * Bind this processor to the destination. + * + * @param destination the destination. + * @param application the application. + * @param inputNode the input node. + */ + public void bind(@NotNull final T destination, @NotNull final JmeToJFXApplication application, + @NotNull final Node inputNode) { + final RenderManager renderManager = application.getRenderManager(); + final List postViews = renderManager.getPostViews(); + if (postViews.isEmpty()) throw new RuntimeException("the list of a post view is empty."); + bind(destination, application, inputNode, postViews.get(postViews.size() - 1), true); + } + + /** + * Bind this processor to the destination. + * + * @param application the application. + * @param destination the destination. + * @param inputNode the input node. + * @param viewPort the view port. + * @param main true if this processor is main. + */ + public void bind(@NotNull final T destination, @NotNull final JmeToJFXApplication application, + @NotNull final Node inputNode, @NotNull final ViewPort viewPort, final boolean main) { + + if (this.application != null) throw new RuntimeException("This process is already bonded."); + + this.enabled = true; + this.main = main; + this.application = application; + this.viewPort = viewPort; + this.viewPort.addProcessor(this); + + JFXPlatform.runInFXThread(() -> bindDestination(application, destination, inputNode)); + } + + /** + * Bind this processor to the destination. + * + * @param application the application. + * @param destination the destination. + * @param inputNode the input node. + */ + protected void bindDestination(@NotNull final JmeToJFXApplication application, @NotNull final T destination, + @NotNull final Node inputNode) { + + if (!Platform.isFxApplicationThread()) { + throw new RuntimeException("this call is not from JavaFX thread."); + } + + if (isMain()) { + final JmeOffscreenSurfaceContext context = (JmeOffscreenSurfaceContext) application.getContext(); + final JFXMouseInput mouseInput = context.getMouseInput(); + mouseInput.bind(inputNode); + final JFXKeyInput keyInput = context.getKeyInput(); + keyInput.bind(inputNode); + } + + this.destination = destination; + bindListeners(); + this.destination.setPickOnBounds(true); + + notifyComponentResized(getDestinationWidth(), getDestinationHeight(), isPreserveRatio()); + } + + /** + * Bind listeners to current destination. + */ + protected void bindListeners() { + } + + /** + * Unbind this processor from its current destination. + */ + public void unbind() { + + if (viewPort != null) { + viewPort.removeProcessor(this); + viewPort = null; + } + + JFXPlatform.runInFXThread(this::unbindDestination); + } + + /** + * Unbind this processor from destination. + */ + protected void unbindDestination() { + + if (!Platform.isFxApplicationThread()) { + throw new RuntimeException("this call is not from JavaFX thread."); + } + + if (application != null && isMain()) { + final JmeOffscreenSurfaceContext context = (JmeOffscreenSurfaceContext) application.getContext(); + final JFXMouseInput mouseInput = context.getMouseInput(); + mouseInput.unbind(); + final JFXKeyInput keyInput = context.getKeyInput(); + keyInput.unbind(); + } + + application = null; + + if (destination == null) return; + unbindListeners(); + destination = null; + } + + /** + * Unbind all listeners from destination. + */ + protected void unbindListeners() { + } + + @Override + public boolean isMain() { + return main; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void setEnabled(final boolean enabled) { + this.enabled = enabled; + } + + @NotNull + protected FrameTransfer reshapeInThread(final int width, final int height, final boolean fixAspect) { + reshapeCurrentViewPort(width, height); + + final FrameBuffer frameBuffer = viewPort.getOutputFrameBuffer(); + final FrameTransfer frameTransfer = createFrameTransfer(width, height, frameBuffer); + frameTransfer.initFor(renderManager.getRenderer(), isMain()); + + if (isMain()) { + final JmeOffscreenSurfaceContext context = (JmeOffscreenSurfaceContext) application.getContext(); + context.setHeight(height); + context.setWidth(width); + } + + return frameTransfer; + } + + @NotNull + protected FrameTransfer createFrameTransfer(final int width, final int height, + @NotNull final FrameBuffer frameBuffer) { + throw new UnsupportedOperationException(); + } + + protected void reshapeCurrentViewPort(final int width, final int height) { + + final Camera cam = viewPort.getCamera(); + + if (isMain()) { + renderManager.notifyReshape(width, height); + cam.setFrustumPerspective(45, (float) cam.getWidth() / cam.getHeight(), 1f, 10000); + return; + } + + cam.resize(width, height, true); + cam.setFrustumPerspective(45, (float) cam.getWidth() / cam.getHeight(), 1f, 10000); + + final List processors = viewPort.getProcessors(); + final Optional any = processors.stream() + .filter(sceneProcessor -> !(sceneProcessor instanceof FrameTransferSceneProcessor)) + .findAny(); + + if (!any.isPresent()) { + + final FrameBuffer frameBuffer = new FrameBuffer(width, height, 1); + frameBuffer.setDepthBuffer(Image.Format.Depth); + frameBuffer.setColorBuffer(Image.Format.BGRA8); + + viewPort.setOutputFrameBuffer(frameBuffer); + } + + for (final SceneProcessor sceneProcessor : processors) { + if (!sceneProcessor.isInitialized()) { + sceneProcessor.initialize(renderManager, viewPort); + } else { + sceneProcessor.reshape(viewPort, width, height); + } + } + } + + @Override + public void initialize(@NotNull final RenderManager renderManager, @NotNull final ViewPort viewPort) { + this.renderManager = renderManager; + } + + @Override + public void reshape(@NotNull final ViewPort viewPort, final int w, final int h) { + } + + @Override + public boolean isInitialized() { + return frameTransfer != null; + } + + @Override + public void preFrame(final float tpf) { + + } + + @Override + public void postQueue(@NotNull final RenderQueue renderQueue) { + + } + + @Override + public void postFrame(@NotNull final FrameBuffer out) { + if (!isEnabled()) return; + + if (frameTransfer != null) { + frameTransfer.copyFrameBufferToImage(renderManager); + } + + // for the next frame + if (destination != null && reshapeNeeded.getAndSet(false)) { + if (frameTransfer != null) frameTransfer.dispose(); + frameTransfer = reshapeInThread(askWidth, askHeight, askFixAspect); + } + } + + @Override + public void cleanup() { + if (frameTransfer != null) { + frameTransfer.dispose(); + frameTransfer = null; + } + } +} diff --git a/src/com/jme3x/jfx/injfx/processor/CanvasFrameTransferSceneProcessor.java b/src/com/jme3x/jfx/injfx/processor/CanvasFrameTransferSceneProcessor.java new file mode 100644 index 0000000..198f9df --- /dev/null +++ b/src/com/jme3x/jfx/injfx/processor/CanvasFrameTransferSceneProcessor.java @@ -0,0 +1,60 @@ +package com.jme3x.jfx.injfx.processor; + +import com.jme3.post.SceneProcessor; +import com.jme3.texture.FrameBuffer; +import com.jme3x.jfx.injfx.JmeToJFXApplication; +import com.jme3x.jfx.injfx.transfer.FrameTransfer; +import com.jme3x.jfx.injfx.transfer.impl.CanvasFrameTransfer; +import com.sun.istack.internal.NotNull; + +import javafx.scene.Node; +import javafx.scene.canvas.Canvas; + +/** + * The implementation of the {@link SceneProcessor} for transferring content between jME and Canvas. + * + * @author JavaSaBr + */ +public class CanvasFrameTransferSceneProcessor extends AbstractFrameTransferSceneProcessor { + + @Override + protected int getDestinationWidth() { + return (int) destination.getWidth(); + } + + @Override + protected int getDestinationHeight() { + return (int) destination.getHeight(); + } + + @Override + protected boolean isPreserveRatio() { + return false; + } + + @Override + protected void bindDestination(@NotNull final JmeToJFXApplication application, @NotNull final Canvas destination, + @NotNull final Node inputNode) { + super.bindDestination(application, destination, inputNode); + destination.setScaleY(-1.0); + } + + @Override + protected void bindListeners() { + destination.widthProperty().addListener(widthListener); + destination.heightProperty().addListener(heightListener); + super.bindListeners(); + } + + @Override + protected void unbindListeners() { + destination.widthProperty().removeListener(widthListener); + destination.heightProperty().removeListener(heightListener); + super.unbindListeners(); + } + + @Override + protected FrameTransfer createFrameTransfer(final int width, final int height, @NotNull final FrameBuffer frameBuffer) { + return new CanvasFrameTransfer(destination, isMain() ? null : frameBuffer, width, height); + } +} \ No newline at end of file diff --git a/src/com/jme3x/jfx/injfx/processor/FrameTransferSceneProcessor.java b/src/com/jme3x/jfx/injfx/processor/FrameTransferSceneProcessor.java new file mode 100644 index 0000000..7c31ec9 --- /dev/null +++ b/src/com/jme3x/jfx/injfx/processor/FrameTransferSceneProcessor.java @@ -0,0 +1,26 @@ +package com.jme3x.jfx.injfx.processor; + +import com.jme3.post.SceneProcessor; + +/** + * The interface for implementing frame transfer processor. + * + * @author JavaSaBr + */ +public interface FrameTransferSceneProcessor extends SceneProcessor { + + /** + * @return if this processor is main. + */ + boolean isMain(); + + /** + * @return true if this processor is enabled. + */ + boolean isEnabled(); + + /** + * @param enabled true if this processor is enabled. + */ + void setEnabled(final boolean enabled); +} diff --git a/src/com/jme3x/jfx/injfx/processor/ImageViewFrameTransferSceneProcessor.java b/src/com/jme3x/jfx/injfx/processor/ImageViewFrameTransferSceneProcessor.java new file mode 100644 index 0000000..4d2712a --- /dev/null +++ b/src/com/jme3x/jfx/injfx/processor/ImageViewFrameTransferSceneProcessor.java @@ -0,0 +1,60 @@ +package com.jme3x.jfx.injfx.processor; + +import com.jme3.post.SceneProcessor; +import com.jme3.texture.FrameBuffer; +import com.jme3x.jfx.injfx.JmeToJFXApplication; +import com.jme3x.jfx.injfx.transfer.FrameTransfer; +import com.jme3x.jfx.injfx.transfer.impl.ImageFrameTransfer; +import com.sun.istack.internal.NotNull; + +import javafx.scene.Node; +import javafx.scene.image.ImageView; + +/** + * The implementation of the {@link SceneProcessor} for transferring content between jME and ImageView. + */ +public class ImageViewFrameTransferSceneProcessor extends AbstractFrameTransferSceneProcessor { + + @Override + protected int getDestinationHeight() { + return (int) destination.getFitHeight(); + } + + @Override + protected int getDestinationWidth() { + return (int) destination.getFitWidth(); + } + + @Override + protected boolean isPreserveRatio() { + return destination.isPreserveRatio(); + } + + @Override + protected void bindDestination(@NotNull final JmeToJFXApplication application, @NotNull final ImageView destination, + @NotNull final Node inputNode) { + super.bindDestination(application, destination, inputNode); + destination.setScaleY(-1.0); + } + + @Override + protected void bindListeners() { + destination.fitWidthProperty().addListener(widthListener); + destination.fitHeightProperty().addListener(heightListener); + destination.preserveRatioProperty().addListener(rationListener); + super.bindListeners(); + } + + @Override + protected void unbindDestination() { + destination.fitWidthProperty().removeListener(widthListener); + destination.fitHeightProperty().removeListener(heightListener); + destination.preserveRatioProperty().removeListener(rationListener); + super.unbindDestination(); + } + + @Override + protected FrameTransfer createFrameTransfer(final int width, final int height, @NotNull final FrameBuffer frameBuffer) { + return new ImageFrameTransfer(destination, isMain() ? null : frameBuffer, width, height); + } +} \ No newline at end of file diff --git a/src/com/jme3x/jfx/injfx/transfer/FrameTransfer.java b/src/com/jme3x/jfx/injfx/transfer/FrameTransfer.java new file mode 100644 index 0000000..d2c055e --- /dev/null +++ b/src/com/jme3x/jfx/injfx/transfer/FrameTransfer.java @@ -0,0 +1,41 @@ +package com.jme3x.jfx.injfx.transfer; + +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.Renderer; +import com.sun.istack.internal.NotNull; + +/** + * The class for transferring content from a jME frame buffer to somewhere. + * + * @author JavaSaBr + */ +public interface FrameTransfer { + + /** + * Init this transfer for the render. + * + * @param renderer the render. + */ + default void initFor(@NotNull final Renderer renderer, final boolean main) { + } + + /** + * @return the width. + */ + int getWidth(); + + /** + * @return the height. + */ + int getHeight(); + + /** + * Copy the content from render to the frameByteBuffer and write this content to image view. + */ + void copyFrameBufferToImage(@NotNull final RenderManager renderManager); + + /** + * Dispose this transfer. + */ + void dispose(); +} diff --git a/src/com/jme3x/jfx/injfx/transfer/impl/AbstractFrameTransfer.java b/src/com/jme3x/jfx/injfx/transfer/impl/AbstractFrameTransfer.java new file mode 100644 index 0000000..cd847fb --- /dev/null +++ b/src/com/jme3x/jfx/injfx/transfer/impl/AbstractFrameTransfer.java @@ -0,0 +1,171 @@ +package com.jme3x.jfx.injfx.transfer.impl; + +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.Renderer; +import com.jme3.texture.FrameBuffer; +import com.jme3.texture.Image; +import com.jme3.util.BufferUtils; +import com.jme3x.jfx.injfx.transfer.FrameTransfer; +import com.jme3x.jfx.util.JFXPlatform; +import com.sun.istack.internal.NotNull; + +import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicInteger; + +import javafx.scene.image.PixelFormat; +import javafx.scene.image.PixelWriter; +import javafx.scene.image.WritablePixelFormat; + +/** + * The base implementation of a frame transfer. + * + * @author JavaSaBr + */ +public abstract class AbstractFrameTransfer implements FrameTransfer { + + protected static final int RUNNING_STATE = 1; + protected static final int WAITING_STATE = 2; + protected static final int DISPOSING_STATE = 3; + protected static final int DISPOSED_STATE = 4; + + protected final AtomicInteger frameState; + protected final AtomicInteger imageState; + + protected final FrameBuffer frameBuffer; + + protected final PixelWriter pixelWriter; + + protected final ByteBuffer frameByteBuffer; + protected final ByteBuffer byteBuffer; + protected final ByteBuffer imageByteBuffer; + + /** + * The width. + */ + private final int width; + + /** + * The height. + */ + private final int height; + + public AbstractFrameTransfer(@NotNull final T destination, final int width, final int height) { + this(destination, null, width, height); + } + + public AbstractFrameTransfer(@NotNull final T destination, @NotNull final FrameBuffer frameBuffer, final int width, final int height) { + this.frameState = new AtomicInteger(WAITING_STATE); + this.imageState = new AtomicInteger(WAITING_STATE); + this.width = frameBuffer != null ? frameBuffer.getWidth() : width; + this.height = frameBuffer != null ? frameBuffer.getHeight() : height; + + if (frameBuffer != null) { + this.frameBuffer = frameBuffer; + } else { + this.frameBuffer = new FrameBuffer(width, height, 1); + this.frameBuffer.setDepthBuffer(Image.Format.Depth); + this.frameBuffer.setColorBuffer(Image.Format.BGRA8); + } + + frameByteBuffer = BufferUtils.createByteBuffer(getWidth() * getHeight() * 4); + byteBuffer = BufferUtils.createByteBuffer(getWidth() * getHeight() * 4); + imageByteBuffer = BufferUtils.createByteBuffer(getWidth() * getHeight() * 4); + pixelWriter = getPixelWriter(destination, frameBuffer, width, height); + } + + @Override + public void initFor(@NotNull final Renderer renderer, final boolean main) { + if (main) renderer.setMainFrameBufferOverride(frameBuffer); + } + + protected PixelWriter getPixelWriter(@NotNull final T destination, @NotNull final FrameBuffer frameBuffer, final int width, final int height) { + throw new UnsupportedOperationException(); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public void copyFrameBufferToImage(final RenderManager renderManager) { + + while (!frameState.compareAndSet(WAITING_STATE, RUNNING_STATE)) { + if (frameState.get() == DISPOSED_STATE) { + return; + } + } + + // Convert screenshot. + try { + + frameByteBuffer.clear(); + + final Renderer renderer = renderManager.getRenderer(); + renderer.readFrameBufferWithFormat(frameBuffer, frameByteBuffer, Image.Format.BGRA8); + + } finally { + if (!frameState.compareAndSet(RUNNING_STATE, WAITING_STATE)) { + throw new RuntimeException("unknown problem with the frame state"); + } + } + + synchronized (byteBuffer) { + byteBuffer.clear(); + byteBuffer.put(frameByteBuffer); + byteBuffer.flip(); + } + + JFXPlatform.runInFXThread(this::writeFrame); + } + + /** + * Write content to image. + */ + protected void writeFrame() { + + while (!imageState.compareAndSet(WAITING_STATE, RUNNING_STATE)) { + if (imageState.get() == DISPOSED_STATE) return; + } + + try { + + imageByteBuffer.clear(); + + synchronized (byteBuffer) { + if (byteBuffer.position() == byteBuffer.limit()) return; + imageByteBuffer.put(byteBuffer); + imageByteBuffer.flip(); + } + + final WritablePixelFormat pixelFormat = PixelFormat.getByteBgraInstance(); + pixelWriter.setPixels(0, 0, width, height, pixelFormat, imageByteBuffer, width * 4); + + } finally { + if (!imageState.compareAndSet(RUNNING_STATE, WAITING_STATE)) { + throw new RuntimeException("unknown problem with the image state"); + } + } + } + + @Override + public void dispose() { + while (!frameState.compareAndSet(WAITING_STATE, DISPOSING_STATE)) ; + while (!imageState.compareAndSet(WAITING_STATE, DISPOSING_STATE)) ; + disposeImpl(); + frameState.compareAndSet(DISPOSING_STATE, DISPOSED_STATE); + imageState.compareAndSet(DISPOSING_STATE, DISPOSED_STATE); + } + + protected void disposeImpl() { + frameBuffer.dispose(); + BufferUtils.destroyDirectBuffer(frameByteBuffer); + BufferUtils.destroyDirectBuffer(byteBuffer); + BufferUtils.destroyDirectBuffer(imageByteBuffer); + } +} diff --git a/src/com/jme3x/jfx/injfx/transfer/impl/CanvasFrameTransfer.java b/src/com/jme3x/jfx/injfx/transfer/impl/CanvasFrameTransfer.java new file mode 100644 index 0000000..2ba6550 --- /dev/null +++ b/src/com/jme3x/jfx/injfx/transfer/impl/CanvasFrameTransfer.java @@ -0,0 +1,30 @@ +package com.jme3x.jfx.injfx.transfer.impl; + +import com.jme3.texture.FrameBuffer; +import com.sun.istack.internal.NotNull; + +import javafx.scene.canvas.Canvas; +import javafx.scene.image.PixelWriter; + +/** + * The class for transferring content from the jME to {@link Canvas}. + * + * @author JavaSaBr + */ +public class CanvasFrameTransfer extends AbstractFrameTransfer { + + public CanvasFrameTransfer(@NotNull final Canvas canvas, @NotNull int width, int height) { + this(canvas, null, width, height); + } + + public CanvasFrameTransfer(@NotNull final Canvas canvas, @NotNull final FrameBuffer frameBuffer, + final int width, final int height) { + super(canvas, frameBuffer, width, height); + } + + @Override + protected PixelWriter getPixelWriter(@NotNull final Canvas destination, @NotNull final FrameBuffer frameBuffer, + final int width, final int height) { + return destination.getGraphicsContext2D().getPixelWriter(); + } +} diff --git a/src/com/jme3x/jfx/injfx/transfer/impl/ImageFrameTransfer.java b/src/com/jme3x/jfx/injfx/transfer/impl/ImageFrameTransfer.java new file mode 100644 index 0000000..c869830 --- /dev/null +++ b/src/com/jme3x/jfx/injfx/transfer/impl/ImageFrameTransfer.java @@ -0,0 +1,34 @@ +package com.jme3x.jfx.injfx.transfer.impl; + +import com.jme3.texture.FrameBuffer; +import com.jme3x.jfx.util.JFXPlatform; +import com.sun.istack.internal.NotNull; + +import javafx.scene.image.ImageView; +import javafx.scene.image.PixelWriter; +import javafx.scene.image.WritableImage; + +/** + * The class for transferring a frame from jME to {@link ImageView}. + * + * @author JavaSaBr + */ +public class ImageFrameTransfer extends AbstractFrameTransfer { + + private WritableImage writableImage; + + public ImageFrameTransfer(@NotNull final ImageView imageView, @NotNull int width, int height) { + this(imageView, null, width, height); + } + + public ImageFrameTransfer(@NotNull final ImageView imageView, @NotNull final FrameBuffer frameBuffer, final int width, final int height) { + super(imageView, frameBuffer, width, height); + JFXPlatform.runInFXThread(() -> imageView.setImage(writableImage)); + } + + @Override + protected PixelWriter getPixelWriter(@NotNull final ImageView destination, @NotNull final FrameBuffer frameBuffer, final int width, final int height) { + writableImage = new WritableImage(width, height); + return writableImage.getPixelWriter(); + } +} diff --git a/src/com/jme3x/jfx/util/JFXPlatform.java b/src/com/jme3x/jfx/util/JFXPlatform.java new file mode 100644 index 0000000..bc9ff0d --- /dev/null +++ b/src/com/jme3x/jfx/util/JFXPlatform.java @@ -0,0 +1,58 @@ +package com.jme3x.jfx.util; + +import com.sun.istack.internal.NotNull; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import javafx.application.Platform; +import rlib.util.array.ArrayFactory; + +/** + * The class with additional utility methods for JavaFX Platform. + * + * @author JavaSaBr. + */ +public class JFXPlatform { + + private static final Class PLATFORM_TYPE; + + private static final String METHOD_STARTUP = "startup"; + + /** + * The example of getting a method handle. + */ + @NotNull + private static final MethodHandle STARTUP_HANDLE; + + static { + + try { + + PLATFORM_TYPE = Class.forName("com.sun.javafx.application.PlatformImpl"); + + final Constructor lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class); + lookupConstructor.setAccessible(true); + + final MethodHandles.Lookup platformLookup = lookupConstructor.newInstance(PLATFORM_TYPE); + STARTUP_HANDLE = platformLookup.findStatic(PLATFORM_TYPE, METHOD_STARTUP, MethodType.methodType(void.class, ArrayFactory.toArray(Runnable.class))); + + } catch (final ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + /** + * Execute the task in JavaFX thread. + */ + public static void runInFXThread(@NotNull final Runnable task) { + if (Platform.isFxApplicationThread()) { + task.run(); + } else { + Platform.runLater(task); + } + } +} diff --git a/src/main/java/com/jme3x/jfx/util/os/OperatingSystem.java b/src/com/jme3x/jfx/util/os/OperatingSystem.java old mode 100755 new mode 100644 similarity index 99% rename from src/main/java/com/jme3x/jfx/util/os/OperatingSystem.java rename to src/com/jme3x/jfx/util/os/OperatingSystem.java index f8948bc..abee3be --- a/src/main/java/com/jme3x/jfx/util/os/OperatingSystem.java +++ b/src/com/jme3x/jfx/util/os/OperatingSystem.java @@ -1,7 +1,7 @@ package com.jme3x.jfx.util.os; /** - * @author Ronn + * @author JavaSaBr */ public class OperatingSystem { diff --git a/src/main/java/com/jme3x/jfx/util/os/OperatingSystemResolver.java b/src/com/jme3x/jfx/util/os/OperatingSystemResolver.java old mode 100755 new mode 100644 similarity index 85% rename from src/main/java/com/jme3x/jfx/util/os/OperatingSystemResolver.java rename to src/com/jme3x/jfx/util/os/OperatingSystemResolver.java index 3be6a2b..0a06170 --- a/src/main/java/com/jme3x/jfx/util/os/OperatingSystemResolver.java +++ b/src/com/jme3x/jfx/util/os/OperatingSystemResolver.java @@ -1,8 +1,12 @@ package com.jme3x.jfx.util.os; +import static java.lang.Double.parseDouble; +import static java.lang.Integer.parseInt; + +import com.sun.istack.internal.NotNull; + import java.io.File; import java.io.FileNotFoundException; -import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -10,13 +14,12 @@ import java.util.Map; import java.util.Scanner; -import static java.lang.Double.parseDouble; -import static java.lang.Integer.parseInt; +import javax.annotation.Nullable; /** * Name resolver OS distribution. * - * @author Ronn + * @author JavaSaBr */ public class OperatingSystemResolver { @@ -34,10 +37,10 @@ public class OperatingSystemResolver { private static final String VERSION = System.getProperty("os.version"); private static final String ARCH = System.getProperty("os.arch"); - private static final Map MAC_OS_VERSION_MAPPING = new HashMap(); - private static final Map DARWIN_VERSION_MAPPING = new HashMap(); + private static final Map MAC_OS_VERSION_MAPPING = new HashMap<>(); + private static final Map DARWIN_VERSION_MAPPING = new HashMap<>(); - private static final List LINUX_VERSION_NAMES = new ArrayList(); + private static final List LINUX_VERSION_NAMES = new ArrayList<>(); static { MAC_OS_VERSION_MAPPING.put(10.0, "Puma"); @@ -66,18 +69,13 @@ public class OperatingSystemResolver { LINUX_VERSION_NAMES.addAll(Arrays.asList("Linux", "SunOS")); } - private String findFile(final File dir, final String postfix) { - - final File[] files = dir.listFiles((FilenameFilter) (directory, filename) -> filename.endsWith(postfix)); - - if (files.length > 0) { - return files[0].getAbsolutePath(); - } - + private String findFile(@NotNull final File dir, @NotNull final String postfix) { + final File[] files = dir.listFiles((directory, filename) -> filename.endsWith(postfix)); + if (files.length > 0) return files[0].getAbsolutePath(); return null; } - protected void resolve(final OperatingSystem system) { + protected void resolve(@NotNull final OperatingSystem system) { system.setName(NAME); system.setArch(ARCH); @@ -103,28 +101,20 @@ else if (NAME.startsWith("Mac")) { } } - private void resolveDarwinOs(final OperatingSystem system) { - + private void resolveDarwinOs(@NotNull final OperatingSystem system) { final String[] versions = VERSION.split("\\."); - system.setDistribution("OS X " + DARWIN_VERSION_MAPPING.get(parseInt(versions[0])) + " (" + VERSION + ")"); } - private void resolveLinuxOs(final OperatingSystem system) { + private void resolveLinuxOs(@NotNull final OperatingSystem system) { // The most likely is to have a LSB compliant distro resolveNameFromLsbRelease(system); - - if (system.getDistribution() != null) { - return; - } + if (system.getDistribution() != null) return; // Generic Linux platform name resolveNameFromFile(system, FILE_ETC_SYSTEM_RELEASE); - - if (system.getDistribution() != null) { - return; - } + if (system.getDistribution() != null) return; final File dir = new File(FILE_ETC); @@ -166,7 +156,7 @@ private void resolveLinuxOs(final OperatingSystem system) { } } - private void resolveMacOs(final OperatingSystem system) { + private void resolveMacOs(@NotNull final OperatingSystem system) { final String[] versions = VERSION.split("\\."); @@ -179,17 +169,11 @@ private void resolveMacOs(final OperatingSystem system) { } } - private void resolveNameFromFile(final OperatingSystem system, final String filename) { - - if (filename == null) { - return; - } + private void resolveNameFromFile(@NotNull final OperatingSystem system, @Nullable final String filename) { + if (filename == null) return; final File file = new File(filename); - - if (!file.exists()) { - return; - } + if (!file.exists()) return; String lastLine = null; @@ -220,13 +204,10 @@ private void resolveNameFromFile(final OperatingSystem system, final String file } } - private void resolveNameFromLsbRelease(final OperatingSystem system) { + private void resolveNameFromLsbRelease(@NotNull final OperatingSystem system) { final File file = new File(FILE_ETC_LSB_RELEASE); - - if (!file.exists()) { - return; - } + if (!file.exists()) return; String description = null; String codename = null; diff --git a/src/main/java/com/jme3x/jfx/FxPlatformExecutor.java b/src/main/java/com/jme3x/jfx/FxPlatformExecutor.java deleted file mode 100755 index a69b123..0000000 --- a/src/main/java/com/jme3x/jfx/FxPlatformExecutor.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jme3x.jfx; - -import javafx.application.Platform; - -/** - * TODO This Class should be replaced by some Workmanager implemntation in the future - * - * @author Heist - */ -public class FxPlatformExecutor { - - public static void runOnFxApplication(Runnable task) { - if (Platform.isFxApplicationThread()) { - task.run(); - } else { - Platform.runLater(task); - } - } -} diff --git a/src/main/java/com/jme3x/jfx/JavaFXPicture.java b/src/main/java/com/jme3x/jfx/JavaFXPicture.java deleted file mode 100755 index 374fb1f..0000000 --- a/src/main/java/com/jme3x/jfx/JavaFXPicture.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.jme3x.jfx; - -import com.jme3.system.JmeContext; -import com.jme3.ui.Picture; -import com.jme3x.jfx.util.JFXUtils; -import com.sun.javafx.embed.EmbeddedStageInterface; - -import javafx.application.Platform; -import rlib.logging.Logger; -import rlib.logging.LoggerManager; - -/** - * Реализация картинки с UI для JME. - * - * @author Ronn - */ -public class JavaFXPicture extends Picture { - - private static final Logger LOGGER = LoggerManager.getLogger(JavaFXPicture.class); - - /** - * Контейнер UI Java FX. - */ - private final JmeFxContainer container; - - public JavaFXPicture(JmeFxContainer container) { - super("JavaFXContainer", true); - this.container = container; - } - - /** - * @return контейнер UI Java FX. - */ - private JmeFxContainer getContainer() { - return container; - } - - @Override - public void updateLogicalState(float tpf) { - - final JmeFxContainer container = getContainer(); - final JmeContext jmeContext = container.getJmeContext(); - - final EmbeddedStageInterface currentStage = container.getStagePeer(); - - try { - - if (currentStage == null) { - return; - } - - final int currentWidth = JFXUtils.getWidth(jmeContext); - final int currentHeight = JFXUtils.getHeight(jmeContext); - - if (currentWidth != container.getPictureWidth() || currentHeight != container.getPictureHeight()) { - container.handleResize(); - } - - final int originalX = JFXUtils.getX(jmeContext); - final int originalY = JFXUtils.getY(jmeContext); - - final int offsetX = JFXUtils.isFullscreen(jmeContext) ? 0 : container.getWindowOffsetX(); - final int offsetY = JFXUtils.isFullscreen(jmeContext) ? 0 : container.getWindowOffsetY(); - - final int x = originalX + offsetX; - final int y = originalY + offsetY; - - if (container.getOldX() != x || container.getOldY() != y) { - - if(JmeFxContainer.isDebug()) { - LOGGER.debug("moved window to [original: " + originalX + ", " + originalY + " offset:" + offsetX + ", " + offsetY +"]"); - } - - container.setOldX(x); - container.setOldY(y); - - Platform.runLater(() -> currentStage.setLocation(x, y)); - } - - } finally { - super.updateLogicalState(tpf); - } - } -} diff --git a/src/main/java/com/jme3x/jfx/JmeFXHostInterfaceImpl.java b/src/main/java/com/jme3x/jfx/JmeFXHostInterfaceImpl.java deleted file mode 100755 index 88b735e..0000000 --- a/src/main/java/com/jme3x/jfx/JmeFXHostInterfaceImpl.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package com.jme3x.jfx; - -import com.jme3x.jfx.cursor.CursorDisplayProvider; -import com.sun.javafx.cursor.CursorFrame; -import com.sun.javafx.embed.AbstractEvents; -import com.sun.javafx.embed.EmbeddedSceneInterface; -import com.sun.javafx.embed.EmbeddedStageInterface; -import com.sun.javafx.embed.HostInterface; - -import rlib.logging.Logger; -import rlib.logging.LoggerManager; - -/** - * Fakes a top level window - */ -public class JmeFXHostInterfaceImpl implements HostInterface { - - private static final Logger LOGGER = LoggerManager.getLogger(JmeFXHostInterfaceImpl.class); - - /** - * контейнер JavaFX UI - */ - private final JmeFxContainer jmeFxContainer; - - public JmeFXHostInterfaceImpl(final JmeFxContainer jmeFxContainer) { - this.jmeFxContainer = jmeFxContainer; - } - - /** - * @return контейнер JavaFX UI. - */ - private JmeFxContainer getJmeFxContainer() { - return jmeFxContainer; - } - - @Override - public boolean grabFocus() { - return true; - } - - @Override - public void repaint() { - final JmeFxContainer jmeFxContainer = getJmeFxContainer(); - jmeFxContainer.paintComponent(); - } - - @Override - public boolean requestFocus() { - return true; - } - - @Override - public void setCursor(final CursorFrame cursorFrame) { - - final JmeFxContainer jmeFxContainer = getJmeFxContainer(); - final CursorDisplayProvider cursorDisplayProvider = jmeFxContainer.getCursorDisplayProvider(); - - if (cursorDisplayProvider != null) { - cursorDisplayProvider.showCursor(cursorFrame); - } - } - - @Override - public void setEmbeddedScene(final EmbeddedSceneInterface embeddedScene) { - - final JmeFxContainer jmeFxContainer = getJmeFxContainer(); - jmeFxContainer.setScenePeer(embeddedScene); - - if (embeddedScene == null) { - return; - } - - embeddedScene.setPixelScaleFactor(1); - - final int width = jmeFxContainer.getPictureWidth(); - final int height = jmeFxContainer.getPictureHeight(); - - if (width > 0 && height > 0) { - embeddedScene.setSize(jmeFxContainer.getPictureWidth(), jmeFxContainer.getPictureHeight()); - } - - embeddedScene.setDragStartListener(new JmeFxDNDHandler(jmeFxContainer)); - } - - @Override - public void setEmbeddedStage(final EmbeddedStageInterface embeddedStage) { - - final JmeFxContainer jmeFxContainer = getJmeFxContainer(); - jmeFxContainer.setStagePeer(embeddedStage); - - if (embeddedStage == null) { - return; - } - - final int width = jmeFxContainer.getPictureWidth(); - final int height = jmeFxContainer.getPictureHeight(); - - if (width > 0 && height > 0) { - embeddedStage.setSize(jmeFxContainer.getPictureWidth(), jmeFxContainer.getPictureHeight()); - } - - embeddedStage.setFocused(true, AbstractEvents.FOCUSEVENT_ACTIVATED); - } - - @Override - public void setEnabled(final boolean enabled) { - final JmeFxContainer jmeFxContainer = getJmeFxContainer(); - jmeFxContainer.setEnabled(enabled); - } - - @Override - public void setPreferredSize(final int width, final int height) { - } - - @Override - public boolean traverseFocusOut(final boolean forward) { - - if (JmeFxContainer.isDebug()) { - LOGGER.debug("Called traverseFocusOut(" + forward + ")"); - } - - return true; - } - - @Override - public void ungrabFocus() { - } -} diff --git a/src/main/java/com/jme3x/jfx/JmeFXInputListener.java b/src/main/java/com/jme3x/jfx/JmeFXInputListener.java deleted file mode 100755 index 5716df5..0000000 --- a/src/main/java/com/jme3x/jfx/JmeFXInputListener.java +++ /dev/null @@ -1,481 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package com.jme3x.jfx; - -import com.jme3.app.Application; -import com.jme3.input.InputManager; -import com.jme3.input.KeyInput; -import com.jme3.input.RawInputListener; -import com.jme3.input.awt.AwtKeyInput; -import com.jme3.input.event.JoyAxisEvent; -import com.jme3.input.event.JoyButtonEvent; -import com.jme3.input.event.KeyInputEvent; -import com.jme3.input.event.MouseButtonEvent; -import com.jme3.input.event.MouseMotionEvent; -import com.jme3.input.event.TouchEvent; -import com.sun.javafx.embed.AbstractEvents; -import com.sun.javafx.embed.EmbeddedSceneInterface; - -import java.awt.event.KeyEvent; -import java.util.BitSet; - -import javafx.application.Platform; -import javafx.scene.Scene; - -/** - * Converts JMEEvents to JFXEvents - * - * @author Heist - */ -public class JmeFXInputListener implements RawInputListener { - - /** - * Контейнер Java FX. - */ - private final JmeFxContainer jmeFxContainer; - - private final BitSet keyStateSet = new BitSet(0xFF); - - /** - * Набор массивов для каждого символа. - */ - private final char[][] keyCharArray = new char[Character.MAX_CODE_POINT][]; - - /** - * Таблица символов. - */ - private final char[] keyCharSet = new char[Character.MAX_CODE_POINT]; - - /** - * Состояние кнопок мыши. - */ - private final boolean[] mouseButtonState = new boolean[3]; - - /** - * Слушатель ввода пользователя. - */ - private volatile RawInputListener everListeningInputListenerAdapter; - - /** - * Обработчик DnD Java FX. - */ - private volatile JmeFxDNDHandler jfxdndHandler; - - public JmeFXInputListener(final JmeFxContainer listensOnContainer) { - this.jmeFxContainer = listensOnContainer; - - for (int i = 0, length = keyCharArray.length; i < length; i++) { - keyCharArray[i] = new char[]{(char) i}; - } - } - - /** - * @return обработчик DnD Java FX. - */ - private JmeFxDNDHandler getJfxdndHandler() { - return jfxdndHandler; - } - - @Override - public void beginInput() { - - final RawInputListener adapter = getEverListeningInputListenerAdapter(); - - if (adapter != null) { - adapter.beginInput(); - } - } - - @Override - public void endInput() { - - final RawInputListener adapter = getEverListeningInputListenerAdapter(); - - if (adapter != null) { - adapter.endInput(); - } - } - - /** - * @return слушатель ввода пользователя. - */ - private RawInputListener getEverListeningInputListenerAdapter() { - return everListeningInputListenerAdapter; - } - - /** - * @return контейнер Java FX. - */ - private JmeFxContainer getJmeFxContainer() { - return jmeFxContainer; - } - - /** - * @return набор массивов для каждого символа. - */ - private char[][] getKeyCharArray() { - return keyCharArray; - } - - /** - * @return таблица символов. - */ - private char[] getKeyCharSet() { - return keyCharSet; - } - - /** - * @return - */ - private BitSet getKeyStateSet() { - return keyStateSet; - } - - /** - * @return состояние кнопок мыши. - */ - private boolean[] getMouseButtonState() { - return mouseButtonState; - } - - @Override - public void onJoyAxisEvent(final JoyAxisEvent event) { - - final RawInputListener adapter = getEverListeningInputListenerAdapter(); - - if (adapter != null) { - adapter.onJoyAxisEvent(event); - } - } - - @Override - public void onJoyButtonEvent(final JoyButtonEvent event) { - - final RawInputListener adapter = getEverListeningInputListenerAdapter(); - - if (adapter != null) { - adapter.onJoyButtonEvent(event); - } - } - - @Override - public void onKeyEvent(final KeyInputEvent event) { - - final RawInputListener adapter = getEverListeningInputListenerAdapter(); - - if (adapter != null) { - adapter.onKeyEvent(event); - } - - final JmeFxContainer jmeFxContainer = getJmeFxContainer(); - final EmbeddedSceneInterface scenePeer = jmeFxContainer.getScenePeer(); - - if (scenePeer == null) { - return; - } - - final BitSet keyStateSet = getKeyStateSet(); - - final char[][] keyCharArray = getKeyCharArray(); - final char[] keyCharSet = getKeyCharSet(); - final char keyChar = event.getKeyChar(); - - final int keyCode = event.getKeyCode(); - - int fxKeyCode = keyCode == KeyInput.KEY_UNKNOWN ? KeyEvent.VK_UNDEFINED : AwtKeyInput.convertJmeCode(keyCode); - - final int keyState = retrieveKeyState(); - - if (fxKeyCode > keyCharSet.length) { - switch (keyChar) { - case '\\': { - fxKeyCode = java.awt.event.KeyEvent.VK_BACK_SLASH; - break; - } - default: { - return; - } - } - } - - if (jmeFxContainer.isFocus()) { - event.setConsumed(); - } - - if (event.isRepeating()) { - - final char x = keyCharSet[fxKeyCode]; - - if (jmeFxContainer.isFocus()) { - scenePeer.keyEvent(AbstractEvents.KEYEVENT_TYPED, fxKeyCode, keyCharArray[x], keyState); - } - - } else if (event.isPressed()) { - - keyCharSet[fxKeyCode] = keyChar; - keyStateSet.set(fxKeyCode); - - if (jmeFxContainer.isFocus()) { - scenePeer.keyEvent(AbstractEvents.KEYEVENT_PRESSED, fxKeyCode, keyCharArray[keyChar], keyState); - scenePeer.keyEvent(AbstractEvents.KEYEVENT_TYPED, fxKeyCode, keyCharArray[keyChar], keyState); - } - - } else { - - final char x = keyCharSet[fxKeyCode]; - - keyStateSet.clear(fxKeyCode); - - if (jmeFxContainer.isFocus()) { - scenePeer.keyEvent(AbstractEvents.KEYEVENT_RELEASED, fxKeyCode, keyCharArray[x], keyState); - } - } - } - - @Override - public void onMouseButtonEvent(final MouseButtonEvent event) { - - final RawInputListener adapter = getEverListeningInputListenerAdapter(); - - if (adapter != null) { - adapter.onMouseButtonEvent(event); - } - - final JmeFxContainer jmeFxContainer = getJmeFxContainer(); - final Application application = jmeFxContainer.getApplication(); - final InputManager inputManager = application.getInputManager(); - - if (jmeFxContainer.getScenePeer() == null) { - return; - } - - final Scene scene = jmeFxContainer.getScene(); - - final int x = event.getX(); - final int y = (int) Math.round(scene.getHeight()) - event.getY(); - - int button; - - switch (event.getButtonIndex()) { - case 0: { - button = AbstractEvents.MOUSEEVENT_PRIMARY_BUTTON; - break; - } - case 1: { - button = AbstractEvents.MOUSEEVENT_SECONDARY_BUTTON; - break; - } - case 2: { - button = AbstractEvents.MOUSEEVENT_MIDDLE_BUTTON; - break; - } - default: { - return; - } - } - - mouseButtonState[event.getButtonIndex()] = event.isPressed(); - - // seems that generating mouse release without corresponding mouse - // pressed is causing problems in Scene.ClickGenerator - - final boolean covered = jmeFxContainer.isCovered(x, y); - - if (!covered) { - jmeFxContainer.loseFocus(); - } else if (inputManager.isCursorVisible()) { - event.setConsumed(); - jmeFxContainer.grabFocus(); - } - - int type; - - if (event.isPressed()) { - type = AbstractEvents.MOUSEEVENT_PRESSED; - } else if (event.isReleased()) { - type = AbstractEvents.MOUSEEVENT_RELEASED; - // and clicked ?? - } else { - return; - } - - if (inputManager.isCursorVisible() || event.isReleased()) { - Platform.runLater(() -> onMouseButtonEventImpl(x, y, button, type)); - } - } - - private void onMouseButtonEventImpl(int x, int y, int button, int type) { - - final boolean[] mouseButtonState = getMouseButtonState(); - final JmeFxDNDHandler jfxdndHandler = getJfxdndHandler(); - - final boolean primaryBtnDown = mouseButtonState[0]; - final boolean middleBtnDown = mouseButtonState[1]; - final boolean secondaryBtnDown = mouseButtonState[2]; - - if (jfxdndHandler != null) { - jfxdndHandler.mouseUpdate(x, y, primaryBtnDown); - } - - final JmeFxContainer fxContainer = getJmeFxContainer(); - final EmbeddedSceneInterface scenePeer = fxContainer.getScenePeer(); - - final int screenX = fxContainer.getOldX() + x; - final int screenY = fxContainer.getOldY() + y; - - final BitSet keyStateSet = getKeyStateSet(); - - final boolean shift = keyStateSet.get(KeyEvent.VK_SHIFT); - final boolean ctrl = keyStateSet.get(KeyEvent.VK_CONTROL); - final boolean alt = keyStateSet.get(KeyEvent.VK_ALT); - final boolean meta = keyStateSet.get(KeyEvent.VK_META); - final boolean popupTrigger = button == AbstractEvents.MOUSEEVENT_SECONDARY_BUTTON; - - scenePeer.mouseEvent(type, button, primaryBtnDown, middleBtnDown, secondaryBtnDown, x, y, screenX, screenY, shift, ctrl, alt, meta, 0, popupTrigger); - } - - @Override - public void onMouseMotionEvent(final MouseMotionEvent event) { - - final RawInputListener adapter = getEverListeningInputListenerAdapter(); - - if (adapter != null) { - adapter.onMouseMotionEvent(event); - } - - final JmeFxContainer jmeFxContainer = getJmeFxContainer(); - final Application application = jmeFxContainer.getApplication(); - final InputManager inputManager = application.getInputManager(); - - if (jmeFxContainer.getScenePeer() == null) { - return; - } - - final Scene scene = jmeFxContainer.getScene(); - - final int x = event.getX(); - final int y = (int) Math.round(scene.getHeight()) - event.getY(); - - final boolean covered = jmeFxContainer.isCovered(x, y); - - if (covered) { - event.setConsumed(); - } - - final boolean[] mouseButtonState = getMouseButtonState(); - // not sure if should be grabbing focus on mouse motion event - // grabFocus(); - - int type = AbstractEvents.MOUSEEVENT_MOVED; - int button = AbstractEvents.MOUSEEVENT_NONE_BUTTON; - - final int wheelRotation = (int) Math.round(event.getDeltaWheel() / -120.0); - - if (wheelRotation != 0) { - type = AbstractEvents.MOUSEEVENT_WHEEL; - button = AbstractEvents.MOUSEEVENT_NONE_BUTTON; - } else if (mouseButtonState[0]) { - type = AbstractEvents.MOUSEEVENT_DRAGGED; - button = AbstractEvents.MOUSEEVENT_PRIMARY_BUTTON; - } else if (mouseButtonState[1]) { - type = AbstractEvents.MOUSEEVENT_DRAGGED; - button = AbstractEvents.MOUSEEVENT_SECONDARY_BUTTON; - } else if (mouseButtonState[2]) { - type = AbstractEvents.MOUSEEVENT_DRAGGED; - button = AbstractEvents.MOUSEEVENT_MIDDLE_BUTTON; - } - - final int ftype = type; - final int fbutton = button; - - if (inputManager.isCursorVisible()) { - Platform.runLater(() -> onMouseMotionEventImpl(x, y, wheelRotation, ftype, fbutton)); - } - } - - private void onMouseMotionEventImpl(int x, int y, int wheelRotation, int ftype, int fbutton) { - - final JmeFxContainer fxContainer = getJmeFxContainer(); - final Application application = fxContainer.getApplication(); - final InputManager inputManager = application.getInputManager(); - - if (!inputManager.isCursorVisible()) { - return; - } - - final JmeFxDNDHandler dndHandler = getJfxdndHandler(); - final boolean[] mouseButtonState = getMouseButtonState(); - - final boolean primaryBtnDown = mouseButtonState[0]; - final boolean middleBtnDown = mouseButtonState[1]; - final boolean secondaryBtnDown = mouseButtonState[2]; - - if (dndHandler != null) { - dndHandler.mouseUpdate(x, y, primaryBtnDown); - } - - final EmbeddedSceneInterface scenePeer = fxContainer.getScenePeer(); - - final int screenX = fxContainer.getOldX() + x; - final int screenY = fxContainer.getOldY() + y; - - final BitSet keyStateSet = getKeyStateSet(); - - final boolean shift = keyStateSet.get(KeyEvent.VK_SHIFT); - final boolean ctrl = keyStateSet.get(KeyEvent.VK_CONTROL); - final boolean alt = keyStateSet.get(KeyEvent.VK_ALT); - final boolean meta = keyStateSet.get(KeyEvent.VK_META); - - scenePeer.mouseEvent(ftype, fbutton, primaryBtnDown, middleBtnDown, secondaryBtnDown, x, y, screenX, screenY, shift, ctrl, alt, meta, wheelRotation, false); - } - - @Override - public void onTouchEvent(final TouchEvent event) { - - final RawInputListener adapter = getEverListeningInputListenerAdapter(); - - if (adapter != null) { - adapter.onTouchEvent(event); - } - } - - public int retrieveKeyState() { - - int embedModifiers = 0; - - final BitSet keyStateSet = getKeyStateSet(); - - if (keyStateSet.get(KeyEvent.VK_SHIFT)) { - embedModifiers |= AbstractEvents.MODIFIER_SHIFT; - } - - if (keyStateSet.get(KeyEvent.VK_CONTROL)) { - embedModifiers |= AbstractEvents.MODIFIER_CONTROL; - } - - if (keyStateSet.get(KeyEvent.VK_ALT)) { - embedModifiers |= AbstractEvents.MODIFIER_ALT; - } - - if (keyStateSet.get(KeyEvent.VK_META)) { - embedModifiers |= AbstractEvents.MODIFIER_META; - } - - return embedModifiers; - } - - public void setEverListeningRawInputListener(final RawInputListener rawInputListenerAdapter) { - this.everListeningInputListenerAdapter = rawInputListenerAdapter; - } - - /** - * set on drag start /nulled on end
necessary so that the drag events can be generated - * appropiatly - */ - public void setMouseDNDListener(final JmeFxDNDHandler jfxdndHandler) { - assert this.jfxdndHandler == null || jfxdndHandler == null : "duplicate jfxdndn handler register? "; - this.jfxdndHandler = jfxdndHandler; - } -} diff --git a/src/main/java/com/jme3x/jfx/JmeFxContainer.java b/src/main/java/com/jme3x/jfx/JmeFxContainer.java deleted file mode 100755 index 67b896f..0000000 --- a/src/main/java/com/jme3x/jfx/JmeFxContainer.java +++ /dev/null @@ -1,1066 +0,0 @@ -package com.jme3x.jfx; - -import com.jme3.app.Application; -import com.jme3.app.state.AbstractAppState; -import com.jme3.app.state.AppState; -import com.jme3.app.state.AppStateManager; -import com.jme3.asset.AssetManager; -import com.jme3.input.InputManager; -import com.jme3.input.RawInputListener; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial.CullHint; -import com.jme3.system.JmeContext; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture2D; -import com.jme3.texture.image.ColorSpace; -import com.jme3.ui.Picture; -import com.jme3.util.BufferUtils; -import com.jme3x.jfx.cursor.CursorDisplayProvider; -import com.jme3x.jfx.listener.PaintListener; -import com.jme3x.jfx.util.JFXUtils; -import com.sun.glass.ui.Pixels; -import com.sun.javafx.application.PlatformImpl; -import com.sun.javafx.embed.AbstractEvents; -import com.sun.javafx.embed.EmbeddedSceneInterface; -import com.sun.javafx.embed.EmbeddedStageInterface; -import com.sun.javafx.embed.HostInterface; -import com.sun.javafx.stage.EmbeddedWindow; - -import java.awt.*; -import java.awt.event.KeyEvent; -import java.nio.ByteBuffer; -import java.nio.IntBuffer; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.function.Function; - -import javafx.application.Platform; -import javafx.scene.Group; -import javafx.scene.Scene; -import rlib.concurrent.atomic.AtomicInteger; -import rlib.concurrent.lock.AsyncReadSyncWriteLock; -import rlib.concurrent.lock.LockFactory; -import rlib.logging.Logger; -import rlib.logging.LoggerManager; - -/** - * Need to pass -Dprism.dirtyopts=false on startup - * - * @author abies / Artur Biesiadowski - */ -public class JmeFxContainer { - - private static final Logger LOGGER = LoggerManager.getLogger(JmeFxContainer.class); - - /** - * Актитвировал ли дебаг. - */ - private static boolean debug; - - /** - * @param debug актитвировал ли дебаг. - */ - public static void setDebug(boolean debug) { - JmeFxContainer.debug = debug; - } - - /** - * @return актитвировал ли дебаг. - */ - public static boolean isDebug() { - return debug; - } - - public static JmeFxContainer install(final Application app, final Node guiNode, final CursorDisplayProvider cursorDisplayProvider) { - - final JmeFxContainer container = new JmeFxContainer(app.getAssetManager(), app, cursorDisplayProvider); - guiNode.attachChild(container.getJmeNode()); - - final JmeFXInputListener inputListener = new JmeFXInputListener(container); - - container.setInputListener(inputListener); - - final InputManager inputManager = app.getInputManager(); - inputManager.addRawInputListener(inputListener); - - return container; - } - - // TODO benchmark - private static Void reorder_ARGB82ABGR8(final ByteBuffer data) { - - final int limit = data.limit() - 3; - - byte v; - - for (int i = 0; i < limit; i += 4) { - v = data.get(i + 1); - data.put(i + 1, data.get(i + 3)); - data.put(i + 3, v); - } - - return null; - } - - // TODO benchmark - private static Void reorder_BGRA82ABGR8(final ByteBuffer data) { - - final int limit = data.limit() - 3; - - byte v0, v1, v2, v3; - - for (int i = 0; i < limit; i += 4) { - v0 = data.get(i); - v1 = data.get(i + 1); - v2 = data.get(i + 2); - v3 = data.get(i + 3); - data.put(i, v3); - data.put(i + 1, v0); - data.put(i + 2, v1); - data.put(i + 3, v2); - } - - return null; - } - - /** - * Игровая стадия FX UI. - */ - private final AppState fxAppState = new AbstractAppState() { - - @Override - public void cleanup() { - Platform.exit(); - super.cleanup(); - } - }; - - protected volatile CompletableFuture nativeFormat = new CompletableFuture<>(); - - /** - * Кол-во незаписанных в JME кадров. - */ - protected final AtomicInteger waitCount; - - /** - * Блокировщик доступа к данным изображений. - */ - protected final AsyncReadSyncWriteLock imageLock; - - /** - * Изображение для отрисовки UI. - */ - protected final Picture picture; - - /** - * Текстура на которой отрисовано UI. - */ - protected final Texture2D texture; - - /** - * Набор слушателей отрисовки. - */ - protected volatile PaintListener[] paintListeners; - - /** - * Текущая стадия UI. - */ - protected volatile EmbeddedStageInterface stagePeer; - - /** - * Текущая сцена UI. - */ - protected volatile EmbeddedSceneInterface scenePeer; - - /** - * Встроенное окно JavaFX UI. - */ - protected volatile EmbeddedWindow stage; - protected volatile HostInterface hostContainer; - - /** - * Слушатель ввода пользователя. - */ - protected volatile JmeFXInputListener inputListener; - - /** - * Текущая сцена UI. - */ - protected volatile Scene scene; - - /** - * Приложение JME. - */ - protected volatile Application application; - - /** - * Рутовый узел текущей сцены. - */ - protected volatile Group rootNode; - - /** - * Отрисованное изображение UI. - */ - protected volatile Image jmeImage; - - /** - * Данные кадра отрисованного в jME. - */ - protected volatile ByteBuffer jmeData; - - /** - * Данные кадра отрисованного в JavaFX. - */ - protected volatile ByteBuffer fxData; - - /** - * Временные данные кадра отрисованного в JavaFX. - */ - protected volatile ByteBuffer tempData; - - /** - * Провайдер по отображению нужных курсоров. - */ - protected volatile CursorDisplayProvider cursorDisplayProvider; - - /** - * Функция реординга данных. - */ - protected volatile Function reorderData; - - /** - * Время последнего изменения размера. - */ - protected volatile long lastResized; - - /** - * Ширина картики для отрисовки UI. - */ - protected volatile int pictureWidth; - - /** - * Высота картики для отрисовки UI. - */ - protected volatile int pictureHeight; - - /** - * Предыдущее положение экрана по X. - */ - protected volatile int oldX = -1; - - /** - * Предыдущее положение экрана по Y. - */ - protected volatile int oldY = -1; - - /** - * Indent the window position to account for window decoration by Ronn - */ - private volatile int windowOffsetX; - private volatile int windowOffsetY; - - /** - * Есть ли сейчас фокус на FX UI. - */ - protected volatile boolean focus; - - /** - * Поддержка полноэкранного режима. - */ - protected volatile boolean fullScreenSupport; - - /** - * Отображается ли курсор. - */ - protected volatile boolean visibleCursor; - - /** - * Доступен ли сейчас JavaFX. - */ - protected volatile boolean enabled; - - /** - * Набор состояний клавиш. - */ - private final BitSet keyStateSet = new BitSet(0xFF); - - /** - * Контекст JME. - */ - private final JmeContext jmeContext; - - protected JmeFxContainer(final AssetManager assetManager, final Application application, final CursorDisplayProvider cursorDisplayProvider) { - this.initFx(); - - this.jmeContext = application.getContext(); - - final Point decorationSize = JFXUtils.getWindowDecorationSize(); - - this.waitCount = new AtomicInteger(); - this.imageLock = LockFactory.newPrimitiveAtomicARSWLock(); - this.paintListeners = new PaintListener[0]; - this.windowOffsetX = (int) decorationSize.getX(); - this.windowOffsetY = (int) decorationSize.getY(); - this.cursorDisplayProvider = cursorDisplayProvider; - this.application = application; - this.visibleCursor = true; - - final AppStateManager stateManager = application.getStateManager(); - stateManager.attach(fxAppState); - - this.hostContainer = new JmeFXHostInterfaceImpl(this); - this.picture = new JavaFXPicture(this); - this.picture.move(0, 0, -1); - this.picture.setPosition(0, 0); - - handleResize(); - - this.texture = new Texture2D(jmeImage); - this.picture.setTexture(assetManager, texture, true); - } - - /** - * @param lastResized время последнего изменения размера. - */ - private void setLastResized(final long lastResized) { - this.lastResized = lastResized; - } - - /** - * @return время последнего изменения размера. - */ - private long getLastResized() { - return lastResized; - } - - /** - * @return приложение JME. - */ - public Application getApplication() { - return application; - } - - /** - * @return контекст JME. - */ - public JmeContext getJmeContext() { - return jmeContext; - } - - /** - * Добавление нового слушателя. - */ - public void addPaintListener(final PaintListener paintListener) { - - final List temp = new ArrayList<>(); - Collections.addAll(temp, getPaintListeners()); - temp.add(paintListener); - - setPaintListeners(temp.toArray(new PaintListener[temp.size()])); - } - - /** - * Создание задачи по записи FX UI на JME. - */ - protected void addWriteTask() { - application.enqueue(this::writeToJME); - } - - /** - * @return провайдер по отображению нужных курсоров. - */ - public CursorDisplayProvider getCursorDisplayProvider() { - return cursorDisplayProvider; - } - - /** - * @return данные кадра отрисованного в JavaFX. - */ - public ByteBuffer getFxData() { - return fxData; - } - - /** - * @return блокировщик доступа к данным изображений. - */ - private AsyncReadSyncWriteLock getImageLock() { - return imageLock; - } - - /** - * @return слушатель ввода пользователя. - */ - public JmeFXInputListener getInputListener() { - return inputListener; - } - - /** - * @param inputListener слушатель ввода пользователя. - */ - public void setInputListener(final JmeFXInputListener inputListener) { - this.inputListener = inputListener; - } - - /** - * @return данные кадра отрисованного в jME. - */ - public ByteBuffer getJmeData() { - return jmeData; - } - - /** - * @return отрисованное изображение UI. - */ - public Image getJmeImage() { - return jmeImage; - } - - /** - * @return изображение для отрисовки UI. - */ - public Picture getJmeNode() { - return picture; - } - - /** - * @return набор состояний клавиш. - */ - public BitSet getKeyStateSet() { - return keyStateSet; - } - - /** - * @return предыдущее положение экрана по X. - */ - public int getOldX() { - return oldX; - } - - /** - * @param oldX предыдущее положение экрана по X. - */ - public void setOldX(final int oldX) { - this.oldX = oldX; - } - - /** - * @return предыдущее положение экрана по Y. - */ - public int getOldY() { - return oldY; - } - - /** - * @param oldY предыдущее положение экрана по Y. - */ - public void setOldY(final int oldY) { - this.oldY = oldY; - } - - /** - * @return набор слушателей отрисовки. - */ - private PaintListener[] getPaintListeners() { - return paintListeners; - } - - /** - * @param paintListeners набор слушателей отрисовки. - */ - private void setPaintListeners(final PaintListener[] paintListeners) { - this.paintListeners = paintListeners; - } - - /** - * @return изображение для отрисовки UI. - */ - public Picture getPicture() { - return picture; - } - - /** - * @return высота картики для отрисовки UI. - */ - public int getPictureHeight() { - return pictureHeight; - } - - /** - * @param pictureHeight высота картики для отрисовки UI. - */ - public void setPictureHeight(final int pictureHeight) { - this.pictureHeight = pictureHeight; - } - - /** - * @return ширина картики для отрисовки UI. - */ - public int getPictureWidth() { - return pictureWidth; - } - - /** - * @param pictureWidth ширина картики для отрисовки UI. - */ - public void setPictureWidth(final int pictureWidth) { - this.pictureWidth = pictureWidth; - } - - /** - * @return функция реординга данных. - */ - public Function getReorderData() { - return reorderData; - } - - /** - * @return рутовый узел текущей сцены. - */ - public Group getRootNode() { - return rootNode; - } - - /** - * @return текущая сцена UI. - */ - public Scene getScene() { - return scene; - } - - /** - * @return текущая сцена UI. - */ - public EmbeddedSceneInterface getScenePeer() { - return scenePeer; - } - - /** - * @param scenePeer текущая сцена UI. - */ - public void setScenePeer(final EmbeddedSceneInterface scenePeer) { - this.scenePeer = scenePeer; - } - - /** - * @return встроенное окно JavaFX UI. - */ - public EmbeddedWindow getStage() { - return stage; - } - - /** - * @return текущая стадия UI. - */ - public EmbeddedStageInterface getStagePeer() { - return stagePeer; - } - - /** - * @param stagePeer текущая стадия UI. - */ - public void setStagePeer(final EmbeddedStageInterface stagePeer) { - this.stagePeer = stagePeer; - } - - /** - * @return временные данные кадра отрисованного в JavaFX. - */ - public ByteBuffer getTempData() { - return tempData; - } - - /** - * @return текстура на которой отрисовано UI. - */ - public Texture2D getTexture() { - return texture; - } - - /** - * @return кол-во незаписанных в JME кадров. - */ - public AtomicInteger getWaitCount() { - return waitCount; - } - - /** - * Indent the window position to account for window decoration. - */ - public int getWindowOffsetX() { - return windowOffsetX; - } - - /** - * Indent the window position to account for window decoration. - */ - public void setWindowOffsetX(final int windowOffsetX) { - this.windowOffsetX = windowOffsetX; - } - - /** - * Indent the window position to account for window decoration. - */ - public int getWindowOffsetY() { - return windowOffsetY; - } - - /** - * Indent the window position to account for window decoration. - */ - public void setWindowOffsetY(final int windowOffsetY) { - this.windowOffsetY = windowOffsetY; - } - - /** - * @return предыдущее положение экрана по X. - */ - public int getWindowX() { - return oldX; - } - - /** - * @return предыдущее положение экрана по Y. - */ - public int getWindowY() { - return oldY; - } - - /** - * Получение фокуса сценой FX UI. - */ - public void grabFocus() { - - final EmbeddedStageInterface stagePeer = getStagePeer(); - - if(isFocus() || stagePeer == null) { - return; - } - - stagePeer.setFocused(true, AbstractEvents.FOCUSEVENT_ACTIVATED); - setFocus(true); - - if(isDebug()) { - LOGGER.debug("got focus."); - } - } - - /** - * Инициализация или обновление размеров изображения. - */ - public void handleResize() { - - final long time = System.currentTimeMillis(); - - if(time - getLastResized() < 300) { - return; - } - - final JmeContext jmeContext = getJmeContext(); - - final int displayWidth = JFXUtils.getWidth(jmeContext); - final int displayHeight = JFXUtils.getHeight(jmeContext); - - final AsyncReadSyncWriteLock lock = getImageLock(); - lock.syncLock(); - try { - - final int pictureWidth = Math.max(displayWidth, 64); - final int pictureHeight = Math.max(displayHeight, 64); - - final Picture picture = getPicture(); - - if(isDebug()) { - LOGGER.debug("handle resize from [" + getPictureWidth() + "x" + getPictureHeight() + "] to [" + pictureWidth + "x" + pictureHeight + "]"); - } - - picture.setWidth(pictureWidth); - picture.setHeight(pictureHeight); - - if (fxData != null) { - BufferUtils.destroyDirectBuffer(fxData); - } - - if (tempData != null) { - BufferUtils.destroyDirectBuffer(tempData); - } - - if (jmeData != null) { - BufferUtils.destroyDirectBuffer(jmeData); - } - - if (jmeImage != null) { - jmeImage.dispose(); - } - - fxData = BufferUtils.createByteBuffer(pictureWidth * pictureHeight * 4); - tempData = BufferUtils.createByteBuffer(pictureWidth * pictureHeight * 4); - jmeData = BufferUtils.createByteBuffer(pictureWidth * pictureHeight * 4); - jmeImage = new Image(nativeFormat.get(), pictureWidth, pictureHeight, jmeData, ColorSpace.sRGB); - - final Texture2D texture = getTexture(); - - if (texture != null) { - texture.setImage(jmeImage); - } - - setPictureHeight(pictureHeight); - setPictureWidth(pictureWidth); - - final EmbeddedStageInterface stagePeer = getStagePeer(); - final EmbeddedSceneInterface scenePeer = getScenePeer(); - - if (stagePeer != null) { - Platform.runLater(() -> { - stagePeer.setSize(pictureWidth, pictureHeight); - scenePeer.setSize(pictureWidth, pictureHeight); - hostContainer.repaint(); - }); - } - - } catch (final Exception e) { - LOGGER.warning(e); - } finally { - lock.syncUnlock(); - } - - setLastResized(time); - } - - private void initFx() { - PlatformImpl.startup(() -> { - // TODO 3.1: use Format.ARGB8 and Format.BGRA8 and remove used - // of exchangeData, fx2jme_ARGB82ABGR8,... - switch (Pixels.getNativeFormat()) { - case Pixels.Format.BYTE_ARGB: - try { - JmeFxContainer.this.nativeFormat.complete(Format.valueOf("ARGB8")); - reorderData = null; - } catch (final Exception exc1) { - JmeFxContainer.this.nativeFormat.complete(Format.ABGR8); - reorderData = JmeFxContainer::reorder_ARGB82ABGR8; - } - break; - case Pixels.Format.BYTE_BGRA_PRE: - try { - JmeFxContainer.this.nativeFormat.complete(Format.valueOf("BGRA8")); - reorderData = null; - } catch (final Exception exc2) { - JmeFxContainer.this.nativeFormat.complete(Format.ABGR8); - reorderData = JmeFxContainer::reorder_BGRA82ABGR8; - } - break; - default: - try { - JmeFxContainer.this.nativeFormat.complete(Format.valueOf("ARGB8")); - reorderData = null; - } catch (final Exception exc3) { - JmeFxContainer.this.nativeFormat.complete(Format.ABGR8); - reorderData = JmeFxContainer::reorder_ARGB82ABGR8; - } - break; - } - }); - } - - /** - * Есть ли по этим координатом элемент JavaFX на сцене. - */ - public boolean isCovered(final int x, final int y) { - - final int pictureWidth = getPictureWidth(); - - if (x < 0 || x >= pictureWidth) { - return false; - } else if (y < 0 || y >= getPictureHeight()) { - return false; - } - - final Image jmeImage = getJmeImage(); - - final ByteBuffer data = jmeImage.getData(0); - data.limit(data.capacity()); - - final int alpha = data.get(3 + 4 * (y * pictureWidth + x)); - - data.limit(0); - - if(isDebug()) { - LOGGER.debug("is covered " + x + ", " + y + " = " + (alpha != 0)); - } - - return alpha != 0; - } - - /** - * @return есть ли сейчас фокус на FX UI. - */ - public boolean isFocus() { - return focus; - } - - /** - * @param focus есть ли сейчас фокус на FX UI. - */ - public void setFocus(final boolean focus) { - this.focus = focus; - } - - /** - * @return поддержка полноэкранного режима. - */ - public boolean isFullScreenSupport() { - return fullScreenSupport; - } - - /** - * @return нужна ли отрисовка. - */ - public boolean isNeedWriteToJME() { - return waitCount.get() > 0; - } - - /** - * @return отображается ли курсор. - */ - public boolean isVisibleCursor() { - return visibleCursor; - } - - /** - * @param visibleCursor отображается ли курсор. - */ - public void setVisibleCursor(final boolean visibleCursor) { - this.visibleCursor = visibleCursor; - } - - /** - * Уберание фокуса из сцены. - */ - public void loseFocus() { - - final EmbeddedStageInterface stagePeer = getStagePeer(); - - if(!isFocus() || stagePeer == null) { - return; - } - - stagePeer.setFocused(false, AbstractEvents.FOCUSEVENT_DEACTIVATED); - - setFocus(false); - - if(isDebug()) { - LOGGER.debug("lost focus."); - } - } - - /** - * Отрисока контейнера. - */ - public void paintComponent() { - - long time = 0; - - if(isDebug()) { - time = System.currentTimeMillis(); - LOGGER.debug("started paint FX scene..."); - } - - final EmbeddedSceneInterface scenePeer = getScenePeer(); - - if (scenePeer == null) { - return; - } - - final PaintListener[] paintListeners = getPaintListeners(); - - if (paintListeners.length > 0) { - for (final PaintListener paintListener : paintListeners) { - paintListener.prePaint(); - } - } - - final ByteBuffer tempData = getTempData(); - tempData.clear(); - - final IntBuffer intBuffer = tempData.asIntBuffer(); - - final int pictureWidth = getPictureWidth(); - final int pictureHeight = getPictureHeight(); - - if (!scenePeer.getPixels(intBuffer, pictureWidth, pictureHeight)) { - return; - } - - tempData.flip(); - tempData.limit(pictureWidth * pictureHeight * 4); - - final AsyncReadSyncWriteLock imageLock = getImageLock(); - imageLock.syncLock(); - try { - - final ByteBuffer fxData = getFxData(); - fxData.clear(); - fxData.put(tempData); - fxData.flip(); - - final Function reorderData = getReorderData(); - - if (reorderData != null) { - reorderData.apply(fxData); - fxData.position(0); - } - - } catch (final Exception exc) { - exc.printStackTrace(); - } finally { - imageLock.syncUnlock(); - } - - if (paintListeners.length > 0) { - for (final PaintListener paintListener : paintListeners) { - paintListener.postPaint(); - } - } - - final AtomicInteger waitCount = getWaitCount(); - waitCount.incrementAndGet(); - - if(isDebug()) { - LOGGER.debug("finished paint FX scene(" + (System.currentTimeMillis() - time) + "ms.)."); - } - } - - /** - * Удаление слушателя отрисовки. - */ - public void removePaintListener(final PaintListener paintListener) { - - final List temp = new ArrayList<>(); - Collections.addAll(temp, getPaintListeners()); - temp.remove(paintListener); - - setPaintListeners(temp.toArray(new PaintListener[temp.size()])); - } - - int retrieveKeyState() { - - int embedModifiers = 0; - - if (keyStateSet.get(KeyEvent.VK_SHIFT)) { - embedModifiers |= AbstractEvents.MODIFIER_SHIFT; - } - - if (keyStateSet.get(KeyEvent.VK_CONTROL)) { - embedModifiers |= AbstractEvents.MODIFIER_CONTROL; - } - - if (keyStateSet.get(KeyEvent.VK_ALT)) { - embedModifiers |= AbstractEvents.MODIFIER_ALT; - } - - if (keyStateSet.get(KeyEvent.VK_META)) { - embedModifiers |= AbstractEvents.MODIFIER_META; - } - - return embedModifiers; - } - - /** - * call via gui manager! - */ - public void setEverListeningRawInputListener(final RawInputListener rawInputListenerAdapter) { - this.inputListener.setEverListeningRawInputListener(rawInputListenerAdapter); - } - - void setFxEnabled(final boolean enabled) { - } - - public void setScene(final Scene newScene, final Group highLevelGroup) { - this.rootNode = highLevelGroup; - FxPlatformExecutor.runOnFxApplication(() -> JmeFxContainer.this.setSceneImpl(newScene)); - } - - /* - * Called on JavaFX application thread. - */ - private void setSceneImpl(final Scene newScene) { - if (this.stage != null && newScene == null) { - this.stage.hide(); - this.stage = null; - } - - this.application.enqueue(() -> { - JmeFxContainer.this.picture.setCullHint(newScene == null ? CullHint.Always : CullHint.Never); - return null; - }); - - this.scene = newScene; - if (this.stage == null && newScene != null) { - this.stage = new EmbeddedWindow(this.hostContainer); - } - if (this.stage != null) { - this.stage.setScene(newScene); - if (!this.stage.isShowing()) { - this.stage.show(); - } - } - } - - /** - * Запись резульата FX UI на текстуру в JME. - */ - public Void writeToJME() { - - final AtomicInteger waitCount = getWaitCount(); - final int currentCount = waitCount.get(); - - long time = 0; - - if(isDebug()) { - time = System.currentTimeMillis(); - LOGGER.debug("started writing FX data to JME..."); - } - - final ByteBuffer jmeData = getJmeData(); - jmeData.clear(); - - final AsyncReadSyncWriteLock imageLock = getImageLock(); - imageLock.syncLock(); - try { - jmeData.put(getFxData()); - } finally { - imageLock.syncUnlock(); - } - - jmeData.flip(); - - final Image jmeImage = getJmeImage(); - jmeImage.setUpdateNeeded(); - - waitCount.subAndGet(currentCount); - - if(isDebug()) { - LOGGER.debug("finished writing FX data to JME(" + (System.currentTimeMillis() - time) + "ms.)."); - } - - return null; - } - - /** - * @param enabled доступен ли сейчас JavaFX. - */ - public void setEnabled(final boolean enabled) { - this.enabled = enabled; - } - - /** - * @return доступен ли сейчас JavaFX. - */ - public boolean isEnabled() { - return enabled; - } -} \ No newline at end of file diff --git a/src/main/java/com/jme3x/jfx/JmeFxDNDHandler.java b/src/main/java/com/jme3x/jfx/JmeFxDNDHandler.java deleted file mode 100644 index 0257995..0000000 --- a/src/main/java/com/jme3x/jfx/JmeFxDNDHandler.java +++ /dev/null @@ -1,213 +0,0 @@ -package com.jme3x.jfx; - -import com.sun.javafx.embed.EmbeddedSceneDSInterface; -import com.sun.javafx.embed.EmbeddedSceneDTInterface; -import com.sun.javafx.embed.EmbeddedSceneInterface; -import com.sun.javafx.embed.HostDragStartListener; - -import java.nio.ByteBuffer; - -import javafx.collections.ObservableList; -import javafx.scene.Group; -import javafx.scene.Node; -import javafx.scene.image.ImageView; -import javafx.scene.image.PixelFormat; -import javafx.scene.image.PixelWriter; -import javafx.scene.image.WritableImage; -import javafx.scene.input.Clipboard; -import javafx.scene.input.TransferMode; -import rlib.logging.Logger; -import rlib.logging.LoggerManager; - -/** - * A very hacky implementation of a DND system, similar to SwingDND but for jme context.
Allows - * for inner application drag and drop support.
Cross GuiManager support is untested. - * - * @author empire - */ -public class JmeFxDNDHandler implements HostDragStartListener { - - private static final Logger LOGGER = LoggerManager.getLogger(JmeFxDNDHandler.class); - - private JmeFxContainer jmeFxContainer; - private EmbeddedSceneDTInterface dropTarget; - - // mouse event stuff - private EmbeddedSceneDSInterface dragSource; - private TransferMode overTarget; - private ImageView dragImage; - - public JmeFxDNDHandler(final JmeFxContainer jmeFxContainer) { - this.jmeFxContainer = jmeFxContainer; - } - - /** - * this is kinda ridiculous, but well at least it seems to work - */ - private void createDragImageProxy(final Object jmeJfxDragImage, final Object offset) { - - if (!(jmeJfxDragImage instanceof ByteBuffer)) { - return; - } - - try { - - final ByteBuffer casted = (ByteBuffer) jmeJfxDragImage; - casted.position(0); - - final int width = casted.getInt(); - final int height = casted.getInt(); - - final byte[] imgdata = new byte[casted.remaining()]; - casted.get(imgdata); - - final WritableImage img = new WritableImage(width, height); - final PixelWriter writer = img.getPixelWriter(); - writer.setPixels(0, 0, width, height, PixelFormat.getByteBgraInstance(), imgdata, 0, width * 4); - - dragImage = new ImageView(img); - dragImage.setStyle("dragimage:true;"); - dragImage.setMouseTransparent(true); - dragImage.setVisible(true); - - if (offset instanceof ByteBuffer) { - - ((ByteBuffer) offset).position(0); - - final int x = ((ByteBuffer) offset).getInt(); - final int y = ((ByteBuffer) offset).getInt(); - - if (LOGGER.isEnabledDebug()) LOGGER.debug("Img offset " + x + ", " + y); - } - - } catch (final Exception e) { - LOGGER.warning(e.getMessage(), e); - } - } - - @Override - public void dragStarted(final EmbeddedSceneDSInterface dragSource, final TransferMode dragAction) { - - final JmeFxContainer jmeFxContainer = getJmeFxContainer(); - final JmeFXInputListener inputListener = jmeFxContainer.getInputListener(); - final EmbeddedSceneInterface scenePeer = jmeFxContainer.getScenePeer(); - final Group rootNode = jmeFxContainer.getRootNode(); - final ObservableList children = rootNode.getChildren(); - - if (dragImage != null) { - children.remove(dragImage); - dragImage = null; - } - - try { - - final Object dragImg = dragSource.getData("application/x-java-drag-image"); - final Object offset = dragSource.getData("application/x-java-drag-image-offset"); - - if (dragImg != null) { - createDragImageProxy(dragImg, offset); - } - - inputListener.setMouseDNDListener(this); - - assert dragAction == TransferMode.COPY : "Only Copy is supported currently"; - - if (LOGGER.isEnabledDebug()) { - LOGGER.debug("Drag started of " + dragSource + " in mode " + dragAction); - } - - final Clipboard clip = Clipboard.getSystemClipboard(); - - if (LOGGER.isEnabledDebug()) { - LOGGER.debug("clip : " + clip); - } - - assert this.dragSource == null; - assert this.dropTarget == null; - - this.dragSource = dragSource; - this.dropTarget = scenePeer.createDropTarget(); - // pseudo enter, we only support inner events, so it stays always entered - this.dropTarget.handleDragEnter(0, 0, 0, 0, TransferMode.COPY, dragSource); - - } catch (final Exception e) { - LOGGER.warning(e.getMessage(), e); - } - } - - public ImageView getDragImage() { - return dragImage; - } - - public JmeFxContainer getJmeFxContainer() { - return jmeFxContainer; - } - - public void mouseUpdate(final int x, final int y, final boolean mousePressed) { - - if (this.dragSource == null || this.dropTarget == null) { - return; - } - - final JmeFxContainer jmeFxContainer = getJmeFxContainer(); - final JmeFXInputListener inputListener = jmeFxContainer.getInputListener(); - final Group rootNode = jmeFxContainer.getRootNode(); - final ObservableList children = rootNode.getChildren(); - - final ImageView dragImage = getDragImage(); - - try { - - if (mousePressed) { - - if (dragImage != null) { - dragImage.relocate(x, y); - - // only add once it has a valid position - if (!children.contains(dragImage)) { - children.add(dragImage); - } - } - - this.overTarget = this.dropTarget.handleDragOver(x, y, x, y, TransferMode.COPY); - - } else { - - if (dragImage != null) { - dragImage.setVisible(false); - } - - if (LOGGER.isEnabledDebug()) { - LOGGER.debug("Drag released!"); - } - - if (this.overTarget != null) { - - // // causes exceptions when done without a target - this.overTarget = this.dropTarget.handleDragOver(x, y, x, y, TransferMode.COPY); - final TransferMode acceptedMode = this.dropTarget.handleDragDrop(x, y, x, y, TransferMode.COPY); - // // Necessary to reset final the internal states, and allow final another drag drop - this.dragSource.dragDropEnd(acceptedMode); - - } else { - - if (LOGGER.isEnabledDebug()) { - LOGGER.debug("invalid drag target"); - } - - // // seems to be necessary if no dragdrop attempt is being made - this.dropTarget.handleDragLeave(); - this.dragSource.dragDropEnd(null); - } - - inputListener.setMouseDNDListener(null); - - this.dragSource = null; - this.dropTarget = null; - } - - } catch (final Exception e) { - LOGGER.warning(e.getMessage(), e); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/jme3x/jfx/PixelUtils.java b/src/main/java/com/jme3x/jfx/PixelUtils.java deleted file mode 100755 index e8311ba..0000000 --- a/src/main/java/com/jme3x/jfx/PixelUtils.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package com.jme3x.jfx; - -/** - * Convertes image formats between jfx and jme - * - * @author Heist - */ -public abstract class PixelUtils { - - static int mergeArgb(final int bg, final int src) { - - final int sa = src >>> 24; - - if (sa == 0) { - return bg; - } - - final int ba = bg >>> 24; - - final int rb = (src & 0x00ff00ff) * sa + (bg & 0x00ff00ff) * (0xff - sa) & 0xff00ff00; - final int g = (src & 0x0000ff00) * sa + (bg & 0x0000ff00) * (0xff - sa) & 0x00ff0000; - final int a = sa + (ba * (0xff - sa) >> 8); - - return a << 24 | (rb | g) >>> 8; - } - - static int mergeBgra(final int bg, final int src) { - - final int sa = src & 0xff; - - if (sa == 0) { - return bg; - } - - final int ba = bg & 0xff; - - final int a = sa + (ba * (0xff - sa) >> 8); - - final int b = ((src & 0xff000000) >> 24) * sa + ((bg & 0xff000000) >> 24) * ba >> 8; - final int g = ((src & 0xff0000) >> 16) * sa + ((bg & 0xff0000) >> 16) * ba >> 8; - final int r = ((src & 0xff00) >> 8) * sa + ((bg & 0xff00) >> 8) * ba >> 8; - - return b << 24 | g << 16 | r << 8 | a; - // return 0xffff0000; - } - - // this is platform specific... assumes little-endian - static int mergeBgraPre(final int bg, final int src) { - - final int sa = src >>> 24; - - if (sa == 0) { - return bg; - } - - final int ba = bg >>> 24; - - final int rb = (src & 0x00ff00ff) * 0xff + (bg & 0x00ff00ff) * (0xff - sa) & 0xff00ff00; - final int g = (src & 0x0000ff00) * 0xff + (bg & 0x0000ff00) * (0xff - sa) & 0x00ff0000; - final int a = sa + (ba * (0xff - sa) >> 8); - - return a << 24 | (rb | g) >>> 8; - } -} diff --git a/src/main/java/com/jme3x/jfx/cursor/CursorDisplayProvider.java b/src/main/java/com/jme3x/jfx/cursor/CursorDisplayProvider.java deleted file mode 100644 index 9cd7833..0000000 --- a/src/main/java/com/jme3x/jfx/cursor/CursorDisplayProvider.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.jme3x.jfx.cursor; - -import com.sun.javafx.cursor.CursorFrame; -import com.sun.javafx.cursor.CursorType; - -public interface CursorDisplayProvider { - - /** - * called by the GuiManager during startup, should be used to create the necessary cursors - */ - void setup(CursorType normal); - - void showCursor(CursorFrame cursorFrame); -} diff --git a/src/main/java/com/jme3x/jfx/cursor/proton/ProtonCursorProvider.java b/src/main/java/com/jme3x/jfx/cursor/proton/ProtonCursorProvider.java deleted file mode 100755 index 1e87e6f..0000000 --- a/src/main/java/com/jme3x/jfx/cursor/proton/ProtonCursorProvider.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.jme3x.jfx.cursor.proton; - -import com.jme3.app.Application; -import com.jme3.asset.AssetManager; -import com.jme3.asset.plugins.ClasspathLocator; -import com.jme3.cursors.plugins.JmeCursor; -import com.jme3.input.InputManager; -import com.jme3x.jfx.cursor.CursorDisplayProvider; -import com.sun.javafx.cursor.CursorFrame; -import com.sun.javafx.cursor.CursorType; - -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; - -import rlib.logging.Logger; -import rlib.logging.LoggerManager; - -/** - * http://www.rw-designer.com/cursor-set/proton by juanello
A cursorProvider that simulates the - * native JFX one and tries to behave similar,
using native cursors and 2D surface logic. - * - * @author empire - */ -public class ProtonCursorProvider implements CursorDisplayProvider { - - private static final Logger LOGGER = LoggerManager.getLogger(ProtonCursorProvider.class); - - private ConcurrentHashMap cache = new ConcurrentHashMap(); - - private AssetManager assetManager; - private InputManager inputManager; - private Application app; - - public ProtonCursorProvider(final Application app, final AssetManager assetManager, final InputManager inputManager) { - this.assetManager = assetManager; - this.inputManager = inputManager; - this.app = app; - assetManager.registerLocator("", ClasspathLocator.class); - } - - @Override - public void setup(final CursorType ctyp) { - JmeCursor loaded = null; - switch (ctyp) { - case CLOSED_HAND: - break; - case CROSSHAIR: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_cross.cur"); - break; - case DEFAULT: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_arrow.cur"); - break; - case DISAPPEAR: - break; - case E_RESIZE: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_ew.cur"); - break; - case HAND: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_link.cur"); - break; - case H_RESIZE: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_ew.cur"); - break; - case IMAGE: - break; - case MOVE: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_move.cur"); - break; - case NE_RESIZE: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_nesw.cur"); - break; - case NONE: - break; - case NW_RESIZE: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_nwse.cur"); - break; - case N_RESIZE: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_ns.cur"); - break; - case OPEN_HAND: - break; - case SE_RESIZE: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_nwse.cur"); - break; - case SW_RESIZE: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_nesw.cur"); - break; - case S_RESIZE: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_ns.cur"); - break; - case TEXT: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_text.cur"); - break; - case V_RESIZE: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_ns.cur"); - break; - case WAIT: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_busy.ani"); - break; - case W_RESIZE: - loaded = (JmeCursor) this.assetManager.loadAsset("com/jme3x/jfx/cursor/proton/aero_ew.cur"); - break; - } - if (loaded != null) { - this.cache.put(ctyp, loaded); - } - } - - @Override - public void showCursor(final CursorFrame cursorFrame) { - CursorType cursorType = cursorFrame.getCursorType(); - if (this.cache.get(cursorType) == null) { - LOGGER.debug("Unkown Cursor! " + cursorType); - cursorType = CursorType.DEFAULT; - } - - final JmeCursor toDisplay = this.cache.get(cursorType); - - if (toDisplay != null) { - this.app.enqueue(new Callable() { - @Override - public Void call() throws Exception { - ProtonCursorProvider.this.inputManager.setMouseCursor(toDisplay); - return null; - } - }); - } - } -} diff --git a/src/main/java/com/jme3x/jfx/injfx/JmeContextOffscreenSurface.java b/src/main/java/com/jme3x/jfx/injfx/JmeContextOffscreenSurface.java deleted file mode 100644 index 5713d60..0000000 --- a/src/main/java/com/jme3x/jfx/injfx/JmeContextOffscreenSurface.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.jme3x.jfx.injfx; - -import com.jme3.input.JoyInput; -import com.jme3.input.KeyInput; -import com.jme3.input.MouseInput; -import com.jme3.input.TouchInput; -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; - -public class JmeContextOffscreenSurface implements JmeContext { - - private final AppSettings settings = new AppSettings(true); - - private JmeContext actualContext; - - public JmeContextOffscreenSurface() { - this.settings.setRenderer(AppSettings.LWJGL_OPENGL2); - } - - @Override - public void create(boolean waitFor) { - lazyActualContext().create(waitFor); - } - - @Override - public void destroy(boolean waitFor) { - if (actualContext == null) throw new IllegalStateException("Not created"); - // destroy wrapped context - actualContext.destroy(waitFor); - } - - @Override - public JoyInput getJoyInput() { - return null; - } - - @Override - public KeyInput getKeyInput() { - //return actualContext.getKeyInput(); - return null; - } - - @Override - public MouseInput getMouseInput() { - //return actualContext.getMouseInput(); - return null; - } - - @Override - public Renderer getRenderer() { - return actualContext.getRenderer(); - } - - @Override - public AppSettings getSettings() { - return settings; - } - - @Override - public void setSettings(AppSettings settings) { - this.settings.copyFrom(settings); - this.settings.setRenderer(AppSettings.LWJGL_OPENGL2); - lazyActualContext().setSettings(this.settings); - } - - @Override - public Timer getTimer() { - return actualContext.getTimer(); - } - - @Override - public TouchInput getTouchInput() { - return null; - } - - @Override - public Type getType() { - return Type.OffscreenSurface; - } - - @Override - public boolean isCreated() { - return actualContext != null && actualContext.isCreated(); - } - - @Override - public boolean isRenderable() { - return actualContext != null && actualContext.isRenderable(); - } - - private JmeContext lazyActualContext() { - if (actualContext == null) { - actualContext = JmeSystem.newContext(this.settings, Type.OffscreenSurface); - } - return actualContext; - } - - @Override - public void restart() { - } - - @Override - public void setAutoFlushFrames(boolean enabled) { - // TODO Auto-generated method stub - - } - - @Override - public void setSystemListener(SystemListener listener) { - lazyActualContext().setSystemListener(listener); - } - - @Override - public void setTitle(String title) { - // TODO Auto-generated method stub - } -} \ No newline at end of file diff --git a/src/main/java/com/jme3x/jfx/injfx/JmeForImageView.java b/src/main/java/com/jme3x/jfx/injfx/JmeForImageView.java deleted file mode 100644 index 43e7176..0000000 --- a/src/main/java/com/jme3x/jfx/injfx/JmeForImageView.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.jme3x.jfx.injfx; - -import com.jme3.app.DebugKeysAppState; -import com.jme3.app.SimpleApplication; -import com.jme3.system.AppSettings; - -import java.util.concurrent.Future; -import java.util.function.Function; - -import javafx.scene.image.ImageView; - -/** - * JmeForImageView create a jme'SimpleApplication viewable into an JavaFX's ImageView.

You can - * manage the wrapped SimpleApplication by calling enqueue (by example to add/remove AppState, Node, - * Light, to change the AppSettings...).

See TestDisplayInImageView.java for sample usage.

- * The usage of the class is optional, it can avoid some pitfall in the configuration. If you want a - * better control, I suggest you to browse the source of this class to see sample of configuration - * and usage of SceneProcessorCopyToImage. - * - * @author davidB - * @TODO auto-stop when the ImageView is removed from JavaFX Stage - */ -public class JmeForImageView { - - private static SimpleApplication makeJmeApplication(int framerate) { - AppSettings settings = new AppSettings(true); - - // important to use those settings - settings.setFullscreen(false); - settings.setUseInput(false); - settings.setFrameRate(Math.max(1, Math.min(60, framerate))); - settings.setCustomRenderer(com.jme3x.jfx.injfx.JmeContextOffscreenSurface.class); - - SimpleApplication app = new SimpleApplication() { - @Override - public void simpleInitApp() { - // to prevent a NPE (due to setUseInput(null)) on Application.stop() - getStateManager().detach(getStateManager().getState(DebugKeysAppState.class)); - } - }; - app.setSettings(settings); - app.setShowSettings(false); - return app; - } - - private SimpleApplication jmeApp0; - private SceneProcessorCopyToImageView jmeAppDisplayBinder = new SceneProcessorCopyToImageView(); - - /** - * Bind the wrapped SimpleApplication to an imageView.

  • Only one imageView can be - * binded.
  • Only jmeApp.getViewPort(), jmeApp.getGuiViewPort() are binded
- * - * @param imageView destination - * @return Future when bind is done (async) - */ - public Future bind(ImageView imageView) { - return enqueue((jmeApp) -> { - jmeAppDisplayBinder.bind(imageView, jmeApp); - return true; - }); - } - - /** - * Enqueue action to apply in Jme's Thread Action can be add/remove AppState, Node, Light, - * change the AppSettings.... - * - * @param f(jmeApp) the action to apply - */ - public Future enqueue(Function f) { - SimpleApplication jmeApp = findOrCreate(); - return jmeApp.enqueue(() -> { - return f.apply(jmeApp); - }); - } - - /** - * Lazy creation of the wrapped SimpleApplication. - */ - private SimpleApplication findOrCreate() { - if (jmeApp0 == null) { - jmeApp0 = makeJmeApplication(30); - jmeApp0.start(); - } - return jmeApp0; - } - - public void stop(boolean waitFor) { - if (jmeApp0 != null) { - try { - unbind().get(); - } catch (Exception exc) { - //TODO - exc.printStackTrace(); - } - jmeApp0.stop(waitFor); - } - } - - public Future unbind() { - return enqueue((jmeApp) -> { - jmeAppDisplayBinder.unbind(); - return true; - }); - } -} diff --git a/src/main/java/com/jme3x/jfx/injfx/SceneProcessorCopyToImageView.java b/src/main/java/com/jme3x/jfx/injfx/SceneProcessorCopyToImageView.java deleted file mode 100644 index 5c134bb..0000000 --- a/src/main/java/com/jme3x/jfx/injfx/SceneProcessorCopyToImageView.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.jme3x.jfx.injfx; - -import com.jme3.app.Application; -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.jme3x.jfx.FxPlatformExecutor; - -import java.nio.ByteBuffer; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -import javafx.beans.value.ChangeListener; -import javafx.scene.image.ImageView; -import javafx.scene.image.PixelFormat; -import javafx.scene.image.WritableImage; - -//https://github.com/caprica/vlcj-javafx/blob/master/src/test/java/uk/co/caprica/vlcj/javafx/test/JavaFXDirectRenderingTest.java -//http://stackoverflow.com/questions/15951284/javafx-image-resizing -//http://hub.jmonkeyengine.org/forum/topic/offscreen-rendering-problem/ -//TODO manage suspend/resume (eg when image/stage is hidden) -public class SceneProcessorCopyToImageView implements SceneProcessor { - - private RenderManager rm; - private ViewPort latestViewPorts; - private int askWidth = 1; - private int askHeight = 1; - private boolean askFixAspect = true; - private TransfertImage timage; - private AtomicBoolean reshapeNeeded = new AtomicBoolean(true); - - private ImageView imgView; - private ChangeListener wlistener = (w, o, n) -> { - componentResized(n.intValue(), (int) this.imgView.getFitHeight(), this.imgView.preserveRatioProperty().get()); - }; - private ChangeListener hlistener = (w, o, n) -> { - componentResized((int) this.imgView.getFitWidth(), n.intValue(), this.imgView.preserveRatioProperty().get()); - }; - private ChangeListener rlistener = (w, o, n) -> { - componentResized((int) this.imgView.getFitWidth(), (int) this.imgView.getFitHeight(), n.booleanValue()); - }; - - public void bind(ImageView view, Application jmeApp) { - unbind(); - - if (jmeApp != null) { - List vps = jmeApp.getRenderManager().getPostViews(); - latestViewPorts = vps.get(vps.size() - 1); - latestViewPorts.addProcessor(this); - } - - FxPlatformExecutor.runOnFxApplication(() -> { - imgView = view; - if (imgView != null) { - imgView.fitWidthProperty().addListener(wlistener); - imgView.fitHeightProperty().addListener(hlistener); - imgView.preserveRatioProperty().addListener(rlistener); - componentResized((int) imgView.getFitWidth(), (int) imgView.getFitHeight(), imgView.isPreserveRatio()); - imgView.setScaleY(-1.0); - } - }); - } - - @Override - public void cleanup() { - if (timage != null) { - timage.dispose(); - timage = null; - } - } - - public void componentResized(int w, int h, boolean fixAspect) { - int newWidth2 = Math.max(w, 1); - int newHeight2 = Math.max(h, 1); - if (askWidth != newWidth2 || askWidth != newHeight2 || askFixAspect != fixAspect) { - askWidth = newWidth2; - askHeight = newHeight2; - askFixAspect = fixAspect; - reshapeNeeded.set(true); - } - } - - @Override - public void initialize(RenderManager rm, ViewPort vp) { - if (this.rm == null) { - // First time called in OGL thread - this.rm = rm; - } - } - - @Override - public boolean isInitialized() { - return timage != null; - } - - @Override - public void postFrame(FrameBuffer out) { - if (imgView != null && timage != null) { - // if (out != timage.fb){ - // throw new IllegalStateException("Why did you change the output framebuffer? " + out + " != " + timage.fb); - // } - timage.copyFrameBufferToImage(rm, imgView); - } - // for the next frame - if (reshapeNeeded.getAndSet(false)) { - timage = reshapeInThread(askWidth, askHeight, askFixAspect); - //TODO dispose previous timage ASAP (when no longer used in JavafFX thread) - } - } - - @Override - public void postQueue(RenderQueue rq) { - } - - @Override - public void preFrame(float tpf) { - } - - @Override - public void reshape(ViewPort vp, int w, int h) { - } - - private TransfertImage reshapeInThread(int width0, int height0, boolean fixAspect) { - TransfertImage ti = new TransfertImage(width0, height0); - - rm.getRenderer().setMainFrameBufferOverride(ti.fb); - rm.notifyReshape(ti.width, ti.height); - - // for (ViewPort vp : viewPorts){ - // vp.getCamera().resize(ti.width, ti.height, fixAspect); - // - // // NOTE: Hack alert. This is done ONLY for custom framebuffers. - // // Main framebuffer should use RenderManager.notifyReshape(). - // for (SceneProcessor sp : vp.getProcessors()){ - // sp.reshape(vp, ti.width, ti.height); - // } - // } - return ti; - } - - public void unbind() { - - if (latestViewPorts != null) { - latestViewPorts.removeProcessor(this); // call this.cleanup() - latestViewPorts = null; - } - - FxPlatformExecutor.runOnFxApplication(() -> { - if (imgView != null) { - imgView.fitWidthProperty().removeListener(wlistener); - imgView.fitHeightProperty().removeListener(hlistener); - } - }); - } - - static class TransfertImage { - public final int width; - public final int height; - public final FrameBuffer fb; - public final ByteBuffer byteBuf; - public final WritableImage img; - private ImageView lastIv = null; - - TransfertImage(int width, int height) { - this.width = width; - this.height = height; - - fb = new FrameBuffer(width, height, 1); - fb.setDepthBuffer(Format.Depth); - fb.setColorBuffer(Format.RGB8); - - byteBuf = BufferUtils.createByteBuffer(width * height * 4); - - img = new WritableImage(width, height); - } - - /** - * SHOULD run in JME'Display thread - */ - void copyFrameBufferToImage(RenderManager rm, ImageView iv) { - synchronized (byteBuf) { - // Convert screenshot. - byteBuf.clear(); - rm.getRenderer().readFrameBuffer(fb, byteBuf); - } - FxPlatformExecutor.runOnFxApplication(() -> { - synchronized (byteBuf) { - if (lastIv != iv) { - lastIv = iv; - lastIv.setImage(img); - } - img.getPixelWriter().setPixels(0, 0, width, height, PixelFormat.getByteBgraInstance(), byteBuf, width * 4); - } - }); - } - - void dispose() { - fb.dispose(); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/jme3x/jfx/listener/PaintListener.java b/src/main/java/com/jme3x/jfx/listener/PaintListener.java deleted file mode 100755 index 52fc1742..0000000 --- a/src/main/java/com/jme3x/jfx/listener/PaintListener.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jme3x.jfx.listener; - -/** - * Реализация слушателя отрисовки интерфейса. - * - * @author Ronn - */ -public interface PaintListener { - - /** - * Обработка действий после отрисовки. - */ - public void postPaint(); - - /** - * Обработка действий перед отрисовкой. - */ - public void prePaint(); -} diff --git a/src/main/java/com/jme3x/jfx/media/TextureMovie.java b/src/main/java/com/jme3x/jfx/media/TextureMovie.java deleted file mode 100644 index ab42c7b..0000000 --- a/src/main/java/com/jme3x/jfx/media/TextureMovie.java +++ /dev/null @@ -1,239 +0,0 @@ -package com.jme3x.jfx.media; - -import com.jme3.math.ColorRGBA; -import com.jme3.math.Vector2f; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture2D; -import com.jme3.util.BufferUtils; -import com.sun.media.jfxmedia.control.VideoDataBuffer; -import com.sun.media.jfxmedia.control.VideoFormat; -import com.sun.media.jfxmedia.events.NewFrameEvent; -import com.sun.media.jfxmedia.events.VideoRendererListener; - -import java.lang.reflect.Method; -import java.nio.ByteBuffer; - -/** - * Example usage

PlatformImpl.startup(() -> {}); media = new Media("http://download.oracle.com/otndocs/products/javafx/oow2010-2.flv"); - * mp = new javafx.scene.media.MediaPlayer(media); mp.play(); tm = new TextureMovie(mp, - * LetterboxMode.VALID_LETTERBOX); tm.setLetterboxColor(ColorRGBA.Black); texture = tm.getTexture(); - *

material.setTexture("ColorMap",texture); Please be sure you do not lose reference to - * TextureMove while you need the result texture - if it ever gets garbage collected, movie can stop - * playing. - */ -public class TextureMovie { - - private static Image emptyImage = new Image(Format.ABGR8, 1, 1, BufferUtils.createByteBuffer(4)); - private final javafx.scene.media.MediaPlayer jPlayer; - private final com.sun.media.jfxmedia.MediaPlayer cPlayer; - private final LetterboxMode mode; - private final ColorRGBA letterboxColor = ColorRGBA.Red.clone(); - private final Vector2f upperLeftCorner = new Vector2f(0, 0); - private final Vector2f bottomRightCorner = new Vector2f(1, 1); - private final VideoRendererListener vrListener; - private float aspectRatio = 1.0f; - private Texture2D texture; - - public TextureMovie(javafx.scene.media.MediaPlayer mediaPlayer) { - this(mediaPlayer, LetterboxMode.VALID_LETTERBOX); - } - - public TextureMovie(javafx.scene.media.MediaPlayer mediaPlayer, ColorRGBA letterboxColor) { - this(mediaPlayer, LetterboxMode.VALID_LETTERBOX); - setLetterboxColor(letterboxColor); - } - - public TextureMovie(javafx.scene.media.MediaPlayer mediaPlayer, LetterboxMode mode) { - this.jPlayer = mediaPlayer; - this.mode = mode; - - try { - Method m1 = jPlayer.getClass().getDeclaredMethod("retrieveJfxPlayer"); - m1.setAccessible(true); - - while (true) { - com.sun.media.jfxmedia.MediaPlayer player = (com.sun.media.jfxmedia.MediaPlayer) m1.invoke(jPlayer); - if (player != null) { - cPlayer = player; - break; - } - Thread.sleep(50); - } - - } catch (Exception exc) { - throw new RuntimeException(exc); - } - - vrListener = createVrListener(); - } - - private VideoRendererListener createVrListener() { - return new VideoRendererListener() { - - @Override - public void releaseVideoFrames() { - // TODO Auto-generated method stub - - } - - @Override - public void videoFrameUpdated(NewFrameEvent event) { - try { - VideoDataBuffer frame = jPlayer.impl_getLatestFrame(); - if (frame == null) { - return; - } - VideoDataBuffer argbFrame = frame.convertToFormat(VideoFormat.ARGB); - frame.releaseFrame(); - - int expectedWidth, expectedHeight; - int xOffset; - int xSize; - int yOffset; - int ySize; - - switch (mode) { - case RAW_SQUARE: - xOffset = 0; - yOffset = 0; - expectedWidth = argbFrame.getEncodedWidth(); - expectedHeight = argbFrame.getEncodedHeight(); - xSize = expectedWidth; - ySize = expectedHeight; - break; - case VALID_SQUARE: - xOffset = 0; - yOffset = 0; - expectedWidth = argbFrame.getWidth(); - expectedHeight = argbFrame.getHeight(); - xSize = expectedWidth; - ySize = expectedHeight; - break; - case VALID_LETTERBOX: - default: - expectedWidth = Math.max(argbFrame.getWidth(), argbFrame.getHeight()); - expectedHeight = expectedWidth; - - if (argbFrame.getWidth() >= argbFrame.getHeight()) { - xOffset = 0; - xSize = expectedWidth; - ySize = argbFrame.getHeight(); - yOffset = (xSize - ySize) / 2; - } else { - yOffset = 0; - ySize = expectedHeight; - xSize = argbFrame.getWidth(); - xOffset = (ySize - xSize) / 2; - } - - break; - - } - - aspectRatio = argbFrame.getWidth() / argbFrame.getHeight(); - - upperLeftCorner.set(xOffset / (float) expectedWidth, yOffset / (float) expectedHeight); - bottomRightCorner.set((xOffset + argbFrame.getWidth()) / (float) expectedWidth, (yOffset + argbFrame.getHeight()) / (float) expectedHeight); - - Image image = texture.getImage(); - - if (image.getWidth() != expectedWidth || image.getHeight() != expectedHeight) { - ByteBuffer bb = BufferUtils.createByteBuffer(expectedHeight * expectedWidth * 4); - - for (int i = 0; i < bb.limit(); i += 4) { - bb.put(i, (byte) (letterboxColor.a * 255)); - bb.put(i + 1, (byte) (letterboxColor.b * 255)); - bb.put(i + 2, (byte) (letterboxColor.g * 255)); - bb.put(i + 3, (byte) (letterboxColor.r * 255)); - } - image = new Image(Format.ABGR8, expectedWidth, expectedHeight, bb); - texture.setImage(image); - } - - - ByteBuffer src = argbFrame.getBufferForPlane(0); - ByteBuffer bb = image.getData(0); - bb.clear(); - - for (int y = 0; y < ySize; y++) { - int ty = expectedHeight - (y + yOffset) - 1; - bb.position(4 * (ty * xSize + xOffset)); - src.position(4 * (y * argbFrame.getEncodedWidth())).limit(4 * (y * argbFrame.getEncodedWidth() + xSize)); - bb.put(src); - src.limit(src.capacity()); - } - - bb.position(bb.limit()); - bb.flip(); - - argbFrame.releaseFrame(); - image.setUpdateNeeded(); - } catch (Exception exc) { - - exc.printStackTrace(); - System.exit(0); - } - } - }; - } - - /** - * @return aspect ratio of played movie (width/height) - for widescreen movies it will be in - * range of 1.8-2.9 - */ - public float getAspectRatio() { - return aspectRatio; - } - - /** - * Corner texture coordinates of valid movie area - */ - public Vector2f getBottomRightCorner() { - return bottomRightCorner; - } - - public Texture2D getTexture() { - if (texture == null) { - texture = new Texture2D(emptyImage); - cPlayer.getVideoRenderControl().addVideoRendererListener(vrListener); - } - return texture; - } - - /** - * Corner texture coordinates of valid movie area - */ - public Vector2f getUpperLeftCorner() { - return upperLeftCorner; - } - - /** - * Sets the color which should be used for letterbox fill. It is annoying red by default to help - * with debugging. - */ - public void setLetterboxColor(ColorRGBA letterboxColor) { - this.letterboxColor.set(letterboxColor); - } - - public enum LetterboxMode { - /** - * This mode uses entire texture including some garbage data on right and bottom side; - * always utilize together with corner data to cut the texture and aspectRatio to size it - * properly - */ - RAW_SQUARE, - /** - * THis mode uses entire texture, but fills it only with valid data. Proper texture - * coordinates will be always 0-1, but you should use aspectRatio for it to look proper - */ - VALID_SQUARE, - /** - * This is cinema-like presentation, with borders on shorter sides (mostly top and down). - * Displaying it on square object will make it look proper aspectRatio-wise. You can utilize - * corner data to display only interesting part of the movie. - */ - VALID_LETTERBOX - } - -} diff --git a/src/main/java/com/jme3x/jfx/util/JFXUtils.java b/src/main/java/com/jme3x/jfx/util/JFXUtils.java deleted file mode 100755 index dbaabe6..0000000 --- a/src/main/java/com/jme3x/jfx/util/JFXUtils.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.jme3x.jfx.util; - -import com.jme3.app.Application; -import com.jme3.system.AppSettings; -import com.jme3.system.JmeContext; -import com.jme3.system.lwjgl.LwjglWindow; -import com.jme3x.jfx.util.os.OperatingSystem; - -import org.lwjgl.BufferUtils; -import org.lwjgl.glfw.GLFW; - -import java.awt.*; -import java.nio.IntBuffer; -import java.util.HashMap; -import java.util.Map; - -/** - * Set of methods for scrap work JFX. - * - * @author Ronn - */ -public class JFXUtils { - - private static final Map OFFSET_MAPPING = new HashMap<>(); - - static { - OFFSET_MAPPING.put("Ubuntu", new Point(0, 0)); - } - - private static final ThreadLocal LOCAL_FIRST_INT_BUFFER = new ThreadLocal() { - - @Override - protected IntBuffer initialValue() { - return BufferUtils.createIntBuffer(1); - } - }; - - private static final ThreadLocal LOCAL_SECOND_INT_BUFFER = new ThreadLocal() { - - @Override - protected IntBuffer initialValue() { - return BufferUtils.createIntBuffer(1); - } - }; - - /** - * Getting the size of the window decorations in the system. - */ - public static final Point getWindowDecorationSize() { - - final OperatingSystem system = new OperatingSystem(); - final String distribution = system.getDistribution(); - - if (OFFSET_MAPPING.containsKey(distribution)) { - return OFFSET_MAPPING.get(distribution); - } - - for (final Map.Entry entry : OFFSET_MAPPING.entrySet()) { - - final String key = entry.getKey(); - - if (distribution.startsWith(key)) { - return entry.getValue(); - } - } - - return new Point(0, 0); - } - - public static int getX(final JmeContext context) { - - final LwjglWindow lwjglContext = (LwjglWindow) context; - final long windowHandle = lwjglContext.getWindowHandle(); - - final IntBuffer x = LOCAL_FIRST_INT_BUFFER.get(); - final IntBuffer y = LOCAL_SECOND_INT_BUFFER.get(); - x.clear(); - y.clear(); - - GLFW.glfwGetWindowPos(windowHandle, x, y); - - return x.get(0); - } - - public static int getY(final JmeContext context) { - - final LwjglWindow lwjglContext = (LwjglWindow) context; - final long windowHandle = lwjglContext.getWindowHandle(); - - final IntBuffer x = LOCAL_FIRST_INT_BUFFER.get(); - final IntBuffer y = LOCAL_SECOND_INT_BUFFER.get(); - x.clear(); - y.clear(); - - GLFW.glfwGetWindowPos(windowHandle, x, y); - - return y.get(0); - } - - public static int getWidth(final JmeContext context) { - - final LwjglWindow lwjglContext = (LwjglWindow) context; - final long windowHandle = lwjglContext.getWindowHandle(); - - final IntBuffer width = LOCAL_FIRST_INT_BUFFER.get(); - final IntBuffer height = LOCAL_SECOND_INT_BUFFER.get(); - width.clear(); - height.clear(); - - GLFW.glfwGetWindowSize(windowHandle, width, height); - - return width.get(0); - } - - public static int getHeight(final JmeContext context) { - - final LwjglWindow lwjglContext = (LwjglWindow) context; - final long windowHandle = lwjglContext.getWindowHandle(); - - final IntBuffer width = LOCAL_FIRST_INT_BUFFER.get(); - final IntBuffer height = LOCAL_SECOND_INT_BUFFER.get(); - width.clear(); - height.clear(); - - GLFW.glfwGetWindowSize(windowHandle, width, height); - - return height.get(0); - } - - public static boolean isFullscreen(final JmeContext jmeContext) { - final AppSettings settings = jmeContext.getSettings(); - return settings.isFullscreen(); - } - - public static void requestFocus(final Application application) { - final LwjglWindow lwjglContext = (LwjglWindow) application.getContext(); - GLFW.glfwShowWindow(lwjglContext.getWindowHandle()); - } -} diff --git a/src/test/java/com/jme3x/jfx/FalseColorGui.frag b/src/test/java/com/jme3x/jfx/FalseColorGui.frag deleted file mode 100755 index c16ea0a..0000000 --- a/src/test/java/com/jme3x/jfx/FalseColorGui.frag +++ /dev/null @@ -1,25 +0,0 @@ -vec2 resolution = vec2(1.0,1.0); - -uniform float g_Time; -uniform sampler2D m_Texture; -varying vec2 texCoord; - -float rand(vec2 co) { - return fract(sin(dot(co.xy ,vec2(12.9898,708.233))) * 403758.5453); -} - -void main() { - vec2 q1 = texCoord.xy / resolution.xy; - vec2 uv = 0.5 + (q1-0.5)*(0.98 + 0.006);//*sin(0.9)); - vec3 col = texture2D(m_Texture,texCoord).xyz; - - col = clamp(col*0.5+0.5*col*col*1.2,0.0,1.0); - col *= 0.6 + 0.4*16.0*uv.x*uv.y*(1.0-uv.x)*(1.0-uv.y); - col *= vec3(0.9,1.0,0.7); - col *= 0.8+0.2*sin(10.0*g_Time+uv.y*512.0); - col *= 1.0-0.9*rand(vec2(g_Time*100.0, tan(g_Time))); - float comp = smoothstep( 0.2, 0.7, sin(g_Time) ); - - gl_FragColor = vec4(col,texture2D(m_Texture,texCoord).w); -} - diff --git a/src/test/java/com/jme3x/jfx/FalseColorGui.j3md b/src/test/java/com/jme3x/jfx/FalseColorGui.j3md deleted file mode 100755 index 768e2c6..0000000 --- a/src/test/java/com/jme3x/jfx/FalseColorGui.j3md +++ /dev/null @@ -1,23 +0,0 @@ -MaterialDef Default GUI { - - MaterialParameters { - Texture2D Texture - } - - Technique { - VertexShader GLSL100: com/jme3x/jfx/FalseColorGui.vert - FragmentShader GLSL100: com/jme3x/jfx/FalseColorGui.frag - - WorldParameters { - WorldViewProjectionMatrix - Time - } - - Defines { - } - } - - Technique { - } - -} \ No newline at end of file diff --git a/src/test/java/com/jme3x/jfx/FalseColorGui.vert b/src/test/java/com/jme3x/jfx/FalseColorGui.vert deleted file mode 100755 index b5442ce..0000000 --- a/src/test/java/com/jme3x/jfx/FalseColorGui.vert +++ /dev/null @@ -1,15 +0,0 @@ -uniform mat4 g_WorldViewProjectionMatrix; -uniform vec4 m_Color; - -attribute vec3 inPosition; - -attribute vec2 inTexCoord; -varying vec2 texCoord; - -varying vec4 color; - -void main() { - gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0); - texCoord = inTexCoord; - color = m_Color; -} \ No newline at end of file diff --git a/src/test/java/com/jme3x/jfx/TestDisplayInImageView.fxml b/src/test/java/com/jme3x/jfx/TestDisplayInImageView.fxml deleted file mode 100644 index 13f0506..0000000 --- a/src/test/java/com/jme3x/jfx/TestDisplayInImageView.fxml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - -
- diff --git a/src/test/java/com/jme3x/jfx/TestDisplayInImageView.java b/src/test/java/com/jme3x/jfx/TestDisplayInImageView.java deleted file mode 100644 index c44519a..0000000 --- a/src/test/java/com/jme3x/jfx/TestDisplayInImageView.java +++ /dev/null @@ -1,193 +0,0 @@ -package com.jme3x.jfx; - -import com.jme3.app.SimpleApplication; -import com.jme3.asset.AssetManager; -import com.jme3.light.DirectionalLight; -import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Vector3f; -import com.jme3.renderer.queue.RenderQueue.Bucket; -import com.jme3.scene.Geometry; -import com.jme3.scene.Node; -import com.jme3.scene.shape.Box; -import com.jme3.scene.shape.Sphere; -import com.jme3.system.AppSettings; -import com.jme3.util.TangentBinormalGenerator; -import com.jme3x.jfx.injfx.JmeForImageView; - -import java.net.URL; - -import javafx.application.Application; -import javafx.application.Platform; -import javafx.event.EventHandler; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.fxml.JavaFXBuilderFactory; -import javafx.scene.Scene; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ColorPicker; -import javafx.scene.control.Label; -import javafx.scene.control.Slider; -import javafx.scene.image.ImageView; -import javafx.scene.layout.Pane; -import javafx.scene.layout.Region; -import javafx.scene.paint.Color; -import javafx.stage.Stage; -import javafx.stage.WindowEvent; - -public class TestDisplayInImageView extends Application { - - static void bindOtherControls(JmeForImageView jme, Controller controller) { - controller.bgColor.valueProperty().addListener((ov, o, n) -> { - jme.enqueue((jmeApp) -> { - jmeApp.getViewPort().setBackgroundColor(new ColorRGBA((float) n.getRed(), (float) n.getGreen(), (float) n.getBlue(), (float) n.getOpacity())); - return null; - }); - }); - controller.bgColor.setValue(Color.LIGHTGRAY); - - controller.showStats.selectedProperty().addListener((ov, o, n) -> { - jme.enqueue((jmeApp) -> { - jmeApp.setDisplayStatView(n); - jmeApp.setDisplayFps(n); - return null; - }); - }); - controller.showStats.setSelected(!controller.showStats.isSelected()); - - controller.fpsReq.valueProperty().addListener((ov, o, n) -> { - jme.enqueue((jmeApp) -> { - AppSettings settings = new AppSettings(false); - settings.setFullscreen(false); - settings.setUseInput(false); - settings.setFrameRate(n.intValue()); - settings.setCustomRenderer(com.jme3x.jfx.injfx.JmeContextOffscreenSurface.class); - jmeApp.setSettings(settings); - jmeApp.restart(); - return null; - }); - }); - controller.fpsReq.setValue(30); - } - - /** - * Create a similar scene to Tutorial "Hello Material" but without texture - * http://hub.jmonkeyengine.org/wiki/doku.php/jme3:beginner:hello_material - * - * @param jmeApp the application where to create a Scene - */ - static boolean createScene(SimpleApplication jmeApp) { - Node rootNode = jmeApp.getRootNode(); - AssetManager assetManager = jmeApp.getAssetManager(); - - /** A simple textured cube -- in good MIP map quality. */ - Box cube1Mesh = new Box(1f, 1f, 1f); - Geometry cube1Geo = new Geometry("My Textured Box", cube1Mesh); - cube1Geo.setLocalTranslation(new Vector3f(-3f, 1.1f, 0f)); - Material cube1Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - cube1Mat.setColor("Color", ColorRGBA.Blue); - cube1Geo.setMaterial(cube1Mat); - rootNode.attachChild(cube1Geo); - - /** A translucent/transparent texture, similar to a window frame. */ - Box cube2Mesh = new Box(1f, 1f, 0.01f); - Geometry cube2Geo = new Geometry("window frame", cube2Mesh); - Material cube2Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - cube2Mat.setColor("Color", ColorRGBA.Brown); - cube2Geo.setQueueBucket(Bucket.Transparent); - cube2Geo.setMaterial(cube2Mat); - rootNode.attachChild(cube2Geo); - - /** A bumpy rock with a shiny light effect.*/ - Sphere sphereMesh = new Sphere(32, 32, 2f); - Geometry sphereGeo = new Geometry("Shiny rock", sphereMesh); - sphereMesh.setTextureMode(Sphere.TextureMode.Projected); // better quality on spheres - TangentBinormalGenerator.generate(sphereMesh); // for lighting effect - Material sphereMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); - sphereMat.setBoolean("UseMaterialColors", true); - sphereMat.setColor("Diffuse", ColorRGBA.Pink); - sphereMat.setColor("Specular", ColorRGBA.White); - sphereMat.setFloat("Shininess", 64f); // [0,128] - sphereGeo.setMaterial(sphereMat); - sphereGeo.setLocalTranslation(0, 2, -2); // Move it a bit - sphereGeo.rotate(1.6f, 0, 0); // Rotate it a bit - rootNode.attachChild(sphereGeo); - - /** Must add a light to make the lit object visible! */ - DirectionalLight sun = new DirectionalLight(); - sun.setDirection(new Vector3f(1, 0, -2).normalizeLocal()); - sun.setColor(ColorRGBA.White); - rootNode.addLight(sun); - return true; - } - - public static void main(String[] args) { - launch(args); - } - - @Override - public void start(Stage stage) throws Exception { - final FXMLLoader fxmlLoader = new FXMLLoader(); - final URL location = Thread.currentThread().getContextClassLoader().getResource(this.getClass().getCanonicalName().replace('.', '/') + ".fxml"); - fxmlLoader.setLocation(location); - //final ResourceBundle defaultRessources = fxmlLoader.getResources(); - //fxmlLoader.setResources(this.addCustomRessources(defaultRessources)); - fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory()); - final Region root = fxmlLoader.load(location.openStream()); - Controller controller = fxmlLoader.getController(); - - JmeForImageView jme = new JmeForImageView(); - jme.bind(controller.image); - - stage.setOnCloseRequest(new EventHandler() { - public void handle(WindowEvent e) { - jme.stop(true); - } - }); - - bindOtherControls(jme, controller); - jme.enqueue(TestDisplayInImageView::createScene); - - Scene scene = new Scene(root, 600, 400); - stage.setTitle(this.getClass().getSimpleName()); - stage.setScene(scene); - stage.show(); - } - - @Override - public void stop() throws Exception { - Platform.exit(); - } - - public static class Controller { - - @FXML - public ImageView image; - - @FXML - public ColorPicker bgColor; - - @FXML - public CheckBox showStats; - - @FXML - private Label fpsLabel; - - @FXML - public Slider fpsReq; - - @FXML - public void initialize() { - //To resize image when parent is resize - //image is wrapped into a "VBOX" or "HBOX" to allow resize smaller - //see http://stackoverflow.com/questions/15951284/javafx-image-resizing - Pane p = (Pane) image.getParent(); - image.fitHeightProperty().bind(p.heightProperty()); - image.fitWidthProperty().bind(p.widthProperty()); - - fpsReq.valueProperty().addListener((ov, o, n) -> fpsLabel.setText(String.format("fps : %4d", n.intValue()))); - image.setPreserveRatio(false); - } - - } -} diff --git a/src/test/java/com/jme3x/jfx/TestMovie.java b/src/test/java/com/jme3x/jfx/TestMovie.java deleted file mode 100644 index 8f8427a..0000000 --- a/src/test/java/com/jme3x/jfx/TestMovie.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.jme3x.jfx; - -import com.jme3.app.SimpleApplication; -import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Vector3f; -import com.jme3.scene.Geometry; -import com.jme3.scene.shape.Quad; -import com.jme3x.jfx.media.TextureMovie; -import com.jme3x.jfx.media.TextureMovie.LetterboxMode; -import com.sun.javafx.application.PlatformImpl; - -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.scene.media.Media; -import javafx.scene.media.MediaException; -import javafx.scene.media.MediaPlayer; - -public class TestMovie extends SimpleApplication { - - public static void main(final String[] args) { - - PlatformImpl.startup(() -> { - }); - - final TestMovie app = new TestMovie(); - app.start(); - } - - private TextureMovie textureMovie; - private MediaPlayer mp; - - @Override - public void destroy() { - super.destroy(); - this.mp.stop(); - PlatformImpl.exit(); - } - - @Override - public void simpleInitApp() { - - final Media media = new Media("http://download.oracle.com/otndocs/products/javafx/oow2010-2.flv"); - media.errorProperty().addListener(new ChangeListener() { - - @Override - public void changed(final ObservableValue observable, final MediaException oldValue, final MediaException newValue) { - newValue.printStackTrace(); - } - }); - this.mp = new MediaPlayer(media); - this.mp.play(); - - this.textureMovie = new TextureMovie(this.mp, LetterboxMode.VALID_LETTERBOX); - this.textureMovie.setLetterboxColor(ColorRGBA.Black); - - final Geometry screen1 = new Geometry("Screen1", new Quad(20, 20)); - - final Material s1mat = new Material(this.assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - s1mat.setTexture("ColorMap", this.textureMovie.getTexture()); - screen1.setMaterial(s1mat); - this.rootNode.attachChild(screen1); - - this.cam.setLocation(new Vector3f(10, 10, 15)); - - } - -} diff --git a/src/test/java/com/jme3x/jfx/Testcontroller.java b/src/test/java/com/jme3x/jfx/Testcontroller.java deleted file mode 100755 index c793361..0000000 --- a/src/test/java/com/jme3x/jfx/Testcontroller.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.jme3x.jfx; - -import javafx.fxml.FXML; -import javafx.scene.layout.StackPane; -import javafx.scene.web.WebView; - -public class Testcontroller { - @FXML - private StackPane rootObject; - - @FXML - private WebView website; - - @FXML - public void initialize() { - this.website.getEngine().load("http://acid3.acidtests.org/"); - } -} diff --git a/src/test/java/com/jme3x/jfx/loading_screen.fxml b/src/test/java/com/jme3x/jfx/loading_screen.fxml deleted file mode 100755 index 1800acc..0000000 --- a/src/test/java/com/jme3x/jfx/loading_screen.fxml +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -
- -
- - -
- - - - - - - - - - - - - -
-
-
-
-
- - -
- -
-
-
- - - -
- -
- - - -
- - - - - - - - - - - -
-
diff --git a/src/test/java/com/jme3x/jfx/test.jpg b/src/test/java/com/jme3x/jfx/test.jpg deleted file mode 100644 index ceaacb6..0000000 Binary files a/src/test/java/com/jme3x/jfx/test.jpg and /dev/null differ diff --git a/test-src/com/jme3x/jfx/HelloInput.java b/test-src/com/jme3x/jfx/HelloInput.java new file mode 100644 index 0000000..25de1de --- /dev/null +++ b/test-src/com/jme3x/jfx/HelloInput.java @@ -0,0 +1,144 @@ +/* + * 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.jme3x.jfx; + +import com.jme3.app.SimpleApplication; +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.AnalogListener; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.input.controls.MouseButtonTrigger; +import com.jme3.light.DirectionalLight; +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.NativeLibraryLoader; + +/** + * Sample 5 - how to map keys and mousebuttons to actions + */ +public class HelloInput extends SimpleApplication { + + public static void main(String[] args) { + HelloInput app = new HelloInput(); + final AppSettings settings = new AppSettings(true); + settings.setHeight(600); + settings.setWidth(600); + settings.setUseInput(true); + app.setSettings(settings); + app.setShowSettings(false); + app.start(); + } + + protected Geometry player; + Boolean isRunning = true; + + @Override + public void start() { + + if ("LWJGL".equals(settings.getAudioRenderer())) { + NativeLibraryLoader.loadNativeLibrary("openal-lwjgl3", true); + } + + NativeLibraryLoader.loadNativeLibrary("lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("glfw-lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("jemalloc-lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("jinput", true); + NativeLibraryLoader.loadNativeLibrary("jinput-dx8", true); + + super.start(); + } + + @Override + public void simpleInitApp() { + Box b = new Box(1, 1, 1); + player = new Geometry("Player", b); + Material mat = new Material(assetManager, "Common/MatDefs/Light/PBRLighting.j3md"); + // mat.setColor("Color", ColorRGBA.Blue); + player.setMaterial(mat); + rootNode.attachChild(player); + rootNode.addLight(new DirectionalLight()); + initKeys(); // load my custom keybinding + } + + /** + * Custom Keybinding: Map named actions to inputs. + */ + private void initKeys() { + /** You can map one or several inputs to one named mapping. */ + inputManager.addMapping("Pause", new KeyTrigger(keyInput.KEY_P)); + inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_J)); + inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_K)); + inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE), // spacebar! + new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); // left click! + /** Add the named mappings to the action listeners. */ + inputManager.addListener(actionListener, "Pause"); + inputManager.addListener(analogListener, "Left", "Right", "Rotate"); + } + + /** + * Use this listener for KeyDown/KeyUp events + */ + private ActionListener actionListener = new ActionListener() { + public void onAction(String name, boolean keyPressed, float tpf) { + if (name.equals("Pause") && !keyPressed) { + isRunning = !isRunning; + } + } + }; + + /** + * Use this listener for continuous events + */ + private AnalogListener analogListener = new AnalogListener() { + public void onAnalog(String name, float value, float tpf) { + if (isRunning) { + if (name.equals("Rotate")) { + player.rotate(0, value, 0); + } + if (name.equals("Right")) { + player.move((new Vector3f(value, 0, 0))); + } + if (name.equals("Left")) { + player.move(new Vector3f(-value, 0, 0)); + } + } else { + System.out.println("Press P to unpause."); + } + } + }; + +} diff --git a/test-src/com/jme3x/jfx/TestContext.java b/test-src/com/jme3x/jfx/TestContext.java new file mode 100644 index 0000000..93875f5 --- /dev/null +++ b/test-src/com/jme3x/jfx/TestContext.java @@ -0,0 +1,54 @@ +package com.jme3x.jfx; + +import com.jme3.system.AppSettings; +import com.jme3.system.NativeLibraryLoader; +import com.jme3.system.lwjgl.LwjglDisplay; + +import rlib.logging.Logger; +import rlib.logging.LoggerManager; + +/** + * Created by ronn on 25.07.16. + */ +public class TestContext extends LwjglDisplay { + + protected static final Logger LOGGER = LoggerManager.getLogger(TestContext.class); + + /** + * Игровой поток рендера экрана. + */ + private Thread thread; + + @Override + public void create(final boolean waitFor) { + + if ("LWJGL".equals(settings.getAudioRenderer())) { + NativeLibraryLoader.loadNativeLibrary("openal-lwjgl3", true); + } + + NativeLibraryLoader.loadNativeLibrary("lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("glfw-lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("jemalloc-lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("jinput", true); + NativeLibraryLoader.loadNativeLibrary("jinput-dx8", true); + + if (created.get()) return; + + thread = new Thread(this); + thread.setPriority(Thread.MAX_PRIORITY); + thread.setName("LWJGL Renderer Thread"); + thread.start(); + + if (waitFor) waitFor(true); + } + + public Thread getThread() { + return thread; + } + + @Override + protected void createContext(final AppSettings settings) { + settings.setRenderer(AppSettings.LWJGL_OPENGL3); + super.createContext(settings); + } +} diff --git a/test-src/com/jme3x/jfx/TestJmeToJFXCanvas.java b/test-src/com/jme3x/jfx/TestJmeToJFXCanvas.java new file mode 100644 index 0000000..6c30fa8 --- /dev/null +++ b/test-src/com/jme3x/jfx/TestJmeToJFXCanvas.java @@ -0,0 +1,137 @@ +package com.jme3x.jfx; + +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.AnalogListener; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.input.controls.MouseButtonTrigger; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +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.NativeLibraryLoader; +import com.jme3x.jfx.injfx.JmeToJFXApplication; +import com.jme3x.jfx.injfx.JmeToJFXIntegrator; + +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.canvas.Canvas; +import javafx.scene.control.Button; +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; + +/** + * Created by ronn on 03.12.16. + */ +public class TestJmeToJFXCanvas extends Application { + + public static void main(final String[] args) { + launch(args); + } + + @Override + public void start(final Stage stage) throws Exception { + + final Canvas canvas = new Canvas(); + final Button button = new Button("BUTTON"); + final StackPane stackPane = new StackPane(canvas, button); + final Scene scene = new Scene(stackPane, 600, 600); + + canvas.widthProperty().bind(stackPane.widthProperty()); + canvas.heightProperty().bind(stackPane.heightProperty()); + + stage.setTitle("Test"); + stage.setScene(scene); + stage.show(); + stage.setOnCloseRequest(event -> System.exit(0)); + + final JmeToJFXApplication application = makeJmeApplication(stage, 40); + + JmeToJFXIntegrator.startAndBind(application, canvas, Thread::new); + } + + private static JmeToJFXApplication makeJmeApplication(Stage stage, int framerate) { + final AppSettings settings = new AppSettings(true); + JmeToJFXIntegrator.prepareSettings(settings, framerate); + + JmeToJFXApplication app = new JmeToJFXApplication() { + + protected Geometry player; + Boolean isRunning = true; + + @Override + public void start() { + + if ("LWJGL".equals(settings.getAudioRenderer())) { + NativeLibraryLoader.loadNativeLibrary("openal-lwjgl3", true); + } + + NativeLibraryLoader.loadNativeLibrary("lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("glfw-lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("jemalloc-lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("jinput", true); + NativeLibraryLoader.loadNativeLibrary("jinput-dx8", true); + + super.start(); + } + + @Override + public void simpleInitApp() { + Box b = new Box(1, 1, 1); + player = new Geometry("Player", b); + Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat.setColor("Color", ColorRGBA.Blue); + player.setMaterial(mat); + rootNode.attachChild(player); + initKeys(); // load my custom keybinding + } + + /** Custom Keybinding: Map named actions to inputs. */ + private void initKeys() { + /** You can map one or several inputs to one named mapping. */ + inputManager.addMapping("Pause", new KeyTrigger(keyInput.KEY_P)); + inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_J)); + inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_K)); + inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE), // spacebar! + new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); // left click! + /** Add the named mappings to the action listeners. */ + inputManager.addListener(actionListener, "Pause"); + inputManager.addListener(analogListener, "Left", "Right", "Rotate"); + } + + /** Use this listener for KeyDown/KeyUp events */ + private ActionListener actionListener = new ActionListener() { + public void onAction(String name, boolean keyPressed, float tpf) { + if (name.equals("Pause") && !keyPressed) { + isRunning = !isRunning; + } + } + }; + + /** Use this listener for continuous events */ + private AnalogListener analogListener = new AnalogListener() { + public void onAnalog(String name, float value, float tpf) { + if (isRunning) { + if (name.equals("Rotate")) { + player.rotate(0, value, 0); + } + if (name.equals("Right")) { + player.move((new Vector3f(value, 0, 0))); + } + if (name.equals("Left")) { + player.move(new Vector3f(-value, 0, 0)); + } + } else { + System.out.println("Press P to unpause."); + } + } + }; + }; + app.setSettings(settings); + app.setShowSettings(false); + return app; + } +} diff --git a/test-src/com/jme3x/jfx/TestJmeToJFXImageView.java b/test-src/com/jme3x/jfx/TestJmeToJFXImageView.java new file mode 100644 index 0000000..81dffbf --- /dev/null +++ b/test-src/com/jme3x/jfx/TestJmeToJFXImageView.java @@ -0,0 +1,137 @@ +package com.jme3x.jfx; + +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.AnalogListener; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.input.controls.MouseButtonTrigger; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +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.NativeLibraryLoader; +import com.jme3x.jfx.injfx.JmeToJFXApplication; +import com.jme3x.jfx.injfx.JmeToJFXIntegrator; + +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.image.ImageView; +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; + +/** + * Created by ronn on 03.12.16. + */ +public class TestJmeToJFXImageView extends Application { + + public static void main(final String[] args) { + launch(args); + } + + @Override + public void start(final Stage stage) throws Exception { + + final ImageView imageView = new ImageView(); + final Button button = new Button("BUTTON"); + final StackPane stackPane = new StackPane(imageView, button); + final Scene scene = new Scene(stackPane, 600, 600); + + imageView.fitWidthProperty().bind(stackPane.widthProperty()); + imageView.fitHeightProperty().bind(stackPane.heightProperty()); + + stage.setTitle("Test"); + stage.setScene(scene); + stage.show(); + stage.setOnCloseRequest(event -> System.exit(0)); + + final JmeToJFXApplication application = makeJmeApplication(stage, 80); + + JmeToJFXIntegrator.startAndBind(application, imageView, Thread::new); + } + + private static JmeToJFXApplication makeJmeApplication(Stage stage, int framerate) { + final AppSettings settings = new AppSettings(true); + JmeToJFXIntegrator.prepareSettings(settings, 60); + + JmeToJFXApplication app = new JmeToJFXApplication() { + + protected Geometry player; + Boolean isRunning = true; + + @Override + public void start() { + + if ("LWJGL".equals(settings.getAudioRenderer())) { + NativeLibraryLoader.loadNativeLibrary("openal-lwjgl3", true); + } + + NativeLibraryLoader.loadNativeLibrary("lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("glfw-lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("jemalloc-lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("jinput", true); + NativeLibraryLoader.loadNativeLibrary("jinput-dx8", true); + + super.start(); + } + + @Override + public void simpleInitApp() { + Box b = new Box(1, 1, 1); + player = new Geometry("Player", b); + Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat.setColor("Color", ColorRGBA.Blue); + player.setMaterial(mat); + rootNode.attachChild(player); + initKeys(); // load my custom keybinding + } + + /** Custom Keybinding: Map named actions to inputs. */ + private void initKeys() { + /** You can map one or several inputs to one named mapping. */ + inputManager.addMapping("Pause", new KeyTrigger(keyInput.KEY_P)); + inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_J)); + inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_K)); + inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE), // spacebar! + new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); // left click! + /** Add the named mappings to the action listeners. */ + inputManager.addListener(actionListener, "Pause"); + inputManager.addListener(analogListener, "Left", "Right", "Rotate"); + } + + /** Use this listener for KeyDown/KeyUp events */ + private ActionListener actionListener = new ActionListener() { + public void onAction(String name, boolean keyPressed, float tpf) { + if (name.equals("Pause") && !keyPressed) { + isRunning = !isRunning; + } + } + }; + + /** Use this listener for continuous events */ + private AnalogListener analogListener = new AnalogListener() { + public void onAnalog(String name, float value, float tpf) { + if (isRunning) { + if (name.equals("Rotate")) { + player.rotate(0, value, 0); + } + if (name.equals("Right")) { + player.move((new Vector3f(value, 0, 0))); + } + if (name.equals("Left")) { + player.move(new Vector3f(-value, 0, 0)); + } + } else { + System.out.println("Press P to unpause."); + } + } + }; + }; + app.setSettings(settings); + app.setShowSettings(false); + return app; + } +} diff --git a/test-src/com/jme3x/jfx/TestOffscreenContext.java b/test-src/com/jme3x/jfx/TestOffscreenContext.java new file mode 100644 index 0000000..c076b7d --- /dev/null +++ b/test-src/com/jme3x/jfx/TestOffscreenContext.java @@ -0,0 +1,54 @@ +package com.jme3x.jfx; + +import com.jme3.system.AppSettings; +import com.jme3.system.NativeLibraryLoader; +import com.jme3.system.lwjgl.LwjglDisplay; + +import rlib.logging.Logger; +import rlib.logging.LoggerManager; + +/** + * Created by ronn on 25.07.16. + */ +public class TestOffscreenContext extends LwjglDisplay { + + protected static final Logger LOGGER = LoggerManager.getLogger(TestOffscreenContext.class); + + /** + * Игровой поток рендера экрана. + */ + private Thread thread; + + @Override + public void create(final boolean waitFor) { + + if ("LWJGL".equals(settings.getAudioRenderer())) { + NativeLibraryLoader.loadNativeLibrary("openal-lwjgl3", true); + } + + NativeLibraryLoader.loadNativeLibrary("lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("glfw-lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("jemalloc-lwjgl3", true); + NativeLibraryLoader.loadNativeLibrary("jinput", true); + NativeLibraryLoader.loadNativeLibrary("jinput-dx8", true); + + if (created.get()) return; + + thread = new Thread(this); + thread.setPriority(Thread.MAX_PRIORITY); + thread.setName("LWJGL Renderer Thread"); + thread.start(); + + if (waitFor) waitFor(true); + } + + public Thread getThread() { + return thread; + } + + @Override + protected void createContext(final AppSettings settings) { + settings.setRenderer(AppSettings.LWJGL_OPENGL3); + super.createContext(settings); + } +}