From dd9fcd52cae157602795a2de84aa0b3227bdaa60 Mon Sep 17 00:00:00 2001
From: Dominic Masters <dominic@domsplace.com>
Date: Wed, 3 Nov 2021 10:48:03 -0700
Subject: [PATCH] Finished asset manager.

---
 CMakeLists.txt                              |  15 ++
 src/file/assetmanager.c                     | 251 ++++++++++++++++++++
 src/file/assetmanager.h                     | 180 ++++++++++++++
 src/game/poker/game.c                       |  15 --
 src/game/sandbox/game.c                     |  59 +++++
 src/game/sandbox/{sandboxscene.h => game.h} |  16 +-
 src/game/sandbox/sandboxscene.c             |  37 ---
 src/vn/vncharacter.c                        |   6 +-
 8 files changed, 519 insertions(+), 60 deletions(-)
 create mode 100644 src/file/assetmanager.c
 create mode 100644 src/file/assetmanager.h
 create mode 100644 src/game/sandbox/game.c
 rename src/game/sandbox/{sandboxscene.h => game.h} (72%)
 delete mode 100644 src/game/sandbox/sandboxscene.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5e494012..420794a0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -118,6 +118,21 @@ elseif(TARGET_TYPE STREQUAL game)
 
       locale_en
     )
+  elseif(TARGET_GAME STREQUAL sandbox)
+    add_compile_definitions(
+      GAME_NAME="Sandbox"
+      GAME_FILE="sandbox/game.h"
+      GAME_TYPE=sandboxgame_t
+      GAME_INIT=sandboxGameInit
+      GAME_UPDATE=sandboxGameUpdate
+      GAME_DISPOSE=sandboxGameDispose
+      GAME_VERSION=1.0
+    )
+    tool_assets(
+      shader_textured
+      font_opensans
+      texture_test
+    )
   endif()
 
   # Common Game Dependencies.
