diff --git a/ClassicalSharp/Entities/Components/ShadowComponent.cs b/ClassicalSharp/Entities/Components/ShadowComponent.cs index 230fb424f..fc428df5f 100644 --- a/ClassicalSharp/Entities/Components/ShadowComponent.cs +++ b/ClassicalSharp/Entities/Components/ShadowComponent.cs @@ -26,8 +26,9 @@ namespace ClassicalSharp.Entities { ShadowComponent.game = game; ShadowComponent.entity = entity; - EntityShadow mode = game.Entities.ShadowMode; Vector3 Position = entity.Position; + if (Position.Y < 0) return; + float posX = Position.X, posZ = Position.Z; int posY = Math.Min((int)Position.Y, game.World.Height - 1); int index = 0, vb = 0; @@ -39,15 +40,14 @@ namespace ClassicalSharp.Entities { ShadowData* data = stackalloc ShadowData[4]; for (int i = 0; i < 4; i++) { coords[i] = new Vector3I(int.MinValue); - data[i] = new ShadowData(); } - if (mode == EntityShadow.SnapToBlock) { + if (game.Entities.ShadowMode == EntityShadow.SnapToBlock) { vb = game.Graphics.texVb; verts = game.Graphics.texVerts; if (!GetBlocks(coords, ref posCount, posX, posZ, posY, data, ref dataCount)) return; float x1 = Utils.Floor(posX), z1 = Utils.Floor(posZ); - DraqSquareShadow(verts, ref index, data[0].Y, 220, x1, z1); + DrawSquareShadow(verts, ref index, data[0].Y, x1, z1); } else { vb = game.ModelCache.vb; verts = game.ModelCache.vertices; @@ -79,9 +79,9 @@ namespace ClassicalSharp.Entities { } const byte c = 255; // avoids 'ambiguous match' compile errors. - static void DraqSquareShadow(VertexP3fT2fC4b[] verts, ref int index, - float y, byte alpha, float x, float z) { - int col = new FastColour(c, c, c, alpha).Pack(); + static void DrawSquareShadow(VertexP3fT2fC4b[] verts, ref int index, + float y, float x, float z) { + int col = new FastColour(c, c, c, (byte)220).Pack(); TextureRec rec = new TextureRec(63/128f, 63/128f, 1/128f, 1/128f); verts[index++] = new VertexP3fT2fC4b(x, y, z, rec.U1, rec.V1, col); verts[index++] = new VertexP3fT2fC4b(x + 1, y, z, rec.U2, rec.V1, col); @@ -137,7 +137,6 @@ namespace ClassicalSharp.Entities { index = 0; // Check we have not processed this particular block already. - if (Position.Y < 0) return false; for (int i = 0; i < 4; i++) { if (coords[i] == p) return false; data[i] = new ShadowData(); @@ -187,16 +186,16 @@ namespace ClassicalSharp.Entities { } static void CalcAlpha(float playerY, ref ShadowData data) { - float y = data.Y; - if ((playerY - y) <= 6) { - data.A = (byte)(160 - 160 * (playerY - y) / 6); + float height = playerY - data.Y; + if (height <= 6) { + data.A = (byte)(160 - 160 * height / 6); data.Y += 1/64f; return; } data.A = 0; - if ((playerY - y) <= 16) data.Y += 1/64f; - else if ((playerY - y) <= 32) data.Y += 1/16f; - else if ((playerY - y) <= 96) data.Y += 1/8f; + if (height <= 16) data.Y += 1/64f; + else if (height <= 32) data.Y += 1/16f; + else if (height <= 96) data.Y += 1/8f; else data.Y += 1/4f; } diff --git a/ClassicalSharp/Game/Game.Init.cs b/ClassicalSharp/Game/Game.Init.cs index 06241a412..38ecfd2bb 100644 --- a/ClassicalSharp/Game/Game.Init.cs +++ b/ClassicalSharp/Game/Game.Init.cs @@ -210,7 +210,7 @@ namespace ClassicalSharp { TabAutocomplete = Options.GetBool(OptionsKey.TabAutocomplete, false); FontName = Options.Get(OptionsKey.FontName) ?? "Arial"; if (ClassicMode) FontName = "Arial"; - + try { using (Font f = new Font(FontName, 16)) { } } catch (Exception) { diff --git a/src/Client/Chat.c b/src/Client/Chat.c index 695b75a38..111fb4c65 100644 --- a/src/Client/Chat.c +++ b/src/Client/Chat.c @@ -90,7 +90,8 @@ void Chat_OpenLog(DateTime* now) { } Chat_LogStream.Data = NULL; - ErrorHandler_LogError("creating chat log", "Failed to open chat log file after 20 tries, giving up"); + String failedMsg = String_FromConst("Failed to open chat log file after 20 tries, giving up"); + ErrorHandler_Log(&failedMsg); } void Chat_AppendLog(STRING_PURE String* text) { diff --git a/src/Client/Entity.c b/src/Client/Entity.c index 32e76f1ff..db3a0d870 100644 --- a/src/Client/Entity.c +++ b/src/Client/Entity.c @@ -314,8 +314,8 @@ void Entities_Free(void) { Event_UnregisterVoid(&GfxEvents_ContextRecreated, Entities_ContextRecreated); Event_UnregisterVoid(&ChatEvents_FontChanged, Entities_ChatFontChanged); - if (ShadowComponent.shadowTex > 0) { - game.Graphics.DeleteTexture(ref ShadowComponent.shadowTex); + if (ShadowComponent_ShadowTex != NULL) { + Gfx_DeleteTexture(&ShadowComponent_ShadowTex); } } @@ -347,7 +347,7 @@ EntityID Entities_GetCloset(Entity* src) { void Entities_DrawShadows(void) { if (Entities_ShadowMode == SHADOW_MODE_NONE) return; - ShadowComponent.boundShadowTex = false; + ShadowComponent_BoundShadowTex = false; Gfx_SetAlphaArgBlend(true); Gfx_SetDepthWrite(false); @@ -355,13 +355,13 @@ void Entities_DrawShadows(void) { Gfx_SetTexturing(true); Gfx_SetBatchFormat(VERTEX_FORMAT_P3FT2FC4B); - ShadowComponent.Draw(Entities_List[ENTITIES_SELF_ID]); + ShadowComponent_Draw(Entities_List[ENTITIES_SELF_ID]); if (Entities_ShadowMode == SHADOW_MODE_CIRCLE_ALL) { UInt32 i; for (i = 0; i < ENTITIES_MAX_COUNT; i++) { if (Entities_List[i] == NULL) continue; Player p = List[i] as Player; - if (p != null) ShadowComponent.Draw(p); + if (p != null) ShadowComponent_Draw(p); } } diff --git a/src/Client/EntityComponents.c b/src/Client/EntityComponents.c index c830c84a0..91bb9441f 100644 --- a/src/Client/EntityComponents.c +++ b/src/Client/EntityComponents.c @@ -8,6 +8,10 @@ #include "Platform.h" #include "Camera.h" #include "Funcs.h" +#include "VertexStructs.h" +#include "GraphicsAPI.h" +#include "GraphicsCommon.h" +#include "ModelCache.h" #define ANIM_MAX_ANGLE (110 * MATH_DEG2RAD) #define ANIM_ARM_MAX (60.0f * MATH_DEG2RAD) @@ -435,4 +439,224 @@ void LocalInterpComp_AdvanceState(InterpComp* interp) { interp->Prev = interp->Next; entity->Position = interp->Next.Pos; InterpComp_AdvanceRotY(interp); -} \ No newline at end of file +} + + +Real32 ShadowComponent_radius = 7.0f; +typedef struct ShadowData_ { Real32 Y; BlockID Block; UInt8 A; } ShadowData; + +bool lequal(Real32 a, Real32 b) { return a < b || Math_AbsF(a - b) < 0.001f; } +void ShadowComponent_DrawCoords(VertexP3fT2fC4b** vertices, Entity* entity, ShadowData* data, Real32 x1, Real32 z1, Real32 x2, Real32 z2) { + Vector3 cen = entity->Position; + if (lequal(x2, x1) || lequal(z2, z1)) return; + Real32 radius = ShadowComponent_radius; + + Real32 u1 = (x1 - cen.X) * 16.0f / (radius * 2) + 0.5f; + Real32 v1 = (z1 - cen.Z) * 16.0f / (radius * 2) + 0.5f; + Real32 u2 = (x2 - cen.X) * 16.0f / (radius * 2) + 0.5f; + Real32 v2 = (z2 - cen.Z) * 16.0f / (radius * 2) + 0.5f; + if (u2 <= 0.0f || v2 <= 0.0f || u1 >= 1.0f || v1 >= 1.0f) return; + + radius /= 16.0f; + x1 = max(x1, cen.X - radius); u1 = max(u1, 0.0f); + z1 = max(z1, cen.Z - radius); v1 = max(v1, 0.0f); + x2 = min(x2, cen.X + radius); u2 = min(u2, 1.0f); + z2 = min(z2, cen.Z + radius); v2 = min(v2, 1.0f); + + PackedCol col = PACKEDCOL_CONST(255, 255, 255, data->A); + VertexP3fT2fC4b* ptr = *vertices; + VertexP3fT2fC4b v; v.Y = data->Y; v.Col = col; + + v.X = x1; v.Z = z1; v.U = u1; v.V = v1; *ptr = v; ptr++; + v.X = x2; v.U = u2; *ptr = v; ptr++; + v.Z = z2; v.V = v2; *ptr = v; ptr++; + v.X = x1; v.U = u1; *ptr = v; ptr++; + + *vertices = ptr; +} + +void ShadowComponent_DrawSquareShadow(VertexP3fT2fC4b** vertices, Real32 y, Real32 x, Real32 z) { + PackedCol col = PACKEDCOL_CONST(255, 255, 255, 220); + TextureRec rec = TextureRec_FromRegion(63.0f / 128.0f, 63.0f / 128.0f, 1.0f / 128.0f, 1.0f / 128.0f); + VertexP3fT2fC4b* ptr = *vertices; + VertexP3fT2fC4b v; v.Y = y; v.Col = col; + + v.X = x; v.Z = z; v.U = rec.U1; v.V = rec.V1; *ptr = v; ptr++; + v.X = x + 1.0f; v.U = rec.U2; *ptr = v; ptr++; + v.Z = z + 1.0f; v.V = rec.V2; *ptr = v; ptr++; + v.X = x; v.U = rec.U1; *ptr = v; ptr++; + + *vertices = ptr; +} + +void ShadowComponent_DrawCircle(VertexP3fT2fC4b** vertices, Entity* entity, ShadowData* data, Real32 x, Real32 z) { + x = (Real32)Math_Floor(x); z = (Real32)Math_Floor(z); + Vector3 min = Block_MinBB[data[0].Block], max = Block_MaxBB[data[0].Block]; + + ShadowComponent_DrawCoords(vertices, entity, &data[0], x + min.X, z + min.Z, x + max.X, z + max.Z); + UInt32 i; + for (i = 1; i < 4; i++) { + if (data[i].Block == BLOCK_AIR) return; + Vector3 nMin = Block_MinBB[data[i].Block], nMax = Block_MaxBB[data[i].Block]; + ShadowComponent_DrawCoords(vertices, entity, &data[i], x + min.X, z + nMin.Z, x + max.X, z + min.Z); + ShadowComponent_DrawCoords(vertices, entity, &data[i], x + min.X, z + max.Z, x + max.X, z + nMax.Z); + + ShadowComponent_DrawCoords(vertices, entity, &data[i], x + nMin.X, z + nMin.Z, x + min.X, z + nMax.Z); + ShadowComponent_DrawCoords(vertices, entity, &data[i], x + max.X, z + nMin.Z, x + nMax.X, z + nMax.Z); + min = nMin; max = nMax; + } +} + +BlockID ShadowComponent_GetBlock(Int32 x, Int32 y, Int32 z) { + if (x < 0 || z < 0 || x >= World_Width || z >= World_Length) { + if (y == WorldEnv_EdgeHeight - 1) + return Block_Draw[WorldEnv_EdgeBlock] == DRAW_GAS ? BLOCK_AIR : BLOCK_BEDROCK; + if (y == WorldEnv_SidesHeight - 1) + return Block_Draw[WorldEnv_SidesBlock] == DRAW_GAS ? BLOCK_AIR : BLOCK_BEDROCK; + return BLOCK_AIR; + } + return World_GetBlock(x, y, z); +} + +void ShadowComponent_CalcAlpha(Real32 playerY, ShadowData* data) { + Real32 height = playerY - data->Y; + if (height <= 6.0f) { + data->A = (UInt8)(160 - 160 * height / 6.0f); + data->Y += 1.0f / 64.0f; return; + } + + data->A = 0; + if (height <= 16.0f) data->Y += 1.0f / 64.0f; + else if (height <= 32.0f) data->Y += 1.0f / 16.0f; + else if (height <= 96.0f) data->Y += 1.0f / 8.0f; + else data->Y += 1.0f / 4.0f; +} + +bool ShadowComponent_GetBlocks(Entity* entity, Vector3I* coords, Real32 x, Real32 z, Int32 posY, ShadowData* data) { + Int32 blockX = Math_Floor(x), blockZ = Math_Floor(z); + Vector3I p = Vector3I_Create3(blockX, 0, blockZ); + + /* Check we have not processed this particular block already */ + UInt32 i, posCount = 0; + ShadowData zeroData = { 0.0f, 0, 0 }; + for (i = 0; i < 4; i++) { + if (Vector3I_Equals(&coords[i], &p)) return false; + if (coords[i].X != Int32_MinValue) posCount++; + data[i] = zeroData; + } + coords[posCount] = p; + + UInt32 count = 0; + ShadowData* cur = data; + Vector3 Position = entity->Position; + + while (posY >= 0 && count < 4) { + BlockID block = ShadowComponent_GetBlock(blockX, posY, blockZ); + posY--; + + UInt8 draw = Block_Draw[block]; + if (draw == DRAW_GAS || draw == DRAW_SPRITE || Block_IsLiquid(block)) continue; + Real32 blockY = posY + 1.0f + Block_MaxBB[block].Y; + if (blockY >= Position.Y + 0.01f) continue; + + cur->Block = block; cur->Y = blockY; + ShadowComponent_CalcAlpha(Position.Y, cur); + count++; cur++; + + /* Check if the casted shadow will continue on further down. */ + if (Block_MinBB[block].X == 0.0f && Block_MaxBB[block].X == 1.0f && + Block_MinBB[block].Z == 0.0f && Block_MaxBB[block].Z == 1.0f) return true; + } + + if (count < 4) { + cur->Block = WorldEnv_EdgeBlock; cur->Y = 0.0f; + ShadowComponent_CalcAlpha(Position.Y, cur); + count++; cur++; + } + return true; +} + +#define size 128 +#define half (size / 2) +void ShadowComponent_MakeTex(void) { + UInt8 pixels[Bitmap_DataSize(size, size)]; + Bitmap bmp; Bitmap_Create(&bmp, size, size, pixels); + + UInt32 inPix = PackedCol_ARGB(0, 0, 0, 200); + UInt32 outPix = PackedCol_ARGB(0, 0, 0, 0); + + UInt32 x, y; + for (y = 0; y < size; y++) { + UInt32* row = Bitmap_GetRow(&bmp, y); + for (x = 0; x < size; x++) { + Real64 dist = + (half - (x + 0.5)) * (half - (x + 0.5)) + + (half - (y + 0.5)) * (half - (y + 0.5)); + row[x] = dist < half * half ? inPix : outPix; + } + } + ShadowComponent_ShadowTex = Gfx_CreateTexture(&bmp, true, false); +} + +void ShadowComponent_Draw(Entity* entity) { + Vector3 Position = entity->Position; + if (Position.Y < 0.0f) return; + + Real32 posX = Position.X, posZ = Position.Z; + Int32 posY = min((Int32)Position.Y, World_MaxY); + GfxResourceID vb; + ShadowComponent_radius = 7.0f * min(entity->ModelScale.Y, 1.0f) * entity->Model->ShadowScale; + + VertexP3fT2fC4b vertices[128]; + Vector3I coords[4]; + ShadowData data[4]; + for (Int32 i = 0; i < 4; i++) { + coords[i] = Vector3I_Create1(Int32_MinValue); + } + + /* TODO: Should shadow component use its own VB? */ + VertexP3fT2fC4b* ptr = vertices; + if (Entities_ShadowMode == SHADOW_MODE_SNAP_TO_BLOCK) { + vb = GfxCommon_texVb; + if (!ShadowComponent_GetBlocks(entity, coords, posX, posZ, posY, data)) return; + + Real32 x1 = (Real32)Math_Floor(posX), z1 = (Real32)Math_Floor(posZ); + ShadowComponent_DrawSquareShadow(&ptr, data[0].Y, x1, z1); + } else { + vb = ModelCache_Vb; + Real32 radius = ShadowComponent_radius / 16.0f; + + Real32 x = posX - radius, z = posZ - radius; + if (ShadowComponent_GetBlocks(entity, coords, x, z, posY, data) && data[0].A > 0) { + ShadowComponent_DrawCircle(&ptr, entity, data, x, z); + } + + x = max(posX - radius, Math_Floor(posX + radius)); + if (ShadowComponent_GetBlocks(entity, coords, x, z, posY, data) && data[0].A > 0) { + ShadowComponent_DrawCircle(&ptr, entity, data, x, z); + } + + z = max(posZ - radius, Math_Floor(posZ + radius)); + if (ShadowComponent_GetBlocks(entity, coords, x, z, posY, data) && data[0].A > 0) { + ShadowComponent_DrawCircle(&ptr, entity, data, x, z); + } + + x = posX - radius; + if (ShadowComponent_GetBlocks(entity, coords, x, z, posY, data) && data[0].A > 0) { + ShadowComponent_DrawCircle(&ptr, entity, data, x, z); + } + } + + if (ptr == vertices) return; + if (ShadowComponent_ShadowTex == NULL) { + ShadowComponent_MakeTex(); + } + + if (!ShadowComponent_BoundShadowTex) { + Gfx_BindTexture(ShadowComponent_ShadowTex); + ShadowComponent_BoundShadowTex = true; + } + + UInt32 vCount = (UInt32)(ptr - vertices) / VertexP3fT2fC4b_Size; + GfxCommon_UpdateDynamicVb_IndexedTris(vb, vertices, vCount); +} diff --git a/src/Client/EntityComponents.h b/src/Client/EntityComponents.h index a8f677afc..8faffba60 100644 --- a/src/Client/EntityComponents.h +++ b/src/Client/EntityComponents.h @@ -72,7 +72,6 @@ void HacksComp_SetUserType(HacksComp* hacks, UInt8 value); void HacksComp_CheckConsistency(HacksComp* hacks); void HacksComp_UpdateState(HacksComp* hacks); - /* Represents a position and orientation state. */ typedef struct InterpState_ { Vector3 Pos; @@ -104,4 +103,11 @@ typedef struct NetInterpComp_ { void NetInterpComp_SetLocation(NetInterpComp* interp, LocationUpdate* update, bool interpolate); void NetInterpComp_AdvanceState(NetInterpComp* interp); + +/* Entity component that draws square and circle shadows beneath entities. */ + +bool ShadowComponent_BoundShadowTex; +GfxResourceID ShadowComponent_ShadowTex; +void ShadowComponent_Draw(Entity* entity); + #endif \ No newline at end of file diff --git a/src/Client/EnvRenderer.c b/src/Client/EnvRenderer.c index 0fd888c96..f5042e91a 100644 --- a/src/Client/EnvRenderer.c +++ b/src/Client/EnvRenderer.c @@ -140,7 +140,7 @@ void EnvRenderer_Render(Real64 deltaTime) { if (EnvRenderer_Minimal) { EnvRenderer_RenderMinimal(deltaTime); } else { - if (env_skyVb == 0 || env_cloudsVb == 0) return; + if (env_skyVb == NULL || env_cloudsVb == NULL) return; if (!SkyboxRenderer_ShouldRender()) { EnvRenderer_RenderSky(deltaTime); EnvRenderer_RenderClouds(deltaTime); diff --git a/src/Client/ModelCache.h b/src/Client/ModelCache.h index cc1ece197..e88d0069c 100644 --- a/src/Client/ModelCache.h +++ b/src/Client/ModelCache.h @@ -25,32 +25,18 @@ typedef struct CachedTexture_ { public CustomModel[] CustomModels = new CustomModel[256]; #endif -/* Maximum number of models (or textures) that can be registered. */ #define MODELCACHE_MAX_MODELS 24 -/* The models that have been registered previously. -The first model (index 0) is the humanoid model. */ CachedModel ModelCache_Models[MODELCACHE_MAX_MODELS]; -/* The textures that have been registered previously. */ CachedTexture ModelCache_Textures[MODELCACHE_MAX_MODELS]; /* Maximum number of vertices a model can have. */ #define MODELCACHE_MAX_VERTICES (24 * 12) -/* Dynamic vertex buffer for model rendering. */ GfxResourceID ModelCache_Vb; -/* Raw vertices for model rendering. */ VertexP3fT2fC4b ModelCache_Vertices[MODELCACHE_MAX_VERTICES]; -/* Initalises the model cache and hooks events. */ void ModelCache_Init(void); -/* Frees textures and vertex buffer of the model cache, and then unhooks events. */ void ModelCache_Free(void); -/* Gets the model which has the given name, or pointer to humanoid model if not found. */ IModel* ModelCache_Get(STRING_PURE String* name); -/* Gets the index of the texture which has the given name, or -1 if not found. */ Int32 ModelCache_GetTextureIndex(STRING_PURE String* texName); -/* Registers the given model to be able to be used as a model for entities. -You can use ModelCache_Get to get pointer to the model. */ void ModelCache_Register(STRING_REF const UInt8* name, STRING_PURE const UInt8* defaultTexName, IModel* instance); -/* Registers the given texture to be tracked by the model cache. -You can use ModelCache_GetTextureIndex to get the index of this texture. */ void ModelCache_RegisterTexture(STRING_REF const UInt8* texName); #endif \ No newline at end of file diff --git a/src/Client/Screens.c b/src/Client/Screens.c index a662f7082..24a25ef61 100644 --- a/src/Client/Screens.c +++ b/src/Client/Screens.c @@ -207,19 +207,19 @@ bool InventoryScreen_HandlesMouseDown(GuiElement* elem, Int32 x, Int32 y, MouseB bool InventoryScreen_HandlesMouseUp(GuiElement* elem, Int32 x, Int32 y, MouseButton btn) { InventoryScreen* screen = (InventoryScreen*)elem; - GuiElement* elem = &screen->Table.Base.Base; + elem = &screen->Table.Base.Base; return elem->HandlesMouseUp(elem, x, y, btn); } bool InventoryScreen_HandlesMouseMove(GuiElement* elem, Int32 x, Int32 y) { InventoryScreen* screen = (InventoryScreen*)elem; - GuiElement* elem = &screen->Table.Base.Base; + elem = &screen->Table.Base.Base; return elem->HandlesMouseMove(elem, x, y); } bool InventoryScreen_HandlesMouseScroll(GuiElement* elem, Real32 delta) { InventoryScreen* screen = (InventoryScreen*)elem; - GuiElement* elem = &screen->Table.Base.Base; + elem = &screen->Table.Base.Base; bool hotbar = Key_IsAltPressed() || Key_IsControlPressed() || Key_IsShiftPressed(); if (hotbar) return false; diff --git a/src/Client/WeatherRenderer.c b/src/Client/WeatherRenderer.c index 17e6606a7..a1d02b265 100644 --- a/src/Client/WeatherRenderer.c +++ b/src/Client/WeatherRenderer.c @@ -119,7 +119,6 @@ void WeatherRenderer_Render(Real64 deltaTime) { weather_accumulator += deltaTime; bool particles = weather == Weather_Rainy; - Int32 vCount = 0; PackedCol col = WorldEnv_SunCol; VertexP3fT2fC4b v; VertexP3fT2fC4b vertices[weather_verticesCount]; @@ -161,21 +160,20 @@ void WeatherRenderer_Render(Real64 deltaTime) { v.Y = y2; v.V = v2; *ptr = v; ptr++; v.X = x1; v.Z = z2; v.U = 0.0f; *ptr = v; ptr++; v.Y = y1; v.V = v1; *ptr = v; ptr++; - - vCount += 8; } } if (particles && (weather_accumulator >= 0.25f || moved)) { weather_accumulator = 0; } - if (vCount == 0) return; + if (ptr == vertices) return; Gfx_SetAlphaTest(false); Gfx_SetDepthWrite(false); Gfx_SetAlphaArgBlend(true); Gfx_SetBatchFormat(VERTEX_FORMAT_P3FT2FC4B); + UInt32 vCount = (UInt32)(ptr - vertices) / VertexP3fT2fC4b_Size; GfxCommon_UpdateDynamicVb_IndexedTris(weather_vb, vertices, vCount); Gfx_SetAlphaArgBlend(false);