Backlog/engine/rend/src/animations/animationResolver.zig

233 lines
7.4 KiB
Zig

pub const AnimResolverRef = core.Reference(AnimResolverInterface);
pub const AnimResolverInterface = core.MakeInterface("AnimResolverVTable", struct {
// this tick function should evaluate the current state of the resolver
// and then update the animator's finals[] matrix list.
resolve: *const fn (*anyopaque, f64, *Animator) void,
onSkeletonSet: ?*const fn (*anyopaque, *Animator) void = null,
create: *const fn (std.mem.Allocator) core.EngineDataEventError!*anyopaque,
destroy: *const fn (*anyopaque) void,
pub fn Implement(comptime TargetType: type) @This() {
const Wrap = struct {
pub fn create(allocator: std.mem.Allocator) core.EngineDataEventError!*anyopaque {
const new = TargetType.create(allocator) catch return core.EngineDataEventError.BadInit;
return @ptrCast(new);
}
pub fn destroy(p: *anyopaque) void {
const ptr: *TargetType = @ptrCast(@alignCast(p));
ptr.destroy();
}
pub fn onSkeletonSet(p: *anyopaque, a: *Animator) void {
const ptr: *TargetType = @ptrCast(@alignCast(p));
ptr.onSkeletonSet(a) catch unreachable;
}
pub fn resolve(p: *anyopaque, dt: f64, a: *Animator) void {
const ptr: *TargetType = @ptrCast(@alignCast(p));
ptr.resolve(dt, a) catch unreachable;
}
};
return .{
.destroy = Wrap.destroy,
.create = Wrap.create,
.resolve = Wrap.resolve,
};
}
});
pub const AnimSampler = struct {
name: ?core.Name = null,
track: ?*AnimationTrack = null,
playbackRate: f32 = 1.0,
time: f32 = 0.0,
outputLocals: std.ArrayListUnmanaged(ozz.SoaTransform) = .{},
// other features
// paused: bool = false,
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
self.outputLocals.deinit(allocator);
}
pub fn getOutput(self: *@This()) []ozz.SoaTransform {
return self.outputLocals.items;
}
pub fn sampleAndAdvance(self: *@This(), allocator: std.mem.Allocator, dt: f64, animator: *Animator) void {
self.sample(allocator, animator);
self.advance(dt);
}
pub fn advance(self: *@This(), dt: f64) void {
if (self.track == null) {
return;
}
FloatHelpers.updateTrackTime(&self.time, dt, self.playbackRate, self.track.?.endTime);
}
pub fn setName(self: *@This(), name: core.Name) void {
self.name = name;
self.track = null;
}
pub fn sample(self: *@This(), allocator: std.mem.Allocator, animator: *Animator) void {
if (self.name == null) {
return;
}
if (self.track == null) {
self.track = animation_system.gAnimationSys.animTracks.get(self.name.?.handle());
}
self.outputLocals.resize(allocator, animator.jointLength) catch return;
if (self.track) |track| {
if (track.endTime < 0.01) {
return;
}
animator.sampleAnimation(self.time, track, self.outputLocals.items);
}
}
};
pub const BlenderList = struct {
backing: std.mem.Allocator,
arena: std.heap.ArenaAllocator,
jobLayers: std.ArrayListUnmanaged(ozz.Layer) = .{},
jobLayersAdditive: std.ArrayListUnmanaged(ozz.Layer) = .{},
useAdditive: bool = false,
threshold: f32 = 0.01,
jointLength: usize = 0,
blendingJob: ozz.BlendingJob = .{},
pub fn create(backingAllocator: std.mem.Allocator) !*@This() {
const self = try backingAllocator.create(@This());
self.* = .{
.backing = backingAllocator,
.arena = std.heap.ArenaAllocator.init(backingAllocator),
};
return self;
}
pub fn destroy(self: *@This()) void {
self.arena.deinit();
self.backing.destroy(self);
}
pub fn updateRestPose(self: *@This(), animator: *Animator) !void {
if (animator.skeleton) |skeleton| {
self.jointLength = skeleton.sk.numJoints();
self.blendingJob.rest_pose = skeleton.sk.getRestPoseModel();
}
}
pub fn clearLayers(self: *@This()) void {
self.jobLayersAdditive.clearRetainingCapacity();
self.jobLayers.clearRetainingCapacity();
}
pub fn addLayer(self: *@This(), transform: []ozz.SoaTransform, weight: f32, settings: anytype) void {
const layer = self.jobLayers.addOne(self.arena.allocator()) catch unreachable;
layer.* = .{
.weight = weight,
.transform = ozz.makeSpan(transform),
};
_ = settings;
}
pub fn updateAndRun(self: *@This(), output: []ozz.SoaTransform) void {
self.updateBlendingJob();
self.runBlendingJob(output) catch return;
}
pub fn updateBlendingJob(self: *@This()) void {
self.blendingJob.threshold = self.threshold;
self.blendingJob.layers = ozz.makeSpan(self.jobLayers.items);
//self.blendingJob.additive_layers = if (self.useAdditive) ozz.makeSpan(self.jobLayersAdditive.items) else .{};
self.blendingJob.additive_layers = .{};
}
pub fn runBlendingJob(self: *@This(), output: []ozz.SoaTransform) !void {
self.blendingJob.output = ozz.makeSpan(output);
if (!self.blendingJob.run()) {
core.engine_logs("blending job failed");
return;
}
}
};
// resolver helpers
pub const FloatHelpers = struct {
pub inline fn updateTrackTime(target: *f32, dt: f64, rate: f32, endTime: f32) void {
target.* += @as(f32, @floatCast(dt)) * rate;
while (target.* > endTime) {
target.* -= endTime;
}
}
};
// samples a single animation, same as the default behaviour.
// used as a test for the resolver system
pub const SingleAnimationResolver = struct {
allocator: std.mem.Allocator,
locals: std.ArrayListUnmanaged(ozz.SoaTransform) = .{},
track: ?*AnimationTrack = null,
playback: f32 = 0.0,
playbackRate: f32 = 1.0,
pub const AnimResolverVTable = AnimResolverInterface.Implement(@This());
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
};
return self;
}
pub fn onSkeletonSet(self: *@This(), animator: *Animator) !void {
if (animator.skeleton) |skeleton| {
try self.locals.resize(self.allocator, skeleton.sk.numSoaJoints());
}
}
pub fn resolve(self: *@This(), dt: f64, animator: *Animator) !void {
self.track = animator.track;
if (self.track == null) {
return;
}
const track = self.track.?;
if (track.endTime < 0.01) {
return;
}
FloatHelpers.updateTrackTime(&self.playback, dt, self.playbackRate, track.endTime);
animator.sampleAnimation(self.playback, track, self.locals.items);
animator.commitLocalToModel(self.locals.items);
animator.modelToFinal();
}
pub fn destroy(self: *@This()) void {
self.locals.deinit(self.allocator);
self.allocator.destroy(self);
}
};
const animation_system = @import("animationSystem.zig");
const Animator = animation_system.Animator;
const AnimationTrack = animation_system.AnimationTrack;
const core = @import("core");
const std = @import("std");
const ozz = @import("ozz");