Creating moving parts in blender that will move in response to user input in 3JS

This should be a basic question, but I have looked everywhere and cannot find an answer. Perhaps I am using the wrong search terms.

I have created a small aircraft in Blender and want to make various parts user-movable in 3JS, such as ailerons, elevator, rudder. It just so happens that they are on vertical and horizontal planes and I have placed the axes at the hinge point, so I should not need bones. Also the model is exported as a gltf/glb file, so animation should be fully supported.

I have previously created an animated engine that rotates in Blender and also automatically rotates in 3JS. But I don’t want these parts to automatically rotate, but to rotate only in response to user inputs (or other 3JS program variables).

Thanks,
Phil

i wouldnt export this as an animation. especially if its interactive. your axes and hinges are just rotation groups, use lerping, gestures, gsap, springs or anything you like to manipulate these with code: https://codesandbox.io/s/r3f-contact-shadow-q23sw?file=/src/Model.js:392-662 (im using react here, but it’s the same principle, i added a hinge-group in blender, exported it as gltf, i change the rotation on user-input.)

1 Like

As @drcmda mentioned above, simple TRS animations can be easily performed in 3js without the need for keyframe track animation from other 3D tools.

However if you still want to do it the Blender way then:

I have previously created an animated engine that rotates in Blender and also automatically rotates in 3JS. But I don’t want these parts to automatically rotate, but to rotate only in response to user inputs

remember you can freely control the animations through AnimationAction. For example you can prepare your animation like in the docs, but call action.play() and action.stop() only on user inputs, etc. Animations in Three.js do not play automatically.

1 Like

Sorry for the delayed response. I caught a bit of a fever after my shingles vaccine and was having dreams which fixated on (non-existent) changes I needed to make to my program.

One reason I was trying to work within Blender was that, when I saved the part separately, the file size was about the size of the entire aircraft. I later realized that this was because I am saving in glb format - the variation of glft which bundles the entire texture with the mesh.

So what I think I need to do is save the aircraft and the parts in glft format so they can all reference a shared texture file.

Once that is done, I should be able to use a fairly simply routine to position the part (which is what I have done to position spinning propellers on my aircraft.)

If that doesn’t work, it sounds like I should use react or a similar program to get the job done. I am surprised that Blender does not include the capability. But I guess that is not a primary function of the program.

Thanks. That’s an interesting possibility. To make it work, I would also need commands to change the speed of the animation, to continue the animation, and to reverse the animation. I’m not sure if that is all possible. But it helps to know that there is not an easier way that I am somehow overlooking.

If that doesn’t work, it sounds like I should use react or a similar program to get the job done. I am surprised that Blender does not include the capability. But I guess that is not a primary function of the program.

blender can most certainly run animations, but a timeline animation is often the wrong tool for interaction. nor is it easier to make and control - it can be harder. but it depends on your actual use-case. im just saying, if i had a lever that the user is supposed to pull up and down, that’s nothing more than a rotation node in blender and a gesture in javascript.

1 Like

A rotation node is probably exactly what I am looking for. Do you know how to access or manipulate those nodes in 3JS?

Hi,
I faced exactly the same issue a while ago, and this is likely to be exactly what you’re looking for.
My goal was to design once in Blender to avoid any tweaking in text files of js. Then connect a parameter to any sort of input, in the demo it’s a gui.dat but my code simply listen to events.
I had to fallback on Custom Properties due to a limitation but that was a while ago (5 Months ago).

I tried to make these params generic, not only rotation or movement but also colors and light,…
here is the full github repo with examples and its readme with gif demos and links to live demos

as a hint, think of having your animation linear in Blender.

the usage is event based :

	let controller_pull = gui.add(config, 'pull',0,1);
	controller_pull.onChange(value => {send_custom_event("three_param",{name:"Axis", param:"pull",val:value})});

2 Likes

you just give it a name and it will be a generic object3d or a group in the gltf export. alter the rotation property and that’s that.

1 Like

That is correct. But doing it that way you’ll have to define for example the rotation limits and axis in text mode or hard-coded in js. The advantage of exporting an animation is to allow the 3D designer to define the extent of the motion and the developer to connect it to any sort of user input.
Which method is easier depend on the scalability of the project. For a simple example I’d go with your suggested method.

1 Like

The aircraft that I save in glb/gltf is composed of several parts. Does 3JS load a list of part names and references when it loads the file? Or do I have to parse the file to do that?

It sounds like you are saying that, once the file is loaded, I can access the parts separately. What command do I use to do that? Something like Object.part.rotation.x = value? (I tried that specific command, but it didn’t work.)

Thanks!

This approach looks like it could be pretty useful.

Currently, I am saving animated parts in separate files and using routines to position them on the aircraft. However, for some reason, with this particular part (the elevator), I am having trouble rotating it correctly along all 3 axes. This is odd because I have been able to attach and rotate a transparent propeller disc to the front. It rotates only on the Z-axis (bank). The elevator rotates only on the X-axis (pitch). But it is vertical of the centerline - so that may be contributing to the problem.

In any case, the animation could help me avoid the mathematical challenges of correctly positioning and rotating the part within the scene. I will give it a try.

That’s exactly what I design this for, so that the position and motion (in my case light bulbs), is done in a professional CAD tool like blender and not wasting my time in adjustments of things I need a launch loop to see or develop an edit tool to modify.

