800th Commit! Regenerating gui_blocks And Performance Fixes

This commit is contained in:
TheBrokenRail 2024-10-26 02:00:16 -04:00
parent ee44a0ce88
commit 69fcc6cdb3
39 changed files with 476 additions and 269 deletions

@ -1 +1 @@
Subproject commit c13072404d3bb7ffe6b58fea72393c305c1f5eab
Subproject commit 377f9ddbc4747ca3a640231d259c0e6fcc71b4b0

View File

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

View File

@ -4,6 +4,7 @@
#include <vector>
#include <libreborn/libreborn.h>
#include <trampoline/types.h>
#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

View File

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

View File

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

View File

@ -2,7 +2,6 @@
#include <SDL/SDL.h>
#include <media-layer/core.h>
#include <libreborn/libreborn.h>
#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
}

View File

@ -0,0 +1,40 @@
#include "media.h"
#include <GLES/gl.h>
// 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++;
}

View File

@ -5,15 +5,21 @@
#include <libreborn/libreborn.h>
// 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);
@ -244,3 +246,9 @@ 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);
}

View File

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

View File

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

View File

@ -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
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<GLuint>());
GLenum target = args.next<GLenum>();
int32_t size = args.next<int32_t>();
#ifdef MCPI_RUNTIME_IS_QEMU
const unsigned char *data = nullptr;
uint32_t data_addr = args.next<uint32_t>();
if (data_addr != 0) {
data = (const unsigned char *) (uintptr_t) (QEMU_GUEST_BASE + data_addr);
}
#else
const unsigned char *data = args.next_arr<unsigned char>();
#endif
GLenum usage = args.next<GLenum>();
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<GLuint>());
GLenum target = args.next<GLenum>();
int32_t offset = args.next<int32_t>();
#ifdef MCPI_RUNTIME_IS_QEMU
int32_t size = args.next<int32_t>();
const unsigned char *data = args.next_arr<unsigned char>();
uint32_t data_addr = args.next<uint32_t>();
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<unsigned char>(&size);
#endif
func(target, offset, size, data);
return 0;
#endif

View File

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

View File

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

View File

@ -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<char>());
#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<int32_t>();
const int height = args.next<int32_t>();
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
}

View File

@ -0,0 +1,5 @@
#pragma once
extern "C" {
void atlas_update_tile(Textures *textures, int texture, const unsigned char *pixels);
}

View File

@ -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<void(Minecraft *)> &func);
void misc_run_on_update(const std::function<void(Minecraft *)> &func);
void misc_run_on_tick(const std::function<void(Minecraft *)> &func);
void misc_run_on_recipes_setup(const std::function<void(Recipes *)> &func);

View File

@ -2,6 +2,9 @@
#include <GLES/gl.h>
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);
}

View File

@ -1,2 +1,2 @@
# `atlas` Mod
This mod allows disabling the `gui_blocks` atlas.
This mod allows regenerating the `gui_blocks` atlas at runtime.

View File

@ -2,124 +2,229 @@
#include <libreborn/libreborn.h>
#include <symbols/minecraft.h>
#include <media-layer/core.h>
#include <mods/feature/feature.h>
#include <mods/misc/misc.h>
#include <mods/textures/textures.h>
#include <mods/atlas/atlas.h>
#include <mods/init/init.h>
// 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<int, std::pair<int, int>> atlas_key_to_pos;
static std::unordered_map<int, std::vector<std::pair<int, int>>> 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<int, int> 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<int, int> 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<int, int> &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<int, int> &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);
}
}

View File

@ -5,6 +5,7 @@
#include <symbols/minecraft.h>
#include <libreborn/libreborn.h>
#include <GLES/gl.h>
#include <mods/misc/misc.h>
#include <mods/feature/feature.h>
@ -124,7 +125,7 @@ static std::vector<std::string> 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<std::string> 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;
if (pass == 0) {
gui->fill(x1, y1, x2, y2, debug_background_color);
}
// Draw Text
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<std::string> &info, const bool right_aligned) {
static void render_debug_info(Gui *self, const std::vector<std::string> &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);
}
}
}
}

View File

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

View File

@ -4,7 +4,6 @@
#include <symbols/minecraft.h>
__attribute__((constructor)) static void init() {
media_ensure_loaded();
reborn_init_patch();
thunk_enabler = reborn_thunk_enabler;
run_tests();

View File

@ -47,6 +47,12 @@ HOOK(media_swap_buffers, void, ()) {
}
// API
void misc_run_on_init(const std::function<void(Minecraft *)> &func) {
overwrite_calls(Minecraft_init, [func](Minecraft_init_t original, Minecraft *self) {
original(self);
func(self);
});
}
void misc_run_on_update(const std::function<void(Minecraft *)> &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;

View File

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

View File

@ -8,7 +8,6 @@
#include <mods/feature/feature.h>
// 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);

View File

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

View File

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

View File

@ -0,0 +1,2 @@
# `multidraw` Mod
This mod optimizes rendering using `glMultiDrawArrays`.

View File

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

View File

@ -0,0 +1,2 @@
# `shading` Mod
This mod implements proper entity shading using OpenGL lighting.

View File

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

View File

@ -27,7 +27,7 @@ static std::vector<pending_skin> &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);

View File

@ -10,6 +10,7 @@
#include <mods/misc/misc.h>
#include <mods/feature/feature.h>
#include <mods/textures/textures.h>
#include <mods/atlas/atlas.h>
#include <mods/init/init.h>
#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<texture_data> &get_texture_data() {
static std::vector<texture_data> 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<texture_data>::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<texture_data>::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, &current_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);
}

View File

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

View File

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

View File

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

View File

@ -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;
property std::vector<DynamicTexture *> dynamic_textures = 0x40;
property uint current_texture = 0x3c;

View File

@ -0,0 +1,3 @@
extends Tile;
vtable 0x110fd0;

View File

@ -0,0 +1,3 @@
extends Bush;
vtable 0x1110f8;