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)
|
||||
{
|
||||
printf(
|
||||
"worldTraveller_gameInit() warning: Sprite sheet is not square: "
|
||||
"asset_loadTextureImage() warning: Sprite sheet is not square: "
|
||||
"%dx%dpx\n", imgWidth, imgHeight);
|
||||
}
|
||||
#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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
for (i32 i = 0; i < numChars; i++)
|
||||
@ -112,3 +122,54 @@ i32 common_atoi(const char *string, const i32 len)
|
||||
|
||||
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 *node = &root;
|
||||
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++)
|
||||
{
|
||||
XmlToken *token = &xmlTokens[i];
|
||||
|
||||
switch (token->type)
|
||||
{
|
||||
|
||||
@ -305,7 +313,16 @@ INTERNAL void assetInit(GameState *state)
|
||||
if (common_strcmp(nextToken->string, node->parent->name) == 0)
|
||||
{
|
||||
node->parent->isClosed = TRUE;
|
||||
node = node->parent;
|
||||
|
||||
if (prevNode)
|
||||
{
|
||||
prevNode->next = NULL;
|
||||
}
|
||||
|
||||
XmlNode *parent = node->parent;
|
||||
PLATFORM_MEM_FREE(arena, node, sizeof(XmlNode));
|
||||
|
||||
node = node->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -360,6 +377,7 @@ INTERNAL void assetInit(GameState *state)
|
||||
case xmltokentype_closeArrow:
|
||||
{
|
||||
XmlToken prevToken = xmlTokens[i - 1];
|
||||
prevNode = node;
|
||||
|
||||
/* Closed node means we can return to parent */
|
||||
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
|
||||
DEBUG_RECURSIVE_PRINT_XML_TREE(&root);
|
||||
#endif
|
||||
@ -411,73 +432,164 @@ INTERNAL void assetInit(GameState *state)
|
||||
{
|
||||
if(common_strcmp(node->name, "TextureAtlas") == 0)
|
||||
{
|
||||
XmlNode *atlasXmlNode = node;
|
||||
XmlNode *atlasChildNode = atlasXmlNode->child;
|
||||
while (atlasChildNode)
|
||||
XmlNode *atlasXmlNode = node;
|
||||
TexAtlasEntry *atlasEntry = NULL;
|
||||
if (common_strcmp(node->attribute.name, "imagePath") == 0)
|
||||
{
|
||||
if (common_strcmp(atlasChildNode->name, "SubTexture") == 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)
|
||||
{
|
||||
XmlAttribute *subTextureAttrib = &atlasChildNode->attribute;
|
||||
while (subTextureAttrib)
|
||||
#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;
|
||||
while (atlasChildNode)
|
||||
{
|
||||
if (common_strcmp(atlasChildNode->name, "SubTexture") == 0)
|
||||
{
|
||||
// TODO(doyle): Fill in details properly
|
||||
Rect rect = {0};
|
||||
XmlAttribute *subTextureAttrib =
|
||||
&atlasChildNode->attribute;
|
||||
|
||||
// TODO(doyle): Work around for now in xml reading,
|
||||
// reading the last node closing node not being merged
|
||||
// to the parent
|
||||
if (!subTextureAttrib->name) continue;
|
||||
AtlasSubTexture newSubTexEntry = {0};
|
||||
while (subTextureAttrib)
|
||||
{
|
||||
|
||||
if (common_strcmp(subTextureAttrib->name, "name") == 0)
|
||||
{
|
||||
// TODO(doyle): Work around for now in xml reading,
|
||||
// reading the last node closing node not being
|
||||
// merged to the parent
|
||||
if (!subTextureAttrib->name) continue;
|
||||
|
||||
if (common_strcmp(subTextureAttrib->name, "name") ==
|
||||
0)
|
||||
{
|
||||
char *value = subTextureAttrib->value;
|
||||
newSubTexEntry.name = value;
|
||||
}
|
||||
else if (common_strcmp(subTextureAttrib->name,
|
||||
"x") == 0)
|
||||
{
|
||||
char *value = subTextureAttrib->value;
|
||||
i32 valueLen = common_strlen(value);
|
||||
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,
|
||||
"width") == 0)
|
||||
{
|
||||
char *value = subTextureAttrib->value;
|
||||
i32 valueLen = common_strlen(value);
|
||||
newSubTexEntry.rect.size.w =
|
||||
CAST(f32) common_atoi(value, valueLen);
|
||||
}
|
||||
else if (common_strcmp(subTextureAttrib->name,
|
||||
"height") == 0)
|
||||
{
|
||||
char *value = subTextureAttrib->value;
|
||||
i32 valueLen = common_strlen(value);
|
||||
newSubTexEntry.rect.size.h =
|
||||
CAST(f32) common_atoi(value, valueLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG(
|
||||
"Unsupported xml attribute in SubTexture");
|
||||
#endif
|
||||
}
|
||||
|
||||
subTextureAttrib = subTextureAttrib->next;
|
||||
}
|
||||
else if (common_strcmp(subTextureAttrib->name, "x") ==
|
||||
0)
|
||||
{
|
||||
char *name = subTextureAttrib->name;
|
||||
i32 nameLen = common_strlen(name);
|
||||
rect.pos.x = CAST(f32) common_atoi(name, nameLen);
|
||||
}
|
||||
else if (common_strcmp(subTextureAttrib->name, "y") ==
|
||||
0)
|
||||
{
|
||||
char *name = subTextureAttrib->name;
|
||||
i32 nameLen = common_strlen(name);
|
||||
rect.pos.y = CAST(f32) common_atoi(name, nameLen);
|
||||
}
|
||||
else if (common_strcmp(subTextureAttrib->name,
|
||||
"width") == 0)
|
||||
{
|
||||
char *name = subTextureAttrib->name;
|
||||
i32 nameLen = common_strlen(name);
|
||||
rect.size.w = CAST(f32) common_atoi(name, nameLen);
|
||||
}
|
||||
else if (common_strcmp(subTextureAttrib->name,
|
||||
"height") == 0)
|
||||
{
|
||||
char *name = subTextureAttrib->name;
|
||||
i32 nameLen = common_strlen(name);
|
||||
rect.size.h = CAST(f32) common_atoi(name, nameLen);
|
||||
}
|
||||
else
|
||||
|
||||
#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
|
||||
DEBUG_LOG(
|
||||
"Unsupported xml attribute in SubTexture");
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
subTextureAttrib = subTextureAttrib->next;
|
||||
*subTexEntry = newSubTexEntry;
|
||||
common_strncpy(subTexEntry->name, newSubTexEntry.name,
|
||||
common_strlen(newSubTexEntry.name));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Unsupported xml node name not parsed");
|
||||
DEBUG_LOG("Unsupported xml node name not parsed");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
atlasChildNode = atlasChildNode->next;
|
||||
atlasChildNode = atlasChildNode->next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Unsupported xml node");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -486,7 +598,6 @@ INTERNAL void assetInit(GameState *state)
|
||||
DEBUG_LOG("Unsupported xml node name not parsed");
|
||||
#endif
|
||||
}
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,8 @@ typedef struct AssetManager
|
||||
Animation anims[32];
|
||||
AudioVorbis audio[32];
|
||||
Font font;
|
||||
|
||||
TexAtlasEntry texAtlas_[8];
|
||||
} AssetManager;
|
||||
|
||||
#define MAX_TEXTURE_SIZE 1024
|
||||
|
@ -81,6 +81,25 @@ typedef struct AudioVorbis
|
||||
i32 size;
|
||||
} 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
|
||||
{
|
||||
// TODO(doyle): String hash based lookup
|
||||
|
@ -30,6 +30,7 @@ typedef double f64;
|
||||
|
||||
i32 common_strlen(const char *const string);
|
||||
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_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);
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user