Index: src/com/jme/util/GameTask.java =================================================================== --- src/com/jme/util/GameTask.java (revision 3942) +++ src/com/jme/util/GameTask.java (working copy) @@ -36,6 +36,8 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; @@ -49,61 +51,114 @@ private static final Logger logger = Logger.getLogger(GameTask.class .getName()); - private Callable callable; + private final Callable callable; + + private V result; + private ExecutionException exception; private boolean cancelled; - private boolean completed; - private V result; - private ExecutionException exc; + private final ReentrantLock stateLock = new ReentrantLock(); + private final Condition finishedCondition = stateLock.newCondition(); public GameTask(Callable callable) { this.callable = callable; } public boolean cancel(boolean mayInterruptIfRunning) { - if (result != null) { - return false; - } - cancelled = true; - return true; + // TODO mayInterruptIfRunning was ignored in previous code, should this param be removed? + stateLock.lock(); + try { + if (result != null) { + return false; + } + cancelled = true; + + finishedCondition.signalAll(); + + return true; + } finally { + stateLock.unlock(); + } } - public synchronized V get() throws InterruptedException, ExecutionException { - while ((!completed) && (exc == null)) { - wait(); - } - if (exc != null) throw exc; - return result; + public V get() throws InterruptedException, ExecutionException { + stateLock.lock(); + try { + while (!isDone()) { + finishedCondition.await(); + } + if (exception != null) { + throw exception; + } + return result; + } finally { + stateLock.unlock(); + } } - public synchronized V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - if ((!completed) && (exc == null)) { - unit.timedWait(this, timeout); - } - if (exc != null) throw exc; - if (result == null) throw new TimeoutException("Object not returned in time allocated."); - return result; + public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + stateLock.lock(); + try { + if (!isDone()) { + finishedCondition.await(timeout, unit); + } + if (exception != null) { + throw exception; + } + if (result == null) { + throw new TimeoutException("Object not returned in time allocated."); + } + return result; + } finally { + stateLock.unlock(); + } } - + public boolean isCancelled() { - return cancelled; + stateLock.lock(); + try { + return cancelled; + } finally { + stateLock.unlock(); + } } public boolean isDone() { - return completed; + stateLock.lock(); + try { + return (result != null) || cancelled || (exception != null); + } finally { + stateLock.unlock(); + } } public Callable getCallable() { return callable; } - public synchronized void invoke() { + public void invoke() { try { - result = callable.call(); - completed = true; + final V tmpResult = callable.call(); + + stateLock.lock(); + try { + result = tmpResult; + + finishedCondition.signalAll(); + } finally { + stateLock.unlock(); + } } catch(Exception e) { logger.logp(Level.SEVERE, this.getClass().toString(), "invoke()", "Exception", e); - exc = new ExecutionException(e); + + stateLock.lock(); + try { + exception = new ExecutionException(e); + + finishedCondition.signalAll(); + } finally { + stateLock.unlock(); + } } - notifyAll(); } + } Index: src/com/jme/util/GameTaskQueueManager.java =================================================================== --- src/com/jme/util/GameTaskQueueManager.java (revision 3942) +++ src/com/jme/util/GameTaskQueueManager.java (working copy) @@ -32,8 +32,9 @@ package com.jme.util; -import java.util.HashMap; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Future; /** @@ -45,13 +46,11 @@ */ public class GameTaskQueueManager { - private static GameTaskQueueManager MANAGER_INSTANCE = null; + private static final GameTaskQueueManager MANAGER_INSTANCE = new GameTaskQueueManager(); - protected HashMap managedQueues = new HashMap(2); + protected final ConcurrentMap managedQueues = new ConcurrentHashMap(2); - public static synchronized GameTaskQueueManager getManager() { - if (MANAGER_INSTANCE == null) - MANAGER_INSTANCE = new GameTaskQueueManager(); + public static GameTaskQueueManager getManager() { return MANAGER_INSTANCE ; } Index: src/com/jme/util/GameTaskQueue.java =================================================================== --- src/com/jme/util/GameTaskQueue.java (revision 3942) +++ src/com/jme/util/GameTaskQueue.java (working copy) @@ -34,6 +34,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; /** * GameTaskQueue is a simple queueing system to enqueue tasks @@ -51,14 +52,9 @@ public static final String RENDER = "render"; public static final String UPDATE = "update"; - private ConcurrentLinkedQueue queue; - private boolean executeAll; + private final ConcurrentLinkedQueue> queue = new ConcurrentLinkedQueue>(); + private final AtomicBoolean executeAll = new AtomicBoolean(); - public GameTaskQueue() { - queue = new ConcurrentLinkedQueue(); - executeAll = false; - } - /** * The state of this GameTaskQueue if it * will execute all enqueued Callables on an execute @@ -68,7 +64,7 @@ * boolean */ public boolean isExecuteAll() { - return executeAll; + return executeAll.get(); } /** @@ -80,7 +76,7 @@ * @param executeAll */ public void setExecuteAll(boolean executeAll) { - this.executeAll = executeAll; + this.executeAll.set(executeAll); } /** @@ -113,6 +109,6 @@ if (task == null) return; } task.invoke(); - } while ((executeAll) && ((task = queue.poll()) != null)); + } while ((executeAll.get()) && ((task = queue.poll()) != null)); } } Index: src/com/jmex/awt/swingui/JMEDesktopState.java =================================================================== --- src/com/jmex/awt/swingui/JMEDesktopState.java (revision 3942) +++ src/com/jmex/awt/swingui/JMEDesktopState.java (working copy) @@ -32,19 +32,18 @@ package com.jmex.awt.swingui; import java.awt.Color; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; +import java.lang.reflect.InvocationTargetException; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.SwingUtilities; + import com.jme.input.InputHandler; import com.jme.renderer.Renderer; import com.jme.scene.Node; import com.jme.scene.Spatial.CullHint; import com.jme.scene.Spatial.LightCombineMode; import com.jme.system.DisplaySystem; -import com.jme.util.GameTaskQueueManager; import com.jmex.game.state.BasicGameState; /** @@ -87,36 +86,38 @@ guiInput = new InputHandler(); guiInput.setEnabled(true); - - Future future = GameTaskQueueManager.getManager().update(new Callable() { - public JMEDesktop call() throws Exception { - if (variableDesktopSize) { - return new JMEDesktop("Desktop", DisplaySystem.getDisplaySystem().getWidth(), DisplaySystem.getDisplaySystem().getHeight(), guiInput); - } else { - return new JMEDesktop("Desktop", width, height, guiInput); - } - } - }); + + if (variableDesktopSize) { + desktop = new JMEDesktop("Desktop", DisplaySystem.getDisplaySystem().getWidth(), DisplaySystem.getDisplaySystem().getHeight(), guiInput); + } else { + desktop = new JMEDesktop("Desktop", width, height, guiInput); + } + try { - desktop = future.get(); - desktop.getJDesktop().setName("Desktop"); - desktop.getJDesktop().setBackground(new Color(0.0f, 0.0f, 0.0f, 0.0f)); - desktop.getJDesktop().setOpaque(true); - guiNode.attachChild(desktop); - guiNode.getLocalTranslation().set(DisplaySystem.getDisplaySystem().getWidth() / 2, DisplaySystem.getDisplaySystem().getHeight() / 2, 0.0f); - guiNode.getLocalScale().set(1.0f, 1.0f, 1.0f); - guiNode.updateRenderState(); - guiNode.updateGeometricState(0.0f, true); - guiNode.setRenderQueueMode(Renderer.QUEUE_ORTHO); - guiNode.updateGeometricState(0.0f, true); - guiNode.updateRenderState(); + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + desktop.getJDesktop().setName("Desktop"); + desktop.getJDesktop().setBackground(new Color(0.0f, 0.0f, 0.0f, 0.0f)); + desktop.getJDesktop().setOpaque(true); + + buildUI(); + }}); + } catch(InvocationTargetException exc) { + logger.logp(Level.SEVERE, this.getClass().toString(), "init()", "Exception", exc); } catch(InterruptedException exc) { - logger.logp(Level.SEVERE, this.getClass().toString(), "init()", "Exception", exc); - } catch(ExecutionException exc) { - logger.logp(Level.SEVERE, this.getClass().toString(), "init()", "Exception", exc); + logger.logp(Level.SEVERE, this.getClass().toString(), "init()", "Exception", exc); } - buildUI(); + guiNode.attachChild(desktop); + guiNode.getLocalTranslation().set(DisplaySystem.getDisplaySystem().getWidth() / 2, DisplaySystem.getDisplaySystem().getHeight() / 2, 0.0f); + guiNode.getLocalScale().set(1.0f, 1.0f, 1.0f); + guiNode.updateRenderState(); + guiNode.updateGeometricState(0.0f, true); + guiNode.setRenderQueueMode(Renderer.QUEUE_ORTHO); + guiNode.updateGeometricState(0.0f, true); + guiNode.updateRenderState(); } protected void buildUI() { Index: src/com/jmex/game/state/StatisticsGameState.java =================================================================== --- src/com/jmex/game/state/StatisticsGameState.java (revision 3942) +++ src/com/jmex/game/state/StatisticsGameState.java (working copy) @@ -31,8 +31,6 @@ */ package com.jmex.game.state; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; import java.util.logging.Logger; import com.jme.renderer.ColorRGBA; @@ -42,7 +40,6 @@ import com.jme.scene.shape.Quad; import com.jme.system.DisplaySystem; import com.jme.util.Debug; -import com.jme.util.GameTaskQueueManager; import com.jme.util.stat.StatCollector; import com.jme.util.stat.StatType; import com.jme.util.stat.graph.GraphFactory; @@ -147,22 +144,12 @@ StatCollector.resume(); } }; - - try { - GameTaskQueueManager.getManager().update(new Callable() { - public Object call() throws Exception { - if (doLineGraph) { - lgrapher = GraphFactory.makeLineGraph((int)(lineGraph.getWidth()+.5f), (int)(lineGraph.getHeight()+.5f), lineGraph); - } - tgrapher = GraphFactory.makeTabledLabelGraph((int)(labGraph.getWidth()+.5f), (int)(labGraph.getHeight()+.5f), labGraph); - return null; - } - }).get(); - } catch (InterruptedException e) { - e.printStackTrace(); - } catch (ExecutionException e) { - e.printStackTrace(); + + if (doLineGraph) { + lgrapher = GraphFactory.makeLineGraph((int)(lineGraph.getWidth()+.5f), (int)(lineGraph.getHeight()+.5f), lineGraph); } + tgrapher = GraphFactory.makeTabledLabelGraph((int)(labGraph.getWidth()+.5f), (int)(labGraph.getHeight()+.5f), labGraph); + if (doLineGraph) { lineGraph.setLocalTranslation((lineGraph.getWidth()*.5f), display.getHeight()-labGraph.getHeight()-(lineGraph.getHeight()*0.5f),0); lineGraph.getDefaultColor().a = alpha; Index: src/jmetest/settings/TestSwingSettingsEditor.java =================================================================== --- src/jmetest/settings/TestSwingSettingsEditor.java (revision 3942) +++ src/jmetest/settings/TestSwingSettingsEditor.java (working copy) @@ -36,6 +36,7 @@ import java.util.concurrent.Callable; import javax.swing.JInternalFrame; +import javax.swing.SwingUtilities; import com.jme.input.MouseInput; import com.jme.util.GameTaskQueueManager; @@ -51,29 +52,37 @@ public static void main(String[] args) throws Exception { final StandardGame game = new StandardGame("TestSwingSettingsEditor"); game.start(); - - // Create a game state to display the configuration menu - final JMEDesktopState desktopState = new JMEDesktopState(); - GameStateManager.getInstance().attachChild(desktopState); - desktopState.setActive(true); - GameTaskQueueManager.getManager().update(new Callable() { - public Object call() throws Exception { - JInternalFrame frame = new JInternalFrame(); - frame.setTitle("Configure Settings"); - Container c = frame.getContentPane(); - c.setLayout(new BorderLayout()); - - GameSettingsPanel csp = new GameSettingsPanel(game.getSettings()); - c.add(csp, BorderLayout.CENTER); - - frame.pack(); - frame.setLocation(200, 100); - frame.setVisible(true); - desktopState.getDesktop().getJDesktop().add(frame); - + + GameTaskQueueManager.getManager().update(new Callable(){ + + @Override + public Void call() throws Exception { + // Create a game state to display the configuration menu + final JMEDesktopState desktopState = new JMEDesktopState(); + GameStateManager.getInstance().attachChild(desktopState); + desktopState.setActive(true); + // Show the mouse cursor MouseInput.get().setCursorVisible(true); - + + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + JInternalFrame frame = new JInternalFrame(); + frame.setTitle("Configure Settings"); + Container c = frame.getContentPane(); + c.setLayout(new BorderLayout()); + + GameSettingsPanel csp = new GameSettingsPanel(game.getSettings()); + c.add(csp, BorderLayout.CENTER); + + frame.pack(); + frame.setLocation(200, 100); + frame.setVisible(true); + desktopState.getDesktop().getJDesktop().add(frame); + }}); + return null; } }); Index: src/jmetest/awt/swingui/TestJMEDesktopState.java =================================================================== --- src/jmetest/awt/swingui/TestJMEDesktopState.java (revision 3942) +++ src/jmetest/awt/swingui/TestJMEDesktopState.java (working copy) @@ -37,6 +37,7 @@ import javax.swing.JButton; import javax.swing.JInternalFrame; +import javax.swing.SwingUtilities; import com.jme.bounding.BoundingSphere; import com.jme.input.MouseInput; @@ -56,44 +57,51 @@ final StandardGame game = new StandardGame("TestJMEDesktopState"); game.start(); - // Lets add a game state behind with some content - BasicGameState debugState = new BasicGameState("BasicGameState"); - GameStateManager.getInstance().attachChild(debugState); - debugState.setActive(true); - Sphere sphere = new Sphere("ExampleSphere", 50, 50, 5.0f); - sphere.setRandomColors(); - sphere.updateRenderState(); - sphere.setModelBound(new BoundingSphere()); - sphere.updateModelBound(); - debugState.getRootNode().attachChild(sphere); - - // Instantiate and add our JMEDesktopState - final JMEDesktopState desktopState = new JMEDesktopState(); - GameStateManager.getInstance().attachChild(desktopState); - desktopState.setActive(true); - GameTaskQueueManager.getManager().update(new Callable() { public Object call() throws Exception { - // Add a Quit button - JButton button = new JButton("Quit" ); - desktopState.getDesktop().getJDesktop().add(button); - button.setLocation(0, 0); - button.setSize(button.getPreferredSize()); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - game.finish(); + // Lets add a game state behind with some content + BasicGameState debugState = new BasicGameState("BasicGameState"); + GameStateManager.getInstance().attachChild(debugState); + debugState.setActive(true); + Sphere sphere = new Sphere("ExampleSphere", 50, 50, 5.0f); + sphere.setRandomColors(); + sphere.updateRenderState(); + sphere.setModelBound(new BoundingSphere()); + sphere.updateModelBound(); + debugState.getRootNode().attachChild(sphere); + + // Instantiate and add our JMEDesktopState + final JMEDesktopState desktopState = new JMEDesktopState(); + GameStateManager.getInstance().attachChild(desktopState); + desktopState.setActive(true); + + // Set the cursor to visible + MouseInput.get().setCursorVisible(true); + + SwingUtilities.invokeLater(new Runnable(){ + + @Override + public void run() { + // Add a Quit button + JButton button = new JButton("Quit" ); + desktopState.getDesktop().getJDesktop().add(button); + button.setLocation(0, 0); + button.setSize(button.getPreferredSize()); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + game.finish(); + } + }); + + // Add JInternalFrame + JInternalFrame frame = new JInternalFrame("Test Internal Frame", true, true, true); + frame.setSize(200, 200); + frame.setLocation(100, 100); + frame.setVisible(true); + desktopState.getDesktop().getJDesktop().add(frame); } - }); - - // Add JInternalFrame - JInternalFrame frame = new JInternalFrame("Test Internal Frame", true, true, true); - frame.setSize(200, 200); - frame.setLocation(100, 100); - frame.setVisible(true); - desktopState.getDesktop().getJDesktop().add(frame); - - // Set the cursor to visible - MouseInput.get().setCursorVisible(true); + }); + return null; } }); Index: src/jmetest/game/state/TestJMEDesktopState.java =================================================================== --- src/jmetest/game/state/TestJMEDesktopState.java (revision 3942) +++ src/jmetest/game/state/TestJMEDesktopState.java (working copy) @@ -31,11 +31,15 @@ */ package jmetest.game.state; +import java.util.concurrent.Callable; + import javax.swing.JButton; +import javax.swing.SwingUtilities; import com.jme.bounding.BoundingSphere; import com.jme.math.Vector3f; import com.jme.scene.shape.Box; +import com.jme.util.GameTaskQueueManager; import com.jmex.awt.swingui.JMEDesktopState; import com.jmex.game.StandardGame; import com.jmex.game.state.DebugGameState; @@ -49,24 +53,39 @@ StandardGame game = new StandardGame("Testing JMEDesktopState"); game.start(); - // Create a DebugGameState - has all the built-in features that SimpleGame provides - // NOTE: for a distributable game implementation you'll want to use something like - // BasicGameState instead and provide control features yourself. - DebugGameState state = new DebugGameState(); - Box box = new Box("my box", new Vector3f(0, 0, 0), 2, 2, 2); - box.setModelBound(new BoundingSphere()); - box.updateModelBound(); - box.updateRenderState(); - state.getRootNode().attachChild(box); - GameStateManager.getInstance().attachChild(state); - state.setActive(true); - - JMEDesktopState desktop = new JMEDesktopState(); - JButton button = new JButton("Click Me"); - desktop.getDesktop().getJDesktop().add(button); - button.setLocation(200, 200); - button.setSize(button.getPreferredSize()); - GameStateManager.getInstance().attachChild(desktop); - desktop.setActive(true); + GameTaskQueueManager.getManager().update(new Callable() { + + @Override + public Void call() throws Exception { + // Create a DebugGameState - has all the built-in features that SimpleGame provides + // NOTE: for a distributable game implementation you'll want to use something like + // BasicGameState instead and provide control features yourself. + DebugGameState state = new DebugGameState(); + Box box = new Box("my box", new Vector3f(0, 0, 0), 2, 2, 2); + box.setModelBound(new BoundingSphere()); + box.updateModelBound(); + box.updateRenderState(); + state.getRootNode().attachChild(box); + GameStateManager.getInstance().attachChild(state); + state.setActive(true); + + final JMEDesktopState desktop = new JMEDesktopState(); + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + JButton button = new JButton("Click Me"); + desktop.getDesktop().getJDesktop().add(button); + button.setLocation(200, 200); + button.setSize(button.getPreferredSize()); + } + }); + + GameStateManager.getInstance().attachChild(desktop); + desktop.setActive(true); + + return null; + } + }); } } Index: src/jmetest/game/state/TestLoadingGameState.java =================================================================== --- src/jmetest/game/state/TestLoadingGameState.java (revision 3942) +++ src/jmetest/game/state/TestLoadingGameState.java (working copy) @@ -31,6 +31,9 @@ */ package jmetest.game.state; +import java.util.concurrent.Callable; + +import com.jme.util.GameTaskQueueManager; import com.jmex.game.StandardGame; import com.jmex.game.state.DebugGameState; import com.jmex.game.state.GameStateManager; @@ -44,33 +47,62 @@ StandardGame game = new StandardGame("Test LoadingGameState"); game.getSettings().clear(); game.start(); + + GameTaskQueueManager.getManager().update(new Callable(){ + + @Override + public Void call() throws Exception { + // Create LoadingGameState and enable + final LoadingGameState loading = new LoadingGameState(); + GameStateManager.getInstance().attachChild(loading); + loading.setActive(true); + + // Enable DebugGameState + DebugGameState debug = new DebugGameState(); + GameStateManager.getInstance().attachChild(debug); + debug.setActive(true); + + GameTaskQueueManager.getManager().update(new LoadingTask(loading, 0)); + + return null; + } + }); + } + + private static class LoadingTask implements Callable { + private final LoadingGameState loading; + private final int progress; - // Create LoadingGameState and enable - LoadingGameState loading = new LoadingGameState(); - GameStateManager.getInstance().attachChild(loading); - loading.setActive(true); + public LoadingTask(LoadingGameState loading, int progress) { + super(); + this.loading = loading; + this.progress = progress; + } - // Enable DebugGameState - DebugGameState debug = new DebugGameState(); - GameStateManager.getInstance().attachChild(debug); - debug.setActive(true); - - // Start our slow loading test - String status = "Started Loading"; - for (int i = 0; i <= 100; i++) { - if (i == 100) { + @Override + public Void call() throws Exception { + String status; + if (progress == 100) { status = "I'm Finished!"; - } else if (i > 80) { + } else if (progress > 80) { status = "Almost There!"; - } else if (i > 70) { + } else if (progress > 70) { status = "Loading Something Extremely Useful"; - } else if (i > 50) { + } else if (progress > 50) { status = "More Than Half-Way There!"; - } else if (i > 20) { + } else if (progress > 20) { status = "Loading Something That You Probably Won't Care About"; + } else { + status = "Started Loading"; } + Thread.sleep(100); - loading.setProgress(i / 100.0f, status); + loading.setProgress(progress / 100.0f, status); + + if (progress < 100) { + GameTaskQueueManager.getManager().update(new LoadingTask(loading, progress + 1)); + } + return null; } } } Index: src/jmetest/game/state/TestDebugGameState.java =================================================================== --- src/jmetest/game/state/TestDebugGameState.java (revision 3942) +++ src/jmetest/game/state/TestDebugGameState.java (working copy) @@ -31,8 +31,11 @@ */ package jmetest.game.state; +import java.util.concurrent.Callable; + import com.jme.math.Vector3f; import com.jme.scene.shape.Box; +import com.jme.util.GameTaskQueueManager; import com.jmex.game.StandardGame; import com.jmex.game.state.DebugGameState; import com.jmex.game.state.GameStateManager; @@ -47,13 +50,21 @@ StandardGame game = new StandardGame("TestGame"); // Create our game game.start(); // Start the game thread - DebugGameState gameState = new DebugGameState(); // Create our game state - GameStateManager.getInstance().attachChild(gameState); // Attach it to the GameStateManager - gameState.setActive(true); // Activate it - - Box box = new Box("TestBox", new Vector3f(), 1.0f, 1.0f, 1.0f); // Create a Box - gameState.getRootNode().attachChild(box); // Attach the box to rootNode in DebugGameState - box.setRandomColors(); // Set random colors on it - it will only be visible if the lights are off though - box.updateRenderState(); // Update the render state so the colors appear (the game is already running, so this must always be done) + GameTaskQueueManager.getManager().update(new Callable(){ + + @Override + public Void call() throws Exception { + DebugGameState gameState = new DebugGameState(); // Create our game state + GameStateManager.getInstance().attachChild(gameState); // Attach it to the GameStateManager + gameState.setActive(true); // Activate it + + Box box = new Box("TestBox", new Vector3f(), 1.0f, 1.0f, 1.0f); // Create a Box + gameState.getRootNode().attachChild(box); // Attach the box to rootNode in DebugGameState + box.setRandomColors(); // Set random colors on it - it will only be visible if the lights are off though + box.updateRenderState(); // Update the render state so the colors appear (the game is already running, so this must always be done) + + return null; + } + }); } } Index: src/jmetest/game/TestStandardGame.java =================================================================== --- src/jmetest/game/TestStandardGame.java (revision 3942) +++ src/jmetest/game/TestStandardGame.java (working copy) @@ -32,9 +32,12 @@ package jmetest.game; +import java.util.concurrent.Callable; + import com.jme.bounding.BoundingSphere; import com.jme.math.Vector3f; import com.jme.scene.shape.Box; +import com.jme.util.GameTaskQueueManager; import com.jmex.editors.swing.settings.GameSettingsPanel; import com.jmex.game.StandardGame; import com.jmex.game.state.DebugGameState; @@ -59,22 +62,30 @@ // Start StandardGame, it will block until it has initialized successfully, then return game.start(); - // Create a DebugGameState - has all the built-in features that SimpleGame provides - // NOTE: for a distributable game implementation you'll want to use something like - // BasicGameState instead and provide control features yourself. - DebugGameState state = new DebugGameState(); - // Put our box in it - Box box = new Box("my box", new Vector3f(0, 0, 0), 2, 2, 2); - box.setModelBound(new BoundingSphere()); - box.updateModelBound(); - // We had to add the following line because the render thread is already running - // Anytime we add content we need to updateRenderState or we get funky effects - state.getRootNode().attachChild(box); - box.updateRenderState(); - // Add it to the manager - GameStateManager.getInstance().attachChild(state); - // Activate the game state - state.setActive(true); + GameTaskQueueManager.getManager().update(new Callable() { + + @Override + public Void call() throws Exception { + // Create a DebugGameState - has all the built-in features that SimpleGame provides + // NOTE: for a distributable game implementation you'll want to use something like + // BasicGameState instead and provide control features yourself. + DebugGameState state = new DebugGameState(); + // Put our box in it + Box box = new Box("my box", new Vector3f(0, 0, 0), 2, 2, 2); + box.setModelBound(new BoundingSphere()); + box.updateModelBound(); + // We had to add the following line because the render thread is already running + // Anytime we add content we need to updateRenderState or we get funky effects + state.getRootNode().attachChild(box); + box.updateRenderState(); + // Add it to the manager + GameStateManager.getInstance().attachChild(state); + // Activate the game state + state.setActive(true); + + return null; + } + }); } } } Index: src/jmetest/effects/RenParticleEditor.java =================================================================== --- src/jmetest/effects/RenParticleEditor.java (revision 3942) +++ src/jmetest/effects/RenParticleEditor.java (working copy) @@ -81,6 +81,7 @@ import javax.swing.JTable; import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; @@ -196,12 +197,17 @@ * String[] */ public static void main(String[] args) { - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception e) { - logger.logp(Level.SEVERE, RenParticleEditor.class.toString(), "main(args)", "Exception", e); - } - new RenParticleEditor(); + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + logger.logp(Level.SEVERE, RenParticleEditor.class.toString(), "main(args)", "Exception", e); + } + new RenParticleEditor(); + }}); } public RenParticleEditor() { @@ -287,8 +293,8 @@ yUp.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - Callable exe = new Callable() { - public Object call() { + Callable exe = new Callable() { + public Void call() { camhand.worldUpVector.set(Vector3f.UNIT_Y); Camera cam = impl.getRenderer().getCamera(); cam.getLocation().set(0, 850, -850); @@ -306,8 +312,8 @@ }); zUp.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - Callable exe = new Callable() { - public Object call() { + Callable exe = new Callable() { + public Void call() { camhand.worldUpVector.set(Vector3f.UNIT_Z); Camera cam = impl.getRenderer().getCamera(); cam.getLocation().set(0, -850, 850); @@ -324,8 +330,8 @@ } }); - Callable exe = new Callable() { - public Object call() { + Callable exe = new Callable() { + public Void call() { if (prefs.getBoolean("yUp", true)) { yUp.doClick(); } else { @@ -783,8 +789,8 @@ .getBackgroundColor(), false)); if (bg != null) { prefs.putInt("bg_color", bg.getRGB()); - Callable exe = new Callable() { - public Object call() { + Callable exe = new Callable() { + public Void call() { impl.getRenderer().setBackgroundColor(makeColorRGBA(bg)); return null; } @@ -1158,8 +1164,8 @@ // -----------END OF GL STUFF------------- - Callable exe = new Callable() { - public Object call() { + Callable exe = new Callable() { + public Void call() { forceUpdateToSize(); ((JMECanvas) glCanvas).setVSync(false); return null; @@ -1185,8 +1191,8 @@ public Vector3f worldUpVector = Vector3f.UNIT_Y.clone(); public void mouseDragged(final MouseEvent arg0) { - Callable exe = new Callable() { - public Object call() { + Callable exe = new Callable() { + public Void call() { int difX = last.x - arg0.getX(); int difY = last.y - arg0.getY(); int mult = arg0.isShiftDown() ? 10 : 1; @@ -1221,8 +1227,8 @@ } public void mouseWheelMoved(final MouseWheelEvent arg0) { - Callable exe = new Callable() { - public Object call() { + Callable exe = new Callable() { + public Void call() { zoomCamera(arg0.getWheelRotation() * (arg0.isShiftDown() ? -100 : -20)); return null; @@ -1233,8 +1239,8 @@ } public void recenterCamera() { - Callable exe = new Callable() { - public Object call() { + Callable exe = new Callable() { + public Void call() { Camera cam = impl.getRenderer().getCamera(); Vector3f.ZERO.subtract(focus, vector); cam.getLocation().addLocal(vector); @@ -1287,8 +1293,8 @@ if (impl != null) { impl.resizeCanvas(glCanvas.getWidth(), glCanvas.getHeight()); if (impl.getCamera() != null) { - Callable exe = new Callable() { - public Object call() { + Callable exe = new Callable() { + public Void call() { impl.getCamera().setFrustumPerspective( 45.0f, (float) glCanvas.getWidth() Index: src/jmetest/input/controls/TestSwingControlEditor.java =================================================================== --- src/jmetest/input/controls/TestSwingControlEditor.java (revision 3942) +++ src/jmetest/input/controls/TestSwingControlEditor.java (working copy) @@ -43,6 +43,7 @@ import javax.swing.JButton; import javax.swing.JInternalFrame; import javax.swing.JPanel; +import javax.swing.SwingUtilities; import com.jme.image.Texture; import com.jme.input.MouseInput; @@ -88,185 +89,211 @@ final StandardGame game = new StandardGame("TestSwingControlEditor"); game.start(); - // Create our sample GameControls - manager = GameControlManager.load(game.getSettings()); - if (manager == null) { - manager = new GameControlManager(); - manager.addControl("Forward"); - manager.addControl("Backward"); - manager.addControl("Rotate Left"); - manager.addControl("Rotate Right"); - manager.addControl("Jump"); - manager.addControl("Crouch"); - manager.addControl("Run"); - manager.addControl("Fire"); - manager.addControl("Cycle Camera"); - } - - // Create a game state to display the configuration menu - final JMEDesktopState desktopState = new JMEDesktopState(); - GameStateManager.getInstance().attachChild(desktopState); - desktopState.setActive(true); - - BasicGameState state = new BasicGameState("Basic"); - GameStateManager.getInstance().attachChild(state); - state.setActive(true); - - // Create Box - Box box = new Box("Test Node", new Vector3f(), 5.0f, 5.0f, 5.0f); - state.getRootNode().attachChild(box); - TextureState ts = game.getDisplay().getRenderer().createTextureState(); - Texture t = TextureManager.loadTexture(TestSwingControlEditor.class.getClassLoader().getResource("jmetest/data/images/Monkey.jpg"), Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear); - t.setWrap(Texture.WrapMode.Repeat); - ts.setTexture(t); - box.setRenderState(ts); - box.updateRenderState(); - - // Create Throttle Controller - final ThrottleController throttle = new ThrottleController(box, manager.getControl("Forward"), 1.0f, manager.getControl("Backward"), -1.0f, 0.05f, 0.5f, 1.0f, false, Axis.Z); - state.getRootNode().addController(throttle); - - final TextGameState textState = new TextGameState("Throttle: 0"); - GameStateManager.getInstance().attachChild(textState); - textState.setActive(true); - - // Monitor the throttle - Controller monitor = new Controller() { - private static final long serialVersionUID = 1L; + GameTaskQueueManager.getManager().update(new SetupState(game)); + } + + private static class SetupState implements Callable { + private final StandardGame game; + + public SetupState(StandardGame game) { + super(); + this.game = game; + } - public void update(float time) { - textState.setText(throttle.getCurrentThrottle() + ", " + throttle.getThrust()); - } - }; - state.getRootNode().addController(monitor); - - // Create Rotation Controller - state.getRootNode().addController(new RotationController(box, manager.getControl("Rotate Left"), manager.getControl("Rotate Right"), 0.2f, Axis.Y)); - // Create ActionController - GameControlAction action = new GameControlAction() { - public void pressed(GameControl control, float time) { - logger.info("Pressed: " + control.getName() + ", elapsed: " + time); - } + public Void call() throws Exception { + // Create our sample GameControls + manager = GameControlManager.load(game.getSettings()); + if (manager == null) { + manager = new GameControlManager(); + manager.addControl("Forward"); + manager.addControl("Backward"); + manager.addControl("Rotate Left"); + manager.addControl("Rotate Right"); + manager.addControl("Jump"); + manager.addControl("Crouch"); + manager.addControl("Run"); + manager.addControl("Fire"); + manager.addControl("Cycle Camera"); + } - public void released(GameControl control, float time) { - logger.info("Released: " + control.getName() + " after " + time); - } - }; - // Jump and Crouch only care about press and release - state.getRootNode().addController(new ActionController(manager.getControl("Jump"), action)); - state.getRootNode().addController(new ActionController(manager.getControl("Crouch"), action)); - // Run cares about the change - doesn't really make sense, but this is just for testing - ControlChangeListener listener = new ControlChangeListener() { - public void changed(GameControl control, float oldValue, float newValue, float time) { - logger.info("Changed: " + control.getName() + ", " + oldValue + ", " + newValue + ", " + time); - } - }; - state.getRootNode().addController(new ActionChangeController(manager.getControl("Run"), listener)); - Runnable runnable = new Runnable() { - private long lastRun; - public void run() { - if (lastRun == 0) lastRun = System.currentTimeMillis(); - logger.info("KABOOM: " + (System.currentTimeMillis() - lastRun)); - lastRun = System.currentTimeMillis(); - } - }; - // Fire action can only occur once per second - state.getRootNode().addController(new ActionRepeatController(manager.getControl("Fire"), 1000, runnable)); - // Create CameraController - CameraController cc = new CameraController(box, game.getCamera(), manager.getControl("Cycle Camera")); - cc.addPerspective(new CameraPerspective() { - private Camera camera; - private Vector3f location; - private Vector3f dir; - private Vector3f left; - private Vector3f up; - - public void update(Camera camera, Spatial spatial, float time) { - if (this.camera == null) { - this.camera = camera; - try { - location = (Vector3f)camera.getLocation().clone(); - dir = (Vector3f)camera.getDirection().clone(); - left = (Vector3f)camera.getLeft().clone(); - up = (Vector3f)camera.getUp().clone(); - } catch(Exception exc) { - logger.logp(Level.SEVERE, this.getClass().toString(), - "main(args)", "Exception", exc); + // Create a game state to display the configuration menu + final JMEDesktopState desktopState = new JMEDesktopState(); + GameStateManager.getInstance().attachChild(desktopState); + desktopState.setActive(true); + + BasicGameState state = new BasicGameState("Basic"); + GameStateManager.getInstance().attachChild(state); + state.setActive(true); + + // Create Box + Box box = new Box("Test Node", new Vector3f(), 5.0f, 5.0f, 5.0f); + state.getRootNode().attachChild(box); + TextureState ts = game.getDisplay().getRenderer().createTextureState(); + Texture t = TextureManager.loadTexture(TestSwingControlEditor.class.getClassLoader().getResource("jmetest/data/images/Monkey.jpg"), Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear); + t.setWrap(Texture.WrapMode.Repeat); + ts.setTexture(t); + box.setRenderState(ts); + box.updateRenderState(); + + // Create Throttle Controller + final ThrottleController throttle = new ThrottleController(box, manager.getControl("Forward"), 1.0f, manager.getControl("Backward"), -1.0f, 0.05f, 0.5f, 1.0f, false, Axis.Z); + state.getRootNode().addController(throttle); + + final TextGameState textState = new TextGameState("Throttle: 0"); + GameStateManager.getInstance().attachChild(textState); + textState.setActive(true); + + // Monitor the throttle + Controller monitor = new Controller() { + private static final long serialVersionUID = 1L; + + public void update(float time) { + textState.setText(throttle.getCurrentThrottle() + ", " + throttle.getThrust()); + } + }; + state.getRootNode().addController(monitor); + + // Create Rotation Controller + state.getRootNode().addController(new RotationController(box, manager.getControl("Rotate Left"), manager.getControl("Rotate Right"), 0.2f, Axis.Y)); + // Create ActionController + GameControlAction action = new GameControlAction() { + public void pressed(GameControl control, float time) { + logger.info("Pressed: " + control.getName() + ", elapsed: " + time); + } + + public void released(GameControl control, float time) { + logger.info("Released: " + control.getName() + " after " + time); + } + }; + // Jump and Crouch only care about press and release + state.getRootNode().addController(new ActionController(manager.getControl("Jump"), action)); + state.getRootNode().addController(new ActionController(manager.getControl("Crouch"), action)); + // Run cares about the change - doesn't really make sense, but this is just for testing + ControlChangeListener listener = new ControlChangeListener() { + public void changed(GameControl control, float oldValue, float newValue, float time) { + logger.info("Changed: " + control.getName() + ", " + oldValue + ", " + newValue + ", " + time); + } + }; + state.getRootNode().addController(new ActionChangeController(manager.getControl("Run"), listener)); + Runnable runnable = new Runnable() { + private long lastRun; + public void run() { + if (lastRun == 0) lastRun = System.currentTimeMillis(); + logger.info("KABOOM: " + (System.currentTimeMillis() - lastRun)); + lastRun = System.currentTimeMillis(); + } + }; + // Fire action can only occur once per second + state.getRootNode().addController(new ActionRepeatController(manager.getControl("Fire"), 1000, runnable)); + // Create CameraController + CameraController cc = new CameraController(box, game.getCamera(), manager.getControl("Cycle Camera")); + cc.addPerspective(new CameraPerspective() { + private Camera camera; + private Vector3f location; + private Vector3f dir; + private Vector3f left; + private Vector3f up; + + public void update(Camera camera, Spatial spatial, float time) { + if (this.camera == null) { + this.camera = camera; + try { + location = (Vector3f)camera.getLocation().clone(); + dir = (Vector3f)camera.getDirection().clone(); + left = (Vector3f)camera.getLeft().clone(); + up = (Vector3f)camera.getUp().clone(); + } catch(Exception exc) { + logger.logp(Level.SEVERE, this.getClass().toString(), + "main(args)", "Exception", exc); + } + } else if (!camera.getLocation().equals(location)) { + logger.info("Changing from: " + camera.getDirection() + " to " + dir); + logger.info("Another: " + camera.getUp() + "\nAnd: " + camera.getLeft()); + camera.setLocation(location); + camera.setDirection(dir); + camera.setLeft(left); + camera.setUp(up); + } + } + + + public void setActive(Camera camera, Spatial spatial, boolean active) { + } + }); + cc.addPerspective(new FixedCameraPerspective(new Vector3f(0.0f, 0.0f, -15.0f))); + state.getRootNode().addController(cc); + + // Show the mouse cursor + MouseInput.get().setCursorVisible(true); + + SwingUtilities.invokeAndWait(new UISetup(game, desktopState)); + + return null; + }}; + + private static class UISetup implements Runnable { + private final StandardGame game; + private final JMEDesktopState desktopState; + + public UISetup(StandardGame game, JMEDesktopState desktopState) { + super(); + this.game = game; + this.desktopState = desktopState; + } + + @Override + public void run() { + JInternalFrame frame = new JInternalFrame(); + frame.setTitle("Configure Controls"); + Container c = frame.getContentPane(); + c.setLayout(new BorderLayout()); + final GameControlEditor editor = new GameControlEditor(manager, 2); + c.add(editor, BorderLayout.CENTER); + JPanel bottom = new JPanel(); + bottom.setLayout(new FlowLayout()); + JButton button = new JButton("Close"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + game.finish(); + } + }); + bottom.add(button); + button = new JButton("Clear"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + editor.clear(); + } + }); + bottom.add(button); + button = new JButton("Reset"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + editor.reset(); + } + }); + bottom.add(button); + button = new JButton("Apply"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + editor.apply(); // Apply bindings + GameControlManager.save(manager, game.getSettings()); // Save them + for (GameControl control : manager.getControls()) { + logger.info(control.getName() + ":"); + for (Binding binding : control.getBindings()) { + logger.info("\t" + binding.getName()); + } + logger.info("-------"); } - } else if (!camera.getLocation().equals(location)) { - logger.info("Changing from: " + camera.getDirection() + " to " + dir); - logger.info("Another: " + camera.getUp() + "\nAnd: " + camera.getLeft()); - camera.setLocation(location); - camera.setDirection(dir); - camera.setLeft(left); - camera.setUp(up); } - } + }); + bottom.add(button); + c.add(bottom, BorderLayout.SOUTH); + frame.pack(); + frame.setLocation(200, 100); + frame.setVisible(true); + desktopState.getDesktop().getJDesktop().add(frame); + } + + } - - public void setActive(Camera camera, Spatial spatial, boolean active) { - } - }); - cc.addPerspective(new FixedCameraPerspective(new Vector3f(0.0f, 0.0f, -15.0f))); - state.getRootNode().addController(cc); - - GameTaskQueueManager.getManager().update(new Callable() { - public Object call() throws Exception { - JInternalFrame frame = new JInternalFrame(); - frame.setTitle("Configure Controls"); - Container c = frame.getContentPane(); - c.setLayout(new BorderLayout()); - final GameControlEditor editor = new GameControlEditor(manager, 2); - c.add(editor, BorderLayout.CENTER); - JPanel bottom = new JPanel(); - bottom.setLayout(new FlowLayout()); - JButton button = new JButton("Close"); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - game.finish(); - } - }); - bottom.add(button); - button = new JButton("Clear"); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - editor.clear(); - } - }); - bottom.add(button); - button = new JButton("Reset"); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - editor.reset(); - } - }); - bottom.add(button); - button = new JButton("Apply"); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - editor.apply(); // Apply bindings - GameControlManager.save(manager, game.getSettings()); // Save them - for (GameControl control : manager.getControls()) { - logger.info(control.getName() + ":"); - for (Binding binding : control.getBindings()) { - logger.info("\t" + binding.getName()); - } - logger.info("-------"); - } - } - }); - bottom.add(button); - c.add(bottom, BorderLayout.SOUTH); - frame.pack(); - frame.setLocation(200, 100); - frame.setVisible(true); - desktopState.getDesktop().getJDesktop().add(frame); - - // Show the mouse cursor - MouseInput.get().setCursorVisible(true); - - return null; - } - }); - } } Index: src/jmetest/text/Test3DFlatText.java =================================================================== --- src/jmetest/text/Test3DFlatText.java (revision 3942) +++ src/jmetest/text/Test3DFlatText.java (working copy) @@ -52,12 +52,14 @@ StandardGame game = new StandardGame("Test 3D Flat Text"); game.start(); - final DebugGameState debug = new DebugGameState(); - GameStateManager.getInstance().attachChild(debug); - debug.setActive(true); - - GameTaskQueueManager.getManager().update(new Callable() { - public Object call() throws Exception { + GameTaskQueueManager.getManager().update(new Callable() { + + @Override + public Void call() throws Exception { + final DebugGameState debug = new DebugGameState(); + GameStateManager.getInstance().attachChild(debug); + debug.setActive(true); + Font3D font = new Font3D(new Font("Arial", Font.PLAIN, 24), 0.001f, true, true, true); Text3D text = font.createText("Testing 1, 2, 3", 50.0f, 0); text.setLocalScale(new Vector3f(5.0f, 5.0f, 0.01f));