toji / gl-matrix

Javascript Matrix and Vector library for High Performance WebGL apps
glmatrix.net
MIT License
5.39k stars 725 forks source link

Dual Quaternions #221

Closed stefnotch closed 6 years ago

stefnotch commented 8 years ago

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:

chinedufn commented 8 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?

stefnotch commented 8 years ago

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...)

chinedufn commented 8 years ago

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!

chinedufn commented 8 years ago

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

stefnotch commented 8 years ago

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.

chinedufn commented 8 years ago

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 :)

stefnotch commented 8 years ago

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. :)

chinedufn commented 8 years ago

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 :)

stefnotch commented 8 years ago

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:

chinedufn commented 8 years ago

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!

stefnotch commented 8 years ago

@toji @sinisterchipmunk Would you be fine with some dual quaternion code in your library? They are quite useful when it comes to gpu skinning.

stefnotch commented 8 years ago

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?

stefnotch commented 8 years ago

@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)

stefnotch commented 8 years ago

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?)

stefnotch commented 8 years ago

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)

stefnotch commented 8 years ago

@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)

chinedufn commented 8 years ago

@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

stefnotch commented 8 years ago

OK, here you go: https://github.com/stefnotch/gl-matrix Have fun!

stefnotch commented 7 years ago

@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())

chinedufn commented 7 years ago

@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

stefnotch commented 7 years ago

Tested it: [[real], [dual]] vs [real dual] https://jsbench.github.io/#98fb469d0ced38f9a834f1be3319abcd Apparently, there is a performance difference...

stefnotch commented 7 years ago

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.

stefnotch commented 7 years ago

@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)

stefnotch commented 7 years ago

Ok, started changing the code to [real dual]. :)

stefnotch commented 7 years ago

@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?