Problem with loading TrailMesh from CloneImportExport?

I'm working on some effects and I ran into a problem with loading a clone that has a TrailMesh as one of its components.  The compiler complains of an InstantationException against com.jmex.effects.TrailMesh.  The trailmesh is set up basically like the test case in the jME2 tests.  I have particle effects, for this effect as well as others, that don't cause a problem when loading as a clone, and I thought that I could do the same with TrailMesh.



Basically, the clone is set up like this:



create model

create particlemesh

create trailmesh



attach particlemesh to model trimesh



attach particlemesh to the base node (to be saved with cloneImportExport)

attach trailmesh to the base node

attach model to the base node



save the base node with cloneImportExport – everything appears to save ok



The error erupts when the call is made to:



Node n = (Node)cloneNode.loadClone();



when I want to attach the node to the rootNode and begin working with it.



The relevent part of the error looks like (and WTLab3.java:369 is the loadClone() line above):



executeSFX()… – my code marker

al[0]… – my code marker

Nov 29, 2008 9:25:41 AM com.jme.scene.Node <init>

INFO: Node created.

Nov 29, 2008 9:25:41 AM com.jme.scene.Node <init>

INFO: Node created.

Nov 29, 2008 9:25:41 AM com.jme.scene.Node attachChild

INFO: Child (ps1_mesh) attached to this node (ps1) – "ps1" is the particle system

Nov 29, 2008 9:25:41 AM class com.jme.util.CloneImportExport create(Savable)

SEVERE: Exception

java.lang.InstantiationException: com.jmex.effects.TrailMesh

at java.lang.Class.newInstance0(Class.java:340)

at java.lang.Class.newInstance(Class.java:308)

at com.jme.util.CloneImportExport.create(CloneImportExport.java:347)

at com.jme.util.CloneImportExport.access$400(CloneImportExport.java:76)

at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1448)

at com.jme.scene.Node.read(Node.java:679)

at com.jme.util.CloneImportExport.load(CloneImportExport.java:246)

at com.jme.util.CloneImportExport.loadClone(CloneImportExport.java:304)

at worldtwogs.WTLab3.executeSFX(WTLab3.java:369)





I'm continuing to look into this, but if anyone knows off-hand how to resolve the issue, direction would be greatly appreciated!




Otherwise, run in debug mode and configure your debugger halt on InstantiationException. Check the nested exception. When you have it, try to get to the source cause. At some point there will be an invalid operation (a nullpointerexception, or a indexoutofbounds, or some other problem).



Or post a one class test runnable test case. Maybe we can help.

