* Fixed Blender loader crash when "Image" texture mode is specified but actual image is not selected

* Replaced oracle proprietary exception with UnsupportedOperationException
 * Shared Geometry Patch - Still need detection mechanism for old versions!
 * Binary/J3O format will now write signature and version
 * Binary/J3O format now has version numbers for exported class hierarchies
 * Fix crash in TestHoveringTank
 * ListMap now uses backing array and a map - increased lookup performance for uniforms/matparams and faster iteration too. Only insertion became slower

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7726 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
sha..rd 14 years ago
parent 090e57b224
commit 348b1d638a
  1. 8
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/TextureHelper.java
  2. 5
      engine/src/blender/com/jme3/scene/plugins/blender/structures/Properties.java
  3. 2
      engine/src/core-plugins/com/jme3/export/binary/BinaryClassObject.java
  4. 84
      engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java
  5. 66
      engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java
  6. 25
      engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java
  7. 62
      engine/src/core-plugins/com/jme3/export/binary/BinaryLoaderModule.java
  8. 24
      engine/src/core/com/jme3/animation/AnimControl.java
  9. 38
      engine/src/core/com/jme3/animation/BoneTrack.java
  10. 104
      engine/src/core/com/jme3/animation/SkeletonControl.java
  11. 2
      engine/src/core/com/jme3/cinematic/Cinematic.java
  12. 2
      engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java
  13. 3
      engine/src/core/com/jme3/cinematic/events/CinematicEvent.java
  14. 32
      engine/src/core/com/jme3/effect/ParticleEmitter.java
  15. 2
      engine/src/core/com/jme3/effect/influencers/DefaultParticleInfluencer.java
  16. 10
      engine/src/core/com/jme3/effect/shapes/EmitterMeshVertexShape.java
  17. 2
      engine/src/core/com/jme3/export/InputCapsule.java
  18. 8
      engine/src/core/com/jme3/export/JmeImporter.java
  19. 3
      engine/src/core/com/jme3/export/Savable.java
  20. 50
      engine/src/core/com/jme3/export/SavableClassUtil.java
  21. 6
      engine/src/core/com/jme3/scene/UserData.java
  22. 4
      engine/src/core/com/jme3/terrain/AbstractGeomap.java
  23. 234
      engine/src/core/com/jme3/util/ListMap.java
  24. 223
      engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java
  25. 2
      engine/src/test/jme3test/bullet/TestHoveringTank.java
  26. 8
      engine/src/xml/com/jme3/export/xml/DOMInputCapsule.java
  27. 11
      engine/src/xml/com/jme3/export/xml/XMLImporter.java

@ -177,8 +177,10 @@ public class TextureHelper extends AbstractBlenderHelper {
break; break;
case TEX_IMAGE:// (it is first because probably this will be most commonly used) case TEX_IMAGE:// (it is first because probably this will be most commonly used)
Pointer pImage = (Pointer) tex.getFieldValue("ima"); Pointer pImage = (Pointer) tex.getFieldValue("ima");
Structure image = pImage.fetchData(dataRepository.getInputStream()).get(0); if (pImage.isNotNull()){
result = this.getTextureFromImage(image, dataRepository); Structure image = pImage.fetchData(dataRepository.getInputStream()).get(0);
result = this.getTextureFromImage(image, dataRepository);
}
break; break;
case TEX_CLOUDS: case TEX_CLOUDS:
result = this.clouds(tex, width, height, dataRepository); result = this.clouds(tex, width, height, dataRepository);
@ -212,7 +214,7 @@ public class TextureHelper extends AbstractBlenderHelper {
break; break;
case TEX_PLUGIN: case TEX_PLUGIN:
case TEX_ENVMAP:// TODO: implement envmap texture 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; break;
default: default:
throw new BlenderFileException("Unknown texture type: " + type + " for texture: " + tex.getName()); throw new BlenderFileException("Unknown texture type: " + type + " for texture: " + tex.getName());

@ -6,9 +6,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Logger; import java.util.logging.Logger;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import com.jme3.export.InputCapsule; import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
@ -158,7 +155,7 @@ public class Properties implements Cloneable, Savable {
break; break;
} }
case IDP_NUMTYPES: case IDP_NUMTYPES:
throw new NotImplementedException(); throw new UnsupportedOperationException();
// case IDP_ID://not yet implemented in blender // case IDP_ID://not yet implemented in blender
// return null; // return null;
default: default:

@ -42,5 +42,5 @@ class BinaryClassObject {
byte[] alias; byte[] alias;
String className; String className;
int[] classHierarchyVersions;
} }

