[SOLVED] Attaching spatial to bone through controls

I’ve read in this thread https://hub.jmonkeyengine.org/t/solved-problem-with-attaching-animated-model-to-bone-attachmentnode/36542/6 that I should instead of using .getAttachmentNode().attachChild() utilize custom controls to achieve the same goal.

Am I supposed to keep the spatial at the rotation and translation of the desired bone in controlUpdate(float arg0)? If not, how?

Thanks in advance.

1 Like

If you decide to use the control to keep the spatial updated then yes you need to keep track of the bone translation and rotation. You would have to pass the bone as an argument in the control’s constructor.

If you use the attachment node, you don’t need to keep track.

The reason why it says in that thread to use a control is because using an attachment node is kinda buggy when hardware skinning is enabled. You can always use software skinning instead but you lose some performance gain.

I want to use a control to attach a gun to a person’s hand but I guess I’m doing it wrong because when the person jumps, the gun doesn’t exactly move with the bone of the hand. I use the world translation of the bone’s attachment node.

Did you make sure that the control is attached to your gun?

Edit: also showing us some code would help us to see what you are doing.

public void equip(int number) {
		AttachmentNodeControl control = new AttachmentNodeControl();
		control.setBonePos(meshRigControl.getAttachmentsNode("handIK.R").getWorldTranslation());
		control.setBoneRot(meshRigControl.getAttachmentsNode("handIK.R").getWorldRotation());
		String suffix = "";
		if (crouched || prone) {
			if (crouched)
				suffix = "Crouched";
			if (prone)
				suffix = "Proned";
		} else
			suffix = "";
		equiping = true;
		try {
			meshRigControl.getAttachmentsNode("handIK.R").detachAllChildren();
			meshRigControl.getAttachmentsNode("handIK.L").detachAllChildren();
			meshRigControl.getAttachmentsNode("tanks").detachAllChildren();
		} catch (Exception e) {
		}
		weapons[number]
				.getMesh()
				.setLocalTranslation(
						WeaponData.arrayOfGunPos[weapons[number].getWeaponId()][characterNumber]);
		weapons[number]
				.getMesh()
				.setLocalRotation(
						WeaponData.arrayOfGunRot[weapons[number].getWeaponId()][characterNumber]);
		rootNode.attachChild(weapons[number].getMesh());
		/*
		 * meshRigControl.getAttachmentsNode(
		 * WeaponData.bone[weapons[number].getWeaponId()]).attachChild(
		 * weapons[number].getMesh());
		 */
		weapons[number].getMesh().addControl(control);
		handsChannel.setAnim("equiping"
				+ weaponsList[weapons[number].getWeaponId()] + suffix);
		handsChannel.setLoopMode(LoopMode.DontLoop);
	}

Should I post more code?

The reason that other thread had to match things up with a control was because the attached thing was animated. That confuses the animation system to have one skeleton control for the whole hierarchy.

If the thing you are attaching isn’t skeleton animated then you shouldn’t need to worry about that. Just attach the gun to the attachment node like everyone else does.

The gun is animated thus I need a custom control.

Then you still use an attachment node… you just push its world translation, world rotation, and world scale onto your gun in controlUpdate().

(or controlRender(), I think would work too… and would run after all of the other controls have done controlUpdate() I believe.)

Isn’t this the same as simply setting the local translation and rotation of the gun to be that of the attachment node? Because either way if I move the mesh to which I attach another spatial, the child spatial appears to be behind.

Well, I can’t see where you are doing that as you haven’t shown the code for your control.

I only see where you are doing weird stuff like: control.setBonePos(meshRigControl.getAttachmentsNode(“handIK.R”).getWorldTranslation()); control.setBoneRot(meshRigControl.getAttachmentsNode(“handIK.R”).getWorldRotation());

…which is very strange. Like, I don’t know why you don’t just set the node instead of praying that those Vector3f/Quaternion instances don’t change underneath you. Very bad practice to save these and rely on them when you can just grab them again any time from the node itself.

But anyway, you may be becoming victim to update ordering issues. Now is the time where some debugging will come into play. Either step through the code or put some printlns in to see what order things are running. That’s why I suggested doing the position update on controlRender() instead of controlUpdate().

