202 lines
7.1 KiB
C++
202 lines
7.1 KiB
C++
#include <optional>
|
|
#include <cstddef>
|
|
#include <algorithm>
|
|
|
|
#include <GLES/gl.h>
|
|
|
|
#include <libreborn/libreborn.h>
|
|
#include <symbols/minecraft.h>
|
|
|
|
#include <mods/multidraw/multidraw.h>
|
|
#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<uint32_t> 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);
|
|
} |