Use better generic hashtable implement. for assets
This commit is contained in:
parent
c93e6ca37c
commit
8b0f2f93e3
@ -22,6 +22,61 @@
|
|||||||
#include "Dengine/OpenGL.h"
|
#include "Dengine/OpenGL.h"
|
||||||
#include "Dengine/Platform.h"
|
#include "Dengine/Platform.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
*********************************
|
||||||
|
* Hash Table Operations
|
||||||
|
*********************************
|
||||||
|
*/
|
||||||
|
INTERNAL HashTableEntry *getFreeHashSlot(HashTable *const table,
|
||||||
|
MemoryArena *arena,
|
||||||
|
const char *const key)
|
||||||
|
{
|
||||||
|
u32 hashIndex = common_getHashIndex(key, table->size);
|
||||||
|
HashTableEntry *result = &table->entries[hashIndex];
|
||||||
|
if (result->key)
|
||||||
|
{
|
||||||
|
while (result->next)
|
||||||
|
{
|
||||||
|
if (common_strcmp(result->key, key) == 0)
|
||||||
|
{
|
||||||
|
// TODO(doyle): Error hash item already exists
|
||||||
|
ASSERT(INVALID_CODE_PATH);
|
||||||
|
}
|
||||||
|
result = result->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
result->next = PLATFORM_MEM_ALLOC(arena, 1, HashTableEntry);
|
||||||
|
result = result->next;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if platform_mem_alloc failed(), otherwise always true if hash table
|
||||||
|
* entries initialised*/
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
// +1 Null terminator
|
||||||
|
i32 keyLen = common_strlen(key) + 1;
|
||||||
|
result->key = PLATFORM_MEM_ALLOC(arena, keyLen, char);
|
||||||
|
common_strncpy(result->key, key, keyLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL HashTableEntry *getEntryFromHash(HashTable *const table,
|
||||||
|
const char *const key)
|
||||||
|
{
|
||||||
|
u32 hashIndex = common_getHashIndex(key, table->size);
|
||||||
|
HashTableEntry *result = &table->entries[hashIndex];
|
||||||
|
if (result->key)
|
||||||
|
{
|
||||||
|
while (result && common_strcmp(result->key, key) != 0)
|
||||||
|
result = result->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*********************************
|
*********************************
|
||||||
* XML Operations
|
* XML Operations
|
||||||
@ -308,7 +363,8 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena *arena,
|
|||||||
if (common_strcmp(node->attribute.name, "imagePath") == 0)
|
if (common_strcmp(node->attribute.name, "imagePath") == 0)
|
||||||
{
|
{
|
||||||
char *imageName = atlasXmlNode->attribute.value;
|
char *imageName = atlasXmlNode->attribute.value;
|
||||||
atlasEntry = asset_makeTexAtlas(assetManager, arena, imageName);
|
atlasEntry =
|
||||||
|
asset_getFreeTexAtlasSlot(assetManager, arena, imageName);
|
||||||
|
|
||||||
char *dataDir = "data/textures/WorldTraveller/";
|
char *dataDir = "data/textures/WorldTraveller/";
|
||||||
char imagePath[512] = {0};
|
char imagePath[512] = {0};
|
||||||
@ -574,29 +630,21 @@ INTERNAL AtlasSubTexture *getFreeAtlasSubTexSlot(TexAtlas *atlas,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERNAL Animation *getFreeAnimationSlot(Animation *table, u32 tableSize,
|
INTERNAL Animation *getFreeAnimationSlot(AssetManager *assetManager,
|
||||||
MemoryArena *arena, char *key)
|
MemoryArena *arena, char *key)
|
||||||
{
|
{
|
||||||
u32 hashIndex = common_getHashIndex(key, tableSize);
|
HashTableEntry *entry = getFreeHashSlot(&assetManager->anims, arena, key);
|
||||||
Animation *result = &table[hashIndex];
|
|
||||||
if (result->key)
|
if (entry)
|
||||||
{
|
{
|
||||||
while (result->next)
|
entry->data = PLATFORM_MEM_ALLOC(arena, 1, Animation);
|
||||||
{
|
Animation *result = CAST(Animation *) entry->data;
|
||||||
if (common_strcmp(result->key, key) == 0)
|
return result;
|
||||||
{
|
}
|
||||||
// TODO(doyle): Error correction whereby if a tex atlas already
|
else
|
||||||
// exists
|
{
|
||||||
ASSERT(INVALID_CODE_PATH);
|
return NULL;
|
||||||
}
|
|
||||||
result = result->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
result->next = PLATFORM_MEM_ALLOC(arena, 1, Animation);
|
|
||||||
result = result->next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect asset_getSubTexRect(TexAtlas *atlas, char *key)
|
Rect asset_getSubTexRect(TexAtlas *atlas, char *key)
|
||||||
@ -623,73 +671,71 @@ AudioVorbis *asset_getVorbis(AssetManager *assetManager,
|
|||||||
|
|
||||||
Texture *asset_getTex(AssetManager *const assetManager, const char *const key)
|
Texture *asset_getTex(AssetManager *const assetManager, const char *const key)
|
||||||
{
|
{
|
||||||
u32 hashIndex = common_getHashIndex(key, ARRAY_COUNT(assetManager->textures));
|
HashTableEntry *entry = getEntryFromHash(&assetManager->textures, key);
|
||||||
Texture *result = &assetManager->textures[hashIndex];
|
|
||||||
if (result->key)
|
Texture *result = NULL;
|
||||||
{
|
if (entry) result = CAST(Texture *)entry->data;
|
||||||
while (result && common_strcmp(result->key, key) != 0)
|
|
||||||
result = result->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TexAtlas *asset_makeTexAtlas(AssetManager *const assetManager,
|
Texture *asset_getFreeTexSlot(AssetManager *const assetManager,
|
||||||
MemoryArena *arena, const char *const key)
|
MemoryArena *const arena, const char *const key)
|
||||||
{
|
{
|
||||||
u32 hashIndex = common_getHashIndex(key, ARRAY_COUNT(assetManager->texAtlas));
|
|
||||||
TexAtlas *result = &assetManager->texAtlas[hashIndex];
|
HashTableEntry *const entry =
|
||||||
if (result->key)
|
getFreeHashSlot(&assetManager->textures, arena, key);
|
||||||
|
|
||||||
|
if (entry)
|
||||||
{
|
{
|
||||||
while (result->next)
|
entry->data = PLATFORM_MEM_ALLOC(arena, 1, Texture);
|
||||||
{
|
Texture *result = CAST(Texture *) entry->data;
|
||||||
if (common_strcmp(result->key, key) == 0)
|
return result;
|
||||||
{
|
|
||||||
// TODO(doyle): Error correction whereby if a tex atlas already
|
|
||||||
// exists
|
|
||||||
ASSERT(INVALID_CODE_PATH);
|
|
||||||
}
|
|
||||||
result = result->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
result->next = PLATFORM_MEM_ALLOC(arena, 1, TexAtlas);
|
|
||||||
result = result->next;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
TexAtlas *asset_getFreeTexAtlasSlot(AssetManager *const assetManager,
|
||||||
|
MemoryArena *arena, const char *const key)
|
||||||
|
{
|
||||||
|
|
||||||
|
HashTableEntry *const entry =
|
||||||
|
getFreeHashSlot(&assetManager->texAtlas, arena, key);
|
||||||
|
|
||||||
|
if (entry)
|
||||||
|
{
|
||||||
|
entry->data = PLATFORM_MEM_ALLOC(arena, 1, TexAtlas);
|
||||||
|
TexAtlas *result = CAST(TexAtlas *) entry->data;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TexAtlas *asset_getTexAtlas(AssetManager *const assetManager,
|
TexAtlas *asset_getTexAtlas(AssetManager *const assetManager,
|
||||||
const char *const key)
|
const char *const key)
|
||||||
{
|
{
|
||||||
u32 hashIndex = common_getHashIndex(key, ARRAY_COUNT(assetManager->texAtlas));
|
|
||||||
TexAtlas *result = &assetManager->texAtlas[hashIndex];
|
HashTableEntry *entry = getEntryFromHash(&assetManager->texAtlas, key);
|
||||||
if (result->key)
|
|
||||||
{
|
TexAtlas *result = NULL;
|
||||||
while (result && common_strcmp(result->key, key) != 0)
|
if (entry) result = CAST(TexAtlas *)entry->data;
|
||||||
result = result->next;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Animation *asset_getAnim(AssetManager *assetManager, char *key)
|
Animation *asset_getAnim(AssetManager *const assetManager,
|
||||||
|
const char *const key)
|
||||||
{
|
{
|
||||||
u32 hashIndex = common_getHashIndex(key, ARRAY_COUNT(assetManager->anims));
|
HashTableEntry *entry = getEntryFromHash(&assetManager->anims, key);
|
||||||
Animation *result = &assetManager->anims[hashIndex];
|
|
||||||
if (result->key)
|
Animation *result = NULL;
|
||||||
{
|
if (entry) result = CAST(Animation *)entry->data;
|
||||||
while (result && common_strcmp(result->key, key) != 0)
|
|
||||||
result = result->next;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -726,33 +772,6 @@ const i32 asset_loadVorbis(AssetManager *assetManager, MemoryArena *arena,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(doyle): Revise passing in the assetmanager
|
|
||||||
Texture *asset_getAndAllocFreeTexSlot(AssetManager *assetManager,
|
|
||||||
MemoryArena *arena, const char *const key)
|
|
||||||
{
|
|
||||||
i32 texListSize = ARRAY_COUNT(assetManager->textures);
|
|
||||||
u32 hashIndex = common_getHashIndex(key, ARRAY_COUNT(assetManager->textures));
|
|
||||||
Texture *result = &assetManager->textures[hashIndex];
|
|
||||||
if (result->key)
|
|
||||||
{
|
|
||||||
while (result->next)
|
|
||||||
{
|
|
||||||
if (common_strcmp(result->key, key) == 0)
|
|
||||||
{
|
|
||||||
// TODO(doyle): Error correction whereby if a tex atlas already
|
|
||||||
// exists
|
|
||||||
ASSERT(INVALID_CODE_PATH);
|
|
||||||
}
|
|
||||||
result = result->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
result->next = PLATFORM_MEM_ALLOC(arena, 1, Texture);
|
|
||||||
result = result->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const i32 asset_loadTextureImage(AssetManager *assetManager, MemoryArena *arena,
|
const i32 asset_loadTextureImage(AssetManager *assetManager, MemoryArena *arena,
|
||||||
const char *const path, const char *const key)
|
const char *const path, const char *const key)
|
||||||
{
|
{
|
||||||
@ -777,7 +796,7 @@ const i32 asset_loadTextureImage(AssetManager *assetManager, MemoryArena *arena,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture *tex = asset_getAndAllocFreeTexSlot(assetManager, arena, key);
|
Texture *tex = asset_getFreeTexSlot(assetManager, arena, key);
|
||||||
*tex = texture_gen(CAST(GLuint)(imgWidth), CAST(GLuint)(imgHeight),
|
*tex = texture_gen(CAST(GLuint)(imgWidth), CAST(GLuint)(imgHeight),
|
||||||
CAST(GLint)(bytesPerPixel), image);
|
CAST(GLint)(bytesPerPixel), image);
|
||||||
|
|
||||||
@ -1015,7 +1034,8 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
|
|||||||
// align the baselines up so we don't need to do baseline adjusting at
|
// align the baselines up so we don't need to do baseline adjusting at
|
||||||
// render?
|
// render?
|
||||||
char charToEncode = CAST(char)codepointRange.x;
|
char charToEncode = CAST(char)codepointRange.x;
|
||||||
TexAtlas *fontAtlas = asset_makeTexAtlas(assetManager, arena, "font");
|
TexAtlas *fontAtlas =
|
||||||
|
asset_getFreeTexAtlasSlot(assetManager, arena, "font");
|
||||||
for (i32 row = 0; row < MAX_TEXTURE_SIZE; row++)
|
for (i32 row = 0; row < MAX_TEXTURE_SIZE; row++)
|
||||||
{
|
{
|
||||||
u32 *destRow = fontBitmap + (row * MAX_TEXTURE_SIZE);
|
u32 *destRow = fontBitmap + (row * MAX_TEXTURE_SIZE);
|
||||||
@ -1095,9 +1115,9 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture tex = texture_gen(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4,
|
Texture *tex = asset_getFreeTexSlot(assetManager, arena, "font");
|
||||||
CAST(u8 *) fontBitmap);
|
*tex = texture_gen(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4,
|
||||||
assetManager->textures[texlist_font] = tex;
|
CAST(u8 *) fontBitmap);
|
||||||
|
|
||||||
#ifdef WT_RENDER_FONT_FILE
|
#ifdef WT_RENDER_FONT_FILE
|
||||||
/* save out a 4 channel image */
|
/* save out a 4 channel image */
|
||||||
@ -1106,7 +1126,7 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
|
|||||||
#endif
|
#endif
|
||||||
PLATFORM_MEM_FREE(arena, fontBitmap, bitmapSize);
|
PLATFORM_MEM_FREE(arena, fontBitmap, bitmapSize);
|
||||||
|
|
||||||
fontAtlas->tex = &assetManager->textures[texlist_font];
|
fontAtlas->tex = tex;
|
||||||
font->atlas = fontAtlas;
|
font->atlas = fontAtlas;
|
||||||
|
|
||||||
// NOTE(doyle): Formula derived from STB Font
|
// NOTE(doyle): Formula derived from STB Font
|
||||||
@ -1130,17 +1150,16 @@ void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena,
|
|||||||
char *animName, TexAtlas *atlas, char **subTextureNames,
|
char *animName, TexAtlas *atlas, char **subTextureNames,
|
||||||
i32 numSubTextures, f32 frameDuration)
|
i32 numSubTextures, f32 frameDuration)
|
||||||
{
|
{
|
||||||
Animation *anim = getFreeAnimationSlot(
|
Animation *anim = getFreeAnimationSlot(assetManager, arena, animName);
|
||||||
assetManager->anims, ARRAY_COUNT(assetManager->anims), arena, animName);
|
|
||||||
|
/* Use same animation ptr for name from entry key */
|
||||||
|
HashTableEntry *entry = getEntryFromHash(&assetManager->anims, animName);
|
||||||
|
anim->name = entry->key;
|
||||||
|
|
||||||
anim->atlas = atlas;
|
anim->atlas = atlas;
|
||||||
anim->frameDuration = frameDuration;
|
anim->frameDuration = frameDuration;
|
||||||
anim->numFrames = numSubTextures;
|
anim->numFrames = numSubTextures;
|
||||||
|
|
||||||
// NOTE(doyle): +1 for the null terminator
|
|
||||||
anim->name = PLATFORM_MEM_ALLOC(arena, common_strlen(animName) + 1, char);
|
|
||||||
common_strncpy(anim->name, animName, common_strlen(animName));
|
|
||||||
|
|
||||||
anim->frameList = PLATFORM_MEM_ALLOC(arena, numSubTextures, char*);
|
anim->frameList = PLATFORM_MEM_ALLOC(arena, numSubTextures, char*);
|
||||||
for (i32 i = 0; i < numSubTextures; i++)
|
for (i32 i = 0; i < numSubTextures; i++)
|
||||||
{
|
{
|
||||||
|
@ -116,12 +116,25 @@ INTERNAL void assetInit(GameState *state)
|
|||||||
AssetManager *assetManager = &state->assetManager;
|
AssetManager *assetManager = &state->assetManager;
|
||||||
MemoryArena *arena = &state->arena;
|
MemoryArena *arena = &state->arena;
|
||||||
|
|
||||||
/* Create empty 1x1 4bpp black texture */
|
i32 texAtlasEntries = 8;
|
||||||
u32 bitmap = (0xFF << 24) | (0xFF << 16) | (0xFF << 8) | (0xFF << 0);
|
assetManager->texAtlas.size = texAtlasEntries;
|
||||||
Texture *tex = asset_getAndAllocFreeTexSlot(assetManager, arena, "nullTex");
|
assetManager->texAtlas.entries =
|
||||||
*tex = texture_gen(1, 1, 4, CAST(u8 *)(&bitmap));
|
PLATFORM_MEM_ALLOC(arena, texAtlasEntries, HashTableEntry);
|
||||||
|
|
||||||
//assetManager->texAtlas.type = hashtabletype_textureAtlas;
|
i32 texEntries = 32;
|
||||||
|
assetManager->textures.size = texEntries;
|
||||||
|
assetManager->textures.entries =
|
||||||
|
PLATFORM_MEM_ALLOC(arena, texEntries, HashTableEntry);
|
||||||
|
|
||||||
|
i32 animEntries = 1024;
|
||||||
|
assetManager->anims.size = animEntries;
|
||||||
|
assetManager->anims.entries =
|
||||||
|
PLATFORM_MEM_ALLOC(arena, animEntries, HashTableEntry);
|
||||||
|
|
||||||
|
/* Create empty 1x1 4bpp black texture */
|
||||||
|
u32 bitmap = (0xFF << 24) | (0xFF << 16) | (0xFF << 8) | (0xFF << 0);
|
||||||
|
Texture *tex = asset_getFreeTexSlot(assetManager, arena, "nullTex");
|
||||||
|
*tex = texture_gen(1, 1, 4, CAST(u8 *)(&bitmap));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*********************************
|
*********************************
|
||||||
|
@ -9,15 +9,6 @@
|
|||||||
typedef struct MemoryArena MemoryArena;
|
typedef struct MemoryArena MemoryArena;
|
||||||
typedef struct PlatformFileRead PlatformFileRead;
|
typedef struct PlatformFileRead PlatformFileRead;
|
||||||
|
|
||||||
enum HashTableType
|
|
||||||
{
|
|
||||||
hashtabletype_unknown,
|
|
||||||
hashtabletype_texture,
|
|
||||||
hashtabletype_textureAtlas,
|
|
||||||
hashtabletype_animation,
|
|
||||||
hashtabletype_count,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct HashTableEntry
|
typedef struct HashTableEntry
|
||||||
{
|
{
|
||||||
void *data;
|
void *data;
|
||||||
@ -30,15 +21,14 @@ typedef struct HashTable
|
|||||||
{
|
{
|
||||||
HashTableEntry *entries;
|
HashTableEntry *entries;
|
||||||
i32 size;
|
i32 size;
|
||||||
enum HashTableType type;
|
|
||||||
} HashTable;
|
} HashTable;
|
||||||
|
|
||||||
typedef struct AssetManager
|
typedef struct AssetManager
|
||||||
{
|
{
|
||||||
/* Hash Tables */
|
/* Hash Tables */
|
||||||
TexAtlas texAtlas[8];
|
HashTable texAtlas;
|
||||||
Texture textures[32];
|
HashTable textures;
|
||||||
Animation anims[1024];
|
HashTable anims;
|
||||||
|
|
||||||
/* Primitive Array */
|
/* Primitive Array */
|
||||||
Shader shaders[32];
|
Shader shaders[32];
|
||||||
@ -55,13 +45,12 @@ typedef struct AssetManager
|
|||||||
*/
|
*/
|
||||||
Rect asset_getSubTexRect(TexAtlas *atlas, char *key);
|
Rect asset_getSubTexRect(TexAtlas *atlas, char *key);
|
||||||
Texture *asset_getTex(AssetManager *const assetManager, const char *const key);
|
Texture *asset_getTex(AssetManager *const assetManager, const char *const key);
|
||||||
TexAtlas *asset_makeTexAtlas(AssetManager *const assetManager,
|
TexAtlas *asset_getFreeTexAtlasSlot(AssetManager *const assetManager,
|
||||||
MemoryArena *arena, const char *const key);
|
MemoryArena *arena, const char *const key);
|
||||||
TexAtlas *asset_getTexAtlas(AssetManager *const assetManager,
|
TexAtlas *asset_getTexAtlas(AssetManager *const assetManager,
|
||||||
const char *const key);
|
const char *const key);
|
||||||
Texture *asset_getAndAllocFreeTexSlot(AssetManager *assetManager,
|
Texture *asset_getFreeTexSlot(AssetManager *const assetManager,
|
||||||
MemoryArena *arena,
|
MemoryArena *const arena, const char *const key);
|
||||||
const char *const key);
|
|
||||||
const i32 asset_loadTextureImage(AssetManager *assetManager, MemoryArena *arena,
|
const i32 asset_loadTextureImage(AssetManager *assetManager, MemoryArena *arena,
|
||||||
const char *const path,
|
const char *const path,
|
||||||
const char *const key);
|
const char *const key);
|
||||||
@ -74,7 +63,8 @@ const i32 asset_loadTextureImage(AssetManager *assetManager, MemoryArena *arena,
|
|||||||
void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena,
|
void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena,
|
||||||
char *animName, TexAtlas *atlas, char **subTextureNames,
|
char *animName, TexAtlas *atlas, char **subTextureNames,
|
||||||
i32 numSubTextures, f32 frameDuration);
|
i32 numSubTextures, f32 frameDuration);
|
||||||
Animation *asset_getAnim(AssetManager *assetManager, char *key);
|
Animation *asset_getAnim(AssetManager *const assetManager,
|
||||||
|
const char *const key);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*********************************
|
*********************************
|
||||||
|
@ -111,8 +111,6 @@ typedef struct Animation
|
|||||||
|
|
||||||
i32 numFrames;
|
i32 numFrames;
|
||||||
f32 frameDuration;
|
f32 frameDuration;
|
||||||
|
|
||||||
struct Animation *next;
|
|
||||||
} Animation;
|
} Animation;
|
||||||
|
|
||||||
// TODO(doyle): We only use the offset and advance metric at the moment, remove?
|
// TODO(doyle): We only use the offset and advance metric at the moment, remove?
|
||||||
|
Loading…
Reference in New Issue
Block a user