diff --git a/build.zig b/build.zig index 6719288..7f86a02 100644 --- a/build.zig +++ b/build.zig @@ -24,32 +24,34 @@ pub fn build(b: *std.Build) void { const options_module = options_step.createModule(); - _ = b.addModule("root", .{ + const mod = 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(.{ + const lib = b.addLibrary(.{ .name = "zmesh", - .target = target, - .optimize = optimize, + .root_module = mod, + .linkage = .dynamic, }); 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)"); + mod.addCMacro("PAR_SHAPES_API", "__declspec(dllexport)"); + mod.addCMacro("CGLTF_API", "__declspec(dllexport)"); + mod.addCMacro("MESHOPTIMIZER_API", "__declspec(dllexport)"); + mod.addCMacro("ZMESH_API", "__declspec(dllexport)"); } break :blk lib; - } else b.addStaticLibrary(.{ + } else b.addLibrary(.{ .name = "zmesh", - .target = target, - .optimize = optimize, + .root_module = mod, + .linkage = .static, }); b.installArtifact(zmesh_lib); @@ -93,13 +95,10 @@ 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 = mod, }); b.installArtifact(tests); - tests.linkLibrary(zmesh_lib); tests.addIncludePath(b.path("libs/cgltf")); test_step.dependOn(&b.addRunArtifact(tests).step); 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..6e497fc 100644 --- a/src/Shape.zig +++ b/src/Shape.zig @@ -1,6 +1,8 @@ -const builtin = @import("builtin"); const std = @import("std"); const assert = std.debug.assert; +const expect = std.testing.expect; +const builtin = @import("builtin"); + const zmeshMalloc = @import("memory.zig").zmeshMalloc; pub const IndexType: type = blk: { @@ -25,10 +27,10 @@ texcoords: ?[][2]f32, handle: ShapeHandle, pub fn init( - indices: std.ArrayList(IndexType), - positions: std.ArrayList([3]f32), - maybe_normals: ?std.ArrayList([3]f32), - maybe_texcoords: ?std.ArrayList([2]f32), + indices: std.array_list.Managed(IndexType), + positions: std.array_list.Managed([3]f32), + maybe_normals: ?std.array_list.Managed([3]f32), + maybe_texcoords: ?std.array_list.Managed([2]f32), ) Shape { const handle = par_shapes_create_empty(); const parmesh = @as( @@ -248,7 +250,7 @@ pub const UvToPositionFn = *const fn ( uv: *const [2]f32, position: *[3]f32, userdata: ?*anyopaque, -) callconv(.C) void; +) callconv(.c) void; pub fn initParametric( fun: UvToPositionFn, @@ -327,8 +329,6 @@ extern fn par_shapes_create_parametric( extern fn par_shapes_create_empty() ShapeHandle; const test_enable_write_to_disk = false; -const expect = std.testing.expect; - test "zmesh.basic" { const zmesh = @import("root.zig"); @@ -461,13 +461,13 @@ test "zmesh.custom" { zmesh.init(std.testing.allocator); defer zmesh.deinit(); - var positions = std.ArrayList([3]f32).init(std.testing.allocator); + var positions = std.array_list.Managed([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); + var indices = std.array_list.Managed(IndexType).init(std.testing.allocator); defer indices.deinit(); try indices.append(0); try indices.append(1); diff --git a/src/io.zig b/src/io.zig index 7587c88..294f9dc 100644 --- a/src/io.zig +++ b/src/io.zig @@ -1,5 +1,6 @@ const std = @import("std"); const assert = std.debug.assert; +const builtin = @import("builtin"); const mem = @import("memory.zig"); @@ -11,40 +12,1012 @@ pub const freeData = zcgltf.freeData; pub const appendMeshPrimitive = zcgltf.appendMeshPrimitive; pub const zcgltf = struct { - const bindings = @import("zcgltf.zig"); - const Data = bindings.Data; + pub const Bool32 = i32; + pub const CString = [*:0]const u8; + pub const MutCString = [*:0]u8; - pub usingnamespace bindings; + pub const FileType = enum(c_int) { + invalid, + gltf, + glb, + }; - pub fn parseAndLoadFile(pathname: [:0]const u8) bindings.Error!*Data { - const options = bindings.Options{ + pub const Result = enum(c_int) { + success, + data_too_short, + unknown_format, + invalid_json, + invalid_gltf, + invalid_options, + file_not_found, + io_error, + out_of_memory, + legacy_gltf, + }; + + 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, + free_func: ?FreeFn = null, + user_data: ?*anyopaque = null, + }; + + pub const FileOptions = extern struct { + const ReadFn = *const fn ( + *const MemoryOptions, + *const FileOptions, + CString, + *usize, + *?*anyopaque, + ) callconv(.c) Result; + + const ReleaseFn = *const fn (*const MemoryOptions, *const FileOptions, ?*anyopaque) callconv(.c) void; + + read: ?ReadFn = null, + release: ?ReleaseFn = null, + user_data: ?*anyopaque = null, + }; + + pub const Options = extern struct { + file_type: FileType = .invalid, + json_token_count: usize = 0, + memory: MemoryOptions = .{}, + file: FileOptions = .{}, + }; + + pub const BufferViewType = enum(c_int) { + invalid, + indices, + vertices, + }; + + pub const AttributeType = enum(c_int) { + invalid, + position, + normal, + tangent, + texcoord, + color, + joints, + weights, + custom, + }; + + pub const ComponentType = enum(c_int) { + invalid, + r_8, + r_8u, + r_16, + r_16u, + r_32u, + r_32f, + }; + + pub const Type = enum(c_int) { + invalid, + scalar, + vec2, + vec3, + vec4, + mat2, + mat3, + mat4, + + pub fn numComponents(dtype: Type) usize { + return switch (dtype) { + .vec2 => 2, + .vec3 => 3, + .vec4 => 4, + .mat2 => 4, + .mat3 => 9, + .mat4 => 16, + else => 1, + }; + } + }; + + pub const PrimitiveType = enum(c_int) { + points, + lines, + line_loop, + line_strip, + triangles, + triangle_strip, + triangle_fan, + }; + + pub const AlphaMode = enum(c_int) { + @"opaque", + mask, + blend, + }; + + pub const AnimationPathType = enum(c_int) { + invalid, + translation, + rotation, + scale, + weights, + }; + + pub const InterpolationType = enum(c_int) { + linear, + step, + cubic_spline, + }; + + pub const CameraType = enum(c_int) { + invalid, + perspective, + orthographic, + }; + + pub const LightType = enum(c_int) { + invalid, + directional, + point, + spot, + }; + + pub const DataFreeMethod = enum(c_int) { + none, + file_release, + memory_free, + }; + + pub const MeshoptCompressionMode = enum(c_int) { + invalid, + attributes, + triangles, + indices, + }; + + pub const MeshoptCompressionFilter = enum(c_int) { + none, + octahedral, + quaternion, + exponential, + }; + + pub const Extras = extern struct { + start_offset: usize, // DEPRECATED: Please use `data` instead. + end_offset: usize, // DEPRECATED: Please use `data` instead. + + data: ?[*]u8, + }; + + pub const Extension = extern struct { + name: ?MutCString, + data: ?MutCString, + }; + + pub const Buffer = extern struct { + name: ?MutCString, + size: usize, + uri: ?MutCString, + data: ?*anyopaque, // loaded by loadBuffers() + data_free_method: DataFreeMethod, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const MeshoptCompression = extern struct { + buffer: *Buffer, + offset: usize, + size: usize, + stride: usize, + count: usize, + mode: MeshoptCompressionMode, + filter: MeshoptCompressionFilter, + }; + + pub const BufferView = extern struct { + name: ?MutCString, + buffer: *Buffer, + offset: usize, + size: usize, + stride: usize, // 0 == automatically determined by accessor + view_type: BufferViewType, + data: ?*anyopaque, // overrides buffer.data if present, filled by extensions + has_meshopt_compression: Bool32, + meshopt_compression: MeshoptCompression, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + + pub fn getData(bv: BufferView) ?[*]u8 { + return cgltf_buffer_view_data(&bv); + } + }; + + pub const AccessorSparse = extern struct { + count: usize, + indices_buffer_view: *BufferView, + indices_byte_offset: usize, + indices_component_type: ComponentType, + values_buffer_view: *BufferView, + values_byte_offset: usize, + extras: Extras, + indices_extras: Extras, + values_extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + indices_extensions_count: usize, + indices_extensions: ?[*]Extension, + values_extensions_count: usize, + values_extensions: ?[*]Extension, + }; + + pub const Accessor = extern struct { + name: ?MutCString, + component_type: ComponentType, + normalized: Bool32, + type: Type, + offset: usize, + count: usize, + stride: usize, + buffer_view: ?*BufferView, + has_min: Bool32, + min: [16]f32, + has_max: Bool32, + max: [16]f32, + is_sparse: Bool32, + sparse: AccessorSparse, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + + pub fn unpackFloatsCount(accessor: Accessor) usize { + return cgltf_accessor_unpack_floats(&accessor, null, 0); + } + + pub fn unpackFloats(accessor: Accessor, out: []f32) []f32 { + const count = cgltf_accessor_unpack_floats(&accessor, out.ptr, out.len); + return out[0..count]; + } + + pub fn unpackIndicesCount(accessor: Accessor) usize { + return cgltf_accessor_unpack_indices(&accessor, null, 0); + } + + pub fn unpackIndices(accessor: Accessor, out: []u32) []u32 { + const count = cgltf_accessor_unpack_indices(&accessor, out.ptr, out.len); + return out[0..count]; + } + + pub fn readFloat(accessor: Accessor, index: usize, out: []f32) bool { + assert(out.len == accessor.type.numComponents()); + const result = cgltf_accessor_read_float(&accessor, index, out.ptr, out.len); + return result != 0; + } + + pub fn readUint(accessor: Accessor, index: usize, out: []u32) bool { + assert(out.len == accessor.type.numComponents()); + const result = cgltf_accessor_read_uint(&accessor, index, out.ptr, out.len); + return result != 0; + } + + pub fn readIndex(accessor: Accessor, index: usize) usize { + return cgltf_accessor_read_index(&accessor, index); + } + }; + + pub const Attribute = extern struct { + name: ?MutCString, + type: AttributeType, + index: i32, + data: *Accessor, + }; + + pub const Image = extern struct { + name: ?MutCString, + uri: ?MutCString, + buffer_view: ?*BufferView, + mime_type: ?MutCString, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const Sampler = extern struct { + uri: ?MutCString, + mag_filter: i32, + min_filter: i32, + wrap_s: i32, + wrap_t: i32, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const Texture = extern struct { + name: ?MutCString, + image: ?*Image, + sampler: ?*Sampler, + has_basisu: Bool32, + basisu_image: ?*Image, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const TextureTransform = extern struct { + offset: [2]f32, + rotation: f32, + scale: [2]f32, + has_texcoord: Bool32, + texcoord: i32, + }; + + pub const TextureView = extern struct { + texture: ?*Texture, + texcoord: i32, + scale: f32, + has_transform: Bool32, + transform: TextureTransform, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const PbrMetallicRoughness = extern struct { + base_color_texture: TextureView, + metallic_roughness_texture: TextureView, + base_color_factor: [4]f32, + metallic_factor: f32, + roughness_factor: f32, + }; + + pub const PbrSpecularGlossiness = extern struct { + diffuse_texture: TextureView, + specular_glossiness_texture: TextureView, + diffuse_factor: [4]f32, + specular_factor: [3]f32, + glossiness_factor: f32, + }; + + pub const Clearcoat = extern struct { + clearcoat_texture: TextureView, + clearcoat_roughness_texture: TextureView, + clearcoat_normal_texture: TextureView, + clearcoat_factor: f32, + clearcoat_roughness_factor: f32, + }; + + pub const Transmission = extern struct { + transmission_texture: TextureView, + transmission_factor: f32, + }; + + pub const Ior = extern struct { + ior: f32, + }; + + pub const Specular = extern struct { + specular_texture: TextureView, + specular_color_texture: TextureView, + specular_color_factor: [3]f32, + specular_factor: f32, + }; + + pub const Volume = extern struct { + thickness_texture: TextureView, + thickness_factor: f32, + attentuation_color: [3]f32, + attentuation_distance: f32, + }; + + pub const Sheen = extern struct { + sheen_color_texture: TextureView, + sheen_color_factor: [3]f32, + sheen_roughness_texture: TextureView, + sheen_roughness_factor: f32, + }; + + pub const EmissiveStrength = extern struct { + emissive_strength: f32, + }; + + pub const Iridescence = extern struct { + iridescence_factor: f32, + iridescence_texture: TextureView, + iridescence_ior: f32, + iridescence_thickness_min: f32, + iridescence_thickness_max: f32, + iridescence_thickness_texture: TextureView, + }; + + pub const Anisotropy = extern struct { + anisotropy_strength: f32, + anisotropy_rotation: f32, + anisotropy_texture: TextureView, + }; + + pub const Material = extern struct { + name: ?MutCString, + has_pbr_metallic_roughness: Bool32, + has_pbr_specular_glossiness: Bool32, + has_clearcoat: Bool32, + has_transmission: Bool32, + has_volume: Bool32, + has_ior: Bool32, + has_specular: Bool32, + has_sheen: Bool32, + has_emissive_strength: Bool32, + has_iridescence: Bool32, + has_anisotropy: Bool32, + pbr_metallic_roughness: PbrMetallicRoughness, + pbr_specular_glossiness: PbrSpecularGlossiness, + clearcoat: Clearcoat, + ior: Ior, + specular: Specular, + sheen: Sheen, + transmission: Transmission, + volume: Volume, + emissive_strength: EmissiveStrength, + iridescence: Iridescence, + anisotropy: Anisotropy, + normal_texture: TextureView, + occlusion_texture: TextureView, + emissive_texture: TextureView, + emissive_factor: [3]f32, + alpha_mode: AlphaMode, + alpha_cutoff: f32, + double_sided: Bool32, + unlit: Bool32, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const MaterialMapping = extern struct { + variant: usize, + material: ?*Material, + extras: Extras, + }; + + pub const MorphTarget = extern struct { + attributes: ?[*]Attribute, + attributes_count: usize, + }; + + pub const DracoMeshCompression = extern struct { + buffer_view: ?*BufferView, + attributes: ?[*]Attribute, + attributes_count: usize, + }; + + pub const MeshGpuInstancing = extern struct { + attributes: ?[*]Attribute, + attributes_count: usize, + }; + + pub const Primitive = extern struct { + type: PrimitiveType, + indices: ?*Accessor, + material: ?*Material, + attributes: [*]Attribute, // required + attributes_count: usize, + targets: ?[*]MorphTarget, + targets_count: usize, + extras: Extras, + has_draco_mesh_compression: Bool32, + draco_mesh_compression: DracoMeshCompression, + mappings: ?[*]MaterialMapping, + mappings_count: usize, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const Mesh = extern struct { + name: ?MutCString, + primitives: [*]Primitive, // required + primitives_count: usize, + weights: ?[*]f32, + weights_count: usize, + target_names: ?[*]MutCString, + target_names_count: usize, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const Skin = extern struct { + name: ?MutCString, + joints: [*]*Node, // required + joints_count: usize, + skeleton: ?*Node, + inverse_bind_matrices: ?*Accessor, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const CameraPerspective = extern struct { + has_aspect_ratio: Bool32, + aspect_ratio: f32, + yfov: f32, + has_zfar: Bool32, + zfar: f32, + znear: f32, + extras: Extras, + }; + + pub const CameraOrthographic = extern struct { + xmag: f32, + ymag: f32, + zfar: f32, + znear: f32, + extras: Extras, + }; + + pub const Camera = extern struct { + name: ?MutCString, + type: CameraType, + data: extern union { + perspective: CameraPerspective, + orthographic: CameraOrthographic, + }, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const Light = extern struct { + name: ?MutCString, + color: [3]f32, + intensity: f32, + type: LightType, + range: f32, + spot_inner_cone_angle: f32, + spot_outer_cone_angle: f32, + extras: Extras, + }; + + pub const Node = extern struct { + name: ?MutCString, + parent: ?*Node, + children: ?[*]*Node, + children_count: usize, + skin: ?*Skin, + mesh: ?*Mesh, + camera: ?*Camera, + light: ?*Light, + weights: [*]f32, + weights_count: usize, + has_translation: Bool32, + has_rotation: Bool32, + has_scale: Bool32, + has_matrix: Bool32, + translation: [3]f32, + rotation: [4]f32, + scale: [3]f32, + matrix: [16]f32, + extras: Extras, + has_mesh_gpu_instancing: Bool32, + mesh_gpu_instancing: MeshGpuInstancing, + extensions_count: usize, + extensions: ?[*]Extension, + + pub fn transformLocal(node: Node) [16]f32 { + var transform: [16]f32 = undefined; + cgltf_node_transform_local(&node, &transform); + return transform; + } + + pub fn transformWorld(node: Node) [16]f32 { + var transform: [16]f32 = undefined; + cgltf_node_transform_world(&node, &transform); + return transform; + } + }; + + pub const Scene = extern struct { + name: ?MutCString, + nodes: ?[*]*Node, + nodes_count: usize, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const AnimationSampler = extern struct { + input: *Accessor, // required + output: *Accessor, // required + interpolation: InterpolationType, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const AnimationChannel = extern struct { + sampler: *AnimationSampler, // required + target_node: ?*Node, + target_path: AnimationPathType, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const Animation = extern struct { + name: ?MutCString, + samplers: [*]AnimationSampler, // required + samplers_count: usize, + channels: [*]AnimationChannel, // required + channels_count: usize, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const MaterialVariant = extern struct { + name: ?MutCString, + extras: Extras, + }; + + pub const Asset = extern struct { + copyright: ?MutCString, + generator: ?MutCString, + version: ?MutCString, + min_version: ?MutCString, + extras: Extras, + extensions_count: usize, + extensions: ?[*]Extension, + }; + + pub const Data = extern struct { + file_type: FileType, + file_data: ?*anyopaque, + + asset: Asset, + + meshes: ?[*]Mesh, + meshes_count: usize, + + materials: ?[*]Material, + materials_count: usize, + + accessors: ?[*]Accessor, + accessors_count: usize, + + buffer_views: ?[*]BufferView, + buffer_views_count: usize, + + buffers: ?[*]Buffer, + buffers_count: usize, + + images: ?[*]Image, + images_count: usize, + + textures: ?[*]Texture, + textures_count: usize, + + samplers: ?[*]Sampler, + samplers_count: usize, + + skins: ?[*]Skin, + skins_count: usize, + + cameras: ?[*]Camera, + cameras_count: usize, + + lights: ?[*]Light, + lights_count: usize, + + nodes: ?[*]Node, + nodes_count: usize, + + scenes: ?[*]Scene, + scenes_count: usize, + + scene: ?*Scene, + + animations: ?[*]Animation, + animations_count: usize, + + variants: ?[*]MaterialVariant, + variants_count: usize, + + extras: Extras, + + data_extensions_count: usize, + data_extensions: ?[*]Extension, + + extensions_used: ?[*]MutCString, + extensions_used_count: usize, + + extensions_required: ?[*]MutCString, + extensions_required_count: usize, + + json: ?CString, + json_size: usize, + + bin: ?*const anyopaque, + bin_size: usize, + + memory: MemoryOptions, + file: FileOptions, + + pub fn writeFile(data: Data, path: [*:0]const u8, options: Options) !void { + const result = cgltf_write_file(&options, path, &data); + try resultToError(result); + } + + pub fn writeBuffer(data: Data, buffer: []u8, options: Options) usize { + return cgltf_write(&options, buffer.ptr, buffer.len, &data); + } + }; + + pub const Error = error{ + DataTooShort, + UnknownFormat, + InvalidJson, + InvalidGltf, + InvalidOptions, + FileNotFound, + IoError, + OutOfMemory, + LegacyGltf, + }; + + pub fn parse(options: Options, data: []const u8) Error!*Data { + var out_data: ?*Data = null; + const result = cgltf_parse(&options, data.ptr, data.len, &out_data); + try resultToError(result); + return out_data.?; + } + + pub fn parseFile(options: Options, path: [*:0]const u8) Error!*Data { + var out_data: ?*Data = null; + const result = cgltf_parse_file(&options, path, &out_data); + try resultToError(result); + return out_data.?; + } + + pub fn loadBuffers(options: Options, data: *Data, gltf_path: [*:0]const u8) Error!void { + const result = cgltf_load_buffers(&options, data, gltf_path); + try resultToError(result); + } + + pub fn free(data: *Data) void { + cgltf_free(data); + } + + pub fn validate(data: *Data) Result { + return cgltf_validate(data); + } + + extern fn cgltf_parse( + options: ?*const Options, + data: ?*const anyopaque, + size: usize, + out_data: ?*?*Data, + ) Result; + + extern fn cgltf_parse_file( + options: ?*const Options, + path: ?[*:0]const u8, + out_data: ?*?*Data, + ) Result; + + extern fn cgltf_load_buffers( + options: ?*const Options, + data: ?*Data, + gltf_path: ?[*:0]const u8, + ) Result; + + extern fn cgltf_load_buffer_base64( + options: ?*const Options, + size: usize, + base64: ?[*:0]const u8, + out_data: ?*?*Data, + ) Result; + + extern fn cgltf_decode_string(string: ?MutCString) usize; + extern fn cgltf_decode_uri(string: ?MutCString) usize; + + extern fn cgltf_free(data: ?*Data) void; + extern fn cgltf_validate(data: ?*Data) Result; + + extern fn cgltf_node_transform_local(node: ?*const Node, out_matrix: ?*[16]f32) void; + extern fn cgltf_node_transform_world(node: ?*const Node, out_matrix: ?*[16]f32) void; + + extern fn cgltf_buffer_view_data(view: ?*const BufferView) ?[*]u8; + + extern fn cgltf_accessor_read_float( + accessor: ?*const Accessor, + index: usize, + out: ?[*]f32, + element_size: usize, + ) Bool32; + + extern fn cgltf_accessor_read_uint( + accessor: ?*const Accessor, + index: usize, + out: ?[*]u32, + element_size: usize, + ) Bool32; + + extern fn cgltf_accessor_read_index( + accessor: ?*const Accessor, + index: usize, + ) usize; + + extern fn cgltf_accessor_unpack_floats( + accessor: ?*const Accessor, + out: ?[*]f32, + float_count: usize, + ) usize; + + extern fn cgltf_accessor_unpack_indices( + accessor: ?*const Accessor, + out: ?[*]u32, + index_count: usize, + ) usize; + + extern fn cgltf_copy_extras_json( + data: ?*const Data, + extras: ?*const Extras, + dest: ?[*]u8, + dest_size: ?*usize, + ) Result; + + extern fn cgltf_write_file( + options: ?*const Options, + path: ?[*:0]const u8, + data: ?*const Data, + ) Result; + + extern fn cgltf_write( + options: ?*const Options, + buffer: ?[*]u8, + size: usize, + data: ?*const Data, + ) usize; + + fn resultToError(result: Result) Error!void { + switch (result) { + .success => return, + .data_too_short => return error.DataTooShort, + .unknown_format => return error.UnknownFormat, + .invalid_json => return error.InvalidJson, + .invalid_gltf => return error.InvalidGltf, + .invalid_options => return error.InvalidOptions, + .file_not_found => return error.FileNotFound, + .io_error => return error.IoError, + .out_of_memory => return error.OutOfMemory, + .legacy_gltf => return error.LegacyGltf, + } + } + + // TESTS /////////////////////////////////////////////////////////////////////////////////////////// + + test { + std.testing.refAllDeclsRecursive(@This()); + } + + test "extern struct layout" { + @setEvalBranchQuota(10_000); + const c = @cImport(@cInclude("cgltf.h")); + inline for (comptime std.meta.declarations(@This())) |decl| { + const ZigType = @field(@This(), decl.name); + if (@TypeOf(ZigType) != type) { + continue; + } + if (comptime std.meta.activeTag(@typeInfo(ZigType)) == .@"struct" and + @typeInfo(ZigType).@"struct".layout == .@"extern") + { + comptime var c_name_buf: [256]u8 = undefined; + const c_name = comptime try cTypeNameFromZigTypeName(&c_name_buf, decl.name); + const CType = @field(c, c_name); + std.testing.expectEqual(@sizeOf(CType), @sizeOf(ZigType)) catch |err| { + std.log.err("@sizeOf({s}) != @sizeOf({s})", .{ decl.name, c_name }); + return err; + }; + comptime var i: usize = 0; + inline for (comptime std.meta.fieldNames(CType)) |c_field_name| { + std.testing.expectEqual( + @offsetOf(CType, c_field_name), + @offsetOf(ZigType, std.meta.fieldNames(ZigType)[i]), + ) catch |err| { + std.log.err( + "@offsetOf({s}, {s}) != @offsetOf({s}, {s})", + .{ decl.name, std.meta.fieldNames(ZigType)[i], c_name, c_field_name }, + ); + return err; + }; + i += 1; + } + } + } + } + + test "enum" { + @setEvalBranchQuota(10_000); + const c = @cImport(@cInclude("cgltf.h")); + inline for (comptime std.meta.declarations(@This())) |decl| { + const ZigType = @field(@This(), decl.name); + if (@TypeOf(ZigType) != type) { + continue; + } + if (comptime std.meta.activeTag(@typeInfo(ZigType)) == .@"enum") { + comptime var c_name_buf: [256]u8 = undefined; + const c_name = comptime try cTypeNameFromZigTypeName(&c_name_buf, decl.name); + const CType = @field(c, c_name); + std.testing.expectEqual(@sizeOf(CType), @sizeOf(ZigType)) catch |err| { + std.log.err("@sizeOf({s}) != @sizeOf({s})", .{ decl.name, c_name }); + return err; + }; + inline for (comptime std.meta.fieldNames(ZigType)) |field_name| { + const c_field_name = comptime buildName: { + var buf: [256]u8 = undefined; + var fbs = std.io.fixedBufferStream(&buf); + try fbs.writer().writeAll(c_name); + try fbs.writer().writeByte('_'); + try fbs.writer().writeAll(field_name); + break :buildName fbs.getWritten(); + }; + std.testing.expectEqual( + @field(c, c_field_name), + @intFromEnum(@field(ZigType, field_name)), + ) catch |err| { + std.log.err(decl.name ++ "." ++ field_name ++ " != " ++ c_field_name, .{}); + return err; + }; + } + } + } + } + + fn cTypeNameFromZigTypeName( + comptime buf: []u8, + comptime zig_name: []const u8, + ) ![]const u8 { + comptime var fbs = std.io.fixedBufferStream(buf); + try fbs.writer().writeAll("cgltf"); + for (zig_name) |char| { + if (std.ascii.isUpper(char)) { + try fbs.writer().writeByte('_'); + try fbs.writer().writeByte(std.ascii.toLower(char)); + } else { + try fbs.writer().writeByte(char); + } + } + return fbs.getWritten(); + } + + pub fn parseAndLoadFile(pathname: [:0]const u8) Error!*Data { + const options = Options{ .memory = .{ .alloc_func = mem.zmeshAllocUser, .free_func = mem.zmeshFreeUser, }, }; - const data = try bindings.parseFile(options, pathname); - errdefer bindings.free(data); + const data = try parseFile(options, pathname); + errdefer free(data); - try bindings.loadBuffers(options, data, pathname); + try loadBuffers(options, data, pathname); return data; } pub fn freeData(data: *Data) void { - bindings.free(data); + 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), + indices: *std.array_list.Managed(u32), + positions: *std.array_list.Managed([3]f32), + normals: ?*std.array_list.Managed([3]f32), + texcoords0: ?*std.array_list.Managed([2]f32), + tangents: ?*std.array_list.Managed([4]f32), ) !void { assert(mesh_index < data.meshes_count); assert(prim_index < data.meshes.?[mesh_index].primitives_count); diff --git a/src/memory.zig b/src/memory.zig index 132631f..d39b923 100644 --- a/src/memory.zig +++ b/src/memory.zig @@ -20,8 +20,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, @@ -33,15 +33,15 @@ var mem_allocations: ?std.AutoHashMap(usize, usize) = null; var mem_mutex: std.Thread.Mutex = .{}; const mem_alignment = 16; -extern var zmeshMallocPtr: ?*const fn (size: usize) callconv(.C) ?*anyopaque; +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(); const mem = mem_allocator.?.alignedAlloc( u8, - mem_alignment, + .fromByteUnits(16), size, ) catch @panic("zmesh: out of memory"); @@ -50,9 +50,9 @@ pub fn zmeshMalloc(size: usize) callconv(.C) ?*anyopaque { return mem.ptr; } -extern var zmeshCallocPtr: ?*const fn (num: usize, size: usize) callconv(.C) ?*anyopaque; +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,14 +61,14 @@ 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; +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(); @@ -91,9 +91,9 @@ fn zmeshRealloc(ptr: ?*anyopaque, size: usize) callconv(.C) ?*anyopaque { return mem.ptr; } -extern var zmeshFreePtr: ?*const fn (maybe_ptr: ?*anyopaque) callconv(.C) void; +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(); @@ -104,7 +104,7 @@ fn zmeshFree(maybe_ptr: ?*anyopaque) callconv(.C) void { } } -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 deleted file mode 100644 index a3de0ed..0000000 --- a/src/zcgltf.zig +++ /dev/null @@ -1,980 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const assert = std.debug.assert; - -pub const Bool32 = i32; -pub const CString = [*:0]const u8; -pub const MutCString = [*:0]u8; - -pub const FileType = enum(c_int) { - invalid, - gltf, - glb, -}; - -pub const Result = enum(c_int) { - success, - data_too_short, - unknown_format, - invalid_json, - invalid_gltf, - invalid_options, - file_not_found, - io_error, - out_of_memory, - legacy_gltf, -}; - -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, - free_func: ?FreeFn = null, - user_data: ?*anyopaque = null, -}; - -pub const FileOptions = extern struct { - const ReadFn = *const fn ( - *const MemoryOptions, - *const FileOptions, - CString, - *usize, - *?*anyopaque, - ) callconv(.C) Result; - - const ReleaseFn = *const fn (*const MemoryOptions, *const FileOptions, ?*anyopaque) callconv(.C) void; - - read: ?ReadFn = null, - release: ?ReleaseFn = null, - user_data: ?*anyopaque = null, -}; - -pub const Options = extern struct { - file_type: FileType = .invalid, - json_token_count: usize = 0, - memory: MemoryOptions = .{}, - file: FileOptions = .{}, -}; - -pub const BufferViewType = enum(c_int) { - invalid, - indices, - vertices, -}; - -pub const AttributeType = enum(c_int) { - invalid, - position, - normal, - tangent, - texcoord, - color, - joints, - weights, - custom, -}; - -pub const ComponentType = enum(c_int) { - invalid, - r_8, - r_8u, - r_16, - r_16u, - r_32u, - r_32f, -}; - -pub const Type = enum(c_int) { - invalid, - scalar, - vec2, - vec3, - vec4, - mat2, - mat3, - mat4, - - pub fn numComponents(dtype: Type) usize { - return switch (dtype) { - .vec2 => 2, - .vec3 => 3, - .vec4 => 4, - .mat2 => 4, - .mat3 => 9, - .mat4 => 16, - else => 1, - }; - } -}; - -pub const PrimitiveType = enum(c_int) { - points, - lines, - line_loop, - line_strip, - triangles, - triangle_strip, - triangle_fan, -}; - -pub const AlphaMode = enum(c_int) { - @"opaque", - mask, - blend, -}; - -pub const AnimationPathType = enum(c_int) { - invalid, - translation, - rotation, - scale, - weights, -}; - -pub const InterpolationType = enum(c_int) { - linear, - step, - cubic_spline, -}; - -pub const CameraType = enum(c_int) { - invalid, - perspective, - orthographic, -}; - -pub const LightType = enum(c_int) { - invalid, - directional, - point, - spot, -}; - -pub const DataFreeMethod = enum(c_int) { - none, - file_release, - memory_free, -}; - -pub const MeshoptCompressionMode = enum(c_int) { - invalid, - attributes, - triangles, - indices, -}; - -pub const MeshoptCompressionFilter = enum(c_int) { - none, - octahedral, - quaternion, - exponential, -}; - -pub const Extras = extern struct { - start_offset: usize, // DEPRECATED: Please use `data` instead. - end_offset: usize, // DEPRECATED: Please use `data` instead. - - data: ?[*]u8, -}; - -pub const Extension = extern struct { - name: ?MutCString, - data: ?MutCString, -}; - -pub const Buffer = extern struct { - name: ?MutCString, - size: usize, - uri: ?MutCString, - data: ?*anyopaque, // loaded by loadBuffers() - data_free_method: DataFreeMethod, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const MeshoptCompression = extern struct { - buffer: *Buffer, - offset: usize, - size: usize, - stride: usize, - count: usize, - mode: MeshoptCompressionMode, - filter: MeshoptCompressionFilter, -}; - -pub const BufferView = extern struct { - name: ?MutCString, - buffer: *Buffer, - offset: usize, - size: usize, - stride: usize, // 0 == automatically determined by accessor - view_type: BufferViewType, - data: ?*anyopaque, // overrides buffer.data if present, filled by extensions - has_meshopt_compression: Bool32, - meshopt_compression: MeshoptCompression, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, - - pub fn getData(bv: BufferView) ?[*]u8 { - return cgltf_buffer_view_data(&bv); - } -}; - -pub const AccessorSparse = extern struct { - count: usize, - indices_buffer_view: *BufferView, - indices_byte_offset: usize, - indices_component_type: ComponentType, - values_buffer_view: *BufferView, - values_byte_offset: usize, - extras: Extras, - indices_extras: Extras, - values_extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, - indices_extensions_count: usize, - indices_extensions: ?[*]Extension, - values_extensions_count: usize, - values_extensions: ?[*]Extension, -}; - -pub const Accessor = extern struct { - name: ?MutCString, - component_type: ComponentType, - normalized: Bool32, - type: Type, - offset: usize, - count: usize, - stride: usize, - buffer_view: ?*BufferView, - has_min: Bool32, - min: [16]f32, - has_max: Bool32, - max: [16]f32, - is_sparse: Bool32, - sparse: AccessorSparse, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, - - pub fn unpackFloatsCount(accessor: Accessor) usize { - return cgltf_accessor_unpack_floats(&accessor, null, 0); - } - - pub fn unpackFloats(accessor: Accessor, out: []f32) []f32 { - const count = cgltf_accessor_unpack_floats(&accessor, out.ptr, out.len); - return out[0..count]; - } - - pub fn unpackIndicesCount(accessor: Accessor) usize { - return cgltf_accessor_unpack_indices(&accessor, null, 0); - } - - pub fn unpackIndices(accessor: Accessor, out: []u32) []u32 { - const count = cgltf_accessor_unpack_indices(&accessor, out.ptr, out.len); - return out[0..count]; - } - - pub fn readFloat(accessor: Accessor, index: usize, out: []f32) bool { - assert(out.len == accessor.type.numComponents()); - const result = cgltf_accessor_read_float(&accessor, index, out.ptr, out.len); - return result != 0; - } - - pub fn readUint(accessor: Accessor, index: usize, out: []u32) bool { - assert(out.len == accessor.type.numComponents()); - const result = cgltf_accessor_read_uint(&accessor, index, out.ptr, out.len); - return result != 0; - } - - pub fn readIndex(accessor: Accessor, index: usize) usize { - return cgltf_accessor_read_index(&accessor, index); - } -}; - -pub const Attribute = extern struct { - name: ?MutCString, - type: AttributeType, - index: i32, - data: *Accessor, -}; - -pub const Image = extern struct { - name: ?MutCString, - uri: ?MutCString, - buffer_view: ?*BufferView, - mime_type: ?MutCString, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const Sampler = extern struct { - uri: ?MutCString, - mag_filter: i32, - min_filter: i32, - wrap_s: i32, - wrap_t: i32, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const Texture = extern struct { - name: ?MutCString, - image: ?*Image, - sampler: ?*Sampler, - has_basisu: Bool32, - basisu_image: ?*Image, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const TextureTransform = extern struct { - offset: [2]f32, - rotation: f32, - scale: [2]f32, - has_texcoord: Bool32, - texcoord: i32, -}; - -pub const TextureView = extern struct { - texture: ?*Texture, - texcoord: i32, - scale: f32, - has_transform: Bool32, - transform: TextureTransform, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const PbrMetallicRoughness = extern struct { - base_color_texture: TextureView, - metallic_roughness_texture: TextureView, - base_color_factor: [4]f32, - metallic_factor: f32, - roughness_factor: f32, -}; - -pub const PbrSpecularGlossiness = extern struct { - diffuse_texture: TextureView, - specular_glossiness_texture: TextureView, - diffuse_factor: [4]f32, - specular_factor: [3]f32, - glossiness_factor: f32, -}; - -pub const Clearcoat = extern struct { - clearcoat_texture: TextureView, - clearcoat_roughness_texture: TextureView, - clearcoat_normal_texture: TextureView, - clearcoat_factor: f32, - clearcoat_roughness_factor: f32, -}; - -pub const Transmission = extern struct { - transmission_texture: TextureView, - transmission_factor: f32, -}; - -pub const Ior = extern struct { - ior: f32, -}; - -pub const Specular = extern struct { - specular_texture: TextureView, - specular_color_texture: TextureView, - specular_color_factor: [3]f32, - specular_factor: f32, -}; - -pub const Volume = extern struct { - thickness_texture: TextureView, - thickness_factor: f32, - attentuation_color: [3]f32, - attentuation_distance: f32, -}; - -pub const Sheen = extern struct { - sheen_color_texture: TextureView, - sheen_color_factor: [3]f32, - sheen_roughness_texture: TextureView, - sheen_roughness_factor: f32, -}; - -pub const EmissiveStrength = extern struct { - emissive_strength: f32, -}; - -pub const Iridescence = extern struct { - iridescence_factor: f32, - iridescence_texture: TextureView, - iridescence_ior: f32, - iridescence_thickness_min: f32, - iridescence_thickness_max: f32, - iridescence_thickness_texture: TextureView, -}; - -pub const Anisotropy = extern struct { - anisotropy_strength: f32, - anisotropy_rotation: f32, - anisotropy_texture: TextureView, -}; - -pub const Material = extern struct { - name: ?MutCString, - has_pbr_metallic_roughness: Bool32, - has_pbr_specular_glossiness: Bool32, - has_clearcoat: Bool32, - has_transmission: Bool32, - has_volume: Bool32, - has_ior: Bool32, - has_specular: Bool32, - has_sheen: Bool32, - has_emissive_strength: Bool32, - has_iridescence: Bool32, - has_anisotropy: Bool32, - pbr_metallic_roughness: PbrMetallicRoughness, - pbr_specular_glossiness: PbrSpecularGlossiness, - clearcoat: Clearcoat, - ior: Ior, - specular: Specular, - sheen: Sheen, - transmission: Transmission, - volume: Volume, - emissive_strength: EmissiveStrength, - iridescence: Iridescence, - anisotropy: Anisotropy, - normal_texture: TextureView, - occlusion_texture: TextureView, - emissive_texture: TextureView, - emissive_factor: [3]f32, - alpha_mode: AlphaMode, - alpha_cutoff: f32, - double_sided: Bool32, - unlit: Bool32, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const MaterialMapping = extern struct { - variant: usize, - material: ?*Material, - extras: Extras, -}; - -pub const MorphTarget = extern struct { - attributes: ?[*]Attribute, - attributes_count: usize, -}; - -pub const DracoMeshCompression = extern struct { - buffer_view: ?*BufferView, - attributes: ?[*]Attribute, - attributes_count: usize, -}; - -pub const MeshGpuInstancing = extern struct { - attributes: ?[*]Attribute, - attributes_count: usize, -}; - -pub const Primitive = extern struct { - type: PrimitiveType, - indices: ?*Accessor, - material: ?*Material, - attributes: [*]Attribute, // required - attributes_count: usize, - targets: ?[*]MorphTarget, - targets_count: usize, - extras: Extras, - has_draco_mesh_compression: Bool32, - draco_mesh_compression: DracoMeshCompression, - mappings: ?[*]MaterialMapping, - mappings_count: usize, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const Mesh = extern struct { - name: ?MutCString, - primitives: [*]Primitive, // required - primitives_count: usize, - weights: ?[*]f32, - weights_count: usize, - target_names: ?[*]MutCString, - target_names_count: usize, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const Skin = extern struct { - name: ?MutCString, - joints: [*]*Node, // required - joints_count: usize, - skeleton: ?*Node, - inverse_bind_matrices: ?*Accessor, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const CameraPerspective = extern struct { - has_aspect_ratio: Bool32, - aspect_ratio: f32, - yfov: f32, - has_zfar: Bool32, - zfar: f32, - znear: f32, - extras: Extras, -}; - -pub const CameraOrthographic = extern struct { - xmag: f32, - ymag: f32, - zfar: f32, - znear: f32, - extras: Extras, -}; - -pub const Camera = extern struct { - name: ?MutCString, - type: CameraType, - data: extern union { - perspective: CameraPerspective, - orthographic: CameraOrthographic, - }, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const Light = extern struct { - name: ?MutCString, - color: [3]f32, - intensity: f32, - type: LightType, - range: f32, - spot_inner_cone_angle: f32, - spot_outer_cone_angle: f32, - extras: Extras, -}; - -pub const Node = extern struct { - name: ?MutCString, - parent: ?*Node, - children: ?[*]*Node, - children_count: usize, - skin: ?*Skin, - mesh: ?*Mesh, - camera: ?*Camera, - light: ?*Light, - weights: [*]f32, - weights_count: usize, - has_translation: Bool32, - has_rotation: Bool32, - has_scale: Bool32, - has_matrix: Bool32, - translation: [3]f32, - rotation: [4]f32, - scale: [3]f32, - matrix: [16]f32, - extras: Extras, - has_mesh_gpu_instancing: Bool32, - mesh_gpu_instancing: MeshGpuInstancing, - extensions_count: usize, - extensions: ?[*]Extension, - - pub fn transformLocal(node: Node) [16]f32 { - var transform: [16]f32 = undefined; - cgltf_node_transform_local(&node, &transform); - return transform; - } - - pub fn transformWorld(node: Node) [16]f32 { - var transform: [16]f32 = undefined; - cgltf_node_transform_world(&node, &transform); - return transform; - } -}; - -pub const Scene = extern struct { - name: ?MutCString, - nodes: ?[*]*Node, - nodes_count: usize, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const AnimationSampler = extern struct { - input: *Accessor, // required - output: *Accessor, // required - interpolation: InterpolationType, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const AnimationChannel = extern struct { - sampler: *AnimationSampler, // required - target_node: ?*Node, - target_path: AnimationPathType, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const Animation = extern struct { - name: ?MutCString, - samplers: [*]AnimationSampler, // required - samplers_count: usize, - channels: [*]AnimationChannel, // required - channels_count: usize, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const MaterialVariant = extern struct { - name: ?MutCString, - extras: Extras, -}; - -pub const Asset = extern struct { - copyright: ?MutCString, - generator: ?MutCString, - version: ?MutCString, - min_version: ?MutCString, - extras: Extras, - extensions_count: usize, - extensions: ?[*]Extension, -}; - -pub const Data = extern struct { - file_type: FileType, - file_data: ?*anyopaque, - - asset: Asset, - - meshes: ?[*]Mesh, - meshes_count: usize, - - materials: ?[*]Material, - materials_count: usize, - - accessors: ?[*]Accessor, - accessors_count: usize, - - buffer_views: ?[*]BufferView, - buffer_views_count: usize, - - buffers: ?[*]Buffer, - buffers_count: usize, - - images: ?[*]Image, - images_count: usize, - - textures: ?[*]Texture, - textures_count: usize, - - samplers: ?[*]Sampler, - samplers_count: usize, - - skins: ?[*]Skin, - skins_count: usize, - - cameras: ?[*]Camera, - cameras_count: usize, - - lights: ?[*]Light, - lights_count: usize, - - nodes: ?[*]Node, - nodes_count: usize, - - scenes: ?[*]Scene, - scenes_count: usize, - - scene: ?*Scene, - - animations: ?[*]Animation, - animations_count: usize, - - variants: ?[*]MaterialVariant, - variants_count: usize, - - extras: Extras, - - data_extensions_count: usize, - data_extensions: ?[*]Extension, - - extensions_used: ?[*]MutCString, - extensions_used_count: usize, - - extensions_required: ?[*]MutCString, - extensions_required_count: usize, - - json: ?CString, - json_size: usize, - - bin: ?*const anyopaque, - bin_size: usize, - - memory: MemoryOptions, - file: FileOptions, - - pub fn writeFile(data: Data, path: [*:0]const u8, options: Options) !void { - const result = cgltf_write_file(&options, path, &data); - try resultToError(result); - } - - pub fn writeBuffer(data: Data, buffer: []u8, options: Options) usize { - return cgltf_write(&options, buffer.ptr, buffer.len, &data); - } -}; - -pub const Error = error{ - DataTooShort, - UnknownFormat, - InvalidJson, - InvalidGltf, - InvalidOptions, - FileNotFound, - IoError, - OutOfMemory, - LegacyGltf, -}; - -pub fn parse(options: Options, data: []const u8) Error!*Data { - var out_data: ?*Data = null; - const result = cgltf_parse(&options, data.ptr, data.len, &out_data); - try resultToError(result); - return out_data.?; -} - -pub fn parseFile(options: Options, path: [*:0]const u8) Error!*Data { - var out_data: ?*Data = null; - const result = cgltf_parse_file(&options, path, &out_data); - try resultToError(result); - return out_data.?; -} - -pub fn loadBuffers(options: Options, data: *Data, gltf_path: [*:0]const u8) Error!void { - const result = cgltf_load_buffers(&options, data, gltf_path); - try resultToError(result); -} - -pub fn free(data: *Data) void { - cgltf_free(data); -} - -pub fn validate(data: *Data) Result { - return cgltf_validate(data); -} - -extern fn cgltf_parse( - options: ?*const Options, - data: ?*const anyopaque, - size: usize, - out_data: ?*?*Data, -) Result; - -extern fn cgltf_parse_file( - options: ?*const Options, - path: ?[*:0]const u8, - out_data: ?*?*Data, -) Result; - -extern fn cgltf_load_buffers( - options: ?*const Options, - data: ?*Data, - gltf_path: ?[*:0]const u8, -) Result; - -extern fn cgltf_load_buffer_base64( - options: ?*const Options, - size: usize, - base64: ?[*:0]const u8, - out_data: ?*?*Data, -) Result; - -extern fn cgltf_decode_string(string: ?MutCString) usize; -extern fn cgltf_decode_uri(string: ?MutCString) usize; - -extern fn cgltf_free(data: ?*Data) void; -extern fn cgltf_validate(data: ?*Data) Result; - -extern fn cgltf_node_transform_local(node: ?*const Node, out_matrix: ?*[16]f32) void; -extern fn cgltf_node_transform_world(node: ?*const Node, out_matrix: ?*[16]f32) void; - -extern fn cgltf_buffer_view_data(view: ?*const BufferView) ?[*]u8; - -extern fn cgltf_accessor_read_float( - accessor: ?*const Accessor, - index: usize, - out: ?[*]f32, - element_size: usize, -) Bool32; - -extern fn cgltf_accessor_read_uint( - accessor: ?*const Accessor, - index: usize, - out: ?[*]u32, - element_size: usize, -) Bool32; - -extern fn cgltf_accessor_read_index( - accessor: ?*const Accessor, - index: usize, -) usize; - -extern fn cgltf_accessor_unpack_floats( - accessor: ?*const Accessor, - out: ?[*]f32, - float_count: usize, -) usize; - -extern fn cgltf_accessor_unpack_indices( - accessor: ?*const Accessor, - out: ?[*]u32, - index_count: usize, -) usize; - -extern fn cgltf_copy_extras_json( - data: ?*const Data, - extras: ?*const Extras, - dest: ?[*]u8, - dest_size: ?*usize, -) Result; - -extern fn cgltf_write_file( - options: ?*const Options, - path: ?[*:0]const u8, - data: ?*const Data, -) Result; - -extern fn cgltf_write( - options: ?*const Options, - buffer: ?[*]u8, - size: usize, - data: ?*const Data, -) usize; - -fn resultToError(result: Result) Error!void { - switch (result) { - .success => return, - .data_too_short => return error.DataTooShort, - .unknown_format => return error.UnknownFormat, - .invalid_json => return error.InvalidJson, - .invalid_gltf => return error.InvalidGltf, - .invalid_options => return error.InvalidOptions, - .file_not_found => return error.FileNotFound, - .io_error => return error.IoError, - .out_of_memory => return error.OutOfMemory, - .legacy_gltf => return error.LegacyGltf, - } -} - -// TESTS /////////////////////////////////////////////////////////////////////////////////////////// - -test { - std.testing.refAllDeclsRecursive(@This()); -} - -test "extern struct layout" { - @setEvalBranchQuota(10_000); - const c = @cImport(@cInclude("cgltf.h")); - inline for (comptime std.meta.declarations(@This())) |decl| { - const ZigType = @field(@This(), decl.name); - if (@TypeOf(ZigType) != type) { - continue; - } - if (comptime std.meta.activeTag(@typeInfo(ZigType)) == .@"struct" and - @typeInfo(ZigType).@"struct".layout == .@"extern") - { - comptime var c_name_buf: [256]u8 = undefined; - const c_name = comptime try cTypeNameFromZigTypeName(&c_name_buf, decl.name); - const CType = @field(c, c_name); - std.testing.expectEqual(@sizeOf(CType), @sizeOf(ZigType)) catch |err| { - std.log.err("@sizeOf({s}) != @sizeOf({s})", .{ decl.name, c_name }); - return err; - }; - comptime var i: usize = 0; - inline for (comptime std.meta.fieldNames(CType)) |c_field_name| { - std.testing.expectEqual( - @offsetOf(CType, c_field_name), - @offsetOf(ZigType, std.meta.fieldNames(ZigType)[i]), - ) catch |err| { - std.log.err( - "@offsetOf({s}, {s}) != @offsetOf({s}, {s})", - .{ decl.name, std.meta.fieldNames(ZigType)[i], c_name, c_field_name }, - ); - return err; - }; - i += 1; - } - } - } -} - -test "enum" { - @setEvalBranchQuota(10_000); - const c = @cImport(@cInclude("cgltf.h")); - inline for (comptime std.meta.declarations(@This())) |decl| { - const ZigType = @field(@This(), decl.name); - if (@TypeOf(ZigType) != type) { - continue; - } - if (comptime std.meta.activeTag(@typeInfo(ZigType)) == .@"enum") { - comptime var c_name_buf: [256]u8 = undefined; - const c_name = comptime try cTypeNameFromZigTypeName(&c_name_buf, decl.name); - const CType = @field(c, c_name); - std.testing.expectEqual(@sizeOf(CType), @sizeOf(ZigType)) catch |err| { - std.log.err("@sizeOf({s}) != @sizeOf({s})", .{ decl.name, c_name }); - return err; - }; - inline for (comptime std.meta.fieldNames(ZigType)) |field_name| { - const c_field_name = comptime buildName: { - var buf: [256]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - try fbs.writer().writeAll(c_name); - try fbs.writer().writeByte('_'); - try fbs.writer().writeAll(field_name); - break :buildName fbs.getWritten(); - }; - std.testing.expectEqual( - @field(c, c_field_name), - @intFromEnum(@field(ZigType, field_name)), - ) catch |err| { - std.log.err(decl.name ++ "." ++ field_name ++ " != " ++ c_field_name, .{}); - return err; - }; - } - } - } -} - -fn cTypeNameFromZigTypeName( - comptime buf: []u8, - comptime zig_name: []const u8, -) ![]const u8 { - comptime var fbs = std.io.fixedBufferStream(buf); - try fbs.writer().writeAll("cgltf"); - for (zig_name) |char| { - if (std.ascii.isUpper(char)) { - try fbs.writer().writeByte('_'); - try fbs.writer().writeByte(std.ascii.toLower(char)); - } else { - try fbs.writer().writeByte(char); - } - } - return fbs.getWritten(); -}