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