#include "WorldRenderer.h" #include #include #include #include #include "../content/Content.h" #include "../graphics/ChunksRenderer.h" #include "../window/Window.h" #include "../window/Camera.h" #include "../graphics/Mesh.h" #include "../graphics/Atlas.h" #include "../graphics/Shader.h" #include "../graphics/Texture.h" #include "../graphics/LineBatch.h" #include "../voxels/Chunks.h" #include "../voxels/Chunk.h" #include "../voxels/Block.h" #include "../world/World.h" #include "../world/Level.h" #include "../world/LevelEvents.h" #include "../objects/Player.h" #include "../assets/Assets.h" #include "../logic/PlayerController.h" #include "../maths/FrustumCulling.h" #include "../maths/voxmaths.h" #include "../settings.h" #include "../engine.h" #include "ContentGfxCache.h" #include "graphics/Skybox.h" using glm::vec3; using glm::vec4; using glm::mat4; using std::string; using std::shared_ptr; WorldRenderer::WorldRenderer(Engine* engine, Level* level, const ContentGfxCache* cache) : engine(engine), level(level), frustumCulling(new Frustum()), lineBatch(new LineBatch()), renderer( new ChunksRenderer(level, cache, engine->getSettings())) { auto& settings = engine->getSettings(); level->events->listen(EVT_CHUNK_HIDDEN, [this](lvl_event_type type, Chunk* chunk) { renderer->unload(chunk); } ); auto assets = engine->getAssets(); skybox = new Skybox(settings.graphics.skyboxResolution, assets->getShader("skybox_gen")); } WorldRenderer::~WorldRenderer() { delete skybox; delete lineBatch; delete renderer; delete frustumCulling; } bool WorldRenderer::drawChunk(size_t index, Camera* camera, Shader* shader, bool culling){ auto chunk = level->chunks->chunks[index]; if (!chunk->isLighted()) { return false; } shared_ptr mesh = renderer->getOrRender(chunk.get()); if (mesh == nullptr) { return false; } if (culling){ vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D); vec3 max(chunk->x * CHUNK_W + CHUNK_W, chunk->top, chunk->z * CHUNK_D + CHUNK_D); if (!frustumCulling->IsBoxVisible(min, max)) return false; } vec3 coord = vec3(chunk->x*CHUNK_W, 0.0f, chunk->z*CHUNK_D+1); mat4 model = glm::translate(mat4(1.0f), coord); shader->uniformMatrix("u_model", model); mesh->draw(); return true; } void WorldRenderer::drawChunks(Chunks* chunks, Camera* camera, Shader* shader) { std::vector indices; for (size_t i = 0; i < chunks->volume; i++){ shared_ptr chunk = chunks->chunks[i]; if (chunk == nullptr) continue; indices.push_back(i); } float px = camera->position.x / (float)CHUNK_W; float pz = camera->position.z / (float)CHUNK_D; std::sort(indices.begin(), indices.end(), [this, chunks, px, pz](size_t i, size_t j) { shared_ptr a = chunks->chunks[i]; shared_ptr b = chunks->chunks[j]; return ((a->x + 0.5f - px)*(a->x + 0.5f - px) + (a->z + 0.5f - pz)*(a->z + 0.5f - pz) > (b->x + 0.5f - px)*(b->x + 0.5f - px) + (b->z + 0.5f - pz)*(b->z + 0.5f - pz)); }); bool culling = engine->getSettings().graphics.frustumCulling; if (culling) { frustumCulling->update(camera->getProjView()); } chunks->visible = 0; for (size_t i = 0; i < indices.size(); i++){ chunks->visible += drawChunk(indices[i], camera, shader, culling); } } void WorldRenderer::draw(const GfxContext& pctx, Camera* camera){ EngineSettings& settings = engine->getSettings(); skybox->refresh(level->world->daytime, fmax(1.0f, 10.0f/(settings.chunks.loadDistance-2)), 4); const Content* content = level->content; const ContentIndices* contentIds = content->indices; Assets* assets = engine->getAssets(); Atlas* atlas = assets->getAtlas("blocks"); Shader* shader = assets->getShader("main"); Shader* linesShader = assets->getShader("lines"); const Viewport& viewport = pctx.getViewport(); int displayWidth = viewport.getWidth(); int displayHeight = viewport.getHeight(); Window::clearDepth(); Window::viewport(0, 0, displayWidth, displayHeight); // Drawing background sky plane Shader* backShader = assets->getShader("background"); backShader->use(); backShader->uniformMatrix("u_view", camera->getView(false)); backShader->uniform1f("u_zoom", camera->zoom); backShader->uniform1f("u_ar", (float)Window::width/(float)Window::height); skybox->draw(backShader); { GfxContext ctx = pctx.sub(); ctx.depthTest(true); ctx.cullFace(true); float fogFactor = 15.0f / ((float)settings.chunks.loadDistance-2); // Setting up main shader shader->use(); shader->uniformMatrix("u_proj", camera->getProjection()); shader->uniformMatrix("u_view", camera->getView()); shader->uniform1f("u_gamma", 1.0f); shader->uniform1f("u_fogFactor", fogFactor); shader->uniform1f("u_fogCurve", settings.graphics.fogCurve); shader->uniform3f("u_cameraPos", camera->position); shader->uniform1i("u_cubemap", 1); { blockid_t id = level->player->choosenBlock; Block* block = contentIds->getBlockDef(id); assert(block != nullptr); float multiplier = 0.5f; shader->uniform3f("u_torchlightColor", block->emission[0] / 15.0f * multiplier, block->emission[1] / 15.0f * multiplier, block->emission[2] / 15.0f * multiplier); shader->uniform1f("u_torchlightDistance", 6.0f); } // Binding main shader textures skybox->bind(); atlas->getTexture()->bind(); drawChunks(level->chunks, camera, shader); // Selected block if (PlayerController::selectedBlockId != -1){ blockid_t id = PlayerController::selectedBlockId; Block* block = contentIds->getBlockDef(id); assert(block != nullptr); const vec3 pos = PlayerController::selectedBlockPosition; const vec3 point = PlayerController::selectedPointPosition; const vec3 norm = PlayerController::selectedBlockNormal; AABB hitbox = block->hitbox; if (block->rotatable) { auto states = PlayerController::selectedBlockStates; block->rotations.variants[states].transform(hitbox); } const vec3 center = pos + hitbox.center(); const vec3 size = hitbox.size(); linesShader->use(); linesShader->uniformMatrix("u_projview", camera->getProjView()); lineBatch->lineWidth(2.0f); lineBatch->box(center, size + vec3(0.02), vec4(0.f, 0.f, 0.f, 0.5f)); if (level->player->debug) lineBatch->line(point, point+norm*0.5f, vec4(1.0f, 0.0f, 1.0f, 1.0f)); lineBatch->render(); } skybox->unbind(); } if (level->player->debug) { GfxContext ctx = pctx.sub(); ctx.depthTest(true); linesShader->use(); if (settings.debug.showChunkBorders){ linesShader->uniformMatrix("u_projview", camera->getProjView()); vec3 coord = level->player->camera->position; if (coord.x < 0) coord.x--; if (coord.z < 0) coord.z--; int cx = floordiv((int)coord.x, CHUNK_W); int cz = floordiv((int)coord.z, CHUNK_D); drawBorders(cx * CHUNK_W, 0, cz * CHUNK_D, (cx + 1) * CHUNK_W, CHUNK_H, (cz + 1) * CHUNK_D); } float length = 40.f; vec3 tsl = vec3(displayWidth/2, displayHeight/2, 0.f); glm::mat4 model(glm::translate(glm::mat4(1.f), tsl)); linesShader->uniformMatrix("u_projview", glm::ortho( 0.f, (float)displayWidth, 0.f, (float)displayHeight, -length, length) * model * glm::inverse(camera->rotation)); ctx.depthTest(false); lineBatch->lineWidth(4.0f); lineBatch->line(0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f); lineBatch->line(0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 0.f, 1.f); lineBatch->line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 1.f); lineBatch->render(); ctx.depthTest(true); lineBatch->lineWidth(2.0f); lineBatch->line(0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f); lineBatch->line(0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 0.f, 1.f); lineBatch->line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 1.f); lineBatch->render(); } } void WorldRenderer::drawBorders(int sx, int sy, int sz, int ex, int ey, int ez) { int ww = ex-sx; int dd = ez-sz; /*corner*/ { lineBatch->line(sx, sy, sz, sx, ey, sz, 0.8f, 0, 0.8f, 1); lineBatch->line(sx, sy, ez, sx, ey, ez, 0.8f, 0, 0.8f, 1); lineBatch->line(ex, sy, sz, ex, ey, sz, 0.8f, 0, 0.8f, 1); lineBatch->line(ex, sy, ez, ex, ey, ez, 0.8f, 0, 0.8f, 1); } for (int i = 2; i < ww; i+=2) { lineBatch->line(sx + i, sy, sz, sx + i, ey, sz, 0, 0, 0.8f, 1); lineBatch->line(sx + i, sy, ez, sx + i, ey, ez, 0, 0, 0.8f, 1); } for (int i = 2; i < dd; i+=2) { lineBatch->line(sx, sy, sz + i, sx, ey, sz + i, 0.8f, 0, 0, 1); lineBatch->line(ex, sy, sz + i, ex, ey, sz + i, 0.8f, 0, 0, 1); } for (int i = sy; i < ey; i+=2){ lineBatch->line(sx, i, sz, sx, i, ez, 0, 0.8f, 0, 1); lineBatch->line(sx, i, ez, ex, i, ez, 0, 0.8f, 0, 1); lineBatch->line(ex, i, ez, ex, i, sz, 0, 0.8f, 0, 1); lineBatch->line(ex, i, sz, sx, i, sz, 0, 0.8f, 0, 1); } lineBatch->render(); }