From 302f96af3ffdaf2ef3d86505068dc01bd99853e7 Mon Sep 17 00:00:00 2001 From: rhinemann Date: Sun, 29 Mar 2026 22:21:59 +0200 Subject: [PATCH] Going crazy with translations --- src/gl_helper/model.zig | 141 +++++++++++++++++--- src/main.zig | 285 ++++++++++++++++++++++++++-------------- src/tree.zig | 258 ++++++++++++++++++++++++------------ 3 files changed, 481 insertions(+), 203 deletions(-) diff --git a/src/gl_helper/model.zig b/src/gl_helper/model.zig index 1f53405..e535068 100644 --- a/src/gl_helper/model.zig +++ b/src/gl_helper/model.zig @@ -7,39 +7,144 @@ const Mesh = @import("mesh.zig"); const Shader = @import("shader.zig"); const Tree = @import("../tree.zig").Tree; +pub const Animation = struct { + rotation: zm.Quat, + translation: zm.Mat, +}; + pub const JointData = struct { mesh: Mesh, - translation: zm.Mat, - rotation: zm.Quat, + animated_transform: zm.Mat = zm.identity(), + local_bind_transform: zm.Mat, + inverse_bind_transform: zm.Mat = zm.identity(), + keyframes: [2]Animation, + count: usize, + + var counter: usize = 0; + + pub fn init(mesh: Mesh, translation: zm.Mat, rotation: zm.Quat, keyframes: [2]zm.Quat) JointData { + const res = JointData{ + .mesh = mesh, + .local_bind_transform = zm.mul( + zm.quatToMat(rotation), + translation, + ), + .count = counter, + .keyframes = .{ + .{ .rotation = keyframes[0], .translation = translation }, + .{ .rotation = keyframes[1], .translation = translation }, + }, + }; + counter += 1; + return res; + } }; data: Tree(JointData), +animaton_duration: f32 = 1, +animaton_time: f32 = 0, -pub fn init( - data: Tree(JointData), -) @This() { +pub fn init(data: Tree(JointData)) !@This() { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const arena_allocator = arena.allocator(); + + var c_data = data; + + var iter = try c_data.levelOrderIterator(arena_allocator); + + while (iter.next()) |node| { + for (node.children.items) |*child| { + child.val.local_bind_transform = zm.mul( + child.val.local_bind_transform, + node.val.local_bind_transform, + ); + } + } return .{ .data = data }; } -pub fn draw(self: *@This(), allocator: std.mem.Allocator, shader: Shader) !void { - var iter = self.data.depthFirstIterator(); - defer iter.deinit(allocator); - while (try iter.next(allocator)) |node| { - const rotation = node.val.rotation; - // var current = node; - // while (current.parent) |parent| { - // rotation = zm.qmul(rotation, parent.val.rotation); - // current = parent; - // } +pub fn draw(self: *@This(), shader: Shader) !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); - const model_matrix = zm.mul(zm.quatToMat(rotation), node.val.translation); - const normal_matrix = zm.transpose(zm.inverse(model_matrix)); + var iter = try self.data.preOrderIterator(allocator); + defer iter.deinit(allocator); + + while (iter.next()) |node| { + const model_matrix = zm.mul( + node.val.animated_transform, + node.val.local_bind_transform, + ); shader.updateUniformMatrix("u_model_matrix", &.{model_matrix}); - shader.updateUniformMatrix("u_normal_matrix", &.{normal_matrix}); + shader.updateUniformMatrix("u_normal_matrix", &.{zm.transpose(zm.inverse(model_matrix))}); node.val.mesh.draw(shader); } } +pub fn animate(self: *@This(), delta_time: f32) !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const arena_allocator = arena.allocator(); + + var iter = try self.data.levelOrderIterator(arena_allocator); + + self.animaton_time += delta_time; + + if (self.animaton_time / self.animaton_duration < 1) { + while (iter.next()) |node| { + node.val.animated_transform = zm.mul( + zm.quatToMat(zm.slerp( + node.val.keyframes[0].rotation, + node.val.keyframes[1].rotation, + self.animaton_time / self.animaton_duration, + )), + node.val.keyframes[0].translation, + ); + + for (node.children.items) |*child| { + child.val.animated_transform = zm.mul( + node.val.animated_transform, + child.val.animated_transform, + ); + } + } + } else if (self.animaton_time / self.animaton_duration < 2) { + while (iter.next()) |node| { + node.val.animated_transform = zm.mul( + zm.quatToMat(zm.slerp( + node.val.keyframes[1].rotation, + node.val.keyframes[0].rotation, + self.animaton_time / self.animaton_duration - 1, + )), + node.val.keyframes[1].translation, + ); + + for (node.children.items) |*child| { + child.val.animated_transform = zm.mul( + child.val.animated_transform, + node.val.animated_transform, + ); + } + } + } else { + self.animaton_time = 0; + return; + } + + iter = try self.data.levelOrderIterator(arena_allocator); + + // while (iter.next()) |node| { + // for (node.children.items) |*child| { + // child.val.animated_transform = zm.mul( + // node.val.animated_transform, + // child.val.animated_transform, + // ); + // } + // } +} + pub fn deinit(self: *@This(), allocator: std.mem.Allocator) !void { try self.data.deinit(allocator); } diff --git a/src/main.zig b/src/main.zig index 0fc4e36..51a53cd 100644 --- a/src/main.zig +++ b/src/main.zig @@ -133,7 +133,6 @@ pub fn main() !void { var previous_time = glfw_helper.getTime(); var current_time: f64 = 0; var delta_time: f64 = undefined; - // var frame_counter: u16 = 0; // OpenGL setup const clear_color = FloatColor.sky_blue; @@ -162,8 +161,8 @@ pub fn main() !void { model_shader.updateUniformMatrix("u_normal_matrix", &.{zm.identity()}); floor.draw(model_shader); - // animateRobot(&frame_counter, robot); - try robot.draw(allocator, model_shader); + try robot.animate(@floatCast(delta_time)); + try robot.draw(model_shader); }, .fish => {}, } @@ -204,125 +203,207 @@ fn initFloor(textures: ?[]const Texture) Mesh { } fn initRobot(allocator: std.mem.Allocator, textures: ?[]const Texture) !Model { - // var meshes = try std.ArrayList(Mesh).initCapacity(allocator, 14); - // var translations = try std.ArrayList(Mat).initCapacity(allocator, 14); - var shape = Shape.initCube(); defer shape.deinit(); shape.unweld(); shape.computeNormals(); shape.translate(-0.5, -1, -0.5); - // shape.rotate(std.math.degreesToRadians(180), 1, 0, 0); - - // var foot = shape.clone(); - // defer foot.deinit(); - // foot.scale(0.1, 0.025, 0.15); - // meshes.appendNTimesAssumeCapacity(Mesh.initShape(foot, textures, .triangles), 2); - // translations.appendAssumeCapacity(zm.translation(-0.07, 0.025, 0)); - // translations.appendAssumeCapacity(zm.translation(0.07, 0.025, 0)); - - // var leg = shape.clone(); - // defer leg.deinit(); - // leg.scale(0.08, 0.2, 0.08); - // meshes.appendNTimesAssumeCapacity(Mesh.initShape(leg, textures, .triangles), 4); - // translations.appendAssumeCapacity(zm.translation(-0.07, 0.225, 0)); - // translations.appendAssumeCapacity(zm.translation(0.07, 0.225, 0)); - // translations.appendAssumeCapacity(zm.translation(-0.07, 0.425, 0)); - // translations.appendAssumeCapacity(zm.translation(0.07, 0.425, 0)); - - // var arm = shape.clone(); - // defer arm.deinit(); - // arm.scale(0.08, 0.2, 0.08); - // meshes.appendNTimesAssumeCapacity(Mesh.initShape(arm, textures, .triangles), 4); - // translations.appendAssumeCapacity(zm.translation(-0.3 / 2.0 - 0.04, 0.225 + 0.4, 0)); - // translations.appendAssumeCapacity(zm.translation(0.3 / 2.0 + 0.04, 0.225 + 0.4, 0)); - // translations.appendAssumeCapacity(zm.translation(-0.3 / 2.0 - 0.04, 0.425 + 0.4, 0)); - // translations.appendAssumeCapacity(zm.translation(0.3 / 2.0 + 0.04, 0.425 + 0.4, 0)); - - // var hand = shape.clone(); - // defer hand.deinit(); - // hand.scale(0.025, 0.08, 0.07); - // meshes.appendNTimesAssumeCapacity(Mesh.initShape(hand, textures, .triangles), 2); - // translations.appendAssumeCapacity(zm.translation(-0.3 / 2.0 - 0.04, 0.425, 0)); - // translations.appendAssumeCapacity(zm.translation(0.3 / 2.0 + 0.04, 0.425, 0)); var chest = shape.clone(); defer chest.deinit(); chest.scale(0.3, 0.4, 0.15); - // meshes.appendAssumeCapacity(Mesh.initShape(chest, textures, .triangles)); - // translations.appendAssumeCapacity(zm.translation(0, 0.425 + 0.4, 0)); - var model_data = Tree(Model.JointData).init( - .{ - .mesh = Mesh.initShape(chest, textures, .triangles), - .translation = zm.translation(0, 0.425 + 0.4, 0), - .rotation = zm.qidentity(), - }, + .init( + .initShape(chest, textures, .triangles), + zm.translation(0, 0.825, 0), + zm.qidentity(), + .{ zm.qidentity(), zm.qidentity() }, + ), ); var head = shape.clone(); defer head.deinit(); head.scale(0.1, 0.1, 0.1); + head.rotate(std.math.degreesToRadians(180), 1, 0, 0); + try model_data.root.append( + allocator, + .init( + .initShape(head, textures, .triangles), + zm.translation(0, 0, 0), + zm.qidentity(), + .{ + zm.quatFromRollPitchYaw(0, -0.5, 0), + zm.quatFromRollPitchYaw(0, 0.5, 0), + }, + ), + ); - try model_data.root.append(allocator, .{ - .mesh = Mesh.initShape(head, textures, .triangles), - .translation = zm.translation(0, 0.425 + 0.4 + 0.1, 0), - .rotation = zm.qidentity(), - }); - // meshes.appendAssumeCapacity(Mesh.initShape(head, textures, .triangles)); - // translations.appendAssumeCapacity(zm.translation(0, 0.425 + 0.4 + 0.1, 0)); + var arm = shape.clone(); + defer arm.deinit(); + arm.scale(0.08, 0.2, 0.08); + var right_arm = try model_data.root.create( + allocator, + .init( + .initShape(arm, textures, .triangles), + zm.translation(-0.3 / 2.0 - 0.04, 0, 0), + zm.qidentity(), + .{ + zm.quatFromRollPitchYaw(0.6, 0, 0), + zm.quatFromRollPitchYaw(-0.6, 0, 0), + }, + ), + ); + right_arm = try right_arm.create( + allocator, + .init( + .initShape(arm, textures, .triangles), + zm.translation(0, -0.2, 0), + zm.quatFromRollPitchYaw(-0.6, 0, 0), + .{ + zm.quatFromRollPitchYaw(0.6, 0, 0), + zm.quatFromRollPitchYaw(-0.6, 0, 0), + }, + ), + ); - return Model.init(model_data); + var left_arm = try model_data.root.create( + allocator, + .init( + .initShape(arm, textures, .triangles), + zm.translation(0.3 / 2.0 + 0.04, 0, 0), + zm.qidentity(), + .{ + zm.qidentity(), + zm.qidentity(), + }, + ), + ); + left_arm = try left_arm.create( + allocator, + .init( + .initShape(arm, textures, .triangles), + zm.translation(0, -0.2, 0), + zm.qidentity(), + .{ + zm.qidentity(), + zm.qidentity(), + }, + ), + ); + + var hand = shape.clone(); + defer hand.deinit(); + hand.scale(0.025, 0.08, 0.07); + try right_arm.append( + allocator, + .init( + .initShape(hand, textures, .triangles), + zm.translation(0, -0.2, 0), + zm.qidentity(), + .{ + zm.qidentity(), + zm.qidentity(), + }, + ), + ); + try left_arm.append( + allocator, + .init( + .initShape(hand, textures, .triangles), + zm.translation(0, -0.2, 0), + zm.qidentity(), + .{ + zm.qidentity(), + zm.qidentity(), + }, + ), + ); + + var leg = shape.clone(); + defer leg.deinit(); + leg.scale(0.08, 0.2, 0.08); + var right_leg = try model_data.root.create( + allocator, + .init( + .initShape(leg, textures, .triangles), + zm.translation(-0.07, -0.4, 0), + zm.qidentity(), + .{ + zm.qidentity(), + zm.qidentity(), + }, + ), + ); + right_leg = try right_leg.create( + allocator, + .init( + .initShape(leg, textures, .triangles), + zm.translation(0, -0.2, 0), + zm.qidentity(), + .{ + zm.qidentity(), + zm.qidentity(), + }, + ), + ); + + var left_leg = try model_data.root.create( + allocator, + .init( + .initShape(leg, textures, .triangles), + zm.translation(0.07, -0.4, 0), + zm.qidentity(), + .{ + zm.qidentity(), + zm.qidentity(), + }, + ), + ); + left_leg = try left_leg.create( + allocator, + .init( + .initShape(leg, textures, .triangles), + zm.translation(0, -0.2, 0), + zm.qidentity(), + .{ + zm.qidentity(), + zm.qidentity(), + }, + ), + ); + + var foot = shape.clone(); + defer foot.deinit(); + foot.scale(0.1, 0.025, 0.15); + try right_leg.append( + allocator, + .init( + .initShape(foot, textures, .triangles), + zm.translation(0, -0.2, 0), + zm.qidentity(), + .{ + zm.qidentity(), + zm.qidentity(), + }, + ), + ); + try left_leg.append( + allocator, + .init( + .initShape(foot, textures, .triangles), + zm.translation(0, -0.2, 0), + zm.qidentity(), + .{ + zm.qidentity(), + zm.qidentity(), + }, + ), + ); + + return try Model.init(model_data); } -// fn initRobot(allocator: std.mem.Allocator, textures: ?[]const Texture) Model { -// var shape = Shape.initCube(); -// defer shape.deinit(); - -// shape.unweld(); -// shape.computeNormals(); -// shape.translate(-0.5, -1, -0.5); - -// var chest = shape.clone(); -// defer chest.deinit(); -// chest.scale(0.3, 0.4, 0.15); - -// var model_data = Tree(Model.JointData).init(.{ -// .mesh = Mesh.initShape(chest, textures, .triangles), -// .translation = zm.translation(0, 0.425 + 0.4, 0), -// .rotation = zm.qidentity(), -// }); - -// var head = shape.clone(); -// defer head.deinit(); -// head.scale(0.1, 0.1, 0.1); - -// try model_data.root.append(allocator, .{ -// .mesh = Mesh.initShape(head, textures, .triangles), -// .translation = zm.translation(0, 0.425 + 0.4 + 0.1, 0), -// .rotation = zm.qidentity(), -// }); - -// return Model.init(model_data); -// } - -// fn animateRobot(frame_counter: *u16, robot: Model) void { -// if (frame_counter.* > robot.animation_frames) { -// robot.translations.items[2] = zm.mul(zm.rotationX(-std.math.degreesToRadians(1)), robot.translations.items[2]); -// robot.translations.items[3] = zm.mul(zm.rotationX(-std.math.degreesToRadians(1)), robot.translations.items[3]); -// } else { -// robot.translations.items[2] = zm.mul(zm.rotationX(std.math.degreesToRadians(1)), robot.translations.items[2]); -// robot.translations.items[3] = zm.mul(zm.rotationX(std.math.degreesToRadians(1)), robot.translations.items[3]); -// } - -// frame_counter.* += 1; - -// if (frame_counter.* > robot.animation_frames * 2) { -// frame_counter.* = 0; -// } -// } - fn drawGui(light_ubo: zgl.Buffer, light_shader: gl_helper.Shader, light_color: *zm.Vec) void { if (app_state.render_gui) { zgui.backend.newFrame(@intCast(app_state.width), @intCast(app_state.height)); diff --git a/src/tree.zig b/src/tree.zig index 076468b..006939f 100644 --- a/src/tree.zig +++ b/src/tree.zig @@ -11,7 +11,6 @@ pub fn Tree(comptime T: type) type { const Children = ArrayList(Node); const Node = struct { val: T, - // index: usize, children: ArrayList(Node), pub fn init(val: T) Node { @@ -25,99 +24,195 @@ pub fn Tree(comptime T: type) type { const child = Node.init(val); try self.children.append(allocator, child); } + + pub fn create(self: *Node, allocator: Allocator, val: T) !*Node { + const child = Node.init(val); + try self.children.append(allocator, child); + return &self.children.items[self.children.items.len - 1]; + } }; pub fn init(val: T) @This() { return .{ .root = .init(val) }; } - pub const DepthFirstIterator = struct { - const Frame = struct { - parent: ?*Node, - index: usize, - }; - const State = enum { - GoDeeper, - GoBroader, - }; - tree: *Tree(T), - current: ?*Node, - path: ArrayList(Frame), - state: State, + pub const PreOrderIterator = struct { + pre_order: ArrayList(*Node), + index: usize, - pub fn init(tree: *Tree(T)) DepthFirstIterator { - return DepthFirstIterator{ - .tree = tree, - .current = &tree.root, - .path = .empty, - .state = State.GoDeeper, + pub fn init(tree: *Tree(T), allocator: Allocator) !PreOrderIterator { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const arena_allocator = arena.allocator(); + + var stack = try ArrayList(*Node).initCapacity(arena_allocator, 1); + stack.appendAssumeCapacity(&tree.root); + + var pre_order = ArrayList(*Node).empty; + + while (stack.items.len > 0) { + if (stack.pop()) |tmp| { + try pre_order.insert(allocator, 0, tmp); + + for (0..tmp.children.items.len) |i| { + try stack.append(arena_allocator, &tmp.children.items[i]); + } + } + } + + return .{ + .index = 0, + .pre_order = pre_order, }; } - pub fn next(it: *DepthFirstIterator, allocator: Allocator) !?*Node { - // State machine - while (it.current) |current| { - switch (it.state) { - State.GoDeeper => { - // Follow child node until deepest possible level - if (current.children.items.len > 0) { - try it.path.append(allocator, .{ - .index = 0, - .parent = current, - }); - it.current = ¤t.children.items[0]; - } else { - it.state = State.GoBroader; - _ = it.path.pop(); - return current; - } - }, - State.GoBroader => { - const last_frame = it.path.pop(); - if (last_frame) |frame| { - if (frame.parent) |parent| { - if (parent.children.items.len > frame.index + 1) { - it.current = &parent.children.items[frame.index + 1]; - } else { - it.current = parent; - // return it.path.pop().?.parent; - } - } else { - return null; - } - // if (parent.children.items.len > current.index + 1) { - // it.current = &parent.children.items[current.index + 1]; - // it.state = .GoDeeper; - // } else { - // it.current = current.parent; - // return current.parent; - // } - } else { - return null; - } - }, - } + pub fn next(it: *PreOrderIterator) ?*Node { + while (it.index < it.pre_order.items.len) { + const val = it.pre_order.items[it.index]; + it.index += 1; + return val; } return null; } - pub fn reset(it: *DepthFirstIterator) void { - it.current = it.tree.root; - } - - pub fn deinit(self: *DepthFirstIterator, allocator: Allocator) void { - self.path.deinit(allocator); + pub fn deinit(self: *PreOrderIterator, allocator: Allocator) void { + self.pre_order.deinit(allocator); } }; - pub fn depthFirstIterator(tree: *@This()) DepthFirstIterator { - return DepthFirstIterator.init(tree); + pub fn preOrderIterator(tree: *@This(), allocator: Allocator) !PreOrderIterator { + return PreOrderIterator.init(tree, allocator); + } + + pub const PostOrderIterator = struct { + post_order: ArrayList(*Node), + index: usize, + + const Frame = struct { + node: *Node, + children_index: usize, + }; + + pub fn init(tree: *Tree(T), allocator: Allocator) !PostOrderIterator { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const arena_allocator = arena.allocator(); + + var stack = ArrayList(Frame).empty; + var current_root_index: usize = 0; + var post_order = ArrayList(*Node).empty; + + var curr_node: ?*Node = &tree.root; + while (curr_node != null or stack.items.len > 0) { + if (curr_node) |cur| { + try stack.append(arena_allocator, .{ + .children_index = current_root_index, + .node = cur, + }); + + if (cur.children.items.len >= 1) { + curr_node = &cur.children.items[0]; + } else { + curr_node = null; + } + continue; + } + + if (stack.pop()) |tmp| { + try post_order.append(allocator, tmp.node); + + while (stack.items.len > 0 and tmp.children_index == stack.items[stack.items.len - 1].node.children.items.len - 1) { + if (stack.pop()) |tmp2| { + try post_order.append(allocator, tmp2.node); + } + } + + if (stack.items.len > 0 and stack.items[stack.items.len - 1].node.children.items.len > tmp.children_index + 1) { + curr_node = &stack.items[stack.items.len - 1].node.children.items[tmp.children_index + 1]; + current_root_index = tmp.children_index + 1; + } + } + } + + return .{ + .index = 0, + .post_order = post_order, + }; + } + + pub fn next(it: *PostOrderIterator) ?*Node { + while (it.index < it.post_order.items.len) { + const val = it.post_order.items[it.index]; + it.index += 1; + return val; + } + return null; + } + + pub fn deinit(self: *PostOrderIterator, allocator: Allocator) void { + self.post_order.deinit(allocator); + } + }; + + pub fn postOrderIterator(tree: *@This(), allocator: Allocator) !PostOrderIterator { + return PostOrderIterator.init(tree, allocator); + } + + pub const LevelOrderIterator = struct { + level_order: ArrayList(*Node), + index: usize, + + pub fn init(tree: *Tree(T), allocator: Allocator) !LevelOrderIterator { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const arena_allocator = arena.allocator(); + + var stack = try ArrayList(*Node).initCapacity(arena_allocator, 1); + stack.appendAssumeCapacity(&tree.root); + + var level_order = ArrayList(*Node).empty; + + while (stack.items.len > 0) { + if (stack.pop()) |tmp| { + try level_order.append(allocator, tmp); + + for (0..tmp.children.items.len) |i| { + try stack.append(arena_allocator, &tmp.children.items[i]); + } + } + } + + return .{ + .index = 0, + .level_order = level_order, + }; + } + + pub fn next(it: *LevelOrderIterator) ?*Node { + while (it.index < it.level_order.items.len) { + const val = it.level_order.items[it.index]; + it.index += 1; + return val; + } + return null; + } + + pub fn deinit(self: *LevelOrderIterator, allocator: Allocator) void { + self.level_order.deinit(allocator); + } + }; + + pub fn levelOrderIterator(tree: *@This(), allocator: Allocator) !LevelOrderIterator { + return LevelOrderIterator.init(tree, allocator); } pub fn deinit(self: *@This(), allocator: Allocator) !void { - var iterator = self.depthFirstIterator(); - defer iterator.deinit(allocator); - while (try iterator.next(allocator)) |node| { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const arena_allocator = arena.allocator(); + + var iterator = try self.postOrderIterator(arena_allocator); + while (iterator.next()) |node| { node.children.deinit(allocator); } } @@ -156,20 +251,17 @@ test "Tree" { const UTree = Tree(u16); var tree = UTree.init(10); - defer tree.deinit(allocator); + defer tree.deinit(allocator) catch {}; try tree.root.append(allocator, 20); try tree.root.append(allocator, 500); - var iterator = tree.depthFirstIterator(); + try tree.root.children.items[1].append(allocator, 999); + + var iterator = try tree.levelOrderIterator(allocator); + defer iterator.deinit(allocator); std.debug.print("\n", .{}); while (iterator.next()) |node| { - var accumulator: u32 = node.val; - var current = node; - while (current.parent) |parent| { - accumulator *= parent.val; - current = parent; - } - std.debug.print("Val: {} Acc: {}\n", .{ node.val, accumulator }); + std.debug.print("Val: {}\n", .{node.val}); } }