diff --git a/sdk/jme3-core/src/com/jme3/gde/core/assets/AssetData.java b/sdk/jme3-core/src/com/jme3/gde/core/assets/AssetData.java index 24661b158..c226fcc64 100644 --- a/sdk/jme3-core/src/com/jme3/gde/core/assets/AssetData.java +++ b/sdk/jme3-core/src/com/jme3/gde/core/assets/AssetData.java @@ -32,13 +32,16 @@ package com.jme3.gde.core.assets; import com.jme3.asset.AssetKey; +import com.jme3.export.Savable; import java.io.BufferedOutputStream; import java.io.BufferedInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; import java.util.Date; +import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.logging.Level; @@ -62,6 +65,7 @@ import org.openide.util.Mutex.Action; public class AssetData { private static final Logger logger = Logger.getLogger(AssetData.class.getName()); + private final List listeners = new ArrayList(); private final Mutex propsMutex = new Mutex(); private final Properties props = new Properties(); private AssetDataObject file; @@ -81,14 +85,28 @@ public class AssetData { this.extension = extension; } - public void setExtension(String extension) { - this.extension = extension; - } + /** + * Sets the extension of the assetData properties file, normally not + * necessary to use this, it will be .[originalsuffix]data, for example + * .j3odata. + * + * @param extension + */ +// public void setExtension(String extension) { +// this.extension = extension; +// } public AssetKey getAssetKey() { return file.getAssetKey(); } + /** + * Applies the supplied keys data to the assets assetKey so it will be + * loaded with these settings next time loadAsset is actually loading the + * asset from the ProjectAssetManager. + * + * @param key + */ public void setAssetKey(AssetKey key) { file.setAssetKeyData(key); } @@ -101,10 +119,23 @@ public class AssetData { file.setSaveCookie(cookie); } - public Object loadAsset() { + /** + * Loads the asset from the DataObject via the ProjectAssetManager in the + * lookup. Returns the currently loaded asset when it has been loaded + * already, close the asset using closeAsset(). + * + * @return + */ + public Savable loadAsset() { return file.loadAsset(); } + /** + * Saves this asset, when a saveExtension is set, saves it as a brother file + * with that extension. + * + * @throws IOException + */ public void saveAsset() throws IOException { file.saveAsset(); } @@ -152,6 +183,7 @@ public class AssetData { } }); writeProperties(); + notifyListeners(key, ret, value); return ret; } @@ -233,4 +265,25 @@ public class AssetData { } }); } + + protected void notifyListeners(String property, String before, String after) { + synchronized (listeners) { + for (Iterator it = listeners.iterator(); it.hasNext();) { + AssetDataPropertyChangeListener assetDataPropertyChangeListener = it.next(); + assetDataPropertyChangeListener.assetDataPropertyChanged(property, before, after); + } + } + } + + public void addPropertyChangeListener(AssetDataPropertyChangeListener listener) { + synchronized (listeners) { + listeners.add(listener); + } + } + + public void removePropertyChangeListener(AssetDataPropertyChangeListener listener) { + synchronized (listeners) { + listeners.remove(listener); + } + } } diff --git a/sdk/jme3-core/src/com/jme3/gde/core/assets/AssetDataObject.java b/sdk/jme3-core/src/com/jme3/gde/core/assets/AssetDataObject.java index 5393a363a..0325f0b43 100644 --- a/sdk/jme3-core/src/com/jme3/gde/core/assets/AssetDataObject.java +++ b/sdk/jme3-core/src/com/jme3/gde/core/assets/AssetDataObject.java @@ -33,13 +33,16 @@ package com.jme3.gde.core.assets; import com.jme3.asset.AssetEventListener; import com.jme3.asset.AssetKey; +import com.jme3.asset.BlenderKey; import com.jme3.export.Savable; import com.jme3.export.binary.BinaryExporter; import com.jme3.gde.core.scene.ApplicationLogHandler.LogLevel; import com.jme3.gde.core.scene.SceneApplication; +import com.jme3.scene.Spatial; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Callable; @@ -75,8 +78,15 @@ import org.openide.util.lookup.ProxyLookup; public class AssetDataObject extends MultiDataObject { protected static final Logger logger = Logger.getLogger(AssetDataObject.class.getName()); - protected final Lookup lookup; protected final InstanceContent lookupContents = new InstanceContent(); + protected final AbstractLookup contentLookup; + protected final Lookup lookup; + protected final AssetData assetData; + protected final ProjectAssetManager assetManager; + protected final AssetListListener listListener; + protected final List assetList = new LinkedList(); + protected final List assetKeyList = new LinkedList(); + protected final List failedList = new LinkedList(); protected SaveCookie saveCookie = new SaveCookie() { public void save() throws IOException { //TODO: On OpenGL thread? -- safest way.. with get()? @@ -92,23 +102,20 @@ public class AssetDataObject extends MultiDataObject { protected AssetKey assetKey; protected Savable savable; protected String saveExtension; - protected AbstractLookup contentLookup; - protected AssetListListener listListener; - protected List assetList = new LinkedList(); - protected List assetKeyList = new LinkedList(); - protected List failedList = new LinkedList(); public AssetDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException { super(pf, loader); - contentLookup = new AbstractLookup(getLookupContents()); - lookupContents.add(new AssetData(this)); + contentLookup = new AbstractLookup(lookupContents); + assetData = new AssetData(this); + lookupContents.add(assetData); lookup = new ProxyLookup(getCookieSet().getLookup(), contentLookup); listListener = new AssetListListener(this, assetList, assetKeyList, failedList); + assetManager = findAssetManager(); + //assign savecookie (same as method) setSaveCookie(saveCookie); - findAssetManager(); } - protected void findAssetManager() { + private ProjectAssetManager findAssetManager() { FileObject file = getPrimaryFile(); ProjectManager pm = ProjectManager.getDefault(); while (file != null) { @@ -119,7 +126,7 @@ public class AssetDataObject extends MultiDataObject { ProjectAssetManager mgr = project.getLookup().lookup(ProjectAssetManager.class); if (mgr != null) { getLookupContents().add(mgr); - return; + return mgr; } } } catch (IOException ex) { @@ -128,7 +135,7 @@ public class AssetDataObject extends MultiDataObject { } file = file.getParent(); } -// getLookupContents().add(new ProjectAssetManager(file.getParent())); + return null; } @Override @@ -163,9 +170,16 @@ public class AssetDataObject extends MultiDataObject { setModified(false); } + /** + * Loads the asset from the DataObject via the ProjectAssetManager in the + * lookup. Returns the currently loaded asset when it has been loaded + * already, close the asset using closeAsset(). + * + * @return + */ public synchronized Savable loadAsset() { - if (isModified() && savable != null) { - return savable; + if (savable != null) { + return (Spatial) savable; } ProjectAssetManager mgr = getLookup().lookup(ProjectAssetManager.class); if (mgr == null) { @@ -190,6 +204,12 @@ public class AssetDataObject extends MultiDataObject { return savable; } + /** + * Saves this asset, when a saveExtension is set, saves it as a brother file + * with that extension. + * + * @throws IOException + */ public synchronized void saveAsset() throws IOException { if (savable == null) { logger.log(Level.WARNING, "Trying to write asset failed, asset data null!\nImport failed?"); @@ -222,14 +242,47 @@ public class AssetDataObject extends MultiDataObject { } } progressHandle.finish(); - logger.log(LogLevel.USERINFO, "File {0} saved successfully", getPrimaryFile().getNameExt()); setModified(false); + logger.log(LogLevel.USERINFO, "File {0} saved successfully", getPrimaryFile().getNameExt()); } + /** + * Closes this asset so that loadAsset will cause it to be loaded + */ public synchronized void closeAsset() { + ProjectAssetManager mgr = getLookup().lookup(ProjectAssetManager.class); + if (mgr != null) { + logger.log(Level.INFO, "Closing asset {0}, deleting from cache.", getName()); + mgr.deleteFromCache(getAssetKey()); + //delete referenced assets too + for (Iterator it = assetKeyList.iterator(); it.hasNext();) { + AssetKey assetKey1 = it.next(); + mgr.deleteFromCache(assetKey1); + } + } else { + logger.log(Level.WARNING, "Closing asset {0} with no ProjectAssetManager assigned..?", getName()); + } savable = null; } + /** + * Returns the AssetKey of this asset type. When extending AssetDataObject + * or a subtype the class should override this so the key type and + * properties can be recognized properly: + *
+     * public synchronized MyKeyType getAssetKey() {
+     *     //return key if already set
+     *     if(super.getAssetKey() instanceof MyKeyType){
+     *         return (MyKeyType)assetKey;
+     *     }
+     *     //set own key type and return
+     *     assetKey = new MyKeyType(super.getAssetKey().getName());
+     *     return (MyKeyType)assetKey;
+     * }
+     * 
+ * + * @return + */ public synchronized AssetKey getAssetKey() { if (assetKey == null) { ProjectAssetManager mgr = getLookup().lookup(ProjectAssetManager.class); @@ -242,6 +295,13 @@ public class AssetDataObject extends MultiDataObject { return assetKey; } + /** + * Applies the supplied keys data to the assets assetKey so it will be + * loaded with these settings next time loadAsset is actually loading the + * asset from the ProjectAssetManager. + * + * @param key + */ public synchronized void setAssetKeyData(AssetKey key) { try { BeanUtils.copyProperties(getAssetKey(), key); @@ -287,9 +347,9 @@ public class AssetDataObject extends MultiDataObject { if (pm == null || loadingThread != Thread.currentThread()) { return; } - FileObject obj = pm.getAssetFileObject(ak); - if (obj != null && !assetList.contains(obj)) { - assetList.add(obj); + FileObject fObj = pm.getAssetFileObject(ak); + if (fObj != null && !assetList.contains(fObj)) { + assetList.add(fObj); assetKeyList.add(ak); } } @@ -299,9 +359,9 @@ public class AssetDataObject extends MultiDataObject { if (pm == null || loadingThread != Thread.currentThread()) { return; } - FileObject obj = pm.getAssetFileObject(ak1); - if (obj != null && assetList.contains(obj)) { - assetList.remove(obj); + FileObject fObj = pm.getAssetFileObject(ak1); + if (fObj != null && assetList.contains(fObj)) { + assetList.remove(fObj); assetKeyList.remove(ak1); } if (!failedList.contains(ak1)) { diff --git a/sdk/jme3-core/src/com/jme3/gde/core/assets/AssetDataPropertyChangeListener.java b/sdk/jme3-core/src/com/jme3/gde/core/assets/AssetDataPropertyChangeListener.java new file mode 100644 index 000000000..83169fb31 --- /dev/null +++ b/sdk/jme3-core/src/com/jme3/gde/core/assets/AssetDataPropertyChangeListener.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2003-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.gde.core.assets; + +/** + * Allows listening for property changes in AssetData objects. The call is not + * necessarily coming from the main AWT thread but from the import thread. + * + * @author normenhansen + */ +public interface AssetDataPropertyChangeListener { + + public void assetDataPropertyChanged(String property, String before, String after); +} diff --git a/sdk/jme3-core/src/com/jme3/gde/core/assets/SpatialAssetDataObject.java b/sdk/jme3-core/src/com/jme3/gde/core/assets/SpatialAssetDataObject.java index cd2b3dde7..71549fdaa 100644 --- a/sdk/jme3-core/src/com/jme3/gde/core/assets/SpatialAssetDataObject.java +++ b/sdk/jme3-core/src/com/jme3/gde/core/assets/SpatialAssetDataObject.java @@ -33,11 +33,9 @@ package com.jme3.gde.core.assets; import com.jme3.asset.AssetKey; import com.jme3.asset.ModelKey; -import com.jme3.scene.Geometry; -import com.jme3.scene.SceneGraphVisitorAdapter; +import com.jme3.gde.core.util.SpatialUtil; import com.jme3.scene.Spatial; import java.io.IOException; -import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; import org.openide.DialogDisplayer; @@ -81,7 +79,7 @@ public class SpatialAssetDataObject extends AssetDataObject { @Override public synchronized Spatial loadAsset() { - if (isModified() && savable != null) { + if (savable != null) { return (Spatial) savable; } ProjectAssetManager mgr = getLookup().lookup(ProjectAssetManager.class); @@ -98,7 +96,7 @@ public class SpatialAssetDataObject extends AssetDataObject { listListener.stop(); savable = spatial; if (!(this instanceof BinaryModelDataObject)) { - storeOriginalPathUserData(); + SpatialUtil.storeOriginalPathUserData(spatial); } lock.releaseLock(); return spatial; @@ -135,56 +133,15 @@ public class SpatialAssetDataObject extends AssetDataObject { AssetData properties = targetModel.getLookup().lookup(AssetData.class); if (properties != null) { if (properties.getProperty("ORIGINAL_PATH") == null) { - properties.setProperty("ORIGINAL_PATH", mgr.getRelativeAssetPath(outFile.getPath())); + String path = mgr.getRelativeAssetPath(getPrimaryFile().getPath()); + properties.setProperty("ORIGINAL_PATH", path); + logger.log(Level.INFO, "Set original path for {0} to {1}", new Object[]{getName(), path}); } + } else { + logger.log(Level.WARNING, "New object {0} has no AssetData?", getName()); } } catch (Exception ex) { Exceptions.printStackTrace(ex); } } - - /** - * Stores ORIGINAL_NAME and ORIGINAL_PATH UserData to all Geometry in - * loaded spatial - */ - protected void storeOriginalPathUserData() { - final ArrayList geomMap = new ArrayList(); - Spatial spat = (Spatial) savable; - if (spat != null) { - spat.depthFirstTraversal(new SceneGraphVisitorAdapter() { - @Override - public void visit(Geometry geom) { - StringBuilder geometryIdentifier = new StringBuilder(); - Spatial curSpat = geom; - String geomName = curSpat.getName(); - if (geomName == null) { - logger.log(Level.WARNING, "Null geometry name!"); - geomName = "null"; - } - geom.setUserData("ORIGINAL_NAME", geomName); - logger.log(Level.FINE, "Set ORIGINAL_NAME for {0}", geomName); - while (curSpat != null) { - String name = curSpat.getName(); - if (name == null) { - logger.log(Level.WARNING, "Null spatial name!"); - name = "null"; - } - geometryIdentifier.insert(0, name); - geometryIdentifier.insert(0, '/'); - curSpat = curSpat.getParent(); - } - String id = geometryIdentifier.toString(); - if (geomMap.contains(id)) { - logger.log(Level.WARNING, "Cannot create unique name for Geometry {0}: {1}", new Object[]{geom, id}); - } - geomMap.add(id); - geom.setUserData("ORIGINAL_PATH", id); - logger.log(Level.FINE, "Set ORIGINAL_PATH for {0}", id); - super.visit(geom); - } - }); - } else { - logger.log(Level.SEVERE, "No geometry available when trying to scan initial geometry configuration"); - } - } } diff --git a/sdk/jme3-core/src/com/jme3/gde/core/util/SpatialUtil.java b/sdk/jme3-core/src/com/jme3/gde/core/util/SpatialUtil.java new file mode 100644 index 000000000..6fcc83330 --- /dev/null +++ b/sdk/jme3-core/src/com/jme3/gde/core/util/SpatialUtil.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2003-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.gde.core.util; + +import com.jme3.gde.core.scene.ApplicationLogHandler.LogLevel; +import com.jme3.scene.Geometry; +import com.jme3.scene.SceneGraphVisitor; +import com.jme3.scene.SceneGraphVisitorAdapter; +import com.jme3.scene.Spatial; +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Various utilities, mostly for operating on Spatials recursively. + * + * @author normenhansen + */ +public class SpatialUtil { + + private static final Logger logger = Logger.getLogger(SpatialUtil.class.getName()); + //TODO: use these variables + public static final String ORIGINAL_NAME = "ORIGINAL_NAME"; + public static final String ORIGINAL_PATH = "ORIGINAL_PATH"; + + /** + * Gets a "pathname" for the given Spatial, combines the Spatials and + * parents names to make a long name. This "path" is stored in geometry + * after the first import for example. + * + * @param spat + * @return + */ + public static String getSpatialPath(Spatial spat) { + StringBuilder geometryIdentifier = new StringBuilder(); + while (spat != null) { + String name = spat.getName(); + if (name == null) { + logger.log(Level.WARNING, "Null spatial name!"); + name = "null"; + } + geometryIdentifier.insert(0, name); + geometryIdentifier.insert(0, '/'); + spat = spat.getParent(); + } + String id = geometryIdentifier.toString(); + return id; + } + + /** + * Stores ORIGINAL_NAME and ORIGINAL_PATH UserData to all Geometry in loaded + * spatial + * + * @param spat + */ + public static void storeOriginalPathUserData(Spatial spat) { + //TODO: only stores for geometry atm + final ArrayList geomMap = new ArrayList(); + if (spat != null) { + spat.depthFirstTraversal(new SceneGraphVisitorAdapter() { + @Override + public void visit(Geometry geom) { + Spatial curSpat = geom; + String geomName = curSpat.getName(); + if (geomName == null) { + logger.log(Level.WARNING, "Null geometry 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 Geometry {0}: {1}", new Object[]{geom, id}); + } + geomMap.add(id); + geom.setUserData("ORIGINAL_PATH", id); + logger.log(Level.FINE, "Set ORIGINAL_PATH for {0}", id); + super.visit(geom); + } + }); + } else { + logger.log(Level.SEVERE, "No geometry available when trying to scan initial geometry configuration"); + } + } + + /** + * Finds a previously marked spatial in the supplied root Spatial, creates + * the name and path to be looked for from the given needle Spatial. + * + * @param root + * @param needle + * @return + */ + public static Spatial findTaggedSpatial(final Spatial root, final Spatial needle) { + if (needle == null) { + logger.log(Level.WARNING, "Trying to find null needle for {0}", root); + return null; + } + final String name = needle.getName(); + final String path = getSpatialPath(needle); + if (name == null || path == null) { + logger.log(Level.INFO, "Trying to find tagged spatial with null name spatial for {0}.", root); + } + final Class clazz = needle.getClass(); + String rootName = root.getUserData("ORIGINAL_NAME"); + String rootPath = root.getUserData("ORIGINAL_PATH"); + if (name.equals(rootName) && path.equals(rootPath)) { + return root; + } + final SpatialHolder holder = new SpatialHolder(); + root.depthFirstTraversal(new SceneGraphVisitor() { + 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}); + } + } + } + }); + return holder.spatial; + } + + /** + * Finds a spatial in the given Spatial tree with the specified name and + * path, the path and name are constructed from the given (sub-)spatial(s) + * and is not read from the UserData of the objects. This is mainly used to + * check if the original spatial still exists in the original file. + * + * @param root + * @param name + * @param path + */ + public static Spatial findSpatial(final Spatial root, final String name, final String path) { + if (name.equals(root.getName()) && getSpatialPath(root).equals(path)) { + return root; + } + final SpatialHolder holder = new SpatialHolder(); + root.depthFirstTraversal(new SceneGraphVisitor() { + public void visit(Spatial spatial) { + if (name.equals(spatial.getName()) && getSpatialPath(spatial).equals(path)) { + if (holder.spatial == null) { + holder.spatial = spatial; + } else { + logger.log(Level.WARNING, "Found spatial {0} twice in {1}", new Object[]{path, root}); + } + } + } + }); + return holder.spatial; + } + + public static void updateOriginalMeshData(final Spatial root, final Spatial original) { + original.depthFirstTraversal(new SceneGraphVisitorAdapter() { + @Override + public void visit(Geometry geom) { + //will always return same class type, so casting is safe + Geometry spat = (Geometry) findTaggedSpatial(root, geom); + if (spat != null) { + spat.setMesh(geom.getMesh().deepClone()); + logger.log(LogLevel.USERINFO, "Updated mesh for geometry {0}", geom.getName()); + } else { +// addNewOriginal() + } + } + }); + return; + } + + private void addNewOriginalGeometry(final Spatial root, final Geometry original) { + + return; + } + + public void clearRemovedOriginals(final Spatial root, final Spatial original) { + + return; + } + + private static class SpatialHolder { + + Spatial spatial; + } +}