aboutsummaryrefslogtreecommitdiff
path: root/src/misc.zig
diff options
context:
space:
mode:
authorNguyễn Gia Phong <mcsinyx@disroot.org>2021-10-02 16:57:18 +0700
committerNguyễn Gia Phong <mcsinyx@disroot.org>2021-10-02 16:57:18 +0700
commit7a4fdb3978c36e4309479806140f38669bcd3883 (patch)
tree46db5374c1e6864a6cf576d2b27263fea6f436ac /src/misc.zig
parent8bfbca0b2329605b92e0242ebd40bfad7372ba28 (diff)
downloadblackshades-7a4fdb3978c36e4309479806140f38669bcd3883.tar.gz
Convert 3D models to OFF
Diffstat (limited to 'src/misc.zig')
-rw-r--r--src/misc.zig91
1 files changed, 91 insertions, 0 deletions
diff --git a/src/misc.zig b/src/misc.zig
index 2bbb9f0..7ff4680 100644
--- a/src/misc.zig
+++ b/src/misc.zig
@@ -24,12 +24,15 @@ usingnamespace @cImport({
});
const Dir = std.fs.Dir;
+const TokenIterator = std.mem.TokenIterator;
const al = @import("zeal");
const allocPrint = std.fmt.allocPrint;
const allocator = std.heap.c_allocator;
+const assert = std.debug.assert;
const count = std.mem.count;
const cwd = std.fs.cwd;
const data_dir = @import("build_options").data_dir ++ [_]u8{ sep };
+const endsWith = std.mem.endsWith;
const eql = std.mem.eql;
const free = std.c.free;
const join = std.fs.path.joinZ;
@@ -38,6 +41,7 @@ const parseFloat = std.fmt.parseFloat;
const parseInt = std.fmt.parseInt;
const sep = std.fs.path.sep;
const span = std.mem.span;
+const startsWith = std.mem.startsWith;
const std = @import("std");
const tokenize = std.mem.tokenize;
@@ -146,6 +150,93 @@ export fn loadJoints(joints: [*]Joint) void {
}
}
+const Vertex = extern struct {
+ x: f32, y: f32, z: f32,
+};
+
+const Face = extern struct {
+ // Only support triangles
+ v: [3]u16,
+ r: f32, g: f32, b: f32,
+};
+
+const OffIterator = struct {
+ token_iterator: TokenIterator,
+
+ pub fn init(buffer: []const u8) OffIterator {
+ var self = .{ .token_iterator = tokenize(buffer, "\n") };
+ if (!endsWith(u8, self.token_iterator.next().?, "OFF"))
+ self.token_iterator.reset();
+ return self;
+ }
+
+ pub fn next(self: *OffIterator) ?TokenIterator {
+ while (self.token_iterator.next()) |line| {
+ var words = tokenize(line, " ");
+ if (words.next()) |word| { // not empty
+ if (!startsWith(u8, word, "#")) { // not comment
+ words.reset();
+ return words;
+ }
+ }
+ }
+ return null;
+ }
+};
+
+/// Load model from given OFF file.
+export fn loadModel(path: [*:0]const u8) extern struct {
+ vertices: extern struct {
+ ptr: [*]Vertex,
+ len: usize,
+ },
+ faces: extern struct {
+ ptr: [*]Face,
+ len: usize,
+ },
+} {
+ var dir = cwd().openDir(data_dir ++ "models", .{}) catch unreachable;
+ defer dir.close();
+ const file = dir.readFileAlloc(allocator, span(path), max_size)
+ catch unreachable;
+ defer allocator.free(file);
+ var lines = OffIterator.init(file);
+ var counts = lines.next().?;
+ const vertex_count = parseInt(usize, counts.next().?, 10) catch unreachable;
+ const face_count = parseInt(usize, counts.next().?, 10) catch unreachable;
+
+ const vertices = allocator.alloc(Vertex, vertex_count) catch unreachable;
+ for (vertices) |*vertex| {
+ var numbers = lines.next().?;
+ vertex.* = .{
+ .x = parseFloat(f32, numbers.next().?) catch unreachable,
+ .y = parseFloat(f32, numbers.next().?) catch unreachable,
+ .z = parseFloat(f32, numbers.next().?) catch unreachable,
+ };
+ }
+
+ const faces = allocator.alloc(Face, face_count) catch unreachable;
+ for (faces) |*face| {
+ var numbers = lines.next().?;
+ assert(eql(u8, numbers.next().?, "3"));
+ face.* = .{
+ .v = .{
+ parseInt(u16, numbers.next().?, 10) catch unreachable,
+ parseInt(u16, numbers.next().?, 10) catch unreachable,
+ parseInt(u16, numbers.next().?, 10) catch unreachable,
+ },
+ .r = parseFloat(f32, numbers.next().?) catch unreachable,
+ .g = parseFloat(f32, numbers.next().?) catch unreachable,
+ .b = parseFloat(f32, numbers.next().?) catch unreachable,
+ };
+ }
+
+ return .{
+ .vertices = .{ .ptr = vertices.ptr , .len = vertices.len },
+ .faces = .{ .ptr = faces.ptr , .len = faces.len },
+ };
+}
+
const Muscle = extern struct {
length: f32,
initlen: f32,