tiny.rig.blend
Defined in tiny.rig.
Blending two poses, and crossfading from one clip to another.
API (5)
Actions
Public operations.
Crossfade.advanceCrossfade.doneCrossfade.weightposes:outmay aliasfromorto.
Types and contracts
Public types and contracts.
Crossfade: A linear crossfade: the weight of the incoming pose rises from zero to one overdurationseconds.
Source
Source: fun/rig/src/blend.zig
zig
//! Blending two poses, and crossfading from one clip to another.//!//! A blend takes the shortest-arc spherical interpolation of each joint's//! rotation and the linear interpolation of its translation. Weights at or//! below zero return the first pose and weights at or above one return the//! second, exactly.const std = @import("std");const transform = @import("transform.zig");const assert = std.debug.assert;const Transform = transform.Transform;/// `out` may alias `from` or `to`.pub fn poses(from: []const Transform, to: []const Transform, weight: f32, out: []Transform) void { assert(to.len == from.len); assert(out.len == from.len); assert(!std.math.isNan(weight)); if (weight <= 0) { if (out.ptr != from.ptr) @memcpy(out, from); return; } if (weight >= 1) { if (out.ptr != to.ptr) @memcpy(out, to); return; } for (from, to, out) |a, b, *mixed| { mixed.* = .{ .translation = a.translation.lerp(b.translation, weight), .rotation = a.rotation.slerp(b.rotation, weight), }; }}/// A linear crossfade: the weight of the incoming pose rises from zero to/// one over `duration` seconds.pub const Crossfade = struct { duration: f32, elapsed: f32 = 0, pub fn advance(crossfade: *Crossfade, seconds: f32) void { assert(std.math.isFinite(seconds)); crossfade.elapsed += seconds; } pub fn weight(crossfade: Crossfade) f32 { assert(!std.math.isNan(crossfade.duration)); if (!(crossfade.duration > 0)) return 1; return std.math.clamp(crossfade.elapsed / crossfade.duration, 0, 1); } pub fn done(crossfade: Crossfade) bool { return crossfade.weight() >= 1; }};test "blend endpoints return their poses and the middle interpolates" { const from = [_]Transform{.{ .translation = .{ .x = 0 } }}; const to = [_]Transform{.{ .translation = .{ .x = 2 }, .rotation = transform.quatFromAxisAngle(.{ .z = 1 }, 1), }}; var out: [1]Transform = undefined; poses(&from, &to, 0, &out); try std.testing.expectEqual(from[0], out[0]); poses(&from, &to, 1, &out); try std.testing.expectEqual(to[0], out[0]); poses(&from, &to, 0.5, &out); try std.testing.expectApproxEqAbs(@as(f32, 1), out[0].translation.x, 1e-6); const half = transform.quatFromAxisAngle(.{ .z = 1 }, 0.5); try std.testing.expectApproxEqAbs(half.z, out[0].rotation.z, 1e-6);}test "a crossfade reaches its target and a zero duration cuts" { var crossfade = Crossfade{ .duration = 0.2 }; crossfade.advance(0.1); try std.testing.expectApproxEqAbs(@as(f32, 0.5), crossfade.weight(), 1e-6); crossfade.advance(0.2); try std.testing.expect(crossfade.done()); try std.testing.expectEqual(@as(f32, 1), (Crossfade{ .duration = 0 }).weight());}Source: fun/rig/src/root.zig:22
zig
pub const blend = @import("blend.zig");Audit
| Definitions | 6 |
|---|---|
| Public names | 6 |
| Members | 2 |
| Version | 26.7.0 |
| Revision | daab053ee433 |