minecraft-pi-reborn/mods/src/textures/textures.cpp

226 lines
7.1 KiB
C++
Raw Normal View History

2021-09-28 18:04:05 +00:00
#include <vector>
#include <assert.h>
#include <cstdint>
#include <GLES/gl.h>
2021-01-27 21:26:19 +00:00
#include <libreborn/libreborn.h>
2021-09-12 03:18:12 +00:00
#include <symbols/minecraft.h>
2020-12-02 23:18:49 +00:00
2022-06-25 21:30:08 +00:00
#include <mods/misc/misc.h>
#include <mods/feature/feature.h>
2023-10-19 05:23:34 +00:00
#include <mods/textures/textures.h>
2022-06-25 21:30:08 +00:00
#include <mods/init/init.h>
2020-12-02 23:18:49 +00:00
2023-11-11 05:44:26 +00:00
#include "stb_image.h"
// Animated Water
2024-01-06 11:30:23 +00:00
static void Minecraft_tick_injection(Minecraft *minecraft) {
2020-12-02 23:18:49 +00:00
// Tick Dynamic Textures
2024-01-06 11:30:23 +00:00
Textures *textures = minecraft->textures;
2020-12-02 23:18:49 +00:00
if (textures != NULL) {
(*Textures_tick)(textures, true);
}
}
2021-09-28 18:04:05 +00:00
// Store Texture Sizes
struct texture_data {
GLint id;
GLsizei width;
GLsizei height;
};
static std::vector<texture_data> &get_texture_data() {
static std::vector<texture_data> data;
return data;
}
HOOK(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;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &data.id);
data.width = width;
data.height = height;
get_texture_data().push_back(data);
// Call Original Method
ensure_glTexImage2D();
(*real_glTexImage2D)(target, level, internalformat, width, height, border, format, type, pixels);
}
HOOK(glDeleteTextures, void, (GLsizei n, const GLuint *textures)) {
// Remove Old Data
for (int i = 0; i < n; i++) {
2021-09-30 23:37:24 +00:00
GLint id = textures[i];
2021-09-28 18:04:05 +00:00
std::vector<texture_data>::iterator it = get_texture_data().begin();
while (it != get_texture_data().end()) {
texture_data data = *it;
if (data.id == id) {
it = get_texture_data().erase(it);
} else {
++it;
}
}
}
// Call Original Method
ensure_glDeleteTextures();
(*real_glDeleteTextures)(n, textures);
}
static void get_texture_size(GLint id, GLsizei *width, GLsizei *height) {
// Iterate
std::vector<texture_data>::iterator it = get_texture_data().begin();
while (it != get_texture_data().end()) {
texture_data data = *it;
if (data.id == id) {
// Found
*width = data.width;
*height = data.height;
return;
}
++it;
}
// Not Found
*width = 0;
*height = 0;
2021-09-28 18:04:05 +00:00
}
// Scale Texture (Remember To Free)
#define PIXEL_SIZE 4
static int get_line_size(int width) {
int line_size = width * PIXEL_SIZE;
{
// Handle Alignment
int alignment;
glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment);
// Round
int diff = line_size % alignment;
if (diff > 0) {
line_size = line_size + (alignment - diff);
}
}
return line_size;
}
static void *scale_texture(const unsigned char *src, GLsizei old_width, GLsizei old_height, GLsizei new_width, GLsizei new_height) {
int old_line_size = get_line_size(old_width);
int new_line_size = get_line_size(new_width);
// Allocate
unsigned char *dst = (unsigned char *) malloc(new_height * new_line_size);
ALLOC_CHECK(dst);
// Scale
for (int new_x = 0; new_x < new_width; new_x++) {
int old_x = (int) (((float) new_x / (float) new_width) * (float) old_width);
for (int new_y = 0; new_y < new_height; new_y++) {
int old_y = (int) (((float) new_y / (float) new_height) * (float) old_height);
// Find Position
int new_position = (new_y * new_line_size) + (new_x * PIXEL_SIZE);
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
2023-10-19 05:23:34 +00:00
void 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) {
2021-09-28 18:04:05 +00:00
// Get Current Texture Size
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
GLsizei texture_width;
GLsizei texture_height;
get_texture_size(current_texture, &texture_width, &texture_height);
// Calculate Factor
2023-10-19 05:23:34 +00:00
float width_factor = ((float) texture_width) / ((float) normal_texture_width);
float height_factor = ((float) texture_height) / ((float) normal_texture_height);
2021-09-28 18:04:05 +00:00
// Only Scale If Needed
if (width_factor == 1.0f && height_factor == 1.0f) {
// No Scaling
glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
} else {
// Check
if (format != GL_RGBA || type != GL_UNSIGNED_BYTE) {
// Pixels Must Be 4 Bytes
2022-04-15 01:12:42 +00:00
ERR("Unsupported Texture Format For Scaling");
2021-09-28 18:04:05 +00:00
}
// Scale
GLsizei new_width = width * width_factor;
GLsizei new_height = height * height_factor;
void *new_pixels = scale_texture((const unsigned char *) pixels, width, height, new_width, new_height);
// Call Original Method
GLint new_xoffset = xoffset * width_factor;
GLint new_yoffset = yoffset * height_factor;
glTexSubImage2D(target, level, new_xoffset, new_yoffset, new_width, new_height, format, type, new_pixels);
// Free
free(new_pixels);
}
}
2023-10-19 05:23:34 +00:00
static void Textures_tick_glTexSubImage2D_injection(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) {
glTexSubImage2D_with_scaling(target, level, xoffset, yoffset, width, height, 256, 256, format, type, pixels);
}
2021-09-28 18:04:05 +00:00
2023-11-11 05:44:26 +00:00
// Load Textures
2024-01-06 11:30:23 +00:00
static Texture AppPlatform_linux_loadTexture_injection(__attribute__((unused)) AppPlatform_linux *app_platform, std::string *path, bool b) {
2023-11-11 05:44:26 +00:00
Texture out;
2024-01-06 11:30:23 +00:00
std::string real_path = *path;
2023-11-11 05:44:26 +00:00
if (b) {
real_path = "data/images/" + real_path;
}
2024-01-06 11:30:23 +00:00
// Empty Texture
out.width = 0;
out.height = 0;
out.data = NULL;
out.field3_0xc = 0;
out.field4_0x10 = true;
out.field5_0x11 = false;
out.field6_0x14 = 0;
out.field7_0x18 = -1;
2023-11-11 05:44:26 +00:00
// Read Image
int width = 0, height = 0, channels = 0;
stbi_uc *img = stbi_load(real_path.c_str(), &width, &height, &channels, STBI_rgb_alpha);
if (!img)
{
// Failed To Parse Image
WARN("Unable To Load Texture: %s", real_path.c_str());
return out;
}
// Copy Image
unsigned char *img2 = new unsigned char[width * height * channels];
memcpy(img2, img, width * height * channels);
stbi_image_free(img);
// Create Texture
out.width = width;
out.height = height;
out.data = img2;
// Return
return out;
}
// Init
2020-12-02 23:18:49 +00:00
void init_textures() {
// Tick Dynamic Textures (Animated Water)
2022-04-10 00:01:16 +00:00
if (feature_has("Animated Water", server_disabled)) {
misc_run_on_tick(Minecraft_tick_injection);
2020-12-02 23:18:49 +00:00
}
2021-09-28 18:04:05 +00:00
// Scale Animated Textures
overwrite_call((void *) 0x53274, (void *) Textures_tick_glTexSubImage2D_injection);
2023-11-11 05:44:26 +00:00
// Load Textures
2024-01-06 11:30:23 +00:00
overwrite((void *) AppPlatform_linux_loadTexture_non_virtual, (void *) AppPlatform_linux_loadTexture_injection);
2021-06-30 22:38:43 +00:00
}