Multidraw
This commit is contained in:
parent
f08afbf654
commit
d4c0f54245
2
dependencies/gles-compatibility-layer/src
vendored
2
dependencies/gles-compatibility-layer/src
vendored
@ -1 +1 @@
|
||||
Subproject commit 68561e0404a4ad09e523129bca67cf6be8cec2fc
|
||||
Subproject commit fedc5ac21865fe44dcbc7adb0818af612205cd57
|
@ -68,4 +68,5 @@ TRUE Fix Held Item Caching
|
||||
TRUE Add Reborn Info To Options
|
||||
FALSE Log FPS
|
||||
TRUE Add Welcome Screen
|
||||
TRUE F3 Debug Information
|
||||
TRUE F3 Debug Information
|
||||
TRUE Multidraw Rendering
|
@ -564,4 +564,13 @@ void media_get_framebuffer_size(int *width, int *height) {
|
||||
*width = DEFAULT_WIDTH;
|
||||
*height = DEFAULT_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
// Check OpenGL Extension
|
||||
int media_has_extension(const char *name) {
|
||||
if (glfw_window) {
|
||||
return glfwExtensionSupported(name);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ void media_set_interactable(int is_interactable);
|
||||
void media_disable_vsync();
|
||||
void media_force_egl();
|
||||
void media_set_raw_mouse_motion_enabled(int enabled);
|
||||
int media_has_extension(const char *name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -138,6 +138,23 @@ CALL(15, glDrawArrays, void, (GLenum mode, GLint first, GLsizei count))
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MCPI_USE_GLES1_COMPATIBILITY_LAYER
|
||||
CALL(70, glMultiDrawArrays, void, (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount))
|
||||
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
|
||||
trampoline(true, gl_state, mode, copy_array(drawcount, first), copy_array(drawcount, count));
|
||||
#else
|
||||
gl_state_t gl_state = args.next<gl_state_t>();
|
||||
gl_state.send_to_driver();
|
||||
GLenum mode = args.next<GLenum>();
|
||||
uint32_t drawcount;
|
||||
const GLint *first = args.next_arr<GLint>(&drawcount);
|
||||
const GLsizei *count = args.next_arr<GLsizei>();
|
||||
func(mode, first, count, GLsizei(drawcount));
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
CALL(16, glColor4f, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha))
|
||||
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
|
||||
trampoline(true, red, green, blue, alpha);
|
||||
@ -162,14 +179,14 @@ CALL(17, glClear, void, (GLbitfield mask))
|
||||
|
||||
CALL(18, glBufferData, void, (GLenum target, GLsizeiptr size, const void *data, GLenum usage))
|
||||
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
|
||||
trampoline(true, gl_state.bound_array_buffer, target, copy_array(size, (unsigned char *) data), usage);
|
||||
trampoline(true, gl_state.bound_array_buffer, target, int32_t(size), copy_array(size, (unsigned char *) data), usage);
|
||||
#else
|
||||
glBindBuffer(GL_ARRAY_BUFFER, args.next<GLuint>());
|
||||
GLenum target = args.next<GLenum>();
|
||||
uint32_t size;
|
||||
const unsigned char *data = args.next_arr<unsigned char>(&size);
|
||||
int32_t size = args.next<int32_t>();
|
||||
const unsigned char *data = args.next_arr<unsigned char>();
|
||||
GLenum usage = args.next<GLenum>();
|
||||
func(target, GLsizeiptr(size), data, usage);
|
||||
func(target, size, data, usage);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@ -738,3 +755,17 @@ CALL(67, glGenBuffers, void, (GLsizei n, GLuint *buffers))
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
CALL(69, 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));
|
||||
#else
|
||||
glBindBuffer(GL_ARRAY_BUFFER, args.next<GLuint>());
|
||||
GLenum target = args.next<GLenum>();
|
||||
int32_t offset = args.next<int32_t>();
|
||||
int32_t size = args.next<int32_t>();
|
||||
const unsigned char *data = args.next_arr<unsigned char>();
|
||||
func(target, offset, size, data);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
@ -38,6 +38,9 @@ struct TrampolineArguments {
|
||||
if (length != nullptr) {
|
||||
*length = size / sizeof(T);
|
||||
}
|
||||
if (size == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
const T *ret = (const T *) raw_args;
|
||||
raw_args += size;
|
||||
return ret;
|
||||
|
@ -182,3 +182,11 @@ CALL(68, media_ensure_loaded, void, ())
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
CALL(71, media_has_extension, int, (const char *name))
|
||||
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
|
||||
return trampoline(false, copy_array(name));
|
||||
#else
|
||||
return func(args.next_arr<char>());
|
||||
#endif
|
||||
}
|
||||
|
@ -89,6 +89,10 @@ set(SRC
|
||||
src/init/init.cpp
|
||||
# f3
|
||||
src/f3/f3.cpp
|
||||
# multidraw
|
||||
src/multidraw/glue.cpp
|
||||
src/multidraw/buffer.cpp
|
||||
src/multidraw/storage.cpp
|
||||
)
|
||||
# Install Splashes
|
||||
install(
|
||||
|
@ -29,4 +29,5 @@ void init_home();
|
||||
void init_override();
|
||||
void init_screenshot();
|
||||
void init_f3();
|
||||
void init_multidraw();
|
||||
}
|
||||
|
@ -43,5 +43,6 @@ __attribute__((constructor)) static void init() {
|
||||
if (!reborn_is_headless()) {
|
||||
init_screenshot();
|
||||
init_f3();
|
||||
init_multidraw();
|
||||
}
|
||||
}
|
||||
|
@ -724,7 +724,7 @@ static void sort_chunks(Chunk **chunks_begin, Chunk **chunks_end, DistanceChunkS
|
||||
// Display Date In Select World Screen
|
||||
static std::string AppPlatform_linux_getDateString_injection(__attribute__((unused)) AppPlatform_linux *app_platform, int time) {
|
||||
// From https://github.com/ReMinecraftPE/mcpe/blob/56e51027b1c2e67fe5a0e8a091cefe51d4d11926/platforms/sdl/base/AppPlatform_sdl_base.cpp#L68-L84
|
||||
time_t tt = time;
|
||||
const time_t tt = time;
|
||||
tm t = {};
|
||||
gmtime_r(&tt, &t);
|
||||
char buf[2048];
|
||||
|
29
mods/src/multidraw/buffer.cpp
Normal file
29
mods/src/multidraw/buffer.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
// Setup Buffer
|
||||
Buffer::Buffer(const ssize_t size) {
|
||||
// Client-Side Data
|
||||
client_side_data = new unsigned char[size];
|
||||
// Server-Side Data
|
||||
server_side_data = 0;
|
||||
glGenBuffers(1, &server_side_data);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, server_side_data);
|
||||
glBufferData(GL_ARRAY_BUFFER, size, nullptr, GL_DYNAMIC_DRAW);
|
||||
}
|
||||
Buffer::~Buffer() {
|
||||
// Client-Side Data
|
||||
delete[] client_side_data;
|
||||
// Server-Side Data
|
||||
glDeleteBuffers(1, &server_side_data);
|
||||
}
|
||||
|
||||
// Upload Data
|
||||
void Buffer::upload(const intptr_t offset, const ssize_t size, const void *data) const {
|
||||
// Client-Side Data
|
||||
memcpy(client_side_data + offset, data, size);
|
||||
// Server-Side Data
|
||||
glBindBuffer(GL_ARRAY_BUFFER, server_side_data);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, offset, size, data);
|
||||
}
|
21
mods/src/multidraw/buffer.h
Normal file
21
mods/src/multidraw/buffer.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <GLES/gl.h>
|
||||
|
||||
// Keep An OpenGL Buffer And Client-Side Memory Synchronized
|
||||
struct Buffer {
|
||||
explicit Buffer(ssize_t size);
|
||||
~Buffer();
|
||||
|
||||
// Prevent Copying
|
||||
Buffer(const Buffer &) = delete;
|
||||
Buffer &operator=(const Buffer &) = delete;
|
||||
|
||||
// Data
|
||||
GLuint server_side_data;
|
||||
unsigned char *client_side_data;
|
||||
|
||||
// Upload Data
|
||||
void upload(intptr_t offset, ssize_t size, const void *data) const;
|
||||
};
|
135
mods/src/multidraw/glue.cpp
Normal file
135
mods/src/multidraw/glue.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
#include <GLES/gl.h>
|
||||
|
||||
#include <symbols/minecraft.h>
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <media-layer/core.h>
|
||||
|
||||
#include <mods/init/init.h>
|
||||
#include <mods/feature/feature.h>
|
||||
|
||||
#include "storage.h"
|
||||
|
||||
// Fake Buffer IDs To Correspond To The Multidraw Storage
|
||||
#define MULTIDRAW_BASE 1073741823
|
||||
|
||||
// Setup
|
||||
static Storage *storage = nullptr;
|
||||
static void setup_multidraw(int chunks, GLuint *buffers) {
|
||||
delete storage;
|
||||
storage = new Storage(chunks);
|
||||
for (int i = 0; i < chunks; i++) {
|
||||
buffers[i] = i + MULTIDRAW_BASE;
|
||||
}
|
||||
}
|
||||
HOOK(glDeleteBuffers, void, (GLsizei n, const GLuint *buffers)) {
|
||||
if (buffers[0] >= MULTIDRAW_BASE) {
|
||||
delete storage;
|
||||
} else {
|
||||
ensure_glDeleteBuffers();
|
||||
real_glDeleteBuffers(n, buffers);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
static int current_chunk = -1;
|
||||
HOOK(glBindBuffer, void, (const GLenum target, GLuint buffer)) {
|
||||
if (target == GL_ARRAY_BUFFER && buffer >= MULTIDRAW_BASE && storage != nullptr) {
|
||||
current_chunk = int(buffer - MULTIDRAW_BASE);
|
||||
buffer = storage->buffer->server_side_data;
|
||||
} else {
|
||||
current_chunk = -1;
|
||||
}
|
||||
ensure_glBindBuffer();
|
||||
real_glBindBuffer(target, buffer);
|
||||
}
|
||||
HOOK(glBufferData, void, (GLenum target, GLsizeiptr size, const void *data, GLenum usage)) {
|
||||
if (target == GL_ARRAY_BUFFER && current_chunk >= 0 && storage != nullptr) {
|
||||
storage->upload(current_chunk, size, data);
|
||||
} else {
|
||||
ensure_glBufferData();
|
||||
real_glBufferData(target, size, data, usage);
|
||||
}
|
||||
}
|
||||
#define VERTEX_SIZE 24
|
||||
#define MAX_RENDER_CHUNKS 4096
|
||||
static bool supports_multidraw() {
|
||||
static int ret = -1;
|
||||
if (ret == -1) {
|
||||
ret = media_has_extension("GL_EXT_multi_draw_arrays");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static int LevelRenderer_renderChunks_injection(__attribute__((unused)) LevelRenderer_renderChunks_t original, LevelRenderer *self, const int start, const int end, const int a, const float b) {
|
||||
// Prepare Offset
|
||||
self->render_list.clear();
|
||||
const Mob *camera = self->minecraft->camera;
|
||||
const float x = camera->old_x + ((camera->x - camera->old_x) * b);
|
||||
const float y = camera->old_y + ((camera->y - camera->old_y) * b);
|
||||
const float z = camera->old_z + ((camera->z - camera->old_z) * b);
|
||||
glPushMatrix();
|
||||
glTranslatef(-x, -y, -z);
|
||||
|
||||
// Setup OpenGL
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
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);
|
||||
|
||||
// Batch
|
||||
static GLint firsts[MAX_RENDER_CHUNKS];
|
||||
static GLsizei counts[MAX_RENDER_CHUNKS];
|
||||
GLsizei total = 0;
|
||||
for (int i = start; i < end; i++) {
|
||||
Chunk *chunk = self->chunks[i];
|
||||
// Check If Chunk Is Visible
|
||||
if (!chunk->field_1c[a] && chunk->visible) {
|
||||
const RenderChunk *render_chunk = chunk->getRenderChunk(a);
|
||||
// Get Data Block
|
||||
const int chunk_id = int(render_chunk->buffer - MULTIDRAW_BASE);
|
||||
const Block *block = storage->chunk_to_block[chunk_id];
|
||||
if (block == nullptr) {
|
||||
continue;
|
||||
}
|
||||
// Queue
|
||||
const int j = total++;
|
||||
firsts[j] = block->offset / VERTEX_SIZE;
|
||||
counts[j] = render_chunk->vertices;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw
|
||||
#ifdef MCPI_USE_GLES1_COMPATIBILITY_LAYER
|
||||
if (supports_multidraw()) {
|
||||
glMultiDrawArrays(GL_TRIANGLES, firsts, counts, total);
|
||||
} else {
|
||||
#endif
|
||||
for (int i = 0; i < total; i++) {
|
||||
glDrawArrays(GL_TRIANGLES, firsts[i], counts[i]);
|
||||
}
|
||||
#ifdef MCPI_USE_GLES1_COMPATIBILITY_LAYER
|
||||
}
|
||||
#endif
|
||||
|
||||
// Cleanup
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glPopMatrix();
|
||||
|
||||
// Return
|
||||
return total;
|
||||
}
|
||||
|
||||
// Init
|
||||
void init_multidraw() {
|
||||
// Setup
|
||||
if (feature_has("Multidraw Rendering", server_disabled)) {
|
||||
overwrite_call((void *) 0x4e51c, (void *) setup_multidraw);
|
||||
overwrite_call((void *) 0x4e6f8, (void *) setup_multidraw);
|
||||
overwrite_calls(LevelRenderer_renderChunks, LevelRenderer_renderChunks_injection);
|
||||
unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
|
||||
patch((void *) 0x479fc, nop_patch);
|
||||
}
|
||||
}
|
185
mods/src/multidraw/storage.cpp
Normal file
185
mods/src/multidraw/storage.cpp
Normal file
@ -0,0 +1,185 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "storage.h"
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
// Setup
|
||||
#define DEFAULT_SIZE 16777216 // 16 MiB
|
||||
Storage::Storage(const int chunks) {
|
||||
// Allocate
|
||||
total_size = DEFAULT_SIZE;
|
||||
used_size = 0;
|
||||
for (int i = 0; i < chunks; i++) {
|
||||
chunk_to_block.push_back(nullptr);
|
||||
}
|
||||
buffer = new Buffer(total_size);
|
||||
// First Free Block
|
||||
Block *block = new Block;
|
||||
block->offset = 0;
|
||||
block->size = total_size;
|
||||
free_blocks.push_back(block);
|
||||
}
|
||||
Storage::~Storage() {
|
||||
delete buffer;
|
||||
for (const std::vector<Block *> &blocks : {free_blocks, used_blocks}) {
|
||||
for (const Block *block : blocks) {
|
||||
delete block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Free Block
|
||||
void Storage::free_block(Block *block) {
|
||||
// Check
|
||||
if (block == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find Block
|
||||
std::vector<Block *>::iterator it = std::find(used_blocks.begin(), used_blocks.end(), block);
|
||||
if (it == used_blocks.end()) {
|
||||
return;
|
||||
}
|
||||
used_blocks.erase(it);
|
||||
|
||||
// Update Size
|
||||
used_size -= block->size;
|
||||
|
||||
// Merge With Next/Previous Blocks
|
||||
for (it = free_blocks.begin(); it < free_blocks.end(); ++it) {
|
||||
const Block *x = *it;
|
||||
if (x->offset == (block->offset + block->size)) {
|
||||
// Found Free Block After Target
|
||||
block->size += x->size;
|
||||
free_blocks.erase(it);
|
||||
delete x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (it = free_blocks.begin(); it < free_blocks.end(); ++it) {
|
||||
const Block *x = *it;
|
||||
if ((x->offset + x->size) == block->offset) {
|
||||
// Found Free Block Before Target
|
||||
block->size += x->size;
|
||||
block->offset -= x->size;
|
||||
free_blocks.erase(it);
|
||||
delete x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add To Free Block List
|
||||
free_blocks.push_back(block);
|
||||
|
||||
// Fragmentation
|
||||
check_fragmentation();
|
||||
}
|
||||
|
||||
// Check Fragmentation
|
||||
ssize_t Storage::get_end() const {
|
||||
ssize_t end = 0;
|
||||
for (const Block *block : used_blocks) {
|
||||
const ssize_t new_end = block->offset + block->size;
|
||||
if (new_end > end) {
|
||||
end = new_end;
|
||||
}
|
||||
}
|
||||
return end;
|
||||
}
|
||||
void Storage::check_fragmentation() {
|
||||
const ssize_t end = get_end();
|
||||
if (end == 0) {
|
||||
return;
|
||||
}
|
||||
const float fragmentation = 1.0f - (float(used_size) / float(end));
|
||||
if (fragmentation >= 0.5f) {
|
||||
// 50% Fragmentation
|
||||
recreate();
|
||||
}
|
||||
}
|
||||
|
||||
// Upload Chunk
|
||||
void Storage::upload(const int chunk, const ssize_t size, const void *data) {
|
||||
// Free Old Block
|
||||
free_block(chunk_to_block[chunk]);
|
||||
|
||||
// Check Size
|
||||
if (size == 0) {
|
||||
chunk_to_block[chunk] = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Find Free Block
|
||||
std::vector<Block *>::iterator it;
|
||||
while (true) {
|
||||
for (it = free_blocks.begin(); it < free_blocks.end(); ++it) {
|
||||
if ((*it)->size >= size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (it == free_blocks.end()) {
|
||||
// Get Extra Space
|
||||
recreate(size);
|
||||
} else {
|
||||
// Done!
|
||||
break;
|
||||
}
|
||||
}
|
||||
Block *old_free_block = *it;
|
||||
|
||||
// Create New Used Block
|
||||
Block *new_used_block = new Block;
|
||||
new_used_block->offset = old_free_block->offset;
|
||||
new_used_block->size = size;
|
||||
used_blocks.push_back(new_used_block);
|
||||
// Update Old Free Block
|
||||
old_free_block->offset += size;
|
||||
old_free_block->size -= size;
|
||||
if (old_free_block->size == 0) {
|
||||
free_blocks.erase(it);
|
||||
delete old_free_block;
|
||||
}
|
||||
|
||||
// Upload
|
||||
buffer->upload(new_used_block->offset, size, data);
|
||||
|
||||
// Assign Block To Chunk
|
||||
chunk_to_block[chunk] = new_used_block;
|
||||
|
||||
// Update Size
|
||||
used_size += size;
|
||||
|
||||
// Check Fragmentation
|
||||
check_fragmentation();
|
||||
}
|
||||
|
||||
// Recreate/Defragment
|
||||
void Storage::recreate(const ssize_t extra_size) {
|
||||
// Create New Buffer
|
||||
const ssize_t new_size = (used_size + extra_size) * 2;
|
||||
Buffer *new_buffer = new Buffer(new_size);
|
||||
|
||||
// Copy Used Blocks
|
||||
intptr_t offset = 0;
|
||||
for (Block *block : used_blocks) {
|
||||
new_buffer->upload(offset, block->size, buffer->client_side_data + block->offset);
|
||||
block->offset = offset;
|
||||
offset += block->size;
|
||||
}
|
||||
|
||||
// Update Free Blocks
|
||||
for (const Block *block : free_blocks) {
|
||||
delete block;
|
||||
}
|
||||
free_blocks.clear();
|
||||
Block *block = new Block;
|
||||
block->offset = offset;
|
||||
block->size = new_size - offset;
|
||||
free_blocks.push_back(block);
|
||||
|
||||
// Use New Buffer
|
||||
delete buffer;
|
||||
buffer = new_buffer;
|
||||
total_size = new_size;
|
||||
}
|
41
mods/src/multidraw/storage.h
Normal file
41
mods/src/multidraw/storage.h
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
// Block Of Data
|
||||
struct Block {
|
||||
intptr_t offset;
|
||||
ssize_t size;
|
||||
// Prevent Copying
|
||||
Block() {
|
||||
offset = 0;
|
||||
size = 0;
|
||||
}
|
||||
Block(const Block &) = delete;
|
||||
Block &operator=(const Block &) = delete;
|
||||
};
|
||||
|
||||
// Storage
|
||||
struct Storage {
|
||||
explicit Storage(int chunks);
|
||||
~Storage();
|
||||
|
||||
// Buffer
|
||||
Buffer *buffer;
|
||||
|
||||
// Chunks
|
||||
std::vector<Block *> chunk_to_block;
|
||||
void upload(int chunk, ssize_t size, const void *data);
|
||||
|
||||
// Management
|
||||
ssize_t total_size;
|
||||
ssize_t used_size;
|
||||
std::vector<Block *> free_blocks;
|
||||
std::vector<Block *> used_blocks;
|
||||
void free_block(Block *block);
|
||||
void recreate(ssize_t extra_size = 0);
|
||||
void check_fragmentation();
|
||||
ssize_t get_end() const;
|
||||
};
|
@ -91,11 +91,11 @@ void screenshot_take(Gui *gui) {
|
||||
if (save_png(file.c_str(), pixels, line_size, width, height)) {
|
||||
WARN("Screenshot Failed: %s", file.c_str());
|
||||
} else {
|
||||
std::string msg = "Screenshot Saved: ";
|
||||
INFO("%s%s", msg.c_str(), file.c_str());
|
||||
INFO("Screenshot Saved: %s", file.c_str());
|
||||
if (gui) {
|
||||
msg += filename;
|
||||
gui->addMessage(&msg);
|
||||
std::string chat_msg = "Saved screenshot as ";
|
||||
chat_msg += filename;
|
||||
gui->addMessage(&chat_msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,9 +79,9 @@ set(SRC
|
||||
src/level/ChunkStorage.def
|
||||
src/level/LightLayer.def
|
||||
src/level/Level.def
|
||||
src/level/LevelRenderer.def
|
||||
src/level/renderer/LevelRenderer.def
|
||||
src/level/LevelStorageSource.def
|
||||
src/level/ParticleEngine.def
|
||||
src/level/renderer/ParticleEngine.def
|
||||
src/level/RandomLevelSource.def
|
||||
src/level/LevelData.def
|
||||
src/level/LevelSettings.def
|
||||
@ -90,7 +90,7 @@ set(SRC
|
||||
src/level/MultiPlayerLevel.def
|
||||
src/level/LevelSummary.def
|
||||
src/level/DistanceChunkSorter.def
|
||||
src/level/Chunk.def
|
||||
src/level/renderer/Chunk.def
|
||||
src/item/ItemRenderer.def
|
||||
src/item/ItemInHandRenderer.def
|
||||
src/item/AuxDataTileItem.def
|
||||
@ -161,6 +161,8 @@ set(SRC
|
||||
src/misc/Vec3.def
|
||||
src/misc/HitResult.def
|
||||
src/misc/PerfRenderer.def
|
||||
src/level/renderer/RenderList.def
|
||||
src/level/renderer/RenderChunk.def
|
||||
src/textures/Texture.def
|
||||
src/textures/Textures.def
|
||||
src/textures/DynamicTexture.def
|
||||
|
@ -1,5 +0,0 @@
|
||||
property int x = 0x4;
|
||||
property int y = 0x8;
|
||||
property int z = 0xc;
|
||||
|
||||
method float distanceToSqr(Entity *entity) = 0x47ba0;
|
9
symbols/src/level/renderer/Chunk.def
Normal file
9
symbols/src/level/renderer/Chunk.def
Normal file
@ -0,0 +1,9 @@
|
||||
property int x = 0x4;
|
||||
property int y = 0x8;
|
||||
property int z = 0xc;
|
||||
property bool field_1c[3] = 0x1c;
|
||||
property bool visible = 0x4c;
|
||||
|
||||
method float distanceToSqr(Entity *entity) = 0x47ba0;
|
||||
method int getList(int a) = 0x47e48;
|
||||
method RenderChunk *getRenderChunk(int a) = 0x47e74;
|
@ -4,5 +4,8 @@ method void renderDebug(AABB *aabb, float delta) = 0x4d310;
|
||||
method void generateSky() = 0x4d0d4;
|
||||
method void renderHitSelect(Player *player, HitResult *hit_result, int i, void *vp, float f) = 0x4e318;
|
||||
method void renderHitOutline(Player *player, HitResult *hit_result, int i, void *vp, float f) = 0x4dc14;
|
||||
method int renderChunks(int start, int end, int a, float b) = 0x4f35c;
|
||||
|
||||
property Minecraft *minecraft = 0x4;
|
||||
property Minecraft *minecraft = 0xb4;
|
||||
property RenderList render_list = 0x34;
|
||||
property Chunk **chunks = 0x98;
|
7
symbols/src/level/renderer/RenderChunk.def
Normal file
7
symbols/src/level/renderer/RenderChunk.def
Normal file
@ -0,0 +1,7 @@
|
||||
size 0x18;
|
||||
|
||||
property uint buffer = 0x0;
|
||||
property int vertices = 0x4;
|
||||
property float x = 0xc;
|
||||
property float y = 0x10;
|
||||
property float z = 0x14;
|
9
symbols/src/level/renderer/RenderList.def
Normal file
9
symbols/src/level/renderer/RenderList.def
Normal file
@ -0,0 +1,9 @@
|
||||
method void render() = 0x52800;
|
||||
method void renderChunks() = 0x52708;
|
||||
method void clear() = 0x5288c;
|
||||
method void init(float x, float y, float z) = 0x526bc;
|
||||
method void addR(RenderChunk *chunk) = 0x526dc;
|
||||
|
||||
property int size = 0x1c;
|
||||
property RenderChunk *chunks = 0x10;
|
||||
property int field_14 = 0x14;
|
Loading…
Reference in New Issue
Block a user