diff --git a/src/Game.c b/src/Game.c index 4407b0117..46cb711a8 100644 --- a/src/Game.c +++ b/src/Game.c @@ -91,29 +91,6 @@ int ScheduledTask_Add(double interval, ScheduledTaskCallback callback) { } -cc_bool Game_ChangeTerrainAtlas(Bitmap* atlas) { - static const String terrain = String_FromConst("terrain.png"); - if (!Game_ValidateBitmap(&terrain, atlas)) return false; - - if (atlas->Height < atlas->Width) { - Chat_AddRaw("&cUnable to use terrain.png from the texture pack."); - Chat_AddRaw("&c Its height is less than its width."); - return false; - } - if (atlas->Width < ATLAS2D_TILES_PER_ROW) { - Chat_AddRaw("&cUnable to use terrain.png from the texture pack."); - Chat_AddRaw("&c It must be 16 or more pixels wide."); - return false; - } - if (Gfx.LostContext) return false; - - Atlas_Free(); - Atlas_Update(atlas); - - Event_RaiseVoid(&TextureEvents.AtlasChanged); - return true; -} - void Game_SetViewDistance(int distance) { distance = min(distance, Game_MaxViewDistance); if (distance == Game_ViewDistance) return; @@ -260,22 +237,6 @@ static void HandleOnNewMapLoaded(void* obj) { } } -static void HandleTextureChanged(void* obj, struct Stream* src, const String* name) { - Bitmap bmp; - cc_result res; - - if (String_CaselessEqualsConst(name, "terrain.png")) { - res = Png_Decode(&bmp, src); - - if (res) { - Logger_Warn2(res, "decoding", name); - Mem_Free(bmp.Scan0); - } else if (!Game_ChangeTerrainAtlas(&bmp)) { - Mem_Free(bmp.Scan0); - } - } -} - static void HandleLowVRAMDetected(void* obj) { if (Game_UserViewDistance <= 16) Logger_Abort("Out of video memory!"); Game_UserViewDistance /= 2; @@ -372,16 +333,12 @@ static void Game_Load(void) { Gfx_Init(); LoadOptions(); - Event_RegisterVoid(&WorldEvents.NewMap, NULL, HandleOnNewMap); - Event_RegisterVoid(&WorldEvents.MapLoaded, NULL, HandleOnNewMapLoaded); - Event_RegisterEntry(&TextureEvents.FileChanged, NULL, HandleTextureChanged); - Event_RegisterVoid(&GfxEvents.LowVRAMDetected, NULL, HandleLowVRAMDetected); + Event_RegisterVoid(&WorldEvents.NewMap, NULL, HandleOnNewMap); + Event_RegisterVoid(&WorldEvents.MapLoaded, NULL, HandleOnNewMapLoaded); + Event_RegisterVoid(&GfxEvents.LowVRAMDetected, NULL, HandleLowVRAMDetected); - Event_RegisterVoid(&WindowEvents.Resized, NULL, Game_OnResize); - Event_RegisterVoid(&WindowEvents.Closing, NULL, Game_Free); - - TextureCache_Init(); - /* TODO: Survival vs Creative game mode */ + Event_RegisterVoid(&WindowEvents.Resized, NULL, Game_OnResize); + Event_RegisterVoid(&WindowEvents.Closing, NULL, Game_Free); InputHandler_Init(); Game_AddComponent(&Blocks_Component); @@ -398,6 +355,7 @@ static void Game_Load(void) { Game_AddComponent(&Animations_Component); Game_AddComponent(&Inventory_Component); + Game_AddComponent(&Textures_Component); World_Reset(); Game_AddComponent(&Builder_Component); @@ -602,16 +560,16 @@ static void Game_RenderFrame(double delta) { void Game_Free(void* obj) { struct IGameComponent* comp; - Atlas_Free(); + /* Most components will call OnContextLost in their Free functions */ + /* Set to false so components will always free managed textures too */ Gfx.ManagedTextures = false; - Event_UnregisterVoid(&WorldEvents.NewMap, NULL, HandleOnNewMap); - Event_UnregisterVoid(&WorldEvents.MapLoaded, NULL, HandleOnNewMapLoaded); - Event_UnregisterEntry(&TextureEvents.FileChanged, NULL, HandleTextureChanged); - Event_UnregisterVoid(&GfxEvents.LowVRAMDetected, NULL, HandleLowVRAMDetected); + Event_UnregisterVoid(&WorldEvents.NewMap, NULL, HandleOnNewMap); + Event_UnregisterVoid(&WorldEvents.MapLoaded, NULL, HandleOnNewMapLoaded); + Event_UnregisterVoid(&GfxEvents.LowVRAMDetected, NULL, HandleLowVRAMDetected); - Event_UnregisterVoid(&WindowEvents.Resized, NULL, Game_OnResize); - Event_UnregisterVoid(&WindowEvents.Closing, NULL, Game_Free); + Event_UnregisterVoid(&WindowEvents.Resized, NULL, Game_OnResize); + Event_UnregisterVoid(&WindowEvents.Closing, NULL, Game_Free); for (comp = comps_head; comp; comp = comp->next) { if (comp->Free) comp->Free(); diff --git a/src/Game.h b/src/Game.h index 056697c42..3c305a302 100644 --- a/src/Game.h +++ b/src/Game.h @@ -49,8 +49,6 @@ enum FpsLimitMethod { }; extern const char* const FpsLimit_Names[FPS_LIMIT_COUNT]; -/* Attempts to change the terrain atlas. (bitmap containing textures for all blocks) */ -cc_bool Game_ChangeTerrainAtlas(Bitmap* atlas); void Game_SetViewDistance(int distance); void Game_UserSetViewDistance(int distance); void Game_SetFov(int fov); diff --git a/src/TexturePack.c b/src/TexturePack.c index 60d456fa9..44ec363de 100644 --- a/src/TexturePack.c +++ b/src/TexturePack.c @@ -12,6 +12,7 @@ #include "ExtMath.h" #include "Options.h" #include "Logger.h" +#include "Chat.h" /* TODO avoid this include */ /*########################################################################################################################* *------------------------------------------------------TerrainAtlas-------------------------------------------------------* @@ -71,7 +72,8 @@ static void Atlas_Update1D(void) { Atlas1D.Shift = Math_Log2(Atlas1D.TilesPerAtlas); } -void Atlas_Update(Bitmap* bmp) { +/* Loads the given atlas and converts it into an array of 1D atlases. */ +static void Atlas_Update(Bitmap* bmp) { Atlas2D.Bmp = *bmp; Atlas2D.TileSize = bmp->Width / ATLAS2D_TILES_PER_ROW; Atlas2D.RowsCount = bmp->Height / Atlas2D.TileSize; @@ -108,7 +110,8 @@ GfxResourceID Atlas2D_LoadTile(TextureLoc texLoc) { } } -void Atlas_Free(void) { +/* Frees the atlas and 1D atlas textures */ +static void Atlas_Free(void) { int i; Mem_Free(Atlas2D.Bmp.Scan0); Atlas2D.Bmp.Scan0 = NULL; @@ -118,13 +121,37 @@ void Atlas_Free(void) { } } +cc_bool Atlas_TryChange(Bitmap* atlas) { + static const String terrain = String_FromConst("terrain.png"); + if (!Game_ValidateBitmap(&terrain, atlas)) return false; + + if (atlas->Height < atlas->Width) { + Chat_AddRaw("&cUnable to use terrain.png from the texture pack."); + Chat_AddRaw("&c Its height is less than its width."); + return false; + } + if (atlas->Width < ATLAS2D_TILES_PER_ROW) { + Chat_AddRaw("&cUnable to use terrain.png from the texture pack."); + Chat_AddRaw("&c It must be 16 or more pixels wide."); + return false; + } + + if (Gfx.LostContext) return false; + Atlas_Free(); + Atlas_Update(atlas); + + Event_RaiseVoid(&TextureEvents.AtlasChanged); + return true; +} + /*########################################################################################################################* *------------------------------------------------------TextureCache-------------------------------------------------------* *#########################################################################################################################*/ static struct EntryList acceptedList, deniedList, etagCache, lastModifiedCache; -void TextureCache_Init(void) { +/* Initialises cache state (loading various lists) */ +static void TextureCache_Init(void) { EntryList_Init(&acceptedList, "texturecache/acceptedurls.txt", ' '); EntryList_Init(&deniedList, "texturecache/deniedurls.txt", ' '); EntryList_Init(&etagCache, "texturecache/etags.txt", ' '); @@ -295,7 +322,7 @@ static cc_result TexturePack_ExtractPng(struct Stream* stream) { if (!res) { Event_RaiseVoid(&TextureEvents.PackChanged); - if (Game_ChangeTerrainAtlas(&bmp)) return 0; + if (Atlas_TryChange(&bmp)) return 0; } Mem_Free(bmp.Scan0); @@ -406,3 +433,37 @@ void TexturePack_DownloadAsync(const String* url, const String* id) { } Http_AsyncGetDataEx(url, true, id, &time, &etag, NULL); } + + +/*########################################################################################################################* +*---------------------------------------------------Textures component----------------------------------------------------* +*#########################################################################################################################*/ +static void OnFileChanged(void* obj, struct Stream* stream, const String* name) { + Bitmap bmp; + cc_result res; + + if (!String_CaselessEqualsConst(name, "terrain.png")) return; + res = Png_Decode(&bmp, stream); + + if (res) { + Logger_Warn2(res, "decoding", name); + Mem_Free(bmp.Scan0); + } else if (!Atlas_TryChange(&bmp)) { + Mem_Free(bmp.Scan0); + } +} + +static void Textures_Init(void) { + Event_RegisterEntry(&TextureEvents.FileChanged, NULL, OnFileChanged); + TextureCache_Init(); +} + +static void Textures_Free(void) { + Event_UnregisterEntry(&TextureEvents.FileChanged, NULL, OnFileChanged); + Atlas_Free(); +} + +struct IGameComponent Textures_Component = { + Textures_Init, /* Init */ + Textures_Free /* Free */ +}; diff --git a/src/TexturePack.h b/src/TexturePack.h index 7f0ca7e48..98a033c17 100644 --- a/src/TexturePack.h +++ b/src/TexturePack.h @@ -11,6 +11,8 @@ struct Stream; struct HttpRequest; +struct IGameComponent; +extern struct IGameComponent Textures_Component; /* Number of tiles in each row */ #define ATLAS2D_TILES_PER_ROW 16 @@ -56,20 +58,15 @@ CC_VAR extern struct _Atlas1DData { /* Returns the index of the 1D atlas within the array of 1D atlases that contains the given tile id */ #define Atlas1D_Index(texLoc) ((texLoc) >> Atlas1D.Shift) /* texLoc / Atlas1D_TilesPerAtlas */ -/* Loads the given atlas and converts it into an array of 1D atlases. */ -/* NOTE: Use Game_ChangeTerrainAtlas to change atlas, because that raises TextureEvents.AtlasChanged */ -void Atlas_Update(Bitmap* bmp); /* Loads the given tile into a new separate texture. */ GfxResourceID Atlas2D_LoadTile(TextureLoc texLoc); -/* Frees the atlas and 1D atlas textures. */ -void Atlas_Free(void); +/* Attempts to change the terrain atlas. (bitmap containing textures for all blocks) */ +cc_bool Atlas_TryChange(Bitmap* bmp); /* Returns the UV rectangle of the given tile id in the 1D atlases. */ /* That is, returns U1/U2/V1/V2 coords that make up the tile in a 1D atlas. */ /* index is set to the index of the 1D atlas that the tile is in. */ TextureRec Atlas1D_TexRec(TextureLoc texLoc, int uCount, int* index); -/* Initialises cache state. (e.g. loading accepted/denied lists) */ -void TextureCache_Init(void); /* Whether the given URL is in list of accepted URLs. */ cc_bool TextureCache_HasAccepted(const String* url); /* Whether the given URL is in list of denied URLs. */