diff --git a/assets.d b/assets.d index 7666e5b..58e5390 100644 --- a/assets.d +++ b/assets.d @@ -1,5 +1,6 @@ module dlib.assets; +import includes; import dlib.aliases; import dlib.util; import dlib.alloc; @@ -16,8 +17,8 @@ AssetInfo[] Asset_Info; u8[][] Asset_Data; -const u32 FILE_VERSION = 2; -const u32 MODEL_VERSION = 1; +const u32 FILE_VERSION = 3; +const u32 MODEL_VERSION = 2; enum AssetType : u32 { @@ -62,27 +63,79 @@ struct Vertex struct ModelData { - Vertex[] vertices; - u32[] indices; - Material[] materials; - TextureInfo[] textures; + Vertex[] vertices; + u32[] indices; + MaterialData[] materials; + TextureInfo[] textures; } -struct Material +enum IllumModel : u32 { - 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; + 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, + Bump, + FaceThickness, + Sheen, + + Max, +} + +enum MatMap : u32 +{ + Ambient, + Albedo, + Specular, + SpecularHighlight, + Alpha, + Bump, + Displacement, + Roughness, + Metallic, + Emissive, + Anisotropy, + Normal, + + Max, +} + +struct MaterialData +{ + Vec4[MatColor.max] colors; + string[MatMap.max] maps; + f32[MatFloat.max] props = 0.0; + IllumModel illum; } struct TextureInfo @@ -196,198 +249,247 @@ GetMapProp(u8[] str) case "aniso": return Anisotropy; case "anisor": return AnisotropyMap; case "norm": return NormalMap; - default: return None; + default: return None; } } +bool +M3DColor(u32 type) +{ + return type == m3dp_Kd || + type == m3dp_Ka || + type == m3dp_Ks || + type == m3dp_Ke || + type == m3dp_Tf; +} + + +MatColor +M3DMatColor(u32 type) +{ + switch(type) with(MatColor) + { + case m3dp_Kd: return Albedo; + case m3dp_Ka: return Ambient; + case m3dp_Ks: return Specular; + case m3dp_Ke: return Emissive; + case m3dp_Tf: return Transmission; + default: assert(false, "Unknown M3D MatColor"); + } +} + +bool +M3DFloat(u32 type) +{ + return type == m3dp_Ns || + type == m3dp_Km || + type == m3dp_d || + type == m3dp_Pr || + type == m3dp_Pm || + type == m3dp_Ps || + type == m3dp_Ni || + type == m3dp_Nt; +} + +MatFloat +M3DMatFloat(u32 type) +{ + switch(type) with(MatFloat) + { + case m3dp_Ns: return SpecularExp; + case m3dp_Km: return Bump; + case m3dp_d: return Alpha; + case m3dp_Pr: return Roughness; + case m3dp_Pm: return Metallic; + case m3dp_Ps: return Sheen; + case m3dp_Ni: return OpticalDensity; + case m3dp_Nt: return FaceThickness; + default: assert(false); + } +} + +bool +M3DMap(u32 type) +{ + return type == m3dp_map_Kd || + type == m3dp_map_Ka || + type == m3dp_map_Ks || + type == m3dp_map_Ns || + type == m3dp_map_Ke || + type == m3dp_map_Tf || + type == m3dp_map_Km || + type == m3dp_map_D || + type == m3dp_map_N || + type == m3dp_map_Pr || + type == m3dp_map_Pm || + type == m3dp_map_Ps || + type == m3dp_map_Ni || + type == m3dp_map_Nt; +} + debug { - -bool g_DIR_SET = false; - -void -SetDir() -{ - if(exists("assets")) + void + SetAssetsDir(string dir) { - chdir("./assets"); - } - else if(exists("Gears") || exists("Gears.exe")) - { - chdir("../assets"); - } - else - { - assert(false, "Unable to set directory"); + assert(ChDir(dir)); } - g_DIR_SET = true; -} - -u8[] -LoadAssetData(Arena* arena, string name) -{ - if(!g_DIR_SET) + u8[] + LoadAssetData(Arena* arena, string name) { - SetDir(); - } + File f; + try + { + f = File(name, "rb"); + } + catch (ErrnoException e) + { + assert(false, "Unable to open file"); + } - File f; - try - { - f = File(name, "rb"); + u8[] mem = Alloc!(u8)(arena, f.size()); + return f.rawRead(mem); } - 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) + void + OpenAssetPack(string file_path) { - 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 + if(!Asset_Pack_Opened) { - 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"); - } + bool success = true; - 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) + // 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_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."); + 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"); } - data = Asset_Data[i]; - break; + 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); } } - return data; -} - -void -UnloadAssetData(string name) -{ - u64 hash = Hash(name); - - foreach(i, info; Asset_Info) + pragma(inline) void + CheckAssetPack() { - if(info.hash == hash) + assert(Asset_Pack_Opened); + } + + AssetInfo + GetAssetInfo(string name) + { + CheckAssetPack(); + + u64 hash = Hash(name); + + AssetInfo asset_info; + foreach(i, info; Asset_Info) { - if(Asset_Data[i] != null) + if(info.hash == hash) { - Free(Asset_Data[i]); + 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 { diff --git a/packer.d b/packer.d new file mode 100644 index 0000000..7fcfffb --- /dev/null +++ b/packer.d @@ -0,0 +1,250 @@ +import dlib; +import std.stdio; +import std.string; +import std.file; +import std.path; +import std.traits; +import std.algorithm.comparison; +import core.memory; +import std.json; + +AssetType[string] Lookup = [ + ".m3d": AT.ModelM3D, + ".obj": AT.ModelObj, + ".png": AT.Texture, + ".jpg": AT.Texture, + ".spv": AT.Shader, +]; + +struct Texture +{ + string name; + u8[] data; + u32 w; + u32 h; + u32 ch; +} + +struct Model +{ + MaterialData[] mats; + +} + +u64 g_asset_count = 0; +string[] g_file_names = []; +Texture[] g_model_textures = []; + +/************************************************** + ****** UPDATE FILE_VERSION AFTER CHANGES !! ****** + **************************************************/ + +void main(string[] argv) +{ + Log("running"); + if(isDir("build")) + { + chdir("build"); + } + + bool pack = false; + bool font = false; + string font_file; + + for(u64 i = 0; i < argv.length; i += 1) + { + if(argv[i] == "-pack") + { + pack = true; + } + } + + if(pack) + { + PackFile(); + TestFile(); + } +} + +void +PackFile() +{ + File ap = File("./assets.sgp", "wb"); + scope(exit) + { + ap.flush(); + ap.close(); + chdir("../build"); + } + + chdir("../assets"); + + foreach(string file; dirEntries(".", SpanMode.depth)) + { + if (isDir(file)) continue; + + g_file_names ~= file; + g_asset_count += 1; + } + + FileHeader h = InitHeader(g_asset_count); + ap.rawWrite([h]); + + u64 offset = FileHeader.sizeof + (AssetInfo.sizeof * g_asset_count); + AssetInfo[] asset_info; + foreach(file; g_file_names) + { + AssetType type = AT.None; + foreach(extension, t; Lookup) + { + if (file.endsWith(extension)) + { + type = t; + break; + } + } + + assert(type != AT.None, "Asset Type is none, offending file " ~ file); + + auto f = File(file, "rb"); + + u64 length = cast(u64)f.size(); + + string base_name = chompPrefix(file, "./"); + AssetInfo info = { + hash: Hash(base_name), + offset: offset, + length: length, + type: type, + }; + + auto data = f.rawRead(new u8[length]); + + assert(length == data.length, "rawRead failure: data length returned doesn't match"); + + ap.seek(offset); + ap.rawWrite(data); + + offset += length; + + asset_info ~= info; + + f.close(); + } + + ap.seek(FileHeader.sizeof); + ap.rawWrite(asset_info); +} + +TexMeta +GetTexMeta(u8[] data) +{ + int ch = 4; + int width, height, has_ch; + auto img = stbi_load_from_memory(data.ptr, cast(int)data.length, &width, &height, &has_ch, ch); + + assert(img != null, "stbi_load_from_image failure: image is NULL"); + assert(width > 0 && height > 0 && has_ch > 0, "stbi_load_from_image failure: dimensions are invalid"); + + stbi_image_free(img); + + return TexMeta(w: width, h: height, ch: has_ch); +} + +void +TestFile() +{ + File ap = File("assets.sgp", "rb"); + scope(exit) + { + ap.flush(); + ap.close(); + chdir("../build"); + } + + FileHeader file_header = ap.rawRead(new FileHeader[1])[0]; + FileHeader test_header = InitHeader(g_asset_count); + assert(file_header == test_header, "TestFile failure: Header is incorrect"); + + AssetInfo[] file_info = ap.rawRead(new AssetInfo[g_asset_count]); + assert(file_info.length == file_header.asset_count, "TestFile failure: Incorrect AssetInfo length returned"); + + chdir("../assets"); + + u64 asset_index = 0; + foreach(i, file; g_file_names) + { + scope(exit) asset_index += 1; + + AssetInfo* info = file_info.ptr + asset_index; + File asset = File(file, "rb"); + + u8[] data = asset.rawRead(new u8[asset.size()]); + assert(data.length == info.length, "TestFile failure: File length read is incorrect"); + + string base_name = chompPrefix(file, "./"); + assert(Hash(base_name) == info.hash, "TestFile failure: File hash is incorrect"); + + ap.seek(info.offset); + u8[] pack_data = ap.rawRead(new u8[info.length]); + assert(equal!((a, b) => a == b)(data[], pack_data[]), "TestFile failure: Asset data does not match file data"); + } +} + +static u32 +MagicValue(string str) +{ + assert(str.length == 4, "Magic value must 4 characters"); + return cast(u32)(cast(u32)(str[0] << 24) | cast(u32)(str[1] << 16) | cast(u32)(str[2] << 8) | cast(u32)(str[3] << 0)); +} + +static FileHeader +InitHeader(u64 asset_count) +{ + FileHeader header = { + magic: MagicValue("steg"), + file_version: FILE_VERSION, + asset_count: asset_count, + asset_info_offset: FileHeader.sizeof, + }; + + return header; +} + +static ModelHeader +InitModelHeader() +{ + ModelHeader header = { + magic: MagicValue("stgm"), + model_version: MODEL_VERSION, + }; + + return header; +} + +Model +ConvertM3D(u8[] data) +{ + Model model; + + m3d_t* m3d = m3d_load(data.ptr, null, null, null); + + m3dm_t[] mats = m3d.material[0 .. m3d.nummaterial]; + foreach(mat; mats) + { + MaterialData matd; + m3dp_t[] props = mat.prop[0 .. mat.numprop]; + + foreach(prop; props) + { + switch(prop.type) + { + case m3dp_Kd: + } + + } + } + + return model; +} + diff --git a/platform.d b/platform.d index fc12963..c1fe081 100644 --- a/platform.d +++ b/platform.d @@ -438,6 +438,64 @@ const char[][] ATOM_STRS = [ alias PThreadProc = extern (C) void* function(void*); +auto +ExecLocation(T)() +{ + static char[512] buf; + + u64 size = readlink("/proc/self/exe", buf.ptr, buf.length); + + char[] str; + for(u64 i = size-1; i64(i) >= 0; i -= 1) + { + if(buf[i] == '/') + { + str = buf[0 .. i]; + break; + } + } + + static if(is(T: u8) || is(T: char) || is(T: i8)) + { + return (cast(T[])str); + } + else static if(is(T: string)) + { + string s = (cast(immutable(char)*)str.ptr)[0 .. str.length]; + return s; + } + else static assert(false); +} + +auto +Cwd(T)() +{ + static char[512] buf; + + getcwd(buf.ptr, buf.length); + + static if(is(T: u8) || is(T: i8) || is(T: char)) + { + return (cast(T*)buf.ptr)[0 .. strlen(buf.ptr)]; + } + else static if(is(T: string)) + { + string s = (cast(immutable(char)*)buf.ptr)[0 .. strlen(buf.ptr)]; + return s; + } +} + +bool +ChDir(u8[] dir) +{ + char[512] buf; + + buf[0 .. dir.length] = (cast(char*)dir)[0 .. dir.length]; + buf[dir.length] = '\0'; + + return chdir(buf.ptr) == 0; +} + SysThread CreateThread(void* proc, void* param) { @@ -1843,5 +1901,16 @@ unittest assert(ch == '`'); } } + + { + string s = ExecLocation!(string)(); + u8[] b = ExecLocation!(u8)(); + + assert(ChDir(b)); + + string wd = Cwd!(string)(); + + assert(wd == s); + } }