Any chance of instanced meshes that support bones animations?

Hi,

I was wondering if there will be any support for bones animations for instanced meshes soon.

1 Like

Hi,

I do not know if there will be any support for it or not, but FWIW, I think I was able to make this work but I did not notice any performance improvement though. Also, I think you can not play a different animation on an individual model if they are instanced.

I needed to remove the below code, so it will share the same mesh on animated models when cloning the model.

Note that the above change will break the Software Skinning. It only works with Hardware Skinning.

Without instancing:

With instancing:

This is the example code:

import com.jme3.anim.AnimComposer;
import com.jme3.anim.SkinningControl;
import com.jme3.app.SimpleApplication;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.math.*;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.SceneGraphVisitorAdapter;
import com.jme3.scene.Spatial;
import com.jme3.scene.instancing.InstancedNode;

import java.util.ArrayList;
import java.util.List;

public class TestAnimInstancing extends SimpleApplication implements ActionListener{


    // private AnimComposer composer;
    final private String[] animNames = {"Dodge", "Walk", "pull", "push"};
    private final static int SIZE = 20;
    private boolean hwSkinningEnable = true;
    final private List<SkinningControl> skControls = new ArrayList<>();
    private BitmapText hwsText;

    public static void main(String[] args) {
        TestAnimInstancing app = new TestAnimInstancing();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        flyCam.setMoveSpeed(10f);
        flyCam.setDragToRotate(true);
        setPauseOnLostFocus(false);
        cam.setLocation(new Vector3f(38.76639f, 14.744472f, 45.097454f));
        cam.setRotation(new Quaternion(-0.06086266f, 0.92303723f, -0.1639443f, -0.34266636f));

        makeHudText();
 
        DirectionalLight dl = new DirectionalLight();
        dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal());
        dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
        rootNode.addLight(dl);

        Node model = (Node) loadModel();

        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++) {
                Geometry s = (Geometry) model.getChild(0).clone(false);
                model.attachChild(s);
                float x = (i - SIZE / 2) / 0.1f;
                float z = (j - SIZE / 2) / 0.1f;
                s.setLocalTranslation(x, 0, z);
            }
        }

        SkinningControl sc = model.getControl(SkinningControl.class);
        model.removeControl(sc);

        InstancedNode instancedNode = new InstancedNode("oto-instance");
        instancedNode.attachChild(model);
        instancedNode.depthFirstTraversal(new SceneGraphVisitorAdapter() {
            @Override
            public void visit(Geometry geom) {
                geom.getMaterial().setBoolean("UseInstancing", true);
            }
        });
        instancedNode.instance();
        instancedNode.addControl(sc);


        rootNode.attachChild(instancedNode);

        inputManager.addListener(this, "toggleHWS");
        inputManager.addMapping("toggleHWS", new KeyTrigger(KeyInput.KEY_SPACE));

    }

    private Spatial loadModel() {
        Spatial model = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
        model.setLocalScale(0.1f);
        AnimComposer composer = model.getControl(AnimComposer.class);

        composer.setCurrentAction(animNames[1]);
        SkinningControl skinningControl = model.getControl(SkinningControl.class);
        skinningControl.setHardwareSkinningPreferred(hwSkinningEnable);
        skControls.add(skinningControl);
        return model;
    }

    @Override
    public void onAction(String name, boolean isPressed, float tpf) {
        if(isPressed && name.equals("toggleHWS")){
            hwSkinningEnable = !hwSkinningEnable;
            for (SkinningControl control : skControls) {
                control.setHardwareSkinningPreferred(hwSkinningEnable);
                hwsText.setText("HWS : "+ hwSkinningEnable);
            }
        }
    }

    private void makeHudText() {
        guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
        hwsText = new BitmapText(guiFont);
        hwsText.setSize(guiFont.getCharSet().getRenderedSize());
        hwsText.setText("HWS : "+ hwSkinningEnable);
        hwsText.setLocalTranslation(0, cam.getHeight(), 0);
        guiNode.attachChild(hwsText);
    }
}

Hope it helps

2 Likes