module dlib.assets; import dlib.aliases; import dlib.util; import dlib.alloc; import std.file; import std.stdio; import std.exception; File Asset_File; FileHeader Asset_Header; AssetInfo[] Asset_Info; u8[][] Asset_Data; const u32 FILE_VERSION = 2; const u32 MODEL_VERSION = 1; enum AssetType : u32 { None, ModelM3D, Shader, Texture, } alias AT = AssetType; struct FileHeader { u32 magic; u32 file_version; u64 asset_count; u64 asset_info_offset; } struct ModelHeader { u32 magic; u32 model_version; u64 vertex_count; u64 vertex_offset; u64 index_count; u64 index_offset; u64 material_count; u64 material_offset; u64 texture_count; u64 texture_offset; } struct Vertex { Vec4 color; Vec4 tangent; Vec3 pos; Vec3 normal; Vec2 uv; } struct ModelData { Vertex[] vertices; u32[] indices; Material[] materials; TextureInfo[] textures; } struct Material { Vec4 ambient; Vec4 albedo; Vec4 specular; u32 albedo_texture; u32 ambient_texture; u32 specular_texture; u32 alpha_texture; b32 albedo_has_texture; b32 ambient_has_texture; b32 specular_has_texture; b32 alpha_has_texture; f32 shininess = 0.0; f32 alpha = 0.0; } struct TextureInfo { string name; u32 id; } struct TextureHeader { u64 str_length; u64 str_offset; u32 texture_id; } struct ModelMeta { u64 index_count; } struct TexData { void* data; TexMeta meta; } struct TexMeta { u32 w; u32 h; u32 ch; } struct AssetInfo { u64 hash; u64 offset; u64 length; AssetType type; } enum MatProp { None, Ambient, Albedo, Specular, SpecularExp, Dissolve, // Transparency 1.0 -> opaque Transparency, // Transparency 0.0 -> opaque Transmission, OpticalDensity, Illumination, AmbientMap, AlbedoMap, SpecularMap, SpecularHighlightMap, AlphaMap, BumpMap, DisplacementMap, Stencil, Roughness, RoughnessMap, Metallic, MetallicMap, Sheen, SheenMap, ClearcoatThickness, ClearcoatRoughness, Emissive, EmissiveMap, Anisotropy, AnisotropyMap, NormalMap, } bool Asset_Pack_Opened = false; MatProp GetMapProp(u8[] str) { string s = ConvToStr!(u8)(str); switch(s) with(MatProp) { case "Ka": return Ambient; case "Kd": return Albedo; case "Ks": return Specular; case "Ns": return SpecularExp; case "d": return Dissolve; case "Tr": return Transparency; case "Tf": return Transmission; case "Ni": return OpticalDensity; case "illum": return Illumination; case "map_Ka": return AmbientMap; case "map_Kd": return AlbedoMap; case "map_Ks": return SpecularMap; case "map_Ns": return SpecularHighlightMap; case "map_d": return AlphaMap; case "map_bump": case "bump": return BumpMap; case "disp": return DisplacementMap; case "decal": return Stencil; case "Pr": return Roughness; case "map_Pr": return RoughnessMap; case "Pm": return Metallic; case "map_Pm": return MetallicMap; case "Pc": return ClearcoatThickness; case "Pcr": return ClearcoatRoughness; case "Ke": return Emissive; case "map_Ke": return EmissiveMap; case "aniso": return Anisotropy; case "anisor": return AnisotropyMap; case "norm": return NormalMap; default: return None; } } debug { bool g_DIR_SET = false; void SetDir() { if(exists("assets")) { chdir("./assets"); } else if(exists("Gears") || exists("Gears.exe")) { chdir("../assets"); } else { assert(false, "Unable to set directory"); } g_DIR_SET = true; } u8[] LoadAssetData(Arena* arena, string name) { if(!g_DIR_SET) { SetDir(); } File f; try { f = File(name, "rb"); } catch (ErrnoException e) { assert(false, "Unable to open file"); } u8[] mem = Alloc!(u8)(arena, f.size()); return f.rawRead(mem); } } else { void OpenAssetPack() { if(!Asset_Pack_Opened) { bool success = true; string file_path = exists("build/assets.sgp") ? "build/assets.sgp" : "assets.sgp"; // TODO: replace this with something that doesn't throw an exception and figure out if this is the best way to handle thing (probably isnt) try { Asset_File = File(file_path, "rb"); } catch (ErrnoException e) { Logf("OpenAssetPack failure: Unable to open file %s", file_path); assert(false, "Unable to open asset pack file"); } FileHeader[1] header_arr; Asset_File.rawRead(header_arr); Asset_Header = header_arr[0]; Asset_Info = Alloc!(AssetInfo)(Asset_Header.asset_count); Asset_Data = Alloc!(u8[])(Asset_Header.asset_count); assert(Asset_Header.file_version == FILE_VERSION, "OpenAssetPack failure: file version incorrect"); Asset_File.seek(Asset_Header.asset_info_offset); Asset_File.rawRead(Asset_Info); } } pragma(inline) void CheckAssetPack() { if(!Asset_Pack_Opened) { OpenAssetPack(); } } AssetInfo GetAssetInfo(string name) { CheckAssetPack(); u64 hash = Hash(name); AssetInfo asset_info; foreach(i, info; Asset_Info) { if(info.hash == hash) { asset_info = info; break; } } assert(asset_info.hash != 0, "GetAssetInfo failure: unable to find matching asset"); return asset_info; } u8[] LoadAssetData(Arena* arena, string name) { CheckAssetPack(); u64 hash = Hash(name); u8[] data = null; foreach(i, info; Asset_Info) { if(info.hash == hash) { data = Alloc!(u8)(arena, info.length); Asset_File.seek(info.offset); Asset_File.rawRead(data); assert(data != null && data.length == info.length, "LoadAssetData failure: Asset data loaded incorrectly"); break; } } return data; } u8[] LoadAssetData(string name) { CheckAssetPack(); u64 hash = Hash(name); u8[] data = null; foreach(i, info; Asset_Info) { if(info.hash == hash) { if(Asset_Data[i].ptr == null) { Asset_Data[i] = Alloc!(u8)(info.length); Asset_File.seek(info.offset); Asset_File.rawRead(Asset_Data[i]); assert(Asset_Data[i] != null && Asset_Data[i].length == info.length, "LoadAssetData failure: Asset data loaded incorrectly."); } data = Asset_Data[i]; break; } } return data; } void UnloadAssetData(string name) { u64 hash = Hash(name); foreach(i, info; Asset_Info) { if(info.hash == hash) { if(Asset_Data[i] != null) { Free(Asset_Data[i]); break; } } } } } unittest { { MatProp prop = GetMapProp(cast(u8[])r"Ka"); } }