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
|
||||
|
||||
# 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
|
||||
*.wav 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 result;
|
||||
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.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;
|
||||
|
||||
return result;
|
||||
|
@ -20,11 +20,11 @@
|
||||
.hash = "1220483cbb42231cb056f4ea6669894c68ccd560d3af5832d6e9c84c61844bc20b7d",
|
||||
},
|
||||
.@"zig-assimp" = .{
|
||||
.url = "https://github.com/sergeypdev/zig-assimp/tarball/59c2f0202bf1e5110f3eb219669f3762e3db2768",
|
||||
.hash = "122015247b178258ee2fd9f7fbd3f8025138e8e38b5cbecdc94262974da49bd1c225",
|
||||
.url = "https://github.com/sergeypdev/zig-assimp/tarball/39380dcc231788b3b54f447540bd6fea296fab10",
|
||||
.hash = "12209b95f2f5a85107ed38814143179f1e7516440704fb94004046d6f5b5ed3c8667",
|
||||
},
|
||||
.zalgebra = .{
|
||||
.url = "git+https://github.com/sergeypdev/zalgebra.git#232ff76712dc7cc270b6c48cedc84617536f3a59",
|
||||
.url = "https://github.com/sergeypdev/zalgebra/tarball/232ff76712dc7cc270b6c48cedc84617536f3a59",
|
||||
.hash = "12206e29e5d0f012c694f413b21cb66238964fdaef0a29781e0bf3ff75ec08a2ed78",
|
||||
},
|
||||
},
|
||||
|
@ -137,6 +137,21 @@ pub fn resolveScene(self: *AssetManager, handle: Handle.Scene) *const formats.Sc
|
||||
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
|
||||
pub fn watchChanges(self: *AssetManager) void {
|
||||
var iter = self.loaded_assets.iterator();
|
||||
@ -278,6 +293,8 @@ const NullScene = LoadedScene{
|
||||
.scene = .{},
|
||||
};
|
||||
|
||||
const NullMaterial = formats.Material{};
|
||||
|
||||
pub fn loadMesh(self: *AssetManager, id: AssetId) *const LoadedMesh {
|
||||
return self.loadMeshErr(id) catch |err| {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
shader: LoadedShader,
|
||||
shaderProgram: LoadedShaderProgram,
|
||||
mesh: LoadedMesh,
|
||||
texture: LoadedTexture,
|
||||
scene: LoadedScene,
|
||||
material: formats.Material,
|
||||
};
|
||||
|
||||
const LoadedShader = struct {
|
||||
@ -666,6 +710,7 @@ fn unloadAssetWithDependees(self: *AssetManager, id: AssetId) void {
|
||||
.scene => |*scene| {
|
||||
self.allocator.free(scene.buf);
|
||||
},
|
||||
.material => {},
|
||||
}
|
||||
}
|
||||
_ = self.loaded_assets.remove(id);
|
||||
|
@ -6,4 +6,5 @@ pub const Handle = struct {
|
||||
pub const ShaderProgram = extern struct { id: AssetId = 0 };
|
||||
pub const Mesh = 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.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 {
|
||||
const val: u32 = @bitCast(value);
|
||||
@ -350,6 +355,7 @@ test "write and read scene" {
|
||||
}
|
||||
|
||||
pub const Material = extern struct {
|
||||
// TODO: rgba
|
||||
albedo: Vec3 = Vec3.one(),
|
||||
albedo_map: Handle.Texture = .{},
|
||||
normal_map: Handle.Texture = .{},
|
||||
@ -359,4 +365,15 @@ pub const Material = extern struct {
|
||||
roughness_map: Handle.Texture = .{},
|
||||
emission: f32 = 0,
|
||||
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) },
|
||||
.mesh = .{
|
||||
.handle = a.Meshes.plane,
|
||||
.material = .{
|
||||
.normal_map = a.Textures.@"tile.norm",
|
||||
},
|
||||
// .material = .{
|
||||
// .normal_map = a.Textures.@"tile.norm",
|
||||
// },
|
||||
},
|
||||
});
|
||||
|
||||
@ -239,11 +239,11 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
|
||||
.flags = .{ .mesh = true },
|
||||
.mesh = .{
|
||||
.handle = a.Meshes.bunny,
|
||||
.material = .{
|
||||
.albedo_map = a.Textures.bunny_tex1,
|
||||
// .normal_map = a.Textures.@"tile.norm",
|
||||
.roughness = @as(f32, @floatFromInt(i)) / 10.0,
|
||||
},
|
||||
// .material = .{
|
||||
// .albedo_map = a.Textures.bunny_tex1,
|
||||
// // .normal_map = a.Textures.@"tile.norm",
|
||||
// .roughness = @as(f32, @floatFromInt(i)) / 10.0,
|
||||
// },
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -257,19 +257,22 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
|
||||
.flags = .{ .mesh = true },
|
||||
.mesh = .{
|
||||
.handle = a.Meshes.bunny,
|
||||
.material = .{
|
||||
.albedo = Vec3.new(1.000, 0.766, 0.336),
|
||||
// .albedo_map = a.Textures.bunny_tex1,
|
||||
// .normal_map = a.Textures.@"tile.norm",
|
||||
.roughness = @as(f32, @floatFromInt(i + 1)) / 10.0,
|
||||
.metallic = 1.0,
|
||||
},
|
||||
// .material = .{
|
||||
// .albedo = Vec3.new(1.000, 0.766, 0.336),
|
||||
// // .albedo_map = a.Textures.bunny_tex1,
|
||||
// // .normal_map = a.Textures.@"tile.norm",
|
||||
// .roughness = @as(f32, @floatFromInt(i + 1)) / 10.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 {
|
||||
@ -456,7 +459,7 @@ export fn game_update() bool {
|
||||
if (ent.data.flags.mesh) {
|
||||
gmem.render.draw(.{
|
||||
.mesh = ent.data.mesh.handle,
|
||||
.material = ent.data.mesh.material,
|
||||
.material = gmem.assetman.resolveMaterial(ent.data.mesh.material).*,
|
||||
.transform = ent.globalMatrix(&gmem.world).*,
|
||||
});
|
||||
} else if (ent.data.flags.point_light) {
|
||||
|
@ -5,6 +5,7 @@ pub const Meshes = manifest.Meshes;
|
||||
pub const Shaders = manifest.Shaders;
|
||||
pub const ShaderPrograms = manifest.ShaderPrograms;
|
||||
pub const Textures = manifest.Textures;
|
||||
pub const Materials = manifest.Materials;
|
||||
|
||||
pub fn getPath(asset_id: u64) []const u8 {
|
||||
manifest.init();
|
||||
|
@ -77,7 +77,7 @@ pub const Entity = struct {
|
||||
|
||||
pub const Mesh = extern struct {
|
||||
handle: AssetManager.Handle.Mesh = .{},
|
||||
material: Material = .{},
|
||||
material: AssetManager.Handle.Material = .{},
|
||||
};
|
||||
pub const PointLight = extern struct {
|
||||
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 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();
|
||||
|
||||
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());
|
||||
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) {
|
||||
.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)
|
||||
/// or a scene + a bunch of sub assets (meshes, materials, textures, animations, etc.)
|
||||
/// 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 {
|
||||
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;
|
||||
defer c.aiReleasePropertyStore(config);
|
||||
// const config: *c.aiPropertyStore = @as(?*c.aiPropertyStore, @ptrCast(c.aiCreatePropertyStore())) orelse return error.PropertyStore;
|
||||
// defer c.aiReleasePropertyStore(config);
|
||||
|
||||
// Remove point and line meshes
|
||||
c.aiSetImportPropertyInteger(config, c.AI_CONFIG_PP_SBP_REMOVE, c.aiPrimitiveType_POINT | c.aiPrimitiveType_LINE);
|
||||
// // Remove point and line meshes
|
||||
// c.aiSetImportPropertyInteger(config, c.AI_CONFIG_PP_SBP_REMOVE, c.aiPrimitiveType_POINT | c.aiPrimitiveType_LINE);
|
||||
|
||||
const maybe_scene: ?*const c.aiScene = @ptrCast(c.aiImportFile(
|
||||
input_z.ptr,
|
||||
@ -154,7 +162,7 @@ fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std
|
||||
return error.ImportFailed;
|
||||
}
|
||||
const scene = maybe_scene.?;
|
||||
defer c.aiReleaseImport(scene);
|
||||
// defer c.aiReleaseImport(scene);
|
||||
|
||||
if (scene.mNumMeshes == 0) return error.NoMeshes;
|
||||
|
||||
@ -168,20 +176,7 @@ fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std
|
||||
} else {
|
||||
const base_asset_path = AssetPath{ .simple = input };
|
||||
|
||||
const meshes: []*c.aiMesh = @ptrCast(scene.mMeshes[0..@intCast(scene.mNumMeshes)]);
|
||||
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);
|
||||
}
|
||||
|
||||
// Embedded textures
|
||||
var texture_outputs = try allocator.alloc(AssetListEntry, @intCast(scene.mNumTextures));
|
||||
if (scene.mTextures != null) {
|
||||
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);
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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]];
|
||||
|
||||
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 {
|
||||
for (mesh_indices) |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.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 {
|
||||
_ = scene; // autofix
|
||||
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 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 height_int: c_int = undefined;
|
||||
var comps: c_int = undefined;
|
||||
var width_int: c_int = 0;
|
||||
var height_int: c_int = 0;
|
||||
var comps: c_int = 0;
|
||||
|
||||
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);
|
||||
@ -711,9 +842,16 @@ inline fn storeColorVec4(pixel: []u8, vec: @Vector(4, f32)) void {
|
||||
pixel[3] = @intFromFloat(out[3]);
|
||||
}
|
||||
|
||||
fn changeExtensionAlloc(allocator: std.mem.Allocator, input: []const u8, new_ext: []const u8) ![]u8 {
|
||||
const input_basename = std.fs.path.basename(input);
|
||||
const ext = std.fs.path.extension(input_basename);
|
||||
const name_without_ext = input_basename[0 .. input_basename.len - ext.len];
|
||||
return try std.mem.concat(allocator, u8, &.{ name_without_ext, ".", new_ext });
|
||||
fn tryAssimp(code: c.aiReturn) !void {
|
||||
switch (code) {
|
||||
c.aiReturn_SUCCESS => {},
|
||||
c.aiReturn_FAILURE => {
|
||||
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,
|
||||
ShaderProgram,
|
||||
Texture,
|
||||
Material,
|
||||
|
||||
pub fn pluralName(self: AssetType) []const u8 {
|
||||
return switch (self) {
|
||||
@ -14,6 +15,7 @@ pub const AssetType = enum {
|
||||
.Shader => "Shaders",
|
||||
.ShaderProgram => "ShaderPrograms",
|
||||
.Texture => "Textures",
|
||||
.Material => "Materials",
|
||||
};
|
||||
}
|
||||
|
||||
@ -24,6 +26,7 @@ pub const AssetType = enum {
|
||||
.Shader => "glsl",
|
||||
.ShaderProgram => "prog",
|
||||
.Texture => "tex",
|
||||
.Material => "mat",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user