Browse Source

adding support for transform updates. removed user message for all data type updates

rickard 3 years ago
parent
commit
2a8564ba9c

+ 99 - 61
jme3-core/src/com/jme3/gde/core/assets/ExternalChangeScanner.java

@@ -39,33 +39,31 @@ import com.jme3.gde.core.util.TaggedSpatialFinder;
 import com.jme3.gde.core.util.datatransfer.AnimationDataFromOriginal;
 import com.jme3.gde.core.util.datatransfer.AnimationDataFromOriginal;
 import com.jme3.gde.core.util.datatransfer.MaterialDataFromOriginal;
 import com.jme3.gde.core.util.datatransfer.MaterialDataFromOriginal;
 import com.jme3.gde.core.util.datatransfer.MeshDataFromOriginal;
 import com.jme3.gde.core.util.datatransfer.MeshDataFromOriginal;
+import com.jme3.gde.core.util.datatransfer.TransformDataFromOriginal;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.Spatial;
-import java.util.concurrent.Callable;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 import org.netbeans.api.progress.ProgressHandle;
 import org.netbeans.api.progress.ProgressHandle;
 import org.openide.DialogDisplayer;
 import org.openide.DialogDisplayer;
 import org.openide.NotifyDescriptor;
 import org.openide.NotifyDescriptor;
-import org.openide.filesystems.FileAttributeEvent;
-import org.openide.filesystems.FileChangeAdapter;
-import org.openide.filesystems.FileChangeListener;
-import org.openide.filesystems.FileEvent;
-import org.openide.filesystems.FileObject;
-import org.openide.filesystems.FileRenameEvent;
 import org.openide.loaders.DataObject;
 import org.openide.loaders.DataObject;
 import org.openide.loaders.DataObjectNotFoundException;
 import org.openide.loaders.DataObjectNotFoundException;
 import org.openide.util.Exceptions;
 import org.openide.util.Exceptions;
 
 
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
 /**
 /**
  * This class scans for external changes of a j3o models original file and tries
  * This class scans for external changes of a j3o models original file and tries
  * to update the data when it changed.
  * to update the data when it changed.
  *
  *
  * @author normenhansen
  * @author normenhansen
  */
  */
-public class ExternalChangeScanner implements AssetDataPropertyChangeListener, FileChangeListener {
+public class ExternalChangeScanner implements AssetDataPropertyChangeListener
+        , FileChangeListener {
 
 
-    private static final Logger logger = Logger.getLogger(ExternalChangeScanner.class.getName());
+    private static final Logger logger =
+            Logger.getLogger(ExternalChangeScanner.class.getName());
     private static final AtomicBoolean userNotified = new AtomicBoolean(false);
     private static final AtomicBoolean userNotified = new AtomicBoolean(false);
     protected final AssetDataObject assetDataObject;
     protected final AssetDataObject assetDataObject;
     protected final AssetData assetData;
     protected final AssetData assetData;
@@ -85,50 +83,61 @@ public class ExternalChangeScanner implements AssetDataPropertyChangeListener, F
             assetDataObject.getPrimaryFile().addFileChangeListener(new FileChangeAdapter() {
             assetDataObject.getPrimaryFile().addFileChangeListener(new FileChangeAdapter() {
                 @Override
                 @Override
                 public void fileDeleted(FileEvent fe) {
                 public void fileDeleted(FileEvent fe) {
-                    logger.log(Level.INFO, "File {0} deleted, remove!", new Object[]{fe.getFile()});
+                    logger.log(Level.INFO, "File {0} deleted, remove!",
+                            new Object[]{fe.getFile()});
                     assetData.removePropertyChangeListener(main);
                     assetData.removePropertyChangeListener(main);
                     fe.getFile().removeFileChangeListener(this);
                     fe.getFile().removeFileChangeListener(this);
                     if (originalObject != null) {
                     if (originalObject != null) {
-                        logger.log(Level.INFO, "Remove file change listener for {0}", originalObject);
+                        logger.log(Level.INFO, "Remove file change listener "
+                                + "for {0}", originalObject);
                         originalObject.removeFileChangeListener(main);
                         originalObject.removeFileChangeListener(main);
                         originalObject = null;
                         originalObject = null;
                     }
                     }
                 }
                 }
             });
             });
         } else {
         } else {
-            logger.log(Level.WARNING, "Trying to observer changes for asset {0} which has no AssetData in Lookup.", assetDataObject.getName());
+            logger.log(Level.WARNING, "Trying to observer changes for asset "
+                            + "{0} which has no AssetData in Lookup.",
+                    assetDataObject.getName());
         }
         }
     }
     }
 
 
     private void notifyUser() {
     private void notifyUser() {
         if (!userNotified.getAndSet(true)) {
         if (!userNotified.getAndSet(true)) {
             //TODO: execute on separate thread?
             //TODO: execute on separate thread?
-            java.awt.EventQueue.invokeLater(new Runnable() {
-                public void run() {
-                    NotifyDescriptor.Confirmation mesg = new NotifyDescriptor.Confirmation("Original file for " + assetDataObject.getName() + " changed\nTry and reapply mesh data to j3o file?",
-                            "Original file changed",
-                            NotifyDescriptor.YES_NO_OPTION, NotifyDescriptor.QUESTION_MESSAGE);
-                    DialogDisplayer.getDefault().notify(mesg);
-                    if (mesg.getValue() != NotifyDescriptor.Confirmation.YES_OPTION) {
-                        userNotified.set(false);
-                        return;
-                    }
-                    SceneApplication.getApplication().enqueue(new Callable<Void>() {
-                        public Void call() throws Exception {
-                            applyExternalData();
-                            return null;
-                        }
-                    });
+            java.awt.EventQueue.invokeLater(() -> {
+                final String NO_OPTION = "No";
+                final String ALL_OPTION = "All";
+                final String MESH_OPTION = "Only mesh data";
+                NotifyDescriptor.Confirmation mesg =
+                        new NotifyDescriptor.Confirmation("Original file for "
+                                + assetDataObject.getName() + " changed\nTry " +
+                                "and reapply data to j3o file?",
+                                "Original file changed",
+                                NotifyDescriptor.YES_NO_CANCEL_OPTION,
+                                NotifyDescriptor.QUESTION_MESSAGE);
+                mesg.setOptions(new Object[]{ALL_OPTION, MESH_OPTION,
+                        NO_OPTION});
+                DialogDisplayer.getDefault().notify(mesg);
+                if (mesg.getValue().equals(NO_OPTION)) {
                     userNotified.set(false);
                     userNotified.set(false);
+                    return;
                 }
                 }
+                SceneApplication.getApplication().enqueue((Callable<Void>) () -> {
+                    applyExternalData(mesg.getValue().equals(MESH_OPTION));
+                    return null;
+                });
+                userNotified.set(false);
             });
             });
         } else {
         } else {
-            logger.log(Level.INFO, "User already notified about change in {0}", assetDataObject.getName());
+            logger.log(Level.INFO, "User already notified about change in "
+                    + "{0}", assetDataObject.getName());
         }
         }
     }
     }
 
 
-    private void applyExternalData() {
-        ProgressHandle handle = ProgressHandle.createHandle("Updating file data");
+    private void applyExternalData(boolean onlyMeshData) {
+        ProgressHandle handle = ProgressHandle.createHandle("Updating file "
+                + "data");
         handle.start();
         handle.start();
         try {
         try {
             Spatial original = loadOriginalSpatial();
             Spatial original = loadOriginalSpatial();
@@ -136,20 +145,20 @@ public class ExternalChangeScanner implements AssetDataPropertyChangeListener, F
             TaggedSpatialFinder finder = new TaggedSpatialFinder();
             TaggedSpatialFinder finder = new TaggedSpatialFinder();
 
 
             new MeshDataFromOriginal(finder).update(spat, original);
             new MeshDataFromOriginal(finder).update(spat, original);
-            new MaterialDataFromOriginal(finder).update(spat, original);
-            if (SpatialUtil.hasAnimations(original)) {
-                NotifyDescriptor.Confirmation mesg = new NotifyDescriptor.Confirmation("Model appears to have animations, try to import as well?\nCurrently this will unlink attachment Nodes and clear\nadded effects tracks.",
-                        "Animations Available",
-                        NotifyDescriptor.YES_NO_OPTION, NotifyDescriptor.QUESTION_MESSAGE);
-                DialogDisplayer.getDefault().notify(mesg);
-                if (mesg.getValue() == NotifyDescriptor.Confirmation.YES_OPTION) {
-                    new AnimationDataFromOriginal(finder).update(spat, original);
+            new TransformDataFromOriginal(finder).update(spat, original);
+            if (!onlyMeshData) {
+                new MaterialDataFromOriginal(finder).update(spat, original);
+                if (SpatialUtil.hasAnimations(original)) {
+                    new AnimationDataFromOriginal(finder).update(spat,
+                            original);
                 }
                 }
             }
             }
+
             closeOriginalSpatial();
             closeOriginalSpatial();
             assetDataObject.saveAsset();
             assetDataObject.saveAsset();
         } catch (Exception e) {
         } catch (Exception e) {
-            logger.log(Level.SEVERE, "Exception when trying to update external data.", e);
+            logger.log(Level.SEVERE, "Exception when trying to update "
+                    + "external data.", e);
         } finally {
         } finally {
             handle.finish();
             handle.finish();
         }
         }
@@ -158,16 +167,23 @@ public class ExternalChangeScanner implements AssetDataPropertyChangeListener, F
     private Spatial loadOriginalSpatial() {
     private Spatial loadOriginalSpatial() {
         try {
         try {
             DataObject dobj = DataObject.find(originalObject);
             DataObject dobj = DataObject.find(originalObject);
-            AssetData originalAssetData = dobj.getLookup().lookup(AssetData.class);
+            AssetData originalAssetData =
+                    dobj.getLookup().lookup(AssetData.class);
             if (originalAssetData != null) {
             if (originalAssetData != null) {
                 Savable sav = originalAssetData.loadAsset();
                 Savable sav = originalAssetData.loadAsset();
                 if (sav instanceof Spatial) {
                 if (sav instanceof Spatial) {
                     return (Spatial) sav;
                     return (Spatial) sav;
                 } else {
                 } else {
-                    logger.log(Level.SEVERE, "Trying to load original for {0} but it is not a Spatial: {1}", new Object[]{assetDataObject.getName(), originalObject});
+                    logger.log(Level.SEVERE, "Trying to load original for {0}"
+                                    + " but it is not a Spatial: {1}",
+                            new Object[]{assetDataObject.getName(),
+                                    originalObject});
                 }
                 }
             } else {
             } else {
-                logger.log(Level.WARNING, "Could not get AssetData for {0}, original file {1}", new Object[]{assetDataObject.getName(), originalObject});
+                logger.log(Level.WARNING, "Could not get AssetData for {0}, "
+                                + "original file {1}",
+                        new Object[]{assetDataObject.getName(),
+                                originalObject});
             }
             }
         } catch (DataObjectNotFoundException ex) {
         } catch (DataObjectNotFoundException ex) {
             Exceptions.printStackTrace(ex);
             Exceptions.printStackTrace(ex);
@@ -178,11 +194,15 @@ public class ExternalChangeScanner implements AssetDataPropertyChangeListener, F
     private Spatial closeOriginalSpatial() {
     private Spatial closeOriginalSpatial() {
         try {
         try {
             DataObject dobj = DataObject.find(originalObject);
             DataObject dobj = DataObject.find(originalObject);
-            AssetData originalAssetData = dobj.getLookup().lookup(AssetData.class);
+            AssetData originalAssetData =
+                    dobj.getLookup().lookup(AssetData.class);
             if (originalAssetData != null) {
             if (originalAssetData != null) {
                 originalAssetData.closeAsset();
                 originalAssetData.closeAsset();
             } else {
             } else {
-                logger.log(Level.WARNING, "Could not get AssetData for {0}, original file {1}", new Object[]{assetDataObject.getName(), originalObject});
+                logger.log(Level.WARNING, "Could not get AssetData for {0}, "
+                                + "original file {1}",
+                        new Object[]{assetDataObject.getName(),
+                                originalObject});
             }
             }
         } catch (DataObjectNotFoundException ex) {
         } catch (DataObjectNotFoundException ex) {
             Exceptions.printStackTrace(ex);
             Exceptions.printStackTrace(ex);
@@ -191,9 +211,11 @@ public class ExternalChangeScanner implements AssetDataPropertyChangeListener, F
     }
     }
 
 
     private void setObservedFilePath(String assetName) {
     private void setObservedFilePath(String assetName) {
-        ProjectAssetManager mgr = assetDataObject.getLookup().lookup(ProjectAssetManager.class);
+        ProjectAssetManager mgr =
+                assetDataObject.getLookup().lookup(ProjectAssetManager.class);
         if (mgr == null) {
         if (mgr == null) {
-            logger.log(Level.WARNING, "File is not part of a jME project but tries to find original model...");
+            logger.log(Level.WARNING, "File is not part of a jME project but "
+                    + "tries to find original model...");
             return;
             return;
         }
         }
         FileObject fileObject = mgr.getAssetFileObject(assetName);
         FileObject fileObject = mgr.getAssetFileObject(assetName);
@@ -202,24 +224,34 @@ public class ExternalChangeScanner implements AssetDataPropertyChangeListener, F
             if (!fileObject.equals(assetDataObject.getPrimaryFile())) {
             if (!fileObject.equals(assetDataObject.getPrimaryFile())) {
                 if (originalObject != null) {
                 if (originalObject != null) {
                     originalObject.removeFileChangeListener(this);
                     originalObject.removeFileChangeListener(this);
-                    logger.log(Level.INFO, "{0} stops listening for external changes on {1}", new Object[]{assetDataObject.getName(), originalObject});
+                    logger.log(Level.INFO, "{0} stops listening for external "
+                                    + "changes on {1}",
+                            new Object[]{assetDataObject.getName(),
+                                    originalObject});
                 }
                 }
                 fileObject.addFileChangeListener(this);
                 fileObject.addFileChangeListener(this);
-                logger.log(Level.INFO, "{0} listening for external changes on {1}", new Object[]{assetDataObject.getName(), fileObject});
+                logger.log(Level.INFO, "{0} listening for external changes on"
+                        + " {1}", new Object[]{assetDataObject.getName(),
+                        fileObject});
                 originalObject = fileObject;
                 originalObject = fileObject;
             } else {
             } else {
-                logger.log(Level.FINE, "Ignoring old reference to self for {0}", assetDataObject.getName());
+                logger.log(Level.FINE, "Ignoring old reference to self for "
+                        + "{0}", assetDataObject.getName());
             }
             }
         } else {
         } else {
-            logger.log(Level.INFO, "Could not get FileObject for {0} when trying to update original data for {1}. Possibly deleted.", new Object[]{assetName, assetDataObject.getName()});
+            logger.log(Level.INFO, "Could not get FileObject for {0} when "
+                    + "trying to update original data for {1}. Possibly deleted"
+                    + ".", new Object[]{assetName, assetDataObject.getName()});
             //TODO: add folder listener for when recreated
             //TODO: add folder listener for when recreated
         }
         }
     }
     }
 
 
     @Override
     @Override
-    public void assetDataPropertyChanged(String property, String before, String after) {
+    public void assetDataPropertyChanged(String property, String before,
+                                         String after) {
         if ("ORIGINAL_PATH".equals(property)) {
         if ("ORIGINAL_PATH".equals(property)) {
-            logger.log(Level.INFO, "Notified about change in AssetData properties for {0}", assetDataObject.getName());
+            logger.log(Level.INFO, "Notified about change in AssetData "
+                    + "properties for {0}", assetDataObject.getName());
             setObservedFilePath(after);
             setObservedFilePath(after);
         }
         }
     }
     }
@@ -231,14 +263,18 @@ public class ExternalChangeScanner implements AssetDataPropertyChangeListener, F
     }
     }
 
 
     public void fileChanged(FileEvent fe) {
     public void fileChanged(FileEvent fe) {
-        logger.log(Level.INFO, "External file {0} for {1} changed!", new Object[]{fe.getFile(), assetDataObject.getName()});
+        logger.log(Level.INFO, "External file {0} for {1} changed!",
+                new Object[]{fe.getFile(), assetDataObject.getName()});
         notifyUser();
         notifyUser();
     }
     }
 
 
     public void fileDeleted(FileEvent fe) {
     public void fileDeleted(FileEvent fe) {
-        logger.log(Level.INFO, "External file {0} for {1} deleted!", new Object[]{fe.getFile(), assetDataObject.getName()});
+        logger.log(Level.INFO, "External file {0} for {1} deleted!",
+                new Object[]{fe.getFile(), assetDataObject.getName()});
         if (originalObject != null) {
         if (originalObject != null) {
-            logger.log(ApplicationLogHandler.LogLevel.INFO, "Remove file change listener for deleted object on {0}", assetDataObject.getName());
+            logger.log(ApplicationLogHandler.LogLevel.INFO, "Remove file "
+                            + "change listener for deleted object on {0}",
+                    assetDataObject.getName());
             originalObject.removeFileChangeListener(this);
             originalObject.removeFileChangeListener(this);
             originalObject = null;
             originalObject = null;
         }
         }
@@ -246,9 +282,11 @@ public class ExternalChangeScanner implements AssetDataPropertyChangeListener, F
     }
     }
 
 
     public void fileRenamed(FileRenameEvent fe) {
     public void fileRenamed(FileRenameEvent fe) {
-        logger.log(Level.INFO, "External file {0} for {1} renamed!", new Object[]{fe.getFile(), assetDataObject.getName()});
+        logger.log(Level.INFO, "External file {0} for {1} renamed!",
+                new Object[]{fe.getFile(), assetDataObject.getName()});
         if (originalObject != null) {
         if (originalObject != null) {
-            logger.log(Level.INFO, "Remove file change listener for renamed object on {0}", assetDataObject.getName());
+            logger.log(Level.INFO, "Remove file change listener for renamed "
+                    + "object on {0}", assetDataObject.getName());
             originalObject.removeFileChangeListener(this);
             originalObject.removeFileChangeListener(this);
             originalObject = null;
             originalObject = null;
         }
         }

+ 31 - 38
jme3-core/src/com/jme3/gde/core/util/SpatialUtil.java

@@ -32,7 +32,6 @@
 package com.jme3.gde.core.util;
 package com.jme3.gde.core.util;
 
 
 import com.jme3.anim.AnimComposer;
 import com.jme3.anim.AnimComposer;
-import com.jme3.scene.SceneGraphVisitor;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.Spatial;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -47,20 +46,20 @@ import java.util.logging.Logger;
  *
  *
  * @author normenhansen
  * @author normenhansen
  */
  */
-@SuppressWarnings({"unchecked", "rawtypes"})
 public class SpatialUtil {
 public class SpatialUtil {
 
 
+    public static final String ORIGINAL_NAME = "ORIGINAL_NAME";
+    public static final String ORIGINAL_PATH = "ORIGINAL_PATH";
     private static final Logger logger =
     private static final Logger logger =
             Logger.getLogger(SpatialUtil.class.getName());
             Logger.getLogger(SpatialUtil.class.getName());
 
 
-
     /**
     /**
      * Gets a "pathname" for the given Spatial, combines the Spatials and
      * Gets a "pathname" for the given Spatial, combines the Spatials and
      * parents names to make a long name. This "path" is stored in geometry
      * parents names to make a long name. This "path" is stored in geometry
      * after the first import for example.
      * after the first import for example.
      *
      *
-     * @param spat
-     * @return
+     * @param spat Spatial
+     * @return id of spatial
      */
      */
     public static String getSpatialPath(Spatial spat) {
     public static String getSpatialPath(Spatial spat) {
         StringBuilder geometryIdentifier = new StringBuilder();
         StringBuilder geometryIdentifier = new StringBuilder();
@@ -74,66 +73,60 @@ public class SpatialUtil {
             geometryIdentifier.insert(0, '/');
             geometryIdentifier.insert(0, '/');
             spat = spat.getParent();
             spat = spat.getParent();
         }
         }
-        String id = geometryIdentifier.toString();
-        return id;
+        return geometryIdentifier.toString();
     }
     }
 
 
     /**
     /**
      * Stores ORIGINAL_NAME and ORIGINAL_PATH UserData to given Spatial and all
      * Stores ORIGINAL_NAME and ORIGINAL_PATH UserData to given Spatial and all
      * sub-Spatials.
      * sub-Spatials.
      *
      *
-     * @param spat
+     * @param spat spatial
      */
      */
     public static void storeOriginalPathUserData(Spatial spat) {
     public static void storeOriginalPathUserData(Spatial spat) {
         //TODO: only stores for geometry atm
         //TODO: only stores for geometry atm
-        final ArrayList<String> geomMap = new ArrayList<String>();
+        final ArrayList<String> geomMap = new ArrayList<>();
         if (spat != null) {
         if (spat != null) {
-            spat.depthFirstTraversal(new SceneGraphVisitor() {
-                @Override
-                public void visit(Spatial geom) {
-                    Spatial curSpat = geom;
-                    String geomName = curSpat.getName();
-                    if (geomName == null) {
-                        logger.log(Level.WARNING, "Null Spatial name!");
-                        geomName = "null";
-                    }
-                    geom.setUserData("ORIGINAL_NAME", geomName);
-                    logger.log(Level.FINE, "Set ORIGINAL_NAME for {0}",
-                            geomName);
-                    String id = SpatialUtil.getSpatialPath(curSpat);
-                    if (geomMap.contains(id)) {
-                        logger.log(Level.WARNING, "Cannot create unique name " +
-                                "for Spatial {0}: {1}", new Object[]{geom, id});
-                    }
-                    geomMap.add(id);
-                    geom.setUserData("ORIGINAL_PATH", id);
-                    logger.log(Level.FINE, "Set ORIGINAL_PATH for {0}", id);
+            spat.depthFirstTraversal(geom -> {
+                Spatial curSpat = geom;
+                String geomName = geom.getName();
+                if (geomName == null) {
+                    logger.log(Level.WARNING, "Null Spatial name!");
+                    geomName = "null";
+                }
+                geom.setUserData("ORIGINAL_NAME", geomName);
+                logger.log(Level.FINE, "Set ORIGINAL_NAME for {0}",
+                        geomName);
+                String id = SpatialUtil.getSpatialPath(curSpat);
+                if (geomMap.contains(id)) {
+                    logger.log(Level.WARNING, "Cannot create unique name "
+                            + "for Spatial {0}: {1}", new Object[]{geom,
+                            id});
                 }
                 }
+                geomMap.add(id);
+                geom.setUserData("ORIGINAL_PATH", id);
+                logger.log(Level.FINE, "Set ORIGINAL_PATH for {0}", id);
             });
             });
         } else {
         } else {
-            logger.log(Level.SEVERE, "No Spatial available when trying to add" +
-                    " Spatial paths.");
+            logger.log(Level.SEVERE, "No Spatial available when trying to add"
+                    + " Spatial paths.");
         }
         }
     }
     }
 
 
     public static void clearRemovedOriginals(final Spatial root,
     public static void clearRemovedOriginals(final Spatial root,
                                              final Spatial original) {
                                              final Spatial original) {
         //TODO: Clear old stuff at all?
         //TODO: Clear old stuff at all?
-        return;
     }
     }
 
 
     /**
     /**
      * Finds out if a spatial has animations.
      * Finds out if a spatial has animations.
      *
      *
-     * @param root
+     * @param root root spatial
      */
      */
     public static boolean hasAnimations(final Spatial root) {
     public static boolean hasAnimations(final Spatial root) {
         final AtomicBoolean animFound = new AtomicBoolean(false);
         final AtomicBoolean animFound = new AtomicBoolean(false);
-        root.depthFirstTraversal(new SceneGraphVisitor() {
-            public void visit(Spatial spatial) {
-                if (spatial.getControl(AnimComposer.class) != null) {
-                    animFound.set(true);
-                }
+        root.depthFirstTraversal(spatial -> {
+            if (spatial.getControl(AnimComposer.class) != null) {
+                animFound.set(true);
             }
             }
         });
         });
         return animFound.get();
         return animFound.get();

+ 17 - 17
jme3-core/src/com/jme3/gde/core/util/TaggedSpatialFinder.java

@@ -1,6 +1,5 @@
 package com.jme3.gde.core.util;
 package com.jme3.gde.core.util;
 
 
-import com.jme3.scene.SceneGraphVisitor;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.Spatial;
 
 
 import java.util.logging.Level;
 import java.util.logging.Level;
@@ -15,6 +14,10 @@ public class TaggedSpatialFinder {
     private static final Logger logger =
     private static final Logger logger =
             Logger.getLogger(TaggedSpatialFinder.class.getName());
             Logger.getLogger(TaggedSpatialFinder.class.getName());
 
 
+    public TaggedSpatialFinder() {
+
+    }
+
     public Spatial find(final Spatial root, final Spatial needle) {
     public Spatial find(final Spatial root, final Spatial needle) {
         if (needle == null) {
         if (needle == null) {
             logger.log(Level.WARNING, "Trying to find null needle for {0}",
             logger.log(Level.WARNING, "Trying to find null needle for {0}",
@@ -23,30 +26,27 @@ public class TaggedSpatialFinder {
         }
         }
         final String name = needle.getName();
         final String name = needle.getName();
         final String path = SpatialUtil.getSpatialPath(needle);
         final String path = SpatialUtil.getSpatialPath(needle);
-        if (name == null || path == null) {
+        if (name == null) {
             logger.log(Level.WARNING, "Trying to find tagged Spatial with " +
             logger.log(Level.WARNING, "Trying to find tagged Spatial with " +
                     "null name spatial for {0}.", root);
                     "null name spatial for {0}.", root);
             return null;
             return null;
         }
         }
-        final Class clazz = needle.getClass();
-        String rootName = root.getUserData("ORIGINAL_NAME");
-        String rootPath = root.getUserData("ORIGINAL_PATH");
+        final Class<? extends Spatial> clazz = needle.getClass();
+        String rootName = root.getUserData(SpatialUtil.ORIGINAL_NAME);
+        String rootPath = root.getUserData(SpatialUtil.ORIGINAL_PATH);
         if (name.equals(rootName) && path.equals(rootPath)) {
         if (name.equals(rootName) && path.equals(rootPath)) {
             return root;
             return root;
         }
         }
         final SpatialHolder holder = new SpatialHolder();
         final SpatialHolder holder = new SpatialHolder();
-        root.depthFirstTraversal(new SceneGraphVisitor() {
-            @Override
-            public void visit(Spatial spatial) {
-                String spName = spatial.getUserData("ORIGINAL_NAME");
-                String spPath = spatial.getUserData("ORIGINAL_PATH");
-                if (name.equals(spName) && path.equals(spPath) && clazz.isInstance(spatial)) {
-                    if (holder.spatial == null) {
-                        holder.spatial = spatial;
-                    } else {
-                        logger.log(Level.WARNING, "Found Spatial {0} twice in" +
-                                " {1}", new Object[]{path, root});
-                    }
+        root.depthFirstTraversal(spatial -> {
+            String spName = spatial.getUserData(SpatialUtil.ORIGINAL_NAME);
+            String spPath = spatial.getUserData(SpatialUtil.ORIGINAL_PATH);
+            if (name.equals(spName) && path.equals(spPath) && clazz.isInstance(spatial)) {
+                if (holder.spatial == null) {
+                    holder.spatial = spatial;
+                } else {
+                    logger.log(Level.WARNING, "Found Spatial {0} twice in" +
+                            " {1}", new Object[]{path, root});
                 }
                 }
             }
             }
         });
         });

+ 40 - 47
jme3-core/src/com/jme3/gde/core/util/datatransfer/AnimationDataFromOriginal.java

@@ -4,7 +4,6 @@ import com.jme3.anim.AnimClip;
 import com.jme3.anim.AnimComposer;
 import com.jme3.anim.AnimComposer;
 import com.jme3.gde.core.scene.ApplicationLogHandler;
 import com.jme3.gde.core.scene.ApplicationLogHandler;
 import com.jme3.gde.core.util.TaggedSpatialFinder;
 import com.jme3.gde.core.util.TaggedSpatialFinder;
-import com.jme3.scene.SceneGraphVisitor;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.Spatial;
 import com.jme3.util.clone.Cloner;
 import com.jme3.util.clone.Cloner;
 
 
@@ -12,7 +11,10 @@ import java.util.Collection;
 import java.util.logging.Level;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.logging.Logger;
 
 
-public class AnimationDataFromOriginal implements SpatialDataTransferInterface {
+/**
+ * Copies AnimComposer and AnimClips from an updated spatial to the original
+ */
+public final class AnimationDataFromOriginal implements SpatialDataTransferInterface {
 
 
     private static final Logger logger =
     private static final Logger logger =
             Logger.getLogger(AnimationDataFromOriginal.class.getName());
             Logger.getLogger(AnimationDataFromOriginal.class.getName());
@@ -27,56 +29,50 @@ public class AnimationDataFromOriginal implements SpatialDataTransferInterface {
     public void update(final Spatial root, final Spatial original) {
     public void update(final Spatial root, final Spatial original) {
         //loop through original to also find new AnimControls, we expect all 
         //loop through original to also find new AnimControls, we expect all 
         // nodes etc. to exist
         // nodes etc. to exist
-        //TODO: can (blender) AnimControls end up in other nodes that are not
-        // a parent of the geometry they modify?
         removeAnimData(root);
         removeAnimData(root);
-        original.depthFirstTraversal(new SceneGraphVisitor() {
-            @Override
-            public void visit(Spatial spat) {
-                AnimComposer animComposer = spat.getControl(AnimComposer.class);
-                if (animComposer != null) {
-                    Spatial mySpatial = finder.find(root, spat);
-                    if (mySpatial != null) {
-                        //TODO: move attachments: have to scan through all 
-                        // nodes and find the ones
-                        //where UserData "AttachedBone" == Bone and move it 
-                        // to new Bone
-                        AnimComposer myAnimControl =
-                                mySpatial.getControl(AnimComposer.class);
+        original.depthFirstTraversal(spatial -> {
+            final AnimComposer animComposer =
+                    spatial.getControl(AnimComposer.class);
+            if (animComposer != null) {
+                Spatial mySpatial = finder.find(root, spatial);
+                if (mySpatial != null) {
+                    //TODO: move attachments: have to scan through all
+                    // nodes and find the ones
+                    //where UserData "AttachedBone" == Bone and move it
+                    // to new Bone
+                    final AnimComposer myAnimControl =
+                            mySpatial.getControl(AnimComposer.class);
 
 
-                        if (myAnimControl != null) {
-                            mySpatial.removeControl(myAnimControl);
-                        }
-
-                        AnimComposer newControl = new AnimComposer();
-                        newControl.cloneFields(new Cloner(),
-                                animComposer.jmeClone());
-                        copyAnimClips(newControl, animComposer);
-                        if (mySpatial.getControl(AnimComposer.class) == null) {
-                            logger.log(Level.INFO, "Adding control for {0}",
-                                    mySpatial.getName());
-                            mySpatial.addControl(newControl);
-                        } else {
-                            logger.log(Level.INFO, "Control for {0} was added" +
-                                    " automatically", mySpatial.getName());
-                        }
+                    if (myAnimControl != null) {
+                        mySpatial.removeControl(myAnimControl);
+                    }
 
 
-                        logger.log(ApplicationLogHandler.LogLevel.USERINFO,
-                                "Updated animation for {0}",
+                    myAnimControl.cloneFields(new Cloner(),
+                            animComposer.jmeClone());
+                    copyAnimClips(myAnimControl, animComposer);
+                    if (mySpatial.getControl(AnimComposer.class) == null) {
+                        logger.log(Level.FINE, "Adding control for {0}",
                                 mySpatial.getName());
                                 mySpatial.getName());
+                        mySpatial.addControl(myAnimControl);
                     } else {
                     } else {
-                        logger.log(Level.WARNING, "Could not find sibling for" +
-                                " {0} in root {1} when trying to apply " +
-                                "AnimControl data", new Object[]{spat, root});
+                        logger.log(Level.FINE, "Control for {0} was added"
+                                + " automatically", mySpatial.getName());
                     }
                     }
+
+                    logger.log(ApplicationLogHandler.LogLevel.FINE,
+                            "Updated animation for {0}",
+                            mySpatial.getName());
+                } else {
+                    logger.log(Level.WARNING, "Could not find sibling for"
+                            + " {0} in root {1} when trying to apply "
+                            + "AnimControl data", new Object[]{spatial, root});
                 }
                 }
             }
             }
         });
         });
-        //TODO: remove old AnimControls?
     }
     }
 
 
     private void copyAnimClips(AnimComposer control, AnimComposer original) {
     private void copyAnimClips(AnimComposer control, AnimComposer original) {
-        Collection<AnimClip> clips = original.getAnimClips();
+        final Collection<AnimClip> clips = original.getAnimClips();
         for (AnimClip c : clips) {
         for (AnimClip c : clips) {
             control.addAnimClip(c);
             control.addAnimClip(c);
         }
         }
@@ -84,14 +80,11 @@ public class AnimationDataFromOriginal implements SpatialDataTransferInterface {
 
 
 
 
     private void removeAnimData(Spatial root) {
     private void removeAnimData(Spatial root) {
-        root.depthFirstTraversal(new SceneGraphVisitor() {
-            @Override
-            public void visit(Spatial spat) {
-                AnimComposer animControl = spat.getControl(AnimComposer.class);
-                if (animControl != null) {
-                    spat.removeControl(animControl);
+        root.depthFirstTraversal(spatial -> {
+            AnimComposer animControl = spatial.getControl(AnimComposer.class);
+            if (animControl != null) {
+                spatial.removeControl(animControl);
 
 
-                }
             }
             }
         });
         });
     }
     }

+ 5 - 2
jme3-core/src/com/jme3/gde/core/util/datatransfer/MaterialDataFromOriginal.java

@@ -8,7 +8,10 @@ import com.jme3.scene.Spatial;
 
 
 import java.util.logging.Logger;
 import java.util.logging.Logger;
 
 
-public class MaterialDataFromOriginal implements SpatialDataTransferInterface {
+/**
+ * Copies material data from an updated model to the original
+ */
+public final class MaterialDataFromOriginal implements SpatialDataTransferInterface {
 
 
     private static final Logger logger =
     private static final Logger logger =
             Logger.getLogger(MaterialDataFromOriginal.class.getName());
             Logger.getLogger(MaterialDataFromOriginal.class.getName());
@@ -30,7 +33,7 @@ public class MaterialDataFromOriginal implements SpatialDataTransferInterface {
                 Geometry spat = (Geometry) finder.find(root, geom);
                 Geometry spat = (Geometry) finder.find(root, geom);
                 if (spat != null && spat.getMaterial() != null && geom.getMaterial() != null) {
                 if (spat != null && spat.getMaterial() != null && geom.getMaterial() != null) {
                     spat.setMaterial(geom.getMaterial());
                     spat.setMaterial(geom.getMaterial());
-                    logger.log(ApplicationLogHandler.LogLevel.USERINFO,
+                    logger.log(ApplicationLogHandler.LogLevel.FINE,
                             "Updated material for Geometry {0}",
                             "Updated material for Geometry {0}",
                             geom.getName());
                             geom.getName());
                 }
                 }

+ 54 - 0
jme3-core/src/com/jme3/gde/core/util/datatransfer/TransformDataFromOriginal.java

@@ -0,0 +1,54 @@
+package com.jme3.gde.core.util.datatransfer;
+
+import com.jme3.gde.core.scene.ApplicationLogHandler;
+import com.jme3.gde.core.util.TaggedSpatialFinder;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+
+import java.util.logging.Logger;
+
+/**
+ * Copies Transform data (translation, rotation, scale) from an updated
+ * spatial to the original
+ */
+public class TransformDataFromOriginal implements SpatialDataTransferInterface {
+
+    private static final Logger logger =
+            Logger.getLogger(AnimationDataFromOriginal.class.getName());
+
+    private final TaggedSpatialFinder finder;
+
+    public TransformDataFromOriginal(TaggedSpatialFinder finder) {
+        this.finder = finder;
+    }
+
+    @Override
+    public void update(Spatial root, Spatial original) {
+        original.depthFirstTraversal(new com.jme3.scene.SceneGraphVisitorAdapter() {
+
+            @Override
+            public void visit(com.jme3.scene.Geometry geom) {
+                Geometry spat =
+                        (Geometry) finder.find(root, geom);
+                if (spat != null) {
+                    spat.setLocalTransform(geom.getLocalTransform());
+                    logger.log(ApplicationLogHandler.LogLevel.FINE,
+                            "Updated transform for Geometry {0}",
+                            geom.getName());
+                }
+            }
+
+            @Override
+            public void visit(com.jme3.scene.Node node) {
+                Node spat =
+                        (Node) finder.find(root, node);
+                if (spat != null) {
+                    spat.setLocalTransform(node.getLocalTransform());
+                    logger.log(ApplicationLogHandler.LogLevel.FINE,
+                            "Updated transform for Node {0}", node.getName());
+                }
+            }
+        });
+    }
+}