#include "client/rendering/Engine.h" #include "client/Game.h" #include "client/rendering/Mesh.h" #include "math/Frustum.h" #include "rendering/Framebuffer.h" #include "rendering/Window.h" #include "utils/Logger.h" #include "utils/Random.h" #include "wrapper/GL.h" static Shader worldShader; static Shader ssaoShader; static Shader ssaoBlurShader; static Shader shadowShader; static Shader postWorldShader; static Shader overlayShader; static Framebuffer<5> worldBuffer; static Framebuffer<1> ssaoBuffer; static Framebuffer<1> ssaoBlurBuffer; static Framebuffer<1> shadowBuffer; 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 Texture 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 Error compileShader(Shader& s, const char* name) { constexpr const char* prefix = "resources/shader/"; return s.compile(StringBuffer<50>(prefix).append(name).append(".vs"), StringBuffer<50>(prefix).append(name).append(".fs")); } static Error initShaders() { Error error = compileShader(worldShader, "world"); if(error.has()) { return error; } error = compileShader(ssaoShader, "ssao"); if(error.has()) { return error; } error = compileShader(ssaoBlurShader, "ssaoBlur"); if(error.has()) { return error; } error = compileShader(shadowShader, "worldShadow"); if(error.has()) { return error; } error = compileShader(postWorldShader, "worldPost"); if(error.has()) { return error; } return compileShader(overlayShader, "overlay"); } static void resizeFramebuffers(const Size& size) { worldBuffer.resize(size); ssaoBuffer.resize(size); ssaoBlurBuffer.resize(size); shadowBuffer.resize(size); } static Error initFramebuffers(const Size& size) { Error error = worldBuffer.init( size, TextureFormat::float32(3), TextureFormat::float32(3), TextureFormat::color8(4), TextureFormat::float32(1), TextureFormat::depth32(true)); if(error.has()) { return error; } error = ssaoBuffer.init(size, TextureFormat::float32(1)); if(error.has()) { return error; } error = ssaoBlurBuffer.init(size, TextureFormat::float32(1)); if(error.has()) { return error; } return shadowBuffer.init(size, TextureFormat::depth32()); } static bool initRectangle() { if(rectangle.init()) { return true; } rectangle.build(TypedBuffer(2) .add({{{-1.0f, -1.0f, +0.0f}, {0.0f, 0.0f}}, {{+1.0f, +1.0f, +0.0f}, {1.0f, 1.0f}}, {{-1.0f, +1.0f, +0.0f}, {0.0f, 1.0f}}}) .add({{{-1.0f, -1.0f, +0.0f}, {0.0f, 0.0f}}, {{+1.0f, -1.0f, +0.0f}, {1.0f, 0.0f}}, {{+1.0f, +1.0f, +0.0f}, {1.0f, 1.0f}}})); return false; } static void initNoise() { ssaoNoise.init(TextureFormat::float32(3), 0); Random r(1); Array data; for(int i = 0; i < 48; i++) { data[i] = r.nextFloat() * 2.0f - 1.0f; } ssaoNoise.setData(4, 4, data.begin()); } 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; } Window::show(); error = initShaders(); if(error.has()) { LOG_ERROR(error.message); return true; } error = initFramebuffers(Window::getSize()); if(error.has()) { LOG_ERROR(error.message); return true; } initNoise(); if(renderer.init() || initRectangle()) { return true; } return false; } static void renderShadow() { shadowBuffer.bindAndClear(); GL::enableDepthTesting(); shadowShader.use(); worldShadowProjView = worldShadowProj; worldShadowProjView *= worldShadowView; shadowShader.setMatrix("projView", worldShadowProjView.getValues()); model.clear(); shadowShader.setMatrix("model", model.peek().getValues()); Engine::matrix = {&shadowShader, model, &worldView}; Game::renderWorld(); } static void renderWorld() { worldBuffer.bindAndClear(); GL::enableDepthTesting(); worldShader.use(); Matrix rWorldShadowProjView; rWorldShadowProjView.scale(0.5f).translate(Vector3(0.5f, 0.5f, 0.5f)); rWorldShadowProjView *= worldShadowProjView; worldShader.setMatrix("projViewShadow", rWorldShadowProjView.getValues()); worldShader.setMatrix("proj", worldProj.getValues()); worldView = Matrix(); worldShader.setMatrix("view", worldView.getValues()); model.clear(); worldShader.setMatrix("model", model.peek().getValues()); shadowBuffer.bindTextureTo(0, 1); worldShader.setInt("shadows", useShadows); worldShader.setFloat("radius", shadowRadius); worldShader.setFloat("zbias", shadowBias); Engine::matrix = {&worldShader, model, &worldView}; Game::renderWorld(); } static void renderSSAO() { ssaoShader.use(); Matrix rProj; rProj.scale(0.5f).translate(Vector3(0.5f, 0.5f, 0.5f)); rProj *= worldProj; ssaoShader.setMatrix("proj", rProj.getValues()); ssaoShader.setInt("width", Window::getSize().width); ssaoShader.setInt("height", Window::getSize().height); worldBuffer.bindTextureTo(0, 0); worldBuffer.bindTextureTo(4, 1); ssaoNoise.bindTo(2); ssaoBuffer.bindAndClear(); rectangle.draw(); ssaoBlurShader.use(); ssaoBuffer.bindTextureTo(0, 0); ssaoBlurBuffer.bindAndClear(); rectangle.draw(); } static void renderPostWorld() { GL::bindMainFramebuffer(); GL::clear(); postWorldShader.use(); worldBuffer.bindTextureTo(2, 0); ssaoBlurBuffer.bindTextureTo(0, 1); worldBuffer.bindTextureTo(3, 2); worldBuffer.bindTextureTo(1, 3); postWorldShader.setInt("ssao", useSsao); postWorldShader.setInt("shadows", useShadows); rectangle.draw(); } static void renderOverlay() { GL::disableDepthTesting(); overlayShader.use(); Matrix m; m.scale(Vector3(2.0f / Window::getSize().width, -2.0f / Window::getSize().height, 1.0f)) .translate(Vector3(-1.0f, 1.0f, 0.0f)); overlayShader.setMatrix("view", m.getValues()); model.clear(); overlayShader.setMatrix("model", model.peek().getValues()); GL::enableBlending(); Engine::matrix = {&overlayShader, 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() { if(Window::hasSizeChanged()) { GL::setViewport(Window::getSize().width, Window::getSize().height); resizeFramebuffers(Window::getSize()); } GL::printError("loop error"); updateWorldProjection(); updateWorldView(); if(useShadows) { renderShadow(); } renderWorld(); if(useSsao) { renderSSAO(); } renderPostWorld(); renderOverlay(); } static void render(float lag) { Engine::lag = lag; startRender(); } static void tick() { Game::tick(); } static bool isRunning() { return running && !Window::shouldClose(); } void Engine::run() { Window::run(50'000'000); } void Engine::stop() { running = false; }