Hello @domas
If you want to attach a gun to the players hand, I recommend to create an “attachmentBone” for your model in blender. All you have to do is open your model in Blender, select a bone near the hand and press e to extract a bone. It neither matters in which direction the bone is stretched nor how long it is. Now, rename that bone to something like “AttachmentBone”. Lastly export your model with ogre, convert it and attach your weapon to that new bone.

I made it this way and it worked perfectly. The problem when you attach the gun directly to the hand bone for example might be that the gun for different animations appears at different locations. If you have one bone which local transform is not changed by animations you can be sure the gun appears at the same location for all animations.
Hopefully I could help you!

Was your gun animated?

The issue here is that if you attach an animated object to be the child of another animated object then JME’s animation system gets confused.

Ups, I have overlooked that. No, the weapon wasn’t animated.
Is it possible to create a control which refreshes translation, rotation, scale of the gun by getting the transformation via “AttachmentBone”. The gun would not be attached to “AttachmentNode” then. It should be attached to root node or so.

Yes, the gun is animated.

Hi, no offense… but have you even read the thread?

The whole thread is talking exactly about a control to do this. Literally, 95% of the text here is talking about exactly that.

Sorry guys, I read the thread too quickly. Will never happen again, I promise :grinning:

@domas could you post the whole control class here? I have to agree with @pspeed, some lines are very strange.

Another question: Why is the gun animated? Does it have a reload, shoot animation etc.? What does the hand model do then?

I’ll post it when I come home.

The gun has got its own animations and so the character model to which I attach the gun does.

Hey @domas, I quickly wrote a test app for you. It just shall show you how to place your gun to the players hand. It might be that you have to adjust your gun with an extra node, so that the gun fits perfectly into the hands.

Here the TestApp:

public class Main extends SimpleApplication {

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

	@Override
	public void simpleInitApp() {
		cam.setLocation(Vector3f.UNIT_Z);

		Spatial player = assetManager.loadModel("Models/Player/Player.j3o");
		rootNode.attachChild(player);

		AnimControl animControl = player.getControl(AnimControl.class);
		AnimChannel channel = animControl.createChannel();
		
		Collection<String> anims = animControl.getAnimationNames();
		Iterator i = anims.iterator();
		channel.setAnim((String) i.next());
		
		animControl.addListener(new AnimEventListener() {
			@Override
			public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
				if (i.hasNext()) {
					channel.setAnim((String) i.next());
				}
			}

			@Override
			public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {}
		});

		SkeletonControl skeletonControl = player.getControl(SkeletonControl.class);
		Bone attachmentBone = skeletonControl.getSkeleton().getBone("YOUR_ATTACHMENT_BONE");

		WeaponAttachmentControl control = new WeaponAttachmentControl(skeletonControl, attachmentBone.getName());

		Spatial gun = assetManager.loadModel("Models/cube.j3o");
		gun.scale(0.1f);
		gun.addControl(control);
		rootNode.attachChild(gun);

	   
		AmbientLight ambient = new AmbientLight();
		ambient.setColor(ColorRGBA.White);
		rootNode.addLight(ambient);
	}

	@Override
	public void simpleUpdate(float tpf) {
	}

	@Override
	public void simpleRender(RenderManager rm) {
	}

}

Here the simple control:

public class WeaponAttachmentControl extends AbstractControl {
	
	private final Node attachmentNode;

	public WeaponAttachmentControl(Node attachmentNode) {
		this.attachmentNode = attachmentNode;
	}
	
	public WeaponAttachmentControl(SkeletonControl skeletonControl, String bone) {
		if (skeletonControl != null && bone != null) {
			attachmentNode = skeletonControl.getAttachmentsNode(bone);
		} else {
			attachmentNode = null;
		}
	}

	@Override
	protected void controlUpdate(float tpf) {
		if (spatial == null || attachmentNode == null) {
			return;
		}
		spatial.setLocalTranslation(attachmentNode.getWorldTranslation());
		spatial.setLocalRotation(attachmentNode.getWorldRotation());
	}
	
	@Override
	protected void controlRender(RenderManager rm, ViewPort vp) {
	}
	
}

If I were you, I would create several controls, one for keeping the gun at the correct position, one to take care of animations, and so on …

It should be no problem to execute animations of the gun anymore.

If you are missing something, feel free to tell us.

Greetings, Domenic

It works perfectly now!

Many thanks! :slight_smile:

1 Like