I know this is a relatively new feature introduced by our good buddy kevglass, but I seem to be having some trouble with it and can't seem to make it do my bidding.
I am able to load my model just fine apart from CloneImportExport, but when I modify my code to utilize it I get the following exception:
Exception in thread "Thread-7" java.lang.NullPointerException
at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
at com.jme.util.CloneImportExport$CloneCapsule.write(CloneImportExport.java:566)
at com.jme.scene.SceneElement.write(SceneElement.java:747)
at com.jme.scene.batch.GeomBatch.write(GeomBatch.java:798)
at com.jme.scene.batch.TriangleBatch.write(TriangleBatch.java:229)
at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
at com.jme.util.CloneImportExport$CloneCapsule.writeSavableArrayList(CloneImportExport.java:631)
at com.jme.scene.Geometry.write(Geometry.java:762)
at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
at com.jme.util.CloneImportExport$CloneCapsule.write(CloneImportExport.java:553)
at com.jme.scene.SharedMesh.write(SharedMesh.java:561)
at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
at com.jme.util.CloneImportExport$CloneCapsule.writeSavableArrayList(CloneImportExport.java:631)
at com.jme.scene.Node.write(Node.java:657)
at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
at com.jme.util.CloneImportExport$CloneCapsule.writeSavableArrayList(CloneImportExport.java:631)
at com.jme.scene.Node.write(Node.java:657)
at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
at com.jme.util.CloneImportExport$CloneCapsule.writeSavableArrayList(CloneImportExport.java:631)
at com.jme.scene.Node.write(Node.java:657)
at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
at com.jme.util.CloneImportExport$CloneCapsule.writeSavableArrayList(CloneImportExport.java:631)
at com.jme.scene.Node.write(Node.java:657)
at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
at com.jme.util.CloneImportExport$CloneCapsule.writeSavableArrayList(CloneImportExport.java:631)
at com.jme.scene.Node.write(Node.java:657)
at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
at com.jme.util.CloneImportExport.saveClone(CloneImportExport.java:243)
at com.captiveimagination.gb.spatial.Ship.initCollada(Ship.java:76)
at com.captiveimagination.gb.spatial.Ship.<init>(Ship.java:24)
at com.captiveimagination.gb.state.SectorOne.initScene(SectorOne.java:43)
at com.captiveimagination.gb.state.SpaceGameState.init(SpaceGameState.java:67)
at com.captiveimagination.gb.state.SpaceGameState.<init>(SpaceGameState.java:40)
at com.captiveimagination.gb.state.SectorOne.<init>(SectorOne.java:25)
at com.captiveimagination.gb.state.MenuGameState$2.run(MenuGameState.java:269)
at java.lang.Thread.run(Thread.java:595)
The following code is what I execute in order to get this fun exception:
private void initHull() throws IOException {
if (cie == null) {
URL url = getClass().getResource(SHIP_RESOURCE);
Node model = (Node)BinaryImporter.getInstance().load(url.openStream());
cie = new CloneImportExport();
cie.saveClone(model);
}
attachChild((Spatial)cie.loadClone());
setLocalScale(0.2f);
}
Yes, I am invoking this in another thread, but I've tried interjecting directly into the OpenGL thread and it made no difference. It is the "cie.saveClone(model);" line that is causing the exception to occur. Any insights as to what I'm doing wrong or a bugfix would be greatly appreciated. ;)
There is a “Bug” in kevglass’ cloner, it uses a simple Hashmap, so if u clone a tree with nodes which want to save attribs with the same name, they will be overriden…
I just gave the code you posted a quick test, and got the same problem with a model of mine. After some quick debugger inspection, I think that it may be safe to just check for null in the method the first line of your stacktrace references:
at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
I believe that it should not provoke any problems to just check for null before calling that save method. Of course I could always be totally wrong, because I am not really aware of what I'm talking about here. Then again, I might be right just as well. It's "take it or leave it", I suppose...
Well, from the little testing I had done it looked like the problem was an empty (null) entry somewhere in the Node's RenderStates, which I thought would be perfectly safe to ignore, as Nodes inherit RenderStates they don't have anyway. But as I said, I am possibly missing something important somewhere.
If I add a null check that simply returns false in the save(Savable) method in CloneImportExport at line 195 I get the following:
java.lang.NullPointerException
at com.jme.util.CloneImportExport.create(CloneImportExport.java:315)
at com.jme.util.CloneImportExport.access$6(CloneImportExport.java:313)
at com.jme.util.CloneImportExport$CloneCapsule.readSavableArray(CloneImportExport.java:1183)
at com.jme.scene.SceneElement.read(SceneElement.java:765)
at com.jme.scene.batch.GeomBatch.read(GeomBatch.java:812)
at com.jme.scene.batch.TriangleBatch.read(TriangleBatch.java:236)
at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
at com.jme.scene.Geometry.read(Geometry.java:769)
at com.jme.util.CloneImportExport$CloneCapsule.readSavable(CloneImportExport.java:1157)
at com.jme.scene.SharedMesh.read(SharedMesh.java:568)
at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
at com.jme.scene.Node.read(Node.java:663)
at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
at com.jme.scene.Node.read(Node.java:663)
at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
at com.jme.scene.Node.read(Node.java:663)
at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
at com.jme.scene.Node.read(Node.java:663)
at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
at com.jme.scene.Node.read(Node.java:663)
at com.jme.util.CloneImportExport.load(CloneImportExport.java:221)
at com.jme.util.CloneImportExport.loadClone(CloneImportExport.java:276)
at com.captiveimagination.gb.spatial.Ship.initHull(Ship.java:79)
at com.captiveimagination.gb.spatial.Ship.<init>(Ship.java:25)
at com.captiveimagination.gb.state.SectorOne.initScene(SectorOne.java:43)
at com.captiveimagination.gb.state.SpaceGameState.init(SpaceGameState.java:67)
at com.captiveimagination.gb.state.SpaceGameState.<init>(SpaceGameState.java:40)
at com.captiveimagination.gb.state.SectorOne.<init>(SectorOne.java:25)
at com.captiveimagination.gb.state.MenuGameState$2.run(MenuGameState.java:269)
at java.lang.Thread.run(Thread.java:595)
Exception in thread "Thread-7" java.lang.RuntimeException: java.lang.RuntimeException: java.lang.NullPointerException
at com.jme.util.CloneImportExport.load(CloneImportExport.java:226)
at com.jme.util.CloneImportExport.loadClone(CloneImportExport.java:276)
at com.captiveimagination.gb.spatial.Ship.initHull(Ship.java:79)
at com.captiveimagination.gb.spatial.Ship.<init>(Ship.java:25)
at com.captiveimagination.gb.state.SectorOne.initScene(SectorOne.java:43)
at com.captiveimagination.gb.state.SpaceGameState.init(SpaceGameState.java:67)
at com.captiveimagination.gb.state.SpaceGameState.<init>(SpaceGameState.java:40)
at com.captiveimagination.gb.state.SectorOne.<init>(SectorOne.java:25)
at com.captiveimagination.gb.state.MenuGameState$2.run(MenuGameState.java:269)
at java.lang.Thread.run(Thread.java:595)
Caused by: java.lang.RuntimeException: java.lang.NullPointerException
at com.jme.util.CloneImportExport.create(CloneImportExport.java:322)
at com.jme.util.CloneImportExport.access$6(CloneImportExport.java:313)
at com.jme.util.CloneImportExport$CloneCapsule.readSavableArray(CloneImportExport.java:1183)
at com.jme.scene.SceneElement.read(SceneElement.java:765)
at com.jme.scene.batch.GeomBatch.read(GeomBatch.java:812)
at com.jme.scene.batch.TriangleBatch.read(TriangleBatch.java:236)
at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
at com.jme.scene.Geometry.read(Geometry.java:769)
at com.jme.util.CloneImportExport$CloneCapsule.readSavable(CloneImportExport.java:1157)
at com.jme.scene.SharedMesh.read(SharedMesh.java:568)
at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
at com.jme.scene.Node.read(Node.java:663)
at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
at com.jme.scene.Node.read(Node.java:663)
at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
at com.jme.scene.Node.read(Node.java:663)
at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
at com.jme.scene.Node.read(Node.java:663)
at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
at com.jme.scene.Node.read(Node.java:663)
at com.jme.util.CloneImportExport.load(CloneImportExport.java:221)
... 9 more
Caused by: java.lang.NullPointerException
at com.jme.util.CloneImportExport.create(CloneImportExport.java:315)
... 29 more
One null after another…ouch…either my model has serious issues or there are deeper problems with this class in certain circumstances.
I can send you the model I'm using if you would be willing to investigate further? Everytime I add a null check another null appears somewhere else and I'm stuck in a place where I don't understand what it's doing well enough to react to a null properly.
Not trying to seem ungrateful for this great class…I just want to be able to use it.
...
Everytime I add a null check another null appears somewhere else and I'm stuck in a place where I don't understand what it's doing well enough to react to a null properly.
...
...
Everytime I add a null check another null appears somewhere else and I'm stuck in a place where I don't understand what it's doing well enough to react to a null properly.
...
try adding breakpoints instead of null ckecks :P
Too bad it's not logic. :P
I know what the problem is, the class simply doesn't handle it properly. My model has null "things" inside it and the class wasn't designed to handle it.
Great, thanks a lot kevglass, I'll give it a shot this evening and see if it solves my problems.
You know, I almost wish that auto-boxing/un-boxing would convert null to/from 0 for numeric values. Since you're obviously trying to get a primitive and nulls don't exist in primitives if you didn't already add your own checking for null it seems logical you'd just want it to assign 0 to it. At the same time that seems wrong to have the system convert a null to a 0 for you.
You know, I almost wish that auto-boxing/un-boxing would convert null to/from 0 for numeric values
WARNING, this comment is pure opinion, and is not intentded to cause offence.
Eugh. No. for the love of <insert religious figure here>. No. Please shoot anyone who suggests that again :)
I hate that, if it's null, and I try to return it as something that can't be null, tell me, don't hide it from me and pretend it was 0. JDBC is the perfect example. If it's null, it probably means 'no value provided', if it's 0, it might mean, erm, 0 :)
Return me the null, it's my lookout (now I know about it) to decide what to do if it's null :).
WARNING this comment is absolute truth and anyone who disagrees shall be burned until they change their mind!
hehe, I was about to say the same thing before I read your last post kevglass. The only valid reason I could see against it is as a "check" for people who aren't really sure what they're writing and don't intend to actually autobox. If that's the case, why not go ahead and check for null and then add your .floatValue() instead? The purpose of autoboxing is convenience and if I know what I'm doing and don't care about nulls since I want a primitive anyway, then give me the convenience of casting and let it become 0 for me.
@Endolf, you borderline purist! What are you doing using autoboxing in the first place? Go back and hardcode your checks and conversions in the same place and you won't have any problem.
@Endolf, you borderline purist! What are you doing using autoboxing in the first place? Go back and hardcode your checks and conversions in the same place and you won't have any problem. :P
SmallTalk rules!!! ;)
Get rid of these nasty primative things, *everything* should be an object :)