Pull out xml loading to assetmanager code
This commit is contained in:
		
							parent
							
								
									1d09bdfa20
								
							
						
					
					
						commit
						c93e6ca37c
					
				| @ -18,10 +18,525 @@ | ||||
| 
 | ||||
| #include "Dengine/AssetManager.h" | ||||
| #include "Dengine/Debug.h" | ||||
| #include "Dengine/MemoryArena.h" | ||||
| #include "Dengine/OpenGL.h" | ||||
| #include "Dengine/Platform.h" | ||||
| 
 | ||||
| INTERNAL AtlasSubTexture *getAtlasSubTexture(TexAtlas *atlas, char *key) | ||||
| /*
 | ||||
|  ********************************* | ||||
|  * XML Operations | ||||
|  ********************************* | ||||
|  */ | ||||
| enum XmlTokenType | ||||
| { | ||||
| 	xmltokentype_unknown, | ||||
| 	xmltokentype_openArrow, | ||||
| 	xmltokentype_closeArrow, | ||||
| 	xmltokentype_name, | ||||
| 	xmltokentype_value, | ||||
| 	xmltokentype_equals, | ||||
| 	xmltokentype_quotes, | ||||
| 	xmltokentype_backslash, | ||||
| 	xmltokentype_count, | ||||
| }; | ||||
| 
 | ||||
| typedef struct XmlToken | ||||
| { | ||||
| 	// TODO(doyle): Dynamic size string in tokens maybe.
 | ||||
| 	enum XmlTokenType type; | ||||
| 	char string[128]; | ||||
| 	i32 len; | ||||
| } XmlToken; | ||||
| 
 | ||||
| INTERNAL XmlToken *tokeniseXmlBuffer(MemoryArena *arena, char *buffer, | ||||
|                                      i32 bufferSize, int *numTokens) | ||||
| { | ||||
| 	XmlToken *xmlTokens = PLATFORM_MEM_ALLOC(arena, 8192, XmlToken); | ||||
| 	i32 tokenIndex      = 0; | ||||
| 	for (i32 i = 0; i < bufferSize; i++) | ||||
| 	{ | ||||
| 		char c = (CAST(char *) buffer)[i]; | ||||
| 		switch (c) | ||||
| 		{ | ||||
| 		case '<': | ||||
| 		case '>': | ||||
| 		case '=': | ||||
| 		case '/': | ||||
| 		{ | ||||
| 
 | ||||
| 			enum XmlTokenType type = xmltokentype_unknown; | ||||
| 			if (c == '<') | ||||
| 			{ | ||||
| 				type = xmltokentype_openArrow; | ||||
| 			} | ||||
| 			else if (c == '>') | ||||
| 			{ | ||||
| 				type = xmltokentype_closeArrow; | ||||
| 			} | ||||
| 			else if (c == '=') | ||||
| 			{ | ||||
| 				type = xmltokentype_equals; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				type = xmltokentype_backslash; | ||||
| 			} | ||||
| 
 | ||||
| 			xmlTokens[tokenIndex].type = type; | ||||
| 			xmlTokens[tokenIndex].len  = 1; | ||||
| 			tokenIndex++; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		case '"': | ||||
| 		{ | ||||
| 			xmlTokens[tokenIndex].type = xmltokentype_value; | ||||
| 			for (i32 j = i + 1; j < bufferSize; j++) | ||||
| 			{ | ||||
| 				char c = (CAST(char *) buffer)[j]; | ||||
| 
 | ||||
| 				if (c == '"') | ||||
| 				{ | ||||
| 					break; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					xmlTokens[tokenIndex].string[xmlTokens[tokenIndex].len++] = | ||||
| 					    c; | ||||
| #ifdef DENGINE_DEBUG | ||||
| 					ASSERT(xmlTokens[tokenIndex].len < | ||||
| 					       ARRAY_COUNT(xmlTokens[tokenIndex].string)); | ||||
| #endif | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			// NOTE(doyle): +1 to skip the closing quotes
 | ||||
| 			i += (xmlTokens[tokenIndex].len + 1); | ||||
| 			tokenIndex++; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		default: | ||||
| 		{ | ||||
| 			if ((c >= 'a' && c <= 'z') || c >= 'A' && c <= 'Z') | ||||
| 			{ | ||||
| 				xmlTokens[tokenIndex].type = xmltokentype_name; | ||||
| 				for (i32 j = i; j < bufferSize; j++) | ||||
| 				{ | ||||
| 					char c = (CAST(char *) buffer)[j]; | ||||
| 
 | ||||
| 					if (c == ' ' || c == '=' || c == '>' || c == '<' || | ||||
| 					    c == '\\') | ||||
| 					{ | ||||
| 						break; | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						xmlTokens[tokenIndex] | ||||
| 						    .string[xmlTokens[tokenIndex].len++] = c; | ||||
| #ifdef DENGINE_DEBUG | ||||
| 						ASSERT(xmlTokens[tokenIndex].len < | ||||
| 						       ARRAY_COUNT(xmlTokens[tokenIndex].string)); | ||||
| #endif | ||||
| 					} | ||||
| 				} | ||||
| 				i += xmlTokens[tokenIndex].len; | ||||
| 				tokenIndex++; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// TODO(doyle): Dynamic token allocation
 | ||||
| 	*numTokens = 8192; | ||||
| 	return xmlTokens; | ||||
| } | ||||
| 
 | ||||
| INTERNAL XmlNode *buildXmlTree(MemoryArena *arena, XmlToken *xmlTokens, | ||||
|                                i32 numTokens) | ||||
| { | ||||
| 	XmlNode *root = PLATFORM_MEM_ALLOC(arena, 1, XmlNode); | ||||
| 	if (!root) return NULL; | ||||
| 
 | ||||
| 	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 < numTokens; i++) | ||||
| 	{ | ||||
| 		XmlToken *token = &xmlTokens[i]; | ||||
| 
 | ||||
| 		switch (token->type) | ||||
| 		{ | ||||
| 
 | ||||
| 		case xmltokentype_openArrow: | ||||
| 		{ | ||||
| 			/* Open arrows indicate closing parent node or new node name */ | ||||
| 			XmlToken *nextToken = &xmlTokens[++i]; | ||||
| 			if (nextToken->type == xmltokentype_backslash) | ||||
| 			{ | ||||
| 				nextToken = &xmlTokens[++i]; | ||||
| 				if (common_strcmp(nextToken->string, node->parent->name) == 0) | ||||
| 				{ | ||||
| 					node->parent->isClosed = TRUE; | ||||
| 
 | ||||
| 					if (prevNode) | ||||
| 					{ | ||||
| 						prevNode->next = NULL; | ||||
| 					} | ||||
| 
 | ||||
| 					XmlNode *parent = node->parent; | ||||
| 					PLATFORM_MEM_FREE(arena, node, sizeof(XmlNode)); | ||||
| 					node            = node->parent; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 					DEBUG_LOG( | ||||
| 					    "Closing xml node name does not match parent name"); | ||||
| #endif | ||||
| 				} | ||||
| 			} | ||||
| 			else if (nextToken->type == xmltokentype_name) | ||||
| 			{ | ||||
| 				node->name = nextToken->string; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 				DEBUG_LOG("Unexpected token type after open arrow"); | ||||
| #endif | ||||
| 			} | ||||
| 
 | ||||
| 			token = nextToken; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		case xmltokentype_name: | ||||
| 		{ | ||||
| 			// TODO(doyle): Store latest attribute pointer so we aren't always
 | ||||
| 			// chasing the linked list each iteration. Do the same for children
 | ||||
| 			// node
 | ||||
| 
 | ||||
| 			/* Xml Attributes are a linked list, get first free entry */ | ||||
| 			XmlAttribute *attribute = &node->attribute; | ||||
| 			if (attribute->init) | ||||
| 			{ | ||||
| 				while (attribute->next) | ||||
| 					attribute = attribute->next; | ||||
| 
 | ||||
| 				attribute->next = PLATFORM_MEM_ALLOC(arena, 1, XmlAttribute); | ||||
| 				attribute       = attribute->next; | ||||
| 			} | ||||
| 
 | ||||
| 			/* Just plain text is a node attribute name */ | ||||
| 			attribute->name = token->string; | ||||
| 
 | ||||
| 			/* Followed by the value */ | ||||
| 			token            = &xmlTokens[++i]; | ||||
| 			attribute->value = token->string; | ||||
| 			attribute->init  = TRUE; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		case xmltokentype_closeArrow: | ||||
| 		{ | ||||
| 			XmlToken prevToken = xmlTokens[i - 1]; | ||||
| 			prevNode = node; | ||||
| 
 | ||||
| 			/* Closed node means we can return to parent */ | ||||
| 			if (prevToken.type == xmltokentype_backslash) | ||||
| 			{ | ||||
| 				node->isClosed = TRUE; | ||||
| 				node = node->parent; | ||||
| 			} | ||||
| 			 | ||||
| 			if (!node->isClosed) | ||||
| 			{ | ||||
| 				/* Unclosed node means next fields will be children of node */ | ||||
| 
 | ||||
| 				/* If the first child is free allocate, otherwise we have to
 | ||||
| 				 * iterate through the child's next node(s) */ | ||||
| 				if (!node->child) | ||||
| 				{ | ||||
| 					// TODO(doyle): Mem alloc error checking
 | ||||
| 					node->child         = PLATFORM_MEM_ALLOC(arena, 1, XmlNode); | ||||
| 					node->child->parent = node; | ||||
| 					node                = node->child; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					XmlNode *nodeToCheck = node->child; | ||||
| 					while (nodeToCheck->next) | ||||
| 						nodeToCheck = nodeToCheck->next; | ||||
| 
 | ||||
| 					nodeToCheck->next = PLATFORM_MEM_ALLOC(arena, 1, XmlNode); | ||||
| 					nodeToCheck->next->parent = node; | ||||
| 					node                      = nodeToCheck->next; | ||||
| 				} | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		default: | ||||
| 		{ | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return root; | ||||
| } | ||||
| 
 | ||||
| INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena *arena, | ||||
|                                  XmlNode *root) | ||||
| { | ||||
| 	XmlNode *node = root; | ||||
| 	while (node) | ||||
| 	{ | ||||
| 		if (common_strcmp(node->name, "TextureAtlas") == 0) | ||||
| 		{ | ||||
| 			XmlNode *atlasXmlNode = node; | ||||
| 			TexAtlas *atlasEntry  = NULL; | ||||
| 			if (common_strcmp(node->attribute.name, "imagePath") == 0) | ||||
| 			{ | ||||
| 				char *imageName = atlasXmlNode->attribute.value; | ||||
| 				atlasEntry = asset_makeTexAtlas(assetManager, arena, imageName); | ||||
| 
 | ||||
| 				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, arena, imagePath, | ||||
| 				                       imageName); | ||||
| 
 | ||||
| 				atlasEntry->key = PLATFORM_MEM_ALLOC( | ||||
| 				    arena, common_strlen(imageName) + 1, char); | ||||
| 				common_strncpy(atlasEntry->key, imageName, | ||||
| 				               common_strlen(imageName)); | ||||
| 
 | ||||
| 				atlasEntry->tex = asset_getTex(assetManager, imageName); | ||||
| 
 | ||||
| 				XmlNode *atlasChildNode = atlasXmlNode->child; | ||||
| 				while (atlasChildNode) | ||||
| 				{ | ||||
| 					if (common_strcmp(atlasChildNode->name, "SubTexture") == 0) | ||||
| 					{ | ||||
| 						XmlAttribute *subTextureAttrib = | ||||
| 						    &atlasChildNode->attribute; | ||||
| 
 | ||||
| 						AtlasSubTexture newSubTexEntry = {0}; | ||||
| 						while (subTextureAttrib) | ||||
| 						{ | ||||
| 
 | ||||
| 							// 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.key = value; | ||||
| 							} | ||||
| 							else if (common_strcmp(subTextureAttrib->name, | ||||
| 							                       "x") == 0) | ||||
| 							{ | ||||
| 								char *value  = subTextureAttrib->value; | ||||
| 								i32 valueLen = common_strlen(value); | ||||
| 								i32 intValue = common_atoi(value, valueLen); | ||||
| 
 | ||||
| 								newSubTexEntry.rect.pos.x = CAST(f32) intValue; | ||||
| 							} | ||||
| 							else if (common_strcmp(subTextureAttrib->name, | ||||
| 							                       "y") == 0) | ||||
| 							{ | ||||
| 								char *value  = subTextureAttrib->value; | ||||
| 								i32 valueLen = common_strlen(value); | ||||
| 
 | ||||
| 								i32 intValue = common_atoi(value, valueLen); | ||||
| 								newSubTexEntry.rect.pos.y = CAST(f32) intValue; | ||||
| 							} | ||||
| 							else if (common_strcmp(subTextureAttrib->name, | ||||
| 							                       "width") == 0) | ||||
| 							{ | ||||
| 								char *value  = subTextureAttrib->value; | ||||
| 								i32 valueLen = common_strlen(value); | ||||
| 								i32 intValue = common_atoi(value, valueLen); | ||||
| 
 | ||||
| 								newSubTexEntry.rect.size.w = CAST(f32) intValue; | ||||
| 							} | ||||
| 							else if (common_strcmp(subTextureAttrib->name, | ||||
| 							                       "height") == 0) | ||||
| 							{ | ||||
| 								char *value  = subTextureAttrib->value; | ||||
| 								i32 valueLen = common_strlen(value); | ||||
| 								i32 intValue = common_atoi(value, valueLen); | ||||
| 
 | ||||
| 								newSubTexEntry.rect.size.h = CAST(f32) intValue; | ||||
| 							} | ||||
| 							else | ||||
| 							{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 								DEBUG_LOG( | ||||
| 								    "Unsupported xml attribute in SubTexture"); | ||||
| #endif | ||||
| 							} | ||||
| 							subTextureAttrib = subTextureAttrib->next; | ||||
| 						} | ||||
| 
 | ||||
| 						// TODO(doyle): XML specifies 0,0 top left, we
 | ||||
| 						// prefer 0,0 bottom right, so offset by size since 0,0
 | ||||
| 						// is top left and size creates a bounding box below it
 | ||||
| 						newSubTexEntry.rect.pos.y = | ||||
| 						    1024 - newSubTexEntry.rect.pos.y; | ||||
| 						newSubTexEntry.rect.pos.y -= newSubTexEntry.rect.size.h; | ||||
| 
 | ||||
| #ifdef DENGINE_DEBUG | ||||
| 						ASSERT(newSubTexEntry.key) | ||||
| #endif | ||||
| 
 | ||||
| 						u32 subTexHashIndex = common_murmurHash2( | ||||
| 						    newSubTexEntry.key, | ||||
| 						    common_strlen(newSubTexEntry.key), 0xDEADBEEF); | ||||
| 						subTexHashIndex = | ||||
| 						    subTexHashIndex % ARRAY_COUNT(atlasEntry->subTex); | ||||
| 
 | ||||
| 						// NOTE(doyle): Hash collision
 | ||||
| 						AtlasSubTexture *subTexEntry = | ||||
| 						    &atlasEntry->subTex[subTexHashIndex]; | ||||
| 						if (subTexEntry->key) | ||||
| 						{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 
 | ||||
| 							// NOTE(doyle): Two textures have the same access
 | ||||
| 							// name
 | ||||
| 							ASSERT(common_strcmp(subTexEntry->key, | ||||
| 							                     newSubTexEntry.key) != 0); | ||||
| #endif | ||||
| 							while (subTexEntry->next) | ||||
| 								subTexEntry = subTexEntry->next; | ||||
| 
 | ||||
| 							subTexEntry->next = | ||||
| 							    PLATFORM_MEM_ALLOC(arena, 1, AtlasSubTexture); | ||||
| 							subTexEntry = subTexEntry->next; | ||||
| 						} | ||||
| 
 | ||||
| 						*subTexEntry = newSubTexEntry; | ||||
| 						i32 keyLen   = common_strlen(newSubTexEntry.key); | ||||
| 
 | ||||
| 						subTexEntry->key = | ||||
| 						    PLATFORM_MEM_ALLOC(arena, keyLen + 1, char); | ||||
| 						common_strncpy(subTexEntry->key, newSubTexEntry.key, | ||||
| 						               keyLen); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 						DEBUG_LOG("Unsupported xml node name not parsed"); | ||||
| #endif | ||||
| 					} | ||||
| 
 | ||||
| 					atlasChildNode = atlasChildNode->next; | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 				DEBUG_LOG("Unsupported xml node"); | ||||
| #endif | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 			DEBUG_LOG("Unsupported xml node name not parsed"); | ||||
| #endif | ||||
| 		} | ||||
| 		node = node->next; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| INTERNAL void recursiveFreeXmlTree(MemoryArena *arena, XmlNode *node) | ||||
| { | ||||
| 	if (!node) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} else | ||||
| 	{ | ||||
| 		// NOTE(doyle): First attribute is statically allocated, only if there's
 | ||||
| 		// more attributes do we dynamically allocate
 | ||||
| 		XmlAttribute *attrib = node->attribute.next; | ||||
| 
 | ||||
| 		while (attrib) | ||||
| 		{ | ||||
| 			XmlAttribute *next = attrib->next; | ||||
| 
 | ||||
| 			attrib->name  = NULL; | ||||
| 			attrib->value = NULL; | ||||
| 			PLATFORM_MEM_FREE(arena, attrib, sizeof(XmlAttribute)); | ||||
| 			attrib = next; | ||||
| 		} | ||||
| 
 | ||||
| 		recursiveFreeXmlTree(arena, node->child); | ||||
| 		recursiveFreeXmlTree(arena, node->next); | ||||
| 
 | ||||
| 		node->name     = NULL; | ||||
| 		node->isClosed = FALSE; | ||||
| 		PLATFORM_MEM_FREE(arena, node, sizeof(XmlNode)); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| INTERNAL void freeXmlData(MemoryArena *arena, XmlToken *tokens, i32 numTokens, | ||||
|                           XmlNode *tree) | ||||
| { | ||||
| 	if (tree) recursiveFreeXmlTree(arena, tree); | ||||
| 	if (tokens) PLATFORM_MEM_FREE(arena, tokens, numTokens * sizeof(XmlToken)); | ||||
| } | ||||
| 
 | ||||
| i32 asset_loadXmlFile(AssetManager *assetManager, MemoryArena *arena, | ||||
|                       PlatformFileRead *fileRead) | ||||
| { | ||||
| 	i32 result = 0; | ||||
| 	/* Tokenise buffer */ | ||||
| 	i32 numTokens       = 0; | ||||
| 	XmlToken *xmlTokens = | ||||
| 	    tokeniseXmlBuffer(arena, fileRead->buffer, fileRead->size, &numTokens); | ||||
| 
 | ||||
| 	/* Build XML tree from tokens */ | ||||
| 	XmlNode *xmlTree = buildXmlTree(arena, xmlTokens, numTokens); | ||||
| 
 | ||||
| 	if (xmlTree) | ||||
| 	{ | ||||
| 		/* Parse XML tree to game structures */ | ||||
| 		parseXmlTreeToGame(assetManager, arena, xmlTree); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		result = -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Free data */ | ||||
| 	freeXmlData(arena, xmlTokens, numTokens, xmlTree); | ||||
| 
 | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  ********************************* | ||||
|  * Texture Operations | ||||
|  ********************************* | ||||
|  */ | ||||
| INTERNAL AtlasSubTexture *getAtlasSubTex(TexAtlas *atlas, char *key) | ||||
| { | ||||
| 	u32 hashIndex = common_getHashIndex(key, ARRAY_COUNT(atlas->subTex)); | ||||
| 	AtlasSubTexture *result = &atlas->subTex[hashIndex]; | ||||
| @ -34,8 +549,8 @@ INTERNAL AtlasSubTexture *getAtlasSubTexture(TexAtlas *atlas, char *key) | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| INTERNAL AtlasSubTexture *makeAtlasSubTexture(TexAtlas *atlas, | ||||
|                                               MemoryArena *arena, char *key) | ||||
| INTERNAL AtlasSubTexture *getFreeAtlasSubTexSlot(TexAtlas *atlas, | ||||
|                                                  MemoryArena *arena, char *key) | ||||
| { | ||||
| 	u32 hashIndex = common_getHashIndex(key, ARRAY_COUNT(atlas->subTex)); | ||||
| 	AtlasSubTexture *result = &atlas->subTex[hashIndex]; | ||||
| @ -59,8 +574,8 @@ INTERNAL AtlasSubTexture *makeAtlasSubTexture(TexAtlas *atlas, | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| INTERNAL Animation *getFreeAnimSlot(Animation *table, u32 tableSize, | ||||
|                                     MemoryArena *arena, char *key) | ||||
| INTERNAL Animation *getFreeAnimationSlot(Animation *table, u32 tableSize, | ||||
|                                          MemoryArena *arena, char *key) | ||||
| { | ||||
| 	u32 hashIndex     = common_getHashIndex(key, tableSize); | ||||
| 	Animation *result = &table[hashIndex]; | ||||
| @ -84,9 +599,9 @@ INTERNAL Animation *getFreeAnimSlot(Animation *table, u32 tableSize, | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| Rect asset_getAtlasSubTexRect(TexAtlas *atlas, char *key) | ||||
| Rect asset_getSubTexRect(TexAtlas *atlas, char *key) | ||||
| { | ||||
| 	AtlasSubTexture *subTex = getAtlasSubTexture(atlas, key); | ||||
| 	AtlasSubTexture *subTex = getAtlasSubTex(atlas, key); | ||||
| 	Rect result = subTex->rect; | ||||
| 	return result; | ||||
| } | ||||
| @ -527,7 +1042,7 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena, | ||||
| 				char charTmp[2] = {0}; | ||||
| 				charTmp[0] = charToEncode; | ||||
| 				AtlasSubTexture *subTex = | ||||
| 				    makeAtlasSubTexture(fontAtlas, arena, charTmp); | ||||
| 				    getFreeAtlasSubTexSlot(fontAtlas, arena, charTmp); | ||||
| 
 | ||||
| 				subTex->key = PLATFORM_MEM_ALLOC(arena, 1, char); | ||||
| 				subTex->key[0] = charToEncode; | ||||
| @ -615,7 +1130,7 @@ void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena, | ||||
|                         char *animName, TexAtlas *atlas, char **subTextureNames, | ||||
|                         i32 numSubTextures, f32 frameDuration) | ||||
| { | ||||
| 	Animation *anim = getFreeAnimSlot( | ||||
| 	Animation *anim = getFreeAnimationSlot( | ||||
| 	    assetManager->anims, ARRAY_COUNT(assetManager->anims), arena, animName); | ||||
| 
 | ||||
| 	anim->atlas         = atlas; | ||||
| @ -629,7 +1144,7 @@ void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena, | ||||
| 	anim->frameList = PLATFORM_MEM_ALLOC(arena, numSubTextures, char*); | ||||
| 	for (i32 i = 0; i < numSubTextures; i++) | ||||
| 	{ | ||||
| 		AtlasSubTexture *subTex = getAtlasSubTexture(atlas, subTextureNames[i]); | ||||
| 		AtlasSubTexture *subTex = getAtlasSubTex(atlas, subTextureNames[i]); | ||||
| 		anim->frameList[i]       = subTex->key; | ||||
| 	} | ||||
| 
 | ||||
| @ -656,3 +1171,29 @@ v2 asset_stringDimInPixels(const Font *const font, const char *const string) | ||||
| 
 | ||||
| 	return stringDim; | ||||
| } | ||||
| 
 | ||||
| void asset_unitTest(MemoryArena *arena) | ||||
| { | ||||
| 	PlatformFileRead xmlFileRead = {0}; | ||||
| 	i32 result = platform_readFileToBuffer( | ||||
| 	    arena, "data/textures/WorldTraveller/ClaudeSprite.xml", &xmlFileRead); | ||||
| 	if (result) | ||||
| 	{ | ||||
| 		DEBUG_LOG( | ||||
| 		    "unitTest() error: Could not load XML file for memory free test"); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		/* Tokenise buffer */ | ||||
| 		i32 memBefore       = arena->bytesAllocated; | ||||
| 		i32 numTokens       = 0; | ||||
| 		XmlToken *xmlTokens = tokeniseXmlBuffer(arena, xmlFileRead.buffer, | ||||
| 		                                        xmlFileRead.size, &numTokens); | ||||
| 		/* Build XML tree from tokens */ | ||||
| 		XmlNode *xmlTree = buildXmlTree(arena, xmlTokens, numTokens); | ||||
| 		freeXmlData(arena, xmlTokens, numTokens, xmlTree); | ||||
| 		i32 memAfter = arena->bytesAllocated; | ||||
| 
 | ||||
| 		ASSERT(memBefore == memAfter); | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										30
									
								
								src/Debug.c
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								src/Debug.c
									
									
									
									
									
								
							| @ -103,6 +103,36 @@ void debug_init(MemoryArena *arena, v2 windowSize, Font font) | ||||
| 	GLOBAL_debug.initialConsoleP = V2(consoleXPos, consoleYPos); | ||||
| } | ||||
| 
 | ||||
| void debug_recursivePrintXmlTree(XmlNode *root, i32 levelsDeep) | ||||
| { | ||||
| 	if (!root) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		for (i32 i = 0; i < levelsDeep; i++) | ||||
| 		{ | ||||
| 			printf("-"); | ||||
| 		} | ||||
| 
 | ||||
| 		printf("%s ", root->name); | ||||
| 
 | ||||
| 		XmlAttribute *attribute = &root->attribute; | ||||
| 		printf("| %s = %s", attribute->name, attribute->value); | ||||
| 
 | ||||
| 		while (attribute->next) | ||||
| 		{ | ||||
| 			attribute = attribute->next; | ||||
| 			printf("| %s = %04s", attribute->name, attribute->value); | ||||
| 		} | ||||
| 		printf("\n"); | ||||
| 
 | ||||
| 		debug_recursivePrintXmlTree(root->child, levelsDeep+1); | ||||
| 		debug_recursivePrintXmlTree(root->next, levelsDeep); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void debug_callCountIncrement(i32 id) | ||||
| { | ||||
| 	ASSERT(id < debugcallcount_num); | ||||
|  | ||||
| @ -56,7 +56,7 @@ void entity_updateAnim(Entity *entity, f32 dt) | ||||
| 	case entitytype_npc: | ||||
| 		char *frameName = anim->frameList[currEntityAnim->currFrame]; | ||||
| 		Rect texRect = | ||||
| 		    asset_getAtlasSubTexRect(anim->atlas, frameName); | ||||
| 		    asset_getSubTexRect(anim->atlas, frameName); | ||||
| 		entity->renderSize = texRect.size; | ||||
| 	default: | ||||
| 		break; | ||||
|  | ||||
| @ -211,7 +211,7 @@ void renderer_string(Renderer *const renderer, MemoryArena *arena, Rect camera, | ||||
| 
 | ||||
| 			/* Get texture out */ | ||||
| 			Rect charTexRect = | ||||
| 			    asset_getAtlasSubTexRect(font->atlas, &CAST(char)codepoint); | ||||
| 			    asset_getSubTexRect(font->atlas, &CAST(char)codepoint); | ||||
| 
 | ||||
| 			v4 deprecatedTexRect = {0}; | ||||
| 			deprecatedTexRect.vec2[0] = charTexRect.pos; | ||||
| @ -251,7 +251,7 @@ void renderer_entity(Renderer *renderer, Rect camera, Entity *entity, | ||||
| 		EntityAnim *entityAnim = &entity->animList[entity->currAnimId]; | ||||
| 		Animation *anim        = entityAnim->anim; | ||||
| 		char *frameName        = anim->frameList[entityAnim->currFrame]; | ||||
| 		Rect animRect = asset_getAtlasSubTexRect(anim->atlas, frameName); | ||||
| 		Rect animRect = asset_getSubTexRect(anim->atlas, frameName); | ||||
| 
 | ||||
| 		// TODO(doyle): Switch to rect
 | ||||
| 		v4 animTexRect = {0}; | ||||
|  | ||||
| @ -111,541 +111,6 @@ INTERNAL void rendererInit(GameState *state, v2 windowSize) | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| typedef struct XmlAttribute | ||||
| { | ||||
| 	b32 init; | ||||
| 	char *name; | ||||
| 	char *value; | ||||
| 
 | ||||
| 	struct XmlAttribute *next; | ||||
| } XmlAttribute; | ||||
| 
 | ||||
| typedef struct XmlNode | ||||
| { | ||||
| 	char *name; | ||||
| 	XmlAttribute attribute; | ||||
| 
 | ||||
| 	// NOTE(doyle): Track if node has more children
 | ||||
| 	b32 isClosed; | ||||
| 
 | ||||
| 	// NOTE(doyle): Direct child/parent
 | ||||
| 	struct XmlNode *parent; | ||||
| 	struct XmlNode *child; | ||||
| 
 | ||||
| 	// NOTE(doyle): Else all other nodes
 | ||||
| 	struct XmlNode *next; | ||||
| } XmlNode; | ||||
| 
 | ||||
| enum XmlTokenType | ||||
| { | ||||
| 	xmltokentype_unknown, | ||||
| 	xmltokentype_openArrow, | ||||
| 	xmltokentype_closeArrow, | ||||
| 	xmltokentype_name, | ||||
| 	xmltokentype_value, | ||||
| 	xmltokentype_equals, | ||||
| 	xmltokentype_quotes, | ||||
| 	xmltokentype_backslash, | ||||
| 	xmltokentype_count, | ||||
| }; | ||||
| 
 | ||||
| typedef struct XmlToken | ||||
| { | ||||
| 	// TODO(doyle): Dynamic size string in tokens maybe.
 | ||||
| 	enum XmlTokenType type; | ||||
| 	char string[128]; | ||||
| 	i32 len; | ||||
| } XmlToken; | ||||
| 
 | ||||
| #define DEBUG_RECURSIVE_PRINT_XML_TREE(sig) debug_recursivePrintXmlTree(sig, 1) | ||||
| INTERNAL void debug_recursivePrintXmlTree(XmlNode *root, i32 levelsDeep) | ||||
| { | ||||
| 	if (!root) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		for (i32 i = 0; i < levelsDeep; i++) | ||||
| 		{ | ||||
| 			printf("-"); | ||||
| 		} | ||||
| 
 | ||||
| 		printf("%s ", root->name); | ||||
| 
 | ||||
| 		XmlAttribute *attribute = &root->attribute; | ||||
| 		printf("| %s = %s", attribute->name, attribute->value); | ||||
| 
 | ||||
| 		while (attribute->next) | ||||
| 		{ | ||||
| 			attribute = attribute->next; | ||||
| 			printf("| %s = %04s", attribute->name, attribute->value); | ||||
| 		} | ||||
| 		printf("\n"); | ||||
| 
 | ||||
| 		debug_recursivePrintXmlTree(root->child, levelsDeep+1); | ||||
| 		debug_recursivePrintXmlTree(root->next, levelsDeep); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| INTERNAL XmlToken *tokeniseXmlBuffer(MemoryArena *arena, char *buffer, | ||||
|                                      i32 bufferSize, int *numTokens) | ||||
| { | ||||
| 	/*
 | ||||
| 	 ********************** | ||||
| 	 * Tokenise XML Buffer | ||||
| 	 ********************** | ||||
| 	 */ | ||||
| 	XmlToken *xmlTokens = PLATFORM_MEM_ALLOC(arena, 8192, XmlToken); | ||||
| 	i32 tokenIndex      = 0; | ||||
| 	for (i32 i = 0; i < bufferSize; i++) | ||||
| 	{ | ||||
| 		char c = (CAST(char *) buffer)[i]; | ||||
| 		switch (c) | ||||
| 		{ | ||||
| 		case '<': | ||||
| 		case '>': | ||||
| 		case '=': | ||||
| 		case '/': | ||||
| 		{ | ||||
| 
 | ||||
| 			enum XmlTokenType type = xmltokentype_unknown; | ||||
| 			if (c == '<') | ||||
| 			{ | ||||
| 				type = xmltokentype_openArrow; | ||||
| 			} | ||||
| 			else if (c == '>') | ||||
| 			{ | ||||
| 				type = xmltokentype_closeArrow; | ||||
| 			} | ||||
| 			else if (c == '=') | ||||
| 			{ | ||||
| 				type = xmltokentype_equals; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				type = xmltokentype_backslash; | ||||
| 			} | ||||
| 
 | ||||
| 			xmlTokens[tokenIndex].type = type; | ||||
| 			xmlTokens[tokenIndex].len  = 1; | ||||
| 			tokenIndex++; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		case '"': | ||||
| 		{ | ||||
| 			xmlTokens[tokenIndex].type = xmltokentype_value; | ||||
| 			for (i32 j = i + 1; j < bufferSize; j++) | ||||
| 			{ | ||||
| 				char c = (CAST(char *) buffer)[j]; | ||||
| 
 | ||||
| 				if (c == '"') | ||||
| 				{ | ||||
| 					break; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					xmlTokens[tokenIndex].string[xmlTokens[tokenIndex].len++] = | ||||
| 					    c; | ||||
| #ifdef DENGINE_DEBUG | ||||
| 					ASSERT(xmlTokens[tokenIndex].len < | ||||
| 					       ARRAY_COUNT(xmlTokens[tokenIndex].string)); | ||||
| #endif | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			// NOTE(doyle): +1 to skip the closing quotes
 | ||||
| 			i += (xmlTokens[tokenIndex].len + 1); | ||||
| 			tokenIndex++; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		default: | ||||
| 		{ | ||||
| 			if ((c >= 'a' && c <= 'z') || c >= 'A' && c <= 'Z') | ||||
| 			{ | ||||
| 				xmlTokens[tokenIndex].type = xmltokentype_name; | ||||
| 				for (i32 j = i; j < bufferSize; j++) | ||||
| 				{ | ||||
| 					char c = (CAST(char *) buffer)[j]; | ||||
| 
 | ||||
| 					if (c == ' ' || c == '=' || c == '>' || c == '<' || | ||||
| 					    c == '\\') | ||||
| 					{ | ||||
| 						break; | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						xmlTokens[tokenIndex] | ||||
| 						    .string[xmlTokens[tokenIndex].len++] = c; | ||||
| #ifdef DENGINE_DEBUG | ||||
| 						ASSERT(xmlTokens[tokenIndex].len < | ||||
| 						       ARRAY_COUNT(xmlTokens[tokenIndex].string)); | ||||
| #endif | ||||
| 					} | ||||
| 				} | ||||
| 				i += xmlTokens[tokenIndex].len; | ||||
| 				tokenIndex++; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// TODO(doyle): Dynamic token allocation
 | ||||
| 	*numTokens = 8192; | ||||
| 	return xmlTokens; | ||||
| } | ||||
| 
 | ||||
| INTERNAL XmlNode *buildXmlTree(MemoryArena *arena, XmlToken *xmlTokens, | ||||
|                                i32 numTokens) | ||||
| { | ||||
| 	XmlNode *root = PLATFORM_MEM_ALLOC(arena, 1, XmlNode); | ||||
| 	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 < numTokens; i++) | ||||
| 	{ | ||||
| 		XmlToken *token = &xmlTokens[i]; | ||||
| 
 | ||||
| 		switch (token->type) | ||||
| 		{ | ||||
| 
 | ||||
| 		case xmltokentype_openArrow: | ||||
| 		{ | ||||
| 			/* Open arrows indicate closing parent node or new node name */ | ||||
| 			XmlToken *nextToken = &xmlTokens[++i]; | ||||
| 			if (nextToken->type == xmltokentype_backslash) | ||||
| 			{ | ||||
| 				nextToken = &xmlTokens[++i]; | ||||
| 				if (common_strcmp(nextToken->string, node->parent->name) == 0) | ||||
| 				{ | ||||
| 					node->parent->isClosed = TRUE; | ||||
| 
 | ||||
| 					if (prevNode) | ||||
| 					{ | ||||
| 						prevNode->next = NULL; | ||||
| 					} | ||||
| 
 | ||||
| 					XmlNode *parent = node->parent; | ||||
| 					PLATFORM_MEM_FREE(arena, node, sizeof(XmlNode)); | ||||
| 
 | ||||
| 					node            = node->parent; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 					DEBUG_LOG( | ||||
| 					    "Closing xml node name does not match parent name"); | ||||
| #endif | ||||
| 				} | ||||
| 			} | ||||
| 			else if (nextToken->type == xmltokentype_name) | ||||
| 			{ | ||||
| 				node->name = nextToken->string; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 				DEBUG_LOG("Unexpected token type after open arrow"); | ||||
| #endif | ||||
| 			} | ||||
| 
 | ||||
| 			token = nextToken; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		case xmltokentype_name: | ||||
| 		{ | ||||
| 			// TODO(doyle): Store latest attribute pointer so we aren't always
 | ||||
| 			// chasing the linked list each iteration. Do the same for children
 | ||||
| 			// node
 | ||||
| 
 | ||||
| 			/* Xml Attributes are a linked list, get first free entry */ | ||||
| 			XmlAttribute *attribute = &node->attribute; | ||||
| 			if (attribute->init) | ||||
| 			{ | ||||
| 				while (attribute->next) | ||||
| 					attribute = attribute->next; | ||||
| 
 | ||||
| 				attribute->next = PLATFORM_MEM_ALLOC(arena, 1, XmlAttribute); | ||||
| 				attribute = attribute->next; | ||||
| 			} | ||||
| 
 | ||||
| 			/* Just plain text is a node attribute name */ | ||||
| 			attribute->name = token->string; | ||||
| 
 | ||||
| 			/* Followed by the value */ | ||||
| 			token            = &xmlTokens[++i]; | ||||
| 			attribute->value = token->string; | ||||
| 			attribute->init  = TRUE; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		case xmltokentype_closeArrow: | ||||
| 		{ | ||||
| 			XmlToken prevToken = xmlTokens[i - 1]; | ||||
| 			prevNode = node; | ||||
| 
 | ||||
| 			/* Closed node means we can return to parent */ | ||||
| 			if (prevToken.type == xmltokentype_backslash) | ||||
| 			{ | ||||
| 				node->isClosed = TRUE; | ||||
| 				node = node->parent; | ||||
| 			} | ||||
| 			 | ||||
| 			if (!node->isClosed) | ||||
| 			{ | ||||
| 				/* Unclosed node means next fields will be children of node */ | ||||
| 
 | ||||
| 				/* If the first child is free allocate, otherwise we have to
 | ||||
| 				 * iterate through the child's next node(s) */ | ||||
| 				if (!node->child) | ||||
| 				{ | ||||
| 					node->child         = PLATFORM_MEM_ALLOC(arena, 1, XmlNode); | ||||
| 					node->child->parent = node; | ||||
| 					node                = node->child; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					XmlNode *nodeToCheck = node->child; | ||||
| 					while (nodeToCheck->next) | ||||
| 						nodeToCheck = nodeToCheck->next; | ||||
| 
 | ||||
| 					nodeToCheck->next = PLATFORM_MEM_ALLOC(arena, 1, XmlNode); | ||||
| 					nodeToCheck->next->parent = node; | ||||
| 					node                      = nodeToCheck->next; | ||||
| 				} | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		default: | ||||
| 		{ | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return root; | ||||
| } | ||||
| 
 | ||||
| INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena *arena, | ||||
|                                  XmlNode *root) | ||||
| { | ||||
| 	XmlNode *node = root; | ||||
| 	while (node) | ||||
| 	{ | ||||
| 		if (common_strcmp(node->name, "TextureAtlas") == 0) | ||||
| 		{ | ||||
| 			XmlNode *atlasXmlNode = node; | ||||
| 			TexAtlas *atlasEntry  = NULL; | ||||
| 			if (common_strcmp(node->attribute.name, "imagePath") == 0) | ||||
| 			{ | ||||
| 				char *imageName = atlasXmlNode->attribute.value; | ||||
| 				atlasEntry = asset_makeTexAtlas(assetManager, arena, imageName); | ||||
| 
 | ||||
| 				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, arena, imagePath, | ||||
| 				                       imageName); | ||||
| 
 | ||||
| 				atlasEntry->key = PLATFORM_MEM_ALLOC( | ||||
| 				    arena, common_strlen(imageName) + 1, char); | ||||
| 				common_strncpy(atlasEntry->key, imageName, | ||||
| 				               common_strlen(imageName)); | ||||
| 
 | ||||
| 				atlasEntry->tex = asset_getTex(assetManager, imageName); | ||||
| 
 | ||||
| 				XmlNode *atlasChildNode = atlasXmlNode->child; | ||||
| 				while (atlasChildNode) | ||||
| 				{ | ||||
| 					if (common_strcmp(atlasChildNode->name, "SubTexture") == 0) | ||||
| 					{ | ||||
| 						XmlAttribute *subTextureAttrib = | ||||
| 						    &atlasChildNode->attribute; | ||||
| 
 | ||||
| 						AtlasSubTexture newSubTexEntry = {0}; | ||||
| 						while (subTextureAttrib) | ||||
| 						{ | ||||
| 
 | ||||
| 							// 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.key = value; | ||||
| 							} | ||||
| 							else if (common_strcmp(subTextureAttrib->name, | ||||
| 							                       "x") == 0) | ||||
| 							{ | ||||
| 								char *value  = subTextureAttrib->value; | ||||
| 								i32 valueLen = common_strlen(value); | ||||
| 								i32 intValue = common_atoi(value, valueLen); | ||||
| 
 | ||||
| 								newSubTexEntry.rect.pos.x = CAST(f32) intValue; | ||||
| 							} | ||||
| 							else if (common_strcmp(subTextureAttrib->name, | ||||
| 							                       "y") == 0) | ||||
| 							{ | ||||
| 								char *value  = subTextureAttrib->value; | ||||
| 								i32 valueLen = common_strlen(value); | ||||
| 
 | ||||
| 								i32 intValue = common_atoi(value, valueLen); | ||||
| 								newSubTexEntry.rect.pos.y = CAST(f32) intValue; | ||||
| 							} | ||||
| 							else if (common_strcmp(subTextureAttrib->name, | ||||
| 							                       "width") == 0) | ||||
| 							{ | ||||
| 								char *value  = subTextureAttrib->value; | ||||
| 								i32 valueLen = common_strlen(value); | ||||
| 								i32 intValue = common_atoi(value, valueLen); | ||||
| 
 | ||||
| 								newSubTexEntry.rect.size.w = CAST(f32) intValue; | ||||
| 							} | ||||
| 							else if (common_strcmp(subTextureAttrib->name, | ||||
| 							                       "height") == 0) | ||||
| 							{ | ||||
| 								char *value  = subTextureAttrib->value; | ||||
| 								i32 valueLen = common_strlen(value); | ||||
| 								i32 intValue = common_atoi(value, valueLen); | ||||
| 
 | ||||
| 								newSubTexEntry.rect.size.h = CAST(f32) intValue; | ||||
| 							} | ||||
| 							else | ||||
| 							{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 								DEBUG_LOG( | ||||
| 								    "Unsupported xml attribute in SubTexture"); | ||||
| #endif | ||||
| 							} | ||||
| 							subTextureAttrib = subTextureAttrib->next; | ||||
| 						} | ||||
| 
 | ||||
| 						// TODO(doyle): XML specifies 0,0 top left, we
 | ||||
| 						// prefer 0,0 bottom right, so offset by size since 0,0
 | ||||
| 						// is top left and size creates a bounding box below it
 | ||||
| 						newSubTexEntry.rect.pos.y = | ||||
| 						    1024 - newSubTexEntry.rect.pos.y; | ||||
| 						newSubTexEntry.rect.pos.y -= newSubTexEntry.rect.size.h; | ||||
| 
 | ||||
| #ifdef DENGINE_DEBUG | ||||
| 						ASSERT(newSubTexEntry.key) | ||||
| #endif | ||||
| 
 | ||||
| 						u32 subTexHashIndex = common_murmurHash2( | ||||
| 						    newSubTexEntry.key, | ||||
| 						    common_strlen(newSubTexEntry.key), 0xDEADBEEF); | ||||
| 						subTexHashIndex = | ||||
| 						    subTexHashIndex % ARRAY_COUNT(atlasEntry->subTex); | ||||
| 
 | ||||
| 						// NOTE(doyle): Hash collision
 | ||||
| 						AtlasSubTexture *subTexEntry = | ||||
| 						    &atlasEntry->subTex[subTexHashIndex]; | ||||
| 						if (subTexEntry->key) | ||||
| 						{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 
 | ||||
| 							// NOTE(doyle): Two textures have the same access
 | ||||
| 							// name
 | ||||
| 							ASSERT(common_strcmp(subTexEntry->key, | ||||
| 							                     newSubTexEntry.key) != 0); | ||||
| #endif | ||||
| 							while (subTexEntry->next) | ||||
| 								subTexEntry = subTexEntry->next; | ||||
| 
 | ||||
| 							subTexEntry->next = | ||||
| 							    PLATFORM_MEM_ALLOC(arena, 1, AtlasSubTexture); | ||||
| 							subTexEntry = subTexEntry->next; | ||||
| 						} | ||||
| 
 | ||||
| 						*subTexEntry = newSubTexEntry; | ||||
| 						i32 keyLen   = common_strlen(newSubTexEntry.key); | ||||
| 
 | ||||
| 						subTexEntry->key = | ||||
| 						    PLATFORM_MEM_ALLOC(arena, keyLen + 1, char); | ||||
| 						common_strncpy(subTexEntry->key, newSubTexEntry.key, | ||||
| 						               keyLen); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 						DEBUG_LOG("Unsupported xml node name not parsed"); | ||||
| #endif | ||||
| 					} | ||||
| 
 | ||||
| 					atlasChildNode = atlasChildNode->next; | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 				DEBUG_LOG("Unsupported xml node"); | ||||
| #endif | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 			DEBUG_LOG("Unsupported xml node name not parsed"); | ||||
| #endif | ||||
| 		} | ||||
| 		node = node->next; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| INTERNAL void recursiveFreeXmlTree(MemoryArena *arena, XmlNode *node) | ||||
| { | ||||
| 	if (!node) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} else | ||||
| 	{ | ||||
| 		// NOTE(doyle): First attribute is statically allocated, only if there's
 | ||||
| 		// more attributes do we dynamically allocate
 | ||||
| 		XmlAttribute *attrib = node->attribute.next; | ||||
| 
 | ||||
| 		while (attrib) | ||||
| 		{ | ||||
| 			XmlAttribute *next = attrib->next; | ||||
| 
 | ||||
| 			attrib->name  = NULL; | ||||
| 			attrib->value = NULL; | ||||
| 			PLATFORM_MEM_FREE(arena, attrib, sizeof(XmlAttribute)); | ||||
| 			attrib = next; | ||||
| 		} | ||||
| 
 | ||||
| 		recursiveFreeXmlTree(arena, node->child); | ||||
| 		recursiveFreeXmlTree(arena, node->next); | ||||
| 
 | ||||
| 		node->name     = NULL; | ||||
| 		node->isClosed = FALSE; | ||||
| 		PLATFORM_MEM_FREE(arena, node, sizeof(XmlNode)); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| INTERNAL void freeXmlData(MemoryArena *arena, XmlToken *tokens, i32 numTokens, | ||||
|                           XmlNode *tree) | ||||
| { | ||||
| 	if (tree) recursiveFreeXmlTree(arena, tree); | ||||
| 	if (tokens) PLATFORM_MEM_FREE(arena, tokens, numTokens * sizeof(XmlToken)); | ||||
| } | ||||
| 
 | ||||
| INTERNAL void assetInit(GameState *state) | ||||
| { | ||||
| 	AssetManager *assetManager = &state->assetManager; | ||||
| @ -656,40 +121,51 @@ INTERNAL void assetInit(GameState *state) | ||||
| 	Texture *tex = asset_getAndAllocFreeTexSlot(assetManager, arena, "nullTex"); | ||||
| 	*tex         = texture_gen(1, 1, 4, CAST(u8 *)(&bitmap)); | ||||
| 
 | ||||
| 	//assetManager->texAtlas.type = hashtabletype_textureAtlas;
 | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 ********************************* | ||||
| 	 * Load terrain texture atlas data | ||||
| 	 ********************************* | ||||
| 	 */ | ||||
| 	PlatformFileRead terrainXml = {0}; | ||||
| 	i32 result = platform_readFileToBuffer( | ||||
| 	    arena, "data/textures/WorldTraveller/terrain.xml", &terrainXml); | ||||
| 	 | ||||
| 	if (result) | ||||
| 	{ | ||||
| 		DEBUG_LOG("Failed to read sprite sheet xml"); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		/* Tokenise buffer */ | ||||
| 		i32 numTokens    = 0; | ||||
| 		XmlToken *xmlTokens = tokeniseXmlBuffer(arena, terrainXml.buffer, | ||||
| 		                                        terrainXml.size, &numTokens); | ||||
| 		result = asset_loadXmlFile(assetManager, arena, &terrainXml); | ||||
| 
 | ||||
| 		/* Build XML tree from tokens */ | ||||
| 		XmlNode *xmlTree = buildXmlTree(arena, xmlTokens, numTokens); | ||||
| 		if (!result) | ||||
| 		{ | ||||
| 			TexAtlas *terrainAtlas = | ||||
| 			    asset_getTexAtlas(assetManager, "terrain.png"); | ||||
| 
 | ||||
| 		/* Parse XML tree to game structures */ | ||||
| 		parseXmlTreeToGame(assetManager, arena, xmlTree); | ||||
| 			i32 numSubTextures    = 1; | ||||
| 			f32 duration          = 1.0f; | ||||
| 			char *grassTerrain[1] = {"grass.png"}; | ||||
| 			asset_addAnimation(assetManager, arena, "terrainGrass", | ||||
| 			                   terrainAtlas, grassTerrain, numSubTextures, | ||||
| 			                   duration); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 			DEBUG_LOG("Failed to load terrain sprite xml data"); | ||||
| #endif | ||||
| 		} | ||||
| 
 | ||||
| 		/* Free data */ | ||||
| 		freeXmlData(arena, xmlTokens, numTokens, xmlTree); | ||||
| 		platform_closeFileRead(arena, &terrainXml); | ||||
| 
 | ||||
| 		TexAtlas *terrainAtlas = asset_getTexAtlas(assetManager, "terrain.png"); | ||||
| 
 | ||||
| 		i32 numSubTextures = 1; | ||||
| 		f32 duration = 1.0f; | ||||
| 		char *grassTerrain[1] = {"grass.png"}; | ||||
| 		asset_addAnimation(assetManager, arena, "terrainGrass", terrainAtlas, | ||||
| 		                   grassTerrain, numSubTextures, duration); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 ********************************* | ||||
| 	 * Load Claude texture atlas data | ||||
| 	 ********************************* | ||||
| 	 */ | ||||
| 	PlatformFileRead claudeXml = {0}; | ||||
| 	result = platform_readFileToBuffer( | ||||
| 	    arena, "data/textures/WorldTraveller/ClaudeSprite.xml", &claudeXml); | ||||
| @ -700,74 +176,78 @@ INTERNAL void assetInit(GameState *state) | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		/* Tokenise buffer */ | ||||
| 		i32 numTokens    = 0; | ||||
| 		XmlToken *xmlTokens = tokeniseXmlBuffer(arena, claudeXml.buffer, | ||||
| 		                                        claudeXml.size, &numTokens); | ||||
| 		result = asset_loadXmlFile(assetManager, arena, &claudeXml); | ||||
| 
 | ||||
| 		/* Build XML tree from tokens */ | ||||
| 		XmlNode *xmlTree = buildXmlTree(arena, xmlTokens, numTokens); | ||||
| 		if (!result) | ||||
| 		{ | ||||
| 			TexAtlas *claudeAtlas = | ||||
| 			    asset_getTexAtlas(assetManager, "ClaudeSprite.png"); | ||||
| 
 | ||||
| 		/* Parse XML tree to game structures */ | ||||
| 		parseXmlTreeToGame(assetManager, arena, xmlTree); | ||||
| 			char *claudeIdle[1] = {"ClaudeSprite_Walk_Left_01"}; | ||||
| 			f32 duration        = 1.0f; | ||||
| 			i32 numRects        = ARRAY_COUNT(claudeIdle); | ||||
| 			asset_addAnimation(assetManager, arena, "claudeIdle", claudeAtlas, | ||||
| 			                   claudeIdle, numRects, duration); | ||||
| 
 | ||||
| 			// Run animation
 | ||||
| 			char *claudeRun[6] = { | ||||
| 			    "ClaudeSprite_Run_Left_01", "ClaudeSprite_Run_Left_02", | ||||
| 			    "ClaudeSprite_Run_Left_03", "ClaudeSprite_Run_Left_04", | ||||
| 			    "ClaudeSprite_Run_Left_05", "ClaudeSprite_Run_Left_06"}; | ||||
| 			duration = 0.1f; | ||||
| 			numRects = ARRAY_COUNT(claudeRun); | ||||
| 			asset_addAnimation(assetManager, arena, "claudeRun", claudeAtlas, | ||||
| 			                   claudeRun, numRects, duration); | ||||
| 
 | ||||
| 			// Battle Idle animation
 | ||||
| 			char *claudeBattleIdle[3] = {"ClaudeSprite_BattleIdle_Left_01", | ||||
| 			                             "ClaudeSprite_BattleIdle_Left_02", | ||||
| 			                             "ClaudeSprite_BattleIdle_Left_03"}; | ||||
| 			numRects = ARRAY_COUNT(claudeBattleIdle); | ||||
| 			duration = 0.2f; | ||||
| 			asset_addAnimation(assetManager, arena, "claudeBattleIdle", | ||||
| 			                   claudeAtlas, claudeBattleIdle, numRects, | ||||
| 			                   duration); | ||||
| 
 | ||||
| 			// Attack Left animation
 | ||||
| 			char *claudeAttack[6] = { | ||||
| 			    "ClaudeSprite_Attack_Left_01", "ClaudeSprite_Attack_Left_02", | ||||
| 			    "ClaudeSprite_Attack_Left_03", "ClaudeSprite_Attack_Left_04", | ||||
| 			    "ClaudeSprite_Attack_Left_05", "ClaudeSprite_Attack_Left_06"}; | ||||
| 			numRects = ARRAY_COUNT(claudeAttack); | ||||
| 			duration = 0.1f; | ||||
| 			asset_addAnimation(assetManager, arena, "claudeAttack", claudeAtlas, | ||||
| 			                   claudeAttack, numRects, duration); | ||||
| 
 | ||||
| 			//  Victory animation
 | ||||
| 			char *claudeVictory[8] = {"ClaudeSprite_Battle_Victory_01", | ||||
| 			                          "ClaudeSprite_Battle_Victory_02", | ||||
| 			                          "ClaudeSprite_Battle_Victory_03", | ||||
| 			                          "ClaudeSprite_Battle_Victory_04", | ||||
| 			                          "ClaudeSprite_Battle_Victory_05", | ||||
| 			                          "ClaudeSprite_Battle_Victory_06", | ||||
| 			                          "ClaudeSprite_Battle_Victory_07", | ||||
| 			                          "ClaudeSprite_Battle_Victory_08"}; | ||||
| 			numRects = ARRAY_COUNT(claudeVictory); | ||||
| 			duration = 0.1f; | ||||
| 			asset_addAnimation(assetManager, arena, "claudeVictory", | ||||
| 			                   claudeAtlas, claudeVictory, numRects, duration); | ||||
| 
 | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| #ifdef DENGINE_DEBUG | ||||
| 			DEBUG_LOG("Failed to load claude sprite xml data"); | ||||
| #endif | ||||
| 		} | ||||
| 
 | ||||
| 		/* Free data */ | ||||
| 		freeXmlData(arena, xmlTokens, numTokens, xmlTree); | ||||
| 		platform_closeFileRead(arena, &claudeXml); | ||||
| 	} | ||||
| 
 | ||||
| 		TexAtlas *claudeAtlas = | ||||
| 		    asset_getTexAtlas(assetManager, "ClaudeSprite.png"); | ||||
| 
 | ||||
| 		char *claudeIdle[1] = {"ClaudeSprite_Walk_Left_01"}; | ||||
| 		f32 duration        = 1.0f; | ||||
| 		i32 numRects        = ARRAY_COUNT(claudeIdle); | ||||
| 		asset_addAnimation(assetManager, arena, "claudeIdle", claudeAtlas, | ||||
| 		                   claudeIdle, numRects, duration); | ||||
| 
 | ||||
| 		// Run animation
 | ||||
| 		char *claudeRun[6] = { | ||||
| 		    "ClaudeSprite_Run_Left_01", "ClaudeSprite_Run_Left_02", | ||||
| 		    "ClaudeSprite_Run_Left_03", "ClaudeSprite_Run_Left_04", | ||||
| 		    "ClaudeSprite_Run_Left_05", "ClaudeSprite_Run_Left_06"}; | ||||
| 		duration = 0.1f; | ||||
| 		numRects = ARRAY_COUNT(claudeRun); | ||||
| 		asset_addAnimation(assetManager, arena, "claudeRun", claudeAtlas, | ||||
| 		                   claudeRun, numRects, duration); | ||||
| 
 | ||||
| 		// Battle Idle animation
 | ||||
| 		char *claudeBattleIdle[3] = {"ClaudeSprite_BattleIdle_Left_01", | ||||
| 		                             "ClaudeSprite_BattleIdle_Left_02", | ||||
| 		                             "ClaudeSprite_BattleIdle_Left_03"}; | ||||
| 		numRects = ARRAY_COUNT(claudeBattleIdle); | ||||
| 		duration = 0.2f; | ||||
| 		asset_addAnimation(assetManager, arena, "claudeBattleIdle", claudeAtlas, | ||||
| 		                   claudeBattleIdle, numRects, duration); | ||||
| 
 | ||||
| 		// Attack Left animation
 | ||||
| 		char *claudeAttack[6] = { | ||||
| 		    "ClaudeSprite_Attack_Left_01", "ClaudeSprite_Attack_Left_02", | ||||
| 		    "ClaudeSprite_Attack_Left_03", "ClaudeSprite_Attack_Left_04", | ||||
| 		    "ClaudeSprite_Attack_Left_05", "ClaudeSprite_Attack_Left_06"}; | ||||
| 		numRects = ARRAY_COUNT(claudeAttack); | ||||
| 		duration = 0.1f; | ||||
| 		asset_addAnimation(assetManager, arena, "claudeAttack", claudeAtlas, | ||||
| 		                   claudeAttack, numRects, duration); | ||||
| 
 | ||||
| 		//  Victory animation
 | ||||
| 		char *claudeVictory[8] = { | ||||
| 		    "ClaudeSprite_Battle_Victory_01", "ClaudeSprite_Battle_Victory_02", | ||||
| 		    "ClaudeSprite_Battle_Victory_03", "ClaudeSprite_Battle_Victory_04", | ||||
| 		    "ClaudeSprite_Battle_Victory_05", "ClaudeSprite_Battle_Victory_06", | ||||
| 		    "ClaudeSprite_Battle_Victory_07", "ClaudeSprite_Battle_Victory_08"}; | ||||
| 		numRects = ARRAY_COUNT(claudeVictory); | ||||
| 		duration = 0.1f; | ||||
| 		asset_addAnimation(assetManager, arena, "claudeVictory", claudeAtlas, | ||||
| 		                   claudeVictory, numRects, duration); | ||||
| 
 | ||||
| #ifdef DENGINE_DEBUG | ||||
| 		DEBUG_LOG("Animations created"); | ||||
| 	DEBUG_LOG("Animations created"); | ||||
| #endif | ||||
| 	} | ||||
| 
 | ||||
| 	/* Load shaders */ | ||||
| 	asset_loadShaderFiles(assetManager, arena, "data/shaders/sprite.vert.glsl", | ||||
| @ -981,30 +461,7 @@ INTERNAL void unitTest(MemoryArena *arena) | ||||
| 	ASSERT(common_atoi(" 32", common_strlen(" 32")) == -1); | ||||
| 	ASSERT(common_atoi("+32", common_strlen("+32")) == 32); | ||||
| 	ASSERT(common_atoi("+ 32", common_strlen("+ 32")) == 0); | ||||
| 
 | ||||
| 
 | ||||
| 	PlatformFileRead xmlFileRead = {0}; | ||||
| 	i32 result = platform_readFileToBuffer( | ||||
| 	    arena, "data/textures/WorldTraveller/ClaudeSprite.xml", &xmlFileRead); | ||||
| 	if (result) | ||||
| 	{ | ||||
| 		DEBUG_LOG( | ||||
| 		    "unitTest() error: Could not load XML file for memory free test"); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		/* Tokenise buffer */ | ||||
| 		i32 memBefore = arena->bytesAllocated; | ||||
| 		i32 numTokens    = 0; | ||||
| 		XmlToken *xmlTokens = tokeniseXmlBuffer(arena, xmlFileRead.buffer, | ||||
| 		                                        xmlFileRead.size, &numTokens); | ||||
| 		/* Build XML tree from tokens */ | ||||
| 		XmlNode *xmlTree = buildXmlTree(arena, xmlTokens, numTokens); | ||||
| 		freeXmlData(arena, xmlTokens, numTokens, xmlTree); | ||||
| 		i32 memAfter = arena->bytesAllocated; | ||||
| 
 | ||||
| 		ASSERT(memBefore == memAfter); | ||||
| 	} | ||||
| 	asset_unitTest(arena); | ||||
| } | ||||
| 
 | ||||
| // TODO(doyle): Remove and implement own random generator!
 | ||||
| @ -2172,7 +1629,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) | ||||
| 	TexAtlas *heroAtlas = | ||||
| 	    asset_getTexAtlas(assetManager, "ClaudeSprite.png"); | ||||
| 	Rect heroAvatarRect = | ||||
| 	    asset_getAtlasSubTexRect(heroAtlas, "ClaudeSprite_Avatar_01"); | ||||
| 	    asset_getSubTexRect(heroAtlas, "ClaudeSprite_Avatar_01"); | ||||
| 	v2 heroAvatarP = | ||||
| 	    V2(10.0f, (renderer->size.h * 0.5f) - (0.5f * heroAvatarRect.size.h)); | ||||
| 
 | ||||
|  | ||||
| @ -7,59 +7,94 @@ | ||||
| 
 | ||||
| /* Forward declaration */ | ||||
| typedef struct MemoryArena MemoryArena; | ||||
| typedef struct PlatformFileRead PlatformFileRead; | ||||
| 
 | ||||
| enum HashTableType | ||||
| { | ||||
| 	hashtabletype_unknown, | ||||
| 	hashtabletype_texture, | ||||
| 	hashtabletype_textureAtlas, | ||||
| 	hashtabletype_animation, | ||||
| 	hashtabletype_count, | ||||
| }; | ||||
| 
 | ||||
| typedef struct HashTableEntry | ||||
| { | ||||
| 	void *data; | ||||
| 	char *key; | ||||
| 
 | ||||
| 	void *next; | ||||
| } HashTableEntry; | ||||
| 
 | ||||
| typedef struct HashTable | ||||
| { | ||||
| 	HashTableEntry *entries; | ||||
| 	i32 size; | ||||
| 	enum HashTableType type; | ||||
| } HashTable; | ||||
| 
 | ||||
| typedef struct AssetManager | ||||
| { | ||||
| 	/* Hash Tables */ | ||||
| 	TexAtlas texAtlas[8]; | ||||
| 	Texture textures[32]; | ||||
| 	Shader shaders[32]; | ||||
| 	Animation anims[1024]; | ||||
| 
 | ||||
| 	/* Primitive Array */ | ||||
| 	Shader shaders[32]; | ||||
| 	AudioVorbis audio[32]; | ||||
| 	Font font; | ||||
| 
 | ||||
| 	TexAtlas texAtlas[8]; | ||||
| } AssetManager; | ||||
| 
 | ||||
| #define MAX_TEXTURE_SIZE 1024 | ||||
| 
 | ||||
| Rect asset_getAtlasSubTexRect(TexAtlas *atlas, char *key); | ||||
| 
 | ||||
| AudioVorbis *asset_getVorbis(AssetManager *assetManager, | ||||
|                              const enum AudioList type); | ||||
| 
 | ||||
| /*
 | ||||
|  ********************************* | ||||
|  * Texture Asset Managing | ||||
|  ********************************* | ||||
|  */ | ||||
| Rect asset_getSubTexRect(TexAtlas *atlas, char *key); | ||||
| Texture *asset_getTex(AssetManager *const assetManager, const char *const key); | ||||
| 
 | ||||
| TexAtlas *asset_makeTexAtlas(AssetManager *const assetManager, | ||||
|                              MemoryArena *arena, const char *const key); | ||||
| 
 | ||||
| Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type); | ||||
| 
 | ||||
| TexAtlas *asset_getTexAtlas(AssetManager *const assetManager, | ||||
|                             const char *const key); | ||||
| 
 | ||||
| Texture *asset_getAndAllocFreeTexSlot(AssetManager *assetManager, | ||||
|                                       MemoryArena *arena, | ||||
|                                       const char *const key); | ||||
| 
 | ||||
| Animation *asset_getAnim(AssetManager *assetManager, char *key); | ||||
| 
 | ||||
| const i32 asset_loadVorbis(AssetManager *assetManager, MemoryArena *arena, | ||||
|                            const char *const path, const enum AudioList type); | ||||
| const i32 asset_loadTextureImage(AssetManager *assetManager, MemoryArena *arena, | ||||
|                                  const char *const path, | ||||
|                                  const char *const key); | ||||
| 
 | ||||
| /*
 | ||||
|  ********************************* | ||||
|  * Animation Asset Managing | ||||
|  ********************************* | ||||
|  */ | ||||
| void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena, | ||||
|                         char *animName, TexAtlas *atlas, char **subTextureNames, | ||||
|                         i32 numSubTextures, f32 frameDuration); | ||||
| Animation *asset_getAnim(AssetManager *assetManager, char *key); | ||||
| 
 | ||||
| /*
 | ||||
|  ********************************* | ||||
|  * Everything else | ||||
|  ********************************* | ||||
|  */ | ||||
| i32 asset_loadXmlFile(AssetManager *assetManager, MemoryArena *arena, | ||||
|                       PlatformFileRead *fileRead); | ||||
| AudioVorbis *asset_getVorbis(AssetManager *assetManager, | ||||
|                              const enum AudioList type); | ||||
| Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type); | ||||
| const i32 asset_loadVorbis(AssetManager *assetManager, MemoryArena *arena, | ||||
|                            const char *const path, const enum AudioList type); | ||||
| const i32 asset_loadShaderFiles(AssetManager *assetManager, MemoryArena *arena, | ||||
|                                 const char *const vertexPath, | ||||
|                                 const char *const fragmentPath, | ||||
|                                 const enum ShaderList type); | ||||
| 
 | ||||
| const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena, | ||||
|                            const char *filePath); | ||||
| 
 | ||||
| void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena, | ||||
|                         char *animName, TexAtlas *atlas, char **subTextureNames, | ||||
|                         i32 numSubTextures, f32 frameDuration); | ||||
| 
 | ||||
| v2 asset_stringDimInPixels(const Font *const font, const char *const string); | ||||
| void asset_unitTest(MemoryArena *arena); | ||||
| 
 | ||||
| #endif | ||||
|  | ||||
| @ -10,6 +10,32 @@ | ||||
| /* Forward Declaration */ | ||||
| typedef struct Texture Texture; | ||||
| 
 | ||||
| typedef struct XmlAttribute | ||||
| { | ||||
| 	b32 init; | ||||
| 	char *name; | ||||
| 	char *value; | ||||
| 
 | ||||
| 	struct XmlAttribute *next; | ||||
| } XmlAttribute; | ||||
| 
 | ||||
| typedef struct XmlNode | ||||
| { | ||||
| 	char *name; | ||||
| 	XmlAttribute attribute; | ||||
| 
 | ||||
| 	// NOTE(doyle): Track if node has more children
 | ||||
| 	b32 isClosed; | ||||
| 
 | ||||
| 	// NOTE(doyle): Direct child/parent
 | ||||
| 	struct XmlNode *parent; | ||||
| 	struct XmlNode *child; | ||||
| 
 | ||||
| 	// NOTE(doyle): Else all other nodes
 | ||||
| 	struct XmlNode *next; | ||||
| } XmlNode; | ||||
| 
 | ||||
| 
 | ||||
| enum TexList | ||||
| { | ||||
| 	texlist_empty, | ||||
|  | ||||
| @ -18,6 +18,9 @@ enum DebugCallCount | ||||
| 
 | ||||
| void debug_init(MemoryArena *arena, v2 windowSize, Font font); | ||||
| 
 | ||||
| #define DEBUG_RECURSIVE_PRINT_XML_TREE(sig) debug_recursivePrintXmlTree(sig, 1) | ||||
| void debug_recursivePrintXmlTree(XmlNode *root, i32 levelsDeep); | ||||
| 
 | ||||
| void debug_callCountIncrement(enum DebugCallCount id); | ||||
| void debug_clearCallCounter(); | ||||
| #define DEBUG_LOG(string) debug_consoleLog(string, __FILE__, __LINE__); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user