summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2024-02-12 17:42:57 -0800
committer3gg <3gg@shellblade.net>2024-02-12 17:42:57 -0800
commitc593c24b62b274fbc1d465f0386a0c5d32423f4f (patch)
treed115b0255a491ab3562c8f79176454b45505be0a
parentef55b40db3cd5cb77f7c017df48fbbcbf07d58d3 (diff)
Initial implementation for an asset cache.
-rw-r--r--game/src/plugins/viewer.c69
-rw-r--r--gfx/CMakeLists.txt1
-rw-r--r--gfx/include/gfx/asset.h98
-rw-r--r--gfx/include/gfx/gfx.h6
-rw-r--r--gfx/include/gfx/sizes.h4
-rw-r--r--gfx/include/gfx/util/scene.h24
-rw-r--r--gfx/include/gfx/util/texture.h62
-rw-r--r--gfx/src/asset/asset_cache.c183
-rw-r--r--gfx/src/asset/asset_cache.h31
-rw-r--r--gfx/src/gfx.c10
-rw-r--r--gfx/src/util/scene.c74
-rw-r--r--gfx/src/util/texture.c8
12 files changed, 419 insertions, 151 deletions
diff --git a/game/src/plugins/viewer.c b/game/src/plugins/viewer.c
index 83fc8ed..1ed3b9d 100644
--- a/game/src/plugins/viewer.c
+++ b/game/src/plugins/viewer.c
@@ -30,31 +30,33 @@ struct State {
30}; 30};
31 31
32/// Load the skyquad texture. 32/// Load the skyquad texture.
33static Texture* load_environment_map(RenderBackend* render_backend) { 33static Texture* load_environment_map(Gfx* gfx) {
34 assert(gfx);
34 return gfx_load_texture( 35 return gfx_load_texture(
35 render_backend, 36 gfx, &(LoadTextureCmd){
36 &(LoadTextureCmd){ 37 .origin = AssetFromFile,
37 .origin = TextureFromFile, 38 .type = LoadCubemap,
38 .type = LoadCubemap, 39 .colour_space = sRGB,
39 .colour_space = sRGB, 40 .filtering = NearestFiltering,
40 .filtering = NearestFiltering, 41 .mipmaps = false,
41 .mipmaps = false, 42 .data.cubemap.filepaths = {
42 .data.cubemap.filepaths = { 43 mstring_make("/assets/skybox/clouds1/clouds1_east.bmp"),
43 mstring_make("/assets/skybox/clouds1/clouds1_east.bmp"), 44 mstring_make("/assets/skybox/clouds1/clouds1_west.bmp"),
44 mstring_make("/assets/skybox/clouds1/clouds1_west.bmp"), 45 mstring_make("/assets/skybox/clouds1/clouds1_up.bmp"),
45 mstring_make("/assets/skybox/clouds1/clouds1_up.bmp"), 46 mstring_make("/assets/skybox/clouds1/clouds1_down.bmp"),
46 mstring_make("/assets/skybox/clouds1/clouds1_down.bmp"), 47 mstring_make("/assets/skybox/clouds1/clouds1_south.bmp"),
47 mstring_make("/assets/skybox/clouds1/clouds1_south.bmp"), 48 mstring_make("/assets/skybox/clouds1/clouds1_north.bmp")}
48 mstring_make("/assets/skybox/clouds1/clouds1_north.bmp")}
49 }); 49 });
50} 50}
51 51
52/// Load the skyquad and return the environment light node. 52/// Load the skyquad and return the environment light node.
53static SceneNode* load_skyquad(RenderBackend* render_backend, SceneNode* root) { 53static SceneNode* load_skyquad(Gfx* gfx, SceneNode* root) {
54 assert(render_backend); 54 assert(gfx);
55 assert(root); 55 assert(root);
56 56
57 Texture* environment_map = load_environment_map(render_backend); 57 RenderBackend* render_backend = gfx_get_render_backend(gfx);
58
59 Texture* environment_map = load_environment_map(gfx);
58 if (!environment_map) { 60 if (!environment_map) {
59 return 0; 61 return 0;
60 } 62 }
@@ -70,20 +72,19 @@ static SceneNode* load_scene(
70 assert(state); 72 assert(state);
71 assert(state->scene); 73 assert(state->scene);
72 74
73 SceneNode* root = gfx_get_scene_root(state->scene);
74 RenderBackend* render_backend = gfx_get_render_backend(game->gfx);
75
76 Camera* camera = gfx_get_camera_camera(state->camera); 75 Camera* camera = gfx_get_camera_camera(state->camera);
77 spatial3_set_position(&camera->spatial, vec3_make(0, 0, 2)); 76 spatial3_set_position(&camera->spatial, vec3_make(0, 0, 2));
78 77
79 SceneNode* sky_light_node = load_skyquad(render_backend, root); 78 SceneNode* root = gfx_get_scene_root(state->scene);
79 SceneNode* sky_light_node = load_skyquad(game->gfx, root);
80 if (!sky_light_node) { 80 if (!sky_light_node) {
81 return 0; 81 return 0; // test
82 } 82 }
83 83
84 SceneNode* scene_node = gfx_load_scene( 84 SceneNode* scene_node = gfx_load_scene(
85 game->gfx, sky_light_node, 85 game->gfx, sky_light_node,
86 &(LoadSceneCmd){.origin = SceneFromFile, .filepath = scene_filepath}); 86 &(LoadSceneCmd){
87 .origin = AssetFromFile, .filepath = mstring_make(scene_filepath)});
87 if (!scene_node) { 88 if (!scene_node) {
88 return 0; 89 return 0;
89 } 90 }
@@ -136,6 +137,10 @@ cleanup:
136void shutdown(Game* game, State* state) { 137void shutdown(Game* game, State* state) {
137 assert(game); 138 assert(game);
138 if (state) { 139 if (state) {
140 // TODO: Destroying the scene here currently does not play well with asset
141 // reloading. The issue is that we expect to mutate the scene/model during
142 // animation. This needs to change if we want to be able to cache assets
143 // in memory.
139 gfx_destroy_camera(&state->camera); 144 gfx_destroy_camera(&state->camera);
140 gfx_destroy_scene(&state->scene); 145 gfx_destroy_scene(&state->scene);
141 // State freed by plugin engine. 146 // State freed by plugin engine.
@@ -165,8 +170,20 @@ static void render_bounding_boxes_rec(ImmRenderer* imm, const SceneNode* node) {
165 assert(node); 170 assert(node);
166 if (gfx_get_node_type(node) == ObjectNode) { 171 if (gfx_get_node_type(node) == ObjectNode) {
167 // TODO: Look at the scene log. The JointNodes are detached from the 172 // TODO: Look at the scene log. The JointNodes are detached from the
168 // ObjectNodes. This is why the boxes are not being transformed as expected 173 // ObjectNodes. This is why the boxes are not being transformed as expected
169 // here. Anima needs to animate boxes? Use OOBB in addition to AABB? 174 // here. Anima needs to animate boxes? Use OOBB in addition to AABB?
175 //
176 // TODO: Idea: when a model is loaded, compute an OOBB per joint using the
177 // vertices that are affected by the joint. Then transform this OOBB when
178 // animating the skeleton. Start with AABB for simplicity. The AABB/OOBB
179 // in the skeleton should be const. The transform AABB/OOBB is derived
180 // on demand. Stack allocator would be best for this kind of per-frame
181 // data.
182 //
183 // TODO: After computing joint AABB/OOBBs, check here whether the node has
184 // a skeleton, and if so, render the skeleton's boxes instead of the
185 // node's (the node's boxes are not animated, but computer from the rest
186 // pose).
170 const mat4 model = gfx_get_node_global_transform(node); 187 const mat4 model = gfx_get_node_global_transform(node);
171 const SceneObject* obj = gfx_get_node_object(node); 188 const SceneObject* obj = gfx_get_node_object(node);
172 const aabb3 box = gfx_calc_object_aabb(obj); 189 const aabb3 box = gfx_calc_object_aabb(obj);
diff --git a/gfx/CMakeLists.txt b/gfx/CMakeLists.txt
index 3aa3312..e5c965e 100644
--- a/gfx/CMakeLists.txt
+++ b/gfx/CMakeLists.txt
@@ -33,6 +33,7 @@ add_shader_library(shaders
33 shaders/view_texture.vert) 33 shaders/view_texture.vert)
34 34
35add_library(gfx SHARED 35add_library(gfx SHARED
36 src/asset/asset_cache.c
36 src/render/buffer.c 37 src/render/buffer.c
37 src/render/framebuffer.c 38 src/render/framebuffer.c
38 src/render/geometry.c 39 src/render/geometry.c
diff --git a/gfx/include/gfx/asset.h b/gfx/include/gfx/asset.h
new file mode 100644
index 0000000..28b8557
--- /dev/null
+++ b/gfx/include/gfx/asset.h
@@ -0,0 +1,98 @@
1/* Asset Management */
2#pragma once
3
4#include <gfx/render_backend.h>
5
6#include <stddef.h>
7
8typedef struct Gfx Gfx;
9typedef struct SceneNode SceneNode;
10typedef struct ShaderProgram ShaderProgram;
11typedef struct Texture Texture;
12
13/// Describes where the asset comes from.
14typedef enum AssetOrigin {
15 AssetFromMemory,
16 AssetFromFile,
17} AssetOrigin;
18
19/// Describes a texture's colour space.
20typedef enum TextureColourSpace {
21 sRGB, // The most likely default.
22 LinearColourSpace,
23} TextureColourSpace;
24
25/// Describes a command to load a texture.
26typedef struct LoadTextureCmd {
27 AssetOrigin origin;
28 enum { LoadTexture, LoadCubemap } type;
29 TextureColourSpace colour_space;
30 TextureFiltering filtering;
31 TextureWrapping wrap;
32 bool mipmaps;
33 union {
34 // A single texture.
35 struct {
36 union {
37 struct {
38 mstring filepath;
39 };
40 struct {
41 const void* data;
42 size_t size_bytes;
43 };
44 };
45 } texture;
46 // Cubemap texture.
47 struct {
48 union {
49 struct {
50 mstring filepath_pos_x;
51 mstring filepath_neg_x;
52 mstring filepath_pos_y;
53 mstring filepath_neg_y;
54 mstring filepath_pos_z;
55 mstring filepath_neg_z;
56 } filepaths;
57 struct {
58 const void* data_pos_x;
59 const void* data_neg_x;
60 const void* data_pos_y;
61 const void* data_neg_y;
62 const void* data_pos_z;
63 const void* data_neg_z;
64 } buffers;
65 };
66 } cubemap;
67 } data;
68} LoadTextureCmd;
69
70/// Describes a command to load a scene.
71typedef struct LoadSceneCmd {
72 AssetOrigin origin;
73 union {
74 struct {
75 mstring filepath;
76 };
77 struct {
78 const void* data;
79 size_t size_bytes;
80 };
81 };
82 ShaderProgram* shader;
83} LoadSceneCmd;
84
85/// Load a scene.
86///
87/// Return a top-level node under which scene elements are rooted. |root_node|
88/// is made the parent of this top-level node.
89///
90/// |shader| is an optional shader program assigned to the loaded scene objects.
91/// If no shader is given, a Cook-Torrance shader based on the object's
92/// characteristics (presence of normals, tangents, etc) is assigned.
93///
94/// Currently only supports the GLTF format.
95SceneNode* gfx_load_scene(Gfx*, SceneNode* root_node, const LoadSceneCmd*);
96
97/// Load a texture.
98Texture* gfx_load_texture(Gfx*, const LoadTextureCmd*);
diff --git a/gfx/include/gfx/gfx.h b/gfx/include/gfx/gfx.h
index b8f2595..bfc457f 100644
--- a/gfx/include/gfx/gfx.h
+++ b/gfx/include/gfx/gfx.h
@@ -1,10 +1,9 @@
1#pragma once 1#pragma once
2 2
3typedef struct AssetCache AssetCache;
3typedef struct ImmRenderer ImmRenderer; 4typedef struct ImmRenderer ImmRenderer;
4typedef struct RenderBackend RenderBackend; 5typedef struct RenderBackend RenderBackend;
5typedef struct Renderer Renderer; 6typedef struct Renderer Renderer;
6typedef struct Scene Scene;
7typedef struct SceneCamera SceneCamera;
8 7
9typedef struct Gfx Gfx; 8typedef struct Gfx Gfx;
10 9
@@ -23,6 +22,9 @@ Renderer* gfx_get_renderer(Gfx*);
23/// Get the immediate mode renderer. 22/// Get the immediate mode renderer.
24ImmRenderer* gfx_get_imm_renderer(Gfx*); 23ImmRenderer* gfx_get_imm_renderer(Gfx*);
25 24
25/// Get the asset cache.
26AssetCache* gfx_get_asset_cache(Gfx*);
27
26/// Remove unused resources from the scene (meshes, materials). 28/// Remove unused resources from the scene (meshes, materials).
27/// TODO: need to think about the interface for scene_purge(). Maybe this 29/// TODO: need to think about the interface for scene_purge(). Maybe this
28/// should be gfx_purge() and take a list of Scenes? 30/// should be gfx_purge() and take a list of Scenes?
diff --git a/gfx/include/gfx/sizes.h b/gfx/include/gfx/sizes.h
index f2ace8a..b6f47ef 100644
--- a/gfx/include/gfx/sizes.h
+++ b/gfx/include/gfx/sizes.h
@@ -83,6 +83,10 @@
83/// Maximum number of matrices in the immediate-mode renderer's matrix stack. 83/// Maximum number of matrices in the immediate-mode renderer's matrix stack.
84#define IMM_MAX_NUM_MATRICES 32 84#define IMM_MAX_NUM_MATRICES 32
85 85
86// Asset Manager.
87
88#define GFX_MAX_NUM_ASSETS 1024
89
86// Gfx. 90// Gfx.
87 91
88#define GFX_MAX_NUM_SCENES 4 92#define GFX_MAX_NUM_SCENES 4
diff --git a/gfx/include/gfx/util/scene.h b/gfx/include/gfx/util/scene.h
index fa9304b..c03e2cb 100644
--- a/gfx/include/gfx/util/scene.h
+++ b/gfx/include/gfx/util/scene.h
@@ -1,26 +1,10 @@
1/// Load scene files. 1/// Load scene files.
2#pragma once 2#pragma once
3 3
4#include <stdbool.h> 4#include <gfx/asset.h>
5#include <stddef.h>
6 5
7typedef struct Gfx Gfx; 6typedef struct Gfx Gfx;
8typedef struct SceneNode SceneNode; 7typedef struct SceneNode SceneNode;
9typedef struct ShaderProgram ShaderProgram;
10
11typedef struct LoadSceneCmd {
12 enum { SceneFromMemory, SceneFromFile } origin;
13 union {
14 struct {
15 const char* filepath;
16 };
17 struct {
18 const void* data;
19 size_t size_bytes;
20 };
21 };
22 ShaderProgram* shader;
23} LoadSceneCmd;
24 8
25/// Load a scene. 9/// Load a scene.
26/// 10///
@@ -32,4 +16,4 @@ typedef struct LoadSceneCmd {
32/// characteristics (presence of normals, tangents, etc) is assigned. 16/// characteristics (presence of normals, tangents, etc) is assigned.
33/// 17///
34/// Currently only supports the GLTF format. 18/// Currently only supports the GLTF format.
35SceneNode* gfx_load_scene(Gfx*, SceneNode* root_node, const LoadSceneCmd*); 19SceneNode* gfx_scene_load(Gfx*, SceneNode* root_node, const LoadSceneCmd*);
diff --git a/gfx/include/gfx/util/texture.h b/gfx/include/gfx/util/texture.h
index cdc582b..a3239fe 100644
--- a/gfx/include/gfx/util/texture.h
+++ b/gfx/include/gfx/util/texture.h
@@ -1,63 +1,7 @@
1/// Load textures from images. 1/// Load textures from images.
2#pragma once 2#pragma once
3 3
4#include <gfx/render_backend.h> 4#include <gfx/asset.h>
5 5
6#include <cstring.h> 6/// Load a texture.
7 7Texture* gfx_texture_load(RenderBackend*, const LoadTextureCmd*);
8#include <stdbool.h>
9#include <stddef.h>
10
11/// Describes a texture's colour space.
12typedef enum TextureColourSpace {
13 sRGB, // The most likely default.
14 LinearColourSpace,
15} TextureColourSpace;
16
17/// Describes a command to load a texture.
18typedef struct LoadTextureCmd {
19 enum { TextureFromMemory, TextureFromFile } origin;
20 enum { LoadTexture, LoadCubemap } type;
21 TextureColourSpace colour_space;
22 TextureFiltering filtering;
23 TextureWrapping wrap;
24 bool mipmaps;
25 union {
26 // A single texture.
27 struct {
28 union {
29 struct {
30 mstring filepath;
31 };
32 struct {
33 const void* data;
34 size_t size_bytes;
35 };
36 };
37 } texture;
38 // Cubemap texture.
39 struct {
40 union {
41 struct {
42 mstring filepath_pos_x;
43 mstring filepath_neg_x;
44 mstring filepath_pos_y;
45 mstring filepath_neg_y;
46 mstring filepath_pos_z;
47 mstring filepath_neg_z;
48 } filepaths;
49 struct {
50 const void* data_pos_x;
51 const void* data_neg_x;
52 const void* data_pos_y;
53 const void* data_neg_y;
54 const void* data_pos_z;
55 const void* data_neg_z;
56 } buffers;
57 };
58 } cubemap;
59 } data;
60} LoadTextureCmd;
61
62/// Load a cubemap texture.
63Texture* gfx_load_texture(RenderBackend*, const LoadTextureCmd*);
diff --git a/gfx/src/asset/asset_cache.c b/gfx/src/asset/asset_cache.c
new file mode 100644
index 0000000..0c6a8dc
--- /dev/null
+++ b/gfx/src/asset/asset_cache.c
@@ -0,0 +1,183 @@
1#include "asset_cache.h"
2
3#include <gfx/asset.h>
4#include <gfx/gfx.h>
5#include <gfx/util/scene.h>
6#include <gfx/util/texture.h>
7
8#include <cstring.h>
9#include <log/log.h>
10
11#include <assert.h>
12#include <stddef.h>
13
14static Hash calc_scene_hash(const LoadSceneCmd* cmd) {
15 assert(cmd);
16 switch (cmd->origin) {
17 case AssetFromFile:
18 return cstring_hash(mstring_cstr(&cmd->filepath));
19 case AssetFromMemory:
20 return (Hash)cmd->data;
21 }
22 assert(false);
23 return 0;
24}
25
26static Hash calc_texture_hash(const LoadTextureCmd* cmd) {
27 assert(cmd);
28 switch (cmd->origin) {
29 case AssetFromFile:
30 switch (cmd->type) {
31 case LoadTexture:
32 return cstring_hash(mstring_cstr(&cmd->data.texture.filepath));
33 case LoadCubemap:
34 return cstring_hash(
35 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_x)) ^
36 cstring_hash(
37 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_neg_x)) ^
38 cstring_hash(
39 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_y)) ^
40 cstring_hash(
41 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_neg_y)) ^
42 cstring_hash(
43 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_z)) ^
44 cstring_hash(
45 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_neg_z));
46 }
47 break;
48 case AssetFromMemory:
49 switch (cmd->type) {
50 case LoadTexture:
51 return (Hash)cmd->data.texture.data;
52 case LoadCubemap:
53 return (Hash)cmd->data.cubemap.buffers.data_pos_x ^
54 (Hash)cmd->data.cubemap.buffers.data_neg_x ^
55 (Hash)cmd->data.cubemap.buffers.data_pos_y ^
56 (Hash)cmd->data.cubemap.buffers.data_neg_y ^
57 (Hash)cmd->data.cubemap.buffers.data_pos_z ^
58 (Hash)cmd->data.cubemap.buffers.data_neg_z;
59 }
60 break;
61 }
62 assert(false);
63 return 0;
64}
65
66static Asset* lookup_cache(AssetCache* cache, Hash hash) {
67 assert(cache);
68 mempool_foreach(&cache->assets, asset, {
69 if (asset->hash == hash) {
70 return asset;
71 }
72 });
73 return 0;
74}
75
76// TODO: questionable function. Make mempool_alloc() fail when out of memory.
77static void insert_into_cache(AssetCache* cache, const Asset* asset) {
78 assert(cache);
79 assert(asset);
80 Asset* poolAsset = mempool_alloc(&cache->assets);
81 assert(asset);
82 *poolAsset = *asset;
83}
84
85static void log_scene_cache_hit(const LoadSceneCmd* cmd, Hash hash) {
86 assert(cmd);
87 switch (cmd->origin) {
88 case AssetFromFile:
89 LOGI(
90 "Found asset [%s] in cache with hash [%lu]",
91 mstring_cstr(&cmd->filepath), hash);
92 break;
93 case AssetFromMemory:
94 LOGI("Found asset [%p] in cache with hash [%lu]", cmd->data, hash);
95 break;
96 }
97}
98
99static void log_scene_loaded(const LoadSceneCmd* cmd) {
100 assert(cmd);
101 switch (cmd->origin) {
102 case AssetFromFile:
103 LOGI("Loaded asset from file: [%s]", mstring_cstr(&cmd->filepath));
104 break;
105 case AssetFromMemory:
106 LOGI("Loaded asset from memory: [%p]", cmd->data);
107 break;
108 }
109}
110
111void gfx_init_asset_cache(AssetCache* cache) {
112 assert(cache);
113 mempool_make(&cache->assets);
114
115 // Allocate a dummy asset at index 0 to guarantee that no assets allocated by
116 // the caller map to index 0.
117 const Asset* dummy = mempool_alloc(&cache->assets);
118 assert(mempool_get_block_index(&cache->assets, dummy) == 0);
119}
120
121void gfx_destroy_asset_cache(AssetCache* cache) {
122 assert(cache);
123 mempool_del(&cache->assets);
124}
125
126SceneNode* gfx_load_scene(
127 Gfx* gfx, SceneNode* root_node, const LoadSceneCmd* cmd) {
128 assert(gfx);
129
130 AssetCache* cache = gfx_get_asset_cache(gfx);
131
132 // First search for the asset in the cache.
133 // TODO: Animated models are currently mutated in place, so sharing them is
134 // not really valid.
135 const uint64_t hash = calc_scene_hash(cmd);
136 Asset* asset = lookup_cache(cache, hash);
137 if (asset) {
138 log_scene_cache_hit(cmd, hash);
139 return (SceneNode*)asset;
140 }
141
142 // Asset not found in the cache.
143 // Load it, insert it into the cache, and return it.
144 SceneNode* node = gfx_scene_load(gfx, root_node, cmd);
145 if (node) {
146 insert_into_cache(
147 cache, &(Asset){
148 .type = SceneAsset,
149 .hash = hash,
150 .data = node,
151 });
152 log_scene_loaded(cmd);
153 }
154 return node;
155}
156
157Texture* gfx_load_texture(Gfx* gfx, const LoadTextureCmd* cmd) {
158 assert(gfx);
159 assert(cmd);
160
161 AssetCache* cache = gfx_get_asset_cache(gfx);
162
163 // First search for the asset in the cache.
164 const uint64_t hash = calc_texture_hash(cmd);
165 Asset* asset = lookup_cache(cache, hash);
166 if (asset) {
167 return (Texture*)asset;
168 }
169
170 // Asset not found in the cache.
171 // Load it, insert it into the cache, and return it.
172 RenderBackend* render_backend = gfx_get_render_backend(gfx);
173 Texture* texture = gfx_texture_load(render_backend, cmd);
174 if (texture) {
175 insert_into_cache(
176 cache, &(Asset){
177 .type = TextureAsset,
178 .hash = hash,
179 .data = texture,
180 });
181 }
182 return texture;
183}
diff --git a/gfx/src/asset/asset_cache.h b/gfx/src/asset/asset_cache.h
new file mode 100644
index 0000000..04baa51
--- /dev/null
+++ b/gfx/src/asset/asset_cache.h
@@ -0,0 +1,31 @@
1#pragma once
2
3#include <gfx/sizes.h>
4
5#include <cstring.h>
6#include <mempool.h>
7
8typedef uint64_t Hash;
9
10typedef enum AssetType {
11 SceneAsset,
12 TextureAsset,
13} AssetType;
14
15typedef struct Asset {
16 AssetType type;
17 Hash hash;
18 void* data;
19} Asset;
20
21DEF_MEMPOOL(asset_pool, Asset, GFX_MAX_NUM_ASSETS)
22
23typedef struct AssetCache {
24 asset_pool assets;
25} AssetCache;
26
27/// Create a new asset cache.
28void gfx_init_asset_cache(AssetCache*);
29
30/// Destroy the asset cache.
31void gfx_destroy_asset_cache(AssetCache*);
diff --git a/gfx/src/gfx.c b/gfx/src/gfx.c
index fc720ed..7095ea1 100644
--- a/gfx/src/gfx.c
+++ b/gfx/src/gfx.c
@@ -1,5 +1,6 @@
1#include <gfx/gfx.h> 1#include <gfx/gfx.h>
2 2
3#include "asset/asset_cache.h"
3#include "render/render_backend_impl.h" 4#include "render/render_backend_impl.h"
4#include "renderer/imm_renderer_impl.h" 5#include "renderer/imm_renderer_impl.h"
5#include "renderer/renderer_impl.h" 6#include "renderer/renderer_impl.h"
@@ -14,6 +15,7 @@
14#include <stdlib.h> 15#include <stdlib.h>
15 16
16typedef struct Gfx { 17typedef struct Gfx {
18 AssetCache asset_cache;
17 RenderBackend render_backend; 19 RenderBackend render_backend;
18 Renderer renderer; 20 Renderer renderer;
19 ImmRenderer imm_renderer; 21 ImmRenderer imm_renderer;
@@ -40,16 +42,17 @@ Gfx* gfx_init(void) {
40 gfx_destroy(&gfx); 42 gfx_destroy(&gfx);
41 return 0; 43 return 0;
42 } 44 }
45 gfx_init_asset_cache(&gfx->asset_cache);
43 scene_mem_init(); 46 scene_mem_init();
44 return gfx; 47 return gfx;
45} 48}
46 49
47void gfx_destroy(Gfx** gfx) { 50void gfx_destroy(Gfx** gfx) {
48 assert(gfx);
49 if (!gfx) { 51 if (!gfx) {
50 return; 52 return;
51 } 53 }
52 scene_mem_destroy(); 54 scene_mem_destroy();
55 gfx_destroy_asset_cache(&(*gfx)->asset_cache);
53 renderer_destroy(&(*gfx)->renderer); 56 renderer_destroy(&(*gfx)->renderer);
54 imm_renderer_destroy(&(*gfx)->imm_renderer); 57 imm_renderer_destroy(&(*gfx)->imm_renderer);
55 gfx_del_render_backend(&(*gfx)->render_backend); 58 gfx_del_render_backend(&(*gfx)->render_backend);
@@ -71,3 +74,8 @@ ImmRenderer* gfx_get_imm_renderer(Gfx* gfx) {
71 assert(gfx); 74 assert(gfx);
72 return &gfx->imm_renderer; 75 return &gfx->imm_renderer;
73} 76}
77
78AssetCache* gfx_get_asset_cache(Gfx* gfx) {
79 assert(gfx);
80 return &gfx->asset_cache;
81}
diff --git a/gfx/src/util/scene.c b/gfx/src/util/scene.c
index 5d79cf2..5bf45aa 100644
--- a/gfx/src/util/scene.c
+++ b/gfx/src/util/scene.c
@@ -655,7 +655,7 @@ static void load_textures_lazy(
655 mstring_concat_path(mstring_make(directory), mstring_make(image->uri)); 655 mstring_concat_path(mstring_make(directory), mstring_make(image->uri));
656 656
657 load_texture_cmds[i] = (LoadTextureCmd){ 657 load_texture_cmds[i] = (LoadTextureCmd){
658 .origin = TextureFromFile, 658 .origin = AssetFromFile,
659 .type = LoadTexture, 659 .type = LoadTexture,
660 .colour_space = sRGB, 660 .colour_space = sRGB,
661 .filtering = filtering, 661 .filtering = filtering,
@@ -670,12 +670,11 @@ static void load_textures_lazy(
670/// This determines a texture's colour space based on its intended use, loads 670/// This determines a texture's colour space based on its intended use, loads
671/// the texture, and then defines the sampler shader uniform. 671/// the texture, and then defines the sampler shader uniform.
672static bool load_texture_and_uniform( 672static bool load_texture_and_uniform(
673 const cgltf_data* data, RenderBackend* render_backend, 673 const cgltf_data* data, Gfx* gfx, const cgltf_texture_view* texture_view,
674 const cgltf_texture_view* texture_view, TextureType texture_type, 674 TextureType texture_type, Texture** textures,
675 Texture** textures, LoadTextureCmd* load_texture_cmds, int* next_uniform, 675 LoadTextureCmd* load_texture_cmds, int* next_uniform, MaterialDesc* desc) {
676 MaterialDesc* desc) {
677 assert(data); 676 assert(data);
678 assert(render_backend); 677 assert(gfx);
679 assert(texture_view); 678 assert(texture_view);
680 assert(textures); 679 assert(textures);
681 assert(next_uniform); 680 assert(next_uniform);
@@ -700,7 +699,7 @@ static bool load_texture_and_uniform(
700 mstring_cstr(&cmd->data.texture.filepath), cmd->mipmaps, 699 mstring_cstr(&cmd->data.texture.filepath), cmd->mipmaps,
701 cmd->filtering); 700 cmd->filtering);
702 701
703 textures[texture_index] = gfx_load_texture(render_backend, cmd); 702 textures[texture_index] = gfx_load_texture(gfx, cmd);
704 if (!textures[texture_index]) { 703 if (!textures[texture_index]) {
705 prepend_error( 704 prepend_error(
706 "Failed to load texture: %s", 705 "Failed to load texture: %s",
@@ -724,11 +723,10 @@ static bool load_texture_and_uniform(
724/// the index of each glTF material in the scene. Also return the number of 723/// the index of each glTF material in the scene. Also return the number of
725/// materials and the textures used by them. 724/// materials and the textures used by them.
726static bool load_materials( 725static bool load_materials(
727 const cgltf_data* data, RenderBackend* render_backend, 726 const cgltf_data* data, Gfx* gfx, LoadTextureCmd* load_texture_cmds,
728 LoadTextureCmd* load_texture_cmds, Texture** textures, 727 Texture** textures, Material** materials) {
729 Material** materials) {
730 assert(data); 728 assert(data);
731 assert(render_backend); 729 assert(gfx);
732 assert(materials); 730 assert(materials);
733 if (data->textures_count > 0) { 731 if (data->textures_count > 0) {
734 assert(load_texture_cmds); 732 assert(load_texture_cmds);
@@ -771,16 +769,15 @@ static bool load_materials(
771 769
772 if (pbr->base_color_texture.texture) { 770 if (pbr->base_color_texture.texture) {
773 if (!load_texture_and_uniform( 771 if (!load_texture_and_uniform(
774 data, render_backend, &pbr->base_color_texture, 772 data, gfx, &pbr->base_color_texture, BaseColorTexture, textures,
775 BaseColorTexture, textures, load_texture_cmds, &next_uniform, 773 load_texture_cmds, &next_uniform, &desc)) {
776 &desc)) {
777 return false; 774 return false;
778 } 775 }
779 } 776 }
780 777
781 if (pbr->metallic_roughness_texture.texture) { 778 if (pbr->metallic_roughness_texture.texture) {
782 if (!load_texture_and_uniform( 779 if (!load_texture_and_uniform(
783 data, render_backend, &pbr->metallic_roughness_texture, 780 data, gfx, &pbr->metallic_roughness_texture,
784 MetallicRoughnessTexture, textures, load_texture_cmds, 781 MetallicRoughnessTexture, textures, load_texture_cmds,
785 &next_uniform, &desc)) { 782 &next_uniform, &desc)) {
786 return false; 783 return false;
@@ -790,24 +787,23 @@ static bool load_materials(
790 787
791 if (mat->emissive_texture.texture) { 788 if (mat->emissive_texture.texture) {
792 if (!load_texture_and_uniform( 789 if (!load_texture_and_uniform(
793 data, render_backend, &mat->emissive_texture, EmissiveTexture, 790 data, gfx, &mat->emissive_texture, EmissiveTexture, textures,
794 textures, load_texture_cmds, &next_uniform, &desc)) { 791 load_texture_cmds, &next_uniform, &desc)) {
795 return false; 792 return false;
796 } 793 }
797 } 794 }
798 795
799 if (mat->occlusion_texture.texture) { 796 if (mat->occlusion_texture.texture) {
800 if (!load_texture_and_uniform( 797 if (!load_texture_and_uniform(
801 data, render_backend, &mat->occlusion_texture, 798 data, gfx, &mat->occlusion_texture, AmbientOcclusionTexture,
802 AmbientOcclusionTexture, textures, load_texture_cmds, 799 textures, load_texture_cmds, &next_uniform, &desc)) {
803 &next_uniform, &desc)) {
804 return false; 800 return false;
805 } 801 }
806 } 802 }
807 803
808 if (mat->normal_texture.texture) { 804 if (mat->normal_texture.texture) {
809 if (!load_texture_and_uniform( 805 if (!load_texture_and_uniform(
810 data, render_backend, &mat->normal_texture, NormalMap, textures, 806 data, gfx, &mat->normal_texture, NormalMap, textures,
811 load_texture_cmds, &next_uniform, &desc)) { 807 load_texture_cmds, &next_uniform, &desc)) {
812 return false; 808 return false;
813 } 809 }
@@ -878,7 +874,7 @@ aabb3 compute_aabb(const cgltf_accessor* accessor, int dim) {
878 874
879/// Load all meshes from the glTF scene. 875/// Load all meshes from the glTF scene.
880static bool load_meshes( 876static bool load_meshes(
881 const cgltf_data* data, Gfx* gfx, Buffer** buffers, 877 const cgltf_data* data, RenderBackend* render_backend, Buffer** buffers,
882 Buffer** tangent_buffers, const cgltfTangentBuffer* cgltf_tangent_buffers, 878 Buffer** tangent_buffers, const cgltfTangentBuffer* cgltf_tangent_buffers,
883 cgltf_size num_tangent_buffers, Material** materials, 879 cgltf_size num_tangent_buffers, Material** materials,
884 ShaderProgram* const shader, size_t primitive_count, Geometry** geometries, 880 ShaderProgram* const shader, size_t primitive_count, Geometry** geometries,
@@ -894,7 +890,7 @@ static bool load_meshes(
894 // Accessor + buffer view BufferView 890 // Accessor + buffer view BufferView
895 // Buffer Buffer 891 // Buffer Buffer
896 assert(data); 892 assert(data);
897 assert(gfx); 893 assert(render_backend);
898 assert(buffers); 894 assert(buffers);
899 assert(materials); 895 assert(materials);
900 assert(geometries); 896 assert(geometries);
@@ -905,9 +901,6 @@ static bool load_meshes(
905 assert(cgltf_tangent_buffers); 901 assert(cgltf_tangent_buffers);
906 } 902 }
907 903
908 // TODO: pass render_backend as argument instead of gfx.
909 RenderBackend* render_backend = gfx_get_render_backend(gfx);
910
911 // Points to the next available Mesh and also the next available Geometry. 904 // Points to the next available Mesh and also the next available Geometry.
912 // There is one (Mesh, Geometry) pair per glTF mesh primitive. 905 // There is one (Mesh, Geometry) pair per glTF mesh primitive.
913 size_t next_mesh = 0; 906 size_t next_mesh = 0;
@@ -1331,6 +1324,10 @@ static void load_nodes(
1331 assert(skin_index < data->skins_count); 1324 assert(skin_index < data->skins_count);
1332 const Skeleton* skeleton = gfx_get_anima_skeleton(anima, skin_index); 1325 const Skeleton* skeleton = gfx_get_anima_skeleton(anima, skin_index);
1333 gfx_set_object_skeleton(object, skeleton); 1326 gfx_set_object_skeleton(object, skeleton);
1327
1328 // TODO: Compute AABBs/OOBBs for the skeleton's joints here. Iterate
1329 // over the mesh's primitives, its vertices, their joint indices, and
1330 // add the vertex to the AABB/OOBB.
1334 } 1331 }
1335 } else if (node->camera) { 1332 } else if (node->camera) {
1336 assert(next_camera < data->cameras_count); 1333 assert(next_camera < data->cameras_count);
@@ -1409,7 +1406,7 @@ static void load_nodes(
1409/// This function ignores the many scenes and default scene of the glTF spec 1406/// This function ignores the many scenes and default scene of the glTF spec
1410/// and instead just loads all scenes into a single gfx Scene. 1407/// and instead just loads all scenes into a single gfx Scene.
1411static SceneNode* load_scene( 1408static SceneNode* load_scene(
1412 cgltf_data* data, Gfx* gfx, SceneNode* root_node, const char* filepath, 1409 cgltf_data* data, Gfx* gfx, SceneNode* root_node, const mstring* filepath,
1413 ShaderProgram* shader, const cgltfTangentBuffer* cgltf_tangent_buffers, 1410 ShaderProgram* shader, const cgltfTangentBuffer* cgltf_tangent_buffers,
1414 cgltf_size num_tangent_buffers) { 1411 cgltf_size num_tangent_buffers) {
1415 // In a GLTF scene, buffers can be shared among meshes, meshes among nodes, 1412 // In a GLTF scene, buffers can be shared among meshes, meshes among nodes,
@@ -1437,8 +1434,8 @@ static SceneNode* load_scene(
1437 RenderBackend* render_backend = gfx_get_render_backend(gfx); 1434 RenderBackend* render_backend = gfx_get_render_backend(gfx);
1438 const size_t primitive_count = get_total_primitives(data); 1435 const size_t primitive_count = get_total_primitives(data);
1439 1436
1440 const mstring directory = mstring_dirname(mstring_make(filepath)); 1437 const mstring directory = mstring_dirname(*filepath);
1441 LOGD("Filepath: %s", filepath); 1438 LOGD("Filepath: %s", mstring_cstr(filepath));
1442 LOGD("Directory: %s", mstring_cstr(&directory)); 1439 LOGD("Directory: %s", mstring_cstr(&directory));
1443 1440
1444 Buffer** tangent_buffers = 0; 1441 Buffer** tangent_buffers = 0;
@@ -1494,13 +1491,12 @@ static SceneNode* load_scene(
1494 data, render_backend, mstring_cstr(&directory), load_texture_cmds); 1491 data, render_backend, mstring_cstr(&directory), load_texture_cmds);
1495 } 1492 }
1496 1493
1497 if (!load_materials( 1494 if (!load_materials(data, gfx, load_texture_cmds, textures, materials)) {
1498 data, render_backend, load_texture_cmds, textures, materials)) {
1499 goto cleanup; 1495 goto cleanup;
1500 } 1496 }
1501 1497
1502 if (!load_meshes( 1498 if (!load_meshes(
1503 data, gfx, buffers, tangent_buffers, cgltf_tangent_buffers, 1499 data, render_backend, buffers, tangent_buffers, cgltf_tangent_buffers,
1504 num_tangent_buffers, materials, shader, primitive_count, geometries, 1500 num_tangent_buffers, materials, shader, primitive_count, geometries,
1505 meshes, scene_objects)) { 1501 meshes, scene_objects)) {
1506 goto cleanup; 1502 goto cleanup;
@@ -1639,7 +1635,7 @@ cleanup:
1639 return anima_node; 1635 return anima_node;
1640} 1636}
1641 1637
1642SceneNode* gfx_load_scene( 1638SceneNode* gfx_scene_load(
1643 Gfx* gfx, SceneNode* root_node, const LoadSceneCmd* cmd) { 1639 Gfx* gfx, SceneNode* root_node, const LoadSceneCmd* cmd) {
1644 assert(gfx); 1640 assert(gfx);
1645 assert(root_node); 1641 assert(root_node);
@@ -1653,10 +1649,10 @@ SceneNode* gfx_load_scene(
1653 1649
1654 cgltf_result result; 1650 cgltf_result result;
1655 switch (cmd->origin) { 1651 switch (cmd->origin) {
1656 case SceneFromFile: 1652 case AssetFromFile:
1657 result = cgltf_parse_file(&options, cmd->filepath, &data); 1653 result = cgltf_parse_file(&options, mstring_cstr(&cmd->filepath), &data);
1658 break; 1654 break;
1659 case SceneFromMemory: 1655 case AssetFromMemory:
1660 result = cgltf_parse(&options, cmd->data, cmd->size_bytes, &data); 1656 result = cgltf_parse(&options, cmd->data, cmd->size_bytes, &data);
1661 break; 1657 break;
1662 } 1658 }
@@ -1664,9 +1660,9 @@ SceneNode* gfx_load_scene(
1664 goto cleanup; 1660 goto cleanup;
1665 } 1661 }
1666 1662
1667 if (cmd->origin == SceneFromFile) { 1663 if (cmd->origin == AssetFromFile) {
1668 // Must call cgltf_load_buffers() to load buffer data. 1664 // Must call cgltf_load_buffers() to load buffer data.
1669 result = cgltf_load_buffers(&options, data, cmd->filepath); 1665 result = cgltf_load_buffers(&options, data, mstring_cstr(&cmd->filepath));
1670 if (result != cgltf_result_success) { 1666 if (result != cgltf_result_success) {
1671 goto cleanup; 1667 goto cleanup;
1672 } 1668 }
@@ -1678,7 +1674,7 @@ SceneNode* gfx_load_scene(
1678 &options, data, &tangent_buffers, &num_tangent_buffers); 1674 &options, data, &tangent_buffers, &num_tangent_buffers);
1679 1675
1680 scene_node = load_scene( 1676 scene_node = load_scene(
1681 data, gfx, root_node, cmd->filepath, cmd->shader, tangent_buffers, 1677 data, gfx, root_node, &cmd->filepath, cmd->shader, tangent_buffers,
1682 num_tangent_buffers); 1678 num_tangent_buffers);
1683 1679
1684cleanup: 1680cleanup:
diff --git a/gfx/src/util/texture.c b/gfx/src/util/texture.c
index 23f15f0..0d2e4b8 100644
--- a/gfx/src/util/texture.c
+++ b/gfx/src/util/texture.c
@@ -43,18 +43,18 @@ static void flip_horizontally(
43// For this reason, we do X and Y flips when doing cubemap textures so that we 43// For this reason, we do X and Y flips when doing cubemap textures so that we
44// can sample cubemaps as if they were given in the usual OpenGL coordinate 44// can sample cubemaps as if they were given in the usual OpenGL coordinate
45// system. 45// system.
46Texture* gfx_load_texture( 46Texture* gfx_texture_load(
47 RenderBackend* render_backend, const LoadTextureCmd* cmd) { 47 RenderBackend* render_backend, const LoadTextureCmd* cmd) {
48 assert(render_backend); 48 assert(render_backend);
49 assert(cmd); 49 assert(cmd);
50 assert(cmd->origin == TextureFromFile || cmd->origin == TextureFromMemory); 50 assert(cmd->origin == AssetFromFile || cmd->origin == AssetFromMemory);
51 assert(cmd->type == LoadTexture || cmd->type == LoadCubemap); 51 assert(cmd->type == LoadTexture || cmd->type == LoadCubemap);
52 52
53 int width, height, components, old_components; 53 int width, height, components, old_components;
54 unsigned char* pixels[6] = {0}; 54 unsigned char* pixels[6] = {0};
55 55
56 switch (cmd->origin) { 56 switch (cmd->origin) {
57 case TextureFromFile: 57 case AssetFromFile:
58 switch (cmd->type) { 58 switch (cmd->type) {
59 case LoadTexture: { 59 case LoadTexture: {
60 const char* filepath = mstring_cstr(&cmd->data.texture.filepath); 60 const char* filepath = mstring_cstr(&cmd->data.texture.filepath);
@@ -91,7 +91,7 @@ Texture* gfx_load_texture(
91 break; 91 break;
92 } 92 }
93 break; 93 break;
94 case TextureFromMemory: 94 case AssetFromMemory:
95 // TODO: Load textures from memory. 95 // TODO: Load textures from memory.
96 set_error("Loading textures from memory is not yet implemented"); 96 set_error("Loading textures from memory is not yet implemented");
97 return 0; 97 return 0;