diff --git a/README.md b/README.md index 3f4f5a3..2d6feeb 100644 --- a/README.md +++ b/README.md @@ -74,11 +74,12 @@ pub fn main() !void { const data = try zmesh.io.zcgltf.parseAndLoadFile(content_dir ++ "cube.gltf"); defer zmesh.io.zcgltf.freeData(data); - var mesh_indices = std.ArrayList(u32).init(allocator); - var mesh_positions = std.ArrayList([3]f32).init(allocator); - var mesh_normals = std.ArrayList([3]f32).init(allocator); + var mesh_indices = std.ArrayListUnmanaged(u32){}; + var mesh_positions = std.ArrayListUnmanaged([3]f32){}; + var mesh_normals = std.ArrayListUnmanaged([3]f32){}; - zmesh.io.zcgltf.appendMeshPrimitive( + try zmesh.io.zcgltf.appendMeshPrimitive( + allocator, data, 0, // mesh index 0, // gltf primitive index (submesh index) @@ -98,8 +99,8 @@ pub fn main() !void { normal: [3]f32, }; - var remap = std.ArrayList(u32).init(allocator); - remap.resize(src_indices.items.len) catch unreachable; + var remap = std.ArrayListUnmanaged(u32){}; + try remap.resize(allocator, src_indices.items.len); const num_unique_vertices = zmesh.opt.generateVertexRemap( remap.items, // 'vertex remap' (destination) @@ -108,8 +109,8 @@ pub fn main() !void { src_vertices.items, // non-optimized vertices ); - var optimized_vertices = std.ArrayList(Vertex).init(allocator); - optimized_vertices.resize(num_unique_vertices) catch unreachable; + var optimized_vertices = std.ArrayListUnmanaged(Vertex){}; + try optimized_vertices.resize(allocator, num_unique_vertices); zmesh.opt.remapVertexBuffer( Vertex, // Zig type describing your vertex diff --git a/build.zig b/build.zig index 6719288..5768607 100644 --- a/build.zig +++ b/build.zig @@ -23,34 +23,31 @@ pub fn build(b: *std.Build) void { } const options_module = options_step.createModule(); - - _ = b.addModule("root", .{ + const zmesh_module = b.addModule("root", .{ .root_source_file = b.path("src/root.zig"), + .target = target, + .optimize = optimize, .imports = &.{ .{ .name = "zmesh_options", .module = options_module }, }, }); - const zmesh_lib = if (options.shared) blk: { - const lib = b.addSharedLibrary(.{ - .name = "zmesh", + const zmesh_lib = b.addLibrary(.{ + .name = "zmesh", + .linkage = if (options.shared) .dynamic else .static, + .root_module = b.createModule(.{ .target = target, .optimize = optimize, - }); + }), + }); - if (target.result.os.tag == .windows) { - lib.root_module.addCMacro("PAR_SHAPES_API", "__declspec(dllexport)"); - lib.root_module.addCMacro("CGLTF_API", "__declspec(dllexport)"); - lib.root_module.addCMacro("MESHOPTIMIZER_API", "__declspec(dllexport)"); - lib.root_module.addCMacro("ZMESH_API", "__declspec(dllexport)"); - } + if (options.shared and target.result.os.tag == .windows) { + zmesh_lib.root_module.addCMacro("PAR_SHAPES_API", "__declspec(dllexport)"); + zmesh_lib.root_module.addCMacro("CGLTF_API", "__declspec(dllexport)"); + zmesh_lib.root_module.addCMacro("MESHOPTIMIZER_API", "__declspec(dllexport)"); + zmesh_lib.root_module.addCMacro("ZMESH_API", "__declspec(dllexport)"); + } - break :blk lib; - } else b.addStaticLibrary(.{ - .name = "zmesh", - .target = target, - .optimize = optimize, - }); b.installArtifact(zmesh_lib); zmesh_lib.linkLibC(); @@ -93,9 +90,7 @@ pub fn build(b: *std.Build) void { const tests = b.addTest(.{ .name = "zmesh-tests", - .root_source_file = b.path("src/root.zig"), - .target = target, - .optimize = optimize, + .root_module = zmesh_module, }); b.installArtifact(tests); diff --git a/build.zig.zon b/build.zig.zon index 1b418ea..2ec8cf3 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -2,7 +2,7 @@ .name = .zmesh, .fingerprint = 0xb56bea39e6c0eda0, .version = "0.11.0-dev", - .minimum_zig_version = "0.14.0", + .minimum_zig_version = "0.15.1", .paths = .{ "build.zig", "build.zig.zon", diff --git a/src/Shape.zig b/src/Shape.zig index bf440f2..90b4dee 100644 --- a/src/Shape.zig +++ b/src/Shape.zig @@ -458,20 +458,22 @@ test "zmesh.invert" { test "zmesh.custom" { const zmesh = @import("root.zig"); - zmesh.init(std.testing.allocator); + const allocator = std.testing.allocator; + + zmesh.init(allocator); defer zmesh.deinit(); - var positions = std.ArrayList([3]f32).init(std.testing.allocator); - defer positions.deinit(); - try positions.append(.{ 0.0, 0.0, 0.0 }); - try positions.append(.{ 1.0, 0.0, 0.0 }); - try positions.append(.{ 1.0, 0.0, 1.0 }); - - var indices = std.ArrayList(IndexType).init(std.testing.allocator); - defer indices.deinit(); - try indices.append(0); - try indices.append(1); - try indices.append(2); + var positions: std.ArrayList([3]f32) = .{}; + defer positions.deinit(allocator); + try positions.append(allocator, .{ 0.0, 0.0, 0.0 }); + try positions.append(allocator, .{ 1.0, 0.0, 0.0 }); + try positions.append(allocator, .{ 1.0, 0.0, 1.0 }); + + var indices: std.ArrayList(IndexType) = .{}; + defer indices.deinit(allocator); + try indices.append(allocator, 0); + try indices.append(allocator, 1); + try indices.append(allocator, 2); var shape = Shape.init(indices, positions, null, null); defer shape.deinit(); diff --git a/src/io.zig b/src/io.zig index 7587c88..6339603 100644 --- a/src/io.zig +++ b/src/io.zig @@ -1,8 +1,6 @@ const std = @import("std"); const assert = std.debug.assert; -const mem = @import("memory.zig"); - /// Deprecated. Use `zmesh.io.zcgltf.parseAndLoadFile` instead. pub const parseAndLoadFile = zcgltf.parseAndLoadFile; /// Deprecated. Use `zmesh.io.zcgltf.freeData` instead. @@ -10,136 +8,4 @@ pub const freeData = zcgltf.freeData; /// Deprecated. Use `zmesh.io.zcgltf.appendMeshPrimitive` instead. pub const appendMeshPrimitive = zcgltf.appendMeshPrimitive; -pub const zcgltf = struct { - const bindings = @import("zcgltf.zig"); - const Data = bindings.Data; - - pub usingnamespace bindings; - - pub fn parseAndLoadFile(pathname: [:0]const u8) bindings.Error!*Data { - const options = bindings.Options{ - .memory = .{ - .alloc_func = mem.zmeshAllocUser, - .free_func = mem.zmeshFreeUser, - }, - }; - - const data = try bindings.parseFile(options, pathname); - errdefer bindings.free(data); - - try bindings.loadBuffers(options, data, pathname); - - return data; - } - - pub fn freeData(data: *Data) void { - bindings.free(data); - } - - pub fn appendMeshPrimitive( - data: *Data, - mesh_index: u32, - prim_index: u32, - indices: *std.ArrayList(u32), - positions: *std.ArrayList([3]f32), - normals: ?*std.ArrayList([3]f32), - texcoords0: ?*std.ArrayList([2]f32), - tangents: ?*std.ArrayList([4]f32), - ) !void { - assert(mesh_index < data.meshes_count); - assert(prim_index < data.meshes.?[mesh_index].primitives_count); - - const mesh = &data.meshes.?[mesh_index]; - const prim = &mesh.primitives[prim_index]; - - const num_vertices: u32 = @as(u32, @intCast(prim.attributes[0].data.count)); - const num_indices: u32 = @as(u32, @intCast(prim.indices.?.count)); - - // Indices. - { - try indices.ensureTotalCapacity(indices.items.len + num_indices); - - const accessor = prim.indices.?; - const buffer_view = accessor.buffer_view.?; - - assert(accessor.stride == buffer_view.stride or buffer_view.stride == 0); - assert(buffer_view.buffer.data != null); - - const data_addr = @as([*]const u8, @ptrCast(buffer_view.buffer.data)) + - accessor.offset + buffer_view.offset; - - if (accessor.stride == 1) { - if (accessor.component_type != .r_8u) { - return error.InvalidIndicesAccessorComponentType; - } - const src = @as([*]const u8, @ptrCast(data_addr)); - var i: u32 = 0; - while (i < num_indices) : (i += 1) { - indices.appendAssumeCapacity(src[i]); - } - } else if (accessor.stride == 2) { - if (accessor.component_type != .r_16u) { - return error.InvalidIndicesAccessorComponentType; - } - const src = @as([*]const u16, @ptrCast(@alignCast(data_addr))); - var i: u32 = 0; - while (i < num_indices) : (i += 1) { - indices.appendAssumeCapacity(src[i]); - } - } else if (accessor.stride == 4) { - if (accessor.component_type != .r_32u) { - return error.InvalidIndicesAccessorComponentType; - } - const src = @as([*]const u32, @ptrCast(@alignCast(data_addr))); - var i: u32 = 0; - while (i < num_indices) : (i += 1) { - indices.appendAssumeCapacity(src[i]); - } - } else { - return error.InvalidIndicesAccessorStride; - } - } - - // Attributes. - { - const attributes = prim.attributes[0..prim.attributes_count]; - for (attributes) |attrib| { - const accessor = attrib.data; - assert(accessor.component_type == .r_32f); - - const buffer_view = accessor.buffer_view.?; - assert(buffer_view.buffer.data != null); - - assert(accessor.stride == buffer_view.stride or buffer_view.stride == 0); - assert(accessor.stride * accessor.count == buffer_view.size); - - const data_addr = @as([*]const u8, @ptrCast(buffer_view.buffer.data)) + - accessor.offset + buffer_view.offset; - - if (attrib.type == .position) { - assert(accessor.type == .vec3); - const slice = @as([*]const [3]f32, @ptrCast(@alignCast(data_addr)))[0..num_vertices]; - try positions.appendSlice(slice); - } else if (attrib.type == .normal) { - if (normals) |n| { - assert(accessor.type == .vec3); - const slice = @as([*]const [3]f32, @ptrCast(@alignCast(data_addr)))[0..num_vertices]; - try n.appendSlice(slice); - } - } else if (attrib.type == .texcoord) { - if (texcoords0) |tc| { - assert(accessor.type == .vec2); - const slice = @as([*]const [2]f32, @ptrCast(@alignCast(data_addr)))[0..num_vertices]; - try tc.appendSlice(slice); - } - } else if (attrib.type == .tangent) { - if (tangents) |tan| { - assert(accessor.type == .vec4); - const slice = @as([*]const [4]f32, @ptrCast(@alignCast(data_addr)))[0..num_vertices]; - try tan.appendSlice(slice); - } - } - } - } - } -}; +pub const zcgltf = @import("zcgltf.zig"); diff --git a/src/memory.zig b/src/memory.zig index 132631f..b72d580 100644 --- a/src/memory.zig +++ b/src/memory.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const options = @import("zmesh_options"); pub fn init(alloc: std.mem.Allocator) void { std.debug.assert(mem_allocator == null and mem_allocations == null); @@ -7,10 +8,27 @@ pub fn init(alloc: std.mem.Allocator) void { mem_allocations = std.AutoHashMap(usize, usize).init(alloc); mem_allocations.?.ensureTotalCapacity(32) catch unreachable; - zmeshMallocPtr = zmeshMalloc; - zmeshCallocPtr = zmeshCalloc; - zmeshReallocPtr = zmeshRealloc; - zmeshFreePtr = zmeshFree; + const zmeshMallocPtr = @extern(*?*const fn (size: usize) callconv(.c) ?*anyopaque, .{ + .name = "zmeshMallocPtr", + .is_dll_import = options.shared, + }); + const zmeshCallocPtr = @extern(*?*const fn (num: usize, size: usize) callconv(.c) ?*anyopaque, .{ + .name = "zmeshCallocPtr", + .is_dll_import = options.shared, + }); + const zmeshReallocPtr = @extern(*?*const fn (ptr: ?*anyopaque, size: usize) callconv(.c) ?*anyopaque, .{ + .name = "zmeshReallocPtr", + .is_dll_import = options.shared, + }); + const zmeshFreePtr = @extern(*?*const fn (maybe_ptr: ?*anyopaque) callconv(.c) void, .{ + .name = "zmeshFreePtr", + .is_dll_import = options.shared, + }); + + zmeshMallocPtr.* = zmeshMalloc; + zmeshCallocPtr.* = zmeshCalloc; + zmeshReallocPtr.* = zmeshRealloc; + zmeshFreePtr.* = zmeshFree; meshopt_setAllocator(zmeshMalloc, zmeshFree); } @@ -20,8 +38,8 @@ pub fn deinit() void { mem_allocator = null; } -const MallocFn = *const fn (size: usize) callconv(.C) ?*anyopaque; -const FreeFn = *const fn (ptr: ?*anyopaque) callconv(.C) void; +const MallocFn = *const fn (size: usize) callconv(.c) ?*anyopaque; +const FreeFn = *const fn (ptr: ?*anyopaque) callconv(.c) void; extern fn meshopt_setAllocator( allocate: MallocFn, @@ -31,11 +49,9 @@ extern fn meshopt_setAllocator( var mem_allocator: ?std.mem.Allocator = null; var mem_allocations: ?std.AutoHashMap(usize, usize) = null; var mem_mutex: std.Thread.Mutex = .{}; -const mem_alignment = 16; +const mem_alignment: std.mem.Alignment = .@"16"; -extern var zmeshMallocPtr: ?*const fn (size: usize) callconv(.C) ?*anyopaque; - -pub fn zmeshMalloc(size: usize) callconv(.C) ?*anyopaque { +pub fn zmeshMalloc(size: usize) callconv(.c) ?*anyopaque { mem_mutex.lock(); defer mem_mutex.unlock(); @@ -50,9 +66,7 @@ pub fn zmeshMalloc(size: usize) callconv(.C) ?*anyopaque { return mem.ptr; } -extern var zmeshCallocPtr: ?*const fn (num: usize, size: usize) callconv(.C) ?*anyopaque; - -fn zmeshCalloc(num: usize, size: usize) callconv(.C) ?*anyopaque { +fn zmeshCalloc(num: usize, size: usize) callconv(.c) ?*anyopaque { const ptr = zmeshMalloc(num * size); if (ptr != null) { @memset(@as([*]u8, @ptrCast(ptr))[0 .. num * size], 0); @@ -61,23 +75,21 @@ fn zmeshCalloc(num: usize, size: usize) callconv(.C) ?*anyopaque { return null; } -pub fn zmeshAllocUser(user: ?*anyopaque, size: usize) callconv(.C) ?*anyopaque { +pub fn zmeshAllocUser(user: ?*anyopaque, size: usize) callconv(.c) ?*anyopaque { _ = user; return zmeshMalloc(size); } -extern var zmeshReallocPtr: ?*const fn (ptr: ?*anyopaque, size: usize) callconv(.C) ?*anyopaque; - -fn zmeshRealloc(ptr: ?*anyopaque, size: usize) callconv(.C) ?*anyopaque { +fn zmeshRealloc(ptr: ?*anyopaque, size: usize) callconv(.c) ?*anyopaque { mem_mutex.lock(); defer mem_mutex.unlock(); const old_size = if (ptr != null) mem_allocations.?.get(@intFromPtr(ptr.?)).? else 0; const old_mem = if (old_size > 0) - @as([*]align(mem_alignment) u8, @ptrCast(@alignCast(ptr)))[0..old_size] + @as([*]align(mem_alignment.toByteUnits()) u8, @ptrCast(@alignCast(ptr)))[0..old_size] else - @as([*]align(mem_alignment) u8, undefined)[0..0]; + @as([*]align(mem_alignment.toByteUnits()) u8, undefined)[0..0]; const mem = mem_allocator.?.realloc(old_mem, size) catch @panic("zmesh: out of memory"); @@ -91,20 +103,18 @@ fn zmeshRealloc(ptr: ?*anyopaque, size: usize) callconv(.C) ?*anyopaque { return mem.ptr; } -extern var zmeshFreePtr: ?*const fn (maybe_ptr: ?*anyopaque) callconv(.C) void; - -fn zmeshFree(maybe_ptr: ?*anyopaque) callconv(.C) void { +fn zmeshFree(maybe_ptr: ?*anyopaque) callconv(.c) void { if (maybe_ptr) |ptr| { mem_mutex.lock(); defer mem_mutex.unlock(); const size = mem_allocations.?.fetchRemove(@intFromPtr(ptr)).?.value; - const mem = @as([*]align(mem_alignment) u8, @ptrCast(@alignCast(ptr)))[0..size]; + const mem = @as([*]align(mem_alignment.toByteUnits()) u8, @ptrCast(@alignCast(ptr)))[0..size]; mem_allocator.?.free(mem); } } -pub fn zmeshFreeUser(user: ?*anyopaque, ptr: ?*anyopaque) callconv(.C) void { +pub fn zmeshFreeUser(user: ?*anyopaque, ptr: ?*anyopaque) callconv(.c) void { _ = user; zmeshFree(ptr); } diff --git a/src/zcgltf.zig b/src/zcgltf.zig index a3de0ed..968acdd 100644 --- a/src/zcgltf.zig +++ b/src/zcgltf.zig @@ -1,11 +1,140 @@ const builtin = @import("builtin"); const std = @import("std"); const assert = std.debug.assert; +const mem = @import("memory.zig"); pub const Bool32 = i32; pub const CString = [*:0]const u8; pub const MutCString = [*:0]u8; +pub fn parseAndLoadFile(pathname: [:0]const u8) Error!*Data { + const options = Options{ + .memory = .{ + .alloc_func = mem.zmeshAllocUser, + .free_func = mem.zmeshFreeUser, + }, + }; + + const data = try parseFile(options, pathname); + errdefer free(data); + + try loadBuffers(options, data, pathname); + + return data; +} + +pub fn freeData(data: *Data) void { + free(data); +} + +pub fn appendMeshPrimitive( + allocator: std.mem.Allocator, + data: *Data, + mesh_index: u32, + prim_index: u32, + indices: *std.ArrayListUnmanaged(u32), + positions: *std.ArrayListUnmanaged([3]f32), + normals: ?*std.ArrayListUnmanaged([3]f32), + texcoords0: ?*std.ArrayListUnmanaged([2]f32), + tangents: ?*std.ArrayListUnmanaged([4]f32), +) !void { + assert(mesh_index < data.meshes_count); + assert(prim_index < data.meshes.?[mesh_index].primitives_count); + + const mesh = &data.meshes.?[mesh_index]; + const prim = &mesh.primitives[prim_index]; + + const num_vertices: u32 = @as(u32, @intCast(prim.attributes[0].data.count)); + const num_indices: u32 = @as(u32, @intCast(prim.indices.?.count)); + + // Indices. + { + try indices.ensureTotalCapacity(allocator, indices.items.len + num_indices); + + const accessor = prim.indices.?; + const buffer_view = accessor.buffer_view.?; + + assert(accessor.stride == buffer_view.stride or buffer_view.stride == 0); + assert(buffer_view.buffer.data != null); + + const data_addr = @as([*]const u8, @ptrCast(buffer_view.buffer.data)) + + accessor.offset + buffer_view.offset; + + if (accessor.stride == 1) { + if (accessor.component_type != .r_8u) { + return error.InvalidIndicesAccessorComponentType; + } + const src = @as([*]const u8, @ptrCast(data_addr)); + var i: u32 = 0; + while (i < num_indices) : (i += 1) { + indices.appendAssumeCapacity(src[i]); + } + } else if (accessor.stride == 2) { + if (accessor.component_type != .r_16u) { + return error.InvalidIndicesAccessorComponentType; + } + const src = @as([*]const u16, @ptrCast(@alignCast(data_addr))); + var i: u32 = 0; + while (i < num_indices) : (i += 1) { + indices.appendAssumeCapacity(src[i]); + } + } else if (accessor.stride == 4) { + if (accessor.component_type != .r_32u) { + return error.InvalidIndicesAccessorComponentType; + } + const src = @as([*]const u32, @ptrCast(@alignCast(data_addr))); + var i: u32 = 0; + while (i < num_indices) : (i += 1) { + indices.appendAssumeCapacity(src[i]); + } + } else { + return error.InvalidIndicesAccessorStride; + } + } + + // Attributes. + { + const attributes = prim.attributes[0..prim.attributes_count]; + for (attributes) |attrib| { + const accessor = attrib.data; + assert(accessor.component_type == .r_32f); + + const buffer_view = accessor.buffer_view.?; + assert(buffer_view.buffer.data != null); + + assert(accessor.stride == buffer_view.stride or buffer_view.stride == 0); + assert(accessor.stride * accessor.count == buffer_view.size); + + const data_addr = @as([*]const u8, @ptrCast(buffer_view.buffer.data)) + + accessor.offset + buffer_view.offset; + + if (attrib.type == .position) { + assert(accessor.type == .vec3); + const slice = @as([*]const [3]f32, @ptrCast(@alignCast(data_addr)))[0..num_vertices]; + try positions.appendSlice(allocator, slice); + } else if (attrib.type == .normal) { + if (normals) |n| { + assert(accessor.type == .vec3); + const slice = @as([*]const [3]f32, @ptrCast(@alignCast(data_addr)))[0..num_vertices]; + try n.appendSlice(allocator, slice); + } + } else if (attrib.type == .texcoord) { + if (texcoords0) |tc| { + assert(accessor.type == .vec2); + const slice = @as([*]const [2]f32, @ptrCast(@alignCast(data_addr)))[0..num_vertices]; + try tc.appendSlice(allocator, slice); + } + } else if (attrib.type == .tangent) { + if (tangents) |tan| { + assert(accessor.type == .vec4); + const slice = @as([*]const [4]f32, @ptrCast(@alignCast(data_addr)))[0..num_vertices]; + try tan.appendSlice(allocator, slice); + } + } + } + } +} + pub const FileType = enum(c_int) { invalid, gltf, @@ -25,8 +154,8 @@ pub const Result = enum(c_int) { legacy_gltf, }; -const MallocFn = *const fn (user: ?*anyopaque, size: usize) callconv(.C) ?*anyopaque; -const FreeFn = *const fn (user: ?*anyopaque, ptr: ?*anyopaque) callconv(.C) void; +const MallocFn = *const fn (user: ?*anyopaque, size: usize) callconv(.c) ?*anyopaque; +const FreeFn = *const fn (user: ?*anyopaque, ptr: ?*anyopaque) callconv(.c) void; pub const MemoryOptions = extern struct { alloc_func: ?MallocFn = null, @@ -41,9 +170,9 @@ pub const FileOptions = extern struct { CString, *usize, *?*anyopaque, - ) callconv(.C) Result; + ) callconv(.c) Result; - const ReleaseFn = *const fn (*const MemoryOptions, *const FileOptions, ?*anyopaque) callconv(.C) void; + const ReleaseFn = *const fn (*const MemoryOptions, *const FileOptions, ?*anyopaque) callconv(.c) void; read: ?ReadFn = null, release: ?ReleaseFn = null,