summaryrefslogtreecommitdiff
path: root/src/util/asset_manager.cc
blob: 2ad8ef9136af99a6766a4642c90619a2d541525c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// This file is part of the 64k demo project.
// It implements the generic asset retrieval logic with runtime caching.

#include "util/asset_manager.h"
#if defined(USE_TEST_ASSETS)
#include "generated/test_assets.h"
#else
#include "generated/assets.h"
#endif /* defined(USE_TEST_ASSETS) */

#include <vector>    // For potential dynamic allocation for procedural assets
#include <new>       // For placement new
#include <cstdlib>   // For free
#include <iostream>  // For std::cerr
#include <map>       // For kAssetManagerProcGenFuncMap
#include <string>    // For std::string in map

#include "procedural/generator.h" // For ProcGenFunc and procedural functions

// Map of procedural function names to their pointers (for runtime dispatch)
static const std::map<std::string, ProcGenFunc> kAssetManagerProcGenFuncMap = {
    {"gen_noise", procedural::gen_noise},
    {"gen_grid", procedural::gen_grid},
    {"make_periodic", procedural::make_periodic},
};

// These are defined in the generated assets_data.cc
#if defined(USE_TEST_ASSETS)
extern const AssetRecord g_assets[];
extern const size_t g_assets_count;
#else
extern const AssetRecord g_assets[];
extern const size_t g_assets_count;
#endif
// Array-based cache for assets.
// Initialized to all zeros (nullptr data, 0 size, false is_procedural)
// The size is derived from the generated ASSET_LAST_ID enum value.
static AssetRecord g_asset_cache[(size_t)AssetId::ASSET_LAST_ID] = {};

const uint8_t* GetAsset(AssetId asset_id, size_t* out_size) {
  uint16_t index = (uint16_t)asset_id;

  // Assert that ASSET_LAST_ID is not used for retrieval.
  // This ensures the size of the cache is correctly used and not accessed out of bounds.
  // If this assert fails, it means assets.txt has an ID larger than expected
  // or ASSET_LAST_ID is not correctly generated/updated.
  // This is a design decision: ASSET_LAST_ID is purely for sizing and range checking,
  // not for a valid asset retrieval itself.
  if (index >= (uint16_t)AssetId::ASSET_LAST_ID) {
    if (out_size)
      *out_size = 0;
    return nullptr; // Invalid asset_id
  }
  
  // Check cache first
  if (g_asset_cache[index].data != nullptr) {
    if (out_size)
      *out_size = g_asset_cache[index].size;
    return g_asset_cache[index].data;
  }

  // Not in cache, retrieve from static data (packed in binary) or generate procedurally
  if (index >= g_assets_count) {
    if (out_size)
      *out_size = 0;
    return nullptr; // Invalid asset_id or asset not in static packed data.
  }

  AssetRecord source_record = g_assets[index];
  AssetRecord cached_record = source_record;

  if (source_record.is_procedural) {
    // Dynamically generate the asset
    auto it = kAssetManagerProcGenFuncMap.find(source_record.proc_func_name_str);
    if (it == kAssetManagerProcGenFuncMap.end()) {
        std::cerr << "Error: Unknown procedural function at runtime: " << source_record.proc_func_name_str << std::endl;
        if (out_size) *out_size = 0; 
        return nullptr; // Procedural asset without a generation function.
    }
    ProcGenFunc proc_gen_func_ptr = it->second;

    // For this demo, assuming procedural textures are RGBA8 256x256 (for simplicity and bump mapping).
    // A more generic solution would pass dimensions in proc_params.
    int width = 256, height = 256;
    size_t data_size = width * height * 4; // RGBA8
    uint8_t* generated_data = new (std::nothrow) uint8_t[data_size];
    if (!generated_data) {
        std::cerr << "Error: Failed to allocate memory for procedural asset." << std::endl;
        if (out_size) *out_size = 0;
        return nullptr;
    }
    proc_gen_func_ptr(generated_data, width, height, source_record.proc_params, source_record.num_proc_params);
    
    cached_record.data = generated_data;
    cached_record.size = data_size;
    cached_record.is_procedural = true;
    // proc_gen_func, proc_params, num_proc_params already copied from source_record

  } else {
    // Static asset (copy from g_assets)
    cached_record.is_procedural = false;
  }
  
  // Store in cache for future use
  g_asset_cache[index] = cached_record;

  if (out_size)
    *out_size = cached_record.size;
  return cached_record.data;
}

void DropAsset(AssetId asset_id, const uint8_t* asset) {
  uint16_t index = (uint16_t)asset_id;
  if (index >= (uint16_t)AssetId::ASSET_LAST_ID) {
    return; // Invalid asset_id
  }

  // Check if the asset is in cache and is procedural, and if the pointer matches.
  // This prevents accidentally freeing static data or freeing twice.
  if (g_asset_cache[index].data == asset && g_asset_cache[index].is_procedural) {
    delete[] g_asset_cache[index].data;
    g_asset_cache[index] = {}; // Zero out the struct to force re-generation
  }
  // For static assets, no dynamic memory to free.
}