godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
91.15k stars 21.19k forks source link

There seem to be no way to access Blend Shapes values via code #15778

Open Rattel opened 6 years ago

Rattel commented 6 years ago

Godot version: 3.0 Beta 2

Issue description: Looks like it's impossible to change values of a Blend Shape in a mesh with a script, or at least it's not documented, even though you can change them in the editor.

JosephCatrambone commented 6 years ago

After a lot of digging I see it's possible. If you have a mesh instance you need to call "set" on the mesh instance with "blend_shapes/". The capitalization has to match, but the spaces are removed. If you had "Face Smile" (as I did) then doing this should work:

onready var body = $Model/Skeleton/Body
var t = 0.0

func _process(delta):
    t += delta  
    if t > 1:
        t = 0
    body.set("blend_shapes/FaceSmile", t)
bojidar-bg commented 6 years ago

Wouldn't it be better to change this issue to a "bug" or "enchancement", given it is quite unintuitive for users? I guess it might be better to add a set_blend_amount(blendshape, amount) instead of documenting the workaround.

JosephCatrambone commented 6 years ago

I'd agree. I spent a long time looking for how to do this and ended up digging through the code before finally stumbling upon a solution. A wrapper for the method wouldn't be unwelcome, especially if it caps the range to 0-1 as is needed by blendshapes.

GeorgeS2019 commented 4 years ago

There is this method in VisualServer.cs

image

/// <summary>
/// <para>Sets the weight for a given blend shape associated with this instance.</para>
/// </summary>
[GodotMethod("instance_set_blend_shape_weight")]
public static void InstanceSetBlendShapeWeight(RID instance, int shape, float weight)
{
     NativeCalls.godot_icall_3_630(method_bind_262, ptr, RID.GetPtr(instance), shape, ref weight);
}
lukostello commented 3 years ago

Just needs better documentation not different implementation IMO

lukostello commented 3 years ago

I think I've actually changed my mind about this. Particularly because of how multimesh should handle blendshapes. If multimesh instances handled it the same way as mesh instances then that would mean that all meshes of the multimesh would have to have the same value of blendshape, where as if we instead were able to call a method you could use one of the parameters that make each mesh of the multimesh unique to make each blendshape have its own value. Which would be good if you had a bird mesh with flying blend shapes and you wanted them all to fly off at different times. In my case I want them all to have the same blend shape but the method would allow that flexibility. Having a method might even make the variables mesh instance has obsolete. I was going to make my own issue for blendshape support for multimeshes but this might solve that.

clayjohn commented 3 years ago

@lukostello Using blendshapes with multimeshes is out of scope for this issue. This issue is about changing the values for a blend shape from script.

With respect to animating different multimesh instances differently with blendshapes, it is not trivial to implement and may not even be possible. MultiMeshes use GPU instancing which is different that having Mesh Instances in Godot. GPU instancing means that only one mesh is sent to the GPU and then it is rendered multiple times in a single draw call. What makes it fast is that only one mesh is used. Your suggestion would require the each instance uses a unique mesh (a blendshape is essentially another mesh) which would make it impossible to use GPU instancing. This problem is described well (from a tech artists persective) in the GDC talk "Creating the Art of ABZU" https://www.gdcvault.com/play/1024409/Creating-the-Art-of-ABZ. the speaker outlines their attempt to animate thousands of fish using blendshapes and how it was not possible, then they propose a solution, which is using vertex animation instead. Vertex animation works very well for simple Meshes (like fish or birds). The technique is also decribed in the Godot docs in the Animating thousands of fish with the MultiMeshInstance tutorial.

lukostello commented 3 years ago

@clayjohn even in the instance where all meshes in the multimesh share the same blendshape?

clayjohn commented 3 years ago

@lukostello It is technically possible for all instances to share a blend shape. However, the animations for all instances will be in synch. Further, I am not sure if Godot properly supports blend shapes with the MultiMeshInstance (from a quick glance it looks like it doesn't).

The way blendshapes work is that they process the mesh before it is drawn and actually morph the mesh stored on the GPU, so when it comes time to issue a draw call, the engine cant tell the difference between a mesh with blend shapes and a static mesh.

This is a problem for MultiMeshes because they only send one copy of the mesh to the GPU and then use GPU instancing to draw the same mesh multiple times.

lukostello commented 3 years ago

@clayjohn that doesn't seem like a problem, just have the copy of the mesh being sent be the mesh made after the blend shapes are applied, but even if that does work that would mean all of them would be in sync which is what I want but its probably a pretty niche usage.

clayjohn commented 3 years ago

that doesn't seem like a problem, just have the copy of the mesh being sent be the mesh made after the blend shapes are applied, but even if that does work that would mean all of them would be in sync which is what I want but its probably a pretty niche usage.

Feel free to open a proposal! I don't think it should be too much trouble to add. But of course, we would need to see if there is user demand before considering it.