233 lines
7.4 KiB
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");
|