1 /** content/asset loader and manager */
2 
3 module re.content;
4 
5 import std.string;
6 import std.file;
7 import std.conv;
8 import std.path;
9 import std.stdio;
10 
11 import re.util.cache;
12 import re.util.interop;
13 static import raylib;
14 
15 /// manages external content loading
16 class ContentManager {
17     alias TexCache = KeyedCache!(raylib.Texture2D);
18     private TexCache _tex_cache;
19     alias ModelCache = KeyedCache!(raylib.Model);
20     private ModelCache _mdl_cache;
21     alias ShaderCache = KeyedCache!(raylib.Shader);
22     private ShaderCache _shd_cache;
23 
24     /// search paths for content
25     public string[] paths;
26 
27     /// initializes the content manager
28     this() {
29         // setup
30         _tex_cache = TexCache((tex) { raylib.UnloadTexture(tex); });
31         _mdl_cache = ModelCache((mdl) { raylib.UnloadModel(mdl); });
32         _shd_cache = ShaderCache((shd) { raylib.UnloadShader(shd); });
33     }
34 
35     /// get the physical path to a logical content path
36     public string get_path(string path) {
37         // check if this is already a valid path
38         if (std.file.exists(path)) {
39             return path;
40         }
41         auto base = string.init;
42         alias join_paths = std.path.buildNormalizedPath;
43         // check search paths first
44         foreach (search_path; paths) {
45             // if the combination path exists, then make this base
46             if (std.file.exists(join_paths(search_path, path))) {
47                 base = search_path;
48                 break;
49             }
50         }
51         return join_paths(base, path);
52     }
53 
54     private char* get_path_cstr(string path) {
55         return get_path(path).c_str;
56     }
57 
58     /// loads a texture from disk
59     public raylib.Texture2D load_texture2d(string path) {
60         raylib.Texture2D tex;
61         auto cached = _tex_cache.get(path);
62         if (cached.isNull) {
63             auto image = raylib.LoadImage(get_path_cstr(path));
64             tex = raylib.LoadTextureFromImage(image);
65             raylib.UnloadImage(image);
66             _tex_cache.put(path, tex);
67         } else {
68             tex = cached.get;
69         }
70 
71         // copy image to VRAM
72         return tex;
73     }
74 
75     /// loads a model from disk
76     public raylib.Model load_model(string path) {
77         raylib.Model mdl;
78         auto cached = _mdl_cache.get(path);
79         if (cached.isNull) {
80             mdl = raylib.LoadModel(get_path_cstr(path));
81             _mdl_cache.put(path, mdl);
82         } else {
83             mdl = cached.get;
84         }
85         return mdl;
86     }
87 
88     public raylib.ModelAnimation[] load_model_animations(string path) {
89         uint num_loaded_anims = 0;
90         raylib.ModelAnimation* loaded_anims = raylib.LoadModelAnimations(get_path_cstr(path), &num_loaded_anims);
91         auto anims = loaded_anims[0 .. num_loaded_anims]; // access array as slice
92         return anims;
93     }
94 
95     /// loads a shader from disk (vertex shader, fragment shader).
96     /// pass null to either arg to use the default
97     public raylib.Shader load_shader(string vs_path, string fs_path, bool bypass_cache = false) {
98         raylib.Shader shd;
99         import std.digest.sha : sha1Of, toHexString;
100 
101         auto path_hash = to!string(sha1Of(vs_path ~ fs_path).toHexString);
102         auto cached = _shd_cache.get(path_hash);
103         if (cached.isNull || bypass_cache) {
104             auto vs = vs_path.length > 0 ? get_path_cstr(vs_path) : null;
105             auto fs = fs_path.length > 0 ? get_path_cstr(fs_path) : null;
106             shd = raylib.LoadShader(vs, fs);
107             if (!bypass_cache)
108                 _shd_cache.put(path_hash, shd);
109         } else {
110             shd = cached.get;
111         }
112         return shd;
113     }
114 
115     public void drop_caches() {
116         // delete textures
117         _tex_cache.drop();
118         // delete models
119         _mdl_cache.drop();
120         // delete shaders
121         _shd_cache.drop();
122     }
123 
124     /// releases all resources
125     public void destroy() {
126         drop_caches();
127     }
128 }