tibia-rme/source/light_drawer.cpp
Victor 21b234e2ab
feat: deferred rendering pipeline, memory optimization, and async map cleanup (#173)
New Features:
- Scene caching with render-to-texture and blitting for faster map redraws.
- Explicit blend-mode controls and FBO-backed render-to-texture workflow.

Bug Fixes:
- More robust sprite-sheet reload and texture release behavior.
- Safer editor/tab shutdown with ensured live-server closure and deferred deletion.

Performance:
- Reworked GPU batching/streaming and command-based flushing for fewer draw calls and efficient uploads.
- Smarter scene invalidation to avoid unnecessary re-renders.

UX:
- Undo/redo now refreshes editor views more reliably.
2026-04-25 10:35:24 -03:00

135 lines
4.5 KiB
C++

//////////////////////////////////////////////////////////////////////
// This file is part of Remere's Map Editor
//////////////////////////////////////////////////////////////////////
// Remere's Map Editor is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Remere's Map Editor is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////////
#include "main.h"
#include "light_drawer.h"
#include "gl_renderer.h"
#include "gl_compat.h"
LightDrawer::LightDrawer() {
texture = 0;
buffer.resize(static_cast<size_t>(rme::ClientMapWidth * rme::ClientMapHeight * rme::PixelFormatRGBA));
global_color = wxColor(50, 50, 50, 255);
}
LightDrawer::~LightDrawer() {
unloadGLTexture();
lights.clear();
}
void LightDrawer::draw(int map_x, int map_y, int end_x, int end_y, int scroll_x, int scroll_y, GLRenderer* renderer) {
if (texture == 0) {
createGLTexture();
}
int w = end_x - map_x;
int h = end_y - map_y;
buffer.resize(static_cast<size_t>(w * h * rme::PixelFormatRGBA));
for (int x = 0; x < w; ++x) {
for (int y = 0; y < h; ++y) {
int mx = (map_x + x);
int my = (map_y + y);
int index = (y * w + x);
int color_index = index * rme::PixelFormatRGBA;
buffer[color_index] = global_color.Red();
buffer[color_index + 1] = global_color.Green();
buffer[color_index + 2] = global_color.Blue();
buffer[color_index + 3] = global_color.Alpha();
for (auto &light : lights) {
float intensity = calculateIntensity(mx, my, light);
if (intensity == 0.f) {
continue;
}
wxColor light_color = colorFromEightBit(light.color);
uint8_t red = static_cast<uint8_t>(light_color.Red() * intensity);
uint8_t green = static_cast<uint8_t>(light_color.Green() * intensity);
uint8_t blue = static_cast<uint8_t>(light_color.Blue() * intensity);
buffer[color_index] = std::max(buffer[color_index], red);
buffer[color_index + 1] = std::max(buffer[color_index + 1], green);
buffer[color_index + 2] = std::max(buffer[color_index + 2], blue);
}
}
}
const int draw_x = map_x * rme::TileSize - scroll_x;
const int draw_y = map_y * rme::TileSize - scroll_y;
int draw_width = w * rme::TileSize;
int draw_height = h * rme::TileSize;
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
renderer->flush();
renderer->setBlendMode(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
renderer->drawTexturedQuad(static_cast<float>(draw_x), static_cast<float>(draw_y), static_cast<float>(draw_width), static_cast<float>(draw_height), texture, { 255, 255, 255, 255 });
renderer->flush();
renderer->resetBlendMode();
}
void LightDrawer::setGlobalLightColor(uint8_t color) {
global_color = colorFromEightBit(color);
}
void LightDrawer::addLight(int map_x, int map_y, int map_z, const SpriteLight &light) {
if (map_z <= rme::MapGroundLayer) {
map_x -= (rme::MapGroundLayer - map_z);
map_y -= (rme::MapGroundLayer - map_z);
}
if (map_x <= 0 || map_x >= rme::MapMaxWidth || map_y <= 0 || map_y >= rme::MapMaxHeight) {
return;
}
uint8_t intensity = std::min(light.intensity, static_cast<uint8_t>(rme::MaxLightIntensity));
if (!lights.empty()) {
Light &previous = lights.back();
if (previous.map_x == map_x && previous.map_y == map_y && previous.color == light.color) {
previous.intensity = std::max(previous.intensity, intensity);
return;
}
}
lights.push_back(Light { static_cast<uint16_t>(map_x), static_cast<uint16_t>(map_y), light.color, intensity });
}
void LightDrawer::clear() noexcept {
lights.clear();
}
void LightDrawer::createGLTexture() {
glGenTextures(1, &texture);
}
void LightDrawer::unloadGLTexture() {
if (texture != 0) {
GLRenderer::invalidateTexture(texture);
glDeleteTextures(1, &texture);
}
}