Split up TexturePack into Animations and TexturePack (they end up being about equal lines of code anyways), remove a bit more of unused freetype code

This commit is contained in:
UnknownShadow200 2020-06-26 21:43:02 +10:00
parent f476eee0c1
commit 664428682c
10 changed files with 420 additions and 490 deletions

400
src/Animations.c Normal file
View File

@ -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 */
};

9
src/Animations.h Normal file
View File

@ -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

View File

@ -187,6 +187,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="Animations.h" />
<ClInclude Include="Http.h" />
<ClInclude Include="Audio.h" />
<ClInclude Include="AxisLinesRenderer.h" />
@ -250,6 +251,7 @@
<ClInclude Include="World.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Animations.c" />
<ClCompile Include="Http.c" />
<ClCompile Include="Audio.c" />
<ClCompile Include="Camera.c" />

View File

@ -309,6 +309,9 @@
<ClInclude Include="Graphics.h">
<Filter>Header Files\Graphics</Filter>
</ClInclude>
<ClInclude Include="Animations.h">
<Filter>Header Files\TexturePack</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="String.c">
@ -530,5 +533,8 @@
<ClCompile Include="Graphics.c">
<Filter>Source Files\Graphics</Filter>
</ClCompile>
<ClCompile Include="Animations.c">
<Filter>Source Files\TexturePack</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -35,6 +35,7 @@
#include "Builder.h"
#include "Protocol.h"
#include "Picking.h"
#include "Animations.h"
struct _GameData Game;
int Game_Port;

View File

@ -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-------------------------------------------------------*
*#########################################################################################################################*/

View File

@ -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

View File

@ -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 }

View File

@ -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 )

View File

@ -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 )