diff --git a/src/Animations.c b/src/Animations.c new file mode 100644 index 000000000..b77674fc9 --- /dev/null +++ b/src/Animations.c @@ -0,0 +1,400 @@ +#include "TexturePack.h" +#include "Constants.h" +#include "Stream.h" +#include "World.h" +#include "Graphics.h" +#include "Event.h" +#include "Game.h" +#include "Funcs.h" +#include "Errors.h" +#include "Chat.h" +#include "ExtMath.h" +#include "Options.h" +#include "Logger.h" + +#define LIQUID_ANIM_MAX 64 +#define WATER_TEX_LOC 14 +#define LAVA_TEX_LOC 30 + +#ifndef CC_BUILD_WEB +/* Based off the incredible work from https://dl.dropboxusercontent.com/u/12694594/lava.txt + mirrored at https://github.com/UnknownShadow200/ClassicalSharp/wiki/Minecraft-Classic-lava-animation-algorithm + Water animation originally written by cybertoon, big thanks! +*/ +/*########################################################################################################################* +*-----------------------------------------------------Lava animation------------------------------------------------------* +*#########################################################################################################################*/ +static float L_soupHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX]; +static float L_potHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX]; +static float L_flameHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX]; +static RNGState L_rnd; +static cc_bool L_rndInited; + +static void LavaAnimation_Tick(BitmapCol* ptr, int size) { + int mask = size - 1, shift = Math_Log2(size); + float soupHeat, potHeat, col; + int x, y, i = 0; + + if (!L_rndInited) { + Random_SeedFromCurrentTime(&L_rnd); + L_rndInited = true; + } + + for (y = 0; y < size; y++) { + for (x = 0; x < size; x++) { + /* Calculate the colour at this coordinate in the heatmap */ + + /* Lookup table for (int)(1.2 * sin([ANGLE] * 22.5 * MATH_DEG2RAD)); */ + /* [ANGLE] is integer x/y, so repeats every 16 intervals */ + static cc_int8 sin_adj_table[16] = { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, -1, -1, -1, 0, 0 }; + int xx = x + sin_adj_table[y & 0xF], yy = y + sin_adj_table[x & 0xF]; + + soupHeat = + L_soupHeat[((yy - 1) & mask) << shift | ((xx - 1) & mask)] + + L_soupHeat[((yy - 1) & mask) << shift | (xx & mask)] + + L_soupHeat[((yy - 1) & mask) << shift | ((xx + 1) & mask)] + + + L_soupHeat[(yy & mask) << shift | ((xx - 1) & mask)] + + L_soupHeat[(yy & mask) << shift | (xx & mask)] + + L_soupHeat[(yy & mask) << shift | ((xx + 1) & mask)] + + + L_soupHeat[((yy + 1) & mask) << shift | ((xx - 1) & mask)] + + L_soupHeat[((yy + 1) & mask) << shift | (xx & mask)] + + L_soupHeat[((yy + 1) & mask) << shift | ((xx + 1) & mask)]; + + potHeat = + L_potHeat[i] + /* x , y */ + L_potHeat[y << shift | ((x + 1) & mask)] + /* x + 1, y */ + L_potHeat[((y + 1) & mask) << shift | x] + /* x , y + 1 */ + L_potHeat[((y + 1) & mask) << shift | ((x + 1) & mask)];/* x + 1, y + 1 */ + + L_soupHeat[i] = soupHeat * 0.1f + potHeat * 0.2f; + + L_potHeat[i] += L_flameHeat[i]; + if (L_potHeat[i] < 0.0f) L_potHeat[i] = 0.0f; + + L_flameHeat[i] -= 0.06f * 0.01f; + if (Random_Float(&L_rnd) <= 0.005f) L_flameHeat[i] = 1.5f * 0.01f; + + /* Output the pixel */ + col = 2.0f * L_soupHeat[i]; + Math_Clamp(col, 0.0f, 1.0f); + + *ptr = BitmapCol_Make( + col * 100.0f + 155.0f, + col * col * 255.0f, + col * col * col * col * 128.0f, + 255); + + ptr++; i++; + } + } +} + + +/*########################################################################################################################* +*----------------------------------------------------Water animation------------------------------------------------------* +*#########################################################################################################################*/ +static float W_soupHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX]; +static float W_potHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX]; +static float W_flameHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX]; +static RNGState W_rnd; +static cc_bool W_rndInited; + +static void WaterAnimation_Tick(BitmapCol* ptr, int size) { + int mask = size - 1, shift = Math_Log2(size); + float soupHeat, col; + int x, y, i = 0; + + if (!W_rndInited) { + Random_SeedFromCurrentTime(&W_rnd); + W_rndInited = true; + } + + for (y = 0; y < size; y++) { + for (x = 0; x < size; x++) { + /* Calculate the colour at this coordinate in the heatmap */ + soupHeat = + W_soupHeat[y << shift | ((x - 1) & mask)] + + W_soupHeat[y << shift | x ] + + W_soupHeat[y << shift | ((x + 1) & mask)]; + + W_soupHeat[i] = soupHeat / 3.3f + W_potHeat[i] * 0.8f; + + W_potHeat[i] += W_flameHeat[i]; + if (W_potHeat[i] < 0.0f) W_potHeat[i] = 0.0f; + + W_flameHeat[i] -= 0.1f * 0.05f; + if (Random_Float(&W_rnd) <= 0.05f) W_flameHeat[i] = 0.5f * 0.05f; + + /* Output the pixel */ + col = W_soupHeat[i]; + Math_Clamp(col, 0.0f, 1.0f); + col = col * col; + + *ptr = BitmapCol_Make( + 32.0f + col * 32.0f, + 50.0f + col * 64.0f, + 255, + 146.0f + col * 50.0f); + + ptr++; i++; + } + } +} +#endif + + +/*########################################################################################################################* +*-------------------------------------------------------Animations--------------------------------------------------------* +*#########################################################################################################################*/ +struct AnimationData { + TextureLoc texLoc; /* Tile (not pixel) coordinates in terrain.png */ + cc_uint16 frameX, frameY; /* Top left pixel coordinates of start frame in animatons.png */ + cc_uint16 frameSize; /* Size of each frame in pixel coordinates */ + cc_uint16 state; /* Current animation frame index */ + cc_uint16 statesCount; /* Total number of animation frames */ + cc_uint16 delay; /* Delay in ticks until next frame is drawn */ + cc_uint16 frameDelay; /* Delay between each frame */ +}; + +static Bitmap anims_bmp; +static struct AnimationData anims_list[ATLAS1D_MAX_ATLASES]; +static int anims_count; +static cc_bool anims_validated, useLavaAnim, useWaterAnim, alwaysLavaAnim, alwaysWaterAnim; +#define ANIM_MIN_ARGS 7 + +static void Animations_ReadDescription(struct Stream* stream, const String* path) { + String line; char lineBuffer[STRING_SIZE * 2]; + String parts[ANIM_MIN_ARGS]; + int count; + struct AnimationData data = { 0 }; + cc_uint8 tileX, tileY; + + cc_uint8 buffer[2048]; + struct Stream buffered; + cc_result res; + + String_InitArray(line, lineBuffer); + /* ReadLine reads single byte at a time */ + Stream_ReadonlyBuffered(&buffered, stream, buffer, sizeof(buffer)); + + for (;;) { + res = Stream_ReadLine(&buffered, &line); + if (res == ERR_END_OF_STREAM) break; + if (res) { Logger_Warn2(res, "reading from", path); break; } + + if (!line.length || line.buffer[0] == '#') continue; + count = String_UNSAFE_Split(&line, ' ', parts, ANIM_MIN_ARGS); + if (count < ANIM_MIN_ARGS) { + Chat_Add1("&cNot enough arguments for anim: %s", &line); continue; + } + + if (!Convert_ParseUInt8(&parts[0], &tileX) || tileX >= ATLAS2D_TILES_PER_ROW) { + Chat_Add1("&cInvalid anim tile X coord: %s", &parts[0]); continue; + } + if (!Convert_ParseUInt8(&parts[1], &tileY) || tileY >= ATLAS2D_MAX_ROWS_COUNT) { + Chat_Add1("&cInvalid anim tile Y coord: %s", &parts[1]); continue; + } + if (!Convert_ParseUInt16(&parts[2], &data.frameX)) { + Chat_Add1("&cInvalid anim frame X coord: %s", &parts[2]); continue; + } + if (!Convert_ParseUInt16(&parts[3], &data.frameY)) { + Chat_Add1("&cInvalid anim frame Y coord: %s", &parts[3]); continue; + } + if (!Convert_ParseUInt16(&parts[4], &data.frameSize) || !data.frameSize) { + Chat_Add1("&cInvalid anim frame size: %s", &parts[4]); continue; + } + if (!Convert_ParseUInt16(&parts[5], &data.statesCount)) { + Chat_Add1("&cInvalid anim states count: %s", &parts[5]); continue; + } + if (!Convert_ParseUInt16(&parts[6], &data.frameDelay)) { + Chat_Add1("&cInvalid anim frame delay: %s", &parts[6]); continue; + } + + if (anims_count == Array_Elems(anims_list)) { + Chat_AddRaw("&cCannot show over 512 animations"); return; + } + + data.texLoc = tileX + (tileY * ATLAS2D_TILES_PER_ROW); + anims_list[anims_count++] = data; + } +} + +#define ANIMS_FAST_SIZE 64 +static void Animations_Draw(struct AnimationData* data, TextureLoc texLoc, int size) { + int dstX = Atlas1D_Index(texLoc), srcX; + int dstY = Atlas1D_RowId(texLoc) * Atlas2D.TileSize; + GfxResourceID tex; + + cc_uint8 buffer[Bitmap_DataSize(ANIMS_FAST_SIZE, ANIMS_FAST_SIZE)]; + cc_uint8* ptr = buffer; + Bitmap frame; + + /* cannot allocate memory on the stack for very big animation.png frames */ + if (size > ANIMS_FAST_SIZE) { + ptr = (cc_uint8*)Mem_Alloc(size * size, 4, "anim frame"); + } + Bitmap_Init(frame, size, size, ptr); + + if (!data) { +#ifndef CC_BUILD_WEB + if (texLoc == LAVA_TEX_LOC) { + LavaAnimation_Tick((BitmapCol*)frame.Scan0, size); + } else if (texLoc == WATER_TEX_LOC) { + WaterAnimation_Tick((BitmapCol*)frame.Scan0, size); + } +#endif + } else { + srcX = data->frameX + data->state * size; + Bitmap_UNSAFE_CopyBlock(srcX, data->frameY, 0, 0, &anims_bmp, &frame, size); + } + + tex = Atlas1D.TexIds[dstX]; + if (tex) { Gfx_UpdateTexturePart(tex, 0, dstY, &frame, Gfx.Mipmaps); } + if (size > ANIMS_FAST_SIZE) Mem_Free(ptr); +} + +static void Animations_Apply(struct AnimationData* data) { + TextureLoc loc; + if (data->delay) { data->delay--; return; } + + data->state++; + data->state %= data->statesCount; + data->delay = data->frameDelay; + + loc = data->texLoc; +#ifndef CC_BUILD_WEB + if (loc == LAVA_TEX_LOC && useLavaAnim) return; + if (loc == WATER_TEX_LOC && useWaterAnim) return; +#endif + Animations_Draw(data, loc, data->frameSize); +} + +static cc_bool Animations_IsDefaultZip(void) { + String texPack; + cc_bool optExists; + if (World_TextureUrl.length) return false; + + optExists = Options_UNSAFE_Get(OPT_DEFAULT_TEX_PACK, &texPack); + return !optExists || String_CaselessEqualsConst(&texPack, "default.zip"); +} + +static void Animations_Clear(void) { + Mem_Free(anims_bmp.Scan0); + anims_count = 0; + anims_bmp.Scan0 = NULL; + anims_validated = false; +} + +static void Animations_Validate(void) { + struct AnimationData data; + int maxX, maxY, tileX, tileY; + int i, j; + + anims_validated = true; + for (i = 0; i < anims_count; i++) { + data = anims_list[i]; + + maxX = data.frameX + data.frameSize * data.statesCount; + maxY = data.frameY + data.frameSize; + tileX = Atlas2D_TileX(data.texLoc); + tileY = Atlas2D_TileY(data.texLoc); + + if (data.frameSize > Atlas2D.TileSize || tileY >= Atlas2D.RowsCount) { + Chat_Add2("&cAnimation frames for tile (%i, %i) are bigger than the size of a tile in terrain.png", &tileX, &tileY); + } else if (maxX > anims_bmp.Width || maxY > anims_bmp.Height) { + Chat_Add2("&cSome of the animation frames for tile (%i, %i) are at coordinates outside animations.png", &tileX, &tileY); + } else { + /* if user has water/lava animations in their default.zip, disable built-in */ + /* However, 'usewateranim' and 'uselavaanim' files should always disable use */ + /* of custom water/lava animations, even when they exist in animations.png */ + if (data.texLoc == LAVA_TEX_LOC && !alwaysLavaAnim) useLavaAnim = false; + if (data.texLoc == WATER_TEX_LOC && !alwaysWaterAnim) useWaterAnim = false; + continue; + } + + /* Remove this animation from the list */ + for (j = i; j < anims_count - 1; j++) { + anims_list[j] = anims_list[j + 1]; + } + i--; anims_count--; + } +} + +static void Animations_Tick(struct ScheduledTask* task) { + int i, size; + +#ifndef CC_BUILD_WEB + if (useLavaAnim) { + size = min(Atlas2D.TileSize, 64); + Animations_Draw(NULL, LAVA_TEX_LOC, size); + } + if (useWaterAnim) { + size = min(Atlas2D.TileSize, 64); + Animations_Draw(NULL, WATER_TEX_LOC, size); + } +#endif + + if (!anims_count) return; + if (!anims_bmp.Scan0) { + Chat_AddRaw("&cCurrent texture pack specifies it uses animations,"); + Chat_AddRaw("&cbut is missing animations.png"); + anims_count = 0; return; + } + + /* deferred, because when reading animations.txt, might not have read animations.png yet */ + if (!anims_validated) Animations_Validate(); + for (i = 0; i < anims_count; i++) { + Animations_Apply(&anims_list[i]); + } +} + + +/*########################################################################################################################* +*--------------------------------------------------Animations component---------------------------------------------------* +*#########################################################################################################################*/ +static void OnPackChanged(void* obj) { + Animations_Clear(); + useLavaAnim = Animations_IsDefaultZip(); + useWaterAnim = useLavaAnim; + alwaysLavaAnim = false; + alwaysWaterAnim = false; +} + +static void OnFileChanged(void* obj, struct Stream* stream, const String* name) { + cc_result res; + if (String_CaselessEqualsConst(name, "animations.png")) { + res = Png_Decode(&anims_bmp, stream); + if (!res) return; + + Logger_Warn2(res, "decoding", name); + Mem_Free(anims_bmp.Scan0); + anims_bmp.Scan0 = NULL; + } else if (String_CaselessEqualsConst(name, "animations.txt")) { + Animations_ReadDescription(stream, name); + } else if (String_CaselessEqualsConst(name, "uselavaanim")) { + useLavaAnim = true; + alwaysLavaAnim = true; + } else if (String_CaselessEqualsConst(name, "usewateranim")) { + useWaterAnim = true; + alwaysWaterAnim = true; + } +} + +static void Animations_Init(void) { + ScheduledTask_Add(GAME_DEF_TICKS, Animations_Tick); + Event_RegisterVoid(&TextureEvents.PackChanged, NULL, OnPackChanged); + Event_RegisterEntry(&TextureEvents.FileChanged, NULL, OnFileChanged); +} + +static void Animations_Free(void) { + Animations_Clear(); + Event_UnregisterVoid(&TextureEvents.PackChanged, NULL, OnPackChanged); + Event_UnregisterEntry(&TextureEvents.FileChanged, NULL, OnFileChanged); +} + +struct IGameComponent Animations_Component = { + Animations_Init, /* Init */ + Animations_Free /* Free */ +}; diff --git a/src/Animations.h b/src/Animations.h new file mode 100644 index 000000000..71e5413fc --- /dev/null +++ b/src/Animations.h @@ -0,0 +1,9 @@ +#ifndef CC_ANIMATIONS_H +#define CC_ANIMATIONS_H +/* Contains everything relating to texture animations (including default water/lava ones) + Copyright 2014-2020 ClassiCube | Licensed under BSD-3 + */ + +struct IGameComponent; +extern struct IGameComponent Animations_Component; +#endif diff --git a/src/ClassiCube.vcxproj b/src/ClassiCube.vcxproj index 5f706b873..5b819ca8b 100644 --- a/src/ClassiCube.vcxproj +++ b/src/ClassiCube.vcxproj @@ -187,6 +187,7 @@ + @@ -250,6 +251,7 @@ + diff --git a/src/ClassiCube.vcxproj.filters b/src/ClassiCube.vcxproj.filters index 627e60248..7a8c44c58 100644 --- a/src/ClassiCube.vcxproj.filters +++ b/src/ClassiCube.vcxproj.filters @@ -309,6 +309,9 @@ Header Files\Graphics + + Header Files\TexturePack + @@ -530,5 +533,8 @@ Source Files\Graphics + + Source Files\TexturePack + \ No newline at end of file diff --git a/src/Game.c b/src/Game.c index 6b3675f72..4407b0117 100644 --- a/src/Game.c +++ b/src/Game.c @@ -35,6 +35,7 @@ #include "Builder.h" #include "Protocol.h" #include "Picking.h" +#include "Animations.h" struct _GameData Game; int Game_Port; diff --git a/src/TexturePack.c b/src/TexturePack.c index d45ba791b..60d456fa9 100644 --- a/src/TexturePack.c +++ b/src/TexturePack.c @@ -1,6 +1,5 @@ #include "TexturePack.h" #include "Constants.h" -#include "Platform.h" #include "Stream.h" #include "World.h" #include "Graphics.h" @@ -9,403 +8,11 @@ #include "Http.h" #include "Platform.h" #include "Deflate.h" -#include "Stream.h" #include "Funcs.h" -#include "Errors.h" -#include "Chat.h" #include "ExtMath.h" -#include "Chat.h" #include "Options.h" #include "Logger.h" -#define LIQUID_ANIM_MAX 64 -#define WATER_TEX_LOC 14 -#define LAVA_TEX_LOC 30 - -#ifndef CC_BUILD_WEB -/* Based off the incredible work from https://dl.dropboxusercontent.com/u/12694594/lava.txt - mirrored at https://github.com/UnknownShadow200/ClassicalSharp/wiki/Minecraft-Classic-lava-animation-algorithm - Water animation originally written by cybertoon, big thanks! -*/ -/*########################################################################################################################* -*-----------------------------------------------------Lava animation------------------------------------------------------* -*#########################################################################################################################*/ -static float L_soupHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX]; -static float L_potHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX]; -static float L_flameHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX]; -static RNGState L_rnd; -static cc_bool L_rndInited; - -static void LavaAnimation_Tick(BitmapCol* ptr, int size) { - int mask = size - 1, shift = Math_Log2(size); - float soupHeat, potHeat, col; - int x, y, i = 0; - - if (!L_rndInited) { - Random_SeedFromCurrentTime(&L_rnd); - L_rndInited = true; - } - - for (y = 0; y < size; y++) { - for (x = 0; x < size; x++) { - /* Calculate the colour at this coordinate in the heatmap */ - - /* Lookup table for (int)(1.2 * sin([ANGLE] * 22.5 * MATH_DEG2RAD)); */ - /* [ANGLE] is integer x/y, so repeats every 16 intervals */ - static cc_int8 sin_adj_table[16] = { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, -1, -1, -1, 0, 0 }; - int xx = x + sin_adj_table[y & 0xF], yy = y + sin_adj_table[x & 0xF]; - - soupHeat = - L_soupHeat[((yy - 1) & mask) << shift | ((xx - 1) & mask)] + - L_soupHeat[((yy - 1) & mask) << shift | (xx & mask)] + - L_soupHeat[((yy - 1) & mask) << shift | ((xx + 1) & mask)] + - - L_soupHeat[(yy & mask) << shift | ((xx - 1) & mask)] + - L_soupHeat[(yy & mask) << shift | (xx & mask)] + - L_soupHeat[(yy & mask) << shift | ((xx + 1) & mask)] + - - L_soupHeat[((yy + 1) & mask) << shift | ((xx - 1) & mask)] + - L_soupHeat[((yy + 1) & mask) << shift | (xx & mask)] + - L_soupHeat[((yy + 1) & mask) << shift | ((xx + 1) & mask)]; - - potHeat = - L_potHeat[i] + /* x , y */ - L_potHeat[y << shift | ((x + 1) & mask)] + /* x + 1, y */ - L_potHeat[((y + 1) & mask) << shift | x] + /* x , y + 1 */ - L_potHeat[((y + 1) & mask) << shift | ((x + 1) & mask)];/* x + 1, y + 1 */ - - L_soupHeat[i] = soupHeat * 0.1f + potHeat * 0.2f; - - L_potHeat[i] += L_flameHeat[i]; - if (L_potHeat[i] < 0.0f) L_potHeat[i] = 0.0f; - - L_flameHeat[i] -= 0.06f * 0.01f; - if (Random_Float(&L_rnd) <= 0.005f) L_flameHeat[i] = 1.5f * 0.01f; - - /* Output the pixel */ - col = 2.0f * L_soupHeat[i]; - Math_Clamp(col, 0.0f, 1.0f); - - *ptr = BitmapCol_Make( - col * 100.0f + 155.0f, - col * col * 255.0f, - col * col * col * col * 128.0f, - 255); - - ptr++; i++; - } - } -} - - -/*########################################################################################################################* -*----------------------------------------------------Water animation------------------------------------------------------* -*#########################################################################################################################*/ -static float W_soupHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX]; -static float W_potHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX]; -static float W_flameHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX]; -static RNGState W_rnd; -static cc_bool W_rndInited; - -static void WaterAnimation_Tick(BitmapCol* ptr, int size) { - int mask = size - 1, shift = Math_Log2(size); - float soupHeat, col; - int x, y, i = 0; - - if (!W_rndInited) { - Random_SeedFromCurrentTime(&W_rnd); - W_rndInited = true; - } - - for (y = 0; y < size; y++) { - for (x = 0; x < size; x++) { - /* Calculate the colour at this coordinate in the heatmap */ - soupHeat = - W_soupHeat[y << shift | ((x - 1) & mask)] + - W_soupHeat[y << shift | x ] + - W_soupHeat[y << shift | ((x + 1) & mask)]; - - W_soupHeat[i] = soupHeat / 3.3f + W_potHeat[i] * 0.8f; - - W_potHeat[i] += W_flameHeat[i]; - if (W_potHeat[i] < 0.0f) W_potHeat[i] = 0.0f; - - W_flameHeat[i] -= 0.1f * 0.05f; - if (Random_Float(&W_rnd) <= 0.05f) W_flameHeat[i] = 0.5f * 0.05f; - - /* Output the pixel */ - col = W_soupHeat[i]; - Math_Clamp(col, 0.0f, 1.0f); - col = col * col; - - *ptr = BitmapCol_Make( - 32.0f + col * 32.0f, - 50.0f + col * 64.0f, - 255, - 146.0f + col * 50.0f); - - ptr++; i++; - } - } -} -#endif - - -/*########################################################################################################################* -*-------------------------------------------------------Animations--------------------------------------------------------* -*#########################################################################################################################*/ -struct AnimationData { - TextureLoc texLoc; /* Tile (not pixel) coordinates in terrain.png */ - cc_uint16 frameX, frameY; /* Top left pixel coordinates of start frame in animatons.png */ - cc_uint16 frameSize; /* Size of each frame in pixel coordinates */ - cc_uint16 state; /* Current animation frame index */ - cc_uint16 statesCount; /* Total number of animation frames */ - cc_uint16 delay; /* Delay in ticks until next frame is drawn */ - cc_uint16 frameDelay; /* Delay between each frame */ -}; - -static Bitmap anims_bmp; -static struct AnimationData anims_list[ATLAS1D_MAX_ATLASES]; -static int anims_count; -static cc_bool anims_validated, useLavaAnim, useWaterAnim, alwaysLavaAnim, alwaysWaterAnim; -#define ANIM_MIN_ARGS 7 - -static void Animations_ReadDescription(struct Stream* stream, const String* path) { - String line; char lineBuffer[STRING_SIZE * 2]; - String parts[ANIM_MIN_ARGS]; - int count; - struct AnimationData data = { 0 }; - cc_uint8 tileX, tileY; - - cc_uint8 buffer[2048]; - struct Stream buffered; - cc_result res; - - String_InitArray(line, lineBuffer); - /* ReadLine reads single byte at a time */ - Stream_ReadonlyBuffered(&buffered, stream, buffer, sizeof(buffer)); - - for (;;) { - res = Stream_ReadLine(&buffered, &line); - if (res == ERR_END_OF_STREAM) break; - if (res) { Logger_Warn2(res, "reading from", path); break; } - - if (!line.length || line.buffer[0] == '#') continue; - count = String_UNSAFE_Split(&line, ' ', parts, ANIM_MIN_ARGS); - if (count < ANIM_MIN_ARGS) { - Chat_Add1("&cNot enough arguments for anim: %s", &line); continue; - } - - if (!Convert_ParseUInt8(&parts[0], &tileX) || tileX >= ATLAS2D_TILES_PER_ROW) { - Chat_Add1("&cInvalid anim tile X coord: %s", &parts[0]); continue; - } - if (!Convert_ParseUInt8(&parts[1], &tileY) || tileY >= ATLAS2D_MAX_ROWS_COUNT) { - Chat_Add1("&cInvalid anim tile Y coord: %s", &parts[1]); continue; - } - if (!Convert_ParseUInt16(&parts[2], &data.frameX)) { - Chat_Add1("&cInvalid anim frame X coord: %s", &parts[2]); continue; - } - if (!Convert_ParseUInt16(&parts[3], &data.frameY)) { - Chat_Add1("&cInvalid anim frame Y coord: %s", &parts[3]); continue; - } - if (!Convert_ParseUInt16(&parts[4], &data.frameSize) || !data.frameSize) { - Chat_Add1("&cInvalid anim frame size: %s", &parts[4]); continue; - } - if (!Convert_ParseUInt16(&parts[5], &data.statesCount)) { - Chat_Add1("&cInvalid anim states count: %s", &parts[5]); continue; - } - if (!Convert_ParseUInt16(&parts[6], &data.frameDelay)) { - Chat_Add1("&cInvalid anim frame delay: %s", &parts[6]); continue; - } - - if (anims_count == Array_Elems(anims_list)) { - Chat_AddRaw("&cCannot show over 512 animations"); return; - } - - data.texLoc = tileX + (tileY * ATLAS2D_TILES_PER_ROW); - anims_list[anims_count++] = data; - } -} - -#define ANIMS_FAST_SIZE 64 -static void Animations_Draw(struct AnimationData* data, TextureLoc texLoc, int size) { - int dstX = Atlas1D_Index(texLoc), srcX; - int dstY = Atlas1D_RowId(texLoc) * Atlas2D.TileSize; - GfxResourceID tex; - - cc_uint8 buffer[Bitmap_DataSize(ANIMS_FAST_SIZE, ANIMS_FAST_SIZE)]; - cc_uint8* ptr = buffer; - Bitmap frame; - - /* cannot allocate memory on the stack for very big animation.png frames */ - if (size > ANIMS_FAST_SIZE) { - ptr = (cc_uint8*)Mem_Alloc(size * size, 4, "anim frame"); - } - Bitmap_Init(frame, size, size, ptr); - - if (!data) { -#ifndef CC_BUILD_WEB - if (texLoc == LAVA_TEX_LOC) { - LavaAnimation_Tick((BitmapCol*)frame.Scan0, size); - } else if (texLoc == WATER_TEX_LOC) { - WaterAnimation_Tick((BitmapCol*)frame.Scan0, size); - } -#endif - } else { - srcX = data->frameX + data->state * size; - Bitmap_UNSAFE_CopyBlock(srcX, data->frameY, 0, 0, &anims_bmp, &frame, size); - } - - tex = Atlas1D.TexIds[dstX]; - if (tex) { Gfx_UpdateTexturePart(tex, 0, dstY, &frame, Gfx.Mipmaps); } - if (size > ANIMS_FAST_SIZE) Mem_Free(ptr); -} - -static void Animations_Apply(struct AnimationData* data) { - TextureLoc loc; - if (data->delay) { data->delay--; return; } - - data->state++; - data->state %= data->statesCount; - data->delay = data->frameDelay; - - loc = data->texLoc; -#ifndef CC_BUILD_WEB - if (loc == LAVA_TEX_LOC && useLavaAnim) return; - if (loc == WATER_TEX_LOC && useWaterAnim) return; -#endif - Animations_Draw(data, loc, data->frameSize); -} - -static cc_bool Animations_IsDefaultZip(void) { - String texPack; - cc_bool optExists; - if (World_TextureUrl.length) return false; - - optExists = Options_UNSAFE_Get(OPT_DEFAULT_TEX_PACK, &texPack); - return !optExists || String_CaselessEqualsConst(&texPack, "default.zip"); -} - -static void Animations_Clear(void) { - Mem_Free(anims_bmp.Scan0); - anims_count = 0; - anims_bmp.Scan0 = NULL; - anims_validated = false; -} - -static void Animations_Validate(void) { - struct AnimationData data; - int maxX, maxY, tileX, tileY; - int i, j; - - anims_validated = true; - for (i = 0; i < anims_count; i++) { - data = anims_list[i]; - - maxX = data.frameX + data.frameSize * data.statesCount; - maxY = data.frameY + data.frameSize; - tileX = Atlas2D_TileX(data.texLoc); - tileY = Atlas2D_TileY(data.texLoc); - - if (data.frameSize > Atlas2D.TileSize || tileY >= Atlas2D.RowsCount) { - Chat_Add2("&cAnimation frames for tile (%i, %i) are bigger than the size of a tile in terrain.png", &tileX, &tileY); - } else if (maxX > anims_bmp.Width || maxY > anims_bmp.Height) { - Chat_Add2("&cSome of the animation frames for tile (%i, %i) are at coordinates outside animations.png", &tileX, &tileY); - } else { - /* if user has water/lava animations in their default.zip, disable built-in */ - /* However, 'usewateranim' and 'uselavaanim' files should always disable use */ - /* of custom water/lava animations, even when they exist in animations.png */ - if (data.texLoc == LAVA_TEX_LOC && !alwaysLavaAnim) useLavaAnim = false; - if (data.texLoc == WATER_TEX_LOC && !alwaysWaterAnim) useWaterAnim = false; - continue; - } - - /* Remove this animation from the list */ - for (j = i; j < anims_count - 1; j++) { - anims_list[j] = anims_list[j + 1]; - } - i--; anims_count--; - } -} - -static void Animations_Tick(struct ScheduledTask* task) { - int i, size; - -#ifndef CC_BUILD_WEB - if (useLavaAnim) { - size = min(Atlas2D.TileSize, 64); - Animations_Draw(NULL, LAVA_TEX_LOC, size); - } - if (useWaterAnim) { - size = min(Atlas2D.TileSize, 64); - Animations_Draw(NULL, WATER_TEX_LOC, size); - } -#endif - - if (!anims_count) return; - if (!anims_bmp.Scan0) { - Chat_AddRaw("&cCurrent texture pack specifies it uses animations,"); - Chat_AddRaw("&cbut is missing animations.png"); - anims_count = 0; return; - } - - /* deferred, because when reading animations.txt, might not have read animations.png yet */ - if (!anims_validated) Animations_Validate(); - for (i = 0; i < anims_count; i++) { - Animations_Apply(&anims_list[i]); - } -} - - -/*########################################################################################################################* -*--------------------------------------------------Animations component---------------------------------------------------* -*#########################################################################################################################*/ -static void OnPackChanged(void* obj) { - Animations_Clear(); - useLavaAnim = Animations_IsDefaultZip(); - useWaterAnim = useLavaAnim; - alwaysLavaAnim = false; - alwaysWaterAnim = false; -} - -static void OnFileChanged(void* obj, struct Stream* stream, const String* name) { - cc_result res; - if (String_CaselessEqualsConst(name, "animations.png")) { - res = Png_Decode(&anims_bmp, stream); - if (!res) return; - - Logger_Warn2(res, "decoding", name); - Mem_Free(anims_bmp.Scan0); - anims_bmp.Scan0 = NULL; - } else if (String_CaselessEqualsConst(name, "animations.txt")) { - Animations_ReadDescription(stream, name); - } else if (String_CaselessEqualsConst(name, "uselavaanim")) { - useLavaAnim = true; - alwaysLavaAnim = true; - } else if (String_CaselessEqualsConst(name, "usewateranim")) { - useWaterAnim = true; - alwaysWaterAnim = true; - } -} - -static void Animations_Init(void) { - ScheduledTask_Add(GAME_DEF_TICKS, Animations_Tick); - Event_RegisterVoid(&TextureEvents.PackChanged, NULL, OnPackChanged); - Event_RegisterEntry(&TextureEvents.FileChanged, NULL, OnFileChanged); -} - -static void Animations_Free(void) { - Animations_Clear(); - Event_UnregisterVoid(&TextureEvents.PackChanged, NULL, OnPackChanged); - Event_UnregisterEntry(&TextureEvents.FileChanged, NULL, OnFileChanged); -} - -struct IGameComponent Animations_Component = { - Animations_Init, /* Init */ - Animations_Free /* Free */ -}; - - /*########################################################################################################################* *------------------------------------------------------TerrainAtlas-------------------------------------------------------* *#########################################################################################################################*/ diff --git a/src/TexturePack.h b/src/TexturePack.h index f5a94cda9..7f0ca7e48 100644 --- a/src/TexturePack.h +++ b/src/TexturePack.h @@ -6,13 +6,11 @@ - Extracting the textures from a .zip archive - Caching terrain atlases and texture packs to avoid redundant downloads - Terrain atlas (including breaking it down into multiple 1D atlases) - - Texture animations (including default water/lava ones) - Copyright 2014-2020 ClassiCube | Licensed under BSD-3 */ + Copyright 2014-2020 ClassiCube | Licensed under BSD-3 +*/ struct Stream; struct HttpRequest; -struct IGameComponent; -extern struct IGameComponent Animations_Component; /* Number of tiles in each row */ #define ATLAS2D_TILES_PER_ROW 16 diff --git a/src/freetype/afmodule.c b/src/freetype/afmodule.c index ee36b5fcf..d7d8c3cf6 100644 --- a/src/freetype/afmodule.c +++ b/src/freetype/afmodule.c @@ -36,40 +36,6 @@ #define FT_COMPONENT trace_afmodule - static FT_Error - af_property_get_face_globals( FT_Face face, - AF_FaceGlobals* aglobals, - AF_Module module ) - { - FT_Error error = FT_Err_Ok; - AF_FaceGlobals globals; - - - if ( !face ) - return FT_THROW( Invalid_Face_Handle ); - - globals = (AF_FaceGlobals)face->autohint.data; - if ( !globals ) - { - /* trigger computation of the global style data */ - /* in case it hasn't been done yet */ - error = af_face_globals_new( face, &globals, module ); - if ( !error ) - { - face->autohint.data = - (FT_Pointer)globals; - face->autohint.finalizer = - (FT_Generic_Finalizer)af_face_globals_free; - } - } - - if ( !error ) - *aglobals = globals; - - return error; - } - - static const FT_ServiceDescRec af_services[] = { { NULL, NULL } diff --git a/src/freetype/ftstream.c b/src/freetype/ftstream.c index d6202f4aa..50bbe51f9 100644 --- a/src/freetype/ftstream.c +++ b/src/freetype/ftstream.c @@ -374,24 +374,6 @@ } - FT_BASE_DEF( FT_ULong ) - FT_Stream_GetUOffset( FT_Stream stream ) - { - FT_Byte* p; - FT_ULong result; - - - FT_ASSERT( stream && stream->cursor ); - - result = 0; - p = stream->cursor; - if ( p + 2 < stream->limit ) - result = FT_NEXT_UOFF3( p ); - stream->cursor = p; - return result; - } - - FT_BASE_DEF( FT_ULong ) FT_Stream_GetULong( FT_Stream stream ) { @@ -410,24 +392,6 @@ } - FT_BASE_DEF( FT_ULong ) - FT_Stream_GetULongLE( FT_Stream stream ) - { - FT_Byte* p; - FT_ULong result; - - - FT_ASSERT( stream && stream->cursor ); - - result = 0; - p = stream->cursor; - if ( p + 3 < stream->limit ) - result = FT_NEXT_ULONG_LE( p ); - stream->cursor = p; - return result; - } - - FT_BASE_DEF( FT_Char ) FT_Stream_ReadChar( FT_Stream stream, FT_Error* error ) diff --git a/src/freetype/ftstream.h b/src/freetype/ftstream.h index 6a5f6144e..06381437a 100644 --- a/src/freetype/ftstream.h +++ b/src/freetype/ftstream.h @@ -205,10 +205,6 @@ FT_BEGIN_HEADER FT_BYTE_U32( p, 1, 8 ) | \ FT_BYTE_U32( p, 0, 0 ) ) -#define FT_PEEK_OFF3_LE( p ) FT_INT32( FT_BYTE_U32( p, 2, 16 ) | \ - FT_BYTE_U32( p, 1, 8 ) | \ - FT_BYTE_U32( p, 0, 0 ) ) - #define FT_PEEK_UOFF3_LE( p ) FT_UINT32( FT_BYTE_U32( p, 2, 16 ) | \ FT_BYTE_U32( p, 1, 8 ) | \ FT_BYTE_U32( p, 0, 0 ) ) @@ -245,9 +241,6 @@ FT_BEGIN_HEADER #define FT_NEXT_USHORT_LE( buffer ) \ ( (unsigned short)( buffer += 2, FT_PEEK_USHORT_LE( buffer - 2 ) ) ) -#define FT_NEXT_OFF3_LE( buffer ) \ - ( (long)( buffer += 3, FT_PEEK_OFF3_LE( buffer - 3 ) ) ) - #define FT_NEXT_UOFF3_LE( buffer ) \ ( (unsigned long)( buffer += 3, FT_PEEK_UOFF3_LE( buffer - 3 ) ) ) @@ -269,16 +262,12 @@ FT_BEGIN_HEADER #define FT_GET_BYTE() FT_GET_MACRO( BYTE ) #define FT_GET_SHORT() FT_GET_MACRO( SHORT ) #define FT_GET_USHORT() FT_GET_MACRO( USHORT ) -#define FT_GET_OFF3() FT_GET_MACRO( OFF3 ) -#define FT_GET_UOFF3() FT_GET_MACRO( UOFF3 ) #define FT_GET_LONG() FT_GET_MACRO( LONG ) #define FT_GET_ULONG() FT_GET_MACRO( ULONG ) #define FT_GET_TAG4() FT_GET_MACRO( ULONG ) #define FT_GET_SHORT_LE() FT_GET_MACRO( SHORT_LE ) #define FT_GET_USHORT_LE() FT_GET_MACRO( USHORT_LE ) -#define FT_GET_LONG_LE() FT_GET_MACRO( LONG_LE ) -#define FT_GET_ULONG_LE() FT_GET_MACRO( ULONG_LE ) #else #define FT_GET_MACRO( func, type ) ( (type)func( stream ) ) @@ -287,16 +276,12 @@ FT_BEGIN_HEADER #define FT_GET_BYTE() FT_GET_MACRO( FT_Stream_GetChar, FT_Byte ) #define FT_GET_SHORT() FT_GET_MACRO( FT_Stream_GetUShort, FT_Short ) #define FT_GET_USHORT() FT_GET_MACRO( FT_Stream_GetUShort, FT_UShort ) -#define FT_GET_OFF3() FT_GET_MACRO( FT_Stream_GetUOffset, FT_Long ) -#define FT_GET_UOFF3() FT_GET_MACRO( FT_Stream_GetUOffset, FT_ULong ) #define FT_GET_LONG() FT_GET_MACRO( FT_Stream_GetULong, FT_Long ) #define FT_GET_ULONG() FT_GET_MACRO( FT_Stream_GetULong, FT_ULong ) #define FT_GET_TAG4() FT_GET_MACRO( FT_Stream_GetULong, FT_ULong ) #define FT_GET_SHORT_LE() FT_GET_MACRO( FT_Stream_GetUShortLE, FT_Short ) #define FT_GET_USHORT_LE() FT_GET_MACRO( FT_Stream_GetUShortLE, FT_UShort ) -#define FT_GET_LONG_LE() FT_GET_MACRO( FT_Stream_GetULongLE, FT_Long ) -#define FT_GET_ULONG_LE() FT_GET_MACRO( FT_Stream_GetULongLE, FT_ULong ) #endif #define FT_READ_MACRO( func, type, var ) \ @@ -418,10 +403,6 @@ FT_BEGIN_HEADER FT_BASE( FT_UShort ) FT_Stream_GetUShort( FT_Stream stream ); - /* read a 24-bit big-endian unsigned integer from an entered frame */ - FT_BASE( FT_ULong ) - FT_Stream_GetUOffset( FT_Stream stream ); - /* read a 32-bit big-endian unsigned integer from an entered frame */ FT_BASE( FT_ULong ) FT_Stream_GetULong( FT_Stream stream ); @@ -430,10 +411,6 @@ FT_BEGIN_HEADER FT_BASE( FT_UShort ) FT_Stream_GetUShortLE( FT_Stream stream ); - /* read a 32-bit little-endian unsigned integer from an entered frame */ - FT_BASE( FT_ULong ) - FT_Stream_GetULongLE( FT_Stream stream ); - /* read a byte from a stream */ FT_BASE( FT_Char )