Skip to content

Commit

Permalink
animation
Browse files Browse the repository at this point in the history
  • Loading branch information
ousttrue committed Sep 30, 2024
1 parent 7469ef3 commit 667e3f6
Show file tree
Hide file tree
Showing 11 changed files with 555 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"name": "(Windows) Launch",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceFolder}/zig-out/bin/vrm0.exe",
"program": "${workspaceFolder}/zig-out/bin/animation.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/zig-out/web",
Expand Down
4 changes: 4 additions & 0 deletions deps/sokol_samples/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ pub const samples = [_]Sample{
.name = "sparse",
.root_source_file = "tutorials/sparse/main.zig",
},
.{
.name = "animation",
.root_source_file = "tutorials/animation/main.zig",
},
//
.{
.name = "glb",
Expand Down
104 changes: 104 additions & 0 deletions deps/sokol_samples/sample_framework/Animation.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
const std = @import("std");
const rowmath = @import("rowmath");
const Vec3 = rowmath.Vec3;
const Quat = rowmath.Quat;

const TimeSection = union(enum) {
index: usize,
range: struct {
begin: usize,
factor: f32,
},
};

pub fn TimeValues(T: type) type {
return struct {
input: []const f32,
output: []const T,
pub fn duration(self: @This()) f32 {
return self.input[self.input.len - 1];
}

pub fn getTimeSection(self: @This(), time: f32) TimeSection {
if (time <= self.input[0]) {
return .{ .index = 0 };
}

if (time >= self.input[self.input.len - 1]) {
return .{ .index = self.input.len - 1 };
}

for (self.input, 0..) |end, i| {
if (end >= time) {
const begin = self.input[i - 1];
return .{ .range = .{
.begin = i - 1,
.factor = (time - begin) / (end - begin),
} };
}
}

unreachable;
}
};
}

pub const Vec3Curve = struct {
values: TimeValues(Vec3),
pub fn sample(self: @This(), time: f32) Vec3 {
switch (self.values.getTimeSection(time)) {
.index => |i| {
return self.values.output[i];
},
.range => |range| {
// todo: learp, slserp... etc
return self.values.output[range.begin];
},
}
}
};

pub const QuatCurve = struct {
values: TimeValues(Quat),
pub fn sample(self: @This(), time: f32) Quat {
switch (self.values.getTimeSection(time)) {
.index => |i| {
return self.values.output[i];
},
.range => |range| {
// todo: learp, slserp... etc
return self.values.output[range.begin];
},
}
}
};

pub const FloatCurve = struct {
values: TimeValues(f32),
pub fn sample(self: @This(), time: f32) []f32 {
_ = self;
_ = time;
unreachable;
// return [0]f32{};
}
};

pub const Target = union(enum) {
translation: Vec3Curve,
rotation: QuatCurve,
scale: Vec3Curve,
weights: FloatCurve,
};

pub const Curve = struct {
node_index: u32,

target: Target,
};

curves: []Curve,
duration: f32,

pub fn loopTime(self: @This(), time: f32) f32 {
return @mod(time, self.duration);
}
122 changes: 122 additions & 0 deletions deps/sokol_samples/sample_framework/Scene.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const Vec3 = rowmath.Vec3;
const Quat = rowmath.Quat;

const Mesh = @import("Mesh.zig");
const Animation = @import("Animation.zig");
pub const Scene = @This();

const light_pos = [3]f32{ -10, -10, -10 };
Expand All @@ -22,6 +23,8 @@ meshes: []Mesh = &.{},
pip: sg.Pipeline = undefined,
gltf: ?std.json.Parsed(zigltf.Gltf) = null,
white_texture: Mesh.Texture = undefined,
animations: []Animation = &.{},
currentAnimation: ?usize = null,

pub fn init(self: *@This(), allocator: std.mem.Allocator) void {
self.allocator = allocator;
Expand Down Expand Up @@ -229,6 +232,125 @@ pub fn load(
));
}
self.meshes = try meshes.toOwnedSlice();

if (gltf.animations.len > 0) {
var animations = std.ArrayList(Animation).init(self.allocator);

for (gltf.animations) |gltf_animation| {
var curves = std.ArrayList(Animation.Curve).init(self.allocator);
var duration: f32 = 0;

for (gltf_animation.channels) |channel| {
const sampler = gltf_animation.samplers[channel.sampler];
if (std.mem.eql(u8, channel.target.path, "translation")) {
const curve = Animation.Vec3Curve{
.values = .{
.input = try gltf_buffer.getAccessorBytes(f32, sampler.input),
.output = try gltf_buffer.getAccessorBytes(Vec3, sampler.output),
},
};
duration = @max(duration, curve.values.duration());
try curves.append(.{
.node_index = channel.target.node,
.target = .{ .translation = curve },
});
} else if (std.mem.eql(u8, channel.target.path, "rotation")) {
const curve = Animation.QuatCurve{
.values = .{
.input = try gltf_buffer.getAccessorBytes(f32, sampler.input),
.output = try gltf_buffer.getAccessorBytes(Quat, sampler.output),
},
};
duration = @max(duration, curve.values.duration());
try curves.append(.{
.node_index = channel.target.node,
.target = .{ .rotation = curve },
});
} else if (std.mem.eql(u8, channel.target.path, "scale")) {
const curve = Animation.Vec3Curve{
.values = .{
.input = try gltf_buffer.getAccessorBytes(f32, sampler.input),
.output = try gltf_buffer.getAccessorBytes(Vec3, sampler.output),
},
};
duration = @max(duration, curve.values.duration());
try curves.append(.{
.node_index = channel.target.node,
.target = .{ .scale = curve },
});
} else if (std.mem.eql(u8, channel.target.path, "weights")) {
const curve = Animation.FloatCurve{
.values = .{
.input = try gltf_buffer.getAccessorBytes(f32, sampler.input),
.output = try gltf_buffer.getAccessorBytes(f32, sampler.output),
},
};
duration = @max(duration, curve.values.duration());
try curves.append(.{
.node_index = channel.target.node,
.target = .{ .weights = curve },
});
} else {
unreachable;
}
}

try animations.append(.{
.duration = duration,
.curves = try curves.toOwnedSlice(),
});
}

self.animations = try animations.toOwnedSlice();
self.currentAnimation = 0;
}
}

pub fn update(self: @This(), time: f32) ?f32 {
const gltf = self.gltf orelse {
return null;
};

const current = self.currentAnimation orelse {
return null;
};

if (current >= self.animations.len) {
return null;
}

const animation = &self.animations[current];
const looptime = animation.loopTime(time);
for (animation.curves) |curve| {
switch (curve.target) {
.translation => |values| {
const t = values.sample(looptime);
gltf.value.nodes[curve.node_index].translation = .{
t.x,
t.y,
t.z,
};
},
.rotation => |values| {
const r = values.sample(looptime);
gltf.value.nodes[curve.node_index].rotation = .{
r.x,
r.y,
r.z,
r.w,
};
},
.scale => |values| {
_ = values;
unreachable;
},
.weights => |values| {
_ = values;
unreachable;
},
}
}
return looptime;
}

pub fn draw(self: *@This(), camera: Camera) void {
Expand Down
Loading

0 comments on commit 667e3f6

Please sign in to comment.