refactor world renderer a bit

This commit is contained in:
MihailRis 2026-02-02 22:40:54 +03:00
parent b69c92b330
commit 5380bcf013
13 changed files with 257 additions and 203 deletions

View file

@ -184,7 +184,7 @@ void LevelScreen::saveWorldPreview() {
static_cast<uint>(previewSize)}
);
renderer->renderFrame(ctx, camera, false, true, 0.0f, *postProcessing);
renderer->renderFrame(ctx, camera, false, *postProcessing);
auto image = postProcessing->toImage();
image->flipY();
imageio::write("world:preview.png", image.get());
@ -271,9 +271,11 @@ void LevelScreen::draw(float delta) {
if (!hud->isPause()) {
scripting::on_entities_render(engine.getTime().getDelta());
}
renderer->renderFrame(
ctx, *camera, hudVisible, hud->isPause(), delta, *postProcessing
);
renderer->update(*camera, delta * !hud->isPause());
renderer->renderFrame(ctx, *camera, hudVisible, *postProcessing);
if (!hud->isPause()) {
scripting::on_frontend_render();
}
if (hudVisible) {
hud->draw(ctx);

View file

@ -56,7 +56,7 @@ public:
static util::ObjectsPool<VoxelsRenderVolume> voxelsVolumesPool {};
ChunksRenderer::ChunksRenderer(
const Level* level,
const Level& level,
const Chunks& chunks,
const Assets& assets,
const Frustum& frustum,
@ -71,7 +71,7 @@ ChunksRenderer::ChunksRenderer(
"chunks-render-pool",
[&]() {
return std::make_unique<RendererWorker>(
*level, cache, settings
level, cache, settings
);
},
[&](RendererResult&& result) {
@ -90,7 +90,7 @@ ChunksRenderer::ChunksRenderer(
threadPool.setStopOnFail(false);
renderer = std::make_unique<BlocksRenderer>(
settings.graphics.chunkMaxVertices.get(),
level->content, cache, settings
level.content, cache, settings
);
logger.info() << "created " << threadPool.getWorkersCount() << " workers";
logger.info() << "memory consumption is "
@ -120,17 +120,32 @@ std::shared_ptr<VoxelsRenderVolume> ChunksRenderer::prepareVoxelsVolume(
return voxelsBuffer;
}
std::shared_ptr<VoxelsVolume> ChunksRenderer::prepareVoxelsVolumeDynamic(
const Chunk& chunk, int padding
) {
auto voxelsBuffer = std::make_unique<VoxelsVolume>(
CHUNK_W + padding * 2, CHUNK_H, CHUNK_D + padding * 2
);
voxelsBuffer->setPosition(
chunk.x * CHUNK_W - padding, 0,
chunk.z * CHUNK_D - padding
);
chunks.getVoxels(
*voxelsBuffer, settings.graphics.backlight.get(), chunk.top + 3
);
return voxelsBuffer;
}
const Mesh<ChunkVertex>* ChunksRenderer::render(
const std::shared_ptr<Chunk>& chunk, bool important, bool lowPriority
) {
glm::ivec2 key(chunk->x, chunk->z);
chunk->flags.modified = false;
if (important) {
if (!important) {
ChunkMesh mesh {};
auto voxelsBuffer = prepareVoxelsVolume(*chunk);
auto mesh = renderer->render(chunk.get(), *voxelsBuffer);
meshes[key] = ChunkMesh {
std::move(mesh.mesh), std::move(mesh.sortingMeshData), nullptr};
mesh = renderer->render(chunk.get(), *voxelsBuffer);
meshes[key] = std::move(mesh);
return meshes[key].mesh.get();
}
if (inwork.find(key) != inwork.end() ||
@ -202,7 +217,7 @@ const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk(
);
auto mesh = getOrRender(
chunk,
distance < CHUNK_W * 1.5f,
distance < CHUNK_W * 1.5f * 10.0f,
distance > CHUNK_W * settings.chunks.loadDistance.get() * 0.5
);
if (mesh == nullptr) {
@ -309,17 +324,17 @@ void ChunksRenderer::drawChunks(
for (int i = indices.size()-1; i >= 0; i--) {
auto& chunk = chunks.getChunks()[indices[i].index];
auto mesh = retrieveChunk(indices[i].index, camera, culling);
if (mesh) {
glm::vec3 coord(
chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f
);
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
shader.uniformMatrix("u_model", model);
mesh->draw(GL_TRIANGLES, glm::distance2(camera.position * glm::vec3(1, 0, 1),
(coord + glm::vec3(CHUNK_W * 0.5f, 0.0f, CHUNK_D * 0.5f))) < denseDistance2);
visibleChunks++;
if (mesh == nullptr) {
continue;
}
glm::vec3 coord(
chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f
);
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
shader.uniformMatrix("u_model", model);
mesh->draw(GL_TRIANGLES, glm::distance2(camera.position * glm::vec3(1, 0, 1),
(coord + glm::vec3(CHUNK_W * 0.5f, 0.0f, CHUNK_D * 0.5f))) < denseDistance2);
visibleChunks++;
}
}

View file

@ -58,10 +58,12 @@ class ChunksRenderer {
size_t index, const Camera& camera, bool culling
);
std::shared_ptr<VoxelsRenderVolume> prepareVoxelsVolume(const Chunk& chunk);
std::shared_ptr<VoxelsVolume> prepareVoxelsVolumeDynamic(const Chunk& chunk, int padding);
size_t enqueuedInFrame = 0;
public:
ChunksRenderer(
const Level* level,
const Level& level,
const Chunks& chunks,
const Assets& assets,
const Frustum& frustum,

View file

@ -21,9 +21,7 @@ HandsRenderer::HandsRenderer(
skeleton(std::move(skeleton)) {
}
void HandsRenderer::renderHands(
const Camera& camera, float delta
) {
void HandsRenderer::render(const Camera& camera) {
auto& skeleton = *this->skeleton;
const auto& config = *skeleton.config;

View file

@ -18,7 +18,7 @@ public:
std::shared_ptr<rigging::Skeleton> skeleton
);
void renderHands(const Camera& camera, float delta);
void render(const Camera& camera);
private:
const Assets& assets;
ModelBatch& modelBatch;

View file

@ -46,12 +46,7 @@ static inline void update_particle(
particle.lifetime -= delta;
}
void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
const auto& right = camera.right;
const auto& up = camera.up;
bool backlight = settings->backlight.get();
void ParticlesRenderer::updateParticles(float delta) {
std::vector<const Texture*> unusedTextures;
for (auto& [texture, vec] : particles) {
@ -59,8 +54,6 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
unusedTextures.push_back(texture);
continue;
}
batch->setTexture(texture);
visibleParticles += vec.size();
auto iter = vec.begin();
@ -87,59 +80,6 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
}
}
update_particle(particle, delta, chunks);
float scale = 1.0f + ((particle.random ^ 2628172) % 1000) *
0.001f * preset.sizeSpread;
glm::vec4 light(1, 1, 1, 0);
if (preset.lighting) {
light = MainBatch::sampleLight(
particle.position,
chunks,
backlight
);
auto size = glm::max(glm::vec3(0.5f), preset.size * scale);
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
for (int z = -1; z <= 1; z++) {
light = glm::max(
light,
MainBatch::sampleLight(
particle.position -
size * glm::vec3(x, y, z),
chunks,
backlight
)
);
}
}
}
light *= 0.9f + (particle.random % 100) * 0.001f;
}
glm::vec3 localRight = right;
glm::vec3 localUp = preset.globalUpVector ? glm::vec3(0, 1, 0) : up;
float angle = particle.angle;
if (glm::abs(angle) >= 0.005f) {
glm::vec3 rotatedRight(glm::cos(angle), -glm::sin(angle), 0.0f);
glm::vec3 rotatedUp(glm::sin(angle), glm::cos(angle), 0.0f);
localRight = right * rotatedRight.x + localUp * rotatedRight.y +
camera.front * rotatedRight.z;
localUp = right * rotatedUp.x + localUp * rotatedUp.y +
camera.front * rotatedUp.z;
}
batch->quad(
particle.position,
localRight,
localUp,
-camera.front,
preset.size * scale,
light,
glm::vec3(1.0f),
particle.region,
preset.lighting ? 0.0f : 1.0f
);
if (particle.lifetime <= 0.0f) {
iter = vec.erase(iter);
emitter.refCount--;
@ -148,19 +88,85 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
}
}
}
batch->flush();
for (const auto& texture : unusedTextures) {
particles.erase(texture);
}
}
void ParticlesRenderer::render(const Camera& camera, float delta) {
batch->begin();
aliveEmitters = emitters.size();
visibleParticles = 0;
static inline glm::vec4 calc_lights(
Particle& particle,
ParticlesPreset& preset,
bool backlight,
float scale,
const Chunks& chunks
) {
auto light = MainBatch::sampleLight(
particle.position,
chunks,
backlight
);
auto size = glm::max(glm::vec3(0.5f), preset.size * scale);
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
for (int z = -1; z <= 1; z++) {
light = glm::max(
light,
MainBatch::sampleLight(
particle.position - size * glm::vec3(x, y, z),
chunks,
backlight
)
);
}
}
}
light *= 0.9f + (particle.random % 100) * 0.001f;
return light;
}
renderParticles(camera, delta);
void ParticlesRenderer::renderParticle(
Particle& particle, const Camera& camera, bool backlight
) {
const auto& right = camera.right;
const auto& up = camera.up;
auto& emitter = *particle.emitter;
auto& preset = emitter.preset;
float scale = 1.0f + ((particle.random ^ 2628172) % 1000) *
0.001f * preset.sizeSpread;
glm::vec4 light(1, 1, 1, 0);
if (preset.lighting) {
light = calc_lights(particle, preset, backlight, scale, chunks);
}
glm::vec3 localRight = right;
glm::vec3 localUp = preset.globalUpVector ? glm::vec3(0, 1, 0) : up;
float angle = particle.angle;
if (glm::abs(angle) >= 0.005f) {
glm::vec3 rotatedRight(glm::cos(angle), -glm::sin(angle), 0.0f);
glm::vec3 rotatedUp(glm::sin(angle), glm::cos(angle), 0.0f);
localRight = right * rotatedRight.x + localUp * rotatedRight.y +
camera.front * rotatedRight.z;
localUp = right * rotatedUp.x + localUp * rotatedUp.y +
camera.front * rotatedUp.z;
}
batch->quad(
particle.position,
localRight,
localUp,
-camera.front,
preset.size * scale,
light,
glm::vec3(1.0f),
particle.region,
preset.lighting ? 0.0f : 1.0f
);
}
void ParticlesRenderer::update(const Camera& camera, float delta) {
updateParticles(delta);
auto iter = emitters.begin();
while (iter != emitters.end()) {
@ -178,6 +184,33 @@ void ParticlesRenderer::render(const Camera& camera, float delta) {
}
}
void ParticlesRenderer::render(const Camera& camera) {
aliveEmitters = emitters.size();
visibleParticles = 0;
bool backlight = settings->backlight.get();
batch->begin();
for (auto& [texture, vec] : particles) {
batch->setTexture(texture);
auto iter = vec.begin();
while (iter != vec.end()) {
auto& particle = *iter;
renderParticle(particle, camera, backlight);
if (particle.lifetime <= 0.0f) {
iter = vec.erase(iter);
particle.emitter->refCount--;
} else {
iter++;
}
}
}
batch->flush();
}
Emitter* ParticlesRenderer::getEmitter(u64id_t id) const {
const auto& found = emitters.find(id);
if (found == emitters.end()) {

View file

@ -25,7 +25,10 @@ class ParticlesRenderer {
std::unordered_map<u64id_t, std::unique_ptr<Emitter>> emitters;
u64id_t nextEmitter = 1;
void renderParticles(const Camera& camera, float delta);
void renderParticle(
Particle& particle, const Camera& camera, bool backlight
);
void updateParticles(float delta);
public:
ParticlesRenderer(
const Assets& assets,
@ -35,7 +38,8 @@ public:
);
~ParticlesRenderer();
void render(const Camera& camera, float delta);
void update(const Camera& camera, float delta);
void render(const Camera& camera);
u64id_t add(std::unique_ptr<Emitter> emitter);

View file

@ -90,11 +90,13 @@ static UVRegion calc_uv(
return {u1, v1, u1 + m * scale, v1 + FACE_SIZE.y * scale};
}
void PrecipitationRenderer::render(
const Camera& camera, float delta, const WeatherPreset& weather
) {
void PrecipitationRenderer::update(float delta) {
timer += delta;
}
void PrecipitationRenderer::render(
const Camera& camera, const WeatherPreset& weather
) {
const int radius = 6;
const int depth = 12;

View file

@ -27,5 +27,7 @@ public:
~PrecipitationRenderer();
void render(const Camera& camera, float delta, const WeatherPreset& weather);
void update(float delta);
void render(const Camera& camera, const WeatherPreset& weather);
};

View file

@ -1,12 +1,5 @@
#include "WorldRenderer.hpp"
#include <assert.h>
#include <algorithm>
#include <glm/ext.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <memory>
#include "assets/Assets.hpp"
#include "assets/assets_util.hpp"
#include "content/Content.hpp"
@ -18,7 +11,6 @@
#include "items/ItemDef.hpp"
#include "items/ItemStack.hpp"
#include "logic/PlayerController.hpp"
#include "logic/scripting/scripting_hud.hpp"
#include "maths/FrustumCulling.hpp"
#include "maths/voxmaths.hpp"
#include "objects/Entities.hpp"
@ -59,6 +51,12 @@
#include "Emitter.hpp"
#include "TextNote.hpp"
#include <assert.h>
#include <algorithm>
#include <glm/ext.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <memory>
using namespace advanced_pipeline;
inline constexpr size_t BATCH3D_CAPACITY = 4096;
@ -81,7 +79,7 @@ WorldRenderer::WorldRenderer(
MODEL_BATCH_CAPACITY, assets, *player.chunks, engine.getSettings()
)),
chunksRenderer(std::make_unique<ChunksRenderer>(
&level,
level,
*player.chunks,
assets,
*frustumCulling,
@ -103,19 +101,18 @@ WorldRenderer::WorldRenderer(
LevelEventType::CHUNK_HIDDEN,
[this](LevelEventType, Chunk* chunk) { chunksRenderer->unload(chunk); }
);
auto assets = engine.getAssets();
skybox = std::make_unique<Skybox>(
settings.graphics.skyboxResolution.get(),
assets->require<Shader>("skybox_gen")
assets.require<Shader>("skybox_gen")
);
const auto& content = level.content;
skeletons = std::make_unique<NamedSkeletons>();
const auto& skeletonConfig = assets->require<rigging::SkeletonConfig>(
const auto& skeletonConfig = assets.require<rigging::SkeletonConfig>(
content.getDefaults()["hand-skeleton"].asString()
);
hands = std::make_unique<HandsRenderer>(
*assets, *modelBatch, skeletons->createSkeleton("hand", &skeletonConfig)
assets, *modelBatch, skeletons->createSkeleton("hand", &skeletonConfig)
);
shadowMapping = std::make_unique<Shadows>(level);
debugLines = std::make_unique<DebugLinesRenderer>(level);
@ -179,8 +176,6 @@ void WorldRenderer::renderOpaque(
const DrawContext& ctx,
const Camera& camera,
const EngineSettings& settings,
float delta,
bool pause,
bool hudVisible
) {
texts->render(ctx, camera, settings, hudVisible, false);
@ -203,13 +198,11 @@ void WorldRenderer::renderOpaque(
assets,
*modelBatch,
culling ? frustumCulling.get() : nullptr,
delta,
pause,
player.currentCamera.get() == player.fpCamera.get() ? player.getEntity()
: 0
);
modelBatch->render();
particles->render(camera, delta * !pause);
particles->render(camera);
auto& shader = assets.require<Shader>("main");
auto& linesShader = assets.require<Shader>("lines");
@ -222,10 +215,6 @@ void WorldRenderer::renderOpaque(
if (hudVisible) {
renderLines(camera, linesShader, ctx);
}
if (!pause) {
scripting::on_frontend_render();
}
skybox->unbind();
}
@ -275,45 +264,17 @@ void WorldRenderer::renderLines(
}
}
void WorldRenderer::renderFrame(
const DrawContext& pctx,
Camera& camera,
bool hudVisible,
bool pause,
float uiDelta,
PostProcessing& postProcessing
) {
// TODO: REFACTOR WHOLE RENDER ENGINE
void WorldRenderer::refreshSettings(Shader** shaders) {
const auto& graphics = engine.getSettings().graphics;
gbufferPipeline = graphics.advancedRender.get();
auto projView = camera.getProjView();
float delta = uiDelta * !pause;
timer += delta;
weather.update(delta);
auto world = level.getWorld();
const auto& vp = pctx.getViewport();
camera.setAspectRatio(vp.x / static_cast<float>(vp.y));
auto& mainShader = assets.require<Shader>("main");
auto& entityShader = assets.require<Shader>("entity");
auto& translucentShader = assets.require<Shader>("translucent");
auto& deferredShader = assets.require<PostEffect>("deferred_lighting").getShader();
const auto& settings = engine.getSettings();
Shader* affectedShaders[] {
&mainShader, &entityShader, &translucentShader, &deferredShader
};
gbufferPipeline = settings.graphics.advancedRender.get();
int shadowsQuality = settings.graphics.shadowsQuality.get() * gbufferPipeline;
int shadowsQuality = graphics.shadowsQuality.get() * gbufferPipeline;
shadowMapping->setQuality(shadowsQuality);
CompileTimeShaderSettings currentSettings {
gbufferPipeline,
shadowsQuality != 0,
settings.graphics.ssao.get() && gbufferPipeline
graphics.ssao.get() && gbufferPipeline
};
if (
prevCTShaderSettings.advancedRender != currentSettings.advancedRender ||
@ -325,11 +286,44 @@ void WorldRenderer::renderFrame(
if (currentSettings.ssao) defines.emplace_back("ENABLE_SSAO");
if (currentSettings.advancedRender) defines.emplace_back("ADVANCED_RENDER");
for (auto shader : affectedShaders) {
shader->recompile(defines);
for (size_t i = 0; shaders[i]; i++) {
shaders[i]->recompile(defines);
}
prevCTShaderSettings = currentSettings;
}
}
void WorldRenderer::update(const Camera& camera, float delta) {
timer += delta;
weather.update(delta);
precipitation->update(delta);
particles->update(camera, delta);
}
void WorldRenderer::renderFrame(
const DrawContext& pctx,
Camera& camera,
bool hudVisible,
PostProcessing& postProcessing
) {
auto projView = camera.getProjView();
auto world = level.getWorld();
const auto& vp = pctx.getViewport();
camera.setAspectRatio(vp.x / static_cast<float>(vp.y));
auto& mainShader = assets.require<Shader>("main");
auto& entityShader = assets.require<Shader>("entity");
auto& translucentShader = assets.require<Shader>("translucent");
auto& deferredShader = assets.require<PostEffect>("deferred_lighting").getShader();
const auto& settings = engine.getSettings();
Shader* affectedShaders[] {
&mainShader, &entityShader, &translucentShader, &deferredShader, nullptr
};
refreshSettings(affectedShaders);
const auto& worldInfo = world->getInfo();
@ -356,7 +350,7 @@ void WorldRenderer::renderFrame(
DrawContext ctx = wctx.sub();
ctx.setDepthTest(true);
ctx.setCullFace(true);
renderOpaque(ctx, camera, settings, uiDelta, pause, hudVisible);
renderOpaque(ctx, camera, settings, hudVisible);
}
texts->render(pctx, camera, settings, hudVisible, true);
}
@ -415,7 +409,7 @@ void WorldRenderer::renderFrame(
entityShader.uniform1i("u_alphaClip", weather->fall.opaque);
entityShader.uniform1f("u_opacity", weather->fall.opaque ? t * t : t);
if (weather->intensity > 1.e-3f && !weather->fall.texture.empty()) {
precipitation->render(camera, pause ? 0.0f : delta, *weather);
precipitation->render(camera, *weather);
}
}
@ -434,7 +428,7 @@ void WorldRenderer::renderFrame(
hudcam.setFov(0.9f);
hudcam.position = {};
hands->renderHands(camera, delta);
hands->render(camera);
display::clearDepth();
setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f);

View file

@ -1,9 +1,6 @@
#pragma once
#include <vector>
#include <memory>
#include <algorithm>
#include <string>
#include "commons.hpp"
#include "typedefs.hpp"
@ -11,30 +8,35 @@
#include "world/Weather.hpp"
#include "window/Camera.hpp"
class Level;
class Player;
class Camera;
class Batch3D;
class LineBatch;
class ChunksRenderer;
class ParticlesRenderer;
class BlockWrapsRenderer;
class PrecipitationRenderer;
class HandsRenderer;
class NamedSkeletons;
class TextsRenderer;
class Shader;
class Frustum;
class Engine;
class LevelFrontend;
class Skybox;
class PostProcessing;
class DrawContext;
class ModelBatch;
#include <vector>
#include <memory>
#include <algorithm>
#include <string>
class Assets;
class Shadows;
class GBuffer;
class Batch3D;
class BlockWrapsRenderer;
class Camera;
class ChunksRenderer;
class DebugLinesRenderer;
class DrawContext;
class Engine;
class Frustum;
class GBuffer;
class HandsRenderer;
class Level;
class LevelFrontend;
class LineBatch;
class ModelBatch;
class NamedSkeletons;
class ParticlesRenderer;
class Player;
class PostProcessing;
class PrecipitationRenderer;
class Shader;
class Shadows;
class Skybox;
class TextsRenderer;
struct EngineSettings;
struct CompileTimeShaderSettings {
@ -93,10 +95,10 @@ class WorldRenderer {
const DrawContext& context,
const Camera& camera,
const EngineSettings& settings,
float delta,
bool pause,
bool hudVisible
);
void refreshSettings(Shader** shaders);
public:
std::unique_ptr<ParticlesRenderer> particles;
std::unique_ptr<TextsRenderer> texts;
@ -110,12 +112,12 @@ public:
WorldRenderer(Engine& engine, LevelFrontend& frontend, Player& player);
~WorldRenderer();
void update(const Camera& camera, float delta);
void renderFrame(
const DrawContext& context,
Camera& camera,
bool hudVisible,
bool pause,
float delta,
PostProcessing& postProcessing
);

View file

@ -341,6 +341,16 @@ void Entities::update(float delta) {
}
updatePhysics(delta);
scripting::on_entities_physics_update(delta);
auto view = registry->view<Transform, rigging::Skeleton>();
for (auto [entity, transform, skeleton] : view.each()) {
if (transform.dirty) {
transform.refresh();
}
if (skeleton.interpolation.isEnabled()) {
skeleton.interpolation.updateTimer(delta);
}
}
}
static void debug_render_skeleton(
@ -415,8 +425,6 @@ void Entities::render(
const Assets& assets,
ModelBatch& batch,
const Frustum* frustum,
float delta,
bool pause,
entityid_t fpsEntity
) {
auto view = registry->view<EntityId, Transform, rigging::Skeleton>();
@ -424,12 +432,6 @@ void Entities::render(
if (eid.uid == fpsEntity) {
continue;
}
if (transform.dirty) {
transform.refresh();
}
if (skeleton.interpolation.isEnabled()) {
skeleton.interpolation.updateTimer(delta);
}
const auto& pos = transform.pos;
const auto& size = transform.size;
if (frustum && !frustum->isBoxVisible(pos - size, pos + size)) {

View file

@ -69,8 +69,6 @@ public:
const Assets& assets,
ModelBatch& batch,
const Frustum* frustum,
float delta,
bool pause,
entityid_t fpsEntity
);