diff --git a/src/file/assetmanager.c b/src/file/assetmanager.c
new file mode 100644
index 00000000..64ad62da
--- /dev/null
+++ b/src/file/assetmanager.c
@@ -0,0 +1,251 @@
+/**
+ * Copyright (c) 2021 Dominic Masters
+ * 
+ * This software is released under the MIT License.
+ * https://opensource.org/licenses/MIT
+ */
+
+#include "assetmanager.h"
+
+assetmanagerloaderdefinition_t ASSET_MANAGER_LOADERS[] = {
+  {
+    &_assetManagerLoaderTextureAsync,
+    &_assetManagerLoaderTextureSync
+  },
+  {
+    &_assetManagerLoaderFontAsync,
+    &_assetManagerLoaderFontSync
+  },
+  {
+    &_assetManagerLoaderShaderAsync,
+    &_assetManagerLoaderShaderSync
+  }
+};
+
+
+void assetManagerInit(assetmanager_t *manager) {
+  threadInit(&manager->thread, &_assetManagerThread);
+  manager->thread.user = manager;
+  manager->itemCount = 0;
+}
+
+void assetManagerStart(assetmanager_t *manager) {
+  threadStart(&manager->thread);
+}
+
+float assetManagerProgressGet(assetmanager_t *manager) {
+  float done;
+  uint8_t i;
+
+  done = 0.0f;
+
+  for(i = 0; i < manager->itemCount; i++) {
+    if(!assetManagerIsItemFinished(manager->items + i)) continue;
+    done++;
+  }
+
+  return done / ((float) manager->itemCount);
+}
+
+bool assetManagerIsItemFinished(assetmanageritem_t *item) {
+  // Sync done is always done
+  if(item->state == ASSET_MANAGER_STATE_SYNC_DONE) return true;
+  // Only check if ASYNC is done.
+  if(item->state != ASSET_MANAGER_STATE_ASYNC_DONE) return false;
+  // Does it need to still sync load?
+  if(ASSET_MANAGER_LOADERS[item->type].loadSync == NULL) return true;
+  return false;
+}
+
+
+// Thread Management
+int32_t _assetManagerThread(thread_t *thread) {
+  // TODO: Can I allow multiple threads to run?
+  uint8_t i;
+  assetmanager_t *manager;
+  assetmanageritem_t *item;
+  assetmanagerloaderdefinition_t *definition;
+  bool result;
+  manager = thread->user;
+
+  for(i = 0; i < manager->itemCount; i++) {
+    item = manager->items + i;
+    definition = ASSET_MANAGER_LOADERS + item->type;
+
+    // Only bother with ASYNC items
+    if(definition->loadAsync == NULL) continue;
+
+    // Are we already loading or already tried to load?
+    if(item->state != ASSET_MANAGER_STATE_PENDING) continue;
+
+    // Begin loading
+    item->state = ASSET_MANAGER_STATE_ASYNC_LOADING;
+    result = definition->loadAsync(item);
+
+    // Finish Loading
+    if(!result) {
+      item->state = ASSET_MANAGER_STATE_ASYNC_ERROR;
+    } else {
+      item->state = ASSET_MANAGER_STATE_ASYNC_DONE;
+    }
+  }
+
+  return 0;
+}
+
+void assetManagerUpdate(assetmanager_t *manager) {
+  uint8_t i;
+  assetmanageritem_t *item;
+  assetmanagerloaderdefinition_t *definition;
+  bool result;
+
+  for(i = 0; i < manager->itemCount; i++) {
+    item = manager->items + i;
+    definition = ASSET_MANAGER_LOADERS + item->type;
+
+    // If requires ASYNC loading, then confirm it has finished loading.
+    if(definition->loadAsync != NULL) {
+      if(item->state != ASSET_MANAGER_STATE_ASYNC_DONE) continue;
+    } else if(item->state != ASSET_MANAGER_STATE_PENDING) {
+      continue;
+    } else if(definition->loadSync == NULL) {
+      continue;
+    }
+    
+    // Begin sync loading
+    item->state = ASSET_MANAGER_STATE_SYNC_LOADING;
+    result = definition->loadSync(item);
+
+    // Finish loading
+    if(!result) {
+      item->state = ASSET_MANAGER_STATE_SYNC_ERROR;
+    } else {
+      item->state = ASSET_MANAGER_STATE_SYNC_DONE;
+    }
+  }
+}
+
+assetmanageritem_t * assetManagerItemAdd(assetmanager_t *manager) {
+  assetmanageritem_t *item = manager->items + manager->itemCount++;
+  item->state = ASSET_MANAGER_STATE_PENDING;
+  return item;
+}
+
+
+// Texture
+assetmanageritem_t * assetManagerLoadTexture(
+  assetmanager_t *manager, texture_t *texture, char *fileName
+) {
+  assetmanageritem_t *item = assetManagerItemAdd(manager);
+  
+  item->type = ASSET_MANAGER_TYPE_TEXTURE;
+  item->data.texture.fileName = fileName;
+  item->data.texture.texture = texture;
+  
+  return item;
+}
+
+bool _assetManagerLoaderTextureAsync(assetmanageritem_t *item) {
+  assetbuffer_t *buffer;
+  int channels;
+  stbi_io_callbacks OPENGL_STBI_CALLBACKS;
+
+  buffer = assetBufferOpen(item->data.texture.fileName);
+  if(buffer == NULL) return false;
+
+  // Setup the interface for STBI
+  OPENGL_STBI_CALLBACKS.read = &assetBufferRead;
+  OPENGL_STBI_CALLBACKS.skip = &assetBufferSkip;
+  OPENGL_STBI_CALLBACKS.eof = &assetBufferEnd; 
+
+  // Buffer the image
+  channels = 0;
+  item->data.texture.data = (pixel_t *)stbi_load_from_callbacks(
+    &OPENGL_STBI_CALLBACKS, buffer,
+    &item->data.texture.width, &item->data.texture.height,
+    &channels, STBI_rgb_alpha
+  );
+
+  // Close the buffer
+  assetBufferClose(buffer);
+  if(item->data.texture.data == NULL) return false;
+  return true;
+}
+
+bool _assetManagerLoaderTextureSync(assetmanageritem_t *item) {
+  // Turn into a texture.
+  textureInit(
+    item->data.texture.texture,
+    item->data.texture.width,
+    item->data.texture.height,
+    item->data.texture.data
+  );
+
+  // Cleanup
+  stbi_image_free(item->data.texture.data);
+  return true;
+}
+
+
+// Font
+assetmanageritem_t * assetManagerLoadFont(
+  assetmanager_t *manager, font_t *font, char *fileName
+) {
+  assetmanageritem_t *item = assetManagerItemAdd(manager);
+  
+  item->type = ASSET_MANAGER_TYPE_FONT;
+  item->data.font.fileName = fileName;
+  item->data.font.font = font;
+
+  return item;
+}
+
+bool _assetManagerLoaderFontAsync(assetmanageritem_t *item) {
+  item->data.font.data = assetStringLoad(item->data.font.fileName);
+  return item->data.font.data != NULL;
+}
+
+bool _assetManagerLoaderFontSync(assetmanageritem_t *item) {
+  fontInit(item->data.font.font, item->data.font.data);
+  free(item->data.font.data);
+  return true;
+}
+
+
+// Shader
+assetmanageritem_t * assetManagerShaderLoad(
+  assetmanager_t *manager, shader_t *shader, char *fileVert, char *fileFrag
+) {
+  assetmanageritem_t *item = assetManagerItemAdd(manager);
+
+  item->type = ASSET_MANAGER_TYPE_SHADER;
+  item->data.shader.shader = shader;
+  item->data.shader.fileVert = fileVert;
+  item->data.shader.fileFrag = fileFrag;
+
+  return item;
+}
+
+bool _assetManagerLoaderShaderAsync(assetmanageritem_t *item) {
+  item->data.shader.dataVert = assetStringLoad(item->data.shader.fileVert);
+  if(item->data.shader.dataVert == NULL) return false;
+
+  item->data.shader.dataFrag = assetStringLoad(item->data.shader.fileFrag);
+  if(item->data.shader.dataFrag == NULL) {
+    free(item->data.shader.fileVert);
+    return false;
+  }
+  
+  return true;
+}
+
+bool _assetManagerLoaderShaderSync(assetmanageritem_t *item) {
+  shaderInit(
+    item->data.shader.shader,
+    item->data.shader.dataVert,
+    item->data.shader.dataFrag
+  );
+  free(item->data.shader.dataFrag);
+  free(item->data.shader.dataVert);
+  return true;
+}
\ No newline at end of file
diff --git a/src/file/assetmanager.h b/src/file/assetmanager.h
new file mode 100644
index 00000000..b409794c
--- /dev/null
+++ b/src/file/assetmanager.h
@@ -0,0 +1,180 @@
+/**
+ * Copyright (c) 2021 Dominic Masters
+ * 
+ * This software is released under the MIT License.
+ * https://opensource.org/licenses/MIT
+ */
+
+#pragma once
+#include "asset.h"
+#include "../util/flags.h"
+#include "../engine/thread.h"
+#include "../display/shader.h"
+#include "../display/texture.h"
+#include "../display/font.h"
+#include "../script/scripter.h"
+#include "xml.h"
+
+#define ASSET_MANAGER_ITEMS_MAX 64
+
+#define ASSET_MANAGER_STATE_PENDING 0x00
+#define ASSET_MANAGER_STATE_ASYNC_LOADING 0x01
+#define ASSET_MANAGER_STATE_ASYNC_ERROR 0x02
+#define ASSET_MANAGER_STATE_ASYNC_DONE 0x03
+#define ASSET_MANAGER_STATE_SYNC_LOADING 0x04
+#define ASSET_MANAGER_STATE_SYNC_ERROR 0x05
+#define ASSET_MANAGER_STATE_SYNC_DONE 0x06
+
+#define ASSET_MANAGER_TYPE_TEXTURE 0x00
+#define ASSET_MANAGER_TYPE_FONT 0x01
+#define ASSET_MANAGER_TYPE_SHADER 0x02
+
+// Types
+typedef struct {
+  texture_t *texture;
+  char *fileName;
+  int32_t width, height;
+  pixel_t *data;
+} assetmanagertexture_t;
+
+typedef struct {
+  shader_t *shader;
+  char *fileVert;
+  char *fileFrag;
+  char *dataVert;
+  char *dataFrag;
+} assetmanagershader_t;
+
+typedef struct {
+  font_t *font;
+  char *fileName;
+  char *data;
+} assetmanagerfont_t;
+
+// Item
+typedef union {
+  assetmanagertexture_t texture;
+  assetmanagershader_t shader;
+  assetmanagerfont_t font;
+} assetmanagerassetdata_t;
+
+typedef struct {
+  uint8_t type;
+  uint8_t state;
+  assetmanagerassetdata_t data;
+} assetmanageritem_t;
+
+// Loader
+typedef bool assetmanagerloader_t(assetmanageritem_t *item);
+
+typedef struct {
+  assetmanagerloader_t *loadAsync;
+  assetmanagerloader_t *loadSync;
+} assetmanagerloaderdefinition_t;
+
+// Manager
+typedef struct {
+  thread_t thread;
+
+  assetmanageritem_t items[ASSET_MANAGER_ITEMS_MAX];
+  uint8_t itemCount;
+} assetmanager_t;
+
+extern assetmanagerloaderdefinition_t ASSET_MANAGER_LOADERS[];
+
+/**
+ * Initialize the asset manager
+ * 
+ * @param manager Manager to initialize.
+ */
+void assetManagerInit(assetmanager_t *manager);
+
+/**
+ * Begin asynchronously loading all of the assets
+ * 
+ * @param manager Manager to begin async loading.
+ */
+void assetManagerStart(assetmanager_t *manager);
+
+/**
+ * Synchronously tick the asset manager. Some assets require some form of sync
+ * operations, such as buffering to the GPU, so make sure this is called at a
+ * good time for that task, such as right at the end of a frame.
+ * 
+ * @param manager Manager to synchronously tick.
+ */
+void assetManagerUpdate(assetmanager_t *manager);
+
+/**
+ * Returns whether or not the given item is finished.
+ * 
+ * @param item Item to check state of.
+ * @return True if finished, otherwise false.
+ */
+bool assetManagerIsItemFinished(assetmanageritem_t *item);
+
+/**
+ * Gets the progress of the asset manager as a representation of 0-1 as a % that
+ * has loaded.
+ * 
+ * @param manager Manager to check the state of.
+ * @return The progress percent as a representation of 0-1
+ */
+float assetManagerProgressGet(assetmanager_t *manager);
+
+/** Private Thread that is executed asynchronously */
+int32_t _assetManagerThread(thread_t *thread);
+
+/**
+ * Private method, simply adds an item to the manager and resets the state.
+ * 
+ * @param manager Manager to add to.
+ * @return The added and reset item.
+ */
+assetmanageritem_t * assetManagerItemAdd(assetmanager_t *manager);
+
+/**
+ * Queue a texture load onto the asset manager buffer.
+ * 
+ * @param manager Manager to queue on to.
+ * @param texture Texture to push the result in to.
+ * @param fileName Texture filename to load.
+ * @return A pointer to the asset manager item for tracking.
+ */
+assetmanageritem_t * assetManagerLoadTexture(
+  assetmanager_t *manager, texture_t *texture, char *fileName
+);
+
+bool _assetManagerLoaderTextureAsync(assetmanageritem_t *item);
+bool _assetManagerLoaderTextureSync(assetmanageritem_t *item);
+
+/**
+ * Queue a font load onto the asset manager buffer.
+ * 
+ * @param manager Manager to queue on to.
+ * @param font Font to push the result in to.
+ * @param fileName Filename of the asset to load.
+ * @return A pointer to the asset manager item for tracking.
+ */
+assetmanageritem_t * assetManagerLoadFont(
+  assetmanager_t *manager, font_t *font, char *fileName
+);
+
+bool _assetManagerLoaderFontAsync(assetmanageritem_t *item);
+bool _assetManagerLoaderFontSync(assetmanageritem_t *item);
+
+/**
+ * Queues a shader load onto the asset manager buffer.
+ * 
+ * @param manager Manager to queue on to.
+ * @param shader Shader to push the result in to.
+ * @param fileVert Vertex file in question to load.
+ * @param fileFrag Fragment file in question to load.
+ * @return A pointer to the asset manager item for tracking.
+ */
+assetmanageritem_t * assetManagerShaderLoad(
+  assetmanager_t *manager, shader_t *shader, char *fileVert, char *fileFrag
+);
+
+bool _assetManagerLoaderShaderAsync(assetmanageritem_t *item);
+bool _assetManagerLoaderShaderSync(assetmanageritem_t *item);
\ No newline at end of file
diff --git a/src/game/poker/game.c b/src/game/poker/game.c
index 9a672d77..5a5fa963 100644
--- a/src/game/poker/game.c
+++ b/src/game/poker/game.c
@@ -7,22 +7,7 @@
 
 #include "game.h"
 
