#include "client/rendering/Engine.h" #include "client/Game.h" #include "client/rendering/Framebuffers.h" #include "client/rendering/Mesh.h" #include "client/rendering/NoiseTexture.h" #include "client/rendering/Shaders.h" #include "math/Frustum.h" #include "rendering/Window.h" #include "utils/Logger.h" #include "wrapper/GL.h" static Window window; static Shaders shaders; static Framebuffers framebuffers; static Size lastSize{0, 0}; static Frustum frustum{60.0f, 0.1f, 1000.0f, window.getSize()}; static MatrixStack<16> model; Renderer Engine::renderer; ShaderMatrix Engine::matrix{nullptr, model, nullptr}; float Engine::lag = 0.0f; static NoiseTexture ssaoNoise; static Mesh rectangle; static Matrix worldProj; static Matrix worldView; static Matrix worldShadowProj; static Matrix worldShadowView; static Matrix worldShadowProjView; static bool useSsao = true; static bool useShadows = false; static float shadowRadius = 0.01f; static float shadowBias = 0.0002f; static bool running = true; static bool initRectangle() { if(rectangle.init()) { return true; } TypedBuffer buffer(2); buffer.add( Triangle(Vertex(Vector3(-1.0f, -1.0f, 0.0f), Vector2(0, 0.0f)), Vertex(Vector3(1.0f, 1.0f, 0.0f), Vector2(1.0f, 1.0f)), Vertex(Vector3(-1.0f, 1.0f, 0.0f), Vector2(0.0f, 1.0f)))); buffer.add( Triangle(Vertex(Vector3(-1.0f, -1.0f, 0.0f), Vector2(0, 0.0f)), Vertex(Vector3(1.0f, -1.0f, 0.0f), Vector2(1.0f, 0.0f)), Vertex(Vector3(1.0f, 1.0f, 0.0f), Vector2(1.0f, 1.0f)))); rectangle.build(buffer); return false; } bool Engine::init() { WindowOptions options(4, 0, {1024, 620}, false, "test"); Error error = window.open(options); if(error.has()) { LOG_ERROR(error.message); return true; } lastSize = window.getSize(); error = shaders.init(); if(error.has()) { LOG_ERROR(error.message); return true; } error = framebuffers.init(window.getSize()); if(error.has()) { LOG_ERROR(error.message); return true; } if(renderer.init() || ssaoNoise.init() || initRectangle()) { return true; } return false; } static void renderShadow() { framebuffers.shadow.bindAndClear(); GL::enableDepthTesting(); shaders.shadow.use(); worldShadowProjView = worldShadowProj; worldShadowProjView *= worldShadowView; shaders.shadow.setMatrix("projView", worldShadowProjView.getValues()); model.clear(); shaders.shadow.setMatrix("model", model.peek().getValues()); Engine::matrix = {&shaders.shadow, model, &worldView}; Game::renderWorld(); } static void renderWorld() { framebuffers.world.bindAndClear(); GL::enableDepthTesting(); shaders.world.use(); Matrix rWorldShadowProjView; rWorldShadowProjView.scale(0.5f).translate(Vector3(0.5f, 0.5f, 0.5f)); rWorldShadowProjView *= worldShadowProjView; shaders.world.setMatrix("projViewShadow", rWorldShadowProjView.getValues()); shaders.world.setMatrix("proj", worldProj.getValues()); worldView = Matrix(); shaders.world.setMatrix("view", worldView.getValues()); model.clear(); shaders.world.setMatrix("model", model.peek().getValues()); framebuffers.shadow.bindTextureTo(0, 1); shaders.world.setInt("shadows", useShadows); shaders.world.setFloat("radius", shadowRadius); shaders.world.setFloat("zbias", shadowBias); Engine::matrix = {&shaders.world, model, &worldView}; Game::renderWorld(); } static void renderSSAO() { shaders.ssao.use(); Matrix rProj; rProj.scale(0.5f).translate(Vector3(0.5f, 0.5f, 0.5f)); rProj *= worldProj; shaders.ssao.setMatrix("proj", rProj.getValues()); const Size& size = window.getSize(); shaders.ssao.setInt("width", size.width); shaders.ssao.setInt("height", size.height); framebuffers.world.bindTextureTo(0, 0); framebuffers.world.bindTextureTo(4, 1); ssaoNoise.bindTo(2); framebuffers.ssao.bindAndClear(); rectangle.draw(); shaders.ssaoBlur.use(); framebuffers.ssao.bindTextureTo(0, 0); framebuffers.ssaoBlur.bindAndClear(); rectangle.draw(); } static void renderPostWorld() { GL::bindMainFramebuffer(); GL::clear(); shaders.postWorld.use(); framebuffers.world.bindTextureTo(2, 0); framebuffers.ssaoBlur.bindTextureTo(0, 1); framebuffers.world.bindTextureTo(3, 2); framebuffers.world.bindTextureTo(1, 3); shaders.postWorld.setInt("ssao", useSsao); shaders.postWorld.setInt("shadows", useShadows); rectangle.draw(); } static void renderOverlay() { GL::disableDepthTesting(); shaders.overlay.use(); const Size& size = window.getSize(); Matrix m; m.scale(Vector3(2.0f / size.width, -2.0f / size.height, 1.0f)) .translate(Vector3(-1.0f, 1.0f, 0.0f)); shaders.overlay.setMatrix("view", m.getValues()); model.clear(); shaders.overlay.setMatrix("model", model.peek().getValues()); GL::enableBlending(); Engine::matrix = {&shaders.overlay, model, &m}; Game::renderOverlay(); GL::disableBlending(); } static void updateWorldProjection() { worldProj = frustum.updateProjection(); if(!useShadows) { return; } worldShadowProj.set(0, Vector4(2.0f / 40.0f, 0.0f, 0.0f, 0.0f)); worldShadowProj.set(1, Vector4(0.0f, 2.0f / 30.0f, 0.0f, 0.0f)); worldShadowProj.set(2, Vector4(0.0f, 0.0f, -2.0f / (1000.0f - 0.1f), 0.0f)); worldShadowProj.set(3, Vector4(0.0f, 0.0f, 0.0f, 1.0f)); } static void updateWorldView() { if(!useShadows) { return; } Vector3 right(0.939693f, 0.0f, -0.34202f); Vector3 back(0.280166f, 0.573576f, 0.769751f); Vector3 up(-0.196175f, 0.819152f, -0.538986f); Vector3 center(16.0f, 24.0f, 24.0f); worldShadowView.set( 0, Vector4(right[0], right[1], right[2], right.dot(-center))); worldShadowView.set(1, Vector4(up[0], up[1], up[2], up.dot(-center))); worldShadowView.set(2, Vector4(back[0], back[1], back[2], back.dot(-center))); worldShadowView.set(3, Vector4(0.0f, 0.0f, 0.0f, 1.0f)); } static void startRender() { const Size& size = window.getSize(); if(size.width != lastSize.width || size.height != lastSize.height) { GL::setViewport(size.width, size.height); framebuffers.resize(size); lastSize = size; } GL::printError("loop error"); updateWorldProjection(); updateWorldView(); if(useShadows) { renderShadow(); } renderWorld(); if(useSsao) { renderSSAO(); } renderPostWorld(); renderOverlay(); } struct Loop final { void render(float lag) { Engine::lag = lag; startRender(); } void tick() { Game::tick(); } bool isRunning() const { return running; } }; void Engine::run() { Loop loop; window.run(loop, 50'000'000); } void Engine::stop() { running = false; } void Engine::setTextInput(TextInput* input) { window.textInput = input; if(input != nullptr) { input->setActive(true); } } bool Engine::isActiveTextInput(TextInput* input) { return window.textInput == input; } Buttons& Engine::getButtons() { return window.buttons; } const Size& Engine::getSize() { return window.getSize(); } const Clock& Engine::getFrameClock() { return window.getFrameClock(); } const Clock& Engine::getTickClock() { return window.getTickClock(); }