module dlib.assets; import includes; 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; __gshared u8[] DEFAULT_TEXTURE = GenerateDefaultTexture(32, 32); const u32 FILE_VERSION = 3; const u32 MODEL_VERSION = 2; // TODO: // Alignment? /************************* ****** FILE PACKER ****** *************************/ struct FileHeader { u32 magic; u32 file_version; u64 asset_count; u64 asset_info_offset; DirHeader base_dir; } struct DirHeader { u32 magic; u64 hash; u64 dir_count; u64 asset_count; } /************************* ****** FILE PACKER ****** *************************/ /************************ *** ASSET STRUCTURES *** ************************/ alias StringID = u64; enum AssetType : u32 { None, Model, Shader, Texture, } alias AT = AssetType; struct AssetInfo { u64 hash; u64 offset; u64 length; AssetType type; } struct Mesh { Vertex[] vtx; u32[] idx; } struct MeshPart { u64 start; u64 length; } struct Model { string name; Mesh[] meshes; Material[] mats; } struct MaterialMap { u32 texture_id; Vec4 color; f32 value; } struct Material { u32 shader_id; MaterialMap[] maps; Vec4 params; } struct StringHeader { u32 id; u32 length; } struct AssetData { AssetType type; u8[] data; } /************************ *** ASSET STRUCTURES *** ************************/ /************************** ***** GFX STRUCTURES ***** **************************/ struct Vertex { Vec4 color; Vec4 tangent; Vec3 pos; Vec3 normal; Vec2 uv; Vec2 uv2; } enum IllumModel : u32 { Color = 0, ColorAmbient, Highlight, ReflectionRayTrace, GlassRayTrace, FresnelRayTrace, RefractionRayTrace, RefractionFresnelRayTrace, Reflection, Glass, ShadowsInvis, } enum MatColor : u32 { Ambient, Albedo, Specular, Transmission, Emissive, Max, } enum MatFloat : u32 { SpecularExp, Alpha, OpticalDensity, Roughness, Metallic, ClearcoatThickness, ClearcoatRoughness, Anisotropy, Sheen, Max, } enum MatMap : u32 { Ambient, Albedo, Specular, SpecularHighlight, Alpha, Bump, Displacement, Roughness, Metallic, Emissive, Anisotropy, Normal, Sheen, Stencil, Max, } /************************** ***** GFX STRUCTURES ***** **************************/ bool Asset_Pack_Opened = false; debug { void SetAssetsDir(string dir) { assert(ChDir(dir)); } u8[] LoadAssetData(Arena* arena, string name) { 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); } extern (C) cgltf_result GLTFLoadCallback(cgltf_memory_options* memory_opts, cgltf_file_options* file_opts, const(char)* path, cgltf_size* size, void** data) { u8[] file_data = OpenFile(ConvToStr(path[0 .. strlen(path)])); if(file_data == null) return cgltf_result_io_error; *size = cast(cgltf_size)file_data.length; *data = Alloc!(u8)(file_data).ptr; return cgltf_result_success; } extern (C) void GLTFFreeCallback(cgltf_memory_options* memory_opts, cgltf_file_options* file_opts, void* data, cgltf_size size) { Free(data[0 .. size]); } } else { void OpenAssetPack(string file_path) { if(!Asset_Pack_Opened) { bool success = true; // 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() { assert(Asset_Pack_Opened); } 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; } } } } } static u8[] GenerateDefaultTexture(u64 x, u64 y) { u64 tex_size = x*y*4; u8[] placeholder_tex = new u8[tex_size]; u8[4] magenta = [255, 0, 255, 255]; u8[4] black = [0, 0, 0, 255]; u64 half = tex_size/2; for(u64 i = 0; i < tex_size; i += 32) { bool swap = i <= half; for(u64 j = 0; j < 16; j += 4) { placeholder_tex[i+j .. i+j+4] = !swap ? magenta[0 .. $] : black[0 .. $]; placeholder_tex[i+j+16 .. i+j+16+4] = !swap ? black[0 .. $] : magenta[0 .. $]; } } return placeholder_tex; } Model LoadGLTF(Arena* arena, string file_name) { Model model; u8[] file_data; // = OpenFile(file_name); cgltf_options opts; cgltf_data* data; opts.file.read = &GLTFLoadCallback; opts.file.release = &GLTFFreeCallback; cgltf_result result = cgltf_parse(&opts, file_data.ptr, file_data.length, &data); if(result == cgltf_result_success) { result = cgltf_load_buffers(&opts, data, file_name.ptr); if(result != cgltf_result_success) { Logf("%s Failure: Unable to load buffers", __FUNCTION__); } u64 primitive_count; for(u64 i = 0; i < data.nodes_count; i += 1) { cgltf_node* node = &data.nodes[i]; if(node.mesh == null) continue; for(u64 j = 0; j < node.mesh.primitives_count; j += 1) { if(node.mesh.primitives[j].type == cgltf_primitive_type_triangles) { primitive_count += 1; } } } model.meshes = Alloc!(Mesh)(arena, primitive_count); model.mats = Alloc!(Material)(arena, data.materials_count+1); // Make and load default material into model.mats[0] string file_path = GetFilePath(file_name); for(u64 i = 0; i < data.materials_count; i += 1) { // model.mats[i+1] = default_material; if(data.materials[i].has_pbr_metallic_roughness) { } } } return model; }