model3d: add m3d implementation / bindings (#589)

This commit is contained in:
Ali Chraghi 2022-10-21 18:12:01 +03:30 committed by GitHub
parent 2d9b1ffba4
commit 3ea1dea4f7
Failed to generate hash of commit
6 changed files with 6803 additions and 0 deletions

Binary file not shown.

36
libs/model3d/build.zig Normal file
View file

@ -0,0 +1,36 @@
const std = @import("std");
pub const pkg = std.build.Pkg{
.name = "model3d",
.source = .{ .path = sdkPath("/src/main.zig") },
};
pub fn build(b: *std.build.Builder) void {
const mode = b.standardReleaseOptions();
const target = b.standardTargetOptions(.{});
const test_step = b.step("test", "Run library tests");
test_step.dependOn(&testStep(b, mode, target).step);
}
pub fn testStep(b: *std.build.Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *std.build.RunStep {
const main_tests = b.addTestExe("model3d-tests", "src/main.zig");
main_tests.setBuildMode(mode);
main_tests.setTarget(target);
link(main_tests);
main_tests.install();
return main_tests.run();
}
pub fn link(step: *std.build.LibExeObjStep) void {
step.addCSourceFile(sdkPath("/src/c/m3d.c"), &.{});
step.addIncludePath(sdkPath("/src/c"));
step.linkLibC();
}
fn sdkPath(comptime suffix: []const u8) []const u8 {
if (suffix[0] != '/') @compileError("suffix must be an absolute path");
return comptime blk: {
const root_dir = std.fs.path.dirname(@src().file) orelse ".";
break :blk root_dir ++ suffix;
};
}

3
libs/model3d/src/c/m3d.c Normal file
View file

@ -0,0 +1,3 @@
#define M3D_IMPLEMENTATION
#define M3D_EXPORTER
#include "m3d.h"

6529
libs/model3d/src/c/m3d.h Normal file

File diff suppressed because it is too large Load diff

231
libs/model3d/src/main.zig Normal file
View file

@ -0,0 +1,231 @@
const std = @import("std");
const c = @cImport(@cInclude("m3d.h"));
const testing = std.testing;
pub const Error = error{
/// memory allocation error
OutOfMemory,
/// malformed text file (ASCII import only)
BadFile,
/// unimplemented interpreter (procedural surface)
Unimplemented,
/// unknown material property record
Unknown,
/// unknown mesh record (only triangles supported)
UnknownMeshRecord,
/// unknown image format (PNG only)
UnknownImageFormat,
/// unknown action or frame or missing bones
UnknownActionFrame,
/// unknown shape command record
UnknownCommand,
/// no voxel dimension or voxel size defined
UnknownVoxel,
/// either the precision or number of bones truncated
Truncated,
/// too many or no chunk of color map
UnexpectedColorMapCount,
/// too many or no chunk of texture map
UnexpectedTextureMapCount,
/// too many or no chunk of vertices
UnexpectedVerticeCount,
/// too many or no chunk of bones
UnexpectedBoneCount,
/// too many or no chunk of material
UnexpectedMaterialCount,
/// too many or no chunk of shape
UnexpectedShapeCount,
/// too many or no chunk of voxel
UnexpectedVoxelCount,
};
const M3d = @This();
handle: *c.m3d_t,
pub fn load(data: [:0]u8, readfile_fn: ?ReadFn, free_fn: ?FreeFn, mtllib: ?M3d) ?M3d {
return .{
.handle = c.m3d_load(
data.ptr,
readfile_fn,
free_fn,
if (mtllib) |m| m.handle else null,
) orelse return null,
};
}
pub fn deinit(self: M3d) void {
c.m3d_free(self.handle);
}
/// return value must be freed with c_allocator
pub fn save(self: M3d, quality: Quality, flags: Flags) Error![]u8 {
var size: u32 = 0;
return if (c.m3d_save(
self.handle,
@enumToInt(quality),
@bitCast(c_int, flags),
&size,
)) |res|
res[0..size]
else
return intToError(self.errCode());
}
pub fn frame(self: M3d, action_id: u32, frame_id: u32, skeleton: []Transform) Error![]Transform {
return if (c.m3d_frame(self.handle, action_id, frame_id, skeleton.ptr)) |res|
res[0..self.handle.numbone]
else
intToError(self.errCode());
}
pub fn pose(self: M3d, action_id: u32, msec: u32) Error![]Bone {
return if (c.m3d_pose(self.handle, action_id, msec)) |res|
res[0..self.handle.numbone]
else
intToError(self.errCode());
}
pub fn name(self: M3d) [:0]const u8 {
return std.mem.span(self.handle.name);
}
pub fn license(self: M3d) [:0]const u8 {
return std.mem.span(self.handle.license);
}
pub fn author(self: M3d) [:0]const u8 {
return std.mem.span(self.handle.author);
}
pub fn description(self: M3d) [:0]const u8 {
return std.mem.span(self.handle.desc);
}
pub fn scale(self: M3d) f32 {
return self.handle.scale;
}
pub fn preview(self: M3d) InlineAsset {
return self.handle.preview;
}
pub fn textures(self: M3d) []TextureData {
return self.handle.texture[0..self.handle.numtexture];
}
pub fn materials(self: M3d) []Material {
return self.handle.material[0..self.handle.nummaterial];
}
pub fn errCode(self: M3d) c_int {
return self.handle.errcode;
}
pub const TextureIndex = c.m3d_texturedata_t;
pub const TextureData = c.m3d_texturedata_t;
pub const Weight = c.m3d_weight_t;
pub const Skin = c.m3d_skin_t;
pub const Bone = c.m3d_bone_t;
pub const Vertex = c.m3d_vertex_t;
pub const Material = c.m3d_material_t;
pub const Face = c.m3d_face_t;
pub const VoxelItem = c.m3d_voxelitem_t;
pub const VoxelType = c.m3d_voxeltype_t;
pub const Voxel = c.m3d_voxel_t;
pub const ShapeCommand = c.m3d_shapecommand_t;
pub const Shape = c.m3d_shape_t;
pub const Label = c.m3d_label_t;
pub const Transform = c.m3d_transform_t;
pub const Frame = c.m3d_frame_t;
pub const Action = c.m3d_action_t;
pub const InlineAsset = c.m3d_inlinedasset_t;
pub const ReadFn = *const fn (filename: [*c]u8, size: [*c]c_uint) callconv(.C) [*c]u8;
pub const FreeFn = *const fn (ptr: ?*anyopaque) callconv(.C) void;
pub const Quality = enum(u2) {
// export with -128 to 127 coordinate precision
int8 = c.M3D_EXP_INT8,
// export with -32768 to 32767 precision
int16 = c.M3D_EXP_INT16,
// export with 32 bit floating point precision
float = c.M3D_EXP_FLOAT,
// export with 64 bit floatint point precision
double = c.M3D_EXP_DOUBLE,
};
pub const Flags = packed struct {
// don't export color map
nocmap: bool = false,
// don't export materials
nomaterial: bool = false,
// don't export model face
noface: bool = false,
// don't export normal vectors
nonormal: bool = false,
// don't export texture UV
notxtcrd: bool = false,
// flip V in texture UVs
fliptxtcrd: bool = false,
// don't recalculate coordinates
norecalc: bool = false,
// the input is left-handed
idosuck: bool = false,
// don't export skeleton
nobone: bool = false,
// no animation nor skeleton saved
noaction: bool = false,
// inline assets into the model
inline_assets: bool = false,
// export unknown chunks too
extra: bool = false,
// export into uncompressed binary
nozlib: bool = false,
// export into ASCII format
ascii: bool = false,
// don't export maximum vertex
novrtmax: bool = false,
_padding: u17 = 0,
};
fn intToError(int: c_int) Error {
return switch (int) {
c.M3D_SUCCESS => unreachable,
c.M3D_ERR_ALLOC => error.OutOfMemory,
c.M3D_ERR_BADFILE => error.BadFile,
c.M3D_ERR_UNIMPL => error.Unimplemented,
c.M3D_ERR_UNKPROP => error.Unknown,
c.M3D_ERR_UNKMESH => error.UnknownMeshRecord,
c.M3D_ERR_UNKIMG => error.UnknownImageFormat,
c.M3D_ERR_UNKFRAME => error.UnknownActionFrame,
c.M3D_ERR_UNKCMD => error.UnknownCommand,
c.M3D_ERR_UNKVOX => error.UnknownVoxel,
c.M3D_ERR_TRUNC => error.Truncated,
c.M3D_ERR_CMAP => error.UnexpectedColorMapCount,
c.M3D_ERR_TMAP => error.UnexpectedTextureMapCount,
c.M3D_ERR_VRTS => error.UnexpectedVerticeCount,
c.M3D_ERR_BONE => error.UnexpectedBoneCount,
c.M3D_ERR_MTRL => error.UnexpectedMaterialCount,
c.M3D_ERR_SHPE => error.UnexpectedShapeCount,
c.M3D_ERR_VOXT => error.UnexpectedVoxelCount,
else => unreachable,
};
}
test {
testing.refAllDeclsRecursive(@This());
var model_file = try std.fs.cwd().openFile("assets/cube.m3d", .{});
defer model_file.close();
var model_data = try model_file.readToEndAllocOptions(testing.allocator, 1024, 119, @alignOf(u8), 0);
defer testing.allocator.free(model_data);
const model = M3d.load(model_data, null, null, null) orelse return error.Fail;
defer model.deinit();
try testing.expectEqualStrings(model.name(), "cube.obj");
var out = try model.save(.float, .{});
defer std.heap.c_allocator.free(out);
try testing.expect(out.len >= 119);
}