-thread_t thread;
-
-int32_t threadTest(thread_t *thread) {
-  printf("Thread?\n");
-  threadSleep(2);
-  printf("THREAD2!?!?\n");
-  return 0;
-}
-
 bool pokerGameInit(pokergame_t *game) {
-  threadInit(&thread, &threadTest);
-  threadStart(&thread);
-  threadJoin(&thread);
-  printf("Done?\n");
-
-
   // Load the Assets.
   pokerGameAssetsInit(&game->assets);
 
diff --git a/src/game/sandbox/game.c b/src/game/sandbox/game.c
new file mode 100644
index 00000000..e9f77255
--- /dev/null
+++ b/src/game/sandbox/game.c
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2021 Dominic Masters
+ * 
+ * This software is released under the MIT License.
+ * https://opensource.org/licenses/MIT
+ */
+
+#include "game.h"
+
+
+bool sandboxGameInit(sandboxgame_t *game) {
+  // assetTextureLoad(&game->texture, "textures/test_texture.png");
+  quadInit(&game->quad, 0, 0,0,0,0, 500,500,1,1);
+
+  game->texture.width = -1;
+  assetManagerInit(&game->manager);
+  
+  assetManagerLoadTexture(&game->manager, &game->texture, "textures/test_texture.png");
+  assetManagerLoadFont(&game->manager, &game->font, "fonts/opensans/OpenSans-Regular.ttf");
+  assetManagerShaderLoad(&game->manager, &game->shader, 
+    "shaders/textured.vert",
+    "shaders/textured.frag"
+  );
+  
+  assetManagerStart(&game->manager);
+
+  return true;
+}
+
+void sandboxGameUpdate(sandboxgame_t *game) {
+  camera_t camera;
+  float n = assetManagerProgressGet(&game->manager);
+  if(n < 1.0f) {
+    assetManagerUpdate(&game->manager);
+    printf("Loading %.2f\n", n);
+    return;
+  }
+
+  cameraOrtho(&camera,
+    0, game->engine.render.width,
+    game->engine.render.height, 0,
+    0.01f, 1000.0f
+  );
+  cameraLookAt(&camera, 0,0,10, 0,0,0);
+  
+  shaderUse(&game->shader);
+  shaderUseCamera(&game->shader, &camera);
+
+
+  if(game->texture.width != -1) {
+    shaderUseTexture(&game->shader, &game->texture);
+    shaderUsePosition(&game->shader, 0,0,0, 0,0,0);
+    primitiveDraw(&game->quad, 0, -1);
+  }
+}
+
+void sandboxGameDispose(sandboxgame_t *game) {
+  // shaderDispose(&game->shader);
+}
\ No newline at end of file
diff --git a/src/game/sandbox/sandboxscene.h b/src/game/sandbox/game.h
similarity index 72%
rename from src/game/sandbox/sandboxscene.h
rename to src/game/sandbox/game.h
index c985317c..cd5162cb 100644
--- a/src/game/sandbox/sandboxscene.h
+++ b/src/game/sandbox/game.h
@@ -10,16 +10,22 @@
 #include "../../display/camera.h"
 #include "../../display/font.h"
 #include "../../display/shader.h"
+#include "../../display/primitive.h"
+#include "../../display/primitives/quad.h"
 #include "../../file/asset.h"
 #include "../../ui/label.h"
 #include "../../ui/breakpoint.h"
+#include "../../file/assetmanager.h"
 
 typedef struct {
   engine_t engine;
   shader_t shader;
-  texture_t texture;
   font_t font;
-} sandboxscene_t;
+  primitive_t quad;
+  assetmanager_t manager;
+  
+  texture_t texture;
+} sandboxgame_t;
 
 /**
  * Initialize the sandbox scene test game.
@@ -28,7 +34,7 @@ typedef struct {
  * @param engine Engine to use during init.
  * @return True if successful, otherwise false.
  */