Also, another hint regarding your comment on separating different parts. You know best what is more practical to your use case, but I do not see the need of separating the parts, that might get more complicated. GLTF is a standard made to combine a whole scene and should handle all their hierarchical positioning. I know in 3D libraries it’s not always easy, but GLTF in three.js and Blender do have exactly the same hierarchy.
An example screenshot below from a scene in blender with Collection and the reversed triangle is a Frame.
scene_in_blender

And once in Three.js you have two powerful method to get you objects back
scene.getObjectByName() if you already hardcoded or configured your object names, or even nicer, an exploration loop where you could discover all sort of items in your scene and manage them accordingly, note in the example the usage of the userData that provides you back what you configured as Custom Properties in Blender :

I you have any issue or question, let me know I’ll help you.

1 Like

Okay, I may be doing this all wrong, but here is what I have done:

I created a keyframe animation in Blender named: tailHE_anim
This rotates the elevator on the X-axis from -24deg to +24deg.
It is a linear progression as you described with a range of 0 to 60.

In my program, when I loaded the file with the animation, I included the following steps:
mixer = new THREE.AnimationMixer(ACObj0);
clip1 = gltf.animations[0];
action1 = mixer.clipAction(clip1);

I assume that action1 is the address to the animation?

What do I do now?

Then you can activate your animation whenever you want with action1.play() or stop it with action1.stop(). Have a look at all available control methods in AnimationAction docs, and especially at .timeScale.

Lastly, to make sure that three.js updates your animation every frame, use
mixer.update( deltaSeconds )
in your animation loop.

1 Like

in your case, I assume that you do not want the plane tails to be moving by themselves with time in that case you should not be calling .play() and .stop(). Instead, you’d like to move the tail to a given position that is decided by the user given a certain input. That input, let’s say in my example is between 0 and 1, will be mapped to the animation duration.
For that you could call mixer.setTime()
The example below is the code from the running example, I showed on the gif animation

An additional hint, I planned the input value to be between 0 and 1. For some reason setting the value 1 if mapped to the full duration would restart the animation from the beginning, therefore the patch on line 420 to lock the position at the end of the animation.

1 Like

Exactly! That’s what I want. So calling mixer.setTime() should be all I have to add to my code?

I couldn’t get the animation to work on that program (who knows why?), but was able to get it to working on another. I tried adding:
mixer.setTime(.5);
but that didn’t seem to have an effect on the file. It sounds like it should have set the animation at midpoint?

EDIT: I got the animation to working on the target program (I needed to add the clock information). So if I include the command action1.play(), the animation will play.
And I realized that the mid-point value for mixer.setTime(t) should be 1.25 seconds (1/2 of 60 frames / 24 fps). But I was still unable to move the elevator to a specific location.

It might be due to missing initializations. Also the time should be mapped over the animation durarion. I don’t see what you are referring to by “that” program. What I can do is review your code, either the original one, or a minimal sample on which you reproduce the issue.
I’m sure you’re pretty close, and with this technique you can scale up to dozens of parameters without touching any js code.

1 Like

The program I am working on is this one:
Flight Simulation Demo
The “other” program is this one:
Flight Display Demo
Neither version has any Blender animations yet. My animations for the props and engine use parts created separately in the 3JS program…

Yes, I believe that my problem is with the initialization. Eventually, I will want to be able to reference several different animations, elevator (pitch), rudder (yaw), ailerons (bank) and flaps. From what I understand of Blender and 3JS, it is possible to have several different animations and to choose between them. And mixer.setTime() appears to be exactly what I want to access each one of them.

I think the missing piece is that I have not figured out how to access/address the specific animations that I will access with mixer.setTime(). For now, my “setup” only invoIves the command:
mixer = new THREE.AnimationMixer(aircraft_object);

I can run the animation by adding the following:
var clip1 = gltf.animations[0];
var action1 = mixer.clipAction(clip1);
action1.play();

But that does not appear to access a specific animation. My understanding is that the mixer is supposed to access all animations. So if I had several animations, this would just run them all. It appears that I need a setup which allows me to separately access each Blender animation that is included within the gltf/glb file.

If it helps I can put together a simple program that merely displays the aircraft in a blank space.

Ok, I see you’re getting there, step by step.

The secret to re-associate animations to objects in three js is in this line :

note the usage there of the variable clip_name that is used to find the animation by its name few lines above with THREE.AnimationClip.findByName(gltf.animations,clip_name);

And here comes the trick on how to find the clip name. I could not figure out a way out of the gltf only, so what I did to keep all the config in one place from Blender is the fallback on userData.parameter which has to be configured with the animation name as I’ve shown in the example above, or here it is again for convenience

Of course, you could simply hardcode it or use any other method to link the animation to your object, but that is the cleanest way I could come up with.

As you have noted, I’m using a structure anim_params to keep information about animations so that I can address it when I get an animation event request.

Nothing prevents an object from having multiple animations, so once you master all the steps to have one animation run, you’ll manage to extend this to multiple animations. You could place their names in the custom properties separated with comma or split them on multiple names from within the custom properties, or even better if you manage to extract the animation names from the GLTF without having to note them down a second time with custom properties.

Also I understand that your current main project does not have animations, there are no hurries, but once you have a branch or separate version or minimal example with animations integrated that might get clearer to you and probably easier to review and check if something is missing, but one step at a time.

An additional hint, the art of programming is not the art of writing code, it’s the art of reading code most of all :slight_smile:

1 Like