model3d: add m3d implementation / bindings (#589)
This commit is contained in:
parent
2d9b1ffba4
commit
3ea1dea4f7
6 changed files with 6803 additions and 0 deletions
|
|
@ -8,6 +8,7 @@ const basisu = @import("libs/basisu/build.zig");
|
||||||
const sysjs = @import("libs/sysjs/build.zig");
|
const sysjs = @import("libs/sysjs/build.zig");
|
||||||
const trimesh2d = @import("libs/trimesh2d/build.zig");
|
const trimesh2d = @import("libs/trimesh2d/build.zig");
|
||||||
const gamemode = @import("libs/gamemode/build.zig");
|
const gamemode = @import("libs/gamemode/build.zig");
|
||||||
|
const model3d = @import("libs/model3d/build.zig");
|
||||||
const wasmserve = @import("tools/wasmserve/wasmserve.zig");
|
const wasmserve = @import("tools/wasmserve/wasmserve.zig");
|
||||||
const gpu_dawn = @import("libs/gpu-dawn/sdk.zig").Sdk(.{
|
const gpu_dawn = @import("libs/gpu-dawn/sdk.zig").Sdk(.{
|
||||||
.glfw = glfw,
|
.glfw = glfw,
|
||||||
|
|
@ -64,6 +65,7 @@ pub fn build(b: *Builder) !void {
|
||||||
const freetype_test_step = b.step("test-freetype", "Run Freetype library tests");
|
const freetype_test_step = b.step("test-freetype", "Run Freetype library tests");
|
||||||
const basisu_test_step = b.step("test-basisu", "Run Basis-Universal library tests");
|
const basisu_test_step = b.step("test-basisu", "Run Basis-Universal library tests");
|
||||||
const sysaudio_test_step = b.step("test-sysaudio", "Run sysaudio library tests");
|
const sysaudio_test_step = b.step("test-sysaudio", "Run sysaudio library tests");
|
||||||
|
const model3d_test_step = b.step("test-model3d", "Run Model3D library tests");
|
||||||
const mach_test_step = b.step("test-mach", "Run Mach Core library tests");
|
const mach_test_step = b.step("test-mach", "Run Mach Core library tests");
|
||||||
|
|
||||||
glfw_test_step.dependOn(&(try glfw.testStep(b, mode, target)).step);
|
glfw_test_step.dependOn(&(try glfw.testStep(b, mode, target)).step);
|
||||||
|
|
@ -72,6 +74,7 @@ pub fn build(b: *Builder) !void {
|
||||||
ecs_test_step.dependOn(&ecs.testStep(b, mode, target).step);
|
ecs_test_step.dependOn(&ecs.testStep(b, mode, target).step);
|
||||||
basisu_test_step.dependOn(&basisu.testStep(b, mode, target).step);
|
basisu_test_step.dependOn(&basisu.testStep(b, mode, target).step);
|
||||||
sysaudio_test_step.dependOn(&sysaudio.testStep(b, mode, target).step);
|
sysaudio_test_step.dependOn(&sysaudio.testStep(b, mode, target).step);
|
||||||
|
model3d_test_step.dependOn(&model3d.testStep(b, mode, target).step);
|
||||||
mach_test_step.dependOn(&testStep(b, mode, target).step);
|
mach_test_step.dependOn(&testStep(b, mode, target).step);
|
||||||
|
|
||||||
all_tests_step.dependOn(glfw_test_step);
|
all_tests_step.dependOn(glfw_test_step);
|
||||||
|
|
@ -80,6 +83,7 @@ pub fn build(b: *Builder) !void {
|
||||||
all_tests_step.dependOn(basisu_test_step);
|
all_tests_step.dependOn(basisu_test_step);
|
||||||
all_tests_step.dependOn(freetype_test_step);
|
all_tests_step.dependOn(freetype_test_step);
|
||||||
all_tests_step.dependOn(sysaudio_test_step);
|
all_tests_step.dependOn(sysaudio_test_step);
|
||||||
|
all_tests_step.dependOn(model3d_test_step);
|
||||||
all_tests_step.dependOn(mach_test_step);
|
all_tests_step.dependOn(mach_test_step);
|
||||||
|
|
||||||
// TODO: we need a way to test wasm stuff
|
// TODO: we need a way to test wasm stuff
|
||||||
|
|
|
||||||
BIN
libs/model3d/assets/cube.m3d
Normal file
BIN
libs/model3d/assets/cube.m3d
Normal file
Binary file not shown.
36
libs/model3d/build.zig
Normal file
36
libs/model3d/build.zig
Normal 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
3
libs/model3d/src/c/m3d.c
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#define M3D_IMPLEMENTATION
|
||||||
|
#define M3D_EXPORTER
|
||||||
|
#include "m3d.h"
|
||||||
6529
libs/model3d/src/c/m3d.h
Normal file
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
231
libs/model3d/src/main.zig
Normal 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);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue