Closed stefnotch closed 6 years ago
When dual quaternion blending all of the dual quaternion operation end up simplifying down to regular quaternion operations (AFAIK).
Is there some other application of dual quaternion that you're looking to support? Or were you thinking of a dual quaternion convenience wrapper around the more "basic" math?
Well, Dual Quaternions are really useful when it comes to skeletal animation. Usually, you tend to have a fair number of bones, which need to be animated. That's rather performance heavy. So, what I am trying to do is to first get a basic wrapper done, then optimize it.
I already got a fair number of functions done. (No test cases done though...)
Which functions? Asking because I'm not sure if you need any dual quaternion functions for skeletal animation. The useful ones all simplify down to a combination of regular quaternion functions. So it seems like you'd just be writing convenience wrappers around quaternion math.
On the CPU side (thus anything that this library might optimize) of Dual Quaternion Linear Blending (skeletal animation) you're usually just linearly blending between two dual quaternion keyframes per joint based on the current time.
Which boils down to blending their rotation quaternions and the translation quaternions.
I'm curious about how you might optimize that?
And please let me know if I'm missing something!
Although to be fair... It's probably much easier for someone to just grab a dual quaternion library than to learn the underlying quaternion math.
Curious to see what you come up with! Not sure if it would get merged into gl-matrix
but might still have a nice home as its own module regardless
First of all, you need to turn a quaternion/rotation and a translation into a dual quaternion. (That's one function which isn't that simple and can be optimized a bit.)
Multiplying dual quaternions (pretty much the same as multiplying matrices) isn't that straightforward either. (I guess it could be optimized a bit. That optimization is quite important, since models tend to have a lot of bones and there can be a lot of models on the screen.)
Then, there also are the from/to matrix conversions. (Which can't be simplified down to quaternions.)
And, I haven't looked that much into blending them. I guess that part can be simplified down to simply blending both parts. (Might have some potential for optimization.)
And, those are the most important functions. The rest would just be convenience wrappers.
Ah right, I was thinking that you'd never need to multiply dual quats during runtime... But you totally would in, say, inverse kinematics (err I think). No experience with IK yet though so I'll take your word for it :)
If it helps, wrote some dual quaternion code recently. Definitely not optimized though:
converting matrix to dual quat on CPU https://github.com/chinedufn/collada-dae-parser/blob/23553a5aea4405b52dbaf3502fabad4379bfc97a/demo/animated-model/render/temporary-refactor-zone/interpolate-joints.js#L68-L89
converting dual quat to matrix in GPU https://github.com/chinedufn/collada-dae-parser/blob/23553a5aea4405b52dbaf3502fabad4379bfc97a/demo/animated-model/shader/vertex.glsl
Please ping me when you're done with your implementation. I'd love to use it :)
Or, when you have a tree of bones, you also need some dual quat multiplication:
root | leg |
---|
foot
You have 3 dual quaternions. If you move the leg, the foot should also move. So, each one of them has a "offset" dual quaternion, meaning it is relative to the parent. Now, if you want to know the actual (world space) dual quaternion, you need to multiply them. (Just like you would do with matrices.)
About the code, thank you! :)
Ok, made a mental note to ping you. :)
Right right, meant that you'll typically have all of that pre-computed before runtime if you're dealing with key-framed skeletal animations. Meaning that perf wouldn't matter all that much.
But thanks for pointing out that there are totally cases when you want to do it in real time :)
Just a few questions, do you think that quat2 is an appropriate name. (It doesn't really fit in with the rest of them. For example, a vec3 has 3 elements, not 3 vectors) Secondly, should I write dual quaternion or dual quat in the comments? (Or interchange them however I want?)
Also, do you have any working dual quaternion library that I could use as a reference? (I am mostly interested in the lerp/slerp functions)
Lastly, here is a quick progress update:
Don't have a code reference sorry, but this paper discusses ScLERP - http://www.xbdev.net/misc_demos/demos/dual_quaternions_beyond/paper.pdf
hmm, thoughts on dual-quat
as the name?
However you want sounds fine IMMO. I'm sure it'll be easier to clean up and make everything consistent once you're done.
Also IMO definitely worth pinging Toji to see if he wants dual quaternion code in here. If not, you'll want to be sure to publish it as a standalone module.
Sweet progress update. Pumped to see your code!
@toji @sinisterchipmunk Would you be fine with some dual quaternion code in your library? They are quite useful when it comes to gpu skinning.
Ok, this is confusing: Normalizing a dual quaternion: Divides it by the length: (Math.Sqrt() being used) https://github.com/mosra/magnum/blob/master/src/Magnum/Math/DualQuaternion.h#L364 https://github.com/bobbens/libdq https://www.cse.unsw.edu.au/~cs9018/vrml98/dualquat.html#sec2.5
Divides it by the length squared: http://www.xbdev.net/misc_demos/demos/dual_quaternions_beyond/paper.pdf https://github.com/markaren/DualQuaternion/blob/master/src/main/java/info/laht/dualquat/DualQuaternion.java#L89 http://wscg.zcu.cz/wscg2012/short/A29-full.pdf
I tried the length squared option, but the "getTranslation" function returns wrong stuff?
@chinedufn I finished the majority of the functions and the corresponding unit tests. Most of the functions aren't optimized though. No unit tests: (Meaning: I can't guarantee that they are correct)
Not implemented:
Should I go ahead and release the code? (Of course, in a fork until @toji or @sinisterchipmunk respond)
Finished a few more unit tests. Though, how should I implement the rotate function? What exactly does it do? (How can I represent it using the other functions?)
Ok, managed to implement it. There are 2 rotate functions now, one that rotates a dual quaternion by a quaternion and one that rotates it around a given axis. (I just used the quat.rotate() function)
@sinisterchipmunk @toji @chinedufn I finished it: https://github.com/stefnotch/gl-matrix
What do you think of it? (It still needs a fair bit of optimizations)
@stefnotch might be safe to assume that Toji is wrapped up in WebVR work and won't have time to take a look. So I'd recommend publishing your code standalone. example -> https://github.com/stackgl/gl-quat
OK, here you go: https://github.com/stefnotch/gl-matrix Have fun!
@chinedufn Thought I'd give you a little update. My lerp function handles the signs correctly now. :)
And, your code seems to have both parts of the dual quaternion in one array. (Which is better for speed and wastes less memory.)
Should I change my code to be like that? (If yes, I am going to add the functions getDual()
and setDual()
)
@stefnotch nice!
One array could be desirable, but could also be negligible. I'm not entirely sure of the impact. Would need a benchmark.
So, whatever is easiest would be a fine start I'd say. Perf can come later
Tested it: [[real], [dual]] vs [real dual] https://jsbench.github.io/#98fb469d0ced38f9a834f1be3319abcd Apparently, there is a performance difference...
Are there any functions that you would want to see? e.g. A more general lerp (lerping more than 2 dual quaternions)
Also, the dual quat code is pretty much finished. All that is left is optimizing it.
@chinedufn I optimized a lot of code! :)
So, all I have left to do is: a) Implement slerp, scale?(Other variant for skinning), lookAt b) Optimize rotateX, rotateY, rotateZ c) Think about changing to [real dual] d) Add support for SIMD? Or asm.js? Or WebAssembly? (Or all of them?) e) Anything else that you might want?
By the way, here is the documentation for everything: https://stefnotch.github.io/gl-matrix/
Note to self: For migrating the code to [real dual]: Calling quat functions on dual quats doesn't cause a perf hit.
Getting the dual part is easy, when Float32Arrays are supported: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray Otherwise, I will have to use slice() (Use feature detection for that?) (subarray is a lot slower than indexing)
Ok, started changing the code to [real dual]
. :)
@chinedufn
Ok, finally finished changing all of the code to [real dual]
! :)
However, I'm not entirely happy with some of the function names: https://stefnotch.github.io/gl-matrix/docs/quat2.html Also, I think that some functions could be removed. Any suggestions?
For example:
getReal and setReal take a quaternion (because I thought that would be more useful) However, the existing convention in this library is that set functions take something like x1, y1, z1, w1
as parameters. Example
Another example: fromRotationTranslationValues seems to be rather useless. Should I remove it?
Would dual quaternions be a useful addition to this library? I would be willing to write the code for them.
Edits: Code is here Documentation: https://stefnotch.github.io/gl-matrix/
Todo list:
SLERP
/ScLERP
lookAt
targetTo
Not so important:forEach
calculateW