* Updated OpenVR implementation to 1.0.9

User can specify external OpenVR library to load with
openvr.library.path system property.
Usage: java -Dopenvr.library.name=my_path_to_library MyApp

Removed reference to OCCULUS VR and OpenVR from VRAppstate as this class
is generic and does not have to be linked to specific implementation.

VRMouseManager can be buggous using OSVR or Occulus VR. 

Refactored VR implementation packages in order to separate all available
implementation. Modifying or adding implementation should no more impact
other ones. 

Renamed some classes in order to be uniform

* Sample and VR mouse manager update

Adding sample for VR AppState

Added OSVRMouseManager in order to handle VRMouseManager for OSVR
environment

Added OcculusMouseManager in order to handle VRMouseManager for Oculus
environment

Changed OpenVRMouseManager reference within VRAppState and VREnvironment
into VRMouseManager

* Revert previous commit as jme3-examples does not handle java8 and lwjgl3

* Adding AWT component rendering within app state

Adding AWT component rendering capabilities for any JMonkeyEngine
renderer (works with LWJGL 3.1.x and compatible with other
implementations)

This implementation differs from the previous one as it's relies on
AppState (compatible with all JME application) and as it use only
produces framebuffer.

With this implementation, it is possible to use AWT component ad
rendering target and so to integrate JMonkey rendering within AWT /
Swing application. This capability was not available with LWJGL3 as the
version 3.1.x does not provide anymore AWT link.

* AWTComponentAppState update

Added Update within AWTComponentAppState and added getter and setter for
transfer mode within AWTFramePRocessor

* Removing Java 8 related stuff
accellbaker
Julien Seinturier 6 years ago committed by empirephoenix
parent 7363662f21
commit 835fbd7957
  1. 135
      jme3-desktop/src/main/java/com/jme3/app/state/AWTComponentAppState.java
  2. 131
      jme3-desktop/src/main/java/com/jme3/input/AWTInput.java
  3. 237
      jme3-desktop/src/main/java/com/jme3/input/AWTKeyInput.java
  4. 239
      jme3-desktop/src/main/java/com/jme3/input/AWTMouseInput.java
  5. 391
      jme3-desktop/src/main/java/com/jme3/system/AWTComponentRenderer.java
  6. 234
      jme3-desktop/src/main/java/com/jme3/system/AWTContext.java
  7. 659
      jme3-desktop/src/main/java/com/jme3/system/AWTFrameProcessor.java
  8. 114
      jme3-desktop/src/main/java/com/jme3/system/AWTTaskExecutor.java

@ -0,0 +1,135 @@
/*
* Copyright (c) 2009-2018 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.app.state;
import java.awt.Component;
import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.system.AWTFrameProcessor;
import com.jme3.system.AWTTaskExecutor;
/**
* An app state dedicated to the rendering of a JMonkey application within an AWT component.
* @author Julien Seinturier - COMEX SA - <a href="http://www.seinturier.fr">http://www.seinturier.fr</a>
*/
public class AWTComponentAppState extends AbstractAppState {
private final AWTTaskExecutor executor = AWTTaskExecutor.getInstance();
private Component component = null;
private AWTFrameProcessor processor = null;
private AWTFrameProcessor.TransferMode transferMode = AWTFrameProcessor.TransferMode.ON_CHANGES;
@Override
public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app);
}
@Override
public void stateAttached(final AppStateManager stateManager) {
processor = new AWTFrameProcessor();
processor.setTransferMode(transferMode);
AWTTaskExecutor.getInstance().addToExecute(new Runnable() {
@Override
public void run() {
processor.bind(component, stateManager.getApplication(), stateManager.getApplication().getViewPort());
}
});
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
}
@Override
public void update(float tpf) {
executor.execute();
super.update(tpf);
}
@Override
public void cleanup() {
super.cleanup();
}
/**
* Create a new app state dedicated to the rendering of a JMonkey application within the given AWT <code>component</code>.
* @param component the component that is used as rendering output.
*/
public AWTComponentAppState(Component component) {
this.component = component;
}
/**
* Get the AWT component that is used as rendering output.
* @return the AWT component that is used as rendering output.
* @see #setComponent(Component)
*/
public Component getComponent() {
return component;
}
/**
* Set the AWT component that is used as rendering output.
* @param component the AWT component that is used as rendering output.
* @see #getComponent()
*/
public void setComponent(Component component) {
this.component = component;
}
/**
* Get the {@link AWTFrameProcessor.TransferMode transfer mode} that is used by the underlying frame processor.
* @return the {@link AWTFrameProcessor.TransferMode transfer mode} that is used by the underlying frame processor.
* @see #setTransferMode(com.jme3.system.AWTFrameProcessor.TransferMode)
*/
public AWTFrameProcessor.TransferMode getTransferMode(){
return transferMode;
}
/**
* Set the {@link AWTFrameProcessor.TransferMode transfer mode} that is used by the underlying frame processor.
* @param mode the {@link AWTFrameProcessor.TransferMode transfer mode} that is used by the underlying frame processor.
* @see #getTransferMode()
*/
public void setTransferMode(AWTFrameProcessor.TransferMode mode) {
this.transferMode = mode;
}
}

@ -0,0 +1,131 @@
/*
* Copyright (c) 2009-2018 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.input;
import java.awt.Component;
import java.util.Objects;
import com.jme3.app.Application;
import com.jme3.input.Input;
import com.jme3.input.RawInputListener;
import com.jme3.system.AWTContext;
import com.jme3.system.AWTTaskExecutor;
/**
* The implementation of the {@link Input} dedicated to AWT {@link Component component}.
* <p>
* This class is based on the <a href="http://www.oracle.com/technetwork/java/javase/overview/javafx-overview-2158620.html">JavaFX</a> original code provided by Alexander Brui (see <a href="https://github.com/JavaSaBr/JME3-JFX">JME3-FX</a>)
* </p>
* @author Julien Seinturier - COMEX SA - <a href="http://www.seinturier.fr">http://www.seinturier.fr</a>
* @author Alexander Brui (JavaSaBr)
*/
public class AWTInput implements Input {
protected static final AWTTaskExecutor EXECUTOR = AWTTaskExecutor.getInstance();
/**
* The context.
*/
protected final AWTContext context;
/**
* The raw listener.
*/
protected RawInputListener listener;
/**
* The input node.
*/
protected Component component;
/**
* The {@link Application JMonkey application} that provide the context.
*/
protected Application application;
/**
* Initializes is it.
*/
protected boolean initialized;
public AWTInput(final AWTContext context) {
this.context = context;
}
public void bind(final Component component) {
this.component = component;
Objects.requireNonNull(this.component, "binded Component cannot be null");
}
public void unbind() {
this.component = 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(RawInputListener listener) {
this.listener = listener;
}
@Override
public long getInputTimeNanos() {
return System.nanoTime();
}
}

@ -0,0 +1,237 @@
/*
* Copyright (c) 2009-2018 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.input;
import java.awt.Component;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import com.jme3.input.KeyInput;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.system.AWTContext;
/**
* The implementation of the {@link KeyInput} dedicated to AWT {@link Component component}.
* <p>
* This class is based on the <a href="http://www.oracle.com/technetwork/java/javase/overview/javafx-overview-2158620.html">JavaFX</a> original code provided by Alexander Brui (see <a href="https://github.com/JavaSaBr/JME3-JFX">JME3-FX</a>)
* </p>
* @author Julien Seinturier - COMEX SA - <a href="http://www.seinturier.fr">http://www.seinturier.fr</a>
* @author Alexander Brui (JavaSaBr)
*/
public class AWTKeyInput extends AWTInput implements KeyInput, KeyListener{
private static final Map<Integer, Integer> KEY_CODE_TO_JME = new HashMap<>();
static {
KEY_CODE_TO_JME.put(KeyEvent.VK_ESCAPE, KEY_ESCAPE);
KEY_CODE_TO_JME.put(KeyEvent.VK_0, KEY_0);
KEY_CODE_TO_JME.put(KeyEvent.VK_1, KEY_1);
KEY_CODE_TO_JME.put(KeyEvent.VK_2, KEY_2);
KEY_CODE_TO_JME.put(KeyEvent.VK_3, KEY_3);
KEY_CODE_TO_JME.put(KeyEvent.VK_4, KEY_4);
KEY_CODE_TO_JME.put(KeyEvent.VK_5, KEY_5);
KEY_CODE_TO_JME.put(KeyEvent.VK_6, KEY_6);
KEY_CODE_TO_JME.put(KeyEvent.VK_7, KEY_7);
KEY_CODE_TO_JME.put(KeyEvent.VK_8, KEY_8);
KEY_CODE_TO_JME.put(KeyEvent.VK_9, KEY_9);
KEY_CODE_TO_JME.put(KeyEvent.VK_MINUS, KEY_MINUS);
KEY_CODE_TO_JME.put(KeyEvent.VK_EQUALS, KEY_EQUALS);
KEY_CODE_TO_JME.put(KeyEvent.VK_BACK_SPACE, KEY_BACK);
KEY_CODE_TO_JME.put(KeyEvent.VK_TAB, KEY_TAB);
KEY_CODE_TO_JME.put(KeyEvent.VK_Q, KEY_Q);
KEY_CODE_TO_JME.put(KeyEvent.VK_W, KEY_W);
KEY_CODE_TO_JME.put(KeyEvent.VK_E, KEY_E);
KEY_CODE_TO_JME.put(KeyEvent.VK_R, KEY_R);
KEY_CODE_TO_JME.put(KeyEvent.VK_T, KEY_T);
KEY_CODE_TO_JME.put(KeyEvent.VK_U, KEY_U);
KEY_CODE_TO_JME.put(KeyEvent.VK_I, KEY_I);
KEY_CODE_TO_JME.put(KeyEvent.VK_O, KEY_O);
KEY_CODE_TO_JME.put(KeyEvent.VK_P, KEY_P);
KEY_CODE_TO_JME.put(KeyEvent.VK_OPEN_BRACKET, KEY_LBRACKET);
KEY_CODE_TO_JME.put(KeyEvent.VK_CLOSE_BRACKET, KEY_RBRACKET);
KEY_CODE_TO_JME.put(KeyEvent.VK_ENTER, KEY_RETURN);
KEY_CODE_TO_JME.put(KeyEvent.VK_CONTROL, KEY_LCONTROL);
KEY_CODE_TO_JME.put(KeyEvent.VK_A, KEY_A);
KEY_CODE_TO_JME.put(KeyEvent.VK_S, KEY_S);
KEY_CODE_TO_JME.put(KeyEvent.VK_D, KEY_D);
KEY_CODE_TO_JME.put(KeyEvent.VK_F, KEY_F);
KEY_CODE_TO_JME.put(KeyEvent.VK_G, KEY_G);
KEY_CODE_TO_JME.put(KeyEvent.VK_H, KEY_H);
KEY_CODE_TO_JME.put(KeyEvent.VK_J, KEY_J);
KEY_CODE_TO_JME.put(KeyEvent.VK_Y, KEY_Y);
KEY_CODE_TO_JME.put(KeyEvent.VK_K, KEY_K);
KEY_CODE_TO_JME.put(KeyEvent.VK_L, KEY_L);
KEY_CODE_TO_JME.put(KeyEvent.VK_SEMICOLON, KEY_SEMICOLON);
KEY_CODE_TO_JME.put(KeyEvent.VK_QUOTE, KEY_APOSTROPHE);
KEY_CODE_TO_JME.put(KeyEvent.VK_DEAD_GRAVE, KEY_GRAVE);
KEY_CODE_TO_JME.put(KeyEvent.VK_SHIFT, KEY_LSHIFT);
KEY_CODE_TO_JME.put(KeyEvent.VK_BACK_SLASH, KEY_BACKSLASH);
KEY_CODE_TO_JME.put(KeyEvent.VK_Z, KEY_Z);
KEY_CODE_TO_JME.put(KeyEvent.VK_X, KEY_X);
KEY_CODE_TO_JME.put(KeyEvent.VK_C, KEY_C);
KEY_CODE_TO_JME.put(KeyEvent.VK_V, KEY_V);
KEY_CODE_TO_JME.put(KeyEvent.VK_B, KEY_B);
KEY_CODE_TO_JME.put(KeyEvent.VK_N, KEY_N);
KEY_CODE_TO_JME.put(KeyEvent.VK_M, KEY_M);
KEY_CODE_TO_JME.put(KeyEvent.VK_COMMA, KEY_COMMA);
KEY_CODE_TO_JME.put(KeyEvent.VK_PERIOD, KEY_PERIOD);
KEY_CODE_TO_JME.put(KeyEvent.VK_SLASH, KEY_SLASH);
KEY_CODE_TO_JME.put(KeyEvent.VK_MULTIPLY, KEY_MULTIPLY);
KEY_CODE_TO_JME.put(KeyEvent.VK_SPACE, KEY_SPACE);
KEY_CODE_TO_JME.put(KeyEvent.VK_CAPS_LOCK, KEY_CAPITAL);
KEY_CODE_TO_JME.put(KeyEvent.VK_F1, KEY_F1);
KEY_CODE_TO_JME.put(KeyEvent.VK_F2, KEY_F2);
KEY_CODE_TO_JME.put(KeyEvent.VK_F3, KEY_F3);
KEY_CODE_TO_JME.put(KeyEvent.VK_F4, KEY_F4);
KEY_CODE_TO_JME.put(KeyEvent.VK_F5, KEY_F5);
KEY_CODE_TO_JME.put(KeyEvent.VK_F6, KEY_F6);
KEY_CODE_TO_JME.put(KeyEvent.VK_F7, KEY_F7);
KEY_CODE_TO_JME.put(KeyEvent.VK_F8, KEY_F8);
KEY_CODE_TO_JME.put(KeyEvent.VK_F9, KEY_F9);
KEY_CODE_TO_JME.put(KeyEvent.VK_F10, KEY_F10);
KEY_CODE_TO_JME.put(KeyEvent.VK_NUM_LOCK, KEY_NUMLOCK);
KEY_CODE_TO_JME.put(KeyEvent.VK_SCROLL_LOCK, KEY_SCROLL);
KEY_CODE_TO_JME.put(KeyEvent.VK_NUMPAD7, KEY_NUMPAD7);
KEY_CODE_TO_JME.put(KeyEvent.VK_NUMPAD8, KEY_NUMPAD8);
KEY_CODE_TO_JME.put(KeyEvent.VK_NUMPAD9, KEY_NUMPAD9);
KEY_CODE_TO_JME.put(KeyEvent.VK_SUBTRACT, KEY_SUBTRACT);
KEY_CODE_TO_JME.put(KeyEvent.VK_NUMPAD4, KEY_NUMPAD4);
KEY_CODE_TO_JME.put(KeyEvent.VK_NUMPAD5, KEY_NUMPAD5);
KEY_CODE_TO_JME.put(KeyEvent.VK_NUMPAD6, KEY_NUMPAD6);
KEY_CODE_TO_JME.put(KeyEvent.VK_ADD, KEY_ADD);
KEY_CODE_TO_JME.put(KeyEvent.VK_NUMPAD1, KEY_NUMPAD1);
KEY_CODE_TO_JME.put(KeyEvent.VK_NUMPAD2, KEY_NUMPAD2);
KEY_CODE_TO_JME.put(KeyEvent.VK_NUMPAD3, KEY_NUMPAD3);
KEY_CODE_TO_JME.put(KeyEvent.VK_NUMPAD0, KEY_NUMPAD0);
KEY_CODE_TO_JME.put(KeyEvent.VK_DECIMAL, KEY_DECIMAL);
KEY_CODE_TO_JME.put(KeyEvent.VK_F11, KEY_F11);
KEY_CODE_TO_JME.put(KeyEvent.VK_F12, KEY_F12);
KEY_CODE_TO_JME.put(KeyEvent.VK_F13, KEY_F13);
KEY_CODE_TO_JME.put(KeyEvent.VK_F14, KEY_F14);
KEY_CODE_TO_JME.put(KeyEvent.VK_F15, KEY_F15);
KEY_CODE_TO_JME.put(KeyEvent.VK_KANA, KEY_KANA);
KEY_CODE_TO_JME.put(KeyEvent.VK_CONVERT, KEY_CONVERT);
KEY_CODE_TO_JME.put(KeyEvent.VK_NONCONVERT, KEY_NOCONVERT);
KEY_CODE_TO_JME.put(KeyEvent.VK_CIRCUMFLEX, KEY_CIRCUMFLEX);
KEY_CODE_TO_JME.put(KeyEvent.VK_AT, KEY_AT);
KEY_CODE_TO_JME.put(KeyEvent.VK_COLON, KEY_COLON);
KEY_CODE_TO_JME.put(KeyEvent.VK_UNDERSCORE, KEY_UNDERLINE);
KEY_CODE_TO_JME.put(KeyEvent.VK_STOP, KEY_STOP);
KEY_CODE_TO_JME.put(KeyEvent.VK_DIVIDE, KEY_DIVIDE);
KEY_CODE_TO_JME.put(KeyEvent.VK_PAUSE, KEY_PAUSE);
KEY_CODE_TO_JME.put(KeyEvent.VK_HOME, KEY_HOME);
KEY_CODE_TO_JME.put(KeyEvent.VK_UP, KEY_UP);
KEY_CODE_TO_JME.put(KeyEvent.VK_PAGE_UP, KEY_PRIOR);
KEY_CODE_TO_JME.put(KeyEvent.VK_LEFT, KEY_LEFT);
KEY_CODE_TO_JME.put(KeyEvent.VK_RIGHT, KEY_RIGHT);
KEY_CODE_TO_JME.put(KeyEvent.VK_END, KEY_END);
KEY_CODE_TO_JME.put(KeyEvent.VK_DOWN, KEY_DOWN);
KEY_CODE_TO_JME.put(KeyEvent.VK_PAGE_DOWN, KEY_NEXT);
KEY_CODE_TO_JME.put(KeyEvent.VK_INSERT, KEY_INSERT);
KEY_CODE_TO_JME.put(KeyEvent.VK_DELETE, KEY_DELETE);
KEY_CODE_TO_JME.put(KeyEvent.VK_ALT, KEY_LMENU);
KEY_CODE_TO_JME.put(KeyEvent.VK_META, KEY_RCONTROL);
}
private final LinkedList<KeyInputEvent> keyInputEvents;
public AWTKeyInput(AWTContext context) {
super(context);
keyInputEvents = new LinkedList<KeyInputEvent>();
}
@Override
public void bind(Component component) {
super.bind(component);
component.addKeyListener(this);
}
@Override
public void unbind() {
if (component != null) {
component.removeKeyListener(this);
}
super.unbind();
}
private void onKeyEvent(KeyEvent keyEvent, boolean pressed) {
int code = convertKeyCode(keyEvent.getID());
char keyChar = keyEvent.getKeyChar();
final KeyInputEvent event = new KeyInputEvent(code, keyChar, pressed, false);
event.setTime(getInputTimeNanos());
EXECUTOR.addToExecute(new Runnable() {
@Override
public void run() {
keyInputEvents.add(event);
}
});
}
@Override
protected void updateImpl() {
while (!keyInputEvents.isEmpty()) {
listener.onKeyEvent(keyInputEvents.poll());
}
}
private int convertKeyCode(int keyCode) {
final Integer code = KEY_CODE_TO_JME.get(keyCode);
return code == null ? KEY_UNKNOWN : code;
}
@Override
public void keyTyped(KeyEvent e) {
System.out.println("Key typed "+e.getKeyChar());
//onKeyEvent(e, false);
}
@Override
public void keyPressed(KeyEvent e) {
System.out.println("Key pressed "+e.getKeyChar());
onKeyEvent(e, true);
}
@Override
public void keyReleased(KeyEvent e) {
System.out.println("Key released "+e.getKeyChar());
onKeyEvent(e, false);
}
}

@ -0,0 +1,239 @@
/*
* Copyright (c) 2009-2018 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.input;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import com.jme3.cursors.plugins.JmeCursor;
import com.jme3.input.MouseInput;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.system.AWTContext;
/**
* The implementation of the {@link MouseInput} dedicated to AWT {@link Component component}.
* <p>
* This class is based on the <a href="http://www.oracle.com/technetwork/java/javase/overview/javafx-overview-2158620.html">JavaFX</a> original code provided by Alexander Brui (see <a href="https://github.com/JavaSaBr/JME3-JFX">JME3-FX</a>)
* </p>
* @author Julien Seinturier - COMEX SA - <a href="http://www.seinturier.fr">http://www.seinturier.fr</a>
* @author Alexander Brui (JavaSaBr)
*/
public class AWTMouseInput extends AWTInput implements MouseInput, MouseListener, MouseMotionListener, MouseWheelListener {
private static final Map<Integer, Integer> MOUSE_BUTTON_TO_JME = new HashMap<>();
static {
MOUSE_BUTTON_TO_JME.put(MouseEvent.BUTTON1, BUTTON_LEFT);
MOUSE_BUTTON_TO_JME.put(MouseEvent.BUTTON2, BUTTON_RIGHT);
MOUSE_BUTTON_TO_JME.put(MouseEvent.BUTTON3, BUTTON_MIDDLE);
}
/**
* The scale factor for scrolling.
*/
private static final int WHEEL_SCALE = 10;
private final LinkedList<MouseMotionEvent> mouseMotionEvents;
private final LinkedList<MouseButtonEvent> mouseButtonEvents;
private int mouseX;
private int mouseY;
private int mouseWheel;
public AWTMouseInput(AWTContext context) {
super(context);
mouseMotionEvents = new LinkedList<MouseMotionEvent>();
mouseButtonEvents = new LinkedList<MouseButtonEvent>();
}
@Override
public void bind(Component component) {
super.bind(component);
component.addMouseListener(this);
component.addMouseMotionListener(this);
component.addMouseWheelListener(this);
}
@Override
public void unbind() {
if (component != null) {
component.removeMouseListener(this);
component.removeMouseMotionListener(this);
component.removeMouseWheelListener(this);
}
super.unbind();
}
@Override
protected void updateImpl() {
while (!mouseMotionEvents.isEmpty()) {
listener.onMouseMotionEvent(mouseMotionEvents.poll());
}
while (!mouseButtonEvents.isEmpty()) {
listener.onMouseButtonEvent(mouseButtonEvents.poll());
}
}
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(new Runnable() {
@Override
public void run() {
mouseMotionEvents.add(mouseMotionEvent);
}
});
}
private void onCursorPos(double xpos, 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(new Runnable() {
@Override
public void run() {
mouseMotionEvents.add(mouseMotionEvent);
}
});
}
private void onMouseButton(MouseEvent event, final boolean pressed) {
final MouseButtonEvent mouseButtonEvent = new MouseButtonEvent(convertButton(event.getButton()), pressed, mouseX, mouseY);
mouseButtonEvent.setTime(getInputTimeNanos());
EXECUTOR.addToExecute(new Runnable() {
@Override
public void run() {
mouseButtonEvents.add(mouseButtonEvent);
}
});
}
private int convertButton(int i) {
final Integer result = MOUSE_BUTTON_TO_JME.get(i);
return result == null ? 0 : result;
}
@Override
public void setCursorVisible(final boolean visible) {
}
@Override
public int getButtonCount() {
return 3;
}
@Override
public void setNativeCursor(JmeCursor cursor) {
}
@Override
public void mouseDragged(java.awt.event.MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseMoved(java.awt.event.MouseEvent e) {
onCursorPos(e.getX(), e.getY());
}
@Override
public void mouseClicked(java.awt.event.MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(java.awt.event.MouseEvent e) {
onMouseButton(e, true);
}
@Override
public void mouseReleased(java.awt.event.MouseEvent e) {
onMouseButton(e, false);
}
@Override
public void mouseEntered(java.awt.event.MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(java.awt.event.MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
onWheelScroll(e.getWheelRotation() * WHEEL_SCALE, e.getWheelRotation() * WHEEL_SCALE);
}
}

@ -0,0 +1,391 @@
/*
* Copyright (c) 2009-2018 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.system;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.system.AWTFrameProcessor.TransferMode;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image;
import com.jme3.util.BufferUtils;
/**
* <p>
* This class enables to update graphics of an AWT component with the result of JMonkey 3D rendering.
* </p>
* <p>
* This class is based on the <a href="http://www.oracle.com/technetwork/java/javase/overview/javafx-overview-2158620.html">JavaFX</a> original code provided by Alexander Brui (see <a href="https://github.com/JavaSaBr/JME3-JFX">JME3-FX</a>)
* </p>
* @author Julien Seinturier - COMEX SA - <a href="http://www.seinturier.fr">http://www.seinturier.fr</a>
* @author Alexander Brui (JavaSaBr)
*
*/
public class AWTComponentRenderer {
/**
* The constant RUNNING_STATE.
*/
protected static final int RUNNING_STATE = 1;
/**
* The constant WAITING_STATE.
*/
protected static final int WAITING_STATE = 2;
/**
* The constant DISPOSING_STATE.
*/
protected static final int DISPOSING_STATE = 3;
/**
* The constant DISPOSED_STATE.
*/
protected static final int DISPOSED_STATE = 4;
/**
* The Frame state.
*/
protected final AtomicInteger frameState;
/**
* The Image state.
*/
protected final AtomicInteger imageState;
/**
* The Frame buffer.
*/
protected final FrameBuffer frameBuffer;
/**
* The Pixel writer.
*/
protected final Graphics pixelWriter;
/**
* The Frame byte buffer.
*/
protected final ByteBuffer frameByteBuffer;
/**
* The transfer mode.
*/
protected final TransferMode transferMode;
/**
* The byte buffer.
*/
protected final byte[] byteBuffer;
/**
* The image byte buffer.
*/
protected final int[] imageByteBuffer;
/**
* The prev image byte buffer.
*/
protected final byte[] prevImageByteBuffer;
/**
* How many frames need to write else.
*/
protected int frameCount;
/**
* The width.
*/
private final int width;
/**
* The height.
*/
private final int height;
private ColorModel colorModel = null;
private Component component = null;
private Graphics2D offGraphics = null;
/**
* Create a new component renderer attached to the given {@link Component destination}.
* The graphics of the destination are updated with the JMonkeyEngine rendering result.
* @param destination the AWT component to use as target of the JMonkeyEngine rendering.
* @param width the width of the component in pixels.
* @param height the height of the component in pixels.
* @param transferMode the rendering mode that can be {@link TransferMode#ALWAYS} if the component has to be rendered at each update
* or {@link TransferMode#ON_CHANGES} if the component has to be rendered only when changes are occurring.
*/
public AWTComponentRenderer(Component destination, final int width, final int height, TransferMode transferMode) {
this(destination, transferMode, null, width, height);
}
/**
* Create a new component renderer attached to the given {@link Component destination}.
* The graphics of the destination are updated with the JMonkeyEngine rendering result.
* @param destination the AWT component to use as target of the JMonkeyEngine rendering.
* @param transferMode the rendering mode that can be {@link TransferMode#ALWAYS} if the component has to be rendered at each update or {@link TransferMode#ON_CHANGES} if the component has to be rendered only when changes are occurring.
* @param frameBuffer the JMonkey frame buffer to use (if <code>null</code> is passed, a new default frame buffer is created)
* @param width the width of the component in pixels.
* @param height the height of the component in pixels.
*/
public AWTComponentRenderer(Component destination, TransferMode transferMode, FrameBuffer frameBuffer, int width, int height) {
this.transferMode = transferMode;
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;
this.frameCount = 0;
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.RGBA8);
this.frameBuffer.setSrgb(true);
}
colorModel = ColorModel.getRGBdefault();
frameByteBuffer = BufferUtils.createByteBuffer(getWidth() * getHeight() * 4);
byteBuffer = new byte[getWidth() * getHeight() * 4];
prevImageByteBuffer = new byte[getWidth() * getHeight() * 4];
imageByteBuffer = new int[getWidth() * getHeight()];
pixelWriter = getGraphics(destination);
this.component = destination;
}
/**
* Initialize the component renderer.
* @param renderer the JMonkey {@link Renderer renderer} to use.
* @param main <code>true</code> if the attached frame buffer is the main one or <code>false</code> otherwise.
*/
public void init(Renderer renderer, boolean main) {
if (main) {
renderer.setMainFrameBufferOverride(frameBuffer);
}
}
/**
* Get the graphics context of the given component.
* @param destination the AWT component used for rendering.
* @return the graphics context of the given component.
*/
protected Graphics getGraphics(Component destination) {
if (destination != null) {
if (destination.getGraphics() != null) {
return destination.getGraphics();
} else {
System.out.println("AWT component "+destination.getClass().getSimpleName()+" does not provide 2D graphics capabilities.");
return null;
//throw new IllegalArgumentException("AWT component "+destination.getClass().getSimpleName()+" does not provide 2D graphics capabilities.");
}
} else {
throw new NullPointerException("Component cannot be null.");
}
}
/**
* Get the width of the area to render.
* @return the width of the area to render.
* @see #getHeight()
*/
public int getWidth() {
return width;
}
/**
* Get the height of the area to render.
* @return the height of the area to render.
* @see #getWidth()
*/
public int getHeight() {
return height;
}
/**
* Copy the JMonkey frame buffer that has been rendered by the JMonkey engine and schedule the rendering of the component.
* @param renderManager the JMonkey render manager.
*/
public void copyFrameBufferToImage(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.RGBA8);
} finally {
if (!frameState.compareAndSet(RUNNING_STATE, WAITING_STATE)) {
throw new RuntimeException("unknown problem with the frame state");
}
}
synchronized (byteBuffer) {
frameByteBuffer.get(byteBuffer);
if (transferMode == TransferMode.ON_CHANGES) {
final byte[] prevBuffer = getPrevImageByteBuffer();
if (Arrays.equals(prevBuffer, byteBuffer)) {
if (frameCount == 0) return;
} else {
frameCount = 2;
System.arraycopy(byteBuffer, 0, prevBuffer, 0, byteBuffer.length);
}
frameByteBuffer.position(0);
frameCount--;
}
}
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
writeFrame();
}});
}
/**
* Write the current rendered frame to the component graphics contex.
*/
protected void writeFrame() {
if (pixelWriter != null) {
while (!imageState.compareAndSet(WAITING_STATE, RUNNING_STATE)) {
if (imageState.get() == DISPOSED_STATE) {
return;
}
}
try {
final int[] imageDataBuffer = getImageByteBuffer();
synchronized (byteBuffer) {
for(int i = 0; i < width * height; i++) {
imageDataBuffer[i] = ((0xff & byteBuffer[i*4+3]) << 24) // Alpha
| ((0xff & byteBuffer[i*4]) << 16) // Red
| ((0xff & byteBuffer[i*4+1]) << 8) // Green
| ((0xff & byteBuffer[i*4+2])); // BLue
}
}
DataBuffer buffer = new DataBufferInt(imageDataBuffer, imageDataBuffer.length);
SampleModel sm = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, getWidth(), getHeight(), new int[] {0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000});
WritableRaster raster = Raster.createWritableRaster(sm, buffer, null);
BufferedImage img = new BufferedImage(colorModel, raster, false, null);
BufferedImage img2 = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
offGraphics = img2.createGraphics();
offGraphics.setColor(component.getBackground());
img2.createGraphics().fillRect(0, 0, getWidth(), getHeight());
img2.createGraphics().drawImage(img, null, null);
component.getGraphics().drawImage(img2, 0, 0, null);
} finally {
if (!imageState.compareAndSet(RUNNING_STATE, WAITING_STATE)) {
throw new RuntimeException("unknown problem with the image state");
}
}
} else {
System.out.println("No graphics context available for rendering.");
}
}
/**
* Get the image byte buffer.
* @return the image byte buffer.
*/
protected int[] getImageByteBuffer() {
return imageByteBuffer;
}
/**
* Get the previous image byte buffer.
* @return the previous image byte buffer.
*/
protected byte[] getPrevImageByteBuffer() {
return prevImageByteBuffer;
}
/**
* Dispose this renderer. The underlying frame buffer is also disposed.
*/
public void dispose() {
while (!frameState.compareAndSet(WAITING_STATE, DISPOSING_STATE)) ;
while (!imageState.compareAndSet(WAITING_STATE, DISPOSING_STATE)) ;
frameBuffer.dispose();
BufferUtils.destroyDirectBuffer(frameByteBuffer);
frameState.compareAndSet(DISPOSING_STATE, DISPOSED_STATE);
imageState.compareAndSet(DISPOSING_STATE, DISPOSED_STATE);
}
}

@ -0,0 +1,234 @@
/*
* Copyright (c) 2009-2018 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.system;
import com.jme3.input.AWTKeyInput;
import com.jme3.input.AWTMouseInput;
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;
/**
* A JMonkey {@link JmeContext context} that is dedicated to AWT component rendering.
* <p>
* This class is based on the <a href="http://www.oracle.com/technetwork/java/javase/overview/javafx-overview-2158620.html">JavaFX</a> original code provided by Alexander Brui (see <a href="https://github.com/JavaSaBr/JME3-JFX">JME3-FX</a>)
* </p>
* @author Julien Seinturier - COMEX SA - <a href="http://www.seinturier.fr">http://www.seinturier.fr</a>
* @author Alexander Brui (JavaSaBr)
*/
public class AWTContext implements JmeContext {
/**
* The settings.
*/
protected final AppSettings settings;
/**
* The key input.
*/
protected final AWTKeyInput keyInput;
/**
* The mouse input.
*/
protected final AWTMouseInput mouseInput;
/**
* The current width.
*/
private volatile int width;
/**
* The current height.
*/
private volatile int height;
/**
* The background context.
*/
protected JmeContext backgroundContext;
public AWTContext() {
this.keyInput = new AWTKeyInput(this);
this.mouseInput = new AWTMouseInput(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.
*/
protected AppSettings createSettings() {
final AppSettings settings = new AppSettings(true);
settings.setRenderer(AppSettings.LWJGL_OPENGL3);
return settings;
}
/**
* @return new context/
*/
protected JmeContext createBackgroundContext() {
return JmeSystem.newContext(settings, Type.OffscreenSurface);
}
@Override
public Type getType() {
return Type.OffscreenSurface;
}
@Override
public void setSettings(AppSettings settings) {
this.settings.copyFrom(settings);
this.settings.setRenderer(AppSettings.LWJGL_OPENGL3);
this.backgroundContext.setSettings(settings);
}
@Override
public void setSystemListener(final SystemListener listener) {
backgroundContext.setSystemListener(listener);
}
@Override
public AppSettings getSettings() {
return settings;
}
@Override
public Renderer getRenderer() {
return backgroundContext.getRenderer();
}
@Override
public Context getOpenCLContext() {
return null;
}
@Override
public AWTMouseInput getMouseInput() {
return mouseInput;
}
@Override
public AWTKeyInput getKeyInput() {
return keyInput;
}
@Override
public JoyInput getJoyInput() {
return null;
}
@Override
public TouchInput getTouchInput() {
return null;
}
@Override
public Timer getTimer() {
return backgroundContext.getTimer();
}
@Override
public void setTitle(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) {
String render = System.getProperty("awt.background.render", AppSettings.LWJGL_OPENGL33);
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);
}
}

@ -0,0 +1,659 @@
/*
* Copyright (c) 2009-2018 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.system;
import java.awt.Component;
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import com.jme3.app.Application;
import com.jme3.input.AWTKeyInput;
import com.jme3.input.AWTMouseInput;
import com.jme3.post.SceneProcessor;
import com.jme3.profile.AppProfiler;
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;
/**
* A frame processor that enables to render JMonkey frame buffer onto an AWT component.
* <p>
* This class is based on the <a href="http://www.oracle.com/technetwork/java/javase/overview/javafx-overview-2158620.html">JavaFX</a> original code provided by Alexander Brui (see <a href="https://github.com/JavaSaBr/JME3-JFX">JME3-FX</a>)
* </p>
* @author Julien Seinturier - COMEX SA - <a href="http://www.seinturier.fr">http://www.seinturier.fr</a>
* @author Alexander Brui (JavaSaBr)
*
*/
public class AWTFrameProcessor implements SceneProcessor, PropertyChangeListener {
public enum TransferMode {
ALWAYS,
ON_CHANGES
}
private Application application = null;
/**
* The width listener.
*/
protected PropertyChangeListener widthListener;
/**
* The height listener.
*/
protected PropertyChangeListener heightListener;
/**
* The ration listener.
*/
protected PropertyChangeListener rationListener;
/**
* The flag to decide when we should resize.
*/
private final AtomicInteger reshapeNeeded;
/**
* The render manager.
*/
private RenderManager renderManager;
/**
* The source view port.
*/
private ViewPort viewPort;
/**
* The frame transfer.
*/
private AWTComponentRenderer frameTransfer;
/**
* The transfer mode.
*/
private TransferMode transferMode;
/**
* The destination of jMe frames.
*/
protected volatile Component destination;
/**
* The flag is true if this processor is main.
*/
private volatile boolean main;
private int askWidth;
private int askHeight;
private boolean askFixAspect;
private boolean enabled;
@Override
public void initialize(RenderManager rm, ViewPort vp) {
this.renderManager = rm;
}
@Override
public void reshape(ViewPort vp, int w, int h) {
// TODO Auto-generated method stub
}
@Override
public boolean isInitialized() {
return frameTransfer != null;
}
@Override
public void preFrame(float tpf) {
// TODO Auto-generated method stub
}
@Override
public void postQueue(RenderQueue rq) {
// TODO Auto-generated method stub
}
@Override
public void postFrame(FrameBuffer out) {
if (!isEnabled()) {
return;
}
final AWTComponentRenderer frameTransfer = getFrameTransfer();
if (frameTransfer != null) {
frameTransfer.copyFrameBufferToImage(getRenderManager());
}
// for the next frame
if (hasDestination() && reshapeNeeded.get() > 0 && reshapeNeeded.decrementAndGet() >= 0) {
if (frameTransfer != null) {
frameTransfer.dispose();
}
setFrameTransfer(reshapeInThread(askWidth, askHeight, askFixAspect));
}
}
@Override
public void cleanup() {
final AWTComponentRenderer frameTransfer = getFrameTransfer();
if (frameTransfer != null) {
frameTransfer.dispose();
setFrameTransfer(null);
}
}
@Override
public void setProfiler(AppProfiler profiler) {
// TODO Auto-generated method stub
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
System.out.println("Property changed: "+evt.getPropertyName()+" "+evt.getOldValue()+" -> "+evt.getNewValue());
}
public AWTFrameProcessor() {
transferMode = TransferMode.ALWAYS;
askWidth = 1;
askHeight = 1;
main = true;
reshapeNeeded = new AtomicInteger(2);
}
/**
* Notify about that the ratio was changed.
*
* @param newValue the new value of the ratio.
*/
protected void notifyChangedRatio(Boolean newValue) {
notifyComponentResized(destination.getWidth(), destination.getHeight(), newValue);
}
/**
* Notify about that the height was changed.
*
* @param newValue the new value of the height.
*/
protected void notifyChangedHeight(Number newValue) {
notifyComponentResized(destination.getWidth(), newValue.intValue(), isPreserveRatio());
}
/**
* Notify about that the width was changed.
*
* @param newValue the new value of the width.
*/
protected void notifyChangedWidth(Number newValue) {
notifyComponentResized(newValue.intValue(), destination.getHeight(), isPreserveRatio());
}
/**
* Gets the application.
*
* @return the application.
*/
protected Application getApplication() {
return application;
}
/**
* Sets the application.
*
* @param application the application.
*/
protected void setApplication(Application application) {
this.application = application;
}
/**
* Gets the current destination.
*
* @return the current destination.
*/
protected Component getDestination() {
return destination;
}
/**
* Sets the destination.
*
* @param destination the destination.
*/
protected void setDestination(Component destination) {
this.destination = destination;
}
/**
* Checks of existing destination.
* @return true if destination is exists.
*/
protected boolean hasDestination() {
return destination != null;
}
/**
* Checks of existing application.
* @return true if destination is exists.
*/
protected boolean hasApplication() {
return application != null;
}
/**
* Gets the frame transfer.
* @return the file transfer.
*/
protected AWTComponentRenderer getFrameTransfer() {
return frameTransfer;
}
/**
* Sets the frame transfer.
*
* @param frameTransfer the file transfer.
*/
protected void setFrameTransfer(AWTComponentRenderer frameTransfer) {
this.frameTransfer = frameTransfer;
}
/**
* Gets the view port.
*
* @return the view port.
*/
protected ViewPort getViewPort() {
return viewPort;
}
/**
* Gets the render manager.
*
* @return the render manager.
*/
protected RenderManager getRenderManager() {
return renderManager;
}
public boolean isMain() {
return main;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
/**
* Handle resizing.
*
* @param newWidth the new width.
* @param newHeight the new height.
* @param fixAspect true if need to fix aspect.
*/
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(2);
}
public void reshape() {
reshapeNeeded.set(2);
}
/**
* Is preserve ratio.
*
* @return is preserve ratio.
*/
protected boolean isPreserveRatio() {
return false;
}
/**
* Gets destination width.
*
* @return the destination width.
*/
protected int getDestinationWidth() {
return getDestination().getWidth();
}
/**
* Gets destination height.
*
* @return the destination height.
*/
protected int getDestinationHeight() {
return getDestination().getHeight();
}
/**
* Bind this processor.
*
* @param destination the destination.
* @param application the application.
*/
public void bind(Component destination, Application application) {
final RenderManager renderManager = application.getRenderManager();
if (renderManager == null) {
throw new RuntimeException("No render manager available from the application.");
}
List<ViewPort> postViews = renderManager.getPostViews();
if (postViews.isEmpty()) {
throw new RuntimeException("the list of a post view is empty.");
}
bind(destination, application, postViews.get(postViews.size() - 1), true);
}
/**
* Bind this processor.
*
* @param destination the destination.
* @param application the application.
* @param viewPort the view port.
*/
public void bind(Component destination, Application application, ViewPort viewPort) {
bind(destination, application, viewPort, true);
}
/**
* Bind this processor.
*
* @param destination the destination.
* @param application the application.
* @param inputNode the input node.
* @param viewPort the view port.
* @param main true if this processor is main.
*/
public void bind(final Component destination, final Application application, ViewPort viewPort, boolean main) {
if (hasApplication()) {
throw new RuntimeException("This process is already bonded.");
}
setApplication(application);
setEnabled(true);
this.main = main;
this.viewPort = viewPort;
this.viewPort.addProcessor(this);
if (EventQueue.isDispatchThread()) {
bindDestination(application, destination);
} else {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
bindDestination(application, destination);
}});
}
}
/**
* Unbind this processor from its current destination.
*/
public void unbind() {
if (viewPort != null) {
viewPort.removeProcessor(this);
viewPort = null;
}
if (EventQueue.isDispatchThread()) {
unbindDestination();
} else {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
unbindDestination();
}});
}
}
/**
* Bind this processor.
*
* @param application the application.
* @param destination the destination.
* @param inputNode the input node.
*/
protected void bindDestination(Application application, Component destination) {
if (!EventQueue.isDispatchThread()) {
throw new RuntimeException("bind has to be done from the Event Dispatching thread.");
}
if (isMain()) {
if (application.getContext() != null) {
if (application.getContext() instanceof AWTContext) {
AWTContext context = (AWTContext) application.getContext();
AWTMouseInput mouseInput = context.getMouseInput();
mouseInput.bind(destination);
AWTKeyInput keyInput = context.getKeyInput();
keyInput.bind(destination);
setDestination(destination);
bindListeners();
notifyComponentResized(getDestinationWidth(), getDestinationHeight(), isPreserveRatio());
} else {
throw new IllegalArgumentException("Underlying application has to use AWTContext (actually using "+application.getContext().getClass().getSimpleName()+")");
}
} else {
throw new IllegalArgumentException("Underlying application has to use a valid AWTContext (context is null)");
}
}
}
/**
* Unbind this processor from destination.
*/
protected void unbindDestination() {
if (!EventQueue.isDispatchThread()) {
throw new RuntimeException("unbind has to be done from the Event Dispatching thread.");
}
if (hasApplication() && isMain()) {
final AWTContext context = (AWTContext) getApplication().getContext();
final AWTMouseInput mouseInput = context.getMouseInput();
mouseInput.unbind();
final AWTKeyInput keyInput = context.getKeyInput();
keyInput.unbind();
}
setApplication(null);
if (hasDestination()) {
unbindListeners();
setDestination(null);
}
}
protected void bindListeners() {
Component destination = getDestination();
destination.addPropertyChangeListener(this);
destination.addPropertyChangeListener(this);
}
protected void unbindListeners() {
Component destination = getDestination();
destination.removePropertyChangeListener(this);
destination.removePropertyChangeListener(this);
}
/**
* Reshape the current frame transfer for the new size.
*
* @param width the width.
* @param height the height.
* @param fixAspect true if need to fix aspect ration.
* @return the new frame transfer.
*/
protected AWTComponentRenderer reshapeInThread(final int width, final int height, final boolean fixAspect) {
reshapeCurrentViewPort(width, height);
ViewPort viewPort = getViewPort();
RenderManager renderManager = getRenderManager();
FrameBuffer frameBuffer = viewPort.getOutputFrameBuffer();
AWTComponentRenderer frameTransfer = createFrameTransfer(frameBuffer, width, height);
frameTransfer.init(renderManager.getRenderer(), isMain());
if (isMain()) {
AWTContext context = (AWTContext) getApplication().getContext();
context.setHeight(height);
context.setWidth(width);
}
return frameTransfer;
}
/**
* Create a new frame transfer.
*
* @param frameBuffer the frame buffer.
* @param width the width.
* @param height the height.
* @return the new frame transfer.
*/
protected AWTComponentRenderer createFrameTransfer(FrameBuffer frameBuffer, int width, int height) {
return new AWTComponentRenderer(getDestination(), getTransferMode(), isMain() ? null : frameBuffer, width, height);
}
/**
* Reshape the current view port.
*
* @param width the width.
* @param height the height.
*/
protected void reshapeCurrentViewPort(int width, int height) {
ViewPort viewPort = getViewPort();
Camera camera = viewPort.getCamera();
int cameraAngle = getCameraAngle();
float aspect = (float) camera.getWidth() / camera.getHeight();
if (isMain()) {
getRenderManager().notifyReshape(width, height);
camera.setFrustumPerspective(cameraAngle, aspect, 1f, 10000);
return;
}
camera.resize(width, height, true);
camera.setFrustumPerspective(cameraAngle, aspect, 1f, 10000);
final List<SceneProcessor> processors = viewPort.getProcessors();
boolean found = false;
Iterator<SceneProcessor> iter = processors.iterator();
while(!found && iter.hasNext()) {
if (!(iter.next() instanceof AWTFrameProcessor)) {
found = true;
}
}
if (found) {
FrameBuffer frameBuffer = new FrameBuffer(width, height, 1);
frameBuffer.setDepthBuffer(Image.Format.Depth);
frameBuffer.setColorBuffer(Image.Format.RGBA8);
frameBuffer.setSrgb(true);
viewPort.setOutputFrameBuffer(frameBuffer);
}
for (final SceneProcessor sceneProcessor : processors) {
if (!sceneProcessor.isInitialized()) {
sceneProcessor.initialize(renderManager, viewPort);
} else {
sceneProcessor.reshape(viewPort, width, height);
}
}
}
/**
* Gets camera angle.
*
* @return the camera angle.
*/
protected int getCameraAngle() {
final String angle = System.getProperty("awt.frame.transfer.camera.angle", "45");
return Integer.parseInt(angle);
}
public TransferMode getTransferMode() {
return transferMode;
}
public void setTransferMode(TransferMode transferMode) {
this.transferMode = transferMode;
}
}

@ -0,0 +1,114 @@
/*
* Copyright (c) 2009-2018 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.system;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* <p>
* This class is dedicated to the queuing of AWT related tasks and their execution.
* It's enable to store tasks that have to be executed within an AWT context and execute them at the specified time.
* </p>
* <p>
* This class is an AWT implementation of the <a href="http://www.oracle.com/technetwork/java/javase/overview/javafx-overview-2158620.html">JavaFX</a> original code provided by Alexander Brui (see <a href="https://github.com/JavaSaBr/JME3-JFX">JME3-FX</a>)
* </p>
* @author Julien Seinturier - COMEX SA - <a href="http://www.seinturier.fr">http://www.seinturier.fr</a>
* @author Alexander Brui (JavaSaBr)
*/
public class AWTTaskExecutor {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private static final AWTTaskExecutor INSTANCE = new AWTTaskExecutor();
/**
* Get the instance of the executor.
* @return the instance of executor.
*/
public static AWTTaskExecutor getInstance() {
return INSTANCE;
}
/**
* The list of waiting tasks.
*/
private final List<Runnable> waitTasks;
private AWTTaskExecutor() {
waitTasks = new LinkedList<Runnable>();
}
public List<Runnable> getWaitingTasks(){
return waitTasks;
}
/**
* Add the given {@link Runnable runnable} to the list of planned executions.
* @param task the task to add.
* @see #execute()
*/
public void addToExecute(final Runnable task) {
lock.writeLock().lock();
try {
waitTasks.add(task);
} catch (Exception e) {
// This try catch block enable to free the lock in case of any unexpected error.
}
lock.writeLock().unlock();
}
/**
* Execute all the tasks that are waiting.
* @see #addToExecute(Runnable)
*/
public void execute() {
if (waitTasks.isEmpty()) return;
lock.readLock().lock();
try {
for(Runnable runnable : waitTasks) {
runnable.run();
}
} catch (Exception e) {
// This try catch block enable to free the lock in case of any unexpected error.
}
waitTasks.clear();
lock.readLock().unlock();
}
}
Loading…
Cancel
Save