Просмотр исходного кода

SDK:
- Improve FakeApplication, integrate into main application
- Add separate execution thread for user code
- Add error handling for failing AppStates
- Add error handling for failing Controls
- AppStateExplorer now works when opening it after the scene

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10079 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

nor..67 12 лет назад
Родитель
Сommit
7dfc14f6e1

+ 13 - 12
jme3-core/src/com/jme3/gde/core/appstates/AppStateExplorerTopComponent.java

@@ -74,31 +74,27 @@ preferredID = "AppStateExplorerTopComponent")
 public final class AppStateExplorerTopComponent extends TopComponent implements ExplorerManager.Provider {
 
     private transient ExplorerManager explorerManager = new ExplorerManager();
-    private FakeApplication fakeApp;
     private ProjectAssetManager mgr;
+    private SceneRequest currentRequest;
+    //TODO: move to global place
     private SceneListener listener = new SceneListener() {
         public void sceneOpened(SceneRequest request) {
+            currentRequest = request;
             Spatial rootNode = request.getRootNode();
             if (!(rootNode instanceof com.jme3.scene.Node)) {
                 return;
             }
             mgr = request.getManager();
-            AssetManager assetManager = request.getManager();
-            Camera cam = SceneApplication.getApplication().getCamera();
-            com.jme3.scene.Node guiNode = SceneApplication.getApplication().getGuiNode();
-            fakeApp = new FakeApplication((com.jme3.scene.Node) rootNode, guiNode, assetManager, cam);
-            //TODO: ermagherd, hackish
-            SceneApplication.getApplication().setFakeApp(fakeApp);
-            final AppStateManagerNode nod = new AppStateManagerNode(fakeApp.getStateManager());
+            final AppStateManagerNode nod = new AppStateManagerNode(request.getFakeApp().getStateManager());
             jButton1.setEnabled(true);
             explorerManager.setRootContext(nod);
             setActivatedNodes(new Node[]{nod});
         }
 
         public void sceneClosed(SceneRequest request) {
+            currentRequest = null;
             SceneApplication.getApplication().setFakeApp(null);
             mgr = null;
-            fakeApp = null;
             jButton1.setEnabled(false);
             explorerManager.setRootContext(Node.EMPTY);
             setActivatedNodes(new Node[]{Node.EMPTY});
@@ -117,6 +113,11 @@ public final class AppStateExplorerTopComponent extends TopComponent implements
 //        map.put("moveup", new MoveUpAction());
 //        map.put("movedown", new MoveDownAction());
         associateLookup(ExplorerUtils.createLookup(explorerManager, map));
+        //TODO: move to scene listener notify in scene?
+        SceneRequest request = SceneApplication.getApplication().getCurrentSceneRequest();
+        if (request != null) {
+            listener.sceneOpened(request);
+        }
         SceneApplication.getApplication().addSceneListener(listener);
     }
 
@@ -165,9 +166,9 @@ public final class AppStateExplorerTopComponent extends TopComponent implements
 
     private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
         ProjectAssetManager projectAssetManager = mgr;
-        FakeApplication fakeApp = this.fakeApp;
-        if (fakeApp != null && mgr != null) {
-            new NewAppStateWizardAction(projectAssetManager, fakeApp).showWizard();
+        SceneRequest currentRequest = this.currentRequest;
+        if (currentRequest != null && mgr != null && currentRequest.getFakeApp() != null) {
+            new NewAppStateWizardAction(projectAssetManager, currentRequest.getFakeApp()).showWizard();
         }
     }//GEN-LAST:event_jButton1ActionPerformed
     // Variables declaration - do not modify//GEN-BEGIN:variables

+ 1 - 1
jme3-core/src/com/jme3/gde/core/appstates/AppStateManagerNode.java

@@ -32,7 +32,7 @@
 package com.jme3.gde.core.appstates;
 
 import com.jme3.app.state.AppState;
-import com.jme3.gde.core.appstates.FakeApplication.FakeAppStateManager;
+import com.jme3.gde.core.scene.FakeApplication.FakeAppStateManager;
 import java.util.LinkedList;
 import java.util.List;
 import org.openide.nodes.AbstractNode;

+ 5 - 4
jme3-core/src/com/jme3/gde/core/scene/ApplicationLogHandler.java

@@ -48,17 +48,18 @@ public class ApplicationLogHandler extends Handler {
     JmeFormatter formatter = new JmeFormatter();
 
     public ApplicationLogHandler() {
+        io.setErrSeparated(true);
     }
 
     @Override
     public void publish(LogRecord record) {
         if (record.getLevel().equals(Level.SEVERE)) {
             io.getErr().println(formatter.formatMessage(record));
-        }
-        else if (record.getLevel().equals(Level.WARNING)) {
+        } else if (record.getLevel().equals(Level.WARNING)) {
             io.getErr().println(formatter.formatMessage(record));
-        }
-        else {
+        } else if (record.getLevel().equals(Level.INFO)) {
+            io.getOut().println(formatter.formatMessage(record));
+        } else {
             io.getOut().println(formatter.formatMessage(record));
         }
     }

+ 164 - 37
jme3-core/src/com/jme3/gde/core/appstates/FakeApplication.java → jme3-core/src/com/jme3/gde/core/scene/FakeApplication.java

@@ -29,7 +29,7 @@
  * 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.gde.core.appstates;
+package com.jme3.gde.core.scene;
 
 import com.jme3.app.Application;
 import com.jme3.app.SimpleApplication;
@@ -38,6 +38,7 @@ import com.jme3.app.state.AppStateManager;
 import com.jme3.asset.AssetManager;
 import com.jme3.audio.AudioRenderer;
 import com.jme3.audio.Listener;
+import com.jme3.gde.core.appstates.AppStateManagerNode;
 import com.jme3.input.FlyByCamera;
 import com.jme3.input.InputManager;
 import com.jme3.renderer.Camera;
@@ -45,6 +46,7 @@ import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.Renderer;
 import com.jme3.renderer.ViewPort;
 import com.jme3.scene.Node;
+import com.jme3.scene.control.Control;
 import com.jme3.system.AppSettings;
 import com.jme3.system.JmeContext;
 import com.jme3.system.JmeContext.Type;
@@ -54,9 +56,14 @@ import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import org.openide.DialogDisplayer;
 import org.openide.NotifyDescriptor;
 import org.openide.util.Exceptions;
@@ -332,9 +339,60 @@ public class FakeApplication extends SimpleApplication {
         //TODO: also nice messages
     }
 
+    public static class FakeAppStateManager extends AppStateManager {
+
+        private AppStateManagerNode node;
+        ArrayList<AppState> states = new ArrayList<AppState>();
+
+        public FakeAppStateManager(Application app) {
+            super(app);
+        }
+
+        public List<AppState> getAddedStates() {
+            return states;
+        }
+
+        @Override
+        public boolean attach(AppState state) {
+            boolean ret = super.attach(state);
+            if (ret) {
+                states.add(state);
+            }
+            if (node != null) {
+                node.refresh();
+            }
+            return ret;
+        }
+
+        @Override
+        public boolean detach(AppState state) {
+            boolean ret = super.detach(state);
+            if (ret) {
+                states.remove(state);
+            }
+            if (node != null) {
+                node.refresh();
+            }
+            return ret;
+        }
+
+        public void setNode(AppStateManagerNode node) {
+            this.node = node;
+        }
+    }
     /*
      * Internal
      */
+    private ScheduledThreadPoolExecutor fakeAppThread;
+
+    public void startFakeApp() {
+        fakeAppThread = new ScheduledThreadPoolExecutor(1);
+    }
+
+    public void stopFakeApp() {
+        fakeAppThread.shutdown();
+    }
+
     private void defaultFakeError() {
         defaultFakeError(false);
     }
@@ -366,60 +424,129 @@ public class FakeApplication extends SimpleApplication {
                 NotifyDescriptor.WARNING_MESSAGE));
     }
 
-    public void updateFake(float tpf) {
-//        System.out.println("UPDATE");
-        appStateManager.update(tpf);
+    private void removeAllStates() {
+        for (Iterator<AppState> it = new ArrayList(appStateManager.getAddedStates()).iterator(); it.hasNext();) {
+            AppState appState = it.next();
+            appStateManager.detach(appState);
+        }
     }
 
-    public void renderFake() {
-        appStateManager.render(renderManager);
+    public boolean updateFake(final float tpf) {
+        Future fut = fakeAppThread.submit(new Callable<Void>() {
+            public Void call() throws Exception {
+                appStateManager.update(tpf);
+                return null;
+            }
+        });
+        try {
+            fut.get(1, TimeUnit.MINUTES);
+        } catch (InterruptedException ex) {
+            Exceptions.printStackTrace(ex);
+        } catch (ExecutionException ex) {
+            removeAllStates();
+            DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message("Exception in AppState, all AppStates removed."));
+            return false;
+        } catch (TimeoutException ex) {
+            fut.cancel(true);
+            removeAllStates();
+            DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message("Update loop was blocked for too long, all AppStates removed."));
+            return false;
+        }
+        return true;
     }
 
-    public static class FakeAppStateManager extends AppStateManager {
-
-        private AppStateManagerNode node;
-        ArrayList<AppState> states = new ArrayList<AppState>();
+    public boolean renderFake() {
+        Future fut = fakeAppThread.submit(new Callable<Void>() {
+            public Void call() throws Exception {
+                appStateManager.render(renderManager);
+                return null;
+            }
+        });
+        try {
+            fut.get(1, TimeUnit.MINUTES);
+        } catch (InterruptedException ex) {
+            Exceptions.printStackTrace(ex);
+        } catch (ExecutionException ex) {
+            removeAllStates();
+            DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message("Exception in AppState, all AppStates removed."));
+            return false;
+        } catch (TimeoutException ex) {
+            fut.cancel(true);
+            removeAllStates();
+            DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message("Render loop was blocked for too long, all AppStates removed."));
+            return false;
+        }
+        return true;
+    }
 
