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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user