-bool sandboxSceneInit(sandboxscene_t *game);
+bool sandboxGameInit(sandboxgame_t *game);
 
 /**
  * Update a sandbox scene.
@@ -36,11 +42,11 @@ bool sandboxSceneInit(sandboxscene_t *game);
  * @param game Game to update.
  * @param engine Engine to use when updating.
  */
-void sandboxSceneUpdate(sandboxscene_t *game);
+void sandboxGameUpdate(sandboxgame_t *game);
 
 /**
  * Dispose a previously created scene game.
  * 
  * @param game Game to dispose.
  */
-void sandboxSceneDispose(sandboxscene_t *game);
\ No newline at end of file
+void sandboxGameDispose(sandboxgame_t *game);
\ No newline at end of file
diff --git a/src/game/sandbox/sandboxscene.c b/src/game/sandbox/sandboxscene.c
deleted file mode 100644
index f36529df..00000000
--- a/src/game/sandbox/sandboxscene.c
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Copyright (c) 2021 Dominic Masters
- * 
- * This software is released under the MIT License.
- * https://opensource.org/licenses/MIT
- */
-
-#include "sandboxscene.h"
-
-bool sandboxSceneInit(sandboxscene_t *game) {
-  // Init Assets
-  assetShaderLoad(&game->shader,
-    "shared/shaders/textured.vert",
-    "shared/shaders/textured.frag"
-  );
-  assetFontLoad(&game->font, "shared/fonts/opensans/OpenSans-Regular.ttf");
-  assetTextureLoad(&game->texture, "shared/test_texture.png");
-  
-  return true;
-}
-
-void sandboxSceneUpdate(sandboxscene_t *game) {
-  camera_t camera;
-
-  shaderUse(&game->shader);
-  cameraOrtho(&camera,
-    0, game->engine.render.width,
-    game->engine.render.height, 0,
-    0.01f, 1000.0f
-  );
-  cameraLookAt(&camera, 0,0,10, 0,0,0);
-  shaderUseCamera(&game->shader, &camera);
-}
-
-void sandboxSceneDispose(sandboxscene_t *game) {
-  // shaderDispose(&game->shader);
-}
\ No newline at end of file
diff --git a/src/vn/vncharacter.c b/src/vn/vncharacter.c
index 54c4c8c9..feca9670 100644
--- a/src/vn/vncharacter.c
+++ b/src/vn/vncharacter.c
@@ -114,9 +114,9 @@ void vnCharacterUpdate(vncharacter_t *character, engine_t *engine) {
 
 uint8_t vnCharacterLayerAdd(vncharacter_t *character,
   uint8_t frames,
-  int32_t lx, int32_t ly,
-  int32_t x, int32_t y,
-  int32_t width, int32_t height
+  int16_t lx, int16_t ly,
+  int16_t x, int16_t y,
+  int16_t width, int16_t height
 ) {
   uint8_t i;
   vncharacterlayer_t *layer;