How to change a mesh through an another but leave the same attachment to bones?

I design the architecture for my game where the main character should have an ability to change weapons, shields, helmets and armors (they protect only body – not feet and hands). There are no problems with the shields, swords and helmets. They are created as separate .OBJ files. When the model of the protagonist is loaded – I attach the weapon-Node to a special bone on the right hand and the shield to the special bone on the left hand. The helmet is attached to the scull-bone directly. Everything is good and I can replace these nodes when the user wants to take a new weapon/shield/helmet.

But I cannot find a way to attach a armor to the players-armature. The armor cannot be attached to a single bone – it is attached to the whole spine and some other bones (collarbones, thighbones). The armor is deformable.

I found only two ways.

Variant 1:

Create for every new armor a new 3D model of the protagonist with the already dressed armor. When the user wants to replace the body armor – I need to replace the whole 3D model and attach the weapon, shield and sword again.

Variant 2:

  1. I create the animated model of the protagonist with a default armor (armorOriginal);

  2. I export the whole model in JME3 using .GLTF-format.

  3. I import the model of the protagonist inside JME3

  4. I create another armor at same place of the coordinate system in Blender (armorNew).

  5. I export this body armor as a separate .GLTF.

  6. I import the model of the body armor inside JME3.

When I need to replace the body armor in the game-code, I do:

  1. I found the node of the default armor (armorOriginal).

  2. I extract vertexData of the original armor.

  3. I attach this vertex data to the new armor (armorNew).

  4. I attach the armorNew to the protagonist

  5. I remove from parent the armor original.

Result: it works only, if the both armors have similar dimensions/meshes. When one of the meshes if large but the second is small – I receive the graphical bugs on the second. I think, it must exist another way to attach a node to the armature in according to the changed dimensions/mesh. I show the video what happens if the body armors have different meshes:

Kingdom of the dead warlock 2025-04-28 15-02-51 (online-video-cutter.com) (1)

I show also the SceneComposer windows of the both models on the next picture.

The code is below. It replaces the body armor every 1500 ms. The most interesting method is private void changeArmorVisibility(boolean toHide):

public class DesktopJmeSwapBodyArmor extends SimpleApplication implements WrapperLauncher {

    private Node player;
    private Node armorOriginal, armorNew;
    private ArmorManager armorManager;
    private Geometry lowLevelOriginalArmorGeometry, lowLevelNewArmorGeometry;   //not used
    private Mesh armorOriginalMesh, armorNewMesh;
    private boolean onceApplied = false;

    public static void main(String[] args) {
        var app = new DesktopJmeSwapBodyArmor();
        app.setSettings(createSettings());
        app.setShowSettings(false);
        app.start(); // start the game
    }

    @Override
    public void simpleInitApp() {
        GlobalVariables.initEngine(this);
        loadScene();
        loadPlayer();
        setCameraParams();
        armorManager = new ArmorManager(1500);
        findOriginalArmorMesh();
        findNewArmorMesh();
    }

    private void findNewArmorMesh() {
        var armor = (Node)armorNew.getChild("Armor_2");
        var armorMesh = (Node)armor.getChild("Armor_mesh.001");
        var armorMesh0 = (Geometry)armorMesh.getChild("Armor_mesh.001_0");
        var mesh = armorMesh0.getMesh();
        this.lowLevelOriginalArmorGeometry = armorMesh0;
        this.armorNewMesh = mesh;
    }

    private void findOriginalArmorMesh() {
        var armature = (Node)player.getChild("Armature");
        var armor = (Node)armature.getChild("Armor");
        var armorMesh = (Node)armor.getChild("Armor_mesh");
        var armorMesh0 = (Geometry)armorMesh.getChild("Armor_mesh_0");
        var mesh = armorMesh0.getMesh();
        this.lowLevelOriginalArmorGeometry = armorMesh0;
        this.armorOriginalMesh = mesh;
    }

    private void setCameraParams() {
        flyCam.setMoveSpeed(flyCam.getMoveSpeed() * 25);
        var cameraPos = getCamera().getLocation();
        cameraPos.y += 15;
        cameraPos.z += 25;
        cameraPos.x += 0;
        getCamera().setLocation(cameraPos);
    }

    private void changeArmorVisibility(boolean toHide) {
        if (toHide) {
            if (!onceApplied) {
                var clonedData = armorOriginalMesh.cloneForAnim();
                onceApplied = true;
                armorNewMesh.extractVertexData(clonedData);
            }
            armorOriginal.removeFromParent();
            player.attachChild(armorNew);
            Logger.debug("Original long brown armor detached. Short red is attached.");
        }
        else {
            armorNew.removeFromParent();
            player.attachChild(armorOriginal);
            Logger.debug("Short red is detached. Original long brown armor attached.");
        }
    }

    private void loadPlayer() {
        String path = WrapperPathsCreationUtil.getPath(false, false, "Models", "Player", "Player.j3o");
        player = (Node) assetManager.loadModel(path);
        ((Node) rootNode.getChild(0)).attachChild(player);
        enableAnimation(player);
        armorOriginal = (Node) player.getChild("Armor");
        String pathToNewArmor = WrapperPathsCreationUtil.getPath(false, false, "Models", "Armor", "Armor.j3o");
        armorNew = (Node) assetManager.loadModel(pathToNewArmor);
    }

    private void enableAnimation(Node player) {
        var composer = player.getControl(AnimComposer.class);
        composer.setCurrentAction("ATTACK_1");
    }

    private void loadScene() {
        String path = WrapperPathsCreationUtil.getPath(false, false,  "Scenes", "Level_-1_test.j3o");
        var scene = (Node) assetManager.loadModel(path);
        rootNode.attachChild(scene);
    }

    @Override
    public void update() {
        super.update();
        armorManager.update();
    }
}

ArmorManager is a simple nested class, which has only one timer (see below):

private class ArmorManager{

        private final MgdsTimer timer;
        private final int time;
        private boolean visibleOriginalArmor = true;

        private ArmorManager(int time) {
            this.time = time;
            timer = new MgdsTimer(time);
        }

        private void update(){
            if (timer.isTime()){
                timer.setNewTimer(time);
                visibleOriginalArmor = !visibleOriginalArmor;
                changeArmorVisibility(!visibleOriginalArmor);
            }
        }
    }

Help me please to find a right way to replace the meshes to save adequate animation possibility. Or maybe replacing the whole 3D-model is a single right way to do that?

I think first of all, the armor will still have to ‘fit’ the character. Often I guess shape keys can be used to temporarily shrink the character’s skin under the armor to prevent bleed through when it bends.

If all of the parts of a model share the same armature then the same AnimComposer should control all of the children. (There may be a little extra fix-up to do when adding a new child to let the main model know about the new mesh.)

When a normal character is exported from Blender, it is quite common for it to already be multiple Geometry+meshes and the anim composer can still animate them. So there is definitely a way to do it.

I’m pretty sure there are users here who have done swappable armor.

1 Like
  1. Of course. The model you can see on the video has a white shirt which must not exist. The “Ready-to-use” model will have only a thin cylinder at the place of the spine-bones. The protagonist should have no body but the mesh with the default brown shirt. And he has this brown shirt which is imported normally (see the picture with the orange line and the subscription: “original default armor mesh”.
    _ Player_with_short_shirt C__GD_Workspace_Kingdom_of_warlock_materials_3d_Player_Player_with_short_shirt.blend - Blender 4.4.1 2025-04-29 10-05-58

  2. Of course I animated the both (brown and red shirts) in Blender. They are attached to same armature. But I think, I should not export the red mesh with the copy of the armature. I think, it is a bad idea. That is why I export only the red shirt and try to attach it to the original spine.

  3. I have also one mode idea: export the character with all possible armors and remove every except on, which is actually put on the protagonist. But I think it is the most worse variant from all.

Of course. Good luck with your game.

Thanks!

This old thread may be helpful: How to add clothing to character model - General Discussion - jMonkeyEngine Hub

After the reading of the post under your link I have understood - the best way is to make all the armors inside the same .BLEND-file and attach them in Blender and export pairs of the models (same body + new bodyArmor) in JME3. In my game I don’t have combinations of flexible wears (no changeable trousers, skirts – only body armors). Sword, shield and helmet can be attached to single bones from the code without deformation ability (see post: post with attaching of a weapon and shield)

1 Like

IIrc, i had a similar issue - i approached it by:

a) keeping separate models for each armor piece
b) weight painting (in blender theres a way to copy → paste weight data, dont remember specifically how). If you want to weight paint from code then you should take a look at Mesh buffers.

@Msnail Does it mean - your armor contained many separate pieces?

I think it will be too hardcore for me to paint weight for every single piece - I would prefer to attach every piece of the armor to a single bone with overlapping some meshes (for single solid armor pieces).

Fortunately any armor in my game is a single mesh which can be automatically attached to bones without detailed fine adjustment. I think it is enough for me. Thanks for your experience.

 Does it mean - your armor contained many separate pieces?

Consider for example chestplate A and B and a character wearing it:

  1. the skeleton of the character wearing the chestplate has a bone spine
  2. chestplates A and B have different models, each with their separate weight paint (in blender, vertex group called “spine” to match target bone name)

When you equip a chestplate, its attached to character node and hence it is automatically deformed when an animation is played on the character

2 Likes