Closed rukai closed 5 years ago
Maybe try the second key with Interpolation::StrokeBezier(5., 0.)
.
I tried it and it gives the same output.
For reference, the example I posted is a simplified version of my gltf animation interpolation code:
let mut points = vec!();
for (input, outputs) in channel.inputs.iter().zip(scales.chunks(3)) {
points.push(Key::new(*input, outputs[1], SplineInterpolation::StrokeBezier(outputs[0], outputs[2])));
println!("spline vertex: {:?}, input tangent: {:?}, output tangent: {:?}", outputs[1], outputs[0], outputs[2]);
}
let spline = Spline::from_vec(points);
if let Some(result) = spline.clamped_sample(seconds) {
scale = result;
}
else {
error!("Failed to interpolate scale spline");
}
Ok so I thought about your issue and I think I got why. Interpolation::Bezier(..)
and Interpolation::StrokeBezier(..)
works in the same dimension as the value carried by the key, not the key itself. What it means is that if you have a Spline<f32, f32>
, you will never get a very interesting Bézier interpolation here (it’s a 1D Bézier interpolation). The T
argument of the Spline
allows you to modulate the speed at which you sample keys. So if you want a 2D spline interpolation, and hence have a better Bézier, you need Spline<f32, [f32; 2]>
, for instance (or whatever algebra crate type you need). If you don’t know which value take for T
, I typically use the same as the x
coordinate of the point. Adding a point then looks like spline.add(Key::new(p[0], p, interpolation))
.
I’ll change spline-editor as linked above to show what stroke Bézier curves look like and I might add some hints in the documentation about the T
parameter.
gltf specifies the input value so using the x component of the output doesnt really make sense for me... https://github.com/rukai/canon_collision/blob/65b800ce02e2b62d8130212bb9cd0ab3d0ca33bf/canon_collision/src/wgpu/animation.rs#L78
Using the branch in your PR the example code I originally posted now outputs:
value at 0: Some(1.0)
value at 1: Some(0.5200000000000001)
value at 2: Some(0.28)
value at 3: Some(0.28)
value at 4: Some(0.5200000000000001)
value at 3: Some(1.0)
I still think it should be outputing 1.0 for all values.
So I don’t really know what happens for Bézier in 1D but I think it cannot work the way you expect it to work. Try to use 2D Bézier as I did here.
Ok, so this is my example in 2D (although I am personally using 3D)
use splines::{Interpolation, Key, Spline};
fn main() {
let keys = vec![
Key::new(0.0, cg::Vector2::new(1.0, 1.0), Interpolation::StrokeBezier(cg::Vector2::new(0.0, 0.0), cg::Vector2::new(0.0, 0.0))),
Key::new(5.0, cg::Vector2::new(1.0, 1.0), Interpolation::StrokeBezier(cg::Vector2::new(0.0, 0.0), cg::Vector2::new(0.0, 0.0))),
];
let spline = Spline::from_vec(keys);
println!("value at 0: {:?}", spline.clamped_sample(0.0));
println!("value at 1: {:?}", spline.clamped_sample(1.0));
println!("value at 2: {:?}", spline.clamped_sample(2.0));
println!("value at 3: {:?}", spline.clamped_sample(3.0));
println!("value at 4: {:?}", spline.clamped_sample(4.0));
println!("value at 5: {:?}", spline.clamped_sample(5.0));
}
this outputs
value at 0: Some(Vector2 [1.0, 1.0])
value at 1: Some(Vector2 [0.5200000000000001, 0.5200000000000001])
value at 2: Some(Vector2 [0.28, 0.28])
value at 3: Some(Vector2 [0.28, 0.28])
value at 4: Some(Vector2 [0.5200000000000001, 0.5200000000000001])
value at 5: Some(Vector2 [1.0, 1.0])
Again, it is quite possible I am misinterpreting the gltf data or I'm meant to handle tangents of 0 as a special case in my code.
I think this is the three.js implementation https://github.com/mrdoob/three.js/blob/25a59dc9a96e5b51c26fd260faa3ea526ddae644/examples/jsm/loaders/GLTFLoader.js#L1087
Oh huh... Just realized gltf specifies the spline implementation at the bottom of the spec https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation Totally missed that!
I've implemented it myself https://github.com/rukai/canon_collision/blob/7fc9af477b074bc5f05dbd88a42896d022a229f8/canon_collision/src/wgpu/animation.rs#L91 Now that I understand how it works, its pretty simple, so I'd rather just re-implement it in my project to avoid an extra dependency.
Thanks for your time implementing extra features in splines! I will close this issue as the problem was that I was trying to use splines for something that it didn't support. Feel free to reopen it if you want to implement the type of spline that gltf uses.
The one implemented in splines
for cubic Bézier is this one, if that might help.
Hey, thanks for the quick implementation of
Interpolation::StrokeBezier
! This example program:Outputs:
I would expect it to output Some(1.0) for every input value.
This is based on some gltf data blender is giving me, used to interpolate the scaling of bones. However the exported animation should not scale the bones hence why I assume that this case should always output 1.0. But maybe I need to be handling this case manually in my code and it is not a problem of the splines crate? What do you think?