Load gltf with embedded albedo, metallic and roughness maps
This commit is contained in:
parent
2067a133f8
commit
d33d6b2454
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -2,6 +2,8 @@
|
|||||||
* text=auto eol=lf
|
* text=auto eol=lf
|
||||||
|
|
||||||
# Git LFS hooks, add large file types to track here.
|
# Git LFS hooks, add large file types to track here.
|
||||||
|
*.glb filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.gltf filter=lfs diff=lfs merge=lfs -text
|
||||||
*.ogg filter=lfs diff=lfs merge=lfs -text
|
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||||
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
BIN
assets/amd_ryzen_9.glb
(Stored with Git LFS)
Normal file
BIN
assets/amd_ryzen_9.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -81,9 +81,9 @@ struct Material {
|
|||||||
Material evalMaterial() {
|
Material evalMaterial() {
|
||||||
Material result;
|
Material result;
|
||||||
result.albedo = textureSize(albedo_map, 0) == ivec2(0) ? pow(color, vec3(2.2)) : texture(albedo_map, VertexOut.uv).rgb;
|
result.albedo = textureSize(albedo_map, 0) == ivec2(0) ? pow(color, vec3(2.2)) : texture(albedo_map, VertexOut.uv).rgb;
|
||||||
float fMetallic = textureSize(metallic_map, 0) == ivec2(0) ? metallic : texture(metallic_map, VertexOut.uv).r;
|
float fMetallic = textureSize(metallic_map, 0) == ivec2(0) ? metallic : texture(metallic_map, VertexOut.uv).b;
|
||||||
result.metallic = fMetallic > 0.5;
|
result.metallic = fMetallic > 0.5;
|
||||||
result.roughness = max(0.01, textureSize(roughness_map, 0) == ivec2(0) ? roughness : texture(roughness_map, VertexOut.uv).r);
|
result.roughness = max(0.01, textureSize(roughness_map, 0) == ivec2(0) ? roughness : texture(roughness_map, VertexOut.uv).g);
|
||||||
result.emission = textureSize(emission_map, 0) == ivec2(0) ? emission : texture(emission_map, VertexOut.uv).rgb;
|
result.emission = textureSize(emission_map, 0) == ivec2(0) ? emission : texture(emission_map, VertexOut.uv).rgb;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -20,11 +20,11 @@
|
|||||||
.hash = "1220483cbb42231cb056f4ea6669894c68ccd560d3af5832d6e9c84c61844bc20b7d",
|
.hash = "1220483cbb42231cb056f4ea6669894c68ccd560d3af5832d6e9c84c61844bc20b7d",
|
||||||
},
|
},
|
||||||
.@"zig-assimp" = .{
|
.@"zig-assimp" = .{
|
||||||
.url = "https://github.com/sergeypdev/zig-assimp/tarball/59c2f0202bf1e5110f3eb219669f3762e3db2768",
|
.url = "https://github.com/sergeypdev/zig-assimp/tarball/39380dcc231788b3b54f447540bd6fea296fab10",
|
||||||
.hash = "122015247b178258ee2fd9f7fbd3f8025138e8e38b5cbecdc94262974da49bd1c225",
|
.hash = "12209b95f2f5a85107ed38814143179f1e7516440704fb94004046d6f5b5ed3c8667",
|
||||||
},
|
},
|
||||||
.zalgebra = .{
|
.zalgebra = .{
|
||||||
.url = "git+https://github.com/sergeypdev/zalgebra.git#232ff76712dc7cc270b6c48cedc84617536f3a59",
|
.url = "https://github.com/sergeypdev/zalgebra/tarball/232ff76712dc7cc270b6c48cedc84617536f3a59",
|
||||||
.hash = "12206e29e5d0f012c694f413b21cb66238964fdaef0a29781e0bf3ff75ec08a2ed78",
|
.hash = "12206e29e5d0f012c694f413b21cb66238964fdaef0a29781e0bf3ff75ec08a2ed78",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -137,6 +137,21 @@ pub fn resolveScene(self: *AssetManager, handle: Handle.Scene) *const formats.Sc
|
|||||||
return &self.loadScene(handle.id).scene;
|
return &self.loadScene(handle.id).scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolveMaterial(self: *AssetManager, handle: Handle.Material) *const formats.Material {
|
||||||
|
if (handle.id == 0) return &NullMaterial;
|
||||||
|
|
||||||
|
if (self.loaded_assets.getPtr(handle.id)) |asset| {
|
||||||
|
switch (asset.*) {
|
||||||
|
.material => |*material| {
|
||||||
|
return material;
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.loadMaterial(handle.id);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: proper watching
|
// TODO: proper watching
|
||||||
pub fn watchChanges(self: *AssetManager) void {
|
pub fn watchChanges(self: *AssetManager) void {
|
||||||
var iter = self.loaded_assets.iterator();
|
var iter = self.loaded_assets.iterator();
|
||||||
@ -278,6 +293,8 @@ const NullScene = LoadedScene{
|
|||||||
.scene = .{},
|
.scene = .{},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const NullMaterial = formats.Material{};
|
||||||
|
|
||||||
pub fn loadMesh(self: *AssetManager, id: AssetId) *const LoadedMesh {
|
pub fn loadMesh(self: *AssetManager, id: AssetId) *const LoadedMesh {
|
||||||
return self.loadMeshErr(id) catch |err| {
|
return self.loadMeshErr(id) catch |err| {
|
||||||
std.log.err("Error: {} loading mesh at path: {s}", .{ err, asset_manifest.getPath(id) });
|
std.log.err("Error: {} loading mesh at path: {s}", .{ err, asset_manifest.getPath(id) });
|
||||||
@ -474,12 +491,39 @@ fn loadSceneErr(self: *AssetManager, id: AssetId) !*const LoadedScene {
|
|||||||
return &self.loaded_assets.getPtr(id).?.scene;
|
return &self.loaded_assets.getPtr(id).?.scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn loadMaterial(self: *AssetManager, id: AssetId) *const formats.Material {
|
||||||
|
return self.loadMaterialErr(id) catch |err| {
|
||||||
|
std.log.err("Error: {} loading material at path {s}\n", .{ err, asset_manifest.getPath(id) });
|
||||||
|
|
||||||
|
return &NullMaterial;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loadMaterialErr(self: *AssetManager, id: AssetId) !*const formats.Material {
|
||||||
|
const path = asset_manifest.getPath(id);
|
||||||
|
const data = try self.loadFile(self.frame_arena, path, TEXTURE_MAX_BYTES);
|
||||||
|
|
||||||
|
const material = formats.Material.fromBuffer(data.bytes);
|
||||||
|
|
||||||
|
try self.loaded_assets.put(
|
||||||
|
self.allocator,
|
||||||
|
id,
|
||||||
|
.{
|
||||||
|
.material = material,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
try self.modified_times.put(self.allocator, id, data.modified);
|
||||||
|
|
||||||
|
return &self.loaded_assets.getPtr(id).?.material;
|
||||||
|
}
|
||||||
|
|
||||||
const LoadedAsset = union(enum) {
|
const LoadedAsset = union(enum) {
|
||||||
shader: LoadedShader,
|
shader: LoadedShader,
|
||||||
shaderProgram: LoadedShaderProgram,
|
shaderProgram: LoadedShaderProgram,
|
||||||
mesh: LoadedMesh,
|
mesh: LoadedMesh,
|
||||||
texture: LoadedTexture,
|
texture: LoadedTexture,
|
||||||
scene: LoadedScene,
|
scene: LoadedScene,
|
||||||
|
material: formats.Material,
|
||||||
};
|
};
|
||||||
|
|
||||||
const LoadedShader = struct {
|
const LoadedShader = struct {
|
||||||
@ -666,6 +710,7 @@ fn unloadAssetWithDependees(self: *AssetManager, id: AssetId) void {
|
|||||||
.scene => |*scene| {
|
.scene => |*scene| {
|
||||||
self.allocator.free(scene.buf);
|
self.allocator.free(scene.buf);
|
||||||
},
|
},
|
||||||
|
.material => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = self.loaded_assets.remove(id);
|
_ = self.loaded_assets.remove(id);
|
||||||
|
@ -6,4 +6,5 @@ pub const Handle = struct {
|
|||||||
pub const ShaderProgram = extern struct { id: AssetId = 0 };
|
pub const ShaderProgram = extern struct { id: AssetId = 0 };
|
||||||
pub const Mesh = extern struct { id: AssetId = 0 };
|
pub const Mesh = extern struct { id: AssetId = 0 };
|
||||||
pub const Texture = extern struct { id: AssetId = 0 };
|
pub const Texture = extern struct { id: AssetId = 0 };
|
||||||
|
pub const Material = extern struct { id: AssetId = 0 };
|
||||||
};
|
};
|
||||||
|
@ -163,6 +163,11 @@ fn writeVector3(writer: anytype, value: Vector3, endian: std.builtin.Endian) !vo
|
|||||||
try writeFloat(writer, value.y, endian);
|
try writeFloat(writer, value.y, endian);
|
||||||
try writeFloat(writer, value.z, endian);
|
try writeFloat(writer, value.z, endian);
|
||||||
}
|
}
|
||||||
|
fn writeVec3(writer: anytype, value: Vec3, endian: std.builtin.Endian) !void {
|
||||||
|
try writeFloat(writer, value.x(), endian);
|
||||||
|
try writeFloat(writer, value.y(), endian);
|
||||||
|
try writeFloat(writer, value.z(), endian);
|
||||||
|
}
|
||||||
|
|
||||||
fn writeFloat(writer: anytype, value: f32, endian: std.builtin.Endian) !void {
|
fn writeFloat(writer: anytype, value: f32, endian: std.builtin.Endian) !void {
|
||||||
const val: u32 = @bitCast(value);
|
const val: u32 = @bitCast(value);
|
||||||
@ -350,6 +355,7 @@ test "write and read scene" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const Material = extern struct {
|
pub const Material = extern struct {
|
||||||
|
// TODO: rgba
|
||||||
albedo: Vec3 = Vec3.one(),
|
albedo: Vec3 = Vec3.one(),
|
||||||
albedo_map: Handle.Texture = .{},
|
albedo_map: Handle.Texture = .{},
|
||||||
normal_map: Handle.Texture = .{},
|
normal_map: Handle.Texture = .{},
|
||||||
@ -359,4 +365,15 @@ pub const Material = extern struct {
|
|||||||
roughness_map: Handle.Texture = .{},
|
roughness_map: Handle.Texture = .{},
|
||||||
emission: f32 = 0,
|
emission: f32 = 0,
|
||||||
emission_map: Handle.Texture = .{},
|
emission_map: Handle.Texture = .{},
|
||||||
|
|
||||||
|
pub fn fromBuffer(buf: []const u8) Material {
|
||||||
|
const mat: *align(1) const Material = @ptrCast(buf);
|
||||||
|
|
||||||
|
return mat.*;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: doesn't respect endianness
|
||||||
|
pub fn writeMaterial(writer: anytype, value: Material) !void {
|
||||||
|
try writer.writeStruct(value);
|
||||||
|
}
|
||||||
|
37
src/game.zig
37
src/game.zig
@ -224,9 +224,9 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
|
|||||||
.transform = .{ .scale = Vec3.one().scale(2) },
|
.transform = .{ .scale = Vec3.one().scale(2) },
|
||||||
.mesh = .{
|
.mesh = .{
|
||||||
.handle = a.Meshes.plane,
|
.handle = a.Meshes.plane,
|
||||||
.material = .{
|
// .material = .{
|
||||||
.normal_map = a.Textures.@"tile.norm",
|
// .normal_map = a.Textures.@"tile.norm",
|
||||||
},
|
// },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -239,11 +239,11 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
|
|||||||
.flags = .{ .mesh = true },
|
.flags = .{ .mesh = true },
|
||||||
.mesh = .{
|
.mesh = .{
|
||||||
.handle = a.Meshes.bunny,
|
.handle = a.Meshes.bunny,
|
||||||
.material = .{
|
// .material = .{
|
||||||
.albedo_map = a.Textures.bunny_tex1,
|
// .albedo_map = a.Textures.bunny_tex1,
|
||||||
// .normal_map = a.Textures.@"tile.norm",
|
// // .normal_map = a.Textures.@"tile.norm",
|
||||||
.roughness = @as(f32, @floatFromInt(i)) / 10.0,
|
// .roughness = @as(f32, @floatFromInt(i)) / 10.0,
|
||||||
},
|
// },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -257,19 +257,22 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
|
|||||||
.flags = .{ .mesh = true },
|
.flags = .{ .mesh = true },
|
||||||
.mesh = .{
|
.mesh = .{
|
||||||
.handle = a.Meshes.bunny,
|
.handle = a.Meshes.bunny,
|
||||||
.material = .{
|
// .material = .{
|
||||||
.albedo = Vec3.new(1.000, 0.766, 0.336),
|
// .albedo = Vec3.new(1.000, 0.766, 0.336),
|
||||||
// .albedo_map = a.Textures.bunny_tex1,
|
// // .albedo_map = a.Textures.bunny_tex1,
|
||||||
// .normal_map = a.Textures.@"tile.norm",
|
// // .normal_map = a.Textures.@"tile.norm",
|
||||||
.roughness = @as(f32, @floatFromInt(i + 1)) / 10.0,
|
// .roughness = @as(f32, @floatFromInt(i + 1)) / 10.0,
|
||||||
.metallic = 1.0,
|
// .metallic = 1.0,
|
||||||
},
|
// },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// const test_scene_root = globals.g_mem.world.createScene(globals.g_assetman.resolveScene(a.Scenes.test_scene.scene));
|
const ryzen = globals.g_mem.world.createScene(globals.g_assetman.resolveScene(a.Scenes.amd_ryzen_9.scene));
|
||||||
|
const ent = globals.g_mem.world.getEntity(ryzen) orelse @panic("WTF");
|
||||||
|
ent.data.transform.pos = Vec3.new(0, 1, 0);
|
||||||
|
ent.data.transform.scale = Vec3.one().scale(0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn game_update() bool {
|
export fn game_update() bool {
|
||||||
@ -456,7 +459,7 @@ export fn game_update() bool {
|
|||||||
if (ent.data.flags.mesh) {
|
if (ent.data.flags.mesh) {
|
||||||
gmem.render.draw(.{
|
gmem.render.draw(.{
|
||||||
.mesh = ent.data.mesh.handle,
|
.mesh = ent.data.mesh.handle,
|
||||||
.material = ent.data.mesh.material,
|
.material = gmem.assetman.resolveMaterial(ent.data.mesh.material).*,
|
||||||
.transform = ent.globalMatrix(&gmem.world).*,
|
.transform = ent.globalMatrix(&gmem.world).*,
|
||||||
});
|
});
|
||||||
} else if (ent.data.flags.point_light) {
|
} else if (ent.data.flags.point_light) {
|
||||||
|
@ -5,6 +5,7 @@ pub const Meshes = manifest.Meshes;
|
|||||||
pub const Shaders = manifest.Shaders;
|
pub const Shaders = manifest.Shaders;
|
||||||
pub const ShaderPrograms = manifest.ShaderPrograms;
|
pub const ShaderPrograms = manifest.ShaderPrograms;
|
||||||
pub const Textures = manifest.Textures;
|
pub const Textures = manifest.Textures;
|
||||||
|
pub const Materials = manifest.Materials;
|
||||||
|
|
||||||
pub fn getPath(asset_id: u64) []const u8 {
|
pub fn getPath(asset_id: u64) []const u8 {
|
||||||
manifest.init();
|
manifest.init();
|
||||||
|
@ -77,7 +77,7 @@ pub const Entity = struct {
|
|||||||
|
|
||||||
pub const Mesh = extern struct {
|
pub const Mesh = extern struct {
|
||||||
handle: AssetManager.Handle.Mesh = .{},
|
handle: AssetManager.Handle.Mesh = .{},
|
||||||
material: Material = .{},
|
material: AssetManager.Handle.Material = .{},
|
||||||
};
|
};
|
||||||
pub const PointLight = extern struct {
|
pub const PointLight = extern struct {
|
||||||
radius: f32 = std.math.floatEps(f32), // should never be 0 or bad things happen
|
radius: f32 = std.math.floatEps(f32), // should never be 0 or bad things happen
|
||||||
|
@ -69,8 +69,9 @@ pub fn main() !void {
|
|||||||
const cwd_path = try std.os.getcwd(&cwd_buf);
|
const cwd_path = try std.os.getcwd(&cwd_buf);
|
||||||
|
|
||||||
const rel_input = try std.fs.path.relative(allocator, cwd_path, abs_input);
|
const rel_input = try std.fs.path.relative(allocator, cwd_path, abs_input);
|
||||||
|
const rel_output = try std.fs.path.relative(allocator, cwd_path, output_dirname);
|
||||||
|
|
||||||
var output_dir = try std.fs.cwd().makeOpenPath(output_dirname, .{});
|
var output_dir = try std.fs.cwd().makeOpenPath(rel_output, .{});
|
||||||
defer output_dir.close();
|
defer output_dir.close();
|
||||||
|
|
||||||
const asset_type = resolveAssetTypeByExtension(abs_input) orelse return error.UnknownAssetType;
|
const asset_type = resolveAssetTypeByExtension(abs_input) orelse return error.UnknownAssetType;
|
||||||
@ -78,7 +79,7 @@ pub fn main() !void {
|
|||||||
var buf_asset_list_writer = std.io.bufferedWriter(std.io.getStdOut().writer());
|
var buf_asset_list_writer = std.io.bufferedWriter(std.io.getStdOut().writer());
|
||||||
const asset_list_writer = buf_asset_list_writer.writer();
|
const asset_list_writer = buf_asset_list_writer.writer();
|
||||||
|
|
||||||
std.log.debug("type: {s}, rel_input: {s}", .{ @tagName(asset_type), rel_input });
|
std.log.debug("type: {s}, rel_input: {s}, output_dir: {s}", .{ @tagName(asset_type), rel_input, rel_output });
|
||||||
|
|
||||||
switch (asset_type) {
|
switch (asset_type) {
|
||||||
.Scene => try processScene(allocator, rel_input, output_dir, asset_list_writer),
|
.Scene => try processScene(allocator, rel_input, output_dir, asset_list_writer),
|
||||||
@ -134,16 +135,23 @@ fn createOutput(_type: AssetType, asset_path: AssetPath, output_dir: std.fs.Dir,
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AI_MATKEY_NAME = "?mat.name";
|
||||||
|
const AI_MATKEY_SHADING_MODEL = "$mat.shadingm";
|
||||||
|
const AI_MATKEY_BASE_COLOR = "$clr.base";
|
||||||
|
const AI_MATKEY_METALLIC_FACTOR = "$mat.metallicFactor";
|
||||||
|
const AI_MATKEY_ROUGHNESS_FACTOR = "$mat.roughnessFactor";
|
||||||
|
const AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE = c.aiTextureType_UNKNOWN;
|
||||||
|
|
||||||
/// This can output either a single mesh (for simple formats like obj)
|
/// This can output either a single mesh (for simple formats like obj)
|
||||||
/// or a scene + a bunch of sub assets (meshes, materials, textures, animations, etc.)
|
/// or a scene + a bunch of sub assets (meshes, materials, textures, animations, etc.)
|
||||||
/// It all depends on the source asset.
|
/// It all depends on the source asset.
|
||||||
fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std.fs.Dir, asset_list_writer: anytype) !void {
|
fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std.fs.Dir, asset_list_writer: anytype) !void {
|
||||||
const input_z = try std.mem.concatWithSentinel(allocator, u8, &.{input}, 0);
|
const input_z = try std.mem.concatWithSentinel(allocator, u8, &.{input}, 0);
|
||||||
const config: *c.aiPropertyStore = @as(?*c.aiPropertyStore, @ptrCast(c.aiCreatePropertyStore())) orelse return error.PropertyStore;
|
// const config: *c.aiPropertyStore = @as(?*c.aiPropertyStore, @ptrCast(c.aiCreatePropertyStore())) orelse return error.PropertyStore;
|
||||||
defer c.aiReleasePropertyStore(config);
|
// defer c.aiReleasePropertyStore(config);
|
||||||
|
|
||||||
// Remove point and line meshes
|
// // Remove point and line meshes
|
||||||
c.aiSetImportPropertyInteger(config, c.AI_CONFIG_PP_SBP_REMOVE, c.aiPrimitiveType_POINT | c.aiPrimitiveType_LINE);
|
// c.aiSetImportPropertyInteger(config, c.AI_CONFIG_PP_SBP_REMOVE, c.aiPrimitiveType_POINT | c.aiPrimitiveType_LINE);
|
||||||
|
|
||||||
const maybe_scene: ?*const c.aiScene = @ptrCast(c.aiImportFile(
|
const maybe_scene: ?*const c.aiScene = @ptrCast(c.aiImportFile(
|
||||||
input_z.ptr,
|
input_z.ptr,
|
||||||
@ -154,7 +162,7 @@ fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std
|
|||||||
return error.ImportFailed;
|
return error.ImportFailed;
|
||||||
}
|
}
|
||||||
const scene = maybe_scene.?;
|
const scene = maybe_scene.?;
|
||||||
defer c.aiReleaseImport(scene);
|
// defer c.aiReleaseImport(scene);
|
||||||
|
|
||||||
if (scene.mNumMeshes == 0) return error.NoMeshes;
|
if (scene.mNumMeshes == 0) return error.NoMeshes;
|
||||||
|
|
||||||
@ -168,20 +176,7 @@ fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std
|
|||||||
} else {
|
} else {
|
||||||
const base_asset_path = AssetPath{ .simple = input };
|
const base_asset_path = AssetPath{ .simple = input };
|
||||||
|
|
||||||
const meshes: []*c.aiMesh = @ptrCast(scene.mMeshes[0..@intCast(scene.mNumMeshes)]);
|
// Embedded textures
|
||||||
var mesh_outputs = try allocator.alloc(AssetListEntry, meshes.len);
|
|
||||||
for (meshes, 0..) |mesh, i| {
|
|
||||||
const name = mesh.mName.data[0..mesh.mName.length];
|
|
||||||
std.log.debug("mesh name {s}\n", .{name});
|
|
||||||
|
|
||||||
var output = try createOutput(.Mesh, base_asset_path.subPath(try allocator.dupe(u8, name)), output_dir, asset_list_writer);
|
|
||||||
defer output.file.close();
|
|
||||||
|
|
||||||
mesh_outputs[i] = output.list_entry;
|
|
||||||
|
|
||||||
try processMesh(allocator, scene, mesh, output.file);
|
|
||||||
}
|
|
||||||
|
|
||||||
var texture_outputs = try allocator.alloc(AssetListEntry, @intCast(scene.mNumTextures));
|
var texture_outputs = try allocator.alloc(AssetListEntry, @intCast(scene.mNumTextures));
|
||||||
if (scene.mTextures != null) {
|
if (scene.mTextures != null) {
|
||||||
const textures: []*c.aiTexture = @ptrCast(scene.mTextures[0..@intCast(scene.mNumTextures)]);
|
const textures: []*c.aiTexture = @ptrCast(scene.mTextures[0..@intCast(scene.mNumTextures)]);
|
||||||
@ -200,12 +195,85 @@ fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std
|
|||||||
var output = try createOutput(.Texture, base_asset_path.subPath(name), output_dir, asset_list_writer);
|
var output = try createOutput(.Texture, base_asset_path.subPath(name), output_dir, asset_list_writer);
|
||||||
defer output.file.close();
|
defer output.file.close();
|
||||||
|
|
||||||
try processTexture(allocator, name, @as([*]u8, @ptrCast(texture.pcData))[0..@intCast(texture.mWidth)], output.file);
|
|
||||||
|
|
||||||
texture_outputs[i] = output.list_entry;
|
texture_outputs[i] = output.list_entry;
|
||||||
|
|
||||||
|
try processTexture(allocator, name, @as([*]u8, @ptrCast(texture.pcData))[0..@intCast(texture.mWidth)], output.file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Materials
|
||||||
|
var material_outputs = try allocator.alloc(AssetListEntry, @intCast(scene.mNumMaterials));
|
||||||
|
if (scene.mMaterials != null) {
|
||||||
|
const materials: []*c.aiMaterial = @ptrCast(scene.mMaterials[0..@intCast(scene.mNumMaterials)]);
|
||||||
|
|
||||||
|
for (materials, 0..) |material, i| {
|
||||||
|
var str: c.aiString = .{};
|
||||||
|
tryAssimp(c.aiGetMaterialString(material, AI_MATKEY_NAME, 0, 0, &str)) catch {};
|
||||||
|
var name: []u8 = @alignCast(str.data[0..str.length]);
|
||||||
|
if (name.len == 0) {
|
||||||
|
name = try std.fmt.allocPrint(allocator, "material_{}", .{i + 1});
|
||||||
|
} else {
|
||||||
|
name = try allocator.dupe(u8, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
var output = try createOutput(.Material, base_asset_path.subPath(name), output_dir, asset_list_writer);
|
||||||
|
defer output.file.close();
|
||||||
|
|
||||||
|
var buf_writer = std.io.bufferedWriter(output.file.writer());
|
||||||
|
|
||||||
|
material_outputs[i] = output.list_entry;
|
||||||
|
|
||||||
|
var mat_output = formats.Material{};
|
||||||
|
|
||||||
|
var base_color: c.aiColor4D = .{};
|
||||||
|
try tryAssimp(c.aiGetMaterialColor(material, AI_MATKEY_BASE_COLOR, 0, 0, &base_color));
|
||||||
|
// TODO: rgba
|
||||||
|
mat_output.albedo = Vec3.new(base_color.r, base_color.g, base_color.b);
|
||||||
|
|
||||||
|
if (c.aiGetMaterialTextureCount(material, c.aiTextureType_BASE_COLOR) > 0) {
|
||||||
|
const mat_texture = try getMaterialTexture(allocator, material, c.aiTextureType_BASE_COLOR, 0);
|
||||||
|
const entry = mat_texture.path.resolveAssetListEntry(texture_outputs);
|
||||||
|
mat_output.albedo_map.id = entry.getAssetId();
|
||||||
|
}
|
||||||
|
|
||||||
|
try tryAssimp(c.aiGetMaterialFloat(material, AI_MATKEY_METALLIC_FACTOR, 0, 0, &mat_output.metallic));
|
||||||
|
if (c.aiGetMaterialTextureCount(material, c.aiTextureType_METALNESS) > 0) {
|
||||||
|
const mat_texture = try getMaterialTexture(allocator, material, c.aiTextureType_METALNESS, 0);
|
||||||
|
const entry = mat_texture.path.resolveAssetListEntry(texture_outputs);
|
||||||
|
mat_output.metallic_map.id = entry.getAssetId();
|
||||||
|
}
|
||||||
|
|
||||||
|
try tryAssimp(c.aiGetMaterialFloat(material, AI_MATKEY_ROUGHNESS_FACTOR, 0, 0, &mat_output.roughness));
|
||||||
|
if (c.aiGetMaterialTextureCount(material, c.aiTextureType_DIFFUSE_ROUGHNESS) > 0) {
|
||||||
|
const mat_texture = try getMaterialTexture(allocator, material, c.aiTextureType_DIFFUSE_ROUGHNESS, 0);
|
||||||
|
const entry = mat_texture.path.resolveAssetListEntry(texture_outputs);
|
||||||
|
mat_output.roughness_map.id = entry.getAssetId();
|
||||||
|
}
|
||||||
|
|
||||||
|
try formats.writeMaterial(buf_writer.writer(), mat_output);
|
||||||
|
try buf_writer.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MeshEntry = struct { mesh: AssetListEntry, material: AssetListEntry };
|
||||||
|
|
||||||
|
const meshes: []*c.aiMesh = @ptrCast(scene.mMeshes[0..@intCast(scene.mNumMeshes)]);
|
||||||
|
var mesh_outputs = try allocator.alloc(MeshEntry, meshes.len);
|
||||||
|
for (meshes, 0..) |mesh, i| {
|
||||||
|
const name = mesh.mName.data[0..mesh.mName.length];
|
||||||
|
|
||||||
|
var output = try createOutput(.Mesh, base_asset_path.subPath(try allocator.dupe(u8, name)), output_dir, asset_list_writer);
|
||||||
|
defer output.file.close();
|
||||||
|
|
||||||
|
if (mesh.mMaterialIndex < 0 or @as(usize, @intCast(mesh.mMaterialIndex)) > material_outputs.len) {
|
||||||
|
return error.InvalidMaterialIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh_outputs[i] = .{ .mesh = output.list_entry, .material = material_outputs[@intCast(mesh.mMaterialIndex)] };
|
||||||
|
|
||||||
|
try processMesh(allocator, scene, mesh, output.file);
|
||||||
|
}
|
||||||
|
|
||||||
if (scene.mRootNode == null) return;
|
if (scene.mRootNode == null) return;
|
||||||
|
|
||||||
var node_to_entity_idx = std.AutoHashMap(*c.aiNode, usize).init(allocator);
|
var node_to_entity_idx = std.AutoHashMap(*c.aiNode, usize).init(allocator);
|
||||||
@ -253,7 +321,8 @@ fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std
|
|||||||
const mesh_entry = mesh_outputs[mesh_indices[0]];
|
const mesh_entry = mesh_outputs[mesh_indices[0]];
|
||||||
|
|
||||||
ent.flags.mesh = true;
|
ent.flags.mesh = true;
|
||||||
ent.mesh.handle = .{ .id = mesh_entry.src_path.hash() };
|
ent.mesh.handle = .{ .id = mesh_entry.mesh.getAssetId() };
|
||||||
|
ent.mesh.material = .{ .id = mesh_entry.material.getAssetId() };
|
||||||
} else {
|
} else {
|
||||||
for (mesh_indices) |mesh_idx| {
|
for (mesh_indices) |mesh_idx| {
|
||||||
const mesh_entry = mesh_outputs[@intCast(mesh_idx)];
|
const mesh_entry = mesh_outputs[@intCast(mesh_idx)];
|
||||||
@ -266,7 +335,8 @@ fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std
|
|||||||
sub_ent.flags.mesh = true;
|
sub_ent.flags.mesh = true;
|
||||||
|
|
||||||
sub_ent.mesh = .{
|
sub_ent.mesh = .{
|
||||||
.handle = .{ .id = mesh_entry.src_path.hash() },
|
.handle = .{ .id = mesh_entry.mesh.getAssetId() },
|
||||||
|
.material = .{ .id = mesh_entry.material.getAssetId() },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,6 +359,67 @@ fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AssimpTextureRef = union(enum) {
|
||||||
|
external: []const u8,
|
||||||
|
embedded: usize,
|
||||||
|
|
||||||
|
pub fn fromString(str: []const u8) !AssimpTextureRef {
|
||||||
|
if (str.len == 0) return error.EmptyPath;
|
||||||
|
|
||||||
|
if (str[0] == '*') {
|
||||||
|
const idx = try std.fmt.parseInt(usize, str[1..], 10);
|
||||||
|
|
||||||
|
return .{ .embedded = idx };
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .external = str };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolveAssetListEntry(self: AssimpTextureRef, embedded: []const AssetListEntry) AssetListEntry {
|
||||||
|
switch (self) {
|
||||||
|
.embedded => |idx| {
|
||||||
|
return embedded[idx];
|
||||||
|
},
|
||||||
|
.external => |path| {
|
||||||
|
// TODO: resolve relative to current input file
|
||||||
|
return AssetListEntry{ .src_path = AssetPath.fromString(path), .type = .Texture };
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const MaterialTexture = struct {
|
||||||
|
path: AssimpTextureRef = .{ .external = "" },
|
||||||
|
mapping: c.aiTextureMapping = 0,
|
||||||
|
uv_index: c_uint = 0,
|
||||||
|
blend: f32 = 0,
|
||||||
|
op: c.aiTextureOp = 0,
|
||||||
|
map_mode: [3]c.aiTextureMapMode = .{ 0, 0, 0 },
|
||||||
|
flags: c_uint = 0,
|
||||||
|
};
|
||||||
|
fn getMaterialTexture(allocator: std.mem.Allocator, material: *c.aiMaterial, _type: c.aiTextureType, index: c_uint) !MaterialTexture {
|
||||||
|
var path: c.aiString = undefined;
|
||||||
|
var result: MaterialTexture = .{};
|
||||||
|
|
||||||
|
try tryAssimp(c.aiGetMaterialTexture(
|
||||||
|
material,
|
||||||
|
_type,
|
||||||
|
index,
|
||||||
|
&path,
|
||||||
|
&result.mapping,
|
||||||
|
&result.uv_index,
|
||||||
|
&result.blend,
|
||||||
|
&result.op,
|
||||||
|
&result.map_mode,
|
||||||
|
&result.flags,
|
||||||
|
));
|
||||||
|
|
||||||
|
const path_str: []u8 = try allocator.dupe(u8, @alignCast(path.data[0..path.length]));
|
||||||
|
result.path = try AssimpTextureRef.fromString(path_str);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
fn processMesh(allocator: std.mem.Allocator, scene: *const c.aiScene, mesh: *const c.aiMesh, out_file: std.fs.File) !void {
|
fn processMesh(allocator: std.mem.Allocator, scene: *const c.aiScene, mesh: *const c.aiMesh, out_file: std.fs.File) !void {
|
||||||
_ = scene; // autofix
|
_ = scene; // autofix
|
||||||
if (mesh.mNormals == null) return error.MissingNormals;
|
if (mesh.mNormals == null) return error.MissingNormals;
|
||||||
@ -422,9 +553,9 @@ fn processTexture(allocator: std.mem.Allocator, input: []const u8, contents: []c
|
|||||||
const sub_ext = std.fs.path.extension(std.fs.path.stem(input));
|
const sub_ext = std.fs.path.extension(std.fs.path.stem(input));
|
||||||
const format = if (std.mem.eql(u8, sub_ext, ".norm")) formats.Texture.Format.bc5 else formats.Texture.Format.bc7;
|
const format = if (std.mem.eql(u8, sub_ext, ".norm")) formats.Texture.Format.bc5 else formats.Texture.Format.bc7;
|
||||||
|
|
||||||
var width_int: c_int = undefined;
|
var width_int: c_int = 0;
|
||||||
var height_int: c_int = undefined;
|
var height_int: c_int = 0;
|
||||||
var comps: c_int = undefined;
|
var comps: c_int = 0;
|
||||||
|
|
||||||
c.stbi_set_flip_vertically_on_load(1);
|
c.stbi_set_flip_vertically_on_load(1);
|
||||||
const rgba_data_c = c.stbi_load_from_memory(contents.ptr, @intCast(contents.len), &width_int, &height_int, &comps, 4);
|
const rgba_data_c = c.stbi_load_from_memory(contents.ptr, @intCast(contents.len), &width_int, &height_int, &comps, 4);
|
||||||
@ -711,9 +842,16 @@ inline fn storeColorVec4(pixel: []u8, vec: @Vector(4, f32)) void {
|
|||||||
pixel[3] = @intFromFloat(out[3]);
|
pixel[3] = @intFromFloat(out[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn changeExtensionAlloc(allocator: std.mem.Allocator, input: []const u8, new_ext: []const u8) ![]u8 {
|
fn tryAssimp(code: c.aiReturn) !void {
|
||||||
const input_basename = std.fs.path.basename(input);
|
switch (code) {
|
||||||
const ext = std.fs.path.extension(input_basename);
|
c.aiReturn_SUCCESS => {},
|
||||||
const name_without_ext = input_basename[0 .. input_basename.len - ext.len];
|
c.aiReturn_FAILURE => {
|
||||||
return try std.mem.concat(allocator, u8, &.{ name_without_ext, ".", new_ext });
|
std.log.err("getMaterialTexture: {s}\n", .{c.aiGetErrorString()});
|
||||||
|
return error.AssimpError;
|
||||||
|
},
|
||||||
|
c.aiReturn_OUTOFMEMORY => {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ pub const AssetType = enum {
|
|||||||
Shader,
|
Shader,
|
||||||
ShaderProgram,
|
ShaderProgram,
|
||||||
Texture,
|
Texture,
|
||||||
|
Material,
|
||||||
|
|
||||||
pub fn pluralName(self: AssetType) []const u8 {
|
pub fn pluralName(self: AssetType) []const u8 {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
@ -14,6 +15,7 @@ pub const AssetType = enum {
|
|||||||
.Shader => "Shaders",
|
.Shader => "Shaders",
|
||||||
.ShaderProgram => "ShaderPrograms",
|
.ShaderProgram => "ShaderPrograms",
|
||||||
.Texture => "Textures",
|
.Texture => "Textures",
|
||||||
|
.Material => "Materials",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,6 +26,7 @@ pub const AssetType = enum {
|
|||||||
.Shader => "glsl",
|
.Shader => "glsl",
|
||||||
.ShaderProgram => "prog",
|
.ShaderProgram => "prog",
|
||||||
.Texture => "tex",
|
.Texture => "tex",
|
||||||
|
.Material => "mat",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user