1
0
Fork 0
Univerxel/src/client/render/gl/Renderer.cpp

202 lines
6.3 KiB
C++

#include "Renderer.hpp"
#include "UI.hpp"
#include "../../../core/world/materials.hpp"
#include "../../control/Camera.hpp"
#include "../../Window.hpp"
#include "../../../core/utils/logger.hpp"
#include <GL/gl3w.h>
#include <TracyOpenGL.hpp>
using namespace render::gl;
constexpr auto GL_MAJOR = 4;
#if GL_OLD
constexpr auto GL_MINOR = 2;
#else
constexpr auto GL_MINOR = 6;
#endif
#define CONTENT_DIR "content/"
#define TEXTURES_DIR CONTENT_DIR "textures/"
Renderer::Renderer(const render::renderOptions& options):
IndicatorCubeBuffer(Shape::LINE_CUBE), IndicatorSphereBuffer(Shape::LINE_SPHERE)
{
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);
reloadShaders(options.voxel);
SkyPass = std::make_unique<pass::SkyProgram>();
SkyEnable = options.skybox;
IndicatorPass = std::make_unique<pass::ColorProgram>();
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 render::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);
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);
glEnable(GL_FRAMEBUFFER_SRGB);
}
std::function<size_t(render::LodModel *const, glm::mat4, glm::vec4, float)> Renderer::beginWorldPass(bool solid) {
if (!solid)
glEnable(GL_BLEND);
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<LodModel *const>(buf)->draw();
};
}
std::function<size_t(render::Model *const, const std::vector<glm::mat4> &)> Renderer::beginEntityPass() {
EntityPass->useIt();
EntityPass->start(this);
return [&](render::Model *const buf, const std::vector<glm::mat4> &models) {
EntityPass->setup(models);
return dynamic_cast<Model *const>(buf)->drawInstanced(models.size());
};
}
std::function<size_t(glm::mat4, world::action::Shape, glm::vec4)> 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() {
glDisable(GL_FRAMEBUFFER_SRGB);
glDisable(GL_BLEND);
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) {
WorldPass = std::make_unique<pass::WorldProgram>(options);
EntityPass = std::make_unique<pass::EntityProgram>(options);
}
void Renderer::reloadTextures(const std::string& texturePath, float mipMapLOD, int anisotropy) {
unloadTextures();
loadTextures(texturePath, mipMapLOD, anisotropy);
}
void Renderer::unloadTextures() {
HOSAtlas.reset();
NormalAtlas.reset();
TextureAtlas.reset();
}
void Renderer::loadTextures(const std::string& texturePath, float mipMapLOD, int anisotropy) {
std::vector<std::string> 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<int>(vSync));
}