Load sprite sheet into new atlas structure
This commit is contained in:
parent
84a2c5e382
commit
b43754986f
@ -117,7 +117,7 @@ const i32 asset_loadTextureImage(AssetManager *assetManager,
|
|||||||
if (imgWidth != imgHeight)
|
if (imgWidth != imgHeight)
|
||||||
{
|
{
|
||||||
printf(
|
printf(
|
||||||
"worldTraveller_gameInit() warning: Sprite sheet is not square: "
|
"asset_loadTextureImage() warning: Sprite sheet is not square: "
|
||||||
"%dx%dpx\n", imgWidth, imgHeight);
|
"%dx%dpx\n", imgWidth, imgHeight);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
61
src/Common.c
61
src/Common.c
@ -21,6 +21,16 @@ i32 common_strcmp(const char *a, const char *b)
|
|||||||
return ((*a < *b) ? -1 : 1);
|
return ((*a < *b) ? -1 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void common_strncat(char *dest, const char *src, i32 numChars)
|
||||||
|
{
|
||||||
|
char *stringPtr = dest;
|
||||||
|
while (*stringPtr)
|
||||||
|
stringPtr++;
|
||||||
|
|
||||||
|
for (i32 i = 0; i < numChars; i++)
|
||||||
|
*(stringPtr++) = src[i];
|
||||||
|
}
|
||||||
|
|
||||||
char *common_strncpy(char *dest, const char *src, i32 numChars)
|
char *common_strncpy(char *dest, const char *src, i32 numChars)
|
||||||
{
|
{
|
||||||
for (i32 i = 0; i < numChars; i++)
|
for (i32 i = 0; i < numChars; i++)
|
||||||
@ -112,3 +122,54 @@ i32 common_atoi(const char *string, const i32 len)
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 common_murmurHash2(const void *key, i32 len, u32 seed)
|
||||||
|
{
|
||||||
|
// 'm' and 'r' are mixing constants generated offline.
|
||||||
|
// They're not really 'magic', they just happen to work well.
|
||||||
|
|
||||||
|
const u32 m = 0x5bd1e995;
|
||||||
|
const i32 r = 24;
|
||||||
|
|
||||||
|
// Initialize the hash to a 'random' value
|
||||||
|
|
||||||
|
u32 h = seed ^ len;
|
||||||
|
|
||||||
|
// Mix 4 bytes at a time into the hash
|
||||||
|
|
||||||
|
const unsigned char * data = (const unsigned char *)key;
|
||||||
|
|
||||||
|
while(len >= 4)
|
||||||
|
{
|
||||||
|
u32 k = *(u32 *)data;
|
||||||
|
|
||||||
|
k *= m;
|
||||||
|
k ^= k >> r;
|
||||||
|
k *= m;
|
||||||
|
|
||||||
|
h *= m;
|
||||||
|
h ^= k;
|
||||||
|
|
||||||
|
data += 4;
|
||||||
|
len -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the last few bytes of the input array
|
||||||
|
|
||||||
|
switch(len)
|
||||||
|
{
|
||||||
|
case 3: h ^= data[2] << 16;
|
||||||
|
case 2: h ^= data[1] << 8;
|
||||||
|
case 1: h ^= data[0];
|
||||||
|
h *= m;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Do a few final mixes of the hash to ensure the last few
|
||||||
|
// bytes are well-incorporated.
|
||||||
|
|
||||||
|
h ^= h >> 13;
|
||||||
|
h *= m;
|
||||||
|
h ^= h >> 15;
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
@ -289,9 +289,17 @@ INTERNAL void assetInit(GameState *state)
|
|||||||
XmlNode root = {0};
|
XmlNode root = {0};
|
||||||
XmlNode *node = &root;
|
XmlNode *node = &root;
|
||||||
node->parent = node;
|
node->parent = node;
|
||||||
|
|
||||||
|
// NOTE(doyle): Used for when closing a node with many children. We
|
||||||
|
// automatically assign the next child after each child close within
|
||||||
|
// a group. Hence on the last child, we open another node but the next
|
||||||
|
// token indicates the group is closing- we need to set the last child's
|
||||||
|
// next reference to NULL
|
||||||
|
XmlNode *prevNode = NULL;
|
||||||
for (i32 i = 0; i < tokenIndex; i++)
|
for (i32 i = 0; i < tokenIndex; i++)
|
||||||
{
|
{
|
||||||
XmlToken *token = &xmlTokens[i];
|
XmlToken *token = &xmlTokens[i];
|
||||||
|
|
||||||
switch (token->type)
|
switch (token->type)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -305,6 +313,15 @@ INTERNAL void assetInit(GameState *state)
|
|||||||
if (common_strcmp(nextToken->string, node->parent->name) == 0)
|
if (common_strcmp(nextToken->string, node->parent->name) == 0)
|
||||||
{
|
{
|
||||||
node->parent->isClosed = TRUE;
|
node->parent->isClosed = TRUE;
|
||||||
|
|
||||||
|
if (prevNode)
|
||||||
|
{
|
||||||
|
prevNode->next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlNode *parent = node->parent;
|
||||||
|
PLATFORM_MEM_FREE(arena, node, sizeof(XmlNode));
|
||||||
|
|
||||||
node = node->parent;
|
node = node->parent;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -360,6 +377,7 @@ INTERNAL void assetInit(GameState *state)
|
|||||||
case xmltokentype_closeArrow:
|
case xmltokentype_closeArrow:
|
||||||
{
|
{
|
||||||
XmlToken prevToken = xmlTokens[i - 1];
|
XmlToken prevToken = xmlTokens[i - 1];
|
||||||
|
prevNode = node;
|
||||||
|
|
||||||
/* Closed node means we can return to parent */
|
/* Closed node means we can return to parent */
|
||||||
if (prevToken.type == xmltokentype_backslash)
|
if (prevToken.type == xmltokentype_backslash)
|
||||||
@ -402,6 +420,9 @@ INTERNAL void assetInit(GameState *state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(doyle): Use a proper random seed
|
||||||
|
#define RANDOM_SEED 0xDEADBEEF
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
DEBUG_RECURSIVE_PRINT_XML_TREE(&root);
|
DEBUG_RECURSIVE_PRINT_XML_TREE(&root);
|
||||||
#endif
|
#endif
|
||||||
@ -412,52 +433,101 @@ INTERNAL void assetInit(GameState *state)
|
|||||||
if(common_strcmp(node->name, "TextureAtlas") == 0)
|
if(common_strcmp(node->name, "TextureAtlas") == 0)
|
||||||
{
|
{
|
||||||
XmlNode *atlasXmlNode = node;
|
XmlNode *atlasXmlNode = node;
|
||||||
|
TexAtlasEntry *atlasEntry = NULL;
|
||||||
|
if (common_strcmp(node->attribute.name, "imagePath") == 0)
|
||||||
|
{
|
||||||
|
char *imageName = atlasXmlNode->attribute.value;
|
||||||
|
u32 atlasHashIndex = common_murmurHash2(
|
||||||
|
imageName, common_strlen(imageName), RANDOM_SEED);
|
||||||
|
atlasHashIndex =
|
||||||
|
atlasHashIndex % ARRAY_COUNT(assetManager->texAtlas_);
|
||||||
|
|
||||||
|
atlasEntry = &assetManager->texAtlas_[atlasHashIndex];
|
||||||
|
if (atlasEntry->name)
|
||||||
|
{
|
||||||
|
#ifdef DENGINE_DEBUG
|
||||||
|
|
||||||
|
// NOTE(doyle): Two atlas textures have the same access name
|
||||||
|
ASSERT(common_strcmp(atlasEntry->name, imageName) != 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (atlasEntry->next)
|
||||||
|
atlasEntry = atlasEntry->next;
|
||||||
|
|
||||||
|
atlasEntry->next =
|
||||||
|
PLATFORM_MEM_ALLOC(arena, 1, TexAtlasEntry);
|
||||||
|
atlasEntry = atlasEntry->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *dataDir = "data/textures/WorldTraveller/";
|
||||||
|
char imagePath[512] = {0};
|
||||||
|
common_strncat(imagePath, dataDir, common_strlen(dataDir));
|
||||||
|
common_strncat(imagePath, imageName, common_strlen(imageName));
|
||||||
|
|
||||||
|
asset_loadTextureImage(assetManager, imagePath, texlist_claude);
|
||||||
|
|
||||||
|
atlasEntry->name =
|
||||||
|
PLATFORM_MEM_ALLOC(arena, common_strlen(imageName), char);
|
||||||
|
common_strncpy(atlasEntry->name, imageName,
|
||||||
|
common_strlen(imageName));
|
||||||
|
|
||||||
|
atlasEntry->tex =
|
||||||
|
asset_getTexture(assetManager, texlist_claude);
|
||||||
|
|
||||||
XmlNode *atlasChildNode = atlasXmlNode->child;
|
XmlNode *atlasChildNode = atlasXmlNode->child;
|
||||||
while (atlasChildNode)
|
while (atlasChildNode)
|
||||||
{
|
{
|
||||||
if (common_strcmp(atlasChildNode->name, "SubTexture") == 0)
|
if (common_strcmp(atlasChildNode->name, "SubTexture") == 0)
|
||||||
{
|
{
|
||||||
XmlAttribute *subTextureAttrib = &atlasChildNode->attribute;
|
XmlAttribute *subTextureAttrib =
|
||||||
|
&atlasChildNode->attribute;
|
||||||
|
|
||||||
|
AtlasSubTexture newSubTexEntry = {0};
|
||||||
while (subTextureAttrib)
|
while (subTextureAttrib)
|
||||||
{
|
{
|
||||||
// TODO(doyle): Fill in details properly
|
|
||||||
Rect rect = {0};
|
|
||||||
|
|
||||||
// TODO(doyle): Work around for now in xml reading,
|
// TODO(doyle): Work around for now in xml reading,
|
||||||
// reading the last node closing node not being merged
|
// reading the last node closing node not being
|
||||||
// to the parent
|
// merged to the parent
|
||||||
if (!subTextureAttrib->name) continue;
|
if (!subTextureAttrib->name) continue;
|
||||||
|
|
||||||
if (common_strcmp(subTextureAttrib->name, "name") == 0)
|
if (common_strcmp(subTextureAttrib->name, "name") ==
|
||||||
{
|
|
||||||
}
|
|
||||||
else if (common_strcmp(subTextureAttrib->name, "x") ==
|
|
||||||
0)
|
0)
|
||||||
{
|
{
|
||||||
char *name = subTextureAttrib->name;
|
char *value = subTextureAttrib->value;
|
||||||
i32 nameLen = common_strlen(name);
|
newSubTexEntry.name = value;
|
||||||
rect.pos.x = CAST(f32) common_atoi(name, nameLen);
|
|
||||||
}
|
}
|
||||||
else if (common_strcmp(subTextureAttrib->name, "y") ==
|
else if (common_strcmp(subTextureAttrib->name,
|
||||||
0)
|
"x") == 0)
|
||||||
{
|
{
|
||||||
char *name = subTextureAttrib->name;
|
char *value = subTextureAttrib->value;
|
||||||
i32 nameLen = common_strlen(name);
|
i32 valueLen = common_strlen(value);
|
||||||
rect.pos.y = CAST(f32) common_atoi(name, nameLen);
|
newSubTexEntry.rect.pos.x =
|
||||||
|
CAST(f32) common_atoi(value, valueLen);
|
||||||
|
}
|
||||||
|
else if (common_strcmp(subTextureAttrib->name,
|
||||||
|
"y") == 0)
|
||||||
|
{
|
||||||
|
char *value = subTextureAttrib->value;
|
||||||
|
i32 valueLen = common_strlen(value);
|
||||||
|
newSubTexEntry.rect.pos.y =
|
||||||
|
CAST(f32) common_atoi(value, valueLen);
|
||||||
}
|
}
|
||||||
else if (common_strcmp(subTextureAttrib->name,
|
else if (common_strcmp(subTextureAttrib->name,
|
||||||
"width") == 0)
|
"width") == 0)
|
||||||
{
|
{
|
||||||
char *name = subTextureAttrib->name;
|
char *value = subTextureAttrib->value;
|
||||||
i32 nameLen = common_strlen(name);
|
i32 valueLen = common_strlen(value);
|
||||||
rect.size.w = CAST(f32) common_atoi(name, nameLen);
|
newSubTexEntry.rect.size.w =
|
||||||
|
CAST(f32) common_atoi(value, valueLen);
|
||||||
}
|
}
|
||||||
else if (common_strcmp(subTextureAttrib->name,
|
else if (common_strcmp(subTextureAttrib->name,
|
||||||
"height") == 0)
|
"height") == 0)
|
||||||
{
|
{
|
||||||
char *name = subTextureAttrib->name;
|
char *value = subTextureAttrib->value;
|
||||||
i32 nameLen = common_strlen(name);
|
i32 valueLen = common_strlen(value);
|
||||||
rect.size.h = CAST(f32) common_atoi(name, nameLen);
|
newSubTexEntry.rect.size.h =
|
||||||
|
CAST(f32) common_atoi(value, valueLen);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -469,6 +539,41 @@ INTERNAL void assetInit(GameState *state)
|
|||||||
|
|
||||||
subTextureAttrib = subTextureAttrib->next;
|
subTextureAttrib = subTextureAttrib->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DENGINE_DEBUG
|
||||||
|
ASSERT(newSubTexEntry.name)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
u32 subTexHashIndex = common_murmurHash2(
|
||||||
|
newSubTexEntry.name, common_strlen(newSubTexEntry.name),
|
||||||
|
RANDOM_SEED);
|
||||||
|
subTexHashIndex =
|
||||||
|
subTexHashIndex % ARRAY_COUNT(atlasEntry->subTex);
|
||||||
|
|
||||||
|
// NOTE(doyle): Hash collision
|
||||||
|
AtlasSubTexture *subTexEntry =
|
||||||
|
&atlasEntry->subTex[subTexHashIndex];
|
||||||
|
if (subTexEntry->name)
|
||||||
|
{
|
||||||
|
#ifdef DENGINE_DEBUG
|
||||||
|
|
||||||
|
// NOTE(doyle): Two textures have the same access
|
||||||
|
// name
|
||||||
|
ASSERT(common_strcmp(subTexEntry->name,
|
||||||
|
newSubTexEntry.name) != 0);
|
||||||
|
#endif
|
||||||
|
while (subTexEntry->next)
|
||||||
|
subTexEntry = subTexEntry->next;
|
||||||
|
|
||||||
|
subTexEntry->next =
|
||||||
|
PLATFORM_MEM_ALLOC(arena, 1, AtlasSubTexture);
|
||||||
|
subTexEntry = subTexEntry->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
*subTexEntry = newSubTexEntry;
|
||||||
|
common_strncpy(subTexEntry->name, newSubTexEntry.name,
|
||||||
|
common_strlen(newSubTexEntry.name));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -482,11 +587,17 @@ INTERNAL void assetInit(GameState *state)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#ifdef DENGINE_DEBUG
|
||||||
|
DEBUG_LOG("Unsupported xml node");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
#ifdef DENGINE_DEBUG
|
#ifdef DENGINE_DEBUG
|
||||||
DEBUG_LOG("Unsupported xml node name not parsed");
|
DEBUG_LOG("Unsupported xml node name not parsed");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
node = node->next;
|
node = node->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@ typedef struct AssetManager
|
|||||||
Animation anims[32];
|
Animation anims[32];
|
||||||
AudioVorbis audio[32];
|
AudioVorbis audio[32];
|
||||||
Font font;
|
Font font;
|
||||||
|
|
||||||
|
TexAtlasEntry texAtlas_[8];
|
||||||
} AssetManager;
|
} AssetManager;
|
||||||
|
|
||||||
#define MAX_TEXTURE_SIZE 1024
|
#define MAX_TEXTURE_SIZE 1024
|
||||||
|
@ -81,6 +81,25 @@ typedef struct AudioVorbis
|
|||||||
i32 size;
|
i32 size;
|
||||||
} AudioVorbis;
|
} AudioVorbis;
|
||||||
|
|
||||||
|
typedef struct AtlasSubTexture
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
Rect rect;
|
||||||
|
|
||||||
|
// NOTE(doyle): For hashing collisions
|
||||||
|
struct AtlasSubTexture *next;
|
||||||
|
} AtlasSubTexture;
|
||||||
|
|
||||||
|
typedef struct TexAtlasEntry
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
Texture *tex;
|
||||||
|
AtlasSubTexture subTex[1024];
|
||||||
|
|
||||||
|
// NOTE(doyle): For hashing collisions
|
||||||
|
struct TexAtlasEntry *next;
|
||||||
|
} TexAtlasEntry;
|
||||||
|
|
||||||
typedef struct TexAtlas
|
typedef struct TexAtlas
|
||||||
{
|
{
|
||||||
// TODO(doyle): String hash based lookup
|
// TODO(doyle): String hash based lookup
|
||||||
|
@ -30,6 +30,7 @@ typedef double f64;
|
|||||||
|
|
||||||
i32 common_strlen(const char *const string);
|
i32 common_strlen(const char *const string);
|
||||||
i32 common_strcmp(const char *a, const char *b);
|
i32 common_strcmp(const char *a, const char *b);
|
||||||
|
void common_strncat(char *dest, const char *src, i32 numChars);
|
||||||
char *common_strncpy(char *dest, const char *src, i32 numChars);
|
char *common_strncpy(char *dest, const char *src, i32 numChars);
|
||||||
char *common_memset(char *const ptr, const i32 value, const i32 numBytes);
|
char *common_memset(char *const ptr, const i32 value, const i32 numBytes);
|
||||||
|
|
||||||
@ -37,4 +38,20 @@ char *common_memset(char *const ptr, const i32 value, const i32 numBytes);
|
|||||||
void common_itoa(i32 value, char *buf, i32 bufSize);
|
void common_itoa(i32 value, char *buf, i32 bufSize);
|
||||||
i32 common_atoi(const char *string, const i32 len);
|
i32 common_atoi(const char *string, const i32 len);
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// MurmurHash2, by Austin Appleby
|
||||||
|
|
||||||
|
// Note - This code makes a few assumptions about how your machine behaves -
|
||||||
|
|
||||||
|
// 1. We can read a 4-byte value from any address without crashing
|
||||||
|
// 2. sizeof(int) == 4
|
||||||
|
|
||||||
|
// And it has a few limitations -
|
||||||
|
|
||||||
|
// 1. It will not work incrementally.
|
||||||
|
// 2. It will not produce the same results on little-endian and big-endian
|
||||||
|
// machines.
|
||||||
|
u32 common_murmurHash2(const void *key, i32 len, u32 seed);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user