The test case based on the jmetest for TrailMesh.  The same error is produced.  Relevent code at the end of simpleInitGame (niether the sphere or the trailmesh are attached to the rootNode until after the clone is loaded – but the app doesn't make it that far):



        Node n = new Node("TrailMeshNode");

        trailMesh.setName("trailMesh1");

        n.attachChild(trailMesh);

        n.attachChild(sphere);

        cie.saveClone(n);

       

        // ERROR ??? !!!

        Node x = (Node)cie.loadClone();

       

        TrailMesh tm = (TrailMesh)x.getChild("trailMesh1");

        Node s = (Node)x.getChild("Sphere");

       

        rootNode.attachChild™;

        rootNode.attachChild(s);




//import jmetest.renderer.TestBoxColor;
//import jmetest.renderer.TestEnvMap;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingSphere;
import com.jme.image.Texture;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.Renderer;
import com.jme.scene.Spatial;
import com.jme.scene.Text;
import com.jme.scene.Spatial.CullHint;
import com.jme.scene.Spatial.LightCombineMode;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.CullState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.util.TextureManager;
import com.jme.util.Timer;
import com.jmex.effects.TrailMesh;

import com.jme.util.CloneImportExport;
import com.jme.scene.Node;

/**
 * <code>TestTrailMesh</code>
 *
 * @author Rikard Herlitz (MrCoder)
 */
public class TrailMeshTest extends SimpleGame {
    private Sphere sphere;
    private TrailMesh trailMesh;

    private Vector3f tangent = new Vector3f();

    private boolean updateTrail = true;
    private boolean variableWidth = false;
    private CloneImportExport cie = new CloneImportExport();

    public static void main(String[] args) {
        TrailMeshTest app = new TrailMeshTest();
        app.setConfigShowMode(ConfigShowMode.AlwaysShow);
        app.start();
    }

    protected void simpleUpdate() {
        if (KeyBindingManager.getKeyBindingManager().isValidCommand("1", false)) {
            trailMesh.setFacingMode(TrailMesh.FacingMode.Tangent);
        }
        if (KeyBindingManager.getKeyBindingManager().isValidCommand("2", false)) {
            trailMesh.setFacingMode(TrailMesh.FacingMode.Billboard);
        }

        if (KeyBindingManager.getKeyBindingManager().isValidCommand("3", false)) {
            trailMesh.setUpdateMode(TrailMesh.UpdateMode.Step);
        }
        if (KeyBindingManager.getKeyBindingManager().isValidCommand("4", false)) {
            trailMesh.setUpdateMode(TrailMesh.UpdateMode.Interpolate);
        }

        if (KeyBindingManager.getKeyBindingManager().isValidCommand("5", false)) {
            trailMesh.setUpdateSpeed(trailMesh.getUpdateSpeed() * 2.0f);
        }
        if (KeyBindingManager.getKeyBindingManager().isValidCommand("6", false)) {
            trailMesh.setUpdateSpeed(trailMesh.getUpdateSpeed() * 0.5f);
        }

        if (KeyBindingManager.getKeyBindingManager().isValidCommand("freeze",
                false)) {
            updateTrail = !updateTrail;
        }

        if (KeyBindingManager.getKeyBindingManager().isValidCommand("width",
                false)) {
            variableWidth = !variableWidth;
        }

        if (KeyBindingManager.getKeyBindingManager().isValidCommand("reset",
                false)) {
            trailMesh.resetPosition(sphere.getWorldTranslation());
        }

        // Update the trail front position
        if (updateTrail) {
            // Do some crappy moving around
            float speed = timer.getTimeInSeconds() * 4.0f;
            float xPos = FastMath.sin(speed)
                    * (FastMath.sin(speed * 0.7f) * 80.0f + 0.0f);
            float yPos = FastMath.sin(speed * 1.5f) * 20.0f + 20.0f;
            float zPos = FastMath.cos(speed * 1.2f)
                    * (FastMath.sin(speed) * 80.0f + 0.0f);

            sphere.getLocalTranslation().set(xPos, yPos, zPos);
            sphere.updateGeometricState(0.0f, true);

            // Create a spin for tangent mode
            tangent.set(xPos, yPos, zPos);
            tangent.normalizeLocal();

            // Setup width
            float width = 7.0f;
            if (variableWidth) {
                width = FastMath.sin(speed * 3.7f) * 10.0f + 15.0f;
            }

            // If you use the Tangent mode you have to send a tangent vector as
            // well (spin), otherwise you can just drop that variable like this:
            // trailMesh.setTrailFront(sphere.getWorldTranslation(), width,
            // Timer
            // .getTimer().getTimePerFrame());

            trailMesh.setTrailFront(sphere.getWorldTranslation(), tangent,
                    width, Timer.getTimer().getTimePerFrame());
        }

        // Update the mesh
        trailMesh.update(cam.getLocation());
    }

    protected void simpleInitGame() {
        display.setTitle("TestTrailMesh");

        cam.getLocation().set(new Vector3f(140, 140, 140));
        cam.lookAt(new Vector3f(), Vector3f.UNIT_Y);

        // Create the trail
        trailMesh = new TrailMesh("TrailMesh", 100);
        trailMesh.setUpdateSpeed(60.0f);
        trailMesh.setFacingMode(TrailMesh.FacingMode.Billboard);
        trailMesh.setUpdateMode(TrailMesh.UpdateMode.Step);

        // Try out some additive blending etc
        trailMesh.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
        trailMesh.setCullHint(CullHint.Never);

        TextureState ts = display.getRenderer().createTextureState();
        ts.setEnabled(true);
//        Texture t1 = TextureManager.loadTexture(
//                TestBoxColor.class.getClassLoader().getResource(
//                        "jmetest/data/texture/trail.png"),
//                Texture.MinificationFilter.Trilinear,
//                Texture.MagnificationFilter.Bilinear);
//        ts.setTexture(t1);
        trailMesh.setRenderState(ts);

        BlendState bs = display.getRenderer().createBlendState();
        bs.setBlendEnabled(true);
        bs.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
        bs.setDestinationFunction(BlendState.DestinationFunction.One);
        bs.setTestEnabled(true);
        trailMesh.setRenderState(bs);

        ZBufferState zs = display.getRenderer().createZBufferState();
        zs.setWritable(false);
        trailMesh.setRenderState(zs);

        CullState cs = display.getRenderer().createCullState();
        cs.setCullFace(CullState.Face.None);
        cs.setEnabled(true);
        trailMesh.setRenderState(cs);

        //rootNode.attachChild(trailMesh);
       
       

        // Create a background floor
        Box floor = new Box("Floor", new Vector3f(), 200, 1, 200);
        floor.setModelBound(new BoundingBox());
        floor.updateModelBound();
        floor.getLocalTranslation().y = -20;
        ts = display.getRenderer().createTextureState();
//        Texture t0 = TextureManager.loadTexture(TestEnvMap.class
//                .getClassLoader().getResource("jmetest/data/texture/top.jpg"),
//                Texture.MinificationFilter.Trilinear,
//                Texture.MagnificationFilter.Bilinear);
//        t0.setWrap(Texture.WrapMode.Repeat);
//        ts.setTexture(t0);
        floor.setRenderState(ts);
        floor.scaleTextureCoordinates(0, 5);
        rootNode.attachChild(floor);

        // Create a sphere as trail guide
        sphere = new Sphere("Sphere", 16, 16, 4);
        sphere.setModelBound(new BoundingSphere());
        sphere.updateModelBound();
        ts = display.getRenderer().createTextureState();
        ts.setEnabled(true);
//        t1 = TextureManager.loadTexture(TestBoxColor.class.getClassLoader()
//                .getResource("jmetest/data/texture/post.jpg"),
//                Texture.MinificationFilter.Trilinear,
//                Texture.MagnificationFilter.Bilinear);
//        ts.setTexture(t1);
        sphere.setRenderState(ts);
       
        //rootNode.attachChild(sphere);

        // No lighting for clarity
        //rootNode.setLightCombineMode(LightCombineMode.Off);
        rootNode.setRenderQueueMode(com.jme.renderer.Renderer.QUEUE_OPAQUE);

        // Setup some keys for playing around with settings
        setupKeyBindings();
       
        Node n = new Node("TrailMeshNode");
        trailMesh.setName("trailMesh1");
        n.attachChild(trailMesh);
        n.attachChild(sphere);
        cie.saveClone(n);
       
        // ERROR ??? !!!
        Node x = (Node)cie.loadClone();
       
        TrailMesh tm = (TrailMesh)x.getChild("trailMesh1");
        Node s = (Node)x.getChild("Sphere");
       
        rootNode.attachChild(tm);
        rootNode.attachChild(s);
       
       
    }

    private void setupKeyBindings() {
        KeyBindingManager.getKeyBindingManager().set("freeze", KeyInput.KEY_F);
        KeyBindingManager.getKeyBindingManager().set("width", KeyInput.KEY_G);

        KeyBindingManager.getKeyBindingManager().set("1", KeyInput.KEY_1);
        KeyBindingManager.getKeyBindingManager().set("2", KeyInput.KEY_2);
        KeyBindingManager.getKeyBindingManager().set("3", KeyInput.KEY_3);
        KeyBindingManager.getKeyBindingManager().set("4", KeyInput.KEY_4);
        KeyBindingManager.getKeyBindingManager().set("5", KeyInput.KEY_5);
        KeyBindingManager.getKeyBindingManager().set("6", KeyInput.KEY_6);

        KeyBindingManager.getKeyBindingManager().set("reset", KeyInput.KEY_E);

        Text t = Text.createDefaultTextLabel("Text", "1/2: Tangent/Billboard");
        t.setRenderQueueMode(Renderer.QUEUE_ORTHO);
        t.setLightCombineMode(Spatial.LightCombineMode.Off);
        t.setLocalTranslation(new Vector3f(0, 120, 1));
        statNode.attachChild(t);

        t = Text.createDefaultTextLabel("Text", "3/4: Step/Interpolate");
        t.setRenderQueueMode(Renderer.QUEUE_ORTHO);
        t.setLightCombineMode(Spatial.LightCombineMode.Off);
        t.setLocalTranslation(new Vector3f(0, 100, 1));
        statNode.attachChild(t);

        t = Text
                .createDefaultTextLabel("Text", "5/6: Raise/Lower update speed");
        t.setRenderQueueMode(Renderer.QUEUE_ORTHO);
        t.setLightCombineMode(Spatial.LightCombineMode.Off);
        t.setLocalTranslation(new Vector3f(0, 80, 1));
        statNode.attachChild(t);

        t = Text.createDefaultTextLabel("Text", "F: Freeze");
        t.setRenderQueueMode(Renderer.QUEUE_ORTHO);
        t.setLightCombineMode(Spatial.LightCombineMode.Off);
        t.setLocalTranslation(new Vector3f(0, 60, 1));
        statNode.attachChild(t);

        t = Text.createDefaultTextLabel("Text",
                "G: Enable/Disable variable width");
        t.setRenderQueueMode(Renderer.QUEUE_ORTHO);
        t.setLightCombineMode(Spatial.LightCombineMode.Off);
        t.setLocalTranslation(new Vector3f(0, 40, 1));
        statNode.attachChild(t);

        t = Text.createDefaultTextLabel("Text", "E: Reset position");
        t.setRenderQueueMode(Renderer.QUEUE_ORTHO);
        t.setLightCombineMode(Spatial.LightCombineMode.Off);
        t.setLocalTranslation(new Vector3f(0, 20, 1));
        statNode.attachChild(t);
    }
}

Just a guess: might this be because TrailMesh does not have an empty constructor?

hevee said:

Just a guess: might this be because TrailMesh does not have an empty constructor?

That would do it. Try adding an empty constructor to the TrailMesh class, if that fixes it then post on the contrib board and we'll get that fixed.

Adding



public TrailMesh(){

}



to TrailMesh.java and recompiling seems to work!  Except I have another code problem now, but I don't think its related to this.



I must pull away for a while now, but will return to test this more thoroughly later!



Thanks!

Adding the empty constructor to the class got past it being handled correctly when cloned, however, there is a deeper problem to it being used as a clone – not all of its essential components are saved, most notably the TrailData map.  It does not appear that maps are a savable type for the write/read capsule calls.  I began to experiment with reconstructing the TrailData map after retrieving the clone, but abandoned that angle for now since creating TrailMeshes are relatively inexpensive (so far as I can tell) and so I'm handling their creation/use in a different way now.  That aside, I will revisit this sometime in the near future as there are few other modifications/enhancements that I'd like to see in TrailMesh and so will incorporate those in my personal work and then post the changes here (in contributions) later, if they work out.



Thanks again.