564 lines
21 KiB
C++
564 lines
21 KiB
C++
#include <cmath>
|
|
|
|
#include <libreborn/libreborn.h>
|
|
#include <symbols/minecraft.h>
|
|
|
|
#include <GLES/gl.h>
|
|
#include <media-layer/core.h>
|
|
|
|
#include <mods/feature/feature.h>
|
|
#include <mods/multidraw/multidraw.h>
|
|
|
|
#include "misc-internal.h"
|
|
|
|
// Properly Generate Buffers
|
|
static void anGenBuffers_injection(__attribute__((unused)) Common_anGenBuffers_t original, const int32_t count, uint32_t *buffers) {
|
|
if (!reborn_is_headless()) {
|
|
glGenBuffers(count, buffers);
|
|
}
|
|
}
|
|
|
|
// Custom Outline Color
|
|
static void LevelRenderer_render_AABB_glColor4f_injection(__attribute__((unused)) GLfloat red, __attribute__((unused)) GLfloat green, __attribute__((unused)) GLfloat blue, __attribute__((unused)) GLfloat alpha) {
|
|
// Set Color
|
|
glColor4f(0, 0, 0, 0.4);
|
|
|
|
// Find Line Width
|
|
const char *custom_line_width = getenv(MCPI_BLOCK_OUTLINE_WIDTH_ENV);
|
|
float line_width;
|
|
if (custom_line_width != nullptr) {
|
|
// Custom
|
|
line_width = strtof(custom_line_width, nullptr);
|
|
} else {
|
|
// Guess
|
|
line_width = 1.5f / Gui::InvGuiScale;
|
|
}
|
|
// Clamp Line Width
|
|
float range[2];
|
|
glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, range);
|
|
if (range[1] < line_width) {
|
|
line_width = range[1];
|
|
} else if (range[0] > line_width) {
|
|
line_width = range[0];
|
|
}
|
|
// Set Line Width
|
|
glLineWidth(line_width);
|
|
}
|
|
|
|
// Java Light Ramp
|
|
static void Dimension_updateLightRamp_injection(__attribute__((unused)) Dimension_updateLightRamp_t original, Dimension *self) {
|
|
// https://github.com/ReMinecraftPE/mcpe/blob/d7a8b6baecf8b3b050538abdbc976f690312aa2d/source/world/level/Dimension.cpp#L92-L105
|
|
for (int i = 0; i <= 15; i++) {
|
|
const float f1 = 1.0f - (((float) i) / 15.0f);
|
|
self->light_ramp[i] = ((1.0f - f1) / (f1 * 3.0f + 1.0f)) * (1.0f - 0.1f) + 0.1f;
|
|
// Default Light Ramp:
|
|
// float fVar4 = 1.0 - ((float) i * 0.0625);
|
|
// self->light_ramp[i] = ((1.0 - fVar4) / (fVar4 * 3.0 + 1.0)) * 0.95 + 0.15;
|
|
}
|
|
}
|
|
|
|
// Sort Chunks
|
|
struct chunk_data {
|
|
Chunk *chunk;
|
|
float distance;
|
|
};
|
|
#define MAX_CHUNKS_SIZE 24336
|
|
static chunk_data data[MAX_CHUNKS_SIZE];
|
|
static void sort_chunks(Chunk **chunks_begin, Chunk **chunks_end, const DistanceChunkSorter sorter) {
|
|
// Calculate Distances
|
|
const int chunks_size = chunks_end - chunks_begin;
|
|
if (chunks_size > MAX_CHUNKS_SIZE) {
|
|
IMPOSSIBLE();
|
|
}
|
|
for (int i = 0; i < chunks_size; i++) {
|
|
Chunk *chunk = chunks_begin[i];
|
|
float distance = chunk->distanceToSqr((Entity *) sorter.mob);
|
|
if ((1024.0 <= distance) && chunk->y < 0x40) {
|
|
distance *= 10.0;
|
|
}
|
|
data[i].chunk = chunk;
|
|
data[i].distance = distance;
|
|
}
|
|
|
|
// Sort
|
|
std::sort(data, data + chunks_size, [](chunk_data &a, chunk_data &b) {
|
|
return a.distance < b.distance;
|
|
});
|
|
for (int i = 0; i < chunks_size; i++) {
|
|
chunks_begin[i] = data[i].chunk;
|
|
}
|
|
}
|
|
|
|
// Fire Rendering
|
|
static void render_fire(EntityRenderer *self, Entity *entity, const float x, float y, const float z) {
|
|
// Check If Entity Is On Fire
|
|
if (!entity->isOnFire()) {
|
|
return;
|
|
}
|
|
// Here Be Decompiled Code
|
|
y -= entity->height_offset;
|
|
const int texture = Tile::fire->texture;
|
|
const int xt = (texture & 0xf) << 4;
|
|
const int yt = texture & 0xf0;
|
|
glPushMatrix();
|
|
glTranslatef(x, y, z);
|
|
const float s = entity->hitbox_width * 1.4f;
|
|
glScalef(s, s, s);
|
|
self->bindTexture("terrain.png");
|
|
Tesselator &t = Tesselator::instance;
|
|
float r = 0.5f;
|
|
float h = entity->hitbox_height / s;
|
|
float yo = entity->y - entity->height_offset - entity->hitbox.y1;
|
|
float player_rot_y = EntityRenderer::entityRenderDispatcher->player_rot_y;
|
|
if (EntityRenderer::entityRenderDispatcher->minecraft->options.third_person == 2) {
|
|
// Handle Front-Facing
|
|
player_rot_y -= 180.f;
|
|
}
|
|
glRotatef(-player_rot_y, 0, 1, 0);
|
|
glTranslatef(0, 0, -0.3f + float(int(h)) * 0.02f);
|
|
glColor4f(1, 1, 1, 1);
|
|
float zo = 0;
|
|
int ss = 0;
|
|
t.begin(7);
|
|
while (h > 0) {
|
|
constexpr float xo = 0.0f;
|
|
float u0;
|
|
float u1;
|
|
float v0;
|
|
float v1;
|
|
if (ss % 2 == 0) {
|
|
u0 = float(xt) / 256.0f;
|
|
u1 = (float(xt) + 15.99f) / 256.0f;
|
|
v0 = float(yt) / 256.0f;
|
|
v1 = (float(yt) + 15.99f) / 256.0f;
|
|
} else {
|
|
u0 = float(xt) / 256.0f;
|
|
u1 = (float(xt) + 15.99f) / 256.0f;
|
|
v0 = (float(yt) + 16) / 256.0f;
|
|
v1 = (float(yt) + 16 + 15.99f) / 256.0f;
|
|
}
|
|
if (ss / 2 % 2 == 0) {
|
|
std::swap(u1, u0);
|
|
}
|
|
t.vertexUV(r - xo, 0 - yo, zo, u1, v1);
|
|
t.vertexUV(-r - xo, 0 - yo, zo, u0, v1);
|
|
t.vertexUV(-r - xo, 1.4f - yo, zo, u0, v0);
|
|
t.vertexUV(r - xo, 1.4f - yo, zo, u1, v0);
|
|
h -= 0.45f;
|
|
yo -= 0.45f;
|
|
r *= 0.9f;
|
|
zo += 0.03f;
|
|
ss++;
|
|
}
|
|
t.draw();
|
|
glPopMatrix();
|
|
}
|
|
|
|
// Entity Shadows
|
|
static void render_shadow_tile(Tile *tile, const float x, const float y, const float z, int xt, int yt, int zt, const float pow, const float r, const float xo, const float yo, const float zo) {
|
|
Tesselator &t = Tesselator::instance;
|
|
if (!tile->isCubeShaped()) {
|
|
return;
|
|
}
|
|
float a = ((pow - (y - (float(yt) + yo)) / 2) * 0.5f) * EntityRenderer::entityRenderDispatcher->level->getBrightness(xt, yt, zt);
|
|
if (a < 0) {
|
|
return;
|
|
} else if (a > 1) {
|
|
a = 1;
|
|
}
|
|
t.color(255, 255, 255, int(a * 255));
|
|
float x0 = float(xt) + tile->x1 + xo;
|
|
float x1 = float(xt) + tile->x2 + xo;
|
|
float y0 = float(yt) + tile->y1 + yo + 1.0f / 64.0f;
|
|
float z0 = float(zt) + tile->z1 + zo;
|
|
float z1 = float(zt) + tile->z2 + zo;
|
|
float u0 = (x - x0) / 2 / r + 0.5f;
|
|
float u1 = (x - x1) / 2 / r + 0.5f;
|
|
float v0 = (z - z0) / 2 / r + 0.5f;
|
|
float v1 = (z - z1) / 2 / r + 0.5f;
|
|
t.vertexUV(x0, y0, z0, u0, v0);
|
|
t.vertexUV(x0, y0, z1, u0, v1);
|
|
t.vertexUV(x1, y0, z1, u1, v1);
|
|
t.vertexUV(x1, y0, z0, u1, v0);
|
|
}
|
|
static void render_shadow(const EntityRenderer *self, Entity *entity, float x, float y, float z, const float a) {
|
|
// Calculate Power
|
|
float pow = 0;
|
|
if (self->shadow_radius > 0) {
|
|
const float dist = EntityRenderer::entityRenderDispatcher->distanceToSqr(entity->x, entity->y, entity->z);
|
|
pow = (1 - dist / (16.0f * 16.0f)) * self->shadow_strength;
|
|
}
|
|
if (pow <= 0) {
|
|
return;
|
|
}
|
|
// Render
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
Textures *textures = EntityRenderer::entityRenderDispatcher->textures;
|
|
textures->loadAndBindTexture("misc/shadow.png");
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
Level *level = EntityRenderer::entityRenderDispatcher->level;
|
|
glDepthMask(false);
|
|
const float r = self->shadow_radius;
|
|
const float ex = entity->old_x + (entity->x - entity->old_x) * a;
|
|
float ey = entity->old_y + (entity->y - entity->old_y) * a + entity->getShadowHeightOffs() - entity->height_offset;
|
|
const float ez = entity->old_z + (entity->z - entity->old_z) * a;
|
|
const int x0 = Mth::floor(ex - r);
|
|
const int x1 = Mth::floor(ex + r);
|
|
const int y0 = Mth::floor(ey - r);
|
|
const int y1 = Mth::floor(ey);
|
|
const int z0 = Mth::floor(ez - r);
|
|
const int z1 = Mth::floor(ez + r);
|
|
const float xo = x - ex;
|
|
const float yo = y - ey;
|
|
const float zo = z - ez;
|
|
Tesselator &tt = Tesselator::instance;
|
|
tt.begin(7);
|
|
for (int xt = x0; xt <= x1; xt++) {
|
|
for (int yt = y0; yt <= y1; yt++) {
|
|
for (int zt = z0; zt <= z1; zt++) {
|
|
const int t = level->getTile(xt, yt - 1, zt);
|
|
if (t > 0 && level->getRawBrightness(xt, yt, zt) > 3) {
|
|
render_shadow_tile(
|
|
Tile::tiles[t],
|
|
x, y + entity->getShadowHeightOffs() - entity->height_offset, z,
|
|
xt, yt, zt,
|
|
pow, r,
|
|
xo, yo + entity->getShadowHeightOffs() - entity->height_offset, zo
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
tt.draw();
|
|
glColor4f(1, 1, 1, 1);
|
|
glDisable(GL_BLEND);
|
|
glDepthMask(true);
|
|
}
|
|
static void EntityRenderDispatcher_assign_injection(EntityRenderDispatcher_assign_t original, EntityRenderDispatcher *self, const uchar entity_id, EntityRenderer *renderer) {
|
|
// Modify Shadow Size
|
|
float new_radius;
|
|
switch (entity_id) {
|
|
case 16:
|
|
case 3: {
|
|
new_radius = 0.5f;
|
|
break;
|
|
}
|
|
case 9:
|
|
case 7:
|
|
case 8: {
|
|
new_radius = 0.7f;
|
|
break;
|
|
}
|
|
case 6: {
|
|
new_radius = 0.3f;
|
|
break;
|
|
}
|
|
default: {
|
|
new_radius = renderer->shadow_radius;
|
|
}
|
|
}
|
|
renderer->shadow_radius = new_radius;
|
|
// Call Original Method
|
|
original(self, entity_id, renderer);
|
|
}
|
|
|
|
// Modify Entity Rendering
|
|
static bool should_render_fire;
|
|
static bool should_render_shadows;
|
|
static void EntityRenderDispatcher_render_EntityRenderer_render_injection(EntityRenderer *self, Entity *entity, float x, float y, float z, float rot, float unknown) {
|
|
// Call Original Method
|
|
self->render(entity, x, y, z, rot, unknown);
|
|
// Render Shadow
|
|
if (should_render_shadows) {
|
|
render_shadow(self, entity, x, y, z, unknown);
|
|
}
|
|
// Render Fire
|
|
if (should_render_fire) {
|
|
render_fire(self, entity, x, y, z);
|
|
}
|
|
}
|
|
|
|
// Nicer Water Rendering
|
|
static bool game_render_anaglyph_color_mask[4];
|
|
static void GameRenderer_render_glColorMask_injection(const bool red, const bool green, const bool blue, const bool alpha) {
|
|
game_render_anaglyph_color_mask[0] = red;
|
|
game_render_anaglyph_color_mask[1] = green;
|
|
game_render_anaglyph_color_mask[2] = blue;
|
|
game_render_anaglyph_color_mask[3] = alpha;
|
|
glColorMask(red, green, blue, alpha);
|
|
}
|
|
static int GameRenderer_render_LevelRenderer_render_injection(LevelRenderer *self, Mob *mob, int param_1, float delta) {
|
|
glColorMask(false, false, false, false);
|
|
const int water_chunks = self->render(mob, param_1, delta);
|
|
glColorMask(true, true, true, true);
|
|
if (self->minecraft->options.anaglyph_3d) {
|
|
glColorMask(game_render_anaglyph_color_mask[0], game_render_anaglyph_color_mask[1], game_render_anaglyph_color_mask[2], game_render_anaglyph_color_mask[3]);
|
|
}
|
|
if (water_chunks > 0) {
|
|
LevelRenderer_renderSameAsLast(self, delta);
|
|
}
|
|
return water_chunks;
|
|
}
|
|
|
|
// Fix grass_carried's Bottom Texture
|
|
static int CarriedTile_getTexture2_injection(CarriedTile_getTexture2_t original, CarriedTile *self, const int face, const int metadata) {
|
|
if (face == 0) return 2;
|
|
return original(self, face, metadata);
|
|
}
|
|
|
|
// Fix Graphics Bug When Switching To First-Person While Sneaking
|
|
static void PlayerRenderer_render_injection(PlayerRenderer *model_renderer, Entity *entity, const float param_2, const float param_3, const float param_4, const float param_5, const float param_6) {
|
|
HumanoidMobRenderer_vtable_base->render((HumanoidMobRenderer *) model_renderer, entity, param_2, param_3, param_4, param_5, param_6);
|
|
HumanoidModel *model = model_renderer->model;
|
|
model->is_sneaking = false;
|
|
}
|
|
|
|
// 3D Chests
|
|
static int32_t Tile_getRenderShape_injection(Tile *tile) {
|
|
if (tile == Tile::chest) {
|
|
// Don't Render "Simple" Chest Model
|
|
return -1;
|
|
} else {
|
|
// Call Original Method
|
|
return tile->getRenderShape();
|
|
}
|
|
}
|
|
static ChestTileEntity *ChestTileEntity_injection(ChestTileEntity_constructor_t original, ChestTileEntity *tile_entity) {
|
|
// Call Original Method
|
|
original(tile_entity);
|
|
|
|
// Enable Renderer
|
|
tile_entity->renderer_id = 1;
|
|
|
|
// Return
|
|
return tile_entity;
|
|
}
|
|
static bool is_rendering_chest = false;
|
|
static void ModelPart_render_injection(ModelPart *model_part, float scale) {
|
|
// Start
|
|
is_rendering_chest = true;
|
|
|
|
// Call Original Method
|
|
model_part->render(scale);
|
|
|
|
// Stop
|
|
is_rendering_chest = false;
|
|
}
|
|
static void Tesselator_vertexUV_injection(Tesselator_vertexUV_t original, Tesselator *tesselator, const float x, const float y, const float z, const float u, float v) {
|
|
// Fix Chest Texture
|
|
if (is_rendering_chest) {
|
|
v /= 2;
|
|
}
|
|
|
|
// Call Original Method
|
|
original(tesselator, x, y, z, u, v);
|
|
}
|
|
static bool ChestTileEntity_shouldSave_injection(__attribute__((unused)) ChestTileEntity_shouldSave_t original, __attribute__((unused)) ChestTileEntity *tile_entity) {
|
|
return true;
|
|
}
|
|
|
|
// Animated 3D Chest
|
|
static ContainerMenu *ContainerMenu_injection(ContainerMenu_constructor_t original, ContainerMenu *container_menu, Container *container, const int32_t param_1) {
|
|
// Call Original Method
|
|
original(container_menu, container, param_1);
|
|
|
|
// Play Animation
|
|
const ChestTileEntity *tile_entity = (ChestTileEntity *) (((unsigned char *) container) - offsetof(ChestTileEntity, container));
|
|
const bool is_client = tile_entity->is_client;
|
|
if (!is_client) {
|
|
container->startOpen();
|
|
}
|
|
|
|
// Return
|
|
return container_menu;
|
|
}
|
|
static ContainerMenu *ContainerMenu_destructor_injection(ContainerMenu_destructor_complete_t original, ContainerMenu *container_menu) {
|
|
// Play Animation
|
|
Container *container = container_menu->container;
|
|
const ChestTileEntity *tile_entity = (ChestTileEntity *) (((unsigned char *) container) - offsetof(ChestTileEntity, container));
|
|
const bool is_client = tile_entity->is_client;
|
|
if (!is_client) {
|
|
container->stopOpen();
|
|
}
|
|
|
|
// Call Original Method
|
|
return original(container_menu);
|
|
}
|
|
|
|
// 3D Dropped Items
|
|
static bool disable_hand_positioning = false;
|
|
static void ItemInHandRenderer_renderItem_glTranslatef_injection(const float x, const float y, const float z) {
|
|
if (disable_hand_positioning) {
|
|
glPopMatrix();
|
|
glPushMatrix();
|
|
} else {
|
|
glTranslatef(x, y, z);
|
|
}
|
|
}
|
|
static void ItemRenderer_render_injection(ItemRenderer_render_t original, ItemRenderer *self, Entity *entity, const float x, const float y, const float z, const float a, const float b) {
|
|
// Get Item
|
|
const ItemEntity *item_entity = (ItemEntity *) entity;
|
|
ItemInstance item = item_entity->item;
|
|
// Check If Item Is Tile
|
|
if (item.id < 256 && TileRenderer::canRender(Tile::tiles[item.id]->getRenderShape())) {
|
|
// Call Original Method
|
|
original(self, entity, x, y, z, a, b);
|
|
} else {
|
|
// 3D Item
|
|
self->random.setSeed(187);
|
|
glPushMatrix();
|
|
|
|
// Count
|
|
int count;
|
|
if (item.count < 2) {
|
|
count = 1;
|
|
} else if (item.count < 16) {
|
|
count = 2;
|
|
} else if (item.count < 32) {
|
|
count = 3;
|
|
} else {
|
|
count = 4;
|
|
}
|
|
|
|
// Bob
|
|
const float age = float(item_entity->age) + b;
|
|
const float bob = (Mth::sin((age / 10.0f) + item_entity->bob_offset) * 0.1f) + 0.1f;
|
|
glTranslatef(x, y + bob, z);
|
|
|
|
// Scale
|
|
glScalef(0.5f, 0.5f, 0.5f);
|
|
|
|
// Spin
|
|
const float spin = ((age / 20.0f) + item_entity->bob_offset) * float(180.0f / M_PI);
|
|
glRotatef(spin, 0, 1, 0);
|
|
|
|
// Position
|
|
constexpr float xo = 0.5f;
|
|
constexpr float yo = 0.25f;
|
|
constexpr float width = 1 / 16.0f;
|
|
constexpr float margin = 0.35f / 16.0f;
|
|
constexpr float zo = width + margin;
|
|
glTranslatef(-xo, -yo, -((zo * float(count)) / 2));
|
|
|
|
// Draw
|
|
disable_hand_positioning = true;
|
|
for (int i = 0; i < count; i++) {
|
|
glTranslatef(0, 0, zo);
|
|
EntityRenderer::entityRenderDispatcher->item_renderer->renderItem(nullptr, &item);
|
|
}
|
|
disable_hand_positioning = false;
|
|
|
|
// Finish
|
|
glPopMatrix();
|
|
}
|
|
}
|
|
|
|
// Init
|
|
void _init_misc_graphics() {
|
|
// Disable V-Sync
|
|
if (feature_has("Disable V-Sync", server_enabled)) {
|
|
media_disable_vsync();
|
|
}
|
|
|
|
// Force EGL
|
|
if (feature_has("Force EGL", server_disabled)) {
|
|
media_force_egl();
|
|
}
|
|
|
|
// Properly Generate Buffers
|
|
if (feature_has("Proper OpenGL Buffer Generation", server_enabled)) {
|
|
overwrite_calls(Common_anGenBuffers, anGenBuffers_injection);
|
|
}
|
|
|
|
// Replace Block Highlight With Outline
|
|
if (feature_has("Replace Block Highlight With Outline", server_disabled)) {
|
|
overwrite_calls(LevelRenderer_renderHitSelect, [](__attribute__((unused)) LevelRenderer_renderHitSelect_t original, LevelRenderer *self, Player *player, const HitResult &hit_result, int i, void *vp, float f) {
|
|
self->renderHitOutline(player, hit_result, i, vp, f);
|
|
});
|
|
unsigned char fix_outline_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
|
|
patch((void *) 0x4d830, fix_outline_patch);
|
|
overwrite_call((void *) 0x4d764, (void *) LevelRenderer_render_AABB_glColor4f_injection);
|
|
}
|
|
|
|
// Properly Hide Block Outline
|
|
if (feature_has("Hide Block Outline When GUI Is Hidden", server_disabled)) {
|
|
overwrite_calls(LevelRenderer_renderHitSelect, [](LevelRenderer_renderHitSelect_t original, LevelRenderer *self, Player *player, const HitResult &hit_result, const int i, void *vp, const float f) {
|
|
if (!self->minecraft->options.hide_gui) {
|
|
original(self, player, hit_result, i, vp, f);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Java Light Ramp
|
|
if (feature_has("Use Java Beta 1.3 Light Ramp", server_disabled)) {
|
|
overwrite_calls(Dimension_updateLightRamp, Dimension_updateLightRamp_injection);
|
|
}
|
|
|
|
// Replace 2011 std::sort With Optimized(TM) Code
|
|
if (feature_has("Optimized Chunk Sorting", server_enabled)) {
|
|
overwrite_calls_manual((void *) 0x51fac, (void *) sort_chunks);
|
|
}
|
|
|
|
// Modify Entity Rendering
|
|
overwrite_call((void *) 0x606c0, (void *) EntityRenderDispatcher_render_EntityRenderer_render_injection);
|
|
should_render_fire = feature_has("Render Fire In Third-Person", server_disabled);
|
|
should_render_shadows = feature_has("Render Entity Shadows", server_disabled);
|
|
if (should_render_shadows) {
|
|
overwrite_calls(EntityRenderDispatcher_assign, EntityRenderDispatcher_assign_injection);
|
|
}
|
|
|
|
// Slightly Nicer Water Rendering
|
|
if (feature_has("Improved Water Rendering", server_disabled)) {
|
|
overwrite_call((void *) 0x49ed4, (void *) GameRenderer_render_glColorMask_injection);
|
|
overwrite_call((void *) 0x4a18c, (void *) GameRenderer_render_LevelRenderer_render_injection);
|
|
unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
|
|
patch((void *) 0x4a12c, nop_patch);
|
|
}
|
|
|
|
// Fix grass_carried's Bottom Texture
|
|
if (feature_has("Fix Carried Grass's Bottom Texture", server_disabled)) {
|
|
overwrite_calls(CarriedTile_getTexture2, CarriedTile_getTexture2_injection);
|
|
}
|
|
|
|
// Fix Graphics Bug When Switching To First-Person While Sneaking
|
|
if (feature_has("Fix Switching Perspective While Sneaking", server_disabled)) {
|
|
patch_vtable(PlayerRenderer_render, PlayerRenderer_render_injection);
|
|
}
|
|
|
|
// 3D Chests
|
|
if (feature_has("3D Chest Model", server_disabled)) {
|
|
overwrite_call((void *) 0x5e830, (void *) Tile_getRenderShape_injection);
|
|
overwrite_calls(ChestTileEntity_constructor, ChestTileEntity_injection);
|
|
overwrite_call((void *) 0x6655c, (void *) ModelPart_render_injection);
|
|
overwrite_call((void *) 0x66568, (void *) ModelPart_render_injection);
|
|
overwrite_call((void *) 0x66574, (void *) ModelPart_render_injection);
|
|
overwrite_calls(Tesselator_vertexUV, Tesselator_vertexUV_injection);
|
|
unsigned char chest_model_patch[4] = {0x13, 0x20, 0xa0, 0xe3}; // "mov r2, #0x13"
|
|
patch((void *) 0x66fc8, chest_model_patch);
|
|
unsigned char chest_color_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
|
|
patch((void *) 0x66404, chest_color_patch);
|
|
|
|
// Animation
|
|
overwrite_calls(ContainerMenu_constructor, ContainerMenu_injection);
|
|
overwrite_calls(ContainerMenu_destructor_complete, ContainerMenu_destructor_injection);
|
|
}
|
|
if (feature_has("Always Save Chest Tile Entities", server_enabled)) {
|
|
overwrite_calls(ChestTileEntity_shouldSave, ChestTileEntity_shouldSave_injection);
|
|
}
|
|
|
|
// 3D Dropped Items
|
|
if (feature_has("3D Dropped Items", server_disabled)) {
|
|
overwrite_calls(ItemRenderer_render, ItemRenderer_render_injection);
|
|
overwrite_call((void *) 0x4bf34, (void *) ItemInHandRenderer_renderItem_glTranslatef_injection);
|
|
}
|
|
|
|
// Don't Render Game In Headless Mode
|
|
if (reborn_is_headless()) {
|
|
overwrite_calls(GameRenderer_render, nop<GameRenderer_render_t, GameRenderer *, float>);
|
|
overwrite_calls(NinecraftApp_initGLStates, nop<NinecraftApp_initGLStates_t, NinecraftApp *>);
|
|
overwrite_calls(Gui_onConfigChanged, nop<Gui_onConfigChanged_t, Gui *, const Config &>);
|
|
overwrite_calls(LevelRenderer_generateSky, nop<LevelRenderer_generateSky_t, LevelRenderer *>);
|
|
}
|
|
} |