diff --git a/launcher/src/client/available-feature-flags b/launcher/src/client/available-feature-flags index f16112ce1d..a372d94fcf 100644 --- a/launcher/src/client/available-feature-flags +++ b/launcher/src/client/available-feature-flags @@ -112,4 +112,5 @@ TRUE Click Buttons On Mouse Down TRUE 3D Dropped Items TRUE Render Entity Shadows TRUE Render Vignette -TRUE Increase Render Chunk Size \ No newline at end of file +TRUE Increase Render Chunk Size +TRUE Proper Entity Shading \ No newline at end of file diff --git a/libreborn/include/libreborn/patch.h b/libreborn/include/libreborn/patch.h index 228b29d5d4..56ad38f294 100644 --- a/libreborn/include/libreborn/patch.h +++ b/libreborn/include/libreborn/patch.h @@ -10,7 +10,7 @@ void reborn_init_patch(); // Replace Call Located At start With A Call To target -void overwrite_call(void *start, void *target); +void overwrite_call(void *start, void *target, bool force_b_instruction = false); // Replace All Calls To Method start With target void *overwrite_calls_manual(void *start, void *target, bool allow_no_callsites = false); diff --git a/libreborn/src/patch/patch.cpp b/libreborn/src/patch/patch.cpp index 0a165a9e0f..a450687da5 100644 --- a/libreborn/src/patch/patch.cpp +++ b/libreborn/src/patch/patch.cpp @@ -21,8 +21,8 @@ static void _overwrite_call_internal(void *start, void *target, const bool use_b // Increment Code Block Position increment_code_block(); } -void overwrite_call(void *start, void *target) { - const bool use_b_instruction = ((unsigned char *) start)[3] == B_INSTRUCTION; +void overwrite_call(void *start, void *target, const bool force_b_instruction) { + const bool use_b_instruction = force_b_instruction || ((unsigned char *) start)[3] == B_INSTRUCTION; _overwrite_call_internal(start, target, use_b_instruction); } diff --git a/media-layer/gles/src/passthrough.cpp b/media-layer/gles/src/passthrough.cpp index 9d63200ebb..36778c5f2d 100644 --- a/media-layer/gles/src/passthrough.cpp +++ b/media-layer/gles/src/passthrough.cpp @@ -228,3 +228,7 @@ GL_FUNC(glGenBuffers, void, (GLsizei n, GLuint *buffers)) void glGenBuffers(const GLsizei n, GLuint *buffers) { real_glGenBuffers()(n, buffers); } +GL_FUNC(glNormalPointer, void, (GLenum type, GLsizei stride, const void *pointer)) +void glNormalPointer(const GLenum type, const GLsizei stride, const void *pointer) { + real_glNormalPointer()(type, stride, pointer); +} \ No newline at end of file diff --git a/media-layer/include/GLES/gl.h b/media-layer/include/GLES/gl.h index e41cda1413..86ca5a5a27 100644 --- a/media-layer/include/GLES/gl.h +++ b/media-layer/include/GLES/gl.h @@ -30,6 +30,7 @@ extern "C" { #define GL_VERTEX_ARRAY 0x8074 #define GL_COLOR_ARRAY 0x8076 #define GL_TEXTURE_COORD_ARRAY 0x8078 +#define GL_NORMAL_ARRAY 0x8075 #define GL_GREATER 0x204 #define GL_ALPHA_TEST 0xbc0 #define GL_TEXTURE_2D 0xde1 @@ -47,6 +48,7 @@ extern "C" { #define GL_TRIANGLES 0x4 #define GL_TRIANGLE_STRIP 0x5 #define GL_TRIANGLE_FAN 0x6 +#define GL_QUADS 0x7 #define GL_FASTEST 0x1101 #define GL_BACK 0x405 #define GL_CULL_FACE 0xb44 @@ -158,6 +160,7 @@ GLenum glGetError(); void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void *data); void glPixelStorei(GLenum pname, GLint param); void glMultiDrawArrays(GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +void glNormalPointer(GLenum type, GLsizei stride, const void *pointer); #ifdef __cplusplus } diff --git a/media-layer/trampoline/src/GLESv1_CM.cpp b/media-layer/trampoline/src/GLESv1_CM.cpp index bfd6bd5ff1..56460b50a6 100644 --- a/media-layer/trampoline/src/GLESv1_CM.cpp +++ b/media-layer/trampoline/src/GLESv1_CM.cpp @@ -33,6 +33,7 @@ struct { gl_array_details_t glVertexPointer; gl_array_details_t glColorPointer; gl_array_details_t glTexCoordPointer; + gl_array_details_t glNormalPointer; } gl_array_details; #endif struct gl_state_t { @@ -41,6 +42,7 @@ struct gl_state_t { bool vertex_array_enabled = false; bool color_array_enabled = false; bool tex_coord_array_enabled = false; + bool normal_array_enabled = false; // Update State bool &get_array_enabled(const GLenum array) { switch (array) { @@ -53,6 +55,9 @@ struct gl_state_t { case GL_TEXTURE_COORD_ARRAY: { return tex_coord_array_enabled; } + case GL_NORMAL_ARRAY: { + return normal_array_enabled; + } default: { ERR("Unsupported Array Type: %i", array); } @@ -71,6 +76,7 @@ struct gl_state_t { send_array_to_driver(GL_VERTEX_ARRAY); send_array_to_driver(GL_COLOR_ARRAY); send_array_to_driver(GL_TEXTURE_COORD_ARRAY); + send_array_to_driver(GL_NORMAL_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, bound_array_buffer); glBindTexture(GL_TEXTURE_2D, bound_texture); } @@ -761,4 +767,21 @@ CALL(69, glBufferSubData, void, (GLenum target, GLintptr offset, GLsizeiptr size func(target, offset, size, data); return 0; #endif +} + +CALL(72, glNormalPointer, void, (GLenum type, GLsizei stride, const void *pointer)) +#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST + gl_array_details_t &state = gl_array_details.glNormalPointer; \ + if (state.type != type || state.stride != stride || state.pointer != uint32_t(pointer)) { \ + state.type = type; \ + state.stride = stride; \ + state.pointer = uint32_t(pointer); \ + trampoline(true, gl_state.bound_array_buffer, state); \ + } +#else + glBindBuffer(GL_ARRAY_BUFFER, args.next()); + gl_array_details_t state = args.next(); + func(state.type, state.stride, (const void *) uintptr_t(state.pointer)); + return 0; +#endif } \ No newline at end of file diff --git a/mods/CMakeLists.txt b/mods/CMakeLists.txt index b7aa57b0a1..7a16f04b59 100644 --- a/mods/CMakeLists.txt +++ b/mods/CMakeLists.txt @@ -99,6 +99,9 @@ set(SRC src/multidraw/storage.cpp # classic-ui src/classic-ui/classic-ui.cpp + # shading + src/shading/init.cpp + src/shading/tesselator.cpp ) # Install Splashes install( diff --git a/mods/include/mods/init/init.h b/mods/include/mods/init/init.h index 994d396af6..66eff5e353 100644 --- a/mods/include/mods/init/init.h +++ b/mods/include/mods/init/init.h @@ -31,4 +31,5 @@ void init_screenshot(); void init_f3(); void init_multidraw(); void init_classic_ui(); +void init_shading(); } diff --git a/mods/include/mods/multidraw/multidraw.h b/mods/include/mods/multidraw/multidraw.h index a37baa810f..c699dbe268 100644 --- a/mods/include/mods/multidraw/multidraw.h +++ b/mods/include/mods/multidraw/multidraw.h @@ -2,6 +2,7 @@ #include +extern int multidraw_vertex_size; extern "C" { void LevelRenderer_renderSameAsLast(LevelRenderer *self, float delta); } diff --git a/mods/src/init/init.cpp b/mods/src/init/init.cpp index 72435b83a8..4f60e7b853 100644 --- a/mods/src/init/init.cpp +++ b/mods/src/init/init.cpp @@ -16,6 +16,7 @@ __attribute__((constructor)) static void init() { init_multiplayer(); if (!reborn_is_headless()) { init_sound(); + init_shading(); } init_input(); init_sign(); diff --git a/mods/src/multidraw/glue.cpp b/mods/src/multidraw/glue.cpp index d56693bda0..733eba7888 100644 --- a/mods/src/multidraw/glue.cpp +++ b/mods/src/multidraw/glue.cpp @@ -50,7 +50,7 @@ HOOK(glBufferData, void, (GLenum target, GLsizeiptr size, const void *data, GLen } // Render -#define VERTEX_SIZE 24 +int multidraw_vertex_size = 24; #define MAX_RENDER_CHUNKS 4096 static bool supports_multidraw() { static int ret = -1; @@ -76,9 +76,9 @@ static void multidraw_renderSameAsLast(const LevelRenderer *self, const float b) glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, storage->buffer->server_side_data); - glVertexPointer(3, GL_FLOAT, VERTEX_SIZE, (void *) 0); - glTexCoordPointer(2, GL_FLOAT, VERTEX_SIZE, (void *) 0xc); - glColorPointer(4, GL_UNSIGNED_BYTE, VERTEX_SIZE, (void *) 0x14); + glVertexPointer(3, GL_FLOAT, multidraw_vertex_size, (void *) 0); + glTexCoordPointer(2, GL_FLOAT, multidraw_vertex_size, (void *) 0xc); + glColorPointer(4, GL_UNSIGNED_BYTE, multidraw_vertex_size, (void *) 0x14); // Draw if (supports_multidraw()) { @@ -110,7 +110,7 @@ static int LevelRenderer_renderChunks_injection(__attribute__((unused)) LevelRen } // Queue const int j = multidraw_total++; - multidraw_firsts[j] = block->offset / VERTEX_SIZE; + multidraw_firsts[j] = block->offset / multidraw_vertex_size; multidraw_counts[j] = render_chunk->vertices; } } diff --git a/mods/src/shading/init.cpp b/mods/src/shading/init.cpp index e69de29bb2..c225cce973 100644 --- a/mods/src/shading/init.cpp +++ b/mods/src/shading/init.cpp @@ -0,0 +1,11 @@ +#include +#include + +#include "shading-internal.h" + +// Init +void init_shading() { + if (feature_has("Proper Entity Shading", server_disabled)) { + _init_custom_tesselator(); + } +} \ No newline at end of file diff --git a/mods/src/shading/tesselator.cpp b/mods/src/shading/tesselator.cpp index e69de29bb2..9a0851e486 100644 --- a/mods/src/shading/tesselator.cpp +++ b/mods/src/shading/tesselator.cpp @@ -0,0 +1,202 @@ +#include +#include +#include + +#include + +#include +#include + +#include +#include "shading-internal.h" + +// Structures +struct UV { + float u; + float v; +}; +struct CustomVertex { + Vec3 pos; + UV uv; + GLuint color; + GLuint normal; +}; +struct CustomTesselator { + int vertex_count; + CustomVertex *vertices; + std::optional normal; + int quad_to_triangle_tracker; + + static CustomTesselator instance; +}; +CustomTesselator CustomTesselator::instance; + +static void Tesselator_clear_injection(Tesselator_clear_t original, Tesselator *self) { + if (original) { + original(self); + } + CustomTesselator::instance.vertex_count = 0; + CustomTesselator::instance.quad_to_triangle_tracker = 0; + CustomTesselator::instance.normal.reset(); +} + +#define MAX_VERTICES 524288 +static void Tesselator_init_injection(Tesselator_init_t original, Tesselator *self) { + original(self); + CustomTesselator::instance.vertices = new CustomVertex[MAX_VERTICES]; + Tesselator_clear_injection(nullptr, nullptr); +} + +static void Tesselator_begin_injection(Tesselator_begin_t original, Tesselator *self, const int mode) { + original(self, mode); + if (!self->void_begin_end) { + Tesselator_clear_injection(nullptr, nullptr); + } +} + +static GLuint get_next_buffer() { + Tesselator::instance.next_buffer_id++; + Tesselator::instance.next_buffer_id %= Tesselator::instance.buffer_count; + const GLuint out = Tesselator::instance.buffers[Tesselator::instance.next_buffer_id]; + return out; +} + +static RenderChunk Tesselator_end_injection(Tesselator *self, const bool use_given_buffer, const int buffer) { + // Check + if (!self->active) { + IMPOSSIBLE(); + } + RenderChunk out; + out.constructor(); + if (self->void_begin_end) { + return out; + } + // Render + out.vertices = CustomTesselator::instance.vertex_count; + if (out.vertices > 0) { + out.buffer = use_given_buffer ? buffer : get_next_buffer(); + glBindBuffer(GL_ARRAY_BUFFER, out.buffer); + glBufferData(GL_ARRAY_BUFFER, out.vertices * sizeof(CustomVertex), CustomTesselator::instance.vertices, GL_STATIC_DRAW); + } + // Finish + self->clear(); + self->active = false; + return out; +} + +static void Tesselator_draw_injection(Tesselator *self) { + // Check + if (!self->active) { + IMPOSSIBLE(); + } + if (self->void_begin_end) { + return; + } + // Render + const int vertices = CustomTesselator::instance.vertex_count; + if (vertices > 0) { + const GLuint buffer = get_next_buffer(); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, vertices * sizeof(CustomVertex), CustomTesselator::instance.vertices, GL_STATIC_DRAW); + if (self->has_texture) { + glTexCoordPointer(2, GL_FLOAT, sizeof(CustomVertex), (void *) offsetof(CustomVertex, uv)); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } + if (self->has_color) { + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(CustomVertex), (void *) offsetof(CustomVertex, color)); + glEnableClientState(GL_COLOR_ARRAY); + } + if (CustomTesselator::instance.normal) { + glNormalPointer(GL_BYTE, sizeof(CustomVertex), (void *) offsetof(CustomVertex, normal)); + glEnableClientState(GL_NORMAL_ARRAY); + } + glVertexPointer(3, GL_FLOAT, sizeof(CustomVertex), (void *) offsetof(CustomVertex, pos)); + glEnableClientState(GL_VERTEX_ARRAY); + int mode = self->mode; + if (mode == GL_QUADS) { + mode = GL_TRIANGLES; + } + glDrawArrays(mode, 0, vertices); + glDisableClientState(GL_VERTEX_ARRAY); + if (self->has_texture) { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + if (self->has_color) { + glDisableClientState(GL_COLOR_ARRAY); + } + if (CustomTesselator::instance.normal) { + glDisableClientState(GL_NORMAL_ARRAY); + } + } + // Finish + self->clear(); + self->active = false; +} + +static void Tesselator_vertex_injection(const Tesselator *self, const float x, const float y, const float z) { + CustomVertex &vertex = CustomTesselator::instance.vertices[CustomTesselator::instance.vertex_count++]; + vertex.pos = { + (self->offset_x + x) * self->sx, + (self->offset_y + y) * self->sy, + self->offset_z + z + }; + if (self->has_texture) { + vertex.uv = {self->u, self->v}; + } + if (self->has_color) { + vertex.color = self->_color; + } + if (CustomTesselator::instance.normal) { + vertex.normal = *CustomTesselator::instance.normal; + } + // Convert To Triangles + if (self->mode == GL_QUADS) { + int &tracker = CustomTesselator::instance.quad_to_triangle_tracker; + if (tracker == 3) { + tracker = 0; + } else { + if (tracker == 2) { + const int last_vertex = CustomTesselator::instance.vertex_count - 1; + for (const int i : {-2, 0}) { + CustomTesselator::instance.vertices[CustomTesselator::instance.vertex_count++] = CustomTesselator::instance.vertices[last_vertex + i]; + } + } + tracker++; + } + } +} + +static void Tesselator_normal_injection(__attribute__((unused)) Tesselator *self, const float nx, const float ny, const float nz) { + const char xx = (char) (nx * 128); + const char yy = (char) (ny * 127); + const char zz = (char) (nz * 127); + CustomTesselator::instance.normal = xx | (yy << 8) | (zz << 16); +} + +static void drawArrayVT_injection(const int buffer, const int vertices, int vertex_size, const uint mode) { + vertex_size = sizeof(CustomVertex); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glTexCoordPointer(2, GL_FLOAT, vertex_size, (void *) offsetof(CustomVertex, uv)); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(3, GL_FLOAT, vertex_size, (void *) offsetof(CustomVertex, pos)); + glEnableClientState(GL_VERTEX_ARRAY); + glNormalPointer(GL_BYTE, sizeof(CustomVertex), (void *) offsetof(CustomVertex, normal)); + glEnableClientState(GL_NORMAL_ARRAY); + glDrawArrays(mode, 0, vertices); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); +} + +// Init +void _init_custom_tesselator() { + multidraw_vertex_size = sizeof(CustomVertex); + overwrite_calls(Tesselator_init, Tesselator_init_injection); + overwrite_calls(Tesselator_clear, Tesselator_clear_injection); + overwrite_calls(Tesselator_begin, Tesselator_begin_injection); + overwrite_call((void *) Tesselator_end->backup, (void *) Tesselator_end_injection, true); + overwrite_call((void *) Tesselator_draw->backup, (void *) Tesselator_draw_injection, true); + overwrite_call((void *) Tesselator_vertex->backup, (void *) Tesselator_vertex_injection, true); + overwrite_call((void *) Tesselator_normal->backup, (void *) Tesselator_normal_injection, true); + overwrite_call((void *) Common_drawArrayVT->backup, (void *) drawArrayVT_injection, true); +} \ No newline at end of file diff --git a/symbols/src/level/renderer/RenderChunk.def b/symbols/src/level/renderer/RenderChunk.def index f5e38bb7cf..fbf36e9cac 100644 --- a/symbols/src/level/renderer/RenderChunk.def +++ b/symbols/src/level/renderer/RenderChunk.def @@ -1,7 +1,10 @@ size 0x18; +constructor () = 0x525ac; + property uint buffer = 0x0; property int vertices = 0x4; -property float x = 0xc; -property float y = 0x10; -property float z = 0x14; \ No newline at end of file +property int id = 0x8; +property Vec3 pos = 0xc; + +mark-as-simple; \ No newline at end of file diff --git a/symbols/src/misc/Common.def b/symbols/src/misc/Common.def index 59fdbcae1c..958794f259 100644 --- a/symbols/src/misc/Common.def +++ b/symbols/src/misc/Common.def @@ -7,3 +7,4 @@ static-method int sdl_key_to_minecraft_key(int sdl_key) = 0x1243c; static-method void anGenBuffers(int count, uint *buffers) = 0x5f28c; static-method int getTimeMs() = 0x13cd4; static-method int getEpochTimeS() = 0x13d00; +static-method void drawArrayVT(int buffer, int vertices, int vertex_size, uint mode) = 0x5f2cc; \ No newline at end of file diff --git a/symbols/src/misc/Tesselator.def b/symbols/src/misc/Tesselator.def index e9aa95650b..90d3ecd46c 100644 --- a/symbols/src/misc/Tesselator.def +++ b/symbols/src/misc/Tesselator.def @@ -1,9 +1,53 @@ +method void init() = 0x5289c; +method void clear() = 0x528ac; + method void begin(int mode) = 0x529d4; +method void begin_quads() = 0x52a24; + +method RenderChunk end(bool use_given_buffer, int buffer) = 0x528d4; method void draw() = 0x52e08; +method void voidBeginAndEndCalls(bool x) = 0x52f74; + method void colorABGR(int color) = 0x52b54; method void color(int r, int g, int b, int a) = 0x52a48; +method void noColor() = 0x52d54; +method void enableColor() = 0x52f7c; + method void vertex(float x, float y, float z) = 0x52bc0; +method void tex(float u, float v) = 0x52a2c; method void vertexUV(float x, float y, float z, float u, float v) = 0x52d40; + +method void scale2d(float sx, float sy) = 0x52b94; +method void resetScale() = 0x52bb0; + +method void normal(float nx, float ny, float nz) = 0x52d68; + +method void offset(float x, float y, float z) = 0x52d80; method void addOffset(float x, float y, float z) = 0x52d90; +method void offset_vec3(const Vec3 &x) = 0x52db8; +method void addOffset_vec3(const Vec3 &x) = 0x52dd4; + +property bool active = 0x3c; +property int mode = 0x58; + +property int next_buffer_id = 0x44; +property uint *buffers = 0x48; +property int buffer_count = 0x40; + +property float offset_x = 0x8; +property float offset_y = 0xc; +property float offset_z = 0x10; + +property bool has_texture = 0x2d; +property float u = 0x14; +property float v = 0x18; + +property bool has_color = 0x2c; +property uint _color = 0x1c; + +property float sx = 0x24; +property float sy = 0x28; + +property bool void_begin_end = 0x30; static-property Tesselator instance = 0x137538;