[SOLVED] Attaching spatial to bone through controls

Glad it works :blush:

Ok, but how do I apply an offset to the attached spatial’s translation using the attachment node’s local axis? Do I need to use attachmentNode.localToWorld()?

You can attach an “offsetNode” to the attachment node and the gun in turn should be attached to the offsetNode. With the OffsetNode you can determine perfect translation, rotation etc. and it is easy to use in SceneComposer.

Attaching the gun to the offset node which is attached to an attachment node triggers the “Unsupported Operation Exception”.

Oh yeah forgot about those 2 animation controls. Then you have to create the offset node in the control I posted above for example. You could give translation, rotation etc. as parameters in the constructor. It’s just a thought of me :slight_smile: . Try it and tell me whether it works or not.

I did create the offset node in the control class but the app crashed because an animated spatial (gun) has got an animated (grand)parent (character).

What? You attached the gun to the character somewhere?

I meant to say “to an attachment node” but the end result is the same: the app crashes.

Could you please post the complete strack trace with the line(s) causing the error?

    java.lang.UnsupportedOperationException: Material instances cannot be shared when hardware skinning is used. Ensure all models use unique material instances.
at com.jme3.animation.SkeletonControl.controlRenderHardware(SkeletonControl.java:258)
at com.jme3.animation.SkeletonControl.controlRender(SkeletonControl.java:299)
at com.jme3.scene.control.AbstractControl.render(AbstractControl.java:135)
at com.jme3.scene.Spatial.runControlRender(Spatial.java:756)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:723)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:733)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:733)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:733)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:733)
at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:712)
at com.jme3.renderer.RenderManager.renderViewPort(RenderManager.java:1086)
at com.jme3.renderer.RenderManager.render(RenderManager.java:1145)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:253)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:193)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:232)
at java.lang.Thread.run(Unknown Source)`

package utilities;
import com.jme3.animation.SkeletonControl;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.control.AbstractControl;

public class WeaponAttachmentControl extends AbstractControl {
	
	private final Node attachmentNode;
	private Node offsetNode;
	private Vector3f gunPos = new Vector3f();
	private Quaternion gunRot = new Quaternion();
	
	public WeaponAttachmentControl(Node attachmentNode) {
		this.attachmentNode = attachmentNode;
	}
	
	public WeaponAttachmentControl(SkeletonControl skeletonControl, String bone,Vector3f pos, Quaternion rot,Node rootNode) {
		if (skeletonControl != null && bone != null) {
			attachmentNode = skeletonControl.getAttachmentsNode(bone);
		} else {
			attachmentNode = null;
		}
		offsetNode = new Node("");
		offsetNode.setLocalTranslation(gunPos);
		attachmentNode.attachChild(offsetNode);
		
		gunRot = rot;
	}

	@Override
	protected void controlUpdate(float tpf) {
		if (spatial == null || attachmentNode == null) {
			return;
		}offsetNode.attachChild(spatial);
		Vector3f v = new Vector3f(0,0,0);
		//attachmentNode.localToWorld(gunPos, v);
		spatial.setLocalTranslation(attachmentNode.getWorldTranslation().add(0,0,0));
		spatial.setLocalRotation(attachmentNode.getWorldRotation().mult(gunRot));
	}
	
	@Override
	protected void controlRender(RenderManager rm, ViewPort vp) {
	}
	
}`

Hmm… Can you remove some lines to see which line of code causes the issue.
By the way, you are doing some strange stuff in update I noticed: offsetNode.attachChild(spatial); whould be one.

This was written by @pspeed: two SkeletonControls are modifying the SAME Material instance as one control detects that another control has modified the same material parameter.

Now you are turn. Look what you have done wrong. If you say that the code I provided worked for you, you have to check where you did something wrong afterwards.

attachmentNode.attachChild(offsetNode);

Without this line the app runs ok. I just didn’t exactly understand how I’m supposed to utilize the offset node. Could you perhaps demonstrate to me what you meant with some pseudo-code?

You had a gun node. You were automatically adjusting the position/rotation of the gun node.

When you want an offset node… you stop doing that.

You have an offset node. Your gun node is attached to that. You automatically adjust the position/rotation of the OFFSET node. Then you can adjust the gun’s position relative to that to give it an offset.

Let’s clear this question first: You did not say why you need an offset, so I believe you want to have one because you want to adjust the weapon so that it perfectly fits into the characters hand, is that right ???

Correct.

Here we go, @pspeed just explained it very well !!! Think about it, please.

attachmentNode.attachChild(offsetNode);
Just tried that, does work for me. Sure this is the significant line?

Afterwards I presume I’m supposed to enter something like this into the update method? spatial.setLocalTranslation(attachmentNode.getWorldTranslation().add(offsetNode.getWorldTranslation()));

Would that not create a circle?
Some ‘attach’ to ‘spatial’ should be done in Control.setSpatial or Control.cloneForSpatial only.
It should be done only once and not every frame (method containing ‘tpf’ usually called once per frame).

Question: What Spatial is this Control attached to? The gun or the character that carries the gun?

The Control is attached to the Spatial of the gun.

The point is that the offset values I use are the same to those when I could simply attach gun spatials to attachment nodes. And I’m thinking whether I can make use of them by using some set of methods.