-
Notifications
You must be signed in to change notification settings - Fork 1.3k
How advanced skinning are implemented #312
Comments
It sounds like this is only implicitly about skinning, but primarily about animation. Specifically, the question
sounds like what is "explained" in the Notes at https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations : There may be multiple animations, and they are supposed to be usable independently. This may also answer the question about the "duration" of animations. But note that there is a caveat regarding the channels of one animation. This is explained at https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoxAnimated (and this is a good test model to start with basic animations). Essentially, when there are multiple channels with different lengths, then the longest one determines the length of the animation, and the individal channels may not "wrap around" or so. In order to test basic skinning+animation, https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/RiggedSimple may be good start. |
sounds great. I still have a confusion with this formula:
the 2 last variables it's ok, I understood them, but I struggle with the first one.
So the Imagine I have this model:
This is an example that I've created under Blender. So I wonder how to get the |
An aside: I have edited the previous comment for formatting. When you have to post multiple lines of code, you can do this with
(these are three backticks at the beginning and the end). Beyond that, it would be good to attach the (complete) model here, maybe in a ZIP file, so that one can try it out easily. But I'll have another look at the comment/questions later. |
thanks for this tip. I thought it's only available on Stack Overflow 😄 I attached the model with its bin file. I really want to implement the algorithm to make this model working: https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/CesiumMan the model that you pointed in the previous comment (Rigged Simple) uses Now I want to describe you what I understood from skinning: |
It's hard to align the questions with the code. I could now read the code, and try to understand it. (Nitpicks: 1. The Trying to focus on the actual questions:
That's a bit surprising. Is there any specific reason why you want to decompose the matrix? Usually, from a plain glTF/rendering perspective, it is the other way around. In an implementation, like a
Note that keeping the T/R/S individually is not "wrong", but ... may be cumbersome.
Nitpicking: The mesh is attached to the node (or, one could also say, "instantiated" by the node: One mesh can be attached to multiple nodes!). More important: The inverse of the global transform of this node is not necessarily constant.
For the implementation itself, there are many options. You could either use some (existing) sopisticated rendering engine. Or you could implement the whole rendering structures on your own. But the latter could only be a very simple one, because... you could literally sink years of work into a "rendering engine".... However: It is conceptually correct that the transformation is "propagated". Imagine a class like this:
In the last line, One difficulty with implementing that: The "parent" of a node is not stored in glTF. I've sneaked the However, from a short look at the code (and the question *"I have to propagate that logic until no child is present?"), you seem to try to compute the matrix in the opposite direction. This is also possible, but it's a bit complicated ... and I think that, in order to implement that sensibly, you'd have to store the transform from the parent node in each node (and probably introduce some "dirty" flags to avoid unnecessary updates...). |
to update the translation, rotation and scale matrices?
if we have one mesh, is not still constant? there are a lot of information, I have to understand that point by point. Thank you. I will try to update my code because I have something like that coming to my mind:
I will try this and then will be back |
That's part of the question: Why are you storing them individually? Again: glTF basically provides two ways to define the transform matrix of a node:
But at runtime (i.e. in a rendering engine), you usually store only a matrix. (The matrix may be computed from the T/R/S properties, but it's unusual to store the T/R/S properties individually) (Note: All this is a bit simplified. Of course, you might have some sophisticated rendering engine, and might want to have functions like The
you could compute the global transforms of all nodes, and assign them to the nodes as There are some pros and cons for all approaches. Very roughly speaking: The recursive solution that I sketched requires the "parent" nodes to be stored, and will re-compute the global transform each time when (Which one is "better"? I don't know - in doubt, it might depend on the structure of the node hierarchy. But for rendering, the global transforms are required anyhow, so your approach may well have an advantage here) |
You don't need to multiply by |
It might be possible to somehow sneak around that for the skinning computations themself (I'd have to scribble a down the math with pencil+paper to be "more sure" about that). But at some point, the (An aside: I haven't checked that, but it might well be that none of the skinning test models uses a global transform that is not the identity matrix. If this is the case, it could give implementors the impression that their skinning works, even though their implementation might fail when the model is transformed. This has to be analyzed further, and if this is the case, it might be worth adding a dedicated sample model to test this...) |
@CppProgrammer23 Don't dispair. The skinning is the hardest part (or... well... at least, before PBR was introduced). I had a short look at the code, but again: It's hard to figure out the intention of some parts, or why they are implemented like that. For example, a function like the And by the way: It may be a matter of style, but I don't see a reason why someone should implement a function like
instead of
I mean, the function to compute the global transform of a node will be required anyhow. And... of course, this function should not check for a certain |
When I wrote some specific code for the model that you have created What I really can't understand is in your tutorial about |
@javagl We've discussed this before. Yes I am sure. About skinning the spec says "...while ignoring the transform of the skinned mesh node". You also wrote that it's only purpose was to cancel out part of the modelview matrix. Yes, the root node still affects the skinned mesh, because it affects |
@scurest You're right, but I still did not do all the maths behind that. I did that back when I wrote the tutorial and the overview, being completely new to vertex skinning, and digging through online resources and the COLLADA specification. But there had been some discussions, updates, clarifications and (apparently) simplifications in the meantime that I had not yet taken into account. But after doing some archeology ( KhronosGroup/glTF#1270 , KhronosGroup/glTF#1403 , and more recently, the one that you now linked to), I think that you're right. I have read a bit in these issues, and tried out different cases (e.g. https://bghgary.github.io/glTF-Assets-Viewer/?manifest=https://raw.githubusercontent.com/KhronosGroup/glTF-Asset-Generator/v0.6.0/Output/Manifest.json&folder=2&model=10 ), with my implementation, once with and once without the global transform being integrated, and the result seems to be the same. (I'm still not "scientist-sure" about that, because I didn't do the math all the way down, but ... I'm "engineer-sure": It seems to be correct from all what I've read and tried out...) This means that the tutorial, the overview (and my implementation) have to be updated accordingly. I'll open an issue for that (but cannot say for sure when I'll be able to tackle that - I've been doing far too little for my "real" work recently anyhow...). |
Hi,
I want to implement an algorithm for advanced skinnig. So I have few questions; first, how can i know which animations start first (suppose that the rotation starts then the translation comes but both transformations have the same key frames and the translation is 0 and only changes after 2 seconds.)?
If i understand, I have to access each animation and change the mode that it belongs to, accordingly to the value at that key frame?
The text was updated successfully, but these errors were encountered: