diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/TextureHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/TextureHelper.java index 4fb1caaa4..99a6be247 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/TextureHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/TextureHelper.java @@ -177,8 +177,10 @@ public class TextureHelper extends AbstractBlenderHelper { break; case TEX_IMAGE:// (it is first because probably this will be most commonly used) Pointer pImage = (Pointer) tex.getFieldValue("ima"); - Structure image = pImage.fetchData(dataRepository.getInputStream()).get(0); - result = this.getTextureFromImage(image, dataRepository); + if (pImage.isNotNull()){ + Structure image = pImage.fetchData(dataRepository.getInputStream()).get(0); + result = this.getTextureFromImage(image, dataRepository); + } break; case TEX_CLOUDS: result = this.clouds(tex, width, height, dataRepository); @@ -212,7 +214,7 @@ public class TextureHelper extends AbstractBlenderHelper { break; case TEX_PLUGIN: case TEX_ENVMAP:// TODO: implement envmap texture - LOGGER.log(Level.WARNING, "Unsupported texture type: " + type + " for texture: " + tex.getName()); + LOGGER.log(Level.WARNING, "Unsupported texture type: {0} for texture: {1}", new Object[]{type, tex.getName()}); break; default: throw new BlenderFileException("Unknown texture type: " + type + " for texture: " + tex.getName()); diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/structures/Properties.java b/engine/src/blender/com/jme3/scene/plugins/blender/structures/Properties.java index a828f4418..02806a961 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/structures/Properties.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/structures/Properties.java @@ -6,9 +6,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; - -import sun.reflect.generics.reflectiveObjects.NotImplementedException; - import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; @@ -158,7 +155,7 @@ public class Properties implements Cloneable, Savable { break; } case IDP_NUMTYPES: - throw new NotImplementedException(); + throw new UnsupportedOperationException(); // case IDP_ID://not yet implemented in blender // return null; default: diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryClassObject.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryClassObject.java index 4151496f0..e66a945f6 100644 --- a/engine/src/core-plugins/com/jme3/export/binary/BinaryClassObject.java +++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryClassObject.java @@ -42,5 +42,5 @@ class BinaryClassObject { byte[] alias; String className; - + int[] classHierarchyVersions; } diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java index c34672f26..b4e30457e 100644 --- a/engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java +++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java @@ -34,6 +34,8 @@ package com.jme3.export.binary; import com.jme3.export.JmeExporter; import com.jme3.export.Savable; +import com.jme3.export.FormatVersion; +import com.jme3.export.SavableClassUtil; import com.jme3.math.FastMath; import java.io.ByteArrayOutputStream; import java.io.File; @@ -43,6 +45,7 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.IdentityHashMap; +import java.util.logging.Level; import java.util.logging.Logger; /** @@ -162,7 +165,7 @@ public class BinaryExporter implements JmeExporter { public static boolean debug = false; public static boolean useFastBufs = true; - + public BinaryExporter() { } @@ -179,32 +182,44 @@ public class BinaryExporter implements JmeExporter { locationTable.clear(); contentKeys.clear(); + // write signature and version + os.write(ByteUtils.convertToBytes(FormatVersion.SIGNATURE)); + os.write(ByteUtils.convertToBytes(FormatVersion.VERSION)); + int id = processBinarySavable(object); // write out tag table - int ttbytes = 0; + int classTableSize = 0; int classNum = classes.keySet().size(); - int aliasWidth = ((int) FastMath.log(classNum, 256) + 1); // make all - // aliases a - // fixed width + int aliasSize = ((int) FastMath.log(classNum, 256) + 1); // make all + // aliases a + // fixed width + os.write(ByteUtils.convertToBytes(classNum)); for (String key : classes.keySet()) { BinaryClassObject bco = classes.get(key); // write alias byte[] aliasBytes = fixClassAlias(bco.alias, - aliasWidth); + aliasSize); os.write(aliasBytes); - ttbytes += aliasWidth; - + classTableSize += aliasSize; + + // jME3 NEW: Write class hierarchy version numbers + os.write( bco.classHierarchyVersions.length ); + for (int version : bco.classHierarchyVersions){ + os.write(ByteUtils.convertToBytes(version)); + } + classTableSize += 1 + bco.classHierarchyVersions.length * 4; + // write classname size & classname byte[] classBytes = key.getBytes(); os.write(ByteUtils.convertToBytes(classBytes.length)); os.write(classBytes); - ttbytes += 4 + classBytes.length; - + classTableSize += 4 + classBytes.length; + + // for each field, write alias, type, and name os.write(ByteUtils.convertToBytes(bco.nameFields.size())); - for (String fieldName : bco.nameFields.keySet()) { BinaryClassField bcf = bco.nameFields.get(fieldName); os.write(bcf.alias); @@ -214,7 +229,7 @@ public class BinaryExporter implements JmeExporter { byte[] fNameBytes = fieldName.getBytes(); os.write(ByteUtils.convertToBytes(fNameBytes.length)); os.write(fNameBytes); - ttbytes += 2 + 4 + fNameBytes.length; + classTableSize += 2 + 4 + fNameBytes.length; } } @@ -242,9 +257,9 @@ public class BinaryExporter implements JmeExporter { alreadySaved.put(savableName + getChunk(pair), bucket); } bucket.add(pair); - byte[] aliasBytes = fixClassAlias(classes.get(savableName).alias, aliasWidth); + byte[] aliasBytes = fixClassAlias(classes.get(savableName).alias, aliasSize); out.write(aliasBytes); - location += aliasWidth; + location += aliasSize; BinaryOutputCapsule cap = contentTable.get(savable).getContent(); out.write(ByteUtils.convertToBytes(cap.bytes.length)); location += 4; // length of bytes @@ -254,13 +269,13 @@ public class BinaryExporter implements JmeExporter { // write out location table // tag/location - int locNum = locationTable.keySet().size(); - os.write(ByteUtils.convertToBytes(locNum)); - int locbytes = 0; + int numLocations = locationTable.keySet().size(); + os.write(ByteUtils.convertToBytes(numLocations)); + int locationTableSize = 0; for (Integer key : locationTable.keySet()) { os.write(ByteUtils.convertToBytes(key)); os.write(ByteUtils.convertToBytes(locationTable.get(key))); - locbytes += 8; + locationTableSize += 8; } // write out number of root ids - hardcoded 1 for now @@ -278,11 +293,11 @@ public class BinaryExporter implements JmeExporter { if (debug ) { logger.info("Stats:"); - logger.info("classes: " + classNum); - logger.info("class table: " + ttbytes + " bytes"); - logger.info("objects: " + locNum); - logger.info("location table: " + locbytes + " bytes"); - logger.info("data: " + location + " bytes"); + logger.log(Level.INFO, "classes: {0}", classNum); + logger.log(Level.INFO, "class table: {0} bytes", classTableSize); + logger.log(Level.INFO, "objects: {0}", numLocations); + logger.log(Level.INFO, "location table: {0} bytes", locationTableSize); + logger.log(Level.INFO, "data: {0} bytes", location); } return true; @@ -331,17 +346,38 @@ public class BinaryExporter implements JmeExporter { return contentTable.get(object).getContent(); } + private BinaryClassObject createClassObject(Class clazz) throws IOException{ + BinaryClassObject bco = new BinaryClassObject(); + bco.alias = generateTag(); + bco.nameFields = new HashMap(); + + ArrayList versionList = new ArrayList(); + Class superclass = clazz; + do { + versionList.add(SavableClassUtil.getSavableVersion(superclass)); + superclass = superclass.getSuperclass(); + } while (superclass != null && SavableClassUtil.isImplementingSavable(superclass)); + + int[] versions = new int[versionList.size()]; + for (int i = 0; i < versionList.size(); i++){ + versions[i] = versionList.get(i); + } + bco.classHierarchyVersions = versions; + + classes.put(clazz.getName(), bco); + + return bco; + } + public int processBinarySavable(Savable object) throws IOException { if (object == null) { return -1; } + Class clazz = object.getClass(); BinaryClassObject bco = classes.get(object.getClass().getName()); // is this class been looked at before? in tagTable? if (bco == null) { - bco = new BinaryClassObject(); - bco.alias = generateTag(); - bco.nameFields = new HashMap(); - classes.put(object.getClass().getName(), bco); + bco = createClassObject(object.getClass()); } // is object in contentTable? diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java index 1582a4930..62aeec877 100644 --- a/engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java +++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java @@ -32,9 +32,11 @@ package com.jme3.export.binary; -import com.jme3.export.SavableClassFinder; +import com.jme3.export.SavableClassUtil; import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetManager; +import com.jme3.export.FormatVersion; +import com.jme3.export.InputCapsule; import com.jme3.export.JmeImporter; import com.jme3.export.ReadListener; import com.jme3.export.Savable; @@ -57,6 +59,7 @@ import java.util.logging.Logger; /** * @author Joshua Slack + * @author Kirill Vainer - Version number, Fast buffer reading */ public final class BinaryImporter implements JmeImporter { private static final Logger logger = Logger.getLogger(BinaryImporter.class @@ -81,6 +84,7 @@ public final class BinaryImporter implements JmeImporter { private byte[] dataArray; private int aliasWidth; + private int formatVersion; private static final boolean fastRead = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN; @@ -89,6 +93,10 @@ public final class BinaryImporter implements JmeImporter { public BinaryImporter() { } + public int getFormatVersion(){ + return formatVersion; + } + public static boolean canUseFastBuffers(){ return fastRead; } @@ -139,20 +147,59 @@ public final class BinaryImporter implements JmeImporter { public Savable load(InputStream is, ReadListener listener, ByteArrayOutputStream baos) throws IOException { contentTable.clear(); BufferedInputStream bis = new BufferedInputStream(is); - int numClasses = ByteUtils.readInt(bis); + + int numClasses; + + // Try to read signature + int maybeSignature = ByteUtils.readInt(bis); + if (maybeSignature == FormatVersion.SIGNATURE){ + // this is a new version J3O file + formatVersion = ByteUtils.readInt(bis); + numClasses = ByteUtils.readInt(bis); + + // check if this binary is from the future + if (formatVersion > FormatVersion.VERSION){ + throw new IOException("The binary file is of newer version than expected! " + + formatVersion + " > " + FormatVersion.VERSION); + } + }else{ + // this is an old version J3O file + // the signature was actually the class count + numClasses = maybeSignature; + + // 0 indicates version before we started adding + // version numbers + formatVersion = 0; + } + int bytes = 4; aliasWidth = ((int)FastMath.log(numClasses, 256) + 1); classes.clear(); for(int i = 0; i < numClasses; i++) { String alias = readString(bis, aliasWidth); - + + // jME3 NEW: Read class version number + int[] classHierarchyVersions; + if (formatVersion >= 1){ + int classHierarchySize = bis.read(); + classHierarchyVersions = new int[classHierarchySize]; + for (int j = 0; j < classHierarchySize; j++){ + classHierarchyVersions[j] = ByteUtils.readInt(bis); + } + }else{ + classHierarchyVersions = new int[]{ 0 }; + } + + // read classname and classname size int classLength = ByteUtils.readInt(bis); String className = readString(bis, classLength); + BinaryClassObject bco = new BinaryClassObject(); bco.alias = alias.getBytes(); bco.className = className; - + bco.classHierarchyVersions = classHierarchyVersions; + int fields = ByteUtils.readInt(bis); bytes += (8 + aliasWidth + classLength); @@ -210,9 +257,9 @@ public final class BinaryImporter implements JmeImporter { Savable rVal = readObject(id); if (debug) { logger.info("Importer Stats: "); - logger.info("Tags: "+numClasses); - logger.info("Objects: "+numLocs); - logger.info("Data Size: "+dataArray.length); + logger.log(Level.INFO, "Tags: {0}", numClasses); + logger.log(Level.INFO, "Objects: {0}", numLocs); + logger.log(Level.INFO, "Data Size: {0}", dataArray.length); } dataArray = null; return rVal; @@ -247,7 +294,8 @@ public final class BinaryImporter implements JmeImporter { return rVal; } - public BinaryInputCapsule getCapsule(Savable id) { + @Override + public InputCapsule getCapsule(Savable id) { return capsuleTable.get(id); } @@ -291,11 +339,11 @@ public final class BinaryImporter implements JmeImporter { int dataLength = ByteUtils.convertIntFromBytes(dataArray, loc); loc+=4; - BinaryInputCapsule cap = new BinaryInputCapsule(this, bco); + Savable out = SavableClassUtil.fromName(bco.className, loaders); + + BinaryInputCapsule cap = new BinaryInputCapsule(this, out, bco); cap.setContent(dataArray, loc, loc+dataLength); - Savable out = SavableClassFinder.fromName(bco.className, cap, loaders); - capsuleTable.put(out, cap); contentTable.put(id, out); diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java index d68c5ef0c..a7e0e2e06 100644 --- a/engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java +++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java @@ -39,7 +39,6 @@ import com.jme3.util.IntMap; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; @@ -60,13 +59,15 @@ final class BinaryInputCapsule implements InputCapsule { protected BinaryImporter importer; protected BinaryClassObject cObj; + protected Savable savable; protected HashMap fieldData; protected int index = 0; - public BinaryInputCapsule(BinaryImporter importer, BinaryClassObject bco) { + public BinaryInputCapsule(BinaryImporter importer, Savable savable, BinaryClassObject bco) { this.importer = importer; this.cObj = bco; + this.savable = savable; } public void setContent(byte[] content, int start, int limit) { @@ -255,6 +256,26 @@ final class BinaryInputCapsule implements InputCapsule { } } } + + public int getSavableVersion(Class desiredClass){ + Class thisClass = savable.getClass(); + int count = 0; + while (thisClass != null && thisClass != desiredClass){ + thisClass = thisClass.getSuperclass(); + count ++; + } + if (thisClass == null){ + throw new IllegalArgumentException(savable.getClass().getName() + + " does not extend " + + desiredClass.getName() + "!"); + }else if (count > cObj.classHierarchyVersions.length){ + throw new IllegalArgumentException(savable.getClass().getName() + + " cannot access version of " + + desiredClass.getName() + + " because it doesn't implement Savable"); + } + return cObj.classHierarchyVersions[count]; + } public BitSet readBitSet(String name, BitSet defVal) throws IOException { BinaryClassField field = cObj.nameFields.get(name); diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryLoaderModule.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryLoaderModule.java deleted file mode 100644 index eae0be055..000000000 --- a/engine/src/core-plugins/com/jme3/export/binary/BinaryLoaderModule.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2009-2010 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.export.binary; - -import com.jme3.export.InputCapsule; -import com.jme3.export.Savable; -import java.io.IOException; - -/** - * BinaryLoaderModule defines two methods, the first provides a key value to - * look for to issue the load command. This key is typically (and should be) - * the class name the loader is responsible for. While load handles creating - * a new instance of the class. - * @author mpowell - * - */ -interface BinaryLoaderModule { - - String getKey(); - - /** - * The inputCapsule parameter is not used at all. - * - * The DOMOutputStream class calls this method with a null parameter, so - * if you make use of the parameter, either handle null 'inputCapsule' - * or rearrange the class hierarchy to satisfy DOMOuptutStream. - * - * @param inputCapsule A value which is currently ignored by all - * implementation classes. - */ - Savable load(InputCapsule inputCapsule) throws IOException; -} diff --git a/engine/src/core/com/jme3/animation/AnimControl.java b/engine/src/core/com/jme3/animation/AnimControl.java index e51ff44bd..dbb0856cb 100644 --- a/engine/src/core/com/jme3/animation/AnimControl.java +++ b/engine/src/core/com/jme3/animation/AnimControl.java @@ -39,7 +39,6 @@ import com.jme3.export.Savable; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.scene.Mesh; -import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.control.AbstractControl; import com.jme3.scene.control.Control; @@ -68,7 +67,7 @@ import java.util.HashMap; * * @author Kirill Vainer */ -public final class AnimControl extends AbstractControl implements Savable, Cloneable { +public final class AnimControl extends AbstractControl implements Cloneable { /** * Skeleton object must contain corresponding data for the targets' weight buffers. @@ -351,16 +350,17 @@ public final class AnimControl extends AbstractControl implements Savable, Clone skeleton = (Skeleton) in.readSavable("skeleton", null); animationMap = (HashMap) in.readStringSavableMap("animations", null); - //changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split - //if we find a target mesh array the AnimControl creates the SkeletonControl for old files and add it to the spatial. - //When backward compatibility won't be needed anymore this can deleted - Savable[] sav = in.readSavableArray("targets", null); - if (sav != null) { - Mesh[] tg = null; - tg = new Mesh[sav.length]; - System.arraycopy(sav, 0, tg, 0, sav.length); - skeletonControl = new SkeletonControl(tg, skeleton); - spatial.addControl(skeletonControl); + if (im.getFormatVersion() == 0){ + //changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split + //if we find a target mesh array the AnimControl creates the SkeletonControl for old files and add it to the spatial. + //When backward compatibility won't be needed anymore this can deleted + Savable[] sav = in.readSavableArray("targets", null); + if (sav != null) { + Mesh[] targets = new Mesh[sav.length]; + System.arraycopy(sav, 0, targets, 0, sav.length); + skeletonControl = new SkeletonControl(targets, skeleton); + spatial.addControl(skeletonControl); + } } } } diff --git a/engine/src/core/com/jme3/animation/BoneTrack.java b/engine/src/core/com/jme3/animation/BoneTrack.java index da3e18fd2..6f72dd8d4 100644 --- a/engine/src/core/com/jme3/animation/BoneTrack.java +++ b/engine/src/core/com/jme3/animation/BoneTrack.java @@ -268,27 +268,27 @@ public final class BoneTrack implements Savable { //Backward compatibility for old j3o files generated before revision 6807 - if (translations == null) { - Savable[] sav = ic.readSavableArray("translations", null); - if (sav != null) { - translations = new CompactVector3Array(); - Vector3f[] transCopy = new Vector3f[sav.length]; - System.arraycopy(sav, 0, transCopy, 0, sav.length); - translations.add(transCopy); - translations.freeze(); + if (im.getFormatVersion() == 0){ + if (translations == null) { + Savable[] sav = ic.readSavableArray("translations", null); + if (sav != null) { + translations = new CompactVector3Array(); + Vector3f[] transCopy = new Vector3f[sav.length]; + System.arraycopy(sav, 0, transCopy, 0, sav.length); + translations.add(transCopy); + translations.freeze(); + } } - } - if (rotations == null) { - Savable[] sav = ic.readSavableArray("rotations", null); - if (sav != null) { - rotations = new CompactQuaternionArray(); - Quaternion[] rotCopy = new Quaternion[sav.length]; - System.arraycopy(sav, 0, rotCopy, 0, sav.length); - rotations.add(rotCopy); - rotations.freeze(); + if (rotations == null) { + Savable[] sav = ic.readSavableArray("rotations", null); + if (sav != null) { + rotations = new CompactQuaternionArray(); + Quaternion[] rotCopy = new Quaternion[sav.length]; + System.arraycopy(sav, 0, rotCopy, 0, sav.length); + rotations.add(rotCopy); + rotations.freeze(); + } } } - - } } diff --git a/engine/src/core/com/jme3/animation/SkeletonControl.java b/engine/src/core/com/jme3/animation/SkeletonControl.java index ef735be6e..5e6d0b16e 100644 --- a/engine/src/core/com/jme3/animation/SkeletonControl.java +++ b/engine/src/core/com/jme3/animation/SkeletonControl.java @@ -17,6 +17,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; import com.jme3.scene.Node; import com.jme3.scene.Spatial; +import com.jme3.scene.UserData; import com.jme3.scene.VertexBuffer; import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.control.AbstractControl; @@ -25,14 +26,16 @@ import com.jme3.util.TempVars; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.FloatBuffer; +import java.util.ArrayList; /** * The Skeleton control deforms a model according to a skeleton, - * It handles the computation of the deformtation matrices and performs the transformations on the mesh + * It handles the computation of the deformation matrices and performs + * the transformations on the mesh * * @author Rémy Bouquet Based on AnimControl by Kirill Vainer */ -public class SkeletonControl extends AbstractControl implements Savable, Cloneable { +public class SkeletonControl extends AbstractControl implements Cloneable { /** * The skeleton of the model @@ -49,21 +52,90 @@ public class SkeletonControl extends AbstractControl implements Savable, Cloneab private boolean wasMeshUpdated = false; /** - * for serialization only + * Serialization only. Do not use. */ public SkeletonControl() { } /** - * Creates a skeleton control - * @param targets the meshes controled by the skeleton + * Creates a skeleton control. + * The list of targets will be acquired automatically when + * the control is attached to a node. + * + * @param skeleton the skeleton + */ + public SkeletonControl(Skeleton skeleton) { + this.skeleton = skeleton; + } + + /** + * Creates a skeleton control. + * + * @param targets the meshes controlled by the skeleton * @param skeleton the skeleton */ - public SkeletonControl(Mesh[] targets, Skeleton skeleton) { + @Deprecated + public SkeletonControl(Mesh[] targets, Skeleton skeleton){ this.skeleton = skeleton; this.targets = targets; } + + private boolean isMeshAnimated(Mesh mesh){ + return mesh.getBuffer(Type.BindPosePosition) != null; + } + private Mesh[] findTargets(Node node){ + Mesh sharedMesh = null; + ArrayList animatedMeshes = new ArrayList(); + + for (Spatial child : node.getChildren()){ + if (!(child instanceof Geometry)){ + continue; // could be an attachment node, ignore. + } + + Geometry geom = (Geometry) child; + + // is this geometry using a shared mesh? + Mesh childSharedMesh = geom.getUserData(UserData.JME_SHAREDMESH); + + if (childSharedMesh != null){ + + // Don't bother with non-animated shared meshes + if (isMeshAnimated(childSharedMesh)){ + + // child is using shared mesh, + // so animate the shared mesh but ignore child + if (sharedMesh == null){ + sharedMesh = childSharedMesh; + }else if (sharedMesh != childSharedMesh){ + throw new IllegalStateException("Two conflicting shared meshes for " + node); + } + } + }else{ + Mesh mesh = geom.getMesh(); + if (isMeshAnimated(mesh)){ + animatedMeshes.add(mesh); + } + } + } + + if (sharedMesh != null){ + animatedMeshes.add(sharedMesh); + } + + return animatedMeshes.toArray(new Mesh[animatedMeshes.size()]); + } + + @Override + public void setSpatial(Spatial spatial){ + if (spatial != null){ + Node node = (Node) spatial; + targets = findTargets(node); + }else{ + targets = null; + } + } + @Override protected void controlRender(RenderManager rm, ViewPort vp) { if (!wasMeshUpdated) { @@ -87,13 +159,11 @@ public class SkeletonControl extends AbstractControl implements Savable, Cloneab @Override protected void controlUpdate(float tpf) { wasMeshUpdated = false; - } void resetToBind() { - for (int i = 0; i < targets.length; i++) { - Mesh mesh = targets[i]; - if (targets[i].getBuffer(Type.BindPosePosition) != null) { + for (Mesh mesh : targets){ + if (isMeshAnimated(mesh)) { VertexBuffer bi = mesh.getBuffer(Type.BoneIndex); ByteBuffer bib = (ByteBuffer) bi.getData(); if (!bib.hasArray()) { @@ -177,12 +247,12 @@ public class SkeletonControl extends AbstractControl implements Savable, Cloneab * sets the skeleton for this control * @param skeleton */ - public void setSkeleton(Skeleton skeleton) { - this.skeleton = skeleton; - } +// public void setSkeleton(Skeleton skeleton) { +// this.skeleton = skeleton; +// } /** - * retuns the targets meshes of this ocntrol + * returns the targets meshes of this control * @return */ public Mesh[] getTargets() { @@ -193,9 +263,9 @@ public class SkeletonControl extends AbstractControl implements Savable, Cloneab * sets the target meshes of this control * @param targets */ - public void setTargets(Mesh[] targets) { - this.targets = targets; - } +// public void setTargets(Mesh[] targets) { +// this.targets = targets; +// } private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) { int maxWeightsPerVert = mesh.getMaxNumWeights(); diff --git a/engine/src/core/com/jme3/cinematic/Cinematic.java b/engine/src/core/com/jme3/cinematic/Cinematic.java index 5c190ed3e..6d8e1f458 100644 --- a/engine/src/core/com/jme3/cinematic/Cinematic.java +++ b/engine/src/core/com/jme3/cinematic/Cinematic.java @@ -61,7 +61,7 @@ import java.util.logging.Logger; * * @author Nehon */ -public class Cinematic extends AbstractCinematicEvent implements Savable, AppState { +public class Cinematic extends AbstractCinematicEvent implements AppState { private static final Logger logger = Logger.getLogger(Application.class.getName()); private String niftyXmlPath = null; diff --git a/engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java b/engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java index e6b1cadeb..059cc3993 100644 --- a/engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java +++ b/engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java @@ -48,7 +48,7 @@ import java.util.List; * * @author Nehon */ -public abstract class AbstractCinematicEvent implements CinematicEvent, Savable { +public abstract class AbstractCinematicEvent implements CinematicEvent { protected PlayState playState = PlayState.Stopped; protected float speed = 1; diff --git a/engine/src/core/com/jme3/cinematic/events/CinematicEvent.java b/engine/src/core/com/jme3/cinematic/events/CinematicEvent.java index cb4abeac1..0cccac535 100644 --- a/engine/src/core/com/jme3/cinematic/events/CinematicEvent.java +++ b/engine/src/core/com/jme3/cinematic/events/CinematicEvent.java @@ -35,12 +35,13 @@ import com.jme3.animation.LoopMode; import com.jme3.app.Application; import com.jme3.cinematic.Cinematic; import com.jme3.cinematic.PlayState; +import com.jme3.export.Savable; /** * * @author Nehon */ -public interface CinematicEvent { +public interface CinematicEvent extends Savable { /** * Starts the animation diff --git a/engine/src/core/com/jme3/effect/ParticleEmitter.java b/engine/src/core/com/jme3/effect/ParticleEmitter.java index d418f50ed..0ee6313ba 100644 --- a/engine/src/core/com/jme3/effect/ParticleEmitter.java +++ b/engine/src/core/com/jme3/effect/ParticleEmitter.java @@ -1118,23 +1118,25 @@ public class ParticleEmitter extends Geometry { particleInfluencer = (ParticleInfluencer) ic.readSavable("influencer", DEFAULT_INFLUENCER); - // compatibility before the control inside particle emitter - // was changed: - // find it in the controls and take it out, then add the proper one in - for (int i = 0; i < controls.size(); i++) { - Object obj = controls.get(i); - if (obj instanceof ParticleEmitter) { - controls.remove(i); - // now add the proper one in - controls.add(control); - break; + if (im.getFormatVersion() == 0){ + // compatibility before the control inside particle emitter + // was changed: + // find it in the controls and take it out, then add the proper one in + for (int i = 0; i < controls.size(); i++){ + Object obj = controls.get(i); + if (obj instanceof ParticleEmitter){ + controls.remove(i); + // now add the proper one in + controls.add(control); + break; + } } - } - // compatability before gravity was not a vector but a float - if (gravity == null) { - gravity = new Vector3f(); - gravity.y = ic.readFloat("gravity", 0); + // compatability before gravity was not a vector but a float + if (gravity == null){ + gravity = new Vector3f(); + gravity.y = ic.readFloat("gravity", 0); + } } } } diff --git a/engine/src/core/com/jme3/effect/influencers/DefaultParticleInfluencer.java b/engine/src/core/com/jme3/effect/influencers/DefaultParticleInfluencer.java index 7eb14e6c7..22a9c6a8d 100644 --- a/engine/src/core/com/jme3/effect/influencers/DefaultParticleInfluencer.java +++ b/engine/src/core/com/jme3/effect/influencers/DefaultParticleInfluencer.java @@ -56,7 +56,7 @@ public class DefaultParticleInfluencer implements ParticleInfluencer { @Override public void read(JmeImporter im) throws IOException { InputCapsule ic = im.getCapsule(this); - startVelocity = (Vector3f) ic.readSavable("startVelocity", Vector3f.ZERO); + startVelocity = (Vector3f) ic.readSavable("startVelocity", Vector3f.ZERO.clone()); velocityVariation = ic.readFloat("variation", 0.2f); } diff --git a/engine/src/core/com/jme3/effect/shapes/EmitterMeshVertexShape.java b/engine/src/core/com/jme3/effect/shapes/EmitterMeshVertexShape.java index 982b51015..07a54b632 100644 --- a/engine/src/core/com/jme3/effect/shapes/EmitterMeshVertexShape.java +++ b/engine/src/core/com/jme3/effect/shapes/EmitterMeshVertexShape.java @@ -1,5 +1,6 @@ package com.jme3.effect.shapes; +import com.jme3.export.InputCapsule; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -141,11 +142,18 @@ public class EmitterMeshVertexShape implements EmitterShape { public void write(JmeExporter ex) throws IOException { OutputCapsule oc = ex.getCapsule(this); oc.writeSavableArrayList((ArrayList>) vertices, "vertices", null); + oc.writeSavableArrayList((ArrayList>) normals, "normals", null); } @Override @SuppressWarnings("unchecked") public void read(JmeImporter im) throws IOException { - this.vertices = im.getCapsule(this).readSavableArrayList("vertices", null); + InputCapsule ic = im.getCapsule(this); + this.vertices = ic.readSavableArrayList("vertices", null); + + List> tmpNormals = ic.readSavableArrayList("normals", null); + if (tmpNormals != null){ + this.normals = tmpNormals; + } } } diff --git a/engine/src/core/com/jme3/export/InputCapsule.java b/engine/src/core/com/jme3/export/InputCapsule.java index 16ae5c3f3..c7c7e167b 100644 --- a/engine/src/core/com/jme3/export/InputCapsule.java +++ b/engine/src/core/com/jme3/export/InputCapsule.java @@ -47,6 +47,8 @@ import java.util.Map; */ public interface InputCapsule { + public int getSavableVersion(Class clazz); + // byte primitive public byte readByte(String name, byte defVal) throws IOException; diff --git a/engine/src/core/com/jme3/export/JmeImporter.java b/engine/src/core/com/jme3/export/JmeImporter.java index 7c756f89e..4986c859a 100644 --- a/engine/src/core/com/jme3/export/JmeImporter.java +++ b/engine/src/core/com/jme3/export/JmeImporter.java @@ -38,4 +38,12 @@ import com.jme3.asset.AssetManager; public interface JmeImporter extends AssetLoader { public InputCapsule getCapsule(Savable id); public AssetManager getAssetManager(); + + /** + * Returns the version number written in the header of the J3O/XML + * file. + * + * @return Global version number for the file + */ + public int getFormatVersion(); } diff --git a/engine/src/core/com/jme3/export/Savable.java b/engine/src/core/com/jme3/export/Savable.java index 9171ffcd3..0e90e3009 100644 --- a/engine/src/core/com/jme3/export/Savable.java +++ b/engine/src/core/com/jme3/export/Savable.java @@ -37,7 +37,8 @@ import java.io.IOException; /** * Savable is an interface for objects that can be serialized * using jME's serialization system. - * @author Dany + * + * @author Kirill Vainer */ public interface Savable { void write(JmeExporter ex) throws IOException; diff --git a/engine/src/core/com/jme3/export/SavableClassFinder.java b/engine/src/core/com/jme3/export/SavableClassUtil.java similarity index 77% rename from engine/src/core/com/jme3/export/SavableClassFinder.java rename to engine/src/core/com/jme3/export/SavableClassUtil.java index c25d8e39b..33f8d1d1e 100644 --- a/engine/src/core/com/jme3/export/SavableClassFinder.java +++ b/engine/src/core/com/jme3/export/SavableClassUtil.java @@ -41,26 +41,27 @@ import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; -import com.jme3.export.InputCapsule; -import com.jme3.export.Savable; import com.jme3.material.MatParamTexture; +import com.jme3.scene.Spatial; +import java.lang.reflect.Field; +import java.lang.reflect.Type; import java.util.HashMap; -import java.util.Iterator; import java.util.List; /** - * SavableClassFinder is used to find classes referenced - * by savables. + * SavableClassUtil contains various utilities to handle + * Savable classes. The methods are general enough to not be specific to any + * particular implementation. * Currently it will remap any classes from old paths to new paths * so that old J3O models can still be loaded. * * @author mpowell * @author Kirill Vainer */ -public class SavableClassFinder { +public class SavableClassUtil { private final static HashMap classRemappings = new HashMap(); - + private static void addRemapping(String oldClass, Class newClass){ classRemappings.put(oldClass, newClass.getName()); } @@ -83,7 +84,32 @@ public class SavableClassFinder { return result; } } + + public static boolean isImplementingSavable(Class clazz){ + Class[] interfaces = clazz.getInterfaces(); + for (Class interfaceClass : interfaces){ + if (interfaceClass == Savable.class){ + return true; + } + } + return false; + } + public static int getSavableVersion(Class clazz) throws IOException{ + try { + Field field = clazz.getField("SAVABLE_VERSION"); + return field.getInt(null); + } catch (IllegalAccessException ex) { + IOException ioEx = new IOException(); + ioEx.initCause(ex); + throw ioEx; + } catch (IllegalArgumentException ex) { + throw ex; // can happen if SAVABLE_VERSION is not static + } catch (NoSuchFieldException ex) { + return 0; // not using versions + } + } + /** * fromName creates a new Savable from the provided class name. First registered modules * are checked to handle special cases, if the modules do not handle the class name, the @@ -96,29 +122,29 @@ public class SavableClassFinder { * @throws ClassNotFoundException thrown if the class name is not in the classpath. * @throws IOException when loading ctor parameters fails */ - public static Savable fromName(String className, InputCapsule inputCapsule) throws InstantiationException, + public static Savable fromName(String className) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException { className = remapClass(className); try { return (Savable) Class.forName(className).newInstance(); } catch (InstantiationException e) { - Logger.getLogger(SavableClassFinder.class.getName()).log( + Logger.getLogger(SavableClassUtil.class.getName()).log( Level.SEVERE, "Could not access constructor of class ''{0}" + "''! \n" + "Some types need to have the BinaryImporter set up in a special way. Please doublecheck the setup.", className); throw e; } catch (IllegalAccessException e) { - Logger.getLogger(SavableClassFinder.class.getName()).log( + Logger.getLogger(SavableClassUtil.class.getName()).log( Level.SEVERE, "{0} \n" + "Some types need to have the BinaryImporter set up in a special way. Please doublecheck the setup.", e.getMessage()); throw e; } } - public static Savable fromName(String className, InputCapsule inputCapsule, List loaders) throws InstantiationException, + public static Savable fromName(String className, List loaders) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException { if (loaders == null) { - return fromName(className, inputCapsule); + return fromName(className); } String newClassName = remapClass(className); @@ -131,6 +157,6 @@ public class SavableClassFinder { } - return fromName(className, inputCapsule); + return fromName(className); } } diff --git a/engine/src/core/com/jme3/scene/UserData.java b/engine/src/core/com/jme3/scene/UserData.java index 7ac8a4478..8a18f77cc 100644 --- a/engine/src/core/com/jme3/scene/UserData.java +++ b/engine/src/core/com/jme3/scene/UserData.java @@ -53,6 +53,12 @@ public final class UserData implements Savable { */ public static final String JME_PHYSICSIGNORE = "JmePhysicsIgnore"; + /** + * For geometries using shared mesh, this will specify the shared + * mesh reference. + */ + public static final String JME_SHAREDMESH = "JmeSharedMesh"; + protected byte type; protected Object value; diff --git a/engine/src/core/com/jme3/scene/VertexBuffer.java b/engine/src/core/com/jme3/scene/VertexBuffer.java index e507d7f3a..cc04fb055 100644 --- a/engine/src/core/com/jme3/scene/VertexBuffer.java +++ b/engine/src/core/com/jme3/scene/VertexBuffer.java @@ -63,7 +63,7 @@ import java.nio.ShortBuffer; * */ public class VertexBuffer extends GLObject implements Savable, Cloneable { - + /** * Type of buffer. Specifies the actual attribute it defines. */ diff --git a/engine/src/core/com/jme3/terrain/AbstractGeomap.java b/engine/src/core/com/jme3/terrain/AbstractGeomap.java index 7caa59543..0e140995b 100644 --- a/engine/src/core/com/jme3/terrain/AbstractGeomap.java +++ b/engine/src/core/com/jme3/terrain/AbstractGeomap.java @@ -44,7 +44,7 @@ import java.nio.IntBuffer; /** * implements all writeXXXXArray methods to reduce boilerplate code - * Geomap implementations are encourged to extend this class + * Geomap implementations are encouraged to extend this class */ public abstract class AbstractGeomap implements Geomap { @@ -60,7 +60,7 @@ public abstract class AbstractGeomap implements Geomap { /* * (non-Javadoc) - * Subclasses are encourged to provide a better implementation + * Subclasses are encouraged to provide a better implementation * which directly accesses the data rather than using getHeight */ public FloatBuffer writeVertexArray(FloatBuffer store, Vector3f scale, boolean center){ diff --git a/engine/src/core/com/jme3/util/ListMap.java b/engine/src/core/com/jme3/util/ListMap.java index eefec07bc..c5b6de41b 100644 --- a/engine/src/core/com/jme3/util/ListMap.java +++ b/engine/src/core/com/jme3/util/ListMap.java @@ -33,9 +33,8 @@ package com.jme3.util; import java.io.Serializable; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; +import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -92,35 +91,48 @@ public final class ListMap implements Map, Cloneable, Serializable { } - private final ArrayList> entries; + private final HashMap backingMap; + private ListMapEntry[] entries; + +// private final ArrayList> entries; public ListMap(){ - entries = new ArrayList>(); + entries = new ListMapEntry[4]; + backingMap = new HashMap(4); +// entries = new ArrayList>(); } public ListMap(int initialCapacity){ - entries = new ArrayList>(initialCapacity); + entries = new ListMapEntry[initialCapacity]; + backingMap = new HashMap(initialCapacity); +// entries = new ArrayList>(initialCapacity); } public ListMap(Map map){ - entries = new ArrayList>(map.size()); + entries = new ListMapEntry[map.size()]; + backingMap = new HashMap(map.size()); +// entries = new ArrayList>(map.size()); putAll(map); } public int size() { - return entries.size(); +// return entries.size(); + return backingMap.size(); } public Entry getEntry(int index){ - return entries.get(index); +// return entries.get(index); + return entries[index]; } public V getValue(int index){ - return entries.get(index).value; +// return entries.get(index).value; + return entries[index].value; } public K getKey(int index){ - return entries.get(index).key; +// return entries.get(index).key; + return entries[index].key; } public boolean isEmpty() { @@ -130,93 +142,142 @@ public final class ListMap implements Map, Cloneable, Serializable { private static boolean keyEq(Object keyA, Object keyB){ return keyA.hashCode() == keyB.hashCode() ? (keyA == keyB) || keyA.equals(keyB) : false; } - - private static boolean valEq(Object a, Object b){ - return a == null ? (b == null) : a.equals(b); - } +// +// private static boolean valEq(Object a, Object b){ +// return a == null ? (b == null) : a.equals(b); +// } public boolean containsKey(Object key) { - if (key == null) - throw new IllegalArgumentException(); - - for (int i = 0; i < entries.size(); i++){ - ListMapEntry entry = entries.get(i); - if (keyEq(entry.key, key)) - return true; - } - return false; + return backingMap.containsKey( (K) key); +// if (key == null) +// throw new IllegalArgumentException(); +// +// for (int i = 0; i < entries.size(); i++){ +// ListMapEntry entry = entries.get(i); +// if (keyEq(entry.key, key)) +// return true; +// } +// return false; } public boolean containsValue(Object value) { - for (int i = 0; i < entries.size(); i++){ - if (valEq(entries.get(i).value, value)) - return true; - } - return false; + return backingMap.containsValue( (V) value); +// for (int i = 0; i < entries.size(); i++){ +// if (valEq(entries.get(i).value, value)) +// return true; +// } +// return false; } public V get(Object key) { - if (key == null) - throw new IllegalArgumentException(); - - for (int i = 0; i < entries.size(); i++){ - ListMapEntry entry = entries.get(i); - if (keyEq(entry.key, key)) - return entry.value; - } - return null; + return backingMap.get( (K) key); +// if (key == null) +// throw new IllegalArgumentException(); +// +// for (int i = 0; i < entries.size(); i++){ +// ListMapEntry entry = entries.get(i); +// if (keyEq(entry.key, key)) +// return entry.value; +// } +// return null; } public V put(K key, V value) { - if (key == null) - throw new IllegalArgumentException(); - - // check if entry exists, if yes, overwrite it with new value - for (int i = 0; i < entries.size(); i++){ - ListMapEntry entry = entries.get(i); - if (keyEq(entry.key, key)){ - V prevValue = entry.value; - entry.value = value; - return prevValue; + if (backingMap.containsKey(key)){ + // set the value on the entry + int size = size(); + for (int i = 0; i < size; i++){ + ListMapEntry entry = entries[i]; + if (keyEq(entry.key, key)){ + entry.value = value; + break; + } + } + }else{ + int size = size(); + // expand list as necessary + if (size == entries.length){ + ListMapEntry[] tmpEntries = entries; + entries = new ListMapEntry[size * 2]; + System.arraycopy(tmpEntries, 0, entries, 0, size); } + entries[size] = new ListMapEntry(key, value); } - - // add a new entry - entries.add(new ListMapEntry(key, value)); - return null; + return backingMap.put(key, value); +// if (key == null) +// throw new IllegalArgumentException(); +// +// // check if entry exists, if yes, overwrite it with new value +// for (int i = 0; i < entries.size(); i++){ +// ListMapEntry entry = entries.get(i); +// if (keyEq(entry.key, key)){ +// V prevValue = entry.value; +// entry.value = value; +// return prevValue; +// } +// } +// +// // add a new entry +// entries.add(new ListMapEntry(key, value)); +// return null; } public V remove(Object key) { - if (key == null) - throw new IllegalArgumentException(); - - for (int i = 0; i < entries.size(); i++){ - ListMapEntry entry = entries.get(i); - if (keyEq(entry.key, key)){ - return entries.remove(i).value; + V element = backingMap.remove( (K) key); + if (element != null){ + // find removed element + int size = size() + 1; // includes removed element + int removedIndex = -1; + for (int i = 0; i < size; i++){ + ListMapEntry entry = entries[i]; + if (keyEq(entry.key, key)){ + removedIndex = i; + break; + } + } + assert removedIndex >= 0; + + size --; + for (int i = removedIndex; i < size; i++){ + entries[i] = entries[i+1]; } } - return null; + return element; +// if (key == null) +// throw new IllegalArgumentException(); +// +// for (int i = 0; i < entries.size(); i++){ +// ListMapEntry entry = entries.get(i); +// if (keyEq(entry.key, key)){ +// return entries.remove(i).value; +// } +// } +// return null; } public void putAll(Map map) { - if (map instanceof ListMap){ - ListMap listMap = (ListMap) map; - ArrayList> otherEntries = listMap.entries; - for (int i = 0; i < otherEntries.size(); i++){ - ListMapEntry entry = otherEntries.get(i); - put(entry.key, entry.value); - } - }else{ - for (Map.Entry entry : map.entrySet()){ - put(entry.getKey(), entry.getValue()); - } + for (Entry entry : map.entrySet()){ + put(entry.getKey(), entry.getValue()); } + +// if (map instanceof ListMap){ +// ListMap listMap = (ListMap) map; +// ArrayList> otherEntries = listMap.entries; +// for (int i = 0; i < otherEntries.size(); i++){ +// ListMapEntry entry = otherEntries.get(i); +// put(entry.key, entry.value); +// } +// }else{ +// for (Map.Entry entry : map.entrySet()){ +// put(entry.getKey(), entry.getValue()); +// } +// } } public void clear() { - entries.clear(); + backingMap.clear(); +// entries.clear(); } @Override @@ -227,27 +288,30 @@ public final class ListMap implements Map, Cloneable, Serializable { } public Set keySet() { - HashSet keys = new HashSet(); - for (int i = 0; i < entries.size(); i++){ - ListMapEntry entry = entries.get(i); - keys.add(entry.key); - } - return keys; + return backingMap.keySet(); +// HashSet keys = new HashSet(); +// for (int i = 0; i < entries.size(); i++){ +// ListMapEntry entry = entries.get(i); +// keys.add(entry.key); +// } +// return keys; } public Collection values() { - ArrayList values = new ArrayList(); - for (int i = 0; i < entries.size(); i++){ - ListMapEntry entry = entries.get(i); - values.add(entry.value); - } - return values; + return backingMap.values(); +// ArrayList values = new ArrayList(); +// for (int i = 0; i < entries.size(); i++){ +// ListMapEntry entry = entries.get(i); +// values.add(entry.value); +// } +// return values; } public Set> entrySet() { - HashSet> entryset = new HashSet>(); - entryset.addAll(entries); - return entryset; + return backingMap.entrySet(); +// HashSet> entryset = new HashSet>(); +// entryset.addAll(entries); +// return entryset; } } diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java index 1ca35b98d..9c4cd97fe 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java @@ -47,6 +47,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; import com.jme3.scene.Node; import com.jme3.scene.Spatial.CullHint; +import com.jme3.scene.UserData; import com.jme3.scene.VertexBuffer; import com.jme3.scene.VertexBuffer.Format; import com.jme3.scene.VertexBuffer.Type; @@ -101,28 +102,30 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { private String folderName; private AssetManager assetManager; private MaterialList materialList; + + // Data per submesh/sharedgeom private ShortBuffer sb; private IntBuffer ib; private FloatBuffer fb; private VertexBuffer vb; private Mesh mesh; private Geometry geom; - private Mesh sharedmesh; - private Geometry sharedgeom; - private int geomIdx = 0; - private int texCoordIdx = 0; - private static volatile int nodeIdx = 0; - private String ignoreUntilEnd = null; - private boolean bigindices = false; + private ByteBuffer indicesData; + private FloatBuffer weightsFloatData; private int vertCount; - private int triCount; + private boolean usesSharedVerts; + private boolean usesBigIndices; + + // Global data + private Mesh sharedMesh; + private int meshIndex = 0; + private int texCoordIndex = 0; + private String ignoreUntilEnd = null; + private List geoms = new ArrayList(); - private List usesSharedGeom = new ArrayList(); private IntMap> lodLevels = new IntMap>(); private AnimData animData; - private ByteBuffer indicesData; - private FloatBuffer weightsFloatData; - + public MeshLoader() { super(); } @@ -130,7 +133,6 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { @Override public void startDocument() { geoms.clear(); - usesSharedGeom.clear(); lodLevels.clear(); sb = null; @@ -139,14 +141,12 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { vb = null; mesh = null; geom = null; - sharedgeom = null; - sharedmesh = null; + sharedMesh = null; + usesSharedVerts = false; vertCount = 0; - triCount = 0; - geomIdx = 0; - texCoordIdx = 0; - nodeIdx = 0; + meshIndex = 0; + texCoordIndex = 0; ignoreUntilEnd = null; animData = null; @@ -171,28 +171,23 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { sb.put((short) i1).put((short) i2).put((short) i3); } } + + private boolean isUsingSharedVerts(Geometry geom){ + return geom.getUserData(UserData.JME_SHAREDMESH) != null; + } private void startFaces(String count) throws SAXException { int numFaces = parseInt(count); int numIndices; if (mesh.getMode() == Mesh.Mode.Triangles) { - //mesh.setTriangleCount(numFaces); numIndices = numFaces * 3; } else { throw new SAXException("Triangle strip or fan not supported!"); } - int numVerts; - if (usesSharedGeom.size() > 0 && usesSharedGeom.get(geoms.size() - 1)) { -// sharedgeom.getMesh().updateCounts(); - numVerts = sharedmesh.getVertexCount(); - } else { -// mesh.updateCounts(); - numVerts = mesh.getVertexCount(); - } vb = new VertexBuffer(VertexBuffer.Type.Index); - if (!bigindices) { + if (!usesBigIndices) { sb = BufferUtils.createShortBuffer(numIndices); ib = null; vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb); @@ -226,16 +221,11 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { if (mat.isTransparent()) { geom.setQueueBucket(Bucket.Transparent); } -// else -// geom.setShadowMode(ShadowMode.CastAndReceive); - -// if (mat.isReceivesShadows()) - - + geom.setMaterial(mat); } - private void startMesh(String matName, String usesharedvertices, String use32bitIndices, String opType) throws SAXException { + private void startSubMesh(String matName, String usesharedvertices, String use32bitIndices, String opType) throws SAXException { mesh = new Mesh(); if (opType == null || opType.equals("triangle_list")) { mesh.setMode(Mesh.Mode.Triangles); @@ -245,24 +235,25 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { mesh.setMode(Mesh.Mode.TriangleFan); } - bigindices = parseBool(use32bitIndices, false); - boolean sharedverts = parseBool(usesharedvertices, false); - if (sharedverts) { - usesSharedGeom.add(true); + usesBigIndices = parseBool(use32bitIndices, false); + usesSharedVerts = parseBool(usesharedvertices, false); + if (usesSharedVerts) { // import vertexbuffers from shared geom - IntMap sharedBufs = sharedmesh.getBuffers(); + IntMap sharedBufs = sharedMesh.getBuffers(); for (Entry entry : sharedBufs) { mesh.setBuffer(entry.getValue()); } - // this mesh is shared! - } else { - usesSharedGeom.add(false); } if (meshName == null) { - geom = new Geometry("OgreSubmesh-" + (++geomIdx), mesh); + geom = new Geometry("OgreSubmesh-" + (++meshIndex), mesh); } else { - geom = new Geometry(meshName + "-geom-" + (++geomIdx), mesh); + geom = new Geometry(meshName + "-geom-" + (++meshIndex), mesh); + } + + if (usesSharedVerts){ + // this mesh is shared! + geom.setUserData(UserData.JME_SHAREDMESH, sharedMesh); } applyMaterial(geom, matName); @@ -270,27 +261,16 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } private void startSharedGeom(String vertexcount) throws SAXException { - sharedmesh = new Mesh(); + sharedMesh = new Mesh(); vertCount = parseInt(vertexcount); -// sharedmesh.setVertexCount(vertCount); + usesSharedVerts = false; - if (meshName == null) { - sharedgeom = new Geometry("Ogre-SharedGeom", sharedmesh); - } else { - sharedgeom = new Geometry(meshName + "-sharedgeom", sharedmesh); - } - - sharedgeom.setCullHint(CullHint.Always); - geoms.add(sharedgeom); - usesSharedGeom.add(false); // shared geometry doesnt use shared geometry (?) - - geom = sharedgeom; - mesh = sharedmesh; + geom = null; + mesh = sharedMesh; } private void startGeometry(String vertexcount) throws SAXException { vertCount = parseInt(vertexcount); -// mesh.setVertexCount(vertCount); } /** @@ -298,7 +278,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { * for all vertices in the buffer. */ private void endBoneAssigns() { - if (mesh != sharedmesh && usesSharedGeom.get(geoms.size() - 1)) { + if (mesh != sharedMesh && isUsingSharedVerts(geom)) { return; } @@ -341,7 +321,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } private void startBoneAssigns() { - if (mesh != sharedmesh && usesSharedGeom.get(geoms.size() - 1)) { + if (mesh != sharedMesh && usesSharedVerts) { // will use bone assignments from shared mesh (?) return; } @@ -424,7 +404,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } private void startVertex() { - texCoordIdx = 0; + texCoordIndex = 0; } private void pushAttrib(Type type, Attributes attribs) throws SAXException { @@ -450,10 +430,10 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } private void pushTexCoord(Attributes attribs) throws SAXException { - if (texCoordIdx >= 8) { + if (texCoordIndex >= 8) { return; // More than 8 not supported by ogre. } - Type type = TEXCOORD_TYPES[texCoordIdx]; + Type type = TEXCOORD_TYPES[texCoordIndex]; VertexBuffer tcvb = mesh.getBuffer(type); FloatBuffer buf = (FloatBuffer) tcvb.getData(); @@ -469,7 +449,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } } - texCoordIdx++; + texCoordIndex++; } private void pushColor(Attributes attribs) throws SAXException { @@ -529,7 +509,6 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } private void startLodGenerated(String depthsqr) { -// dist = Float.parseFloat(depthsqr); } private void pushBoneAssign(String vertIndex, String boneIndex, String weight) throws SAXException { @@ -560,10 +539,6 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { private void startSkeleton(String name) { animData = (AnimData) assetManager.loadAsset(folderName + name + ".xml"); - //TODO:workaround for meshxml / mesh.xml - if (animData == null) { - animData = (AnimData) assetManager.loadAsset(folderName + name + "xml"); - } } private void startSubmeshName(String indexStr, String nameStr) { @@ -620,7 +595,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } else if (qName.equals("boneassignments")) { startBoneAssigns(); } else if (qName.equals("submesh")) { - startMesh(attribs.getValue("material"), + startSubMesh(attribs.getValue("material"), attribs.getValue("usesharedvertices"), attribs.getValue("use32bitindexes"), attribs.getValue("operationtype")); @@ -653,18 +628,18 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { if (ignoreUntilEnd.equals(qName)) { ignoreUntilEnd = null; } - return; } if (qName.equals("submesh")) { - bigindices = false; + usesBigIndices = false; geom = null; mesh = null; } else if (qName.equals("submeshes")) { - // IMPORTANT: restore sharedgeoemtry, for use with shared boneweights - geom = sharedgeom; - mesh = sharedmesh; + // IMPORTANT: restore sharedmesh, for use with shared boneweights + geom = null; + mesh = sharedMesh; + usesSharedVerts = false; } else if (qName.equals("faces")) { if (ib != null) { ib.flip(); @@ -711,80 +686,82 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } private Node compileModel() { - String nodeName; - if (meshName == null) { - nodeName = "OgreMesh" + (++nodeIdx); - } else { - nodeName = meshName + "-ogremesh"; + Node model = new Node(meshName + "-ogremesh"); + + for (int i = 0; i < geoms.size(); i++) { + Geometry g = geoms.get(i); + Mesh m = g.getMesh(); + if (sharedMesh != null && isUsingSharedVerts(geom)) { + m.setBound(sharedMesh.getBound().clone()); + } + model.attachChild(geoms.get(i)); } - - Node model = new Node(nodeName); + + // Do not attach shared geometry to the node! + if (animData != null) { - ArrayList newMeshes = new ArrayList(geoms.size()); - - // generate bind pose for mesh and add to skin-list + // This model uses animation + + // generate bind pose for mesh // ONLY if not using shared geometry // This includes the shared geoemtry itself actually + if (sharedMesh != null){ + sharedMesh.generateBindPose(!HARDWARE_SKINNING); + } + for (int i = 0; i < geoms.size(); i++) { Geometry g = geoms.get(i); Mesh m = geoms.get(i).getMesh(); - boolean useShared = usesSharedGeom.get(i); - // create bind pose + boolean useShared = isUsingSharedVerts(g); + + if (!useShared) { + // create bind pose m.generateBindPose(!HARDWARE_SKINNING); - newMeshes.add(m); - } else { - VertexBuffer bindPos = sharedmesh.getBuffer(Type.BindPosePosition); - VertexBuffer bindNorm = sharedmesh.getBuffer(Type.BindPoseNormal); - VertexBuffer boneIndex = sharedmesh.getBuffer(Type.BoneIndex); - VertexBuffer boneWeight = sharedmesh.getBuffer(Type.BoneWeight); - - if (bindPos != null) { - m.setBuffer(bindPos); - } - - if (bindNorm != null) { - m.setBuffer(bindNorm); - } - - if (boneIndex != null) { - m.setBuffer(boneIndex); - } - - if (boneWeight != null) { - m.setBuffer(boneWeight); - } +// } else { + // Inherit animation data from shared mesh +// VertexBuffer bindPos = sharedMesh.getBuffer(Type.BindPosePosition); +// VertexBuffer bindNorm = sharedMesh.getBuffer(Type.BindPoseNormal); +// VertexBuffer boneIndex = sharedMesh.getBuffer(Type.BoneIndex); +// VertexBuffer boneWeight = sharedMesh.getBuffer(Type.BoneWeight); +// +// if (bindPos != null) { +// m.setBuffer(bindPos); +// } +// +// if (bindNorm != null) { +// m.setBuffer(bindNorm); +// } +// +// if (boneIndex != null) { +// m.setBuffer(boneIndex); +// } +// +// if (boneWeight != null) { +// m.setBuffer(boneWeight); +// } } } - Mesh[] meshes = new Mesh[newMeshes.size()]; - for (int i = 0; i < meshes.length; i++) { - meshes[i] = newMeshes.get(i); - } - + + // Put the animations in the AnimControl HashMap anims = new HashMap(); ArrayList animList = animData.anims; for (int i = 0; i < animList.size(); i++) { BoneAnimation anim = animList.get(i); anims.put(anim.getName(), anim); } - - //AnimControl ctrl = new AnimControl(model, meshes, animData.skeleton); + AnimControl ctrl = new AnimControl(animData.skeleton); ctrl.setAnimations(anims); model.addControl(ctrl); - SkeletonControl skeletonControl = new SkeletonControl(meshes, animData.skeleton); + + // Put the skeleton in the skeleton control + SkeletonControl skeletonControl = new SkeletonControl(animData.skeleton); + + // This will acquire the targets from the node model.addControl(skeletonControl); } - for (int i = 0; i < geoms.size(); i++) { - Geometry g = geoms.get(i); - Mesh m = g.getMesh(); - if (sharedmesh != null && usesSharedGeom.get(i)) { - m.setBound(sharedmesh.getBound().clone()); - } - model.attachChild(geoms.get(i)); - } - return model; } diff --git a/engine/src/test/jme3test/bullet/TestHoveringTank.java b/engine/src/test/jme3test/bullet/TestHoveringTank.java index a9a0eaa8e..56b4c4adc 100644 --- a/engine/src/test/jme3test/bullet/TestHoveringTank.java +++ b/engine/src/test/jme3test/bullet/TestHoveringTank.java @@ -270,7 +270,7 @@ public class TestHoveringTank extends SimpleApplication implements AnalogListene rock.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap_2", rock); matRock.setFloat("DiffuseMap_2_scale", 128); - Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.png"); + Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); normalMap0.setWrap(WrapMode.Repeat); Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); normalMap1.setWrap(WrapMode.Repeat); diff --git a/engine/src/xml/com/jme3/export/xml/DOMInputCapsule.java b/engine/src/xml/com/jme3/export/xml/DOMInputCapsule.java index 2238668b2..97e9fdf9f 100644 --- a/engine/src/xml/com/jme3/export/xml/DOMInputCapsule.java +++ b/engine/src/xml/com/jme3/export/xml/DOMInputCapsule.java @@ -34,7 +34,7 @@ package com.jme3.export.xml; import com.jme3.export.InputCapsule; import com.jme3.export.Savable; -import com.jme3.export.SavableClassFinder; +import com.jme3.export.SavableClassUtil; import com.jme3.util.BufferUtils; import com.jme3.util.IntMap; import java.io.IOException; @@ -78,6 +78,10 @@ public class DOMInputCapsule implements InputCapsule { currentElem = doc.getDocumentElement(); } + public int getSavableVersion(Class clazz) { + return 0; // TODO: figure this out ... + } + private static String decodeString(String s) { if (s == null) { return null; @@ -973,7 +977,7 @@ public class DOMInputCapsule implements InputCapsule { } else if (currentElem.hasAttribute("class")) { className = currentElem.getAttribute("class"); } - tmp = SavableClassFinder.fromName(className, null); + tmp = SavableClassUtil.fromName(className, null); String refID = currentElem.getAttribute("reference_ID"); if (refID.length() < 1) refID = currentElem.getAttribute("id"); if (refID.length() > 0) referencedSavables.put(refID, tmp); diff --git a/engine/src/xml/com/jme3/export/xml/XMLImporter.java b/engine/src/xml/com/jme3/export/xml/XMLImporter.java index d4aac7ee1..50ae5c4ed 100644 --- a/engine/src/xml/com/jme3/export/xml/XMLImporter.java +++ b/engine/src/xml/com/jme3/export/xml/XMLImporter.java @@ -52,11 +52,15 @@ public class XMLImporter implements JmeImporter { private AssetManager assetManager; private DOMInputCapsule domIn; - // A single-load-state base URI for texture-loading. public XMLImporter() { } +// TODO: ....... + public int getFormatVersion() { + return 0; + } + public AssetManager getAssetManager(){ return assetManager; } @@ -73,10 +77,7 @@ public class XMLImporter implements JmeImporter { return obj; } - synchronized public Savable load(InputStream f) throws IOException { - /* Leave this method synchronized. Calling this method from more than - * one thread at a time for the same XMLImporter instance will clobber - * the XML Document instantiated here. */ + public Savable load(InputStream f) throws IOException { try { domIn = new DOMInputCapsule(DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(f), this); return domIn.readSavable(null, null);