-        public FakeAppStateManager(Application app) {
-            super(app);
+    public boolean updateExternalLogicalState(final Node externalNode, final float tpf) {
+        Future fut = fakeAppThread.submit(new Callable<Void>() {
+            public Void call() throws Exception {
+                externalNode.updateLogicalState(tpf);
+                return null;
+            }
+        });
+        try {
+            fut.get(1, TimeUnit.MINUTES);
+        } catch (InterruptedException ex) {
+            Exceptions.printStackTrace(ex);
+        } catch (ExecutionException ex) {
+            clearNode(externalNode);
+            DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message("Exception in Control, scene content removed.\n" + ex.getMessage()));
+            return false;
+        } catch (TimeoutException ex) {
+            fut.cancel(true);
+            clearNode(externalNode);
+            DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message("Render loop was blocked for too long, scene content removed."));
+            return false;
         }
+        return true;
+    }
 
-        public List<AppState> getAddedStates() {
-            return states;
+    public boolean updateExternalGeometricState(final Node externalNode) {
+        Future fut = fakeAppThread.submit(new Callable<Void>() {
+            public Void call() throws Exception {
+                externalNode.updateGeometricState();
+                return null;
+            }
+        });
+        try {
+            fut.get(1, TimeUnit.MINUTES);
+        } catch (InterruptedException ex) {
+            Exceptions.printStackTrace(ex);
+        } catch (ExecutionException ex) {
+            clearNode(externalNode);
+            DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message("Exception in Control, scene content removed.\n" + ex.getMessage()));
+            return false;
+        } catch (TimeoutException ex) {
+            fut.cancel(true);
+            clearNode(externalNode);
+            DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message("Render loop was blocked for too long, scene content removed."));
+            return false;
         }
+        return true;
+    }
 
-        @Override
-        public boolean attach(AppState state) {
-            boolean ret = super.attach(state);
+    private void clearNode(final Node externalNode) {
+        while (!externalNode.getChildren().isEmpty()) {
             try {
-                states.add(state);
+                externalNode.detachAllChildren();
             } catch (Exception e) {
                 Exceptions.printStackTrace(e);
+            } catch (Error e) {
+                Exceptions.printStackTrace(e);
             }
-//            DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(
-//                    "attach state",
-//                    NotifyDescriptor.WARNING_MESSAGE));
-            if (node != null) {
-//                DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(
-//                        "refresh node",
-//                        NotifyDescriptor.WARNING_MESSAGE));
-                node.refresh();
-            }
-            return ret;
         }
-
-        @Override
-        public boolean detach(AppState state) {
+        Control control = externalNode.getControl(Control.class);
+        while (control != null) {
             try {
-                states.remove(state);
+                externalNode.removeControl(control);
             } catch (Exception e) {
                 Exceptions.printStackTrace(e);
+            } catch (Error e) {
+                Exceptions.printStackTrace(e);
             }
-            return super.detach(state);
-        }
-
-        public void setNode(AppStateManagerNode node) {
-            this.node = node;
+            control = externalNode.getControl(Control.class);
         }
     }
 }

+ 24 - 21
jme3-core/src/com/jme3/gde/core/scene/SceneApplication.java

@@ -24,7 +24,6 @@
  */
 package com.jme3.gde.core.scene;
 
-import com.jme3.gde.core.appstates.FakeApplication;
 import com.jme3.app.Application;
 import com.jme3.app.StatsView;
 import com.jme3.bullet.BulletAppState;
@@ -77,7 +76,7 @@ import org.openide.util.NbPreferences;
 import org.openide.util.lookup.Lookups;
 
 /**
- * 
+ *
  * @author normenhansen
  */
 @SuppressWarnings("unchecked")
@@ -171,7 +170,6 @@ public class SceneApplication extends Application implements LookupProvider {
 
     private void attachPanel() {
         enqueue(new Callable() {
-
             public Object call() throws Exception {
                 panel.attachTo(true, viewPort, overlayView, guiViewPort);
                 return null;
@@ -267,7 +265,7 @@ public class SceneApplication extends Application implements LookupProvider {
         }
         try {
             super.update();
-            FakeApplication fakap=fakeApp;
+            FakeApplication fakap = fakeApp;
             float tpf = timer.getTimePerFrame();
             camLight.setPosition(cam.getLocation());
             secondCounter += tpf;
@@ -277,16 +275,21 @@ public class SceneApplication extends Application implements LookupProvider {
                 secondCounter = 0.0f;
             }
             getStateManager().update(tpf);
-            if(fakap!=null){
+            toolsNode.updateLogicalState(tpf);
+            if (fakap != null) {
                 fakap.updateFake(tpf);
+                fakap.updateExternalLogicalState(rootNode, tpf);
+                fakap.updateExternalLogicalState(guiNode, tpf);
+                fakap.updateExternalGeometricState(rootNode);
+                fakap.updateExternalGeometricState(guiNode);
+            } else {
+                rootNode.updateLogicalState(tpf);
+                guiNode.updateLogicalState(tpf);
+                rootNode.updateGeometricState();
+                guiNode.updateGeometricState();
             }
-            rootNode.updateLogicalState(tpf);
-            guiNode.updateLogicalState(tpf);
-            toolsNode.updateLogicalState(tpf);
-            rootNode.updateGeometricState();
-            guiNode.updateGeometricState();
             toolsNode.updateGeometricState();
-            if(fakap!=null){
+            if (fakap != null) {
                 fakap.renderFake();
             }
             getStateManager().render(renderManager);
@@ -331,7 +334,6 @@ public class SceneApplication extends Application implements LookupProvider {
 
     public void notifyPreview(final PreviewRequest request) {
         java.awt.EventQueue.invokeLater(new Runnable() {
-
             public void run() {
                 for (Iterator<SceneListener> it = listeners.iterator(); it.hasNext();) {
                     SceneListener sceneViewerListener = it.next();
@@ -347,12 +349,12 @@ public class SceneApplication extends Application implements LookupProvider {
 
     /**
      * method to display the node tree of a plugin (threadsafe)
+     *
      * @param request
      */
     public void openScene(final SceneRequest request) {
         closeScene(currentSceneRequest, request);
         java.awt.EventQueue.invokeLater(new Runnable() {
-
             public void run() {
                 if (request == null) {
                     return;
@@ -370,8 +372,10 @@ public class SceneApplication extends Application implements LookupProvider {
                 } else {
                     camController.disable();
                 }
+                fakeApp = new FakeApplication(rootNode, guiNode, request.getManager(), cam);
+                fakeApp.startFakeApp();
+                request.setFakeApp(fakeApp);
                 enqueue(new Callable() {
-
                     public Object call() throws Exception {
                         if (request.getManager() != null) {
                             assetManager = request.getManager();
@@ -396,6 +400,7 @@ public class SceneApplication extends Application implements LookupProvider {
 
     /**
      * method to close a scene displayed by a scene request (threadsafe)
+     *
      * @param request
      */
     public void closeScene(final SceneRequest request) {
@@ -404,7 +409,6 @@ public class SceneApplication extends Application implements LookupProvider {
 
     private void closeScene(final SceneRequest oldRequest, final SceneRequest newRequest) {
         java.awt.EventQueue.invokeLater(new Runnable() {
-
             public void run() {
                 if (oldRequest == null) {
                     return;
@@ -425,8 +429,11 @@ public class SceneApplication extends Application implements LookupProvider {
                 if (oldRequest.getRequester() instanceof SceneApplication) {
                     camController.disable();
                 }
+                if (fakeApp != null) {
+                    fakeApp.stopFakeApp();
+                }
+                fakeApp = null;
                 enqueue(new Callable() {
-
                     public Object call() throws Exception {
                         if (physicsState != null) {
                             physicsState.getPhysicsSpace().removeAll(rootNode);
@@ -465,7 +472,7 @@ public class SceneApplication extends Application implements LookupProvider {
                 req.setModified(false);
             }
         }
-        if ((request != null) && (request.getDataObject()instanceof AssetDataObject)){
+        if ((request != null) && (request.getDataObject() instanceof AssetDataObject)) {
             AssetDataObject obj = (AssetDataObject) request.getDataObject();
             obj.closeAsset();
         }
@@ -501,7 +508,6 @@ public class SceneApplication extends Application implements LookupProvider {
 
     public void enableCamLight(final boolean enabled) {
         enqueue(new Callable() {
-
             public Object call() throws Exception {
                 if (enabled) {
                     rootNode.removeLight(camLight);
@@ -516,7 +522,6 @@ public class SceneApplication extends Application implements LookupProvider {
 
     public void enableStats(final boolean enabled) {
         enqueue(new Callable() {
-
             public Object call() throws Exception {
                 if (enabled) {
                     guiNode.attachChild(statsGuiNode);
@@ -530,7 +535,6 @@ public class SceneApplication extends Application implements LookupProvider {
 
     public void enableWireFrame(final boolean selected) {
         enqueue(new Callable() {
-
             public Object call() throws Exception {
                 if (selected) {
                     viewPort.addProcessor(wireProcessor);
@@ -544,7 +548,6 @@ public class SceneApplication extends Application implements LookupProvider {
 
     public void setPhysicsEnabled(final boolean enabled) {
         enqueue(new Callable() {
-
             public Object call() throws Exception {
                 if (enabled) {
                     if (physicsState == null) {

+ 10 - 0
jme3-core/src/com/jme3/gde/core/scene/SceneRequest.java

@@ -54,6 +54,7 @@ public class SceneRequest {
     private boolean displayed = false;
     private DataObject dataObject;
     private HelpCtx helpCtx;
+    private FakeApplication fakeApp;
 
     public SceneRequest(Object requester, JmeNode rootNode, ProjectAssetManager manager) {
         this.requester = requester;
@@ -175,4 +176,13 @@ public class SceneRequest {
     public void setHelpCtx(HelpCtx helpCtx) {
         this.helpCtx = helpCtx;
     }
+
+    public void setFakeApp(FakeApplication fakeApp) {
+        this.fakeApp = fakeApp;
+    }
+
+    public FakeApplication getFakeApp() {
+        return fakeApp;
+    }
+    
 }