#include "Renderer.hpp" #include "UI.hpp" #include "../../../core/world/materials.hpp" #include "../../control/Camera.hpp" #include "../../Window.hpp" #include "../../../core/utils/logger.hpp" #include #include using namespace render::gl; constexpr auto GL_MAJOR = 4; constexpr auto GL_MINOR = 6; #define CONTENT_DIR "content/" #define TEXTURES_DIR CONTENT_DIR "textures/" Renderer::Renderer(const renderOptions& options): IndicatorCubeBuffer(Shape::LINE_CUBE), IndicatorSphereBuffer(Shape::LINE_SPHERE) { glGenVertexArrays(1, &VertexArrayID); glBindVertexArray(VertexArrayID); reloadShaders(options.voxel); SkyPass = std::make_unique(); SkyEnable = options.skybox; IndicatorPass = std::make_unique(); FogColor = glm::vec3(options.clear_color.x, options.clear_color.y, options.clear_color.z); loadTextures(options.textures, options.getMipmapLodBias(), options.getAnisotropy()); setFillMode(options.wireframe); } Renderer::~Renderer() { render::UI::Unload(); unloadTextures(); glDeleteVertexArrays(1, &VertexArrayID); } void framebuffer_size_callback(GLFWwindow *, int width, int height) { glViewport(0, 0, width, height); } bool Renderer::Load(Window& window, const renderOptions& opt, const windowOptions& windOpt) { Window::CreateInfo windowInfo; windowInfo.pfnResize = framebuffer_size_callback; windowInfo.client = {Window::CreateInfo::Client::Type::GL, GL_MAJOR, GL_MINOR}; windowInfo.samples = windOpt.getSamples(); windowInfo.fps = windOpt.targetFPS; windowInfo.fullscreen = windOpt.fullscreen; if (!window.create(windowInfo)) return false; if (gl3wInit() != GL3W_OK) { LOG_E("Failed to initialize OpenGL"); return false; } if (!gl3wIsSupported(GL_MAJOR, GL_MINOR)) { LOG_E("OpenGL " << GL_MAJOR << "." << GL_MINOR << " not supported"); return false; } LOG_D("OpenGL " << glGetString(GL_VERSION) << ", GLSL " << glGetString(GL_SHADING_LANGUAGE_VERSION)); // Enable depth test glEnable(GL_DEPTH_TEST); // Accept fragment if it closer to the camera than the former one glDepthFunc(GL_LEQUAL); // Cull triangles which normal is not towards the camera glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); GLint smp; glGetIntegerv(GL_SAMPLES, &smp); if (smp > 0) { glEnable(GL_MULTISAMPLE); } glClearColor(opt.clear_color.x, opt.clear_color.y, opt.clear_color.z, opt.clear_color.w); TracyGpuContext; sInstance = new Renderer(opt); sInstance->setVSync(windOpt.targetFPS < Window::MIN_FPS); Model::MakeDefault(); LodModel::MakeDefault(); return true; } void Renderer::loadUI(Window& w) { UI::Load(w); } void Renderer::beginFrame() { TracyGpuZone("Render"); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } std::function Renderer::beginWorldPass(bool solid) { if (solid) { WorldPass->useIt(); WorldPass->start(this); } return [&](render::LodModel *const buf, glm::mat4 model, glm::vec4 sph, float curv) { WorldPass->setup(model, sph, curv); return dynamic_cast(buf)->draw(); }; } std::function &)> Renderer::beginEntityPass() { EntityPass->useIt(); EntityPass->start(this); return [&](render::Model *const buf, const std::vector &models) { EntityPass->setup(models); return dynamic_cast(buf)->drawInstanced(models.size()); }; } std::function Renderer::beginIndicatorPass() { IndicatorPass->useIt(); return [&](glm::mat4 model, world::action::Shape shape, glm::vec4 color) { IndicatorPass->setup(this, model, color); return shape == world::action::Shape::Cube ? IndicatorCubeBuffer.draw(GL_LINES) : IndicatorSphereBuffer.draw(GL_LINES); }; } void Renderer::postProcess() { if(SkyEnable) { SkyPass->draw(this); } } void Renderer::endFrame() { } void Renderer::swapBuffer(Window& w) { TracyGpuZone("Swap"); glfwSwapBuffers(w.getPtr()); TracyGpuCollect; } void Renderer::reloadShaders(const pass::VoxelProgram::options& options) { if (options.transparency) { glEnable(GL_BLEND); } else { glDisable(GL_BLEND); } WorldPass = std::make_unique(options); EntityPass = std::make_unique(options); } void Renderer::reloadTextures(const std::string& texturePath, float mipMapLOD, float anisotropy) { unloadTextures(); loadTextures(texturePath, mipMapLOD, anisotropy); } void Renderer::unloadTextures() { HOSAtlas.reset(); NormalAtlas.reset(); TextureAtlas.reset(); } void Renderer::loadTextures(const std::string& texturePath, float mipMapLOD, float anisotropy) { std::vector terrainTextures; auto makePaths = [&](const std::string& suffix) { terrainTextures.clear(); for(const auto& texture: world::materials::textures) { terrainTextures.emplace_back(TEXTURES_DIR + texturePath + "/terrain/" + texture + suffix + ".dds"); } return terrainTextures; }; auto sampling = Texture::sampling{true, true, Texture::Wrap::REPEAT, anisotropy, true, mipMapLOD}; TextureAtlas = TextureArray::LoadFromFiles(makePaths(""), sampling); NormalAtlas = TextureArray::LoadFromFiles(makePaths(".nrm"), sampling); HOSAtlas = TextureArray::LoadFromFiles(makePaths(".hos"), sampling); Skybox = TextureCube::LoadFromFiles(TEXTURES_DIR + texturePath + "/sky/Space_tray.cube", {}); } void Renderer::lookFrom(const Camera& camera) { ProjectionMatrix = camera.getProjectionMatrix(); ViewMatrix = camera.getViewMatrix(); FogDepth = camera.getDepth(); } void Renderer::setClearColor(glm::vec4 c) { FogColor = c; glClearColor(c.r, c.g, c.b, c.a); } void Renderer::setFillMode(bool wireframe) { glPolygonMode(GL_FRONT_AND_BACK, wireframe ? GL_LINE : GL_FILL); } void Renderer::setVSync(bool vSync) { glfwSwapInterval(static_cast(vSync)); }