diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index be9fdbfa8e..c4b19fd25f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -47,6 +47,7 @@ * `Fix Crash When Generating Certain Seeds` (Enabled By Default) * `Click Buttons On Mouse Down` (Enabled By Default) * `3D Dropped Items` (Enabled By Default) + * `Render Entity Shadows` (Enabled By Default) * Existing Functionality (All Enabled By Default) * `Fix Screen Rendering When Hiding HUD` * `Sanitize Usernames` diff --git a/images/CMakeLists.txt b/images/CMakeLists.txt index 913aabdc56..d0fc577e8c 100644 --- a/images/CMakeLists.txt +++ b/images/CMakeLists.txt @@ -19,6 +19,12 @@ install( DESTINATION "${MCPI_INSTALL_DIR}/data/images/item" ) +# Entity Shadow +install( + FILES "shadow.png" + DESTINATION "${MCPI_INSTALL_DIR}/data/images/misc" +) + # Icon set(ICON_DIR "${MCPI_SHARE_DIR}/icons/hicolor/512x512/apps") install( diff --git a/images/shadow.png b/images/shadow.png new file mode 100644 index 0000000000..06d999b208 Binary files /dev/null and b/images/shadow.png differ diff --git a/launcher/src/client/available-feature-flags b/launcher/src/client/available-feature-flags index 3b371b81fe..215b21d51d 100644 --- a/launcher/src/client/available-feature-flags +++ b/launcher/src/client/available-feature-flags @@ -109,4 +109,5 @@ TRUE Use Updated Title TRUE Hide Block Outline When GUI Is Hidden TRUE Fix Crash When Generating Certain Seeds TRUE Click Buttons On Mouse Down -TRUE 3D Dropped Items \ No newline at end of file +TRUE 3D Dropped Items +TRUE Render Entity Shadows \ No newline at end of file diff --git a/mods/src/f3/f3.cpp b/mods/src/f3/f3.cpp index 0db8abbcc0..cc4c22876a 100644 --- a/mods/src/f3/f3.cpp +++ b/mods/src/f3/f3.cpp @@ -135,6 +135,7 @@ static std::vector get_debug_info_right(const Minecraft *minecraft) } xyz_precision = debug_precision; } + minecraft->command_server->pos_translator.to(x, y, z); info.push_back(""); info.push_back("Target X: " + to_string_with_precision(x, xyz_precision)); info.push_back("Target Y: " + to_string_with_precision(y, xyz_precision)); diff --git a/mods/src/misc/graphics.cpp b/mods/src/misc/graphics.cpp index d9d11bc171..db91d75166 100644 --- a/mods/src/misc/graphics.cpp +++ b/mods/src/misc/graphics.cpp @@ -90,69 +90,193 @@ static void sort_chunks(Chunk **chunks_begin, Chunk **chunks_end, const Distance } // Fire Rendering +static void render_fire(EntityRenderer *self, Entity *entity, const float x, float y, const float z) { + // Check If Entity Is On Fire + if (!entity->isOnFire()) { + return; + } + // Here Be Decompiled Code + y -= entity->height_offset; + const int texture = Tile::fire->texture; + const int xt = (texture & 0xf) << 4; + const int yt = texture & 0xf0; + glPushMatrix(); + glTranslatef(x, y, z); + const float s = entity->hitbox_width * 1.4f; + glScalef(s, s, s); + self->bindTexture("terrain.png"); + Tesselator &t = Tesselator::instance; + float r = 0.5f; + float h = entity->hitbox_height / s; + float yo = entity->y - entity->height_offset - entity->hitbox.y1; + float player_rot_y = EntityRenderer::entityRenderDispatcher->player_rot_y; + if (EntityRenderer::entityRenderDispatcher->minecraft->options.third_person == 2) { + // Handle Front-Facing + player_rot_y -= 180.f; + } + glRotatef(-player_rot_y, 0, 1, 0); + glTranslatef(0, 0, -0.3f + float(int(h)) * 0.02f); + glColor4f(1, 1, 1, 1); + float zo = 0; + int ss = 0; + t.begin(7); + while (h > 0) { + constexpr float xo = 0.0f; + float u0; + float u1; + float v0; + float v1; + if (ss % 2 == 0) { + u0 = float(xt) / 256.0f; + u1 = (float(xt) + 15.99f) / 256.0f; + v0 = float(yt) / 256.0f; + v1 = (float(yt) + 15.99f) / 256.0f; + } else { + u0 = float(xt) / 256.0f; + u1 = (float(xt) + 15.99f) / 256.0f; + v0 = (float(yt) + 16) / 256.0f; + v1 = (float(yt) + 16 + 15.99f) / 256.0f; + } + if (ss / 2 % 2 == 0) { + std::swap(u1, u0); + } + t.vertexUV(r - xo, 0 - yo, zo, u1, v1); + t.vertexUV(-r - xo, 0 - yo, zo, u0, v1); + t.vertexUV(-r - xo, 1.4f - yo, zo, u0, v0); + t.vertexUV(r - xo, 1.4f - yo, zo, u1, v0); + h -= 0.45f; + yo -= 0.45f; + r *= 0.9f; + zo += 0.03f; + ss++; + } + t.draw(); + glPopMatrix(); +} + +// Entity Shadows +static void render_shadow_tile(Tile *tile, const float x, const float y, const float z, int xt, int yt, int zt, const float pow, const float r, const float xo, const float yo, const float zo) { + Tesselator &t = Tesselator::instance; + if (!tile->isCubeShaped()) { + return; + } + float a = ((pow - (y - (float(yt) + yo)) / 2) * 0.5f) * EntityRenderer::entityRenderDispatcher->level->getBrightness(xt, yt, zt); + if (a < 0) { + return; + } else if (a > 1) { + a = 1; + } + t.color(255, 255, 255, int(a * 255)); + float x0 = float(xt) + tile->x1 + xo; + float x1 = float(xt) + tile->x2 + xo; + float y0 = float(yt) + tile->y1 + yo + 1.0f / 64.0f; + float z0 = float(zt) + tile->z1 + zo; + float z1 = float(zt) + tile->z2 + zo; + float u0 = (x - x0) / 2 / r + 0.5f; + float u1 = (x - x1) / 2 / r + 0.5f; + float v0 = (z - z0) / 2 / r + 0.5f; + float v1 = (z - z1) / 2 / r + 0.5f; + t.vertexUV(x0, y0, z0, u0, v0); + t.vertexUV(x0, y0, z1, u0, v1); + t.vertexUV(x1, y0, z1, u1, v1); + t.vertexUV(x1, y0, z0, u1, v0); +} +static void render_shadow(const EntityRenderer *self, Entity *entity, float x, float y, float z, const float a) { + // Calculate Power + float pow = 0; + if (self->shadow_radius > 0) { + const float dist = EntityRenderer::entityRenderDispatcher->distanceToSqr(entity->x, entity->y, entity->z); + pow = (1 - dist / (16.0f * 16.0f)) * self->shadow_strength; + } + if (pow <= 0) { + return; + } + // Render + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + Textures *textures = EntityRenderer::entityRenderDispatcher->textures; + textures->loadAndBindTexture("misc/shadow.png"); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + Level *level = EntityRenderer::entityRenderDispatcher->level; + glDepthMask(false); + const float r = self->shadow_radius; + const float ex = entity->old_x + (entity->x - entity->old_x) * a; + float ey = entity->old_y + (entity->y - entity->old_y) * a + entity->getShadowHeightOffs() - entity->height_offset; + const float ez = entity->old_z + (entity->z - entity->old_z) * a; + const int x0 = Mth::floor(ex - r); + const int x1 = Mth::floor(ex + r); + const int y0 = Mth::floor(ey - r); + const int y1 = Mth::floor(ey); + const int z0 = Mth::floor(ez - r); + const int z1 = Mth::floor(ez + r); + const float xo = x - ex; + const float yo = y - ey; + const float zo = z - ez; + Tesselator &tt = Tesselator::instance; + tt.begin(7); + for (int xt = x0; xt <= x1; xt++) { + for (int yt = y0; yt <= y1; yt++) { + for (int zt = z0; zt <= z1; zt++) { + const int t = level->getTile(xt, yt - 1, zt); + if (t > 0 && level->getRawBrightness(xt, yt, zt) > 3) { + render_shadow_tile( + Tile::tiles[t], + x, y + entity->getShadowHeightOffs() - entity->height_offset, z, + xt, yt, zt, + pow, r, + xo, yo + entity->getShadowHeightOffs() - entity->height_offset, zo + ); + } + } + } + } + tt.draw(); + glColor4f(1, 1, 1, 1); + glDisable(GL_BLEND); + glDepthMask(true); +} +static void EntityRenderDispatcher_assign_injection(EntityRenderDispatcher_assign_t original, EntityRenderDispatcher *self, const uchar entity_id, EntityRenderer *renderer) { + // Modify Shadow Size + float new_radius; + switch (entity_id) { + case 16: + case 3: { + new_radius = 0.5f; + break; + } + case 9: + case 7: + case 8: { + new_radius = 0.7f; + break; + } + case 6: { + new_radius = 0.3f; + break; + } + default: { + new_radius = renderer->shadow_radius; + } + } + renderer->shadow_radius = new_radius; + // Call Original Method + original(self, entity_id, renderer); +} + +// Modify Entity Rendering +static bool should_render_fire; +static bool should_render_shadows; static void EntityRenderDispatcher_render_EntityRenderer_render_injection(EntityRenderer *self, Entity *entity, float x, float y, float z, float rot, float unknown) { // Call Original Method self->render(entity, x, y, z, rot, unknown); - + // Render Shadow + if (should_render_shadows) { + render_shadow(self, entity, x, y, z, unknown); + } // Render Fire - if (entity->isOnFire()) { - // Here Be Decompiled Code - y -= entity->height_offset; - const int texture = Tile::fire->texture; - const int xt = (texture & 0xf) << 4; - const int yt = texture & 0xf0; - glPushMatrix(); - glTranslatef(x, y, z); - const float s = entity->hitbox_width * 1.4f; - glScalef(s, s, s); - self->bindTexture("terrain.png"); - Tesselator &t = Tesselator::instance; - float r = 0.5f; - float h = entity->hitbox_height / s; - float yo = entity->y - entity->height_offset - entity->hitbox.y1; - float player_rot_y = EntityRenderer::entityRenderDispatcher->player_rot_y; - if (EntityRenderer::entityRenderDispatcher->minecraft->options.third_person == 2) { - // Handle Front-Facing - player_rot_y -= 180.f; - } - glRotatef(-player_rot_y, 0, 1, 0); - glTranslatef(0, 0, -0.3f + float(int(h)) * 0.02f); - glColor4f(1, 1, 1, 1); - float zo = 0; - int ss = 0; - t.begin(7); - while (h > 0) { - constexpr float xo = 0.0f; - float u0; - float u1; - float v0; - float v1; - if (ss % 2 == 0) { - u0 = float(xt) / 256.0f; - u1 = (float(xt) + 15.99f) / 256.0f; - v0 = float(yt) / 256.0f; - v1 = (float(yt) + 15.99f) / 256.0f; - } else { - u0 = float(xt) / 256.0f; - u1 = (float(xt) + 15.99f) / 256.0f; - v0 = (float(yt) + 16) / 256.0f; - v1 = (float(yt) + 16 + 15.99f) / 256.0f; - } - if (ss / 2 % 2 == 0) { - std::swap(u1, u0); - } - t.vertexUV(r - xo, 0 - yo, zo, u1, v1); - t.vertexUV(-r - xo, 0 - yo, zo, u0, v1); - t.vertexUV(-r - xo, 1.4f - yo, zo, u0, v0); - t.vertexUV(r - xo, 1.4f - yo, zo, u1, v0); - h -= 0.45f; - yo -= 0.45f; - r *= 0.9f; - zo += 0.03f; - ss++; - } - t.draw(); - glPopMatrix(); + if (should_render_fire) { + render_fire(self, entity, x, y, z); } } @@ -377,9 +501,12 @@ void _init_misc_graphics() { overwrite_calls_manual((void *) 0x51fac, (void *) sort_chunks); } - // Render Fire In Third-Person - if (feature_has("Render Fire In Third-Person", server_disabled)) { - overwrite_call((void *) 0x606c0, (void *) EntityRenderDispatcher_render_EntityRenderer_render_injection); + // Modify Entity Rendering + overwrite_call((void *) 0x606c0, (void *) EntityRenderDispatcher_render_EntityRenderer_render_injection); + should_render_fire = feature_has("Render Fire In Third-Person", server_disabled); + should_render_shadows = feature_has("Render Entity Shadows", server_disabled); + if (should_render_shadows) { + overwrite_calls(EntityRenderDispatcher_assign, EntityRenderDispatcher_assign_injection); } // Slightly Nicer Water Rendering diff --git a/symbols/src/entity/Entity.def b/symbols/src/entity/Entity.def index 679a65a558..f82b32ab49 100644 --- a/symbols/src/entity/Entity.def +++ b/symbols/src/entity/Entity.def @@ -22,15 +22,16 @@ virtual-method int getAuxData() = 0xf4; virtual-method bool isOnFire() = 0x90; virtual-method void baseTick() = 0x38; virtual-method bool isSneaking() = 0x88; +virtual-method float getShadowHeightOffs() = 0x60; property float x = 0x4; property float y = 0x8; property float z = 0xc; property int id = 0x1c; property Level *level = 0x24; -property float old_x = 0x28; -property float old_y = 0x2c; -property float old_z = 0x30; +property float old_x = 0x7c; +property float old_y = 0x80; +property float old_z = 0x84; property float vel_x = 0x34; property float vel_y = 0x38; property float vel_z = 0x3c; diff --git a/symbols/src/entity/EntityRenderDispatcher.def b/symbols/src/entity/EntityRenderDispatcher.def index 28f0f09381..6d1d969a31 100644 --- a/symbols/src/entity/EntityRenderDispatcher.def +++ b/symbols/src/entity/EntityRenderDispatcher.def @@ -2,8 +2,11 @@ constructor () = 0x6096c; method void assign(uchar entity_id, EntityRenderer *renderer) = 0x6094c; method void render(Entity *entity, float x, float y, float z, float rot, float unknown) = 0x60674; +method float distanceToSqr(float x, float y, float z) = 0x60790; static-method EntityRenderDispatcher *getInstance() = 0x60e90; property ItemInHandRenderer *item_renderer = 0x0; property float player_rot_y = 0x14; -property Minecraft *minecraft = 0xc; \ No newline at end of file +property Minecraft *minecraft = 0xc; +property Level *level = 0x8; +property Textures *textures = 0x4; \ No newline at end of file diff --git a/symbols/src/entity/EntityRenderer.def b/symbols/src/entity/EntityRenderer.def index 77973732b9..4f6d6843b6 100644 --- a/symbols/src/entity/EntityRenderer.def +++ b/symbols/src/entity/EntityRenderer.def @@ -5,5 +5,8 @@ virtual-method void render(Entity *entity, float param_2, float param_3, float p // Can be called without an EntityRenderer, just do EntityRenderer_bindTexture->get(false)(NULL, &file); method void bindTexture(const std::string &file) = 0x62540; +property float shadow_radius = 0x4; +property float shadow_strength = 0x8; + // Globals static-property EntityRenderDispatcher *entityRenderDispatcher = 0x137bc0; diff --git a/symbols/src/level/LevelSource.def b/symbols/src/level/LevelSource.def index e9162c2f01..3a98e64919 100644 --- a/symbols/src/level/LevelSource.def +++ b/symbols/src/level/LevelSource.def @@ -1,6 +1,6 @@ virtual-method int getTile(int x, int y, int z) = 0x8; virtual-method int isEmptyTile(int x, int y, int z) = 0xc; -virtual-method int getBrightness(int x, int y, int z) = 0x10; +virtual-method float getBrightness(int x, int y, int z) = 0x10; virtual-method int getData(int x, int y, int z) = 0x14; virtual-method Material *getMaterial(int x, int y, int z) = 0x18; virtual-method bool isSolidRenderTile(int x, int y, int z) = 0x1c;