|
|
|
@@ -42,7 +42,7 @@
|
|
|
|
|
#include "graphics/core/Shader.hpp"
|
|
|
|
|
#include "graphics/core/Texture.hpp"
|
|
|
|
|
#include "graphics/core/Font.hpp"
|
|
|
|
|
#include "graphics/core/ShadowMap.hpp"
|
|
|
|
|
#include "graphics/core/Shadows.hpp"
|
|
|
|
|
#include "graphics/core/GBuffer.hpp"
|
|
|
|
|
#include "BlockWrapsRenderer.hpp"
|
|
|
|
|
#include "ParticlesRenderer.hpp"
|
|
|
|
@@ -62,8 +62,6 @@ using namespace advanced_pipeline;
|
|
|
|
|
|
|
|
|
|
inline constexpr size_t BATCH3D_CAPACITY = 4096;
|
|
|
|
|
inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000;
|
|
|
|
|
inline constexpr GLenum TEXTURE_MAIN = GL_TEXTURE0;
|
|
|
|
|
inline constexpr int MIN_SHADOW_MAP_RES = 512;
|
|
|
|
|
|
|
|
|
|
bool WorldRenderer::showChunkBorders = false;
|
|
|
|
|
bool WorldRenderer::showEntitiesDebug = false;
|
|
|
|
@@ -82,7 +80,7 @@ WorldRenderer::WorldRenderer(
|
|
|
|
|
MODEL_BATCH_CAPACITY, assets, *player.chunks, engine.getSettings()
|
|
|
|
|
)),
|
|
|
|
|
guides(std::make_unique<GuidesRenderer>()),
|
|
|
|
|
chunks(std::make_unique<ChunksRenderer>(
|
|
|
|
|
chunksRenderer(std::make_unique<ChunksRenderer>(
|
|
|
|
|
&level,
|
|
|
|
|
*player.chunks,
|
|
|
|
|
assets,
|
|
|
|
@@ -103,7 +101,7 @@ WorldRenderer::WorldRenderer(
|
|
|
|
|
auto& settings = engine.getSettings();
|
|
|
|
|
level.events->listen(
|
|
|
|
|
LevelEventType::CHUNK_HIDDEN,
|
|
|
|
|
[this](LevelEventType, Chunk* chunk) { chunks->unload(chunk); }
|
|
|
|
|
[this](LevelEventType, Chunk* chunk) { chunksRenderer->unload(chunk); }
|
|
|
|
|
);
|
|
|
|
|
auto assets = engine.getAssets();
|
|
|
|
|
skybox = std::make_unique<Skybox>(
|
|
|
|
@@ -120,10 +118,24 @@ WorldRenderer::WorldRenderer(
|
|
|
|
|
*assets, *modelBatch, skeletons->createSkeleton("hand", &skeletonConfig)
|
|
|
|
|
);
|
|
|
|
|
lines = std::make_unique<LinesRenderer>();
|
|
|
|
|
shadowMapping = std::make_unique<Shadows>(level);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WorldRenderer::~WorldRenderer() = default;
|
|
|
|
|
|
|
|
|
|
static void setup_weather(Shader& shader, const Weather& weather) {
|
|
|
|
|
shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity());
|
|
|
|
|
shader.uniform1f("u_weatherFogDencity", weather.fogDencity());
|
|
|
|
|
shader.uniform1f("u_weatherFogCurve", weather.fogCurve());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void setup_camera(Shader& shader, const Camera& camera) {
|
|
|
|
|
shader.uniformMatrix("u_model", glm::mat4(1.0f));
|
|
|
|
|
shader.uniformMatrix("u_proj", camera.getProjection());
|
|
|
|
|
shader.uniformMatrix("u_view", camera.getView());
|
|
|
|
|
shader.uniform3f("u_cameraPos", camera.position);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorldRenderer::setupWorldShader(
|
|
|
|
|
Shader& shader,
|
|
|
|
|
const Camera& camera,
|
|
|
|
@@ -131,45 +143,20 @@ void WorldRenderer::setupWorldShader(
|
|
|
|
|
float fogFactor
|
|
|
|
|
) {
|
|
|
|
|
shader.use();
|
|
|
|
|
shader.uniformMatrix("u_model", glm::mat4(1.0f));
|
|
|
|
|
shader.uniformMatrix("u_proj", camera.getProjection());
|
|
|
|
|
shader.uniformMatrix("u_view", camera.getView());
|
|
|
|
|
|
|
|
|
|
setup_camera(shader, camera);
|
|
|
|
|
setup_weather(shader, weather);
|
|
|
|
|
shadowMapping->setup(shader, weather);
|
|
|
|
|
|
|
|
|
|
shader.uniform1f("u_timer", timer);
|
|
|
|
|
shader.uniform1f("u_gamma", settings.graphics.gamma.get());
|
|
|
|
|
shader.uniform1f("u_fogFactor", fogFactor);
|
|
|
|
|
shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
|
|
|
|
|
shader.uniform1i("u_debugLights", lightsDebug);
|
|
|
|
|
shader.uniform1i("u_debugNormals", false);
|
|
|
|
|
shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity());
|
|
|
|
|
shader.uniform1f("u_weatherFogDencity", weather.fogDencity());
|
|
|
|
|
shader.uniform1f("u_weatherFogCurve", weather.fogCurve());
|
|
|
|
|
shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime);
|
|
|
|
|
shader.uniform2f("u_lightDir", skybox->getLightDir());
|
|
|
|
|
shader.uniform3f("u_cameraPos", camera.position);
|
|
|
|
|
shader.uniform1i("u_skybox", 1);
|
|
|
|
|
shader.uniform1i("u_enableShadows", shadows);
|
|
|
|
|
|
|
|
|
|
if (shadows) {
|
|
|
|
|
const auto& worldInfo = level.getWorld()->getInfo();
|
|
|
|
|
float cloudsIntensity = glm::max(worldInfo.fog, weather.clouds());
|
|
|
|
|
shader.uniform1i("u_screen", 0);
|
|
|
|
|
shader.uniformMatrix("u_shadowsMatrix[0]", shadowCamera.getProjView());
|
|
|
|
|
shader.uniformMatrix("u_shadowsMatrix[1]", wideShadowCamera.getProjView());
|
|
|
|
|
shader.uniform3f("u_sunDir", shadowCamera.front);
|
|
|
|
|
shader.uniform1i("u_shadowsRes", shadowMap->getResolution());
|
|
|
|
|
shader.uniform1f("u_shadowsOpacity", 1.0f - cloudsIntensity); // TODO: make it configurable
|
|
|
|
|
shader.uniform1f("u_shadowsSoftness", 1.0f + cloudsIntensity * 4); // TODO: make it configurable
|
|
|
|
|
|
|
|
|
|
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS0);
|
|
|
|
|
shader.uniform1i("u_shadows[0]", TARGET_SHADOWS0);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, shadowMap->getDepthMap());
|
|
|
|
|
|
|
|
|
|
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS1);
|
|
|
|
|
shader.uniform1i("u_shadows[1]", TARGET_SHADOWS1);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, wideShadowMap->getDepthMap());
|
|
|
|
|
|
|
|
|
|
glActiveTexture(TEXTURE_MAIN);
|
|
|
|
|
}
|
|
|
|
|
shader.uniform1i("u_skybox", TARGET_SKYBOX);
|
|
|
|
|
|
|
|
|
|
auto indices = level.content.getIndices();
|
|
|
|
|
// Light emission when an emissive item is chosen
|
|
|
|
@@ -188,7 +175,7 @@ void WorldRenderer::setupWorldShader(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorldRenderer::renderLevel(
|
|
|
|
|
void WorldRenderer::renderOpaque(
|
|
|
|
|
const DrawContext& ctx,
|
|
|
|
|
const Camera& camera,
|
|
|
|
|
const EngineSettings& settings,
|
|
|
|
@@ -227,7 +214,7 @@ void WorldRenderer::renderLevel(
|
|
|
|
|
|
|
|
|
|
setupWorldShader(shader, camera, settings, fogFactor);
|
|
|
|
|
|
|
|
|
|
chunks->drawChunks(camera, shader);
|
|
|
|
|
chunksRenderer->drawChunks(camera, shader);
|
|
|
|
|
blockWraps->draw(ctx, player);
|
|
|
|
|
|
|
|
|
|
if (hudVisible) {
|
|
|
|
@@ -286,79 +273,7 @@ void WorldRenderer::renderLines(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorldRenderer::generateShadowsMap(
|
|
|
|
|
const Camera& camera,
|
|
|
|
|
const DrawContext& pctx,
|
|
|
|
|
ShadowMap& shadowMap,
|
|
|
|
|
Camera& shadowCamera,
|
|
|
|
|
float scale
|
|
|
|
|
) {
|
|
|
|
|
auto& shadowsShader = assets.require<Shader>("shadows");
|
|
|
|
|
|
|
|
|
|
auto world = level.getWorld();
|
|
|
|
|
const auto& worldInfo = world->getInfo();
|
|
|
|
|
|
|
|
|
|
const auto& settings = engine.getSettings();
|
|
|
|
|
int resolution = shadowMap.getResolution();
|
|
|
|
|
int quality = settings.graphics.shadowsQuality.get();
|
|
|
|
|
float shadowMapScale = 0.32f / (1 << glm::max(0, quality)) * scale;
|
|
|
|
|
float shadowMapSize = resolution * shadowMapScale;
|
|
|
|
|
|
|
|
|
|
glm::vec3 basePos = glm::floor(camera.position / 4.0f) * 4.0f;
|
|
|
|
|
glm::vec3 prevPos = shadowCamera.position;
|
|
|
|
|
shadowCamera = Camera(
|
|
|
|
|
glm::distance2(prevPos, basePos) > 25.0f ? basePos : prevPos,
|
|
|
|
|
shadowMapSize
|
|
|
|
|
);
|
|
|
|
|
shadowCamera.near = 0.1f;
|
|
|
|
|
shadowCamera.far = 1000.0f;
|
|
|
|
|
shadowCamera.perspective = false;
|
|
|
|
|
shadowCamera.setAspectRatio(1.0f);
|
|
|
|
|
|
|
|
|
|
float t = worldInfo.daytime - 0.25f;
|
|
|
|
|
if (t < 0.0f) {
|
|
|
|
|
t += 1.0f;
|
|
|
|
|
}
|
|
|
|
|
t = fmod(t, 0.5f);
|
|
|
|
|
|
|
|
|
|
float sunCycleStep = 1.0f / 500.0f;
|
|
|
|
|
float sunAngle = glm::radians(
|
|
|
|
|
90.0f -
|
|
|
|
|
((static_cast<int>(t / sunCycleStep)) * sunCycleStep + 0.25f) * 360.0f
|
|
|
|
|
);
|
|
|
|
|
float sunAltitude = glm::pi<float>() * 0.25f;
|
|
|
|
|
shadowCamera.rotate(
|
|
|
|
|
-glm::cos(sunAngle + glm::pi<float>() * 0.5f) * sunAltitude,
|
|
|
|
|
sunAngle - glm::pi<float>() * 0.5f,
|
|
|
|
|
glm::radians(0.0f)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
shadowCamera.position -= shadowCamera.front * 500.0f;
|
|
|
|
|
shadowCamera.position += shadowCamera.up * 0.0f;
|
|
|
|
|
shadowCamera.position += camera.front * 0.0f;
|
|
|
|
|
|
|
|
|
|
auto view = shadowCamera.getView();
|
|
|
|
|
|
|
|
|
|
auto currentPos = shadowCamera.position;
|
|
|
|
|
auto topRight = shadowCamera.right + shadowCamera.up;
|
|
|
|
|
auto min = view * glm::vec4(currentPos - topRight * shadowMapSize * 0.5f, 1.0f);
|
|
|
|
|
auto max = view * glm::vec4(currentPos + topRight * shadowMapSize * 0.5f, 1.0f);
|
|
|
|
|
|
|
|
|
|
shadowCamera.setProjection(glm::ortho(min.x, max.x, min.y, max.y, 0.1f, 1000.0f));
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
auto sctx = pctx.sub();
|
|
|
|
|
sctx.setDepthTest(true);
|
|
|
|
|
sctx.setCullFace(true);
|
|
|
|
|
sctx.setViewport({resolution, resolution});
|
|
|
|
|
shadowMap.bind();
|
|
|
|
|
setupWorldShader(shadowsShader, shadowCamera, settings, 0.0f);
|
|
|
|
|
chunks->drawChunksShadowsPass(shadowCamera, shadowsShader, camera);
|
|
|
|
|
shadowMap.unbind();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorldRenderer::draw(
|
|
|
|
|
void WorldRenderer::renderFrame(
|
|
|
|
|
const DrawContext& pctx,
|
|
|
|
|
Camera& camera,
|
|
|
|
|
bool hudVisible,
|
|
|
|
@@ -382,22 +297,17 @@ void WorldRenderer::draw(
|
|
|
|
|
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 resolution = MIN_SHADOW_MAP_RES << shadowsQuality;
|
|
|
|
|
if (shadowsQuality > 0 && !shadows) {
|
|
|
|
|
shadowMap = std::make_unique<ShadowMap>(resolution);
|
|
|
|
|
wideShadowMap = std::make_unique<ShadowMap>(resolution);
|
|
|
|
|
shadows = true;
|
|
|
|
|
} else if (shadowsQuality == 0 && shadows) {
|
|
|
|
|
shadowMap.reset();
|
|
|
|
|
wideShadowMap.reset();
|
|
|
|
|
shadows = false;
|
|
|
|
|
}
|
|
|
|
|
shadowMapping->setQuality(shadowsQuality);
|
|
|
|
|
|
|
|
|
|
CompileTimeShaderSettings currentSettings {
|
|
|
|
|
gbufferPipeline,
|
|
|
|
|
shadows,
|
|
|
|
|
shadowsQuality != 0,
|
|
|
|
|
settings.graphics.ssao.get() && gbufferPipeline
|
|
|
|
|
};
|
|
|
|
|
if (
|
|
|
|
@@ -408,18 +318,12 @@ void WorldRenderer::draw(
|
|
|
|
|
Shader::preprocessor->setDefined("ENABLE_SHADOWS", currentSettings.shadows);
|
|
|
|
|
Shader::preprocessor->setDefined("ENABLE_SSAO", currentSettings.ssao);
|
|
|
|
|
Shader::preprocessor->setDefined("ADVANCED_RENDER", currentSettings.advancedRender);
|
|
|
|
|
mainShader.recompile();
|
|
|
|
|
entityShader.recompile();
|
|
|
|
|
deferredShader.recompile();
|
|
|
|
|
translucentShader.recompile();
|
|
|
|
|
for (auto shader : affectedShaders) {
|
|
|
|
|
shader->recompile();
|
|
|
|
|
}
|
|
|
|
|
prevCTShaderSettings = currentSettings;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shadows && shadowMap->getResolution() != resolution) {
|
|
|
|
|
shadowMap = std::make_unique<ShadowMap>(resolution);
|
|
|
|
|
wideShadowMap = std::make_unique<ShadowMap>(resolution);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto& worldInfo = world->getInfo();
|
|
|
|
|
|
|
|
|
|
float clouds = weather.clouds();
|
|
|
|
@@ -428,30 +332,27 @@ void WorldRenderer::draw(
|
|
|
|
|
|
|
|
|
|
skybox->refresh(pctx, worldInfo.daytime, mie, 4);
|
|
|
|
|
|
|
|
|
|
chunks->update();
|
|
|
|
|
chunksRenderer->update();
|
|
|
|
|
|
|
|
|
|
static int frameid = 0;
|
|
|
|
|
if (shadows) {
|
|
|
|
|
if (frameid % 2 == 0) {
|
|
|
|
|
generateShadowsMap(camera, pctx, *shadowMap, shadowCamera, 1.0f);
|
|
|
|
|
} else {
|
|
|
|
|
generateShadowsMap(camera, pctx, *wideShadowMap, wideShadowCamera, 3.0f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
frameid++;
|
|
|
|
|
shadowMapping->refresh(camera, pctx, [this, &camera](Camera& shadowCamera) {
|
|
|
|
|
auto& shader = assets.require<Shader>("shadows");
|
|
|
|
|
setupWorldShader(shader, shadowCamera, engine.getSettings(), 0.0f);
|
|
|
|
|
chunksRenderer->drawShadowsPass(shadowCamera, shader, camera);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
auto& linesShader = assets.require<Shader>("lines");
|
|
|
|
|
/* World render scope with diegetic HUD included */ {
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
DrawContext wctx = pctx.sub();
|
|
|
|
|
postProcessing.use(wctx, gbufferPipeline);
|
|
|
|
|
|
|
|
|
|
display::clearDepth();
|
|
|
|
|
|
|
|
|
|
/* Actually world render with depth buffer on */ {
|
|
|
|
|
/* Main opaque pass (GBuffer pass) */ {
|
|
|
|
|
DrawContext ctx = wctx.sub();
|
|
|
|
|
ctx.setDepthTest(true);
|
|
|
|
|
ctx.setCullFace(true);
|
|
|
|
|
renderLevel(ctx, camera, settings, uiDelta, pause, hudVisible);
|
|
|
|
|
renderOpaque(ctx, camera, settings, uiDelta, pause, hudVisible);
|
|
|
|
|
// Debug lines
|
|
|
|
|
if (hudVisible) {
|
|
|
|
|
if (debug) {
|
|
|
|
@@ -480,23 +381,27 @@ void WorldRenderer::draw(
|
|
|
|
|
} else {
|
|
|
|
|
postProcessing.getFramebuffer()->bind();
|
|
|
|
|
}
|
|
|
|
|
// Drawing background sky plane
|
|
|
|
|
|
|
|
|
|
// Background sky plane
|
|
|
|
|
skybox->draw(ctx, camera, assets, worldInfo.daytime, clouds);
|
|
|
|
|
|
|
|
|
|
// In-world lines
|
|
|
|
|
linesShader.use();
|
|
|
|
|
lines->draw(*lineBatch);
|
|
|
|
|
lineBatch->flush();
|
|
|
|
|
|
|
|
|
|
// Translucent blocks
|
|
|
|
|
{
|
|
|
|
|
auto sctx = ctx.sub();
|
|
|
|
|
sctx.setCullFace(true);
|
|
|
|
|
skybox->bind();
|
|
|
|
|
translucentShader.use();
|
|
|
|
|
setupWorldShader(translucentShader, camera, settings, fogFactor);
|
|
|
|
|
chunks->drawSortedMeshes(camera, translucentShader);
|
|
|
|
|
chunksRenderer->drawSortedMeshes(camera, translucentShader);
|
|
|
|
|
skybox->unbind();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Weather effects
|
|
|
|
|
entityShader.use();
|
|
|
|
|
setupWorldShader(entityShader, camera, settings, fogFactor);
|
|
|
|
|
|
|
|
|
@@ -588,7 +493,7 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorldRenderer::clear() {
|
|
|
|
|
chunks->clear();
|
|
|
|
|
chunksRenderer->clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorldRenderer::setDebug(bool flag) {
|
|
|
|
|