@ -34,6 +34,8 @@ package com.jme3.export.binary;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
import com.jme3.export.Savable; import com.jme3.export.Savable;
import com.jme3.export.FormatVersion;
import com.jme3.export.SavableClassUtil;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
@ -43,6 +45,7 @@ import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**
@ -179,32 +182,44 @@ public class BinaryExporter implements JmeExporter {
locationTable.clear(); locationTable.clear();
contentKeys.clear(); contentKeys.clear();
// write signature and version
os.write(ByteUtils.convertToBytes(FormatVersion.SIGNATURE));
os.write(ByteUtils.convertToBytes(FormatVersion.VERSION));
int id = processBinarySavable(object); int id = processBinarySavable(object);
// write out tag table // write out tag table
int ttbytes = 0; int classTableSize = 0;
int classNum = classes.keySet().size(); int classNum = classes.keySet().size();
int aliasWidth = ((int) FastMath.log(classNum, 256) + 1); // make all int aliasSize = ((int) FastMath.log(classNum, 256) + 1); // make all
// aliases a // aliases a
// fixed width // fixed width
os.write(ByteUtils.convertToBytes(classNum)); os.write(ByteUtils.convertToBytes(classNum));
for (String key : classes.keySet()) { for (String key : classes.keySet()) {
BinaryClassObject bco = classes.get(key); BinaryClassObject bco = classes.get(key);
// write alias // write alias
byte[] aliasBytes = fixClassAlias(bco.alias, byte[] aliasBytes = fixClassAlias(bco.alias,
aliasWidth); aliasSize);
os.write(aliasBytes); 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 // write classname size & classname
byte[] classBytes = key.getBytes(); byte[] classBytes = key.getBytes();
os.write(ByteUtils.convertToBytes(classBytes.length)); os.write(ByteUtils.convertToBytes(classBytes.length));
os.write(classBytes); 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())); os.write(ByteUtils.convertToBytes(bco.nameFields.size()));
for (String fieldName : bco.nameFields.keySet()) { for (String fieldName : bco.nameFields.keySet()) {
BinaryClassField bcf = bco.nameFields.get(fieldName); BinaryClassField bcf = bco.nameFields.get(fieldName);
os.write(bcf.alias); os.write(bcf.alias);
@ -214,7 +229,7 @@ public class BinaryExporter implements JmeExporter {
byte[] fNameBytes = fieldName.getBytes(); byte[] fNameBytes = fieldName.getBytes();
os.write(ByteUtils.convertToBytes(fNameBytes.length)); os.write(ByteUtils.convertToBytes(fNameBytes.length));
os.write(fNameBytes); 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); alreadySaved.put(savableName + getChunk(pair), bucket);
} }
bucket.add(pair); bucket.add(pair);
byte[] aliasBytes = fixClassAlias(classes.get(savableName).alias, aliasWidth); byte[] aliasBytes = fixClassAlias(classes.get(savableName).alias, aliasSize);
out.write(aliasBytes); out.write(aliasBytes);
location += aliasWidth; location += aliasSize;
BinaryOutputCapsule cap = contentTable.get(savable).getContent(); BinaryOutputCapsule cap = contentTable.get(savable).getContent();
out.write(ByteUtils.convertToBytes(cap.bytes.length)); out.write(ByteUtils.convertToBytes(cap.bytes.length));
location += 4; // length of bytes location += 4; // length of bytes
@ -254,13 +269,13 @@ public class BinaryExporter implements JmeExporter {
// write out location table // write out location table
// tag/location // tag/location
int locNum = locationTable.keySet().size(); int numLocations = locationTable.keySet().size();
os.write(ByteUtils.convertToBytes(locNum)); os.write(ByteUtils.convertToBytes(numLocations));
int locbytes = 0; int locationTableSize = 0;
for (Integer key : locationTable.keySet()) { for (Integer key : locationTable.keySet()) {
os.write(ByteUtils.convertToBytes(key)); os.write(ByteUtils.convertToBytes(key));
os.write(ByteUtils.convertToBytes(locationTable.get(key))); os.write(ByteUtils.convertToBytes(locationTable.get(key)));
locbytes += 8; locationTableSize += 8;
} }
// write out number of root ids - hardcoded 1 for now // write out number of root ids - hardcoded 1 for now
@ -278,11 +293,11 @@ public class BinaryExporter implements JmeExporter {
if (debug ) { if (debug ) {
logger.info("Stats:"); logger.info("Stats:");
logger.info("classes: " + classNum); logger.log(Level.INFO, "classes: {0}", classNum);
logger.info("class table: " + ttbytes + " bytes"); logger.log(Level.INFO, "class table: {0} bytes", classTableSize);
logger.info("objects: " + locNum); logger.log(Level.INFO, "objects: {0}", numLocations);
logger.info("location table: " + locbytes + " bytes"); logger.log(Level.INFO, "location table: {0} bytes", locationTableSize);
logger.info("data: " + location + " bytes"); logger.log(Level.INFO, "data: {0} bytes", location);
} }
return true; return true;
@ -331,17 +346,38 @@ public class BinaryExporter implements JmeExporter {
return contentTable.get(object).getContent(); return contentTable.get(object).getContent();
} }
private BinaryClassObject createClassObject(Class clazz) throws IOException{
BinaryClassObject bco = new BinaryClassObject();
bco.alias = generateTag();
bco.nameFields = new HashMap<String, BinaryClassField>();
ArrayList<Integer> versionList = new ArrayList<Integer>();
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 { public int processBinarySavable(Savable object) throws IOException {
if (object == null) { if (object == null) {
return -1; return -1;
} }
Class<? extends Savable> clazz = object.getClass();
BinaryClassObject bco = classes.get(object.getClass().getName()); BinaryClassObject bco = classes.get(object.getClass().getName());
// is this class been looked at before? in tagTable? // is this class been looked at before? in tagTable?
if (bco == null) { if (bco == null) {
bco = new BinaryClassObject(); bco = createClassObject(object.getClass());
bco.alias = generateTag();
bco.nameFields = new HashMap<String, BinaryClassField>();
classes.put(object.getClass().getName(), bco);
} }
// is object in contentTable? // is object in contentTable?

@ -32,9 +32,11 @@
package com.jme3.export.binary; package com.jme3.export.binary;
import com.jme3.export.SavableClassFinder; import com.jme3.export.SavableClassUtil;
import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetManager; import com.jme3.asset.AssetManager;
import com.jme3.export.FormatVersion;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
import com.jme3.export.ReadListener; import com.jme3.export.ReadListener;
import com.jme3.export.Savable; import com.jme3.export.Savable;
@ -57,6 +59,7 @@ import java.util.logging.Logger;
/** /**
* @author Joshua Slack * @author Joshua Slack
* @author Kirill Vainer - Version number, Fast buffer reading
*/ */
public final class BinaryImporter implements JmeImporter { public final class BinaryImporter implements JmeImporter {
private static final Logger logger = Logger.getLogger(BinaryImporter.class private static final Logger logger = Logger.getLogger(BinaryImporter.class
@ -81,6 +84,7 @@ public final class BinaryImporter implements JmeImporter {
private byte[] dataArray; private byte[] dataArray;
private int aliasWidth; private int aliasWidth;
private int formatVersion;
private static final boolean fastRead = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN; private static final boolean fastRead = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
@ -89,6 +93,10 @@ public final class BinaryImporter implements JmeImporter {
public BinaryImporter() { public BinaryImporter() {
} }
public int getFormatVersion(){
return formatVersion;
}
public static boolean canUseFastBuffers(){ public static boolean canUseFastBuffers(){
return fastRead; return fastRead;
} }
@ -139,7 +147,31 @@ public final class BinaryImporter implements JmeImporter {
public Savable load(InputStream is, ReadListener listener, ByteArrayOutputStream baos) throws IOException { public Savable load(InputStream is, ReadListener listener, ByteArrayOutputStream baos) throws IOException {
contentTable.clear(); contentTable.clear();
BufferedInputStream bis = new BufferedInputStream(is); 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; int bytes = 4;
aliasWidth = ((int)FastMath.log(numClasses, 256) + 1); aliasWidth = ((int)FastMath.log(numClasses, 256) + 1);
@ -147,11 +179,26 @@ public final class BinaryImporter implements JmeImporter {
for(int i = 0; i < numClasses; i++) { for(int i = 0; i < numClasses; i++) {
String alias = readString(bis, aliasWidth); 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); int classLength = ByteUtils.readInt(bis);
String className = readString(bis, classLength); String className = readString(bis, classLength);
BinaryClassObject bco = new BinaryClassObject(); BinaryClassObject bco = new BinaryClassObject();
bco.alias = alias.getBytes(); bco.alias = alias.getBytes();
bco.className = className; bco.className = className;
bco.classHierarchyVersions = classHierarchyVersions;
int fields = ByteUtils.readInt(bis); int fields = ByteUtils.readInt(bis);
bytes += (8 + aliasWidth + classLength); bytes += (8 + aliasWidth + classLength);
@ -210,9 +257,9 @@ public final class BinaryImporter implements JmeImporter {
Savable rVal = readObject(id); Savable rVal = readObject(id);
if (debug) { if (debug) {
logger.info("Importer Stats: "); logger.info("Importer Stats: ");
logger.info("Tags: "+numClasses); logger.log(Level.INFO, "Tags: {0}", numClasses);
logger.info("Objects: "+numLocs); logger.log(Level.INFO, "Objects: {0}", numLocs);
logger.info("Data Size: "+dataArray.length); logger.log(Level.INFO, "Data Size: {0}", dataArray.length);
} }
dataArray = null; dataArray = null;
return rVal; return rVal;
@ -247,7 +294,8 @@ public final class BinaryImporter implements JmeImporter {
return rVal; return rVal;
} }
public BinaryInputCapsule getCapsule(Savable id) { @Override
public InputCapsule getCapsule(Savable id) {
return capsuleTable.get(id); return capsuleTable.get(id);
} }
@ -291,10 +339,10 @@ public final class BinaryImporter implements JmeImporter {
int dataLength = ByteUtils.convertIntFromBytes(dataArray, loc); int dataLength = ByteUtils.convertIntFromBytes(dataArray, loc);
loc+=4; loc+=4;
BinaryInputCapsule cap = new BinaryInputCapsule(this, bco); Savable out = SavableClassUtil.fromName(bco.className, loaders);
cap.setContent(dataArray, loc, loc+dataLength);
Savable out = SavableClassFinder.fromName(bco.className, cap, loaders); BinaryInputCapsule cap = new BinaryInputCapsule(this, out, bco);
cap.setContent(dataArray, loc, loc+dataLength);
capsuleTable.put(out, cap); capsuleTable.put(out, cap);
contentTable.put(id, out); contentTable.put(id, out);

@ -39,7 +39,6 @@ import com.jme3.util.IntMap;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
@ -60,13 +59,15 @@ final class BinaryInputCapsule implements InputCapsule {
protected BinaryImporter importer; protected BinaryImporter importer;
protected BinaryClassObject cObj; protected BinaryClassObject cObj;
protected Savable savable;
protected HashMap<Byte, Object> fieldData; protected HashMap<Byte, Object> fieldData;
protected int index = 0; protected int index = 0;
public BinaryInputCapsule(BinaryImporter importer, BinaryClassObject bco) { public BinaryInputCapsule(BinaryImporter importer, Savable savable, BinaryClassObject bco) {
this.importer = importer; this.importer = importer;
this.cObj = bco; this.cObj = bco;
this.savable = savable;
} }
public void setContent(byte[] content, int start, int limit) { public void setContent(byte[] content, int start, int limit) {
@ -256,6 +257,26 @@ final class BinaryInputCapsule implements InputCapsule {
} }
} }
public int getSavableVersion(Class<? extends Savable> 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 { public BitSet readBitSet(String name, BitSet defVal) throws IOException {
BinaryClassField field = cObj.nameFields.get(name); BinaryClassField field = cObj.nameFields.get(name);
if (field == null || !fieldData.containsKey(field.alias)) if (field == null || !fieldData.containsKey(field.alias))

@ -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;
}

@ -39,7 +39,6 @@ import com.jme3.export.Savable;
import com.jme3.renderer.RenderManager; import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort; import com.jme3.renderer.ViewPort;
import com.jme3.scene.Mesh; import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl; import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
@ -68,7 +67,7 @@ import java.util.HashMap;
* *
* @author Kirill Vainer * @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. * 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); skeleton = (Skeleton) in.readSavable("skeleton", null);
animationMap = (HashMap<String, BoneAnimation>) in.readStringSavableMap("animations", null); animationMap = (HashMap<String, BoneAnimation>) in.readStringSavableMap("animations", null);
//changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split if (im.getFormatVersion() == 0){
//if we find a target mesh array the AnimControl creates the SkeletonControl for old files and add it to the spatial. //changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split
//When backward compatibility won't be needed anymore this can deleted //if we find a target mesh array the AnimControl creates the SkeletonControl for old files and add it to the spatial.
Savable[] sav = in.readSavableArray("targets", null); //When backward compatibility won't be needed anymore this can deleted
if (sav != null) { Savable[] sav = in.readSavableArray("targets", null);
Mesh[] tg = null; if (sav != null) {
tg = new Mesh[sav.length]; Mesh[] targets = new Mesh[sav.length];
System.arraycopy(sav, 0, tg, 0, sav.length); System.arraycopy(sav, 0, targets, 0, sav.length);
skeletonControl = new SkeletonControl(tg, skeleton); skeletonControl = new SkeletonControl(targets, skeleton);
spatial.addControl(skeletonControl); spatial.addControl(skeletonControl);
}
} }
} }
} }

@ -268,27 +268,27 @@ public final class BoneTrack implements Savable {
//Backward compatibility for old j3o files generated before revision 6807 //Backward compatibility for old j3o files generated before revision 6807
if (translations == null) { if (im.getFormatVersion() == 0){
Savable[] sav = ic.readSavableArray("translations", null); if (translations == null) {
if (sav != null) { Savable[] sav = ic.readSavableArray("translations", null);
translations = new CompactVector3Array(); if (sav != null) {
Vector3f[] transCopy = new Vector3f[sav.length]; translations = new CompactVector3Array();
System.arraycopy(sav, 0, transCopy, 0, sav.length); Vector3f[] transCopy = new Vector3f[sav.length];
translations.add(transCopy); System.arraycopy(sav, 0, transCopy, 0, sav.length);
translations.freeze(); translations.add(transCopy);
translations.freeze();
}
} }
} if (rotations == null) {
if (rotations == null) { Savable[] sav = ic.readSavableArray("rotations", null);
Savable[] sav = ic.readSavableArray("rotations", null); if (sav != null) {
if (sav != null) { rotations = new CompactQuaternionArray();
rotations = new CompactQuaternionArray(); Quaternion[] rotCopy = new Quaternion[sav.length];
Quaternion[] rotCopy = new Quaternion[sav.length]; System.arraycopy(sav, 0, rotCopy, 0, sav.length);
System.arraycopy(sav, 0, rotCopy, 0, sav.length); rotations.add(rotCopy);
rotations.add(rotCopy); rotations.freeze();
rotations.freeze(); }
} }
} }
} }
} }

@ -17,6 +17,7 @@ import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh; import com.jme3.scene.Mesh;
import com.jme3.scene.Node; import com.jme3.scene.Node;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.UserData;
import com.jme3.scene.VertexBuffer; import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.control.AbstractControl; import com.jme3.scene.control.AbstractControl;
@ -25,14 +26,16 @@ import com.jme3.util.TempVars;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.util.ArrayList;
/** /**
* The Skeleton control deforms a model according to a skeleton, * 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 * @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 * The skeleton of the model
@ -49,21 +52,90 @@ public class SkeletonControl extends AbstractControl implements Savable, Cloneab
private boolean wasMeshUpdated = false; private boolean wasMeshUpdated = false;
/** /**
* for serialization only * Serialization only. Do not use.
*/ */
public SkeletonControl() { public SkeletonControl() {
} }
/** /**
* Creates a skeleton control * Creates a skeleton control.
* @param targets the meshes controled by the skeleton * The list of targets will be acquired automatically when
* the control is attached to a node.
*
* @param skeleton the skeleton * @param skeleton the skeleton
*/ */
public SkeletonControl(Mesh[] targets, Skeleton skeleton) { public SkeletonControl(Skeleton skeleton) {
this.skeleton = skeleton;
}
/**
* Creates a skeleton control.
*
* @param targets the meshes controlled by the skeleton
* @param skeleton the skeleton
*/
@Deprecated
public SkeletonControl(Mesh[] targets, Skeleton skeleton){
this.skeleton = skeleton; this.skeleton = skeleton;
this.targets = targets; this.targets = targets;
} }
private boolean isMeshAnimated(Mesh mesh){
return mesh.getBuffer(Type.BindPosePosition) != null;
}
private Mesh[] findTargets(Node node){
Mesh sharedMesh = null;
ArrayList<Mesh> animatedMeshes = new ArrayList<Mesh>();
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 @Override
protected void controlRender(RenderManager rm, ViewPort vp) { protected void controlRender(RenderManager rm, ViewPort vp) {
if (!wasMeshUpdated) { if (!wasMeshUpdated) {
@ -87,13 +159,11 @@ public class SkeletonControl extends AbstractControl implements Savable, Cloneab
@Override @Override
protected void controlUpdate(float tpf) { protected void controlUpdate(float tpf) {
wasMeshUpdated = false; wasMeshUpdated = false;
} }
void resetToBind() { void resetToBind() {
for (int i = 0; i < targets.length; i++) { for (Mesh mesh : targets){
Mesh mesh = targets[i]; if (isMeshAnimated(mesh)) {
if (targets[i].getBuffer(Type.BindPosePosition) != null) {
VertexBuffer bi = mesh.getBuffer(Type.BoneIndex); VertexBuffer bi = mesh.getBuffer(Type.BoneIndex);
ByteBuffer bib = (ByteBuffer) bi.getData(); ByteBuffer bib = (ByteBuffer) bi.getData();
if (!bib.hasArray()) { if (!bib.hasArray()) {
@ -177,12 +247,12 @@ public class SkeletonControl extends AbstractControl implements Savable, Cloneab
* sets the skeleton for this control * sets the skeleton for this control
* @param skeleton * @param skeleton
*/ */
public void setSkeleton(Skeleton skeleton) { // public void setSkeleton(Skeleton skeleton) {
this.skeleton = skeleton; // this.skeleton = skeleton;
} // }
/** /**
* retuns the targets meshes of this ocntrol * returns the targets meshes of this control
* @return * @return
*/ */
public Mesh[] getTargets() { public Mesh[] getTargets() {
@ -193,9 +263,9 @@ public class SkeletonControl extends AbstractControl implements Savable, Cloneab
* sets the target meshes of this control * sets the target meshes of this control
* @param targets * @param targets
*/ */
public void setTargets(Mesh[] targets) { // public void setTargets(Mesh[] targets) {
this.targets = targets; // this.targets = targets;
} // }
private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) { private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) {
int maxWeightsPerVert = mesh.getMaxNumWeights(); int maxWeightsPerVert = mesh.getMaxNumWeights();

@ -61,7 +61,7 @@ import java.util.logging.Logger;
* *
* @author Nehon * @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 static final Logger logger = Logger.getLogger(Application.class.getName());
private String niftyXmlPath = null; private String niftyXmlPath = null;

@ -48,7 +48,7 @@ import java.util.List;
* *
* @author Nehon * @author Nehon
*/ */
public abstract class AbstractCinematicEvent implements CinematicEvent, Savable { public abstract class AbstractCinematicEvent implements CinematicEvent {
protected PlayState playState = PlayState.Stopped; protected PlayState playState = PlayState.Stopped;
protected float speed = 1; protected float speed = 1;

@ -35,12 +35,13 @@ import com.jme3.animation.LoopMode;
import com.jme3.app.Application; import com.jme3.app.Application;
import com.jme3.cinematic.Cinematic; import com.jme3.cinematic.Cinematic;
import com.jme3.cinematic.PlayState; import com.jme3.cinematic.PlayState;
import com.jme3.export.Savable;
/** /**
* *
* @author Nehon * @author Nehon
*/ */
public interface CinematicEvent { public interface CinematicEvent extends Savable {
/** /**
* Starts the animation * Starts the animation

@ -1118,23 +1118,25 @@ public class ParticleEmitter extends Geometry {
particleInfluencer = (ParticleInfluencer) ic.readSavable("influencer", DEFAULT_INFLUENCER); particleInfluencer = (ParticleInfluencer) ic.readSavable("influencer", DEFAULT_INFLUENCER);
// compatibility before the control inside particle emitter if (im.getFormatVersion() == 0){
// was changed: // compatibility before the control inside particle emitter
// find it in the controls and take it out, then add the proper one in // was changed:
for (int i = 0; i < controls.size(); i++) { // find it in the controls and take it out, then add the proper one in
Object obj = controls.get(i); for (int i = 0; i < controls.size(); i++){
if (obj instanceof ParticleEmitter) { Object obj = controls.get(i);
controls.remove(i); if (obj instanceof ParticleEmitter){
// now add the proper one in controls.remove(i);
controls.add(control); // now add the proper one in
break; controls.add(control);
break;
}
} }
}
// compatability before gravity was not a vector but a float // compatability before gravity was not a vector but a float
if (gravity == null) { if (gravity == null){
gravity = new Vector3f(); gravity = new Vector3f();
gravity.y = ic.readFloat("gravity", 0); gravity.y = ic.readFloat("gravity", 0);
}
} }
} }
} }

@ -56,7 +56,7 @@ public class DefaultParticleInfluencer implements ParticleInfluencer {
@Override @Override
public void read(JmeImporter im) throws IOException { public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this); 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); velocityVariation = ic.readFloat("variation", 0.2f);
} }

@ -1,5 +1,6 @@
package com.jme3.effect.shapes; package com.jme3.effect.shapes;
import com.jme3.export.InputCapsule;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -141,11 +142,18 @@ public class EmitterMeshVertexShape implements EmitterShape {
public void write(JmeExporter ex) throws IOException { public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this); OutputCapsule oc = ex.getCapsule(this);
oc.writeSavableArrayList((ArrayList<List<Vector3f>>) vertices, "vertices", null); oc.writeSavableArrayList((ArrayList<List<Vector3f>>) vertices, "vertices", null);
oc.writeSavableArrayList((ArrayList<List<Vector3f>>) normals, "normals", null);
} }
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void read(JmeImporter im) throws IOException { 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<List<Vector3f>> tmpNormals = ic.readSavableArrayList("normals", null);
if (tmpNormals != null){
this.normals = tmpNormals;
}
} }
} }

@ -47,6 +47,8 @@ import java.util.Map;
*/ */
public interface InputCapsule { public interface InputCapsule {
public int getSavableVersion(Class<? extends Savable> clazz);
// byte primitive // byte primitive
public byte readByte(String name, byte defVal) throws IOException; public byte readByte(String name, byte defVal) throws IOException;

@ -38,4 +38,12 @@ import com.jme3.asset.AssetManager;
public interface JmeImporter extends AssetLoader { public interface JmeImporter extends AssetLoader {
public InputCapsule getCapsule(Savable id); public InputCapsule getCapsule(Savable id);
public AssetManager getAssetManager(); 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();
} }

@ -37,7 +37,8 @@ import java.io.IOException;
/** /**
* <code>Savable</code> is an interface for objects that can be serialized * <code>Savable</code> is an interface for objects that can be serialized
* using jME's serialization system. * using jME's serialization system.
* @author Dany *
* @author Kirill Vainer
*/ */
public interface Savable { public interface Savable {
void write(JmeExporter ex) throws IOException; void write(JmeExporter ex) throws IOException;

@ -41,23 +41,24 @@ import java.io.IOException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jme3.export.InputCapsule;
import com.jme3.export.Savable;
import com.jme3.material.MatParamTexture; 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.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
/** /**
* <code>SavableClassFinder</code> is used to find classes referenced * <code>SavableClassUtil</code> contains various utilities to handle
* by savables. * 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 * Currently it will remap any classes from old paths to new paths
* so that old J3O models can still be loaded. * so that old J3O models can still be loaded.
* *
* @author mpowell * @author mpowell
* @author Kirill Vainer * @author Kirill Vainer
*/ */
public class SavableClassFinder { public class SavableClassUtil {
private final static HashMap<String, String> classRemappings = new HashMap<String, String>(); private final static HashMap<String, String> classRemappings = new HashMap<String, String>();
@ -84,6 +85,31 @@ public class SavableClassFinder {
} }
} }
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<? extends Savable> 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 * 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 * 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 ClassNotFoundException thrown if the class name is not in the classpath.
* @throws IOException when loading ctor parameters fails * @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 { IllegalAccessException, ClassNotFoundException, IOException {
className = remapClass(className); className = remapClass(className);
try { try {
return (Savable) Class.forName(className).newInstance(); return (Savable) Class.forName(className).newInstance();
} catch (InstantiationException e) { } 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" 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); + "Some types need to have the BinaryImporter set up in a special way. Please doublecheck the setup.", className);
throw e; throw e;
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
Logger.getLogger(SavableClassFinder.class.getName()).log( Logger.getLogger(SavableClassUtil.class.getName()).log(
Level.SEVERE, "{0} \n" Level.SEVERE, "{0} \n"
+ "Some types need to have the BinaryImporter set up in a special way. Please doublecheck the setup.", e.getMessage()); + "Some types need to have the BinaryImporter set up in a special way. Please doublecheck the setup.", e.getMessage());
throw e; throw e;
} }
} }
public static Savable fromName(String className, InputCapsule inputCapsule, List<ClassLoader> loaders) throws InstantiationException, public static Savable fromName(String className, List<ClassLoader> loaders) throws InstantiationException,
IllegalAccessException, ClassNotFoundException, IOException { IllegalAccessException, ClassNotFoundException, IOException {
if (loaders == null) { if (loaders == null) {
return fromName(className, inputCapsule); return fromName(className);
} }
String newClassName = remapClass(className); String newClassName = remapClass(className);
@ -131,6 +157,6 @@ public class SavableClassFinder {
} }
return fromName(className, inputCapsule); return fromName(className);
} }
} }

@ -53,6 +53,12 @@ public final class UserData implements Savable {
*/ */
public static final String JME_PHYSICSIGNORE = "JmePhysicsIgnore"; 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 byte type;
protected Object value; protected Object value;

@ -44,7 +44,7 @@ import java.nio.IntBuffer;
/** /**
* implements all writeXXXXArray methods to reduce boilerplate code * 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 { public abstract class AbstractGeomap implements Geomap {
@ -60,7 +60,7 @@ public abstract class AbstractGeomap implements Geomap {
/* /*
* (non-Javadoc) * (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 * which directly accesses the data rather than using getHeight
*/ */
public FloatBuffer writeVertexArray(FloatBuffer store, Vector3f scale, boolean center){ public FloatBuffer writeVertexArray(FloatBuffer store, Vector3f scale, boolean center){

@ -33,9 +33,8 @@
package com.jme3.util; package com.jme3.util;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
@ -92,35 +91,48 @@ public final class ListMap<K, V> implements Map<K, V>, Cloneable, Serializable {
} }
private final ArrayList<ListMapEntry<K,V>> entries; private final HashMap<K, V> backingMap;
private ListMapEntry<K, V>[] entries;
// private final ArrayList<ListMapEntry<K,V>> entries;
public ListMap(){ public ListMap(){
entries = new ArrayList<ListMapEntry<K,V>>(); entries = new ListMapEntry[4];
backingMap = new HashMap<K, V>(4);
// entries = new ArrayList<ListMapEntry<K,V>>();
} }
public ListMap(int initialCapacity){ public ListMap(int initialCapacity){
entries = new ArrayList<ListMapEntry<K, V>>(initialCapacity); entries = new ListMapEntry[initialCapacity];
backingMap = new HashMap<K, V>(initialCapacity);
// entries = new ArrayList<ListMapEntry<K, V>>(initialCapacity);
} }
public ListMap(Map<? extends K, ? extends V> map){ public ListMap(Map<? extends K, ? extends V> map){
entries = new ArrayList<ListMapEntry<K, V>>(map.size()); entries = new ListMapEntry[map.size()];
backingMap = new HashMap<K, V>(map.size());
// entries = new ArrayList<ListMapEntry<K, V>>(map.size());
putAll(map); putAll(map);
} }
public int size() { public int size() {
return entries.size(); // return entries.size();
return backingMap.size();
} }
public Entry<K, V> getEntry(int index){ public Entry<K, V> getEntry(int index){
return entries.get(index); // return entries.get(index);
return entries[index];
} }
public V getValue(int 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){ public K getKey(int index){
return entries.get(index).key; // return entries.get(index).key;
return entries[index].key;
} }
public boolean isEmpty() { public boolean isEmpty() {
@ -130,93 +142,142 @@ public final class ListMap<K, V> implements Map<K, V>, Cloneable, Serializable {
private static boolean keyEq(Object keyA, Object keyB){ private static boolean keyEq(Object keyA, Object keyB){
return keyA.hashCode() == keyB.hashCode() ? (keyA == keyB) || keyA.equals(keyB) : false; return keyA.hashCode() == keyB.hashCode() ? (keyA == keyB) || keyA.equals(keyB) : false;
} }
//
private static boolean valEq(Object a, Object b){ // private static boolean valEq(Object a, Object b){
return a == null ? (b == null) : a.equals(b); // return a == null ? (b == null) : a.equals(b);
} // }
public boolean containsKey(Object key) { public boolean containsKey(Object key) {
if (key == null) return backingMap.containsKey( (K) key);
throw new IllegalArgumentException(); // if (key == null)
// throw new IllegalArgumentException();
for (int i = 0; i < entries.size(); i++){ //
ListMapEntry<K,V> entry = entries.get(i); // for (int i = 0; i < entries.size(); i++){
if (keyEq(entry.key, key)) // ListMapEntry<K,V> entry = entries.get(i);
return true; // if (keyEq(entry.key, key))
} // return true;
return false; // }
// return false;
} }
public boolean containsValue(Object value) { public boolean containsValue(Object value) {
for (int i = 0; i < entries.size(); i++){ return backingMap.containsValue( (V) value);
if (valEq(entries.get(i).value, value)) // for (int i = 0; i < entries.size(); i++){
return true; // if (valEq(entries.get(i).value, value))
} // return true;
return false; // }
// return false;
} }
public V get(Object key) { public V get(Object key) {
if (key == null) return backingMap.get( (K) key);
throw new IllegalArgumentException(); // if (key == null)
// throw new IllegalArgumentException();
for (int i = 0; i < entries.size(); i++){ //
ListMapEntry<K,V> entry = entries.get(i); // for (int i = 0; i < entries.size(); i++){
if (keyEq(entry.key, key)) // ListMapEntry<K,V> entry = entries.get(i);
return entry.value; // if (keyEq(entry.key, key))
} // return entry.value;
return null; // }
// return null;
} }
public V put(K key, V value) { public V put(K key, V value) {
if (key == null) if (backingMap.containsKey(key)){
throw new IllegalArgumentException(); // set the value on the entry
int size = size();
// check if entry exists, if yes, overwrite it with new value for (int i = 0; i < size; i++){
for (int i = 0; i < entries.size(); i++){ ListMapEntry<K, V> entry = entries[i];
ListMapEntry<K,V> entry = entries.get(i); if (keyEq(entry.key, key)){
if (keyEq(entry.key, key)){ entry.value = value;
V prevValue = entry.value; break;
entry.value = value; }
return prevValue; }
}else{
int size = size();
// expand list as necessary
if (size == entries.length){
ListMapEntry<K, V>[] tmpEntries = entries;
entries = new ListMapEntry[size * 2];
System.arraycopy(tmpEntries, 0, entries, 0, size);
} }
entries[size] = new ListMapEntry<K, V>(key, value);
} }
return backingMap.put(key, value);
// add a new entry // if (key == null)
entries.add(new ListMapEntry<K, V>(key, value)); // throw new IllegalArgumentException();
return null; //
// // check if entry exists, if yes, overwrite it with new value
// for (int i = 0; i < entries.size(); i++){
// ListMapEntry<K,V> 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<K, V>(key, value));
// return null;
} }
public V remove(Object key) { public V remove(Object key) {
if (key == null) V element = backingMap.remove( (K) key);
throw new IllegalArgumentException(); if (element != null){
// find removed element
int size = size() + 1; // includes removed element
int removedIndex = -1;
for (int i = 0; i < size; i++){
ListMapEntry<K, V> entry = entries[i];
if (keyEq(entry.key, key)){
removedIndex = i;
break;
}
}
assert removedIndex >= 0;
for (int i = 0; i < entries.size(); i++){ size --;
ListMapEntry<K,V> entry = entries.get(i); for (int i = removedIndex; i < size; i++){
if (keyEq(entry.key, key)){ entries[i] = entries[i+1];
return entries.remove(i).value;
} }
} }
return null; return element;
// if (key == null)
// throw new IllegalArgumentException();
//
// for (int i = 0; i < entries.size(); i++){
// ListMapEntry<K,V> entry = entries.get(i);
// if (keyEq(entry.key, key)){
// return entries.remove(i).value;
// }
// }
// return null;
} }
public void putAll(Map<? extends K, ? extends V> map) { public void putAll(Map<? extends K, ? extends V> map) {
if (map instanceof ListMap){ for (Entry<? extends K, ? extends V> entry : map.entrySet()){
ListMap<K, V> listMap = (ListMap<K, V>) map; put(entry.getKey(), entry.getValue());
ArrayList<ListMapEntry<K, V>> otherEntries = listMap.entries;
for (int i = 0; i < otherEntries.size(); i++){
ListMapEntry<K, V> entry = otherEntries.get(i);
put(entry.key, entry.value);
}
}else{
for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()){
put(entry.getKey(), entry.getValue());
}
} }
// if (map instanceof ListMap){
// ListMap<K, V> listMap = (ListMap<K, V>) map;
// ArrayList<ListMapEntry<K, V>> otherEntries = listMap.entries;
// for (int i = 0; i < otherEntries.size(); i++){
// ListMapEntry<K, V> entry = otherEntries.get(i);
// put(entry.key, entry.value);
// }
// }else{
// for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()){
// put(entry.getKey(), entry.getValue());
// }
// }
} }
public void clear() { public void clear() {
entries.clear(); backingMap.clear();
// entries.clear();
} }
@Override @Override
@ -227,27 +288,30 @@ public final class ListMap<K, V> implements Map<K, V>, Cloneable, Serializable {
} }
public Set<K> keySet() { public Set<K> keySet() {
HashSet<K> keys = new HashSet<K>(); return backingMap.keySet();
for (int i = 0; i < entries.size(); i++){ // HashSet<K> keys = new HashSet<K>();
ListMapEntry<K,V> entry = entries.get(i); // for (int i = 0; i < entries.size(); i++){
keys.add(entry.key); // ListMapEntry<K,V> entry = entries.get(i);
} // keys.add(entry.key);
return keys; // }
// return keys;
} }
public Collection<V> values() { public Collection<V> values() {
ArrayList<V> values = new ArrayList<V>(); return backingMap.values();
for (int i = 0; i < entries.size(); i++){ // ArrayList<V> values = new ArrayList<V>();
ListMapEntry<K,V> entry = entries.get(i); // for (int i = 0; i < entries.size(); i++){
values.add(entry.value); // ListMapEntry<K,V> entry = entries.get(i);
} // values.add(entry.value);
return values; // }
// return values;
} }
public Set<Entry<K, V>> entrySet() { public Set<Entry<K, V>> entrySet() {
HashSet<Entry<K, V>> entryset = new HashSet<Entry<K, V>>(); return backingMap.entrySet();
entryset.addAll(entries); // HashSet<Entry<K, V>> entryset = new HashSet<Entry<K, V>>();
return entryset; // entryset.addAll(entries);
// return entryset;
} }
} }

@ -47,6 +47,7 @@ import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh; import com.jme3.scene.Mesh;
import com.jme3.scene.Node; import com.jme3.scene.Node;
import com.jme3.scene.Spatial.CullHint; import com.jme3.scene.Spatial.CullHint;
import com.jme3.scene.UserData;
import com.jme3.scene.VertexBuffer; import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Format; import com.jme3.scene.VertexBuffer.Format;
import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.VertexBuffer.Type;
@ -101,27 +102,29 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
private String folderName; private String folderName;
private AssetManager assetManager; private AssetManager assetManager;
private MaterialList materialList; private MaterialList materialList;
// Data per submesh/sharedgeom
private ShortBuffer sb; private ShortBuffer sb;
private IntBuffer ib; private IntBuffer ib;
private FloatBuffer fb; private FloatBuffer fb;
private VertexBuffer vb; private VertexBuffer vb;
private Mesh mesh; private Mesh mesh;
private Geometry geom; private Geometry geom;
private Mesh sharedmesh; private ByteBuffer indicesData;
private Geometry sharedgeom; private FloatBuffer weightsFloatData;
private int geomIdx = 0;
private int texCoordIdx = 0;
private static volatile int nodeIdx = 0;
private String ignoreUntilEnd = null;
private boolean bigindices = false;
private int vertCount; 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<Geometry> geoms = new ArrayList<Geometry>(); private List<Geometry> geoms = new ArrayList<Geometry>();
private List<Boolean> usesSharedGeom = new ArrayList<Boolean>();
private IntMap<List<VertexBuffer>> lodLevels = new IntMap<List<VertexBuffer>>(); private IntMap<List<VertexBuffer>> lodLevels = new IntMap<List<VertexBuffer>>();
private AnimData animData; private AnimData animData;
private ByteBuffer indicesData;
private FloatBuffer weightsFloatData;
public MeshLoader() { public MeshLoader() {
super(); super();
@ -130,7 +133,6 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
@Override @Override
public void startDocument() { public void startDocument() {
geoms.clear(); geoms.clear();
usesSharedGeom.clear();
lodLevels.clear(); lodLevels.clear();
sb = null; sb = null;
@ -139,14 +141,12 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
vb = null; vb = null;
mesh = null; mesh = null;
geom = null; geom = null;
sharedgeom = null; sharedMesh = null;
sharedmesh = null;
usesSharedVerts = false;
vertCount = 0; vertCount = 0;
triCount = 0; meshIndex = 0;
geomIdx = 0; texCoordIndex = 0;
texCoordIdx = 0;
nodeIdx = 0;
ignoreUntilEnd = null; ignoreUntilEnd = null;
animData = null; animData = null;
@ -172,27 +172,22 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
} }
} }
private boolean isUsingSharedVerts(Geometry geom){
return geom.getUserData(UserData.JME_SHAREDMESH) != null;
}
private void startFaces(String count) throws SAXException { private void startFaces(String count) throws SAXException {
int numFaces = parseInt(count); int numFaces = parseInt(count);
int numIndices; int numIndices;
if (mesh.getMode() == Mesh.Mode.Triangles) { if (mesh.getMode() == Mesh.Mode.Triangles) {
//mesh.setTriangleCount(numFaces);
numIndices = numFaces * 3; numIndices = numFaces * 3;
} else { } else {
throw new SAXException("Triangle strip or fan not supported!"); 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); vb = new VertexBuffer(VertexBuffer.Type.Index);
if (!bigindices) { if (!usesBigIndices) {
sb = BufferUtils.createShortBuffer(numIndices); sb = BufferUtils.createShortBuffer(numIndices);
ib = null; ib = null;
vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb); vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb);
@ -226,16 +221,11 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
if (mat.isTransparent()) { if (mat.isTransparent()) {
geom.setQueueBucket(Bucket.Transparent); geom.setQueueBucket(Bucket.Transparent);
} }
// else
// geom.setShadowMode(ShadowMode.CastAndReceive);
// if (mat.isReceivesShadows())
geom.setMaterial(mat); 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(); mesh = new Mesh();
if (opType == null || opType.equals("triangle_list")) { if (opType == null || opType.equals("triangle_list")) {
mesh.setMode(Mesh.Mode.Triangles); mesh.setMode(Mesh.Mode.Triangles);
@ -245,24 +235,25 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
mesh.setMode(Mesh.Mode.TriangleFan); mesh.setMode(Mesh.Mode.TriangleFan);
} }
bigindices = parseBool(use32bitIndices, false); usesBigIndices = parseBool(use32bitIndices, false);
boolean sharedverts = parseBool(usesharedvertices, false); usesSharedVerts = parseBool(usesharedvertices, false);
if (sharedverts) { if (usesSharedVerts) {
usesSharedGeom.add(true);
// import vertexbuffers from shared geom // import vertexbuffers from shared geom
IntMap<VertexBuffer> sharedBufs = sharedmesh.getBuffers(); IntMap<VertexBuffer> sharedBufs = sharedMesh.getBuffers();
for (Entry<VertexBuffer> entry : sharedBufs) { for (Entry<VertexBuffer> entry : sharedBufs) {
mesh.setBuffer(entry.getValue()); mesh.setBuffer(entry.getValue());
} }
// this mesh is shared!
} else {
usesSharedGeom.add(false);
} }
if (meshName == null) { if (meshName == null) {
geom = new Geometry("OgreSubmesh-" + (++geomIdx), mesh); geom = new Geometry("OgreSubmesh-" + (++meshIndex), mesh);
} else { } 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); applyMaterial(geom, matName);
@ -270,27 +261,16 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
} }
private void startSharedGeom(String vertexcount) throws SAXException { private void startSharedGeom(String vertexcount) throws SAXException {
sharedmesh = new Mesh(); sharedMesh = new Mesh();
vertCount = parseInt(vertexcount); vertCount = parseInt(vertexcount);
// sharedmesh.setVertexCount(vertCount); usesSharedVerts = false;
if (meshName == null) { geom = null;
sharedgeom = new Geometry("Ogre-SharedGeom", sharedmesh); mesh = 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;
} }
private void startGeometry(String vertexcount) throws SAXException { private void startGeometry(String vertexcount) throws SAXException {
vertCount = parseInt(vertexcount); vertCount = parseInt(vertexcount);
// mesh.setVertexCount(vertCount);
} }
/** /**
@ -298,7 +278,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
* for all vertices in the buffer. * for all vertices in the buffer.
*/ */
private void endBoneAssigns() { private void endBoneAssigns() {
if (mesh != sharedmesh && usesSharedGeom.get(geoms.size() - 1)) { if (mesh != sharedMesh && isUsingSharedVerts(geom)) {
return; return;
} }
@ -341,7 +321,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
} }
private void startBoneAssigns() { private void startBoneAssigns() {
if (mesh != sharedmesh && usesSharedGeom.get(geoms.size() - 1)) { if (mesh != sharedMesh && usesSharedVerts) {
// will use bone assignments from shared mesh (?) // will use bone assignments from shared mesh (?)
return; return;
} }
@ -424,7 +404,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
} }
private void startVertex() { private void startVertex() {
texCoordIdx = 0; texCoordIndex = 0;
} }
private void pushAttrib(Type type, Attributes attribs) throws SAXException { 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 { private void pushTexCoord(Attributes attribs) throws SAXException {
if (texCoordIdx >= 8) { if (texCoordIndex >= 8) {
return; // More than 8 not supported by ogre. return; // More than 8 not supported by ogre.
} }
Type type = TEXCOORD_TYPES[texCoordIdx]; Type type = TEXCOORD_TYPES[texCoordIndex];
VertexBuffer tcvb = mesh.getBuffer(type); VertexBuffer tcvb = mesh.getBuffer(type);
FloatBuffer buf = (FloatBuffer) tcvb.getData(); 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 { private void pushColor(Attributes attribs) throws SAXException {
@ -529,7 +509,6 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
} }
private void startLodGenerated(String depthsqr) { private void startLodGenerated(String depthsqr) {
// dist = Float.parseFloat(depthsqr);
} }
private void pushBoneAssign(String vertIndex, String boneIndex, String weight) throws SAXException { 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) { private void startSkeleton(String name) {
animData = (AnimData) assetManager.loadAsset(folderName + name + ".xml"); 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) { private void startSubmeshName(String indexStr, String nameStr) {
@ -620,7 +595,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
} else if (qName.equals("boneassignments")) { } else if (qName.equals("boneassignments")) {
startBoneAssigns(); startBoneAssigns();
} else if (qName.equals("submesh")) { } else if (qName.equals("submesh")) {
startMesh(attribs.getValue("material"), startSubMesh(attribs.getValue("material"),
attribs.getValue("usesharedvertices"), attribs.getValue("usesharedvertices"),
attribs.getValue("use32bitindexes"), attribs.getValue("use32bitindexes"),
attribs.getValue("operationtype")); attribs.getValue("operationtype"));
@ -653,18 +628,18 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
if (ignoreUntilEnd.equals(qName)) { if (ignoreUntilEnd.equals(qName)) {
ignoreUntilEnd = null; ignoreUntilEnd = null;
} }
return; return;
} }
if (qName.equals("submesh")) { if (qName.equals("submesh")) {
bigindices = false; usesBigIndices = false;
geom = null; geom = null;
mesh = null; mesh = null;
} else if (qName.equals("submeshes")) { } else if (qName.equals("submeshes")) {
// IMPORTANT: restore sharedgeoemtry, for use with shared boneweights // IMPORTANT: restore sharedmesh, for use with shared boneweights
geom = sharedgeom; geom = null;
mesh = sharedmesh; mesh = sharedMesh;
usesSharedVerts = false;
} else if (qName.equals("faces")) { } else if (qName.equals("faces")) {
if (ib != null) { if (ib != null) {
ib.flip(); ib.flip();
@ -711,56 +686,64 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
} }
private Node compileModel() { private Node compileModel() {
String nodeName; Node model = new Node(meshName + "-ogremesh");
if (meshName == null) {
nodeName = "OgreMesh" + (++nodeIdx); for (int i = 0; i < geoms.size(); i++) {
} else { Geometry g = geoms.get(i);
nodeName = meshName + "-ogremesh"; 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) { if (animData != null) {
ArrayList<Mesh> newMeshes = new ArrayList<Mesh>(geoms.size()); // This model uses animation
// generate bind pose for mesh and add to skin-list // generate bind pose for mesh
// ONLY if not using shared geometry // ONLY if not using shared geometry
// This includes the shared geoemtry itself actually // This includes the shared geoemtry itself actually
if (sharedMesh != null){
sharedMesh.generateBindPose(!HARDWARE_SKINNING);
}
for (int i = 0; i < geoms.size(); i++) { for (int i = 0; i < geoms.size(); i++) {
Geometry g = geoms.get(i); Geometry g = geoms.get(i);
Mesh m = geoms.get(i).getMesh(); Mesh m = geoms.get(i).getMesh();
boolean useShared = usesSharedGeom.get(i); boolean useShared = isUsingSharedVerts(g);
// create bind pose
if (!useShared) { if (!useShared) {
// create bind pose
m.generateBindPose(!HARDWARE_SKINNING); m.generateBindPose(!HARDWARE_SKINNING);
newMeshes.add(m); // } else {
} else { // Inherit animation data from shared mesh
VertexBuffer bindPos = sharedmesh.getBuffer(Type.BindPosePosition); // VertexBuffer bindPos = sharedMesh.getBuffer(Type.BindPosePosition);
VertexBuffer bindNorm = sharedmesh.getBuffer(Type.BindPoseNormal); // VertexBuffer bindNorm = sharedMesh.getBuffer(Type.BindPoseNormal);
VertexBuffer boneIndex = sharedmesh.getBuffer(Type.BoneIndex); // VertexBuffer boneIndex = sharedMesh.getBuffer(Type.BoneIndex);
VertexBuffer boneWeight = sharedmesh.getBuffer(Type.BoneWeight); // VertexBuffer boneWeight = sharedMesh.getBuffer(Type.BoneWeight);
//
if (bindPos != null) { // if (bindPos != null) {
m.setBuffer(bindPos); // m.setBuffer(bindPos);
} // }
//
if (bindNorm != null) { // if (bindNorm != null) {
m.setBuffer(bindNorm); // m.setBuffer(bindNorm);
} // }
//
if (boneIndex != null) { // if (boneIndex != null) {
m.setBuffer(boneIndex); // m.setBuffer(boneIndex);
} // }
//
if (boneWeight != null) { // if (boneWeight != null) {
m.setBuffer(boneWeight); // 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<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>(); HashMap<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>();
ArrayList<BoneAnimation> animList = animData.anims; ArrayList<BoneAnimation> animList = animData.anims;
for (int i = 0; i < animList.size(); i++) { for (int i = 0; i < animList.size(); i++) {
@ -768,21 +751,15 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
anims.put(anim.getName(), anim); anims.put(anim.getName(), anim);
} }
//AnimControl ctrl = new AnimControl(model, meshes, animData.skeleton);
AnimControl ctrl = new AnimControl(animData.skeleton); AnimControl ctrl = new AnimControl(animData.skeleton);
ctrl.setAnimations(anims); ctrl.setAnimations(anims);
model.addControl(ctrl); model.addControl(ctrl);
SkeletonControl skeletonControl = new SkeletonControl(meshes, animData.skeleton);
model.addControl(skeletonControl);
}
for (int i = 0; i < geoms.size(); i++) { // Put the skeleton in the skeleton control
Geometry g = geoms.get(i); SkeletonControl skeletonControl = new SkeletonControl(animData.skeleton);
Mesh m = g.getMesh();
if (sharedmesh != null && usesSharedGeom.get(i)) { // This will acquire the targets from the node
m.setBound(sharedmesh.getBound().clone()); model.addControl(skeletonControl);
}
model.attachChild(geoms.get(i));
} }
return model; return model;

@ -270,7 +270,7 @@ public class TestHoveringTank extends SimpleApplication implements AnalogListene
rock.setWrap(WrapMode.Repeat); rock.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_2", rock); matRock.setTexture("DiffuseMap_2", rock);
matRock.setFloat("DiffuseMap_2_scale", 128); 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); normalMap0.setWrap(WrapMode.Repeat);
Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png");
normalMap1.setWrap(WrapMode.Repeat); normalMap1.setWrap(WrapMode.Repeat);

@ -34,7 +34,7 @@ package com.jme3.export.xml;
import com.jme3.export.InputCapsule; import com.jme3.export.InputCapsule;
import com.jme3.export.Savable; import com.jme3.export.Savable;
import com.jme3.export.SavableClassFinder; import com.jme3.export.SavableClassUtil;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import com.jme3.util.IntMap; import com.jme3.util.IntMap;
import java.io.IOException; import java.io.IOException;
@ -78,6 +78,10 @@ public class DOMInputCapsule implements InputCapsule {
currentElem = doc.getDocumentElement(); currentElem = doc.getDocumentElement();
} }
public int getSavableVersion(Class<? extends Savable> clazz) {
return 0; // TODO: figure this out ...
}
private static String decodeString(String s) { private static String decodeString(String s) {
if (s == null) { if (s == null) {
return null; return null;
@ -973,7 +977,7 @@ public class DOMInputCapsule implements InputCapsule {
} else if (currentElem.hasAttribute("class")) { } else if (currentElem.hasAttribute("class")) {
className = currentElem.getAttribute("class"); className = currentElem.getAttribute("class");
} }
tmp = SavableClassFinder.fromName(className, null); tmp = SavableClassUtil.fromName(className, null);
String refID = currentElem.getAttribute("reference_ID"); String refID = currentElem.getAttribute("reference_ID");
if (refID.length() < 1) refID = currentElem.getAttribute("id"); if (refID.length() < 1) refID = currentElem.getAttribute("id");
if (refID.length() > 0) referencedSavables.put(refID, tmp); if (refID.length() > 0) referencedSavables.put(refID, tmp);

@ -52,11 +52,15 @@ public class XMLImporter implements JmeImporter {
private AssetManager assetManager; private AssetManager assetManager;
private DOMInputCapsule domIn; private DOMInputCapsule domIn;
// A single-load-state base URI for texture-loading.
public XMLImporter() { public XMLImporter() {
} }
// TODO: .......
public int getFormatVersion() {
return 0;
}
public AssetManager getAssetManager(){ public AssetManager getAssetManager(){
return assetManager; return assetManager;
} }
@ -73,10 +77,7 @@ public class XMLImporter implements JmeImporter {
return obj; return obj;
} }
synchronized public Savable load(InputStream f) throws IOException { 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. */
try { try {
domIn = new DOMInputCapsule(DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(f), this); domIn = new DOMInputCapsule(DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(f), this);
return domIn.readSavable(null, null); return domIn.readSavable(null, null);

Loading…
Cancel
Save