add asset packer

This commit is contained in:
Matthew 2025-11-08 11:58:20 +11:00
parent 59b778012f
commit d290e3131c
3 changed files with 600 additions and 179 deletions

198
assets.d
View File

@ -1,5 +1,6 @@
module dlib.assets; module dlib.assets;
import includes;
import dlib.aliases; import dlib.aliases;
import dlib.util; import dlib.util;
import dlib.alloc; import dlib.alloc;
@ -16,8 +17,8 @@ AssetInfo[] Asset_Info;
u8[][] Asset_Data; u8[][] Asset_Data;
const u32 FILE_VERSION = 2; const u32 FILE_VERSION = 3;
const u32 MODEL_VERSION = 1; const u32 MODEL_VERSION = 2;
enum AssetType : u32 enum AssetType : u32
{ {
@ -64,25 +65,77 @@ struct ModelData
{ {
Vertex[] vertices; Vertex[] vertices;
u32[] indices; u32[] indices;
Material[] materials; MaterialData[] materials;
TextureInfo[] textures; TextureInfo[] textures;
} }
struct Material enum IllumModel : u32
{ {
Vec4 ambient; Color = 0,
Vec4 albedo; ColorAmbient,
Vec4 specular; Highlight,
u32 albedo_texture; ReflectionRayTrace,
u32 ambient_texture; GlassRayTrace,
u32 specular_texture; FresnelRayTrace,
u32 alpha_texture; RefractionRayTrace,
b32 albedo_has_texture; RefractionFresnelRayTrace,
b32 ambient_has_texture; Reflection,
b32 specular_has_texture; Glass,
b32 alpha_has_texture; ShadowsInvis,
f32 shininess = 0.0; }
f32 alpha = 0.0;
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 struct TextureInfo
@ -200,38 +253,91 @@ GetMapProp(u8[] str)
} }
} }
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 debug
{ {
bool g_DIR_SET = false;
void void
SetDir() SetAssetsDir(string dir)
{ {
if(exists("assets")) assert(ChDir(dir));
{
chdir("./assets");
}
else if(exists("Gears") || exists("Gears.exe"))
{
chdir("../assets");
}
else
{
assert(false, "Unable to set directory");
}
g_DIR_SET = true;
} }
u8[] u8[]
LoadAssetData(Arena* arena, string name) LoadAssetData(Arena* arena, string name)
{ {
if(!g_DIR_SET)
{
SetDir();
}
File f; File f;
try try
{ {
@ -249,14 +355,12 @@ LoadAssetData(Arena* arena, string name)
} }
else else
{ {
void void
OpenAssetPack() OpenAssetPack(string file_path)
{ {
if(!Asset_Pack_Opened) if(!Asset_Pack_Opened)
{ {
bool success = true; 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) // 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 try
@ -289,10 +393,7 @@ OpenAssetPack()
pragma(inline) void pragma(inline) void
CheckAssetPack() CheckAssetPack()
{ {
if(!Asset_Pack_Opened) assert(Asset_Pack_Opened);
{
OpenAssetPack();
}
} }
AssetInfo AssetInfo
@ -386,9 +487,10 @@ UnloadAssetData(string name)
} }
} }
} }
} }
unittest unittest
{ {
{ {

250
packer.d Normal file
View File

@ -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;
}

View File

@ -438,6 +438,64 @@ const char[][] ATOM_STRS = [
alias PThreadProc = extern (C) void* function(void*); 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 SysThread
CreateThread(void* proc, void* param) CreateThread(void* proc, void* param)
{ {
@ -1843,5 +1901,16 @@ unittest
assert(ch == '`'); assert(ch == '`');
} }
} }
{
string s = ExecLocation!(string)();
u8[] b = ExecLocation!(u8)();
assert(ChDir(b));
string wd = Cwd!(string)();
assert(wd == s);
}
} }