diff --git a/dependencies/runtime/src b/dependencies/runtime/src index c13072404d..377f9ddbc4 160000 --- a/dependencies/runtime/src +++ b/dependencies/runtime/src @@ -1 +1 @@ -Subproject commit c13072404d3bb7ffe6b58fea72393c305c1f5eab +Subproject commit 377f9ddbc4747ca3a640231d259c0e6fcc71b4b0 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 5c58d95d2b..5124c3676a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -76,8 +76,11 @@ * `Force Survival Mode Inventory Behavior` (Disabled By Default) * `Maximize Creative Mode Inventory Stack Size` (Disabled By Default) * Rename `Disable Buggy Held Item Caching` Feature Flag To `Fix Held Item Caching` +* Rename `Disable 'gui_blocks' Atlas` Feature Flag To `Regenerate "gui_blocks" Atlas` * Add Milk Buckets * Included In The `Add Buckets` Feature Flag +* Removed `Property Scale Animated Textures` Feature Flag +* Removed `Remove Invalid Item Background` Feature Flag * Improve Death Messages * Massive Build System Improvements * Fix Item Dropping When Killing Players From The Server Console @@ -277,7 +280,7 @@ **2.2.11** * Add `Close Current Screen On Death` Feature Flag (Enabled By Default) To Prevent Bugs -* Fix More Furnace UI Bugs When Using "Disable 'gui_blocks' Atlas" +* Fix More Furnace UI Bugs When Using `Disable 'gui_blocks' Atlas` **2.2.10** * Fix Bug With Picking Up Items In "Remove Creative Mode Restrictions" Mode diff --git a/launcher/src/bootstrap.cpp b/launcher/src/bootstrap.cpp index 6a4d1c9990..0238d6bfd0 100644 --- a/launcher/src/bootstrap.cpp +++ b/launcher/src/bootstrap.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "util.h" #include "bootstrap.h" @@ -179,7 +180,7 @@ void bootstrap(const options_t &options) { // Fix QEMU Bug #ifdef MCPI_RUNTIME_IS_QEMU args.push_back("-B"); - args.push_back("0x40000"); // Arbitrary Value (Aligns To 4k And 16k Page Sizes) + args.push_back(std::to_string(QEMU_GUEST_BASE)); #endif // Specify MCPI Binary diff --git a/launcher/src/client/available-feature-flags b/launcher/src/client/available-feature-flags index a372d94fcf..9f2b5bb90d 100644 --- a/launcher/src/client/available-feature-flags +++ b/launcher/src/client/available-feature-flags @@ -15,8 +15,7 @@ FALSE Maximize Creative Mode Inventory Stack Size TRUE Animated Water TRUE Animated Lava TRUE Animated Fire -TRUE Remove Invalid Item Background -TRUE Disable "gui_blocks" Atlas +TRUE Regenerate "gui_blocks" Atlas TRUE Fix Camera Rendering TRUE Implement Chat FALSE Hide Chat Messages @@ -102,7 +101,6 @@ TRUE Log Chat Messages TRUE Log Game Status TRUE Screenshot Support TRUE Fix Camera Functionality -TRUE Property Scale Animated Textures TRUE Allow High-Resolution Title TRUE Improved Classic Title Positioning TRUE Use Updated Title diff --git a/media-layer/core/CMakeLists.txt b/media-layer/core/CMakeLists.txt index 79c7df4191..efa72a28d9 100644 --- a/media-layer/core/CMakeLists.txt +++ b/media-layer/core/CMakeLists.txt @@ -7,6 +7,7 @@ set(CORE_SRC src/cursor.cpp src/util.cpp src/events.cpp + src/offscreen.cpp src/audio/api.cpp src/audio/engine.c src/audio/file.cpp diff --git a/media-layer/core/src/base.cpp b/media-layer/core/src/base.cpp index 814e32d094..ac92152f41 100644 --- a/media-layer/core/src/base.cpp +++ b/media-layer/core/src/base.cpp @@ -2,7 +2,6 @@ #include -#include #include #include "media.h" @@ -37,7 +36,3 @@ int media_SDL_PushEvent(SDL_Event *event) { queue.push_back(*event); return 1; } - -void media_ensure_loaded() { - // NOP -} diff --git a/media-layer/core/src/offscreen.cpp b/media-layer/core/src/offscreen.cpp new file mode 100644 index 0000000000..c294127bcf --- /dev/null +++ b/media-layer/core/src/offscreen.cpp @@ -0,0 +1,40 @@ +#include "media.h" + +#include + +// Offscreen Rendering +static GLFWwindow *offscreen_window = nullptr; +void media_begin_offscreen_render(const int width, const int height) { + if (!glfw_window) { + IMPOSSIBLE(); + } + // Setup GLFW + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); + glfwWindowHint(GLFW_ALPHA_BITS, 8); + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); + // Open Window + offscreen_window = glfwCreateWindow(width, height, "Offscreen Rendering", nullptr, nullptr); + if (!offscreen_window) { + ERR("Unable To Create Offscreen Window"); + } + // Switch Context + glfwMakeContextCurrent(offscreen_window); + media_context_id++; + // Check Framebuffer Size + int fb_width; + int fb_height; + glfwGetFramebufferSize(offscreen_window, &fb_width, &fb_height); + if (fb_width != width || fb_height != height) { + ERR("Offscreen Framebuffer Has Invalid Size"); + } +} +void media_end_offscreen_render() { + // Destroy Window + glfwDestroyWindow(offscreen_window); + offscreen_window = nullptr; + // Switch Context + glfwMakeContextCurrent(glfw_window); + media_context_id++; +} \ No newline at end of file diff --git a/media-layer/gles/src/passthrough.cpp b/media-layer/gles/src/passthrough.cpp index e680a953ae..eede57cc03 100644 --- a/media-layer/gles/src/passthrough.cpp +++ b/media-layer/gles/src/passthrough.cpp @@ -5,15 +5,21 @@ #include // Load GL Function -static char *gl_dlerror() { - return (char *) "Unknown Error"; -} -#define dlerror gl_dlerror -static void *gl_dlsysm(__attribute__((unused)) void *handle, __attribute__((unused)) const char *name) { - return (void *) glfwGetProcAddress(name); -} -#define dlsym gl_dlsysm -#define GL_FUNC EXTERNAL_FUNC +unsigned int media_context_id = 0; +#define GL_FUNC(name, return_type, args) \ + typedef return_type (*real_##name##_t)args; \ + __attribute__((__unused__)) static real_##name##_t real_##name() { \ + static real_##name##_t func = nullptr; \ + static unsigned int old_context = 0; \ + if (!func || old_context != media_context_id) { \ + old_context = media_context_id; \ + func = (real_##name##_t) glfwGetProcAddress(#name); \ + if (!func) { \ + ERR("Error Resolving OpenGL Function: " #name); \ + } \ + } \ + return func; \ + } // Passthrough Functions GL_FUNC(glFogfv, void, (GLenum pname, const GLfloat *params)) @@ -36,10 +42,6 @@ GL_FUNC(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count)) void media_glDrawArrays(const GLenum mode, const GLint first, const GLsizei count) { real_glDrawArrays()(mode, first, count); } -GL_FUNC(glMultiDrawArraysEXT, void, (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount)) -void media_glMultiDrawArrays(const GLenum mode, const GLint *first, const GLsizei *count, const GLsizei drawcount) { - real_glMultiDrawArraysEXT()(mode, first, count, drawcount); -} GL_FUNC(glColor4f, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) void media_glColor4f(const GLfloat red, const GLfloat green, const GLfloat blue, const GLfloat alpha) { real_glColor4f()(red, green, blue, alpha); @@ -243,4 +245,10 @@ void media_glColorMaterial(const GLenum face, const GLenum mode) { GL_FUNC(glLightModelfv, void, (GLenum pname, const GLfloat *params)) void media_glLightModelfv(const GLenum pname, const GLfloat *params) { real_glLightModelfv()(pname, params); +} + +// GL_EXT_multi_draw_arrays +GL_FUNC(glMultiDrawArraysEXT, void, (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount)) +void media_glMultiDrawArrays(const GLenum mode, const GLint *first, const GLsizei *count, const GLsizei drawcount) { + real_glMultiDrawArraysEXT()(mode, first, count, drawcount); } \ No newline at end of file diff --git a/media-layer/include/GLES/gl.h b/media-layer/include/GLES/gl.h index 3d6657dcce..aa192f3f20 100644 --- a/media-layer/include/GLES/gl.h +++ b/media-layer/include/GLES/gl.h @@ -99,6 +99,7 @@ extern "C" { #define GL_FRONT_AND_BACK 0x408 #define GL_AMBIENT_AND_DIFFUSE 0x1602 #define GL_LIGHT_MODEL_AMBIENT 0xb53 +#define GL_STREAM_DRAW 0x88e0 typedef float GLfloat; typedef float GLclampf; @@ -176,6 +177,8 @@ void media_glLightfv(GLenum light, GLenum pname, const GLfloat *params); void media_glColorMaterial(GLenum face, GLenum mode); void media_glLightModelfv(GLenum pname, const GLfloat *params); +extern unsigned int media_context_id; + #ifdef __cplusplus } #endif diff --git a/media-layer/include/media-layer/core.h b/media-layer/include/media-layer/core.h index a2d7b69c3e..27b23cd57e 100644 --- a/media-layer/include/media-layer/core.h +++ b/media-layer/include/media-layer/core.h @@ -12,8 +12,6 @@ extern "C" { #define USER_EVENT_CHARACTER 0 // data1 = 8-Bit Character #define USER_EVENT_REAL_KEY 1 // data1 = SDL_RELEASED/PRESSED, data2 = GLFW Key Code -void media_ensure_loaded(); - void media_toggle_fullscreen(); void media_swap_buffers(); void media_cleanup(); @@ -23,6 +21,8 @@ void media_disable_vsync(); void media_force_egl(); void media_set_raw_mouse_motion_enabled(int enabled); int media_has_extension(const char *name); +void media_begin_offscreen_render(int width, int height); +void media_end_offscreen_render(); #ifdef __cplusplus } diff --git a/media-layer/trampoline/src/GLESv1_CM.cpp b/media-layer/trampoline/src/GLESv1_CM.cpp index 89f0961297..c3119e170e 100644 --- a/media-layer/trampoline/src/GLESv1_CM.cpp +++ b/media-layer/trampoline/src/GLESv1_CM.cpp @@ -29,12 +29,13 @@ struct gl_array_details_t { uint32_t pointer = 0; }; #ifdef MEDIA_LAYER_TRAMPOLINE_GUEST -struct { +struct gl_array_details_obj_t { gl_array_details_t media_glVertexPointer; gl_array_details_t media_glColorPointer; gl_array_details_t media_glTexCoordPointer; gl_array_details_t media_glNormalPointer; -} gl_array_details; +}; +static gl_array_details_obj_t gl_array_details; #endif struct gl_state_t { GLuint bound_array_buffer = 0; @@ -86,6 +87,22 @@ struct gl_state_t { static gl_state_t gl_state; #endif +// Backup/Restore State (For Offscreen Rendering) +#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST +static gl_state_t gl_state_backup; +static gl_array_details_obj_t gl_array_details_backup; +void _media_backup_gl_state() { + gl_state_backup = gl_state; + gl_array_details_backup = gl_array_details; + gl_state = gl_state_t(); + gl_array_details = gl_array_details_obj_t(); +} +void _media_restore_gl_state() { + gl_state = gl_state_backup; + gl_array_details = gl_array_details_backup; +} +#endif + // 'pointer' Is Only Supported As An Integer, Not As An Actual Pointer #ifdef MEDIA_LAYER_TRAMPOLINE_GUEST #define CALL_GL_POINTER(unique_id, name) \ @@ -184,12 +201,25 @@ CALL(17, media_glClear, void, (GLbitfield mask)) CALL(18, media_glBufferData, void, (GLenum target, GLsizeiptr size, const void *data, GLenum usage)) #ifdef MEDIA_LAYER_TRAMPOLINE_GUEST - trampoline(true, gl_state.bound_array_buffer, target, int32_t(size), copy_array(size, (unsigned char *) data), usage); + static bool use_syscall = getenv(TRAMPOLINE_ARGUMENTS_PIPE_ENV) == nullptr; + if (use_syscall) { + trampoline(false, gl_state.bound_array_buffer, target, int32_t(size), uint32_t(data), usage); + } else { + trampoline(true, gl_state.bound_array_buffer, target, int32_t(size), copy_array(size, (unsigned char *) data), usage); + } #else media_glBindBuffer(GL_ARRAY_BUFFER, args.next()); GLenum target = args.next(); int32_t size = args.next(); +#ifdef MCPI_RUNTIME_IS_QEMU + const unsigned char *data = nullptr; + uint32_t data_addr = args.next(); + if (data_addr != 0) { + data = (const unsigned char *) (uintptr_t) (QEMU_GUEST_BASE + data_addr); + } +#else const unsigned char *data = args.next_arr(); +#endif GLenum usage = args.next(); func(target, size, data, usage); return 0; @@ -768,13 +798,24 @@ CALL(67, media_glGenBuffers, void, (GLsizei n, GLuint *buffers)) CALL(69, media_glBufferSubData, void, (GLenum target, GLintptr offset, GLsizeiptr size, const void *data)) #ifdef MEDIA_LAYER_TRAMPOLINE_GUEST - trampoline(true, gl_state.bound_array_buffer, target, int32_t(offset), int32_t(size), copy_array(size, (unsigned char *) data)); + static bool use_syscall = getenv(TRAMPOLINE_ARGUMENTS_PIPE_ENV) == nullptr; + if (use_syscall) { + trampoline(false, gl_state.bound_array_buffer, target, int32_t(offset), int32_t(size), uint32_t(data)); + } else { + trampoline(true, gl_state.bound_array_buffer, target, int32_t(offset), copy_array(size, (unsigned char *) data)); + } #else media_glBindBuffer(GL_ARRAY_BUFFER, args.next()); GLenum target = args.next(); int32_t offset = args.next(); +#ifdef MCPI_RUNTIME_IS_QEMU int32_t size = args.next(); - const unsigned char *data = args.next_arr(); + uint32_t data_addr = args.next(); + const unsigned char *data = (const unsigned char *) (uintptr_t) (QEMU_GUEST_BASE + data_addr); +#else + uint32_t size; + const unsigned char *data = args.next_arr(&size); +#endif func(target, offset, size, data); return 0; #endif diff --git a/media-layer/trampoline/src/guest/guest.cpp b/media-layer/trampoline/src/guest/guest.cpp index 672cdb51ee..0270819040 100644 --- a/media-layer/trampoline/src/guest/guest.cpp +++ b/media-layer/trampoline/src/guest/guest.cpp @@ -67,10 +67,7 @@ static uint32_t trampoline_pipe(const uint32_t id, const bool allow_early_return // Main Function uint32_t _raw_trampoline(const uint32_t id, const bool allow_early_return, const uint32_t length, const unsigned char *args) { // Configure Method - static int use_syscall = -1; - if (use_syscall == -1) { - use_syscall = getenv(TRAMPOLINE_ARGUMENTS_PIPE_ENV) == nullptr; - } + static bool use_syscall = getenv(TRAMPOLINE_ARGUMENTS_PIPE_ENV) == nullptr; // Use Correct Method if (use_syscall) { return trampoline_syscall(id, length, args); diff --git a/media-layer/trampoline/src/guest/guest.h b/media-layer/trampoline/src/guest/guest.h index 0ac1ab4156..8cf3133fdd 100644 --- a/media-layer/trampoline/src/guest/guest.h +++ b/media-layer/trampoline/src/guest/guest.h @@ -65,3 +65,7 @@ unsigned int _trampoline(const unsigned int id, const bool allow_early_return, A #define CALL(unique_id, name, return_type, args) \ return_type name args { \ static unsigned char _id = unique_id; + +// Handle Cached GL State When Switching To Offscreen Context +__attribute__((visibility("internal"))) void _media_backup_gl_state(); +__attribute__((visibility("internal"))) void _media_restore_gl_state(); diff --git a/media-layer/trampoline/src/media-layer-core.cpp b/media-layer/trampoline/src/media-layer-core.cpp index fa9b3134a7..ba53abc2d8 100644 --- a/media-layer/trampoline/src/media-layer-core.cpp +++ b/media-layer/trampoline/src/media-layer-core.cpp @@ -174,15 +174,6 @@ CALL(66, media_force_egl, void, ()) #endif } -CALL(68, media_ensure_loaded, void, ()) -#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST - trampoline(true); -#else - func(); - return 0; -#endif -} - CALL(71, media_has_extension, int, (const char *name)) #ifdef MEDIA_LAYER_TRAMPOLINE_GUEST return trampoline(false, copy_array(name)); @@ -190,3 +181,25 @@ CALL(71, media_has_extension, int, (const char *name)) return func(args.next_arr()); #endif } + +CALL(76, media_begin_offscreen_render, void, (const int width, const int height)) +#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST + trampoline(true, width, height); + _media_backup_gl_state(); +#else + const int width = args.next(); + const int height = args.next(); + func(width, height); + return 0; +#endif +} + +CALL(77, media_end_offscreen_render, void, ()) +#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST + trampoline(true); + _media_restore_gl_state(); +#else + func(); + return 0; +#endif +} \ No newline at end of file diff --git a/mods/include/mods/atlas/atlas.h b/mods/include/mods/atlas/atlas.h new file mode 100644 index 0000000000..c186c6f2e3 --- /dev/null +++ b/mods/include/mods/atlas/atlas.h @@ -0,0 +1,5 @@ +#pragma once + +extern "C" { +void atlas_update_tile(Textures *textures, int texture, const unsigned char *pixels); +} \ No newline at end of file diff --git a/mods/include/mods/misc/misc.h b/mods/include/mods/misc/misc.h index bbfd71cd1f..ff57a6f5b1 100644 --- a/mods/include/mods/misc/misc.h +++ b/mods/include/mods/misc/misc.h @@ -15,6 +15,7 @@ typedef RakNet_RakString *(*RakNet_RakString_constructor_2_t)(RakNet_RakString * extern RakNet_RakString_constructor_2_t RakNet_RakString_constructor_2; } +void misc_run_on_init(const std::function &func); void misc_run_on_update(const std::function &func); void misc_run_on_tick(const std::function &func); void misc_run_on_recipes_setup(const std::function &func); diff --git a/mods/include/mods/textures/textures.h b/mods/include/mods/textures/textures.h index 777c38a6b6..022b9a59ee 100644 --- a/mods/include/mods/textures/textures.h +++ b/mods/include/mods/textures/textures.h @@ -2,6 +2,9 @@ #include +struct Texture; + extern "C" { -void media_glTexSubImage2D_with_scaling(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLsizei normal_texture_width, GLsizei normal_texture_height, GLenum format, GLenum type, const void *pixels); +void media_glTexSubImage2D_with_scaling(const Texture *target, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLsizei normal_texture_width, GLsizei normal_texture_height, const void *pixels); +void textures_add(int width, int height, const unsigned char *pixels); } \ No newline at end of file diff --git a/mods/src/atlas/README.md b/mods/src/atlas/README.md index 0de8aec819..f4302bb7df 100644 --- a/mods/src/atlas/README.md +++ b/mods/src/atlas/README.md @@ -1,2 +1,2 @@ # `atlas` Mod -This mod allows disabling the `gui_blocks` atlas. +This mod allows regenerating the `gui_blocks` atlas at runtime. diff --git a/mods/src/atlas/atlas.cpp b/mods/src/atlas/atlas.cpp index 7ca46d4371..bdc1467634 100644 --- a/mods/src/atlas/atlas.cpp +++ b/mods/src/atlas/atlas.cpp @@ -2,124 +2,229 @@ #include #include +#include #include +#include +#include +#include #include -// Fix Grass And Leaves Inventory Rendering When The gui_blocks Atlas Is Disabled -static void ItemRenderer_renderGuiItemCorrect_injection_one(ItemRenderer_renderGuiItemCorrect_t original, Font *font, Textures *textures, const ItemInstance *item_instance, const int32_t param_1, const int32_t param_2) { - const int32_t leaves_id = Tile::leaves->id; - const int32_t grass_id = Tile::grass->id; - // Replace Rendered Item With Carried Variant - ItemInstance carried_item_instance; - bool use_carried = false; - if (item_instance != nullptr) { - if (item_instance->id == leaves_id) { - carried_item_instance.constructor_tile_extra(Tile::leaves_carried, item_instance->count, item_instance->auxiliary); - use_carried = true; - } else if (item_instance->id == grass_id) { - carried_item_instance.constructor_tile_extra(Tile::grass_carried, item_instance->count, item_instance->auxiliary); - use_carried = true; +// Render Atlas +#define ATLAS_SIZE 32 +#define ATLAS_ENTRY_SIZE 48 +static int get_atlas_key(Item *item, const int data) { + const int id = item->id; + const int icon = item->getIcon(data); + return (id << 16) | icon; +} +static std::unordered_map> atlas_key_to_pos; +static std::unordered_map>> tile_texture_to_atlas_pos; +static void render_atlas(Textures *textures) { + int x = 0; + int y = 0; + // Loop Over All Possible IDs + for (int id = 0; id < 512; id++) { + Item *item = Item::items[id]; + if (!item) { + // Invalid ID + continue; + } + // Count Unique Textures + constexpr int amount_of_data_values_to_check = 512; + std::unordered_map key_to_data; + for (int data = amount_of_data_values_to_check - 1; data >= 0; data--) { + int key = get_atlas_key(item, data); + key_to_data[key] = data; + } + // Loop Over All Data Values With Unique Textures + for (const std::pair info : key_to_data) { + const int key = info.first; + const int data = info.second; + // Check Remaining Space (Leave Last Slot Empty) + if (x == (ATLAS_SIZE - 1) && y == (ATLAS_SIZE - 1)) { + WARN("Out Of gui_blocks Atlas Space!"); + return; + } + // Position + media_glPushMatrix(); + media_glTranslatef(ATLAS_ENTRY_SIZE * x, ATLAS_ENTRY_SIZE * y, 0); + constexpr float scale = ATLAS_ENTRY_SIZE / 16.0f; + media_glScalef(scale, scale, 1); + // Render + ItemInstance obj = { + .count = 1, + .id = id, + .auxiliary = data + }; + ItemRenderer::renderGuiItemCorrect(nullptr, textures, &obj, 0, 0); + media_glPopMatrix(); + // Store + atlas_key_to_pos[key] = {x, y}; + if (id < 256) { + Tile *tile = Tile::tiles[id]; + if (tile && !TileRenderer::canRender(tile->getRenderShape())) { + int icon = item->getIcon(data); + tile_texture_to_atlas_pos[icon].push_back(atlas_key_to_pos[key]); + } + } + // Advance To Next Slot + x++; + if (x >= ATLAS_SIZE) { + x = 0; + y++; + } } } +} +static void generate_atlas(Minecraft *minecraft) { + // Setup Offscreen Rendering + constexpr int atlas_size = ATLAS_SIZE * ATLAS_ENTRY_SIZE; + media_begin_offscreen_render(atlas_size, atlas_size); - // Fix Toolbar Rendering - const GLboolean depth_test_was_enabled = media_glIsEnabled(GL_DEPTH_TEST); + // Setup OpenGL + ((NinecraftApp *) minecraft)->initGLStates(); + media_glViewport(0, 0, atlas_size, atlas_size); + media_glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + media_glMatrixMode(GL_PROJECTION); + media_glLoadIdentity(); + media_glOrthof(0, atlas_size, atlas_size, 0, 2000, 3000); + media_glMatrixMode(GL_MODELVIEW); + media_glLoadIdentity(); + media_glTranslatef(0, 0, -2000); media_glDisable(GL_DEPTH_TEST); - // Call Original Method - original(font, textures, use_carried ? &carried_item_instance : item_instance, param_1, param_2); + // Re-Upload Textures + Textures *textures = Textures::allocate(); + textures->constructor(&minecraft->options, minecraft->platform()); - // Revert GL State Changes - if (depth_test_was_enabled) { - media_glEnable(GL_DEPTH_TEST); + // Render + render_atlas(textures); + + // Copy Open Inventory Button + textures->loadAndBindTexture("gui/gui_blocks.png"); + constexpr int icon_width = 28; + constexpr int icon_height = 8; + minecraft->gui.blit(atlas_size - icon_width, atlas_size - icon_height, 242, 252, icon_width, icon_height, 14, 4); + + // Read Texture + int line_size = atlas_size * 4; + { + // Handle Alignment + int alignment; + media_glGetIntegerv(GL_PACK_ALIGNMENT, &alignment); + // Round + line_size = ALIGN_UP(line_size, alignment); } -} - -// Fix Translucent Preview Items In Furnace UI Being Fully Opaque When The gui_blocks Atlas Is Disabled -static int item_color_fix_mode = 0; -#define POTENTIAL_FURNACE_ITEM_TRANSPARENCY 0x33 -#define INVALID_FURNACE_ITEM_MULTIPLIER 0.25f -static void Tesselator_color_injection(Tesselator_color_t original, Tesselator *tesselator, int32_t r, int32_t g, int32_t b, int32_t a) { - // Fix Furnace UI - if (item_color_fix_mode != 0) { - // Force Translucent - if (item_color_fix_mode == 1) { - a = POTENTIAL_FURNACE_ITEM_TRANSPARENCY; - } else { - static double multiplier = INVALID_FURNACE_ITEM_MULTIPLIER; - b *= multiplier; - g *= multiplier; - r *= multiplier; + Texture texture; + texture.width = atlas_size; + texture.height = atlas_size; + texture.field3_0xc = 0; + texture.field4_0x10 = true; + texture.field5_0x11 = false; + texture.field6_0x14 = 0; + texture.field7_0x18 = -1; + texture.data = new unsigned char[atlas_size * line_size]; + media_glReadPixels(0, 0, atlas_size, atlas_size, GL_RGBA, GL_UNSIGNED_BYTE, texture.data); + for (int y = 0; y < (texture.height / 2); y++) { + for (int x = 0; x < (texture.width * 4); x++) { + unsigned char &a = texture.data[(y * line_size) + x]; + unsigned char &b = texture.data[((texture.height - y - 1) * line_size) + x]; + std::swap(a, b); } } - // Call Original Method - original(tesselator, r, g, b, a); -} -static void Tesselator_begin_injection(Tesselator_begin_t original, Tesselator *tesselator, const int32_t mode) { - // Call Original Method - original(tesselator, mode); + // Restore Old Context + textures->destructor(); + ::operator delete(textures); + media_end_offscreen_render(); - // Fix Furnace UI - if (item_color_fix_mode != 0) { - // Implicit Translucent - tesselator->color(0xff, 0xff, 0xff, 0xff); + // Upload Texture + minecraft->textures->assignTexture("gui/gui_blocks.png", texture); + DEBUG("Generated gui_blocks Atlas"); +} + +// Use New Atlas +static void ItemRenderer_renderGuiItem_two_injection(__attribute__((unused)) ItemRenderer_renderGuiItem_two_t original, __attribute__((unused)) Font *font, Textures *textures, const ItemInstance *item_instance_ptr, const float x, const float y, const float w, const float h, __attribute__((unused)) bool param_5) { + // "Carried" Items + ItemInstance item_instance = *item_instance_ptr; + if (item_instance.id == Tile::leaves->id) { + item_instance.id = Tile::leaves_carried->id; + } else if (item_instance.id == Tile::grass->id) { + item_instance.id = Tile::grass_carried->id; } -} -static void InventoryPane_renderBatch_Tesselator_color_injection(Tesselator *tesselator, int32_t r, int32_t g, int32_t b) { - // Call Original Method - tesselator->color(r, g, b, 0xff); - - // Enable Item Color Fix - item_color_fix_mode = 2; -} -static void ItemRenderer_renderGuiItem_two_injection_one(ItemRenderer_renderGuiItem_two_t original, Font *font, Textures *textures, const ItemInstance *item_instance, const float param_1, const float param_2, const float param_3, const float param_4, const bool param_5) { - // Call Original Method - original(font, textures, item_instance, param_1, param_2, param_3, param_4, param_5); - - // Disable Item Color Fix - item_color_fix_mode = 0; -} -static void FurnaceScreen_render_ItemRenderer_renderGuiItem_one_injection(Font *font, Textures *textures, ItemInstance *item_instance, float param_1, float param_2, bool param_3) { - // Enable Item Color Fix - item_color_fix_mode = 1; - - // Call Original Method - ItemRenderer::renderGuiItem_one(font, textures, item_instance, param_1, param_2, param_3); + // Get Position + Item *item = Item::items[item_instance.id]; + if (!item) { + return; + } + const int key = get_atlas_key(item, item_instance.auxiliary); + if (!atlas_key_to_pos.contains(key)) { + WARN("Skipping Item Not In gui_blocks Atlas: %i:%i", item_instance.id, item_instance.auxiliary); + return; + } + const std::pair &pos = atlas_key_to_pos[key]; + // Convert To UV + float u0 = float(pos.first) / ATLAS_SIZE; + float u1 = float(pos.first + 1) / ATLAS_SIZE; + float v0 = float(pos.second) / ATLAS_SIZE; + float v1 = float(pos.second + 1) / ATLAS_SIZE; + // Render + textures->loadAndBindTexture("gui/gui_blocks.png"); + Tesselator &t = Tesselator::instance; + t.begin(GL_QUADS); + t.colorABGR(item_instance.count > 0 ? 0xffffffff : 0x60ffffff); + t.vertexUV(x, y + h, 0, u0, v1); + t.vertexUV(x + w, y + h, 0, u1, v1); + t.vertexUV(x + w, y, 0, u1, v0); + t.vertexUV(x, y, 0, u0, v0); + t.draw(); } -// Smooth Scrolling -static float target_x; -static float target_y; -static void ItemRenderer_renderGuiItem_two_injection_two(ItemRenderer_renderGuiItem_two_t original, Font *font, Textures *textures, const ItemInstance *item_instance, const float x, const float y, const float w, const float h, const bool param_5) { - target_x = x; - target_y = y; - original(font, textures, item_instance, x, y, w, h, param_5); +// Fix Buggy Crop Textures +#define MAX_CROP_DATA 7 +static int CropTile_getTexture2_injection(CropTile_getTexture2_t original, CropTile *self, const int face, int data) { + if (data > MAX_CROP_DATA) { + data = MAX_CROP_DATA; + } + return original(self, face, data); } -static void ItemRenderer_renderGuiItemCorrect_injection_two(ItemRenderer_renderGuiItemCorrect_t original, Font *font, Textures *textures, const ItemInstance *item_instance, __attribute__((unused)) int x, __attribute__((unused)) int y) { - media_glPushMatrix(); - media_glTranslatef(target_x, target_y, 0); - original(font, textures, item_instance, 0, 0); - media_glPopMatrix();; + +// Fix Open Inventory Button +static void Gui_renderToolBar_GuiComponent_blit_injection(GuiComponent *self, int x_dest, int y_dest, __attribute__((unused)) const int x_src, __attribute__((unused)) const int y_src, const int width_dest, const int height_dest, const int width_src, const int height_src) { + constexpr float size_scale = 2.0f / (ATLAS_SIZE * ATLAS_ENTRY_SIZE); + constexpr float u1 = 1.0f; + const float u0 = u1 - (float(width_src) * size_scale); + constexpr float v1 = 1.0f; + const float v0 = v1 - (float(height_src) * size_scale); + Tesselator &t = Tesselator::instance; + t.begin(GL_QUADS); + t.vertexUV(x_dest, y_dest + height_dest, self->z, u0, v1); + t.vertexUV(x_dest + width_dest, y_dest + height_dest, self->z, u1, v1); + t.vertexUV(x_dest + width_dest, y_dest, self->z, u1, v0); + t.vertexUV(x_dest, y_dest, self->z, u0, v0); + t.draw(); +} + +// Update Dynamic Textures In Atlas +void atlas_update_tile(Textures *textures, const int texture, const unsigned char *pixels) { + // Update Texture + for (const std::pair &pos : tile_texture_to_atlas_pos[texture]) { + uint32_t atlas_id = textures->loadAndBindTexture("gui/gui_blocks.png"); + const Texture *atlas_data = textures->getTemporaryTextureData(atlas_id); + constexpr int texture_size = 16; + constexpr int fake_atlas_size = (ATLAS_SIZE * texture_size); + media_glTexSubImage2D_with_scaling(atlas_data, pos.first * texture_size, pos.second * texture_size, texture_size, texture_size, fake_atlas_size, fake_atlas_size, pixels); + } } // Init void init_atlas() { - // Disable The gui_blocks Atlas Which Contains Pre-Rendered Textures For Blocks In The Inventory - if (feature_has("Disable \"gui_blocks\" Atlas", server_disabled)) { - unsigned char disable_gui_blocks_atlas_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop" - patch((void *) 0x63c2c, disable_gui_blocks_atlas_patch); - // Fix Grass And Leaves Inventory Rendering When The gui_blocks Atlas Is Disabled - overwrite_calls(ItemRenderer_renderGuiItemCorrect, ItemRenderer_renderGuiItemCorrect_injection_one); - // Fix Furnace UI - overwrite_calls(Tesselator_begin, Tesselator_begin_injection); - overwrite_calls(Tesselator_color, Tesselator_color_injection); - overwrite_call((void *) 0x32324, (void *) FurnaceScreen_render_ItemRenderer_renderGuiItem_one_injection); - overwrite_call((void *) 0x1e21c, (void *) InventoryPane_renderBatch_Tesselator_color_injection); - overwrite_calls(ItemRenderer_renderGuiItem_two, ItemRenderer_renderGuiItem_two_injection_one); - // Fix Inventory Scrolling - overwrite_calls(ItemRenderer_renderGuiItem_two, ItemRenderer_renderGuiItem_two_injection_two); - overwrite_calls(ItemRenderer_renderGuiItemCorrect, ItemRenderer_renderGuiItemCorrect_injection_two); + // Generate Atlas + if (feature_has("Regenerate \"gui_blocks\" Atlas", server_disabled)) { + misc_run_on_init(generate_atlas); + overwrite_calls(CropTile_getTexture2, CropTile_getTexture2_injection); + overwrite_calls(ItemRenderer_renderGuiItem_two, ItemRenderer_renderGuiItem_two_injection); + overwrite_call((void *) 0x26f50, (void *) Gui_renderToolBar_GuiComponent_blit_injection); } } diff --git a/mods/src/f3/f3.cpp b/mods/src/f3/f3.cpp index cc4c22876a..d64022cedb 100644 --- a/mods/src/f3/f3.cpp +++ b/mods/src/f3/f3.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -124,7 +125,7 @@ static std::vector get_debug_info_right(const Minecraft *minecraft) // Entity Entity *entity = target.entity; x = entity->x; - y = entity->y; + y = entity->y - entity->height_offset; z = entity->z; type = "Entity"; type_info.push_back("Type ID: " + std::to_string(entity->getEntityTypeId())); // TODO: Specify name when RJ PR is merged @@ -151,7 +152,7 @@ static std::vector get_debug_info_right(const Minecraft *minecraft) // Render Text With Background static constexpr uint32_t debug_background_color = 0x90505050; static constexpr int debug_text_color = 0xe0e0e0; -static void render_debug_line(Gui *gui, const std::string &line, int x, const int y, const bool right_aligned) { +static void render_debug_line(Gui *gui, const std::string &line, int x, const int y, const bool right_aligned, const int pass) { // Draw Background const int width = gui->minecraft->font->width(line); if (width == 0) { @@ -165,26 +166,46 @@ static void render_debug_line(Gui *gui, const std::string &line, int x, const in int y1 = y - 1; int x2 = x + width; int y2 = y + line_height; - gui->fill(x1, y1, x2, y2, debug_background_color); + if (pass == 0) { + gui->fill(x1, y1, x2, y2, debug_background_color); + } // Draw Text - gui->minecraft->font->draw(line, float(x), float(y), debug_text_color); + if (pass == 1) { + gui->minecraft->font->draw(line, float(x), float(y), debug_text_color); + } } // Draw Debug Information static bool debug_info_shown = false; static constexpr int debug_margin = 2; static constexpr int debug_line_padding = 1; -static void render_debug_info(Gui *self, const std::vector &info, const bool right_aligned) { +static void render_debug_info(Gui *self, const std::vector &info, const bool right_aligned, const int pass) { int y = debug_margin; for (const std::string &line : info) { - render_debug_line(self, line, debug_margin, y, right_aligned); + render_debug_line(self, line, debug_margin, y, right_aligned, pass); y += line_height; y += debug_line_padding; } } static void Gui_renderDebugInfo_injection(__attribute__((unused)) Gui_renderDebugInfo_t original, Gui *self) { if (debug_info_shown) { - render_debug_info(self, get_debug_info_left(self->minecraft), false); - render_debug_info(self, get_debug_info_right(self->minecraft), true); + for (int pass = 0; pass < 2; pass++) { + Tesselator &t = Tesselator::instance; + t.begin(GL_QUADS); + t.voidBeginAndEndCalls(true); + render_debug_info(self, get_debug_info_left(self->minecraft), false, pass); + render_debug_info(self, get_debug_info_right(self->minecraft), true, pass); + t.voidBeginAndEndCalls(false); + if (pass == 0) { + media_glEnable(GL_BLEND); + media_glDisable(GL_TEXTURE_2D); + media_glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + t.draw(); + if (pass == 0) { + media_glEnable(GL_TEXTURE_2D); + media_glDisable(GL_BLEND); + } + } } } diff --git a/mods/src/game-mode/game-mode.cpp b/mods/src/game-mode/game-mode.cpp index 496101f6f0..59b037ae7d 100644 --- a/mods/src/game-mode/game-mode.cpp +++ b/mods/src/game-mode/game-mode.cpp @@ -8,7 +8,9 @@ static int is_survival = -1; // Patch Game Mode -static void set_is_survival(bool new_is_survival) { +static void *survival_mode_constructor; +static void *creative_mode_constructor; +static void set_is_survival(const bool new_is_survival) { if (is_survival != new_is_survival) { DEBUG("Setting Game Mode: %s", new_is_survival ? "Survival" : "Creative"); @@ -21,7 +23,7 @@ static void set_is_survival(bool new_is_survival) { patch((void *) 0x16ee4, size_patch); // Replace Default CreatorMode Constructor With CreatorMode Or SurvivalMode Constructor - overwrite_call((void *) 0x16ef4, new_is_survival ? (void *) SurvivalMode_constructor->get(true) : (void *) CreatorMode_constructor->get(true)); + overwrite_call((void *) 0x16ef4, new_is_survival ? survival_mode_constructor : creative_mode_constructor); is_survival = new_is_survival; } @@ -50,6 +52,8 @@ static unsigned char *Minecraft_getCreator_injection(Minecraft_getCreator_t orig void init_game_mode() { // Dynamic Game Mode Switching if (feature_has("Implement Game-Mode Switching", server_enabled)) { + survival_mode_constructor = (void *) SurvivalMode_constructor->get(true); + creative_mode_constructor = (void *) CreatorMode_constructor->get(true); set_is_survival(true); overwrite_calls(Minecraft_setIsCreativeMode, Minecraft_setIsCreativeMode_injection); diff --git a/mods/src/init/init.cpp b/mods/src/init/init.cpp index 4f60e7b853..075769cdaa 100644 --- a/mods/src/init/init.cpp +++ b/mods/src/init/init.cpp @@ -4,7 +4,6 @@ #include __attribute__((constructor)) static void init() { - media_ensure_loaded(); reborn_init_patch(); thunk_enabler = reborn_thunk_enabler; run_tests(); diff --git a/mods/src/misc/api.cpp b/mods/src/misc/api.cpp index 28621366da..fc015523b0 100644 --- a/mods/src/misc/api.cpp +++ b/mods/src/misc/api.cpp @@ -47,6 +47,12 @@ HOOK(media_swap_buffers, void, ()) { } // API +void misc_run_on_init(const std::function &func) { + overwrite_calls(Minecraft_init, [func](Minecraft_init_t original, Minecraft *self) { + original(self); + func(self); + }); +} void misc_run_on_update(const std::function &func) { overwrite_calls(Minecraft_update, [func](Minecraft_update_t original, Minecraft *self) { original(self); @@ -115,7 +121,7 @@ void misc_render_background(int color, const Minecraft *minecraft, const int x, media_glColor4f(1, 1, 1, 1); minecraft->textures->loadAndBindTexture("gui/background.png"); Tesselator *t = &Tesselator::instance; - t->begin(7); + t->begin(GL_QUADS); t->color(color, color, color, 255); float x1 = x; float x2 = x + width; diff --git a/mods/src/misc/graphics.cpp b/mods/src/misc/graphics.cpp index ac6fc7b5bf..e721581f45 100644 --- a/mods/src/misc/graphics.cpp +++ b/mods/src/misc/graphics.cpp @@ -119,7 +119,7 @@ static void render_fire(EntityRenderer *self, Entity *entity, const float x, flo media_glColor4f(1, 1, 1, 1); float zo = 0; int ss = 0; - t.begin(7); + t.begin(GL_QUADS); while (h > 0) { constexpr float xo = 0.0f; float u0; @@ -220,7 +220,7 @@ static void render_shadow(const EntityRenderer *self, Entity *entity, const floa const float yo = y - ey; const float zo = z - ez; Tesselator &tt = Tesselator::instance; - tt.begin(7); + tt.begin(GL_QUADS); for (int xt = x0; xt <= x1; xt++) { for (int yt = y0; yt <= y1; yt++) { for (int zt = z0; zt <= z1; zt++) { diff --git a/mods/src/misc/logging.cpp b/mods/src/misc/logging.cpp index a3b223c19f..ee6ddbfaa1 100644 --- a/mods/src/misc/logging.cpp +++ b/mods/src/misc/logging.cpp @@ -8,7 +8,6 @@ #include // Print Chat To Log -static bool Gui_addMessage_recursing = false; static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const std::string &text) { // Sanitize Message char *new_message = strdup(text.c_str()); @@ -17,9 +16,10 @@ static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const const std::string cpp_str = new_message; // Process Message - if (!Gui_addMessage_recursing) { + static bool recursing = false; + if (!recursing) { // Start Recursing - Gui_addMessage_recursing = true; + recursing = true; // Print Log Message char *safe_message = from_cp437(new_message); @@ -30,7 +30,7 @@ static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const original(gui, cpp_str); // End Recursing - Gui_addMessage_recursing = false; + recursing = false; } else { // Call Original Method original(gui, cpp_str); diff --git a/mods/src/misc/misc.cpp b/mods/src/misc/misc.cpp index 31d57d5498..c5a16795e6 100644 --- a/mods/src/misc/misc.cpp +++ b/mods/src/misc/misc.cpp @@ -592,6 +592,13 @@ void init_misc() { overwrite_call((void *) 0xb198c, (void *) Dimension_isValidSpawn_Level_getTopTile_injection); } + // Disable overwrite_calls() After Minecraft::init + misc_run_on_init([](__attribute__((unused)) Minecraft *minecraft) { + thunk_enabler = [](__attribute__((unused)) void *a, __attribute__((unused)) void *b) -> void * { + IMPOSSIBLE(); + }; + }); + // Init Other Components _init_misc_tinting(); _init_misc_ui(); diff --git a/mods/src/misc/ui.cpp b/mods/src/misc/ui.cpp index 73653f9f50..17e675366a 100644 --- a/mods/src/misc/ui.cpp +++ b/mods/src/misc/ui.cpp @@ -261,12 +261,6 @@ void _init_misc_ui() { overwrite_calls(Screen_render, Screen_render_injection); } - // Remove Invalid Item Background (A Red Background That Appears For Items That Are Not Included In The gui_blocks Atlas) - if (feature_has("Remove Invalid Item Background", server_disabled)) { - unsigned char invalid_item_background_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop" - patch((void *) 0x63c98, invalid_item_background_patch); - } - // Close Current Screen On Death To Prevent Bugs if (feature_has("Close Current Screen On Death", server_disabled)) { overwrite_calls(LocalPlayer_die, LocalPlayer_die_injection); diff --git a/mods/src/multidraw/README.md b/mods/src/multidraw/README.md new file mode 100644 index 0000000000..842521087b --- /dev/null +++ b/mods/src/multidraw/README.md @@ -0,0 +1,2 @@ +# `multidraw` Mod +This mod optimizes rendering using `glMultiDrawArrays`. \ No newline at end of file diff --git a/mods/src/screenshot/screenshot.cpp b/mods/src/screenshot/screenshot.cpp index 18524f747c..e2b9764e2d 100644 --- a/mods/src/screenshot/screenshot.cpp +++ b/mods/src/screenshot/screenshot.cpp @@ -84,7 +84,7 @@ void screenshot_take(Gui *gui) { const int size = height * line_size; // Read Pixels - unsigned char *pixels = (unsigned char *) malloc(size); + unsigned char *pixels = new unsigned char[size]; ALLOC_CHECK(pixels); media_glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); @@ -101,7 +101,7 @@ void screenshot_take(Gui *gui) { } // Free - free(pixels); + delete[] pixels; } // Init diff --git a/mods/src/shading/README.md b/mods/src/shading/README.md new file mode 100644 index 0000000000..5dcdec5d94 --- /dev/null +++ b/mods/src/shading/README.md @@ -0,0 +1,2 @@ +# `shading` Mod +This mod implements proper entity shading using OpenGL lighting. \ No newline at end of file diff --git a/mods/src/shading/tesselator.cpp b/mods/src/shading/tesselator.cpp index 84307dd529..3bcb25f950 100644 --- a/mods/src/shading/tesselator.cpp +++ b/mods/src/shading/tesselator.cpp @@ -97,7 +97,7 @@ static void Tesselator_draw_injection(Tesselator *self) { if (vertices > 0) { const GLuint buffer = get_next_buffer(); media_glBindBuffer(GL_ARRAY_BUFFER, buffer); - media_glBufferData(GL_ARRAY_BUFFER, vertices * sizeof(CustomVertex), CustomTesselator::instance.vertices, GL_STATIC_DRAW); + media_glBufferData(GL_ARRAY_BUFFER, vertices * sizeof(CustomVertex), CustomTesselator::instance.vertices, GL_STREAM_DRAW); if (self->has_texture) { media_glTexCoordPointer(2, GL_FLOAT, sizeof(CustomVertex), (void *) offsetof(CustomVertex, uv)); media_glEnableClientState(GL_TEXTURE_COORD_ARRAY); diff --git a/mods/src/skin/loader.cpp b/mods/src/skin/loader.cpp index 311208bb3a..4653d55ea1 100644 --- a/mods/src/skin/loader.cpp +++ b/mods/src/skin/loader.cpp @@ -27,7 +27,7 @@ static std::vector &get_pending_skins() { return pending_skins; } static pthread_mutex_t pending_skins_lock = PTHREAD_MUTEX_INITIALIZER; -static void load_pending_skins(__attribute__((unused)) Minecraft *minecraft) { +static void load_pending_skins(Minecraft *minecraft) { // Lock pthread_mutex_lock(&pending_skins_lock); @@ -44,7 +44,8 @@ static void load_pending_skins(__attribute__((unused)) Minecraft *minecraft) { GLint last_texture; media_glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); media_glBindTexture(GL_TEXTURE_2D, skin.texture_id); - media_glTexSubImage2D_with_scaling(GL_TEXTURE_2D, 0, 0, 0, width, height, SKIN_WIDTH, SKIN_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, img); + const Texture *texture = minecraft->textures->getTemporaryTextureData(skin.texture_id); + media_glTexSubImage2D_with_scaling(texture, 0, 0, width, height, SKIN_WIDTH, SKIN_HEIGHT, img); media_glBindTexture(GL_TEXTURE_2D, last_texture); // Free @@ -114,7 +115,7 @@ static void *loader_thread(void *user_data) { } // Intercept Texture Creation -static int32_t Textures_assignTexture_injection(Textures_assignTexture_t original, Textures *textures, const std::string &name, unsigned char *data) { +static int32_t Textures_assignTexture_injection(Textures_assignTexture_t original, Textures *textures, const std::string &name, const Texture &data) { // Call Original Method const int32_t id = original(textures, name, data); diff --git a/mods/src/textures/textures.cpp b/mods/src/textures/textures.cpp index 77d21f274d..ee9ff9d98b 100644 --- a/mods/src/textures/textures.cpp +++ b/mods/src/textures/textures.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "textures-internal.h" @@ -20,65 +21,20 @@ static void Minecraft_tick_injection(const Minecraft *minecraft) { // Tick Dynamic Textures Textures *textures = minecraft->textures; if (textures != nullptr) { - textures->tick(true); - } -} - -// Store Texture Sizes -struct texture_data { - GLuint id; - GLsizei width; - GLsizei height; -}; -static std::vector &get_texture_data() { - static std::vector data; - return data; -} -HOOK(media_glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels)) { - // Store - texture_data data = {}; - media_glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &data.id); - data.width = width; - data.height = height; - get_texture_data().push_back(data); - - // Call Original Method - real_media_glTexImage2D()(target, level, internalformat, width, height, border, format, type, pixels); -} -HOOK(media_glDeleteTextures, void, (GLsizei n, const GLuint *textures)) { - // Remove Old Data - for (int i = 0; i < n; i++) { - const GLuint id = textures[i]; - std::vector::iterator it = get_texture_data().begin(); - while (it != get_texture_data().end()) { - const texture_data data = *it; - if (data.id == id) { - it = get_texture_data().erase(it); - } else { - ++it; + for (DynamicTexture *texture : textures->dynamic_textures) { + texture->tick(); + texture->bindTexture(textures); + for (int x = 0; x < texture->texture_size; x++) { + for (int y = 0; y < texture->texture_size; y++) { + const Texture *data = textures->getTemporaryTextureData(textures->current_texture); + const int x_offset = 16 * ((texture->texture_index % 16) + x); + const int y_offset = 16 * ((texture->texture_index / 16) + y); + media_glTexSubImage2D_with_scaling(data, x_offset, y_offset, 16, 16, 256, 256, texture->pixels); + } } + atlas_update_tile(textures, texture->texture_index, texture->pixels); } } - - // Call Original Method - real_media_glDeleteTextures()(n, textures); -} -static void get_texture_size(const GLuint id, GLsizei *width, GLsizei *height) { - // Iterate - std::vector::iterator it = get_texture_data().begin(); - while (it != get_texture_data().end()) { - const texture_data data = *it; - if (data.id == id) { - // Found - *width = data.width; - *height = data.height; - return; - } - ++it; - } - // Not Found - *width = 0; - *height = 0; } // Scale Texture (Remember To Free) @@ -94,42 +50,33 @@ static int get_line_size(const int width) { } return line_size; } -static void *scale_texture(const unsigned char *src, const GLsizei old_width, const GLsizei old_height, const GLsizei new_width, const GLsizei new_height) { +static unsigned char *scale_texture(const unsigned char *src, const GLsizei old_width, const GLsizei old_height, const GLsizei new_width, const GLsizei new_height) { const int old_line_size = get_line_size(old_width); const int new_line_size = get_line_size(new_width); - // Allocate - unsigned char *dst = (unsigned char *) malloc(new_height * new_line_size); - ALLOC_CHECK(dst); - + unsigned char *dst = new unsigned char[new_height * new_line_size]; // Scale for (int new_x = 0; new_x < new_width; new_x++) { - const int old_x = (int) (((float) new_x / (float) new_width) * (float) old_width); for (int new_y = 0; new_y < new_height; new_y++) { + const int old_x = (int) (((float) new_x / (float) new_width) * (float) old_width); const int old_y = (int) (((float) new_y / (float) new_height) * (float) old_height); - // Find Position const int new_position = (new_y * new_line_size) + (new_x * PIXEL_SIZE); const int old_position = (old_y * old_line_size) + (old_x * PIXEL_SIZE); - // Copy static_assert(sizeof (int32_t) == PIXEL_SIZE, "Pixel Size Doesn't Match 32-Bit Integer Size"); *(int32_t *) &dst[new_position] = *(int32_t *) &src[old_position]; } } - // Return return dst; } // Scale Animated Textures -void media_glTexSubImage2D_with_scaling(const GLenum target, const GLint level, const GLint xoffset, const GLint yoffset, const GLsizei width, const GLsizei height, const GLsizei normal_texture_width, const GLsizei normal_texture_height, const GLenum format, const GLenum type, const void *pixels) { +void media_glTexSubImage2D_with_scaling(const Texture *target, const GLint xoffset, const GLint yoffset, const GLsizei width, const GLsizei height, const GLsizei normal_texture_width, const GLsizei normal_texture_height, const void *pixels) { // Get Current Texture Size - GLint current_texture; - media_glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture); - GLsizei texture_width; - GLsizei texture_height; - get_texture_size(current_texture, &texture_width, &texture_height); + const GLsizei texture_width = target->width; + const GLsizei texture_height = target->height; // Calculate Factor const float width_factor = ((float) texture_width) / ((float) normal_texture_width); @@ -138,31 +85,22 @@ void media_glTexSubImage2D_with_scaling(const GLenum target, const GLint level, // Only Scale If Needed if (width_factor == 1.0f && height_factor == 1.0f) { // No Scaling - media_glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); + media_glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); } else { - // Check - if (format != GL_RGBA || type != GL_UNSIGNED_BYTE) { - // Pixels Must Be 4 Bytes - ERR("Unsupported Texture Format For Scaling"); - } - // Scale - const GLsizei new_width = width * width_factor; - const GLsizei new_height = height * height_factor; - void *new_pixels = scale_texture((const unsigned char *) pixels, width, height, new_width, new_height); + const GLsizei new_width = GLsizei(float(width) * width_factor); + const GLsizei new_height = GLsizei(float(height) * height_factor); + const unsigned char *new_pixels = scale_texture((const unsigned char *) pixels, width, height, new_width, new_height); // Call Original Method - const GLint new_xoffset = xoffset * width_factor; - const GLint new_yoffset = yoffset * height_factor; - media_glTexSubImage2D(target, level, new_xoffset, new_yoffset, new_width, new_height, format, type, new_pixels); + const GLint new_xoffset = GLint(float(xoffset) * width_factor); + const GLint new_yoffset = GLint(float(yoffset) * height_factor); + media_glTexSubImage2D(GL_TEXTURE_2D, 0, new_xoffset, new_yoffset, new_width, new_height, GL_RGBA, GL_UNSIGNED_BYTE, new_pixels); // Free - free(new_pixels); + delete[] new_pixels; } } -static void Textures_tick_glTexSubImage2D_injection(const GLenum target, const GLint level, const GLint xoffset, const GLint yoffset, const GLsizei width, const GLsizei height, const GLenum format, const GLenum type, const void *pixels) { - media_glTexSubImage2D_with_scaling(target, level, xoffset, yoffset, width, height, 256, 256, format, type, pixels); -} // Load Textures static Texture AppPlatform_linux_loadTexture_injection(__attribute__((unused)) AppPlatform_linux_loadTexture_t original, __attribute__((unused)) AppPlatform_linux *app_platform, const std::string &path, const bool b) { @@ -231,11 +169,10 @@ void init_textures() { _init_textures_lava(animated_water, animated_lava, animated_fire); } - // Scale Animated Textures - if (feature_has("Property Scale Animated Textures", server_disabled)) { - overwrite_call((void *) 0x53274, (void *) Textures_tick_glTexSubImage2D_injection); - } - // Load Textures overwrite_calls(AppPlatform_linux_loadTexture, AppPlatform_linux_loadTexture_injection); + + // Stop Reloading Textures On Resize + unsigned char texture_reset_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop" + patch((void *) 0x126b4, texture_reset_patch); } diff --git a/mods/src/title-screen/title-screen.cpp b/mods/src/title-screen/title-screen.cpp index bc01fa75c7..dfc6be44d9 100644 --- a/mods/src/title-screen/title-screen.cpp +++ b/mods/src/title-screen/title-screen.cpp @@ -109,9 +109,9 @@ static void StartMenuScreen_render_Screen_renderBackground_injection(StartMenuSc const float y = float(get_title_y(self)); constexpr int w = modern_title_width / 2; constexpr int h = modern_title_height; - Tesselator& t = Tesselator::instance; + Tesselator &t = Tesselator::instance; media_glColor4f(1, 1, 1, 1); - t.begin(7); + t.begin(GL_QUADS); t.vertexUV(x - w, y + h, self->z, 0, 1); t.vertexUV(x + w, y + h, self->z, 1, 1); t.vertexUV(x + w, y, self->z, 1, 0); diff --git a/symbols/CMakeLists.txt b/symbols/CMakeLists.txt index 995e416b21..da35d97763 100644 --- a/symbols/CMakeLists.txt +++ b/symbols/CMakeLists.txt @@ -161,6 +161,8 @@ set(SRC src/tile/GrassTile.def src/tile/HeavyTile.def src/tile/EntityTile.def + src/tile/Bush.def + src/tile/CropTile.def src/misc/Strings.def src/misc/I18n.def src/misc/SimpleFoodData.def diff --git a/symbols/src/game/Minecraft.def b/symbols/src/game/Minecraft.def index 54dbd6a27b..85ce5dea49 100644 --- a/symbols/src/game/Minecraft.def +++ b/symbols/src/game/Minecraft.def @@ -22,6 +22,7 @@ virtual-method void update() = 0x24; virtual-method int handleBack(bool do_nothing) = 0x34; virtual-method void init() = 0x38; virtual-method void selectLevel(const std::string &level_dir, const std::string &level_name, const LevelSettings &settings) = 0x40; +virtual-method AppPlatform *platform() = 0x8; property int screen_width = 0x20; property int screen_height = 0x24; diff --git a/symbols/src/textures/Textures.def b/symbols/src/textures/Textures.def index a23edb4fad..a051f53309 100644 --- a/symbols/src/textures/Textures.def +++ b/symbols/src/textures/Textures.def @@ -1,8 +1,15 @@ +size 0x4c; + +constructor (Options *options, AppPlatform *app_platform) = 0x530e4; +method void *destructor() = 0x53458; + method void tick(bool param_1) = 0x531c4; method uint loadAndBindTexture(const std::string &name) = 0x539cc; method uint loadTexture(const std::string &name, bool param_1) = 0x53800; -method uint assignTexture(const std::string &name, uchar *data) = 0x5354c; +method uint assignTexture(const std::string &name, const Texture &data) = 0x5354c; method void addDynamicTexture(DynamicTexture *texture) = 0x534f8; method Texture *getTemporaryTextureData(uint id) = 0x53168; -property bool blur = 0x39; \ No newline at end of file +property bool blur = 0x39; +property std::vector dynamic_textures = 0x40; +property uint current_texture = 0x3c; \ No newline at end of file diff --git a/symbols/src/tile/Bush.def b/symbols/src/tile/Bush.def new file mode 100644 index 0000000000..79423d05e9 --- /dev/null +++ b/symbols/src/tile/Bush.def @@ -0,0 +1,3 @@ +extends Tile; + +vtable 0x110fd0; \ No newline at end of file diff --git a/symbols/src/tile/CropTile.def b/symbols/src/tile/CropTile.def new file mode 100644 index 0000000000..38baa42290 --- /dev/null +++ b/symbols/src/tile/CropTile.def @@ -0,0 +1,3 @@ +extends Bush; + +vtable 0x1110f8; \ No newline at end of file