diff --git a/src/data/dv.hpp b/src/data/dv.hpp index 3c3f4a71e..8acd22136 100644 --- a/src/data/dv.hpp +++ b/src/data/dv.hpp @@ -9,6 +9,10 @@ #include #include +#ifdef VC_ENABLE_REFLECTION +#include "util/EnumMetadata.hpp" +#endif + namespace util { template class Buffer; } @@ -70,6 +74,14 @@ namespace dv { return ptr != nullptr; } + inline value* operator->() noexcept { + return ptr; + } + + inline const value* operator->() const noexcept { + return ptr; + } + inline value& operator*() noexcept { return *ptr; } @@ -78,6 +90,11 @@ namespace dv { return *ptr; } +#ifdef VC_ENABLE_REFLECTION + template + bool get(T& dst, const util::EnumMetadata& mt) const; +#endif + bool get(std::string& dst) const; bool get(bool& dst) const; bool get(char& dst) const; @@ -552,6 +569,17 @@ namespace dv { } return false; } + +#ifdef VC_ENABLE_REFLECTION + template + inline bool optionalvalue::get(T& dst, const util::EnumMetadata& mt) const { + if (ptr) { + return mt.getItem(ptr->asString(), dst); + } + return false; + } +#endif + inline bool optionalvalue::get(std::string& dst) const { if (ptr) { dst = ptr->asString(); diff --git a/src/graphics/core/Cubemap.cpp b/src/graphics/core/Cubemap.cpp index 631a8d7e4..e55fb3127 100644 --- a/src/graphics/core/Cubemap.cpp +++ b/src/graphics/core/Cubemap.cpp @@ -4,7 +4,7 @@ #include Cubemap::Cubemap(uint width, uint height, ImageFormat imageFormat) - : Texture(0, width, height) + : Texture(0U, width, height, imageFormat) { glGenTextures(1, &id); glBindTexture(GL_TEXTURE_CUBE_MAP, id); @@ -14,7 +14,6 @@ Cubemap::Cubemap(uint width, uint height, ImageFormat imageFormat) glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - uint format = gl::to_glenum(imageFormat); for (uint face = 0; face < 6; face++) { glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, @@ -37,3 +36,23 @@ void Cubemap::bind() const { void Cubemap::unbind() const { glBindTexture(GL_TEXTURE_CUBE_MAP, 0); } + +void Cubemap::reload(const ubyte* data, uint width, uint height) { + this->width = width; + this->height = height; + glBindTexture(GL_TEXTURE_CUBE_MAP, id); + for (uint face = 0; face < 6; face++) { + glTexImage2D( + GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, + 0, + format, + width, + height, + 0, + format, + GL_UNSIGNED_BYTE, + data + ); + } + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +} diff --git a/src/graphics/core/Cubemap.hpp b/src/graphics/core/Cubemap.hpp index 4ca1b9884..e266c4381 100644 --- a/src/graphics/core/Cubemap.hpp +++ b/src/graphics/core/Cubemap.hpp @@ -9,4 +9,5 @@ public: virtual void bind() const override; virtual void unbind() const override; + virtual void reload(const ubyte* data, uint w, uint h) override; }; diff --git a/src/graphics/core/DrawContext.cpp b/src/graphics/core/DrawContext.cpp index fc37fd46d..86e2131b1 100644 --- a/src/graphics/core/DrawContext.cpp +++ b/src/graphics/core/DrawContext.cpp @@ -74,6 +74,18 @@ DrawContext::~DrawContext() { if (lineWidth != parent->lineWidth) { glLineWidth(parent->lineWidth); } + + for (int i = 0; i < textures.size(); i++) { + auto texture = parent->textures[i]; + if (textures[i] != texture) { + glActiveTexture(GL_TEXTURE0 + i); + if (texture == nullptr) { + glBindTexture(GL_TEXTURE_2D, 0); + } else { + texture->bind(); + } + } + } } const glm::uvec2& DrawContext::getViewport() const { @@ -154,3 +166,13 @@ void DrawContext::setLineWidth(float width) { lineWidth = width; glLineWidth(width); } + +void DrawContext::useTexture(int target, const Bindable* texture) { + glActiveTexture(GL_TEXTURE0 + target); + if (texture == nullptr) { + glBindTexture(GL_TEXTURE_2D, 0); + } else { + texture->bind(); + } + textures.at(target) = texture; +} diff --git a/src/graphics/core/DrawContext.hpp b/src/graphics/core/DrawContext.hpp index b14bf71bc..7e568e337 100644 --- a/src/graphics/core/DrawContext.hpp +++ b/src/graphics/core/DrawContext.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -23,6 +24,7 @@ class DrawContext { BlendMode blendMode = BlendMode::normal; int scissorsCount = 0; float lineWidth = 1.0f; + std::array textures {}; public: DrawContext( const DrawContext* parent, @@ -44,4 +46,5 @@ public: void setBlendMode(BlendMode mode); void setScissors(const glm::vec4& area); void setLineWidth(float width); + void useTexture(int target, const Bindable* texture); }; diff --git a/src/graphics/core/Framebuffer.cpp b/src/graphics/core/Framebuffer.cpp index 5d6be967d..1dc677096 100644 --- a/src/graphics/core/Framebuffer.cpp +++ b/src/graphics/core/Framebuffer.cpp @@ -1,14 +1,14 @@ #include "Framebuffer.hpp" #include + #include "Texture.hpp" #include "debug/Logger.hpp" static debug::Logger logger("gl-framebuffer"); Framebuffer::Framebuffer(uint fbo, uint depth, std::unique_ptr texture) - : fbo(fbo), depth(depth), texture(std::move(texture)) -{ + : fbo(fbo), depth(depth), texture(std::move(texture)) { if (this->texture) { width = this->texture->getWidth(); height = this->texture->getHeight(); @@ -18,35 +18,55 @@ Framebuffer::Framebuffer(uint fbo, uint depth, std::unique_ptr texture) } } -static std::unique_ptr create_texture(int width, int height, int format) { - GLuint tex; - glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, nullptr); +static std::unique_ptr create_texture( + int width, int height, bool alpha +) { + GLenum glformat = alpha ? GL_RGBA : GL_RGB; + + GLuint textureid; + glGenTextures(1, &textureid); + glBindTexture(GL_TEXTURE_2D, textureid); + glTexImage2D( + GL_TEXTURE_2D, + 0, + glformat, + width, + height, + 0, + glformat, + GL_UNSIGNED_BYTE, + nullptr + ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); - return std::make_unique(tex, width, height); + glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid, 0 + ); + return std::make_unique( + textureid, + width, + height, + alpha ? ImageFormat::RGBA8888 : ImageFormat::RGB888 + ); } -Framebuffer::Framebuffer(uint width, uint height, bool alpha) - : width(width), height(height) -{ +Framebuffer::Framebuffer(uint width, uint height, bool alpha) + : width(width), height(height) { glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); - format = alpha ? GL_RGBA : GL_RGB; - // Setup color attachment (texture) - texture = create_texture(width, height, format); + texture = create_texture(width, height, alpha); // Setup depth attachment glGenRenderbuffers(1, &depth); glBindRenderbuffer(GL_RENDERBUFFER, depth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth); + glFramebufferRenderbuffer( + GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth + ); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { logger.error() << "framebuffer is not complete!"; @@ -60,11 +80,15 @@ Framebuffer::~Framebuffer() { glDeleteRenderbuffers(1, &depth); } -void Framebuffer::bind() { +void Framebuffer::setTexture(std::unique_ptr texture) { + this->texture = std::move(texture); +} + +void Framebuffer::bind() const { glBindFramebuffer(GL_FRAMEBUFFER, fbo); } -void Framebuffer::unbind() { +void Framebuffer::unbind() const { glBindFramebuffer(GL_FRAMEBUFFER, 0); } @@ -81,7 +105,7 @@ void Framebuffer::resize(uint width, uint height) { glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); glBindRenderbuffer(GL_RENDERBUFFER, 0); - texture = create_texture(width, height, format); + texture->resize(width, height); glBindFramebuffer(GL_FRAMEBUFFER, 0); } @@ -102,5 +126,5 @@ uint Framebuffer::getHeight() const { } uint Framebuffer::getFBO() const { - return fbo; + return fbo; } diff --git a/src/graphics/core/Framebuffer.hpp b/src/graphics/core/Framebuffer.hpp index 4d21449c5..5f9dc3491 100644 --- a/src/graphics/core/Framebuffer.hpp +++ b/src/graphics/core/Framebuffer.hpp @@ -12,18 +12,19 @@ class Framebuffer : public Bindable { uint depth; uint width; uint height; - uint format; std::shared_ptr texture; public: Framebuffer(uint fbo, uint depth, std::unique_ptr texture); Framebuffer(uint width, uint height, bool alpha=false); ~Framebuffer(); + void setTexture(std::unique_ptr texture); + /// @brief Use framebuffer - void bind() override; + void bind() const override; /// @brief Stop using framebuffer - void unbind() override; + void unbind() const override; /// @brief Update framebuffer texture size /// @param width new width diff --git a/src/graphics/core/GBuffer.cpp b/src/graphics/core/GBuffer.cpp index aba3103fc..30b0c817a 100644 --- a/src/graphics/core/GBuffer.cpp +++ b/src/graphics/core/GBuffer.cpp @@ -189,7 +189,7 @@ GBuffer::~GBuffer() { glDeleteFramebuffers(1, &ssaoFbo); } -void GBuffer::bind() { +void GBuffer::bind() const { glBindFramebuffer(GL_FRAMEBUFFER, fbo); glClear(GL_COLOR_BUFFER_BIT); } @@ -198,7 +198,7 @@ void GBuffer::bindSSAO() const { glBindFramebuffer(GL_FRAMEBUFFER, ssaoFbo); } -void GBuffer::unbind() { +void GBuffer::unbind() const { glBindFramebuffer(GL_FRAMEBUFFER, 0); } diff --git a/src/graphics/core/GBuffer.hpp b/src/graphics/core/GBuffer.hpp index ae433c873..a549b720b 100644 --- a/src/graphics/core/GBuffer.hpp +++ b/src/graphics/core/GBuffer.hpp @@ -9,9 +9,9 @@ public: GBuffer(uint width, uint height); ~GBuffer() override; - void bind() override; + void bind() const override; void bindSSAO() const; - void unbind() override; + void unbind() const override; void bindBuffers() const; void bindSSAOBuffer() const; diff --git a/src/graphics/core/Texture.cpp b/src/graphics/core/Texture.cpp index d60bd2779..5f326d20c 100644 --- a/src/graphics/core/Texture.cpp +++ b/src/graphics/core/Texture.cpp @@ -7,8 +7,8 @@ uint Texture::MAX_RESOLUTION = 1024; // Window.initialize overrides it -Texture::Texture(uint id, uint width, uint height) - : id(id), width(width), height(height) { +Texture::Texture(uint id, uint width, uint height, ImageFormat imageFormat) + : id(id), width(width), height(height), format(gl::to_glenum(imageFormat)) { } Texture::Texture(const ubyte* data, uint width, uint height, ImageFormat imageFormat) @@ -17,7 +17,7 @@ Texture::Texture(const ubyte* data, uint width, uint height, ImageFormat imageFo glBindTexture(GL_TEXTURE_2D, id); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - GLenum format = gl::to_glenum(imageFormat); + format = gl::to_glenum(imageFormat); glTexImage2D( GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, static_cast(data) @@ -41,23 +41,27 @@ void Texture::unbind() const { glBindTexture(GL_TEXTURE_2D, 0); } +void Texture::resize(uint width, uint height) { + reload(nullptr, width, height); +} + void Texture::reload(const ImageData& image) { - width = image.getWidth(); - height = image.getHeight(); reload(image.getData(), width, height); } void Texture::reload(const ubyte* data, uint width, uint height) { + this->width = width; + this->height = height; glBindTexture(GL_TEXTURE_2D, id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, static_cast(data)); + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, + format, GL_UNSIGNED_BYTE, static_cast(data)); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); } void Texture::reloadPartial(const ImageData& image, uint x, uint y, uint w, uint h) { glBindTexture(GL_TEXTURE_2D, id); - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image.getData()); + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, format, GL_UNSIGNED_BYTE, image.getData()); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); } diff --git a/src/graphics/core/Texture.hpp b/src/graphics/core/Texture.hpp index 3c8f1b4cb..30959ed60 100644 --- a/src/graphics/core/Texture.hpp +++ b/src/graphics/core/Texture.hpp @@ -1,29 +1,32 @@ #pragma once -#include "typedefs.hpp" -#include "maths/UVRegion.hpp" -#include "ImageData.hpp" - #include -class Texture { +#include "ImageData.hpp" +#include "commons.hpp" +#include "maths/UVRegion.hpp" +#include "typedefs.hpp" + +class Texture : public Bindable { protected: uint id; uint width; uint height; + uint format; public: - Texture(uint id, uint width, uint height); + Texture(uint id, uint width, uint height, ImageFormat imageFormat); Texture(const ubyte* data, uint width, uint height, ImageFormat format); virtual ~Texture(); - virtual void bind() const; - virtual void unbind() const; - void reload(const ubyte* data, uint w, uint h); + virtual void bind() const override; + virtual void unbind() const override; + virtual void reload(const ubyte* data, uint w, uint h); void reloadPartial(const ImageData& image, uint x, uint y, uint w, uint h); void setNearestFilter(); void reload(const ImageData& image); + virtual void resize(uint w, uint h); void setMipMapping(bool flag, bool pixelated); diff --git a/src/graphics/core/commons.hpp b/src/graphics/core/commons.hpp index 8785cec10..0ca5a6470 100644 --- a/src/graphics/core/commons.hpp +++ b/src/graphics/core/commons.hpp @@ -59,6 +59,8 @@ namespace advanced_pipeline { inline constexpr int TARGET_SSAO = 5; inline constexpr int TARGET_SHADOWS0 = 6; inline constexpr int TARGET_SHADOWS1 = 7; + + inline constexpr int TARGETS_COUNT = 8; } VC_ENUM_METADATA(CursorShape) @@ -85,6 +87,6 @@ class Bindable { public: virtual ~Bindable() = default; - virtual void bind() = 0; - virtual void unbind() = 0; + virtual void bind() const = 0; + virtual void unbind() const = 0; }; diff --git a/src/graphics/render/HandsRenderer.cpp b/src/graphics/render/HandsRenderer.cpp index 8e2f757b2..17c38d76d 100644 --- a/src/graphics/render/HandsRenderer.cpp +++ b/src/graphics/render/HandsRenderer.cpp @@ -34,4 +34,7 @@ void HandsRenderer::render(const Camera& camera) { glm::vec3(), glm::vec3(1.0f) ); + + modelBatch.render(); + modelBatch.setLightsOffset(glm::vec3()); } diff --git a/src/graphics/render/Skybox.cpp b/src/graphics/render/Skybox.cpp index c546c62e3..9cdf45785 100644 --- a/src/graphics/render/Skybox.cpp +++ b/src/graphics/render/Skybox.cpp @@ -1,91 +1,64 @@ #include "Skybox.hpp" -#include "assets/Assets.hpp" -#include "graphics/core/Shader.hpp" -#include "graphics/core/Mesh.hpp" -#include "graphics/core/Batch3D.hpp" -#include "graphics/core/Texture.hpp" -#include "graphics/core/Cubemap.hpp" -#include "graphics/core/Framebuffer.hpp" -#include "graphics/core/DrawContext.hpp" -#include "window/Window.hpp" -#include "window/Camera.hpp" -#include "maths/UVRegion.hpp" + +#include #include -#include -#include #include -#include #include +#include +#include + +#include "assets/Assets.hpp" +#include "graphics/core/Batch3D.hpp" +#include "graphics/core/Cubemap.hpp" +#include "graphics/core/DrawContext.hpp" +#include "graphics/core/Framebuffer.hpp" +#include "graphics/core/Mesh.hpp" +#include "graphics/core/Shader.hpp" +#include "graphics/core/Texture.hpp" +#include "maths/UVRegion.hpp" +#include "window/Camera.hpp" +#include "window/Window.hpp" using namespace advanced_pipeline; const int STARS_COUNT = 3000; const int STARS_SEED = 632; -Skybox::Skybox(uint size, Shader& shader) - : size(size), - shader(shader), - batch3d(std::make_unique(4096)) -{ - auto cubemap = std::make_unique(size, size, ImageFormat::RGB888); +Skybox::Skybox(uint size, const Assets& assets) + : size(size), + assets(assets), + shader(assets.require("skybox_gen")), + batch3d(std::make_unique(4096)) { - uint fboid; - glGenFramebuffers(1, &fboid); - fbo = std::make_unique(fboid, 0, std::move(cubemap)); - - SkyboxVertex vertices[]{ + SkyboxVertex vertices[] { {{-1.0f, -1.0f}}, {{-1.0f, 1.0f}}, {{1.0f, 1.0f}}, {{-1.0f, -1.0f}}, {{1.0f, 1.0f}}, - {{1.0f, -1.0f}} - }; + {{1.0f, -1.0f}}}; mesh = std::make_unique>(vertices, 6); - - sprites.push_back(SkySprite { - "misc/moon", - glm::pi() * 0.5f, - 4.0f, - false, - glm::pi() * 0.25f, - }); - - sprites.push_back(SkySprite { - "misc/moon_flare", - glm::pi() * 0.5f, - 0.5f, - false, - glm::pi() * 0.25f, - }); - - sprites.push_back(SkySprite { - "misc/sun", - glm::pi() * 1.5f, - 4.0f, - true, - glm::pi() * 0.25f, - }); + setMode(mode); } Skybox::~Skybox() = default; -void Skybox::drawBackground( - const Camera& camera, const Assets& assets, int width, int height -) { +void Skybox::drawBackground(const Camera& camera, int width, int height) { auto backShader = assets.get("background"); backShader->use(); backShader->uniformMatrix("u_view", camera.getView(false)); backShader->uniform1f( "u_zoom", camera.zoom * camera.getFov() / glm::half_pi() ); - backShader->uniform1f("u_ar", float(width)/float(height)); + backShader->uniform1f("u_ar", float(width) / float(height)); backShader->uniform1i("u_skybox", 1); - bind(); + + auto texture = fbo->getTexture(); + texture->bind(); mesh->draw(); - unbind(); + texture->unbind(); } void Skybox::drawStars(float angle, float opacity) { @@ -93,9 +66,7 @@ void Skybox::drawStars(float angle, float opacity) { random.setSeed(STARS_SEED); glm::mat4 rotation = glm::rotate( - glm::mat4(1.0f), - -angle + glm::pi() * 0.5f, - glm::vec3(0, 0, -1) + glm::mat4(1.0f), -angle + glm::pi() * 0.5f, glm::vec3(0, 0, -1) ); rotation = glm::rotate(rotation, sunAltitude, glm::vec3(1, 0, 0)); @@ -108,41 +79,21 @@ void Skybox::drawStars(float angle, float opacity) { glm::vec3 pos = glm::vec4(rx, ry, rz, 1) * rotation; float sopacity = random.randFloat(); - if (pos.y < 0.0f) - continue; + if (pos.y < 0.0f) continue; sopacity *= (0.2f + std::sqrt(std::cos(angle)) * 0.5f) - 0.05f; - glm::vec4 tint (1,1,1, sopacity * opacity); + glm::vec4 tint(1, 1, 1, sopacity * opacity); batch3d->point(pos * depth, tint); } batch3d->flushPoints(); } -void Skybox::draw( - const DrawContext& pctx, - const Camera& camera, - const Assets& assets, +void Skybox::drawSkySprites( float daytime, - float fog) -{ - const glm::uvec2& viewport = pctx.getViewport(); - - glActiveTexture(GL_TEXTURE0); - - drawBackground(camera, assets, viewport.x, viewport.y); - - DrawContext ctx = pctx.sub(); - ctx.setBlendMode(BlendMode::addition); - - auto shader = assets.get("ui3d"); - shader->use(); - shader->uniformMatrix("u_projview", camera.getProjView(false)); - shader->uniformMatrix("u_apply", glm::mat4(1.0f)); - batch3d->begin(); - - float angle = daytime * glm::pi() * 2.0f; - float opacity = glm::pow(1.0f - fog, 7.0f); - + float angle, + float opacity, + const std::vector& sprites +) { float depthScale = 2e3; for (auto& sprite : sprites) { batch3d->texture(assets.get(sprite.texture)); @@ -159,39 +110,124 @@ void Skybox::draw( glm::vec3 pos = glm::vec4(0, distance, 0, 1) * rotation; glm::vec3 up = glm::vec4(depthScale, 0, 0, 1) * rotation; glm::vec3 right = glm::vec4(0, 0, depthScale, 1) * rotation; - glm::vec4 tint (1,1,1, opacity); + glm::vec4 tint(1, 1, 1, opacity); if (!sprite.emissive) { - tint *= 0.6f + std::cos(angle)*0.4; + tint *= 0.6f + std::cos(angle) * 0.4; } - batch3d->sprite(pos, right, - up, 1, 1, UVRegion(), tint); + batch3d->sprite(pos, right, up, 1, 1, UVRegion(), tint); } batch3d->flush(); - drawStars(angle, opacity); } -void Skybox::refresh(const DrawContext& pctx, float t, float mie, const glm::vec3& tint, const glm::vec3& hightlight, uint quality) { +void Skybox::draw( + const Environment& environment, + const DrawContext& pctx, + const Camera& camera, + float daytime, + float fog +) { + const glm::uvec2& viewport = pctx.getViewport(); + drawBackground(camera, viewport.x, viewport.y); + + float angle = daytime * glm::pi() * 2.0f; + float opacity = glm::pow(1.0f - fog, 7.0f); + + DrawContext ctx = pctx.sub(); + ctx.setBlendMode(BlendMode::addition); + + if (environment.sky.sprites.empty() && !environment.sky.stars) { + return; + } + + auto shader = assets.get("ui3d"); + shader->use(); + shader->uniformMatrix("u_projview", camera.getProjView(false)); + shader->uniformMatrix("u_apply", glm::mat4(1.0f)); + batch3d->begin(); + + if (!environment.sky.sprites.empty()) { + drawSkySprites(daytime, angle, opacity, environment.sky.sprites); + } + if (environment.sky.stars) { + drawStars(angle, opacity); + } +} + +void Skybox::setMode(SkyMode mode) { + if (this->mode == mode && fbo) { + return; + } + this->mode = mode; + + uint size = mode == SkyMode::BOX ? this->size : 1U; + prevT = std::numeric_limits().min(); + + uint fboid; + glGenFramebuffers(1, &fboid); + fbo = std::make_unique( + fboid, + false, + std::make_unique(size, size, ImageFormat::RGB888) + ); +} + +void Skybox::refresh( + const Environment& environment, + const DrawContext& pctx, + float dayTime, + float mie, + const glm::vec3& tint, + const glm::vec3& hightlight, + uint quality +) { + setMode(environment.sky.mode); + + if (mode == SkyMode::NONE) { + return; + } else if (mode == SkyMode::SOLID) { + DrawContext ctx = pctx.sub(); + ctx.setDepthMask(false); + ctx.setDepthTest(false); + ctx.setFramebuffer(fbo.get()); + ctx.setViewport({fbo->getWidth(), fbo->getHeight()}); + ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, fbo->getTexture()); + ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); + + auto cubemap = dynamic_cast(fbo->getTexture()); + assert(cubemap != nullptr); + + for (int face = 0; face < 6; face++) { + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, + cubemap->getId(), + 0 + ); + glClearColor(tint.r, tint.g, tint.b, 1); + glClear(GL_COLOR_BUFFER_BIT); + } + return; + } + frameid++; - float dayTime = t; DrawContext ctx = pctx.sub(); ctx.setDepthMask(false); ctx.setDepthTest(false); ctx.setFramebuffer(fbo.get()); - ctx.setViewport({size, size}); + ctx.setViewport({fbo->getWidth(), fbo->getHeight()}); + ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, fbo->getTexture()); + ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); auto cubemap = dynamic_cast(fbo->getTexture()); assert(cubemap != nullptr); - ready = true; - glActiveTexture(GL_TEXTURE0 + TARGET_SKYBOX); - cubemap->bind(); - shader.use(); - t *= glm::two_pi(); - + float t = dayTime * glm::two_pi(); lightDir = glm::normalize(glm::vec3(sin(t), -cos(t), 0.0f)); - float sunAngle = glm::radians((t / glm::two_pi() - 0.25f) * 360.0f); - float x = -glm::cos(sunAngle + glm::pi() * 0.5f) * glm::radians(sunAltitude); + float sunAngle = glm::radians((dayTime - 0.25f) * 360.0f); + float x = -glm::cos(sunAngle + glm::pi() * 0.5f) * + glm::radians(sunAltitude); float y = sunAngle - glm::pi() * 0.5f; float z = glm::radians(0.0f); rotation = glm::rotate(glm::mat4(1.0f), y, glm::vec3(0, 1, 0)); @@ -199,6 +235,7 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, const glm::vec rotation = glm::rotate(rotation, z, glm::vec3(0, 0, 1)); lightDir = glm::vec3(rotation * glm::vec4(0, 0, -1, 1)); + shader.use(); shader.uniform1i("u_quality", quality); shader.uniform1f("u_mie", mie); shader.uniform3f("u_tint", tint); @@ -207,23 +244,22 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, const glm::vec shader.uniform3f("u_lightDir", lightDir); shader.uniform1f("u_dayTime", dayTime); - if (glm::abs(mie-prevMie) + glm::abs(t-prevT) + glm::abs(prevHighlight.r - hightlight.r) >= 0.01) { + if (glm::abs(mie - prevMie) + glm::abs(t - prevT) + + glm::abs(prevHighlight.r - hightlight.r) >= + 0.01) { for (uint face = 0; face < 6; face++) { - refreshFace(face, cubemap); + refreshFace(face, *cubemap); } } else { uint face = frameid % 6; - refreshFace(face, cubemap); + refreshFace(face, *cubemap); } prevMie = mie; prevT = t; prevHighlight = hightlight; - - cubemap->unbind(); - glActiveTexture(GL_TEXTURE0); } -void Skybox::refreshFace(uint face, Cubemap* cubemap) { +void Skybox::refreshFace(uint face, Cubemap& cubemap) { const glm::vec3 xaxs[] = { {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, 1.0f}, @@ -256,7 +292,7 @@ void Skybox::refreshFace(uint face, Cubemap* cubemap) { GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, - cubemap->getId(), + cubemap.getId(), 0 ); shader.uniform3f("u_xaxis", xaxs[face]); @@ -265,14 +301,6 @@ void Skybox::refreshFace(uint face, Cubemap* cubemap) { mesh->draw(); } -void Skybox::bind() const { - glActiveTexture(GL_TEXTURE0 + TARGET_SKYBOX); - fbo->getTexture()->bind(); - glActiveTexture(GL_TEXTURE0); -} - -void Skybox::unbind() const { - glActiveTexture(GL_TEXTURE0 + TARGET_SKYBOX); - fbo->getTexture()->unbind(); - glActiveTexture(GL_TEXTURE0); +const Cubemap* Skybox::getCubemap() const { + return dynamic_cast(fbo->getTexture()); } diff --git a/src/graphics/render/Skybox.hpp b/src/graphics/render/Skybox.hpp index d2dde6f5e..0420bc55e 100644 --- a/src/graphics/render/Skybox.hpp +++ b/src/graphics/render/Skybox.hpp @@ -1,14 +1,17 @@ #pragma once +#include #include #include #include -#include -#include "typedefs.hpp" -#include "maths/fastmaths.hpp" -#include "graphics/core/MeshData.hpp" -template class Mesh; +#include "graphics/core/MeshData.hpp" +#include "maths/fastmaths.hpp" +#include "typedefs.hpp" +#include "world/Environment.hpp" + +template +class Mesh; class Shader; class Assets; class Camera; @@ -21,29 +24,20 @@ struct SkyboxVertex { glm::vec2 position; static constexpr VertexAttribute ATTRIBUTES[] { - {VertexAttribute::Type::FLOAT, false, 2}, - {{}, 0}}; -}; - -struct SkySprite { - std::string texture; - float phase; - float distance; - bool emissive; - float altitude; + {VertexAttribute::Type::FLOAT, false, 2}, {{}, 0}}; }; class Skybox { - std::unique_ptr fbo; + SkyMode mode = SkyMode::SOLID; uint size; + std::unique_ptr fbo; + const Assets& assets; Shader& shader; - bool ready = false; FastRandom random; glm::vec3 lightDir; std::unique_ptr> mesh; std::unique_ptr batch3d; - std::vector sprites; int frameid = 0; float prevMie = -1.0f; @@ -53,30 +47,41 @@ class Skybox { glm::mat4 rotation; void drawStars(float angle, float opacity); - void drawBackground( - const Camera& camera, const Assets& assets, int width, int height + void drawBackground(const Camera& camera, int width, int height); + void drawSkySprites( + float daytime, + float angle, + float opacity, + const std::vector& sprites ); - void refreshFace(uint face, Cubemap* cubemap); + void refreshFace(uint face, Cubemap& cubemap); public: - Skybox(uint size, Shader& shader); + Skybox(uint size, const Assets& assets); ~Skybox(); + void setMode(SkyMode mode); + void draw( - const DrawContext& pctx, - const Camera& camera, - const Assets& assets, + const Environment& environment, + const DrawContext& pctx, + const Camera& camera, float daytime, float fog ); - void refresh(const DrawContext& pctx, float t, float mie, const glm::vec3& tint, const glm::vec3& hightlight, uint quality); - void bind() const; - void unbind() const; - bool isReady() const { - return ready; - } + void refresh( + const Environment& environment, + const DrawContext& pctx, + float t, + float mie, + const glm::vec3& tint, + const glm::vec3& hightlight, + uint quality + ); - const glm::vec3 getLightDir() const { + const Cubemap* getCubemap() const; + + const glm::vec3& getLightDir() const { return lightDir; } }; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 2e150e1f7..57cc90f24 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -1,12 +1,39 @@ #include "WorldRenderer.hpp" +#include "BlockWrapsRenderer.hpp" +#include "ChunksRenderer.hpp" +#include "CloudsRenderer.hpp" +#include "DebugLinesRenderer.hpp" +#include "Emitter.hpp" +#include "HandsRenderer.hpp" +#include "ModelBatch.hpp" +#include "NamedSkeletons.hpp" +#include "ParticlesRenderer.hpp" +#include "PrecipitationRenderer.hpp" +#include "Skybox.hpp" +#include "TextNote.hpp" +#include "TextsRenderer.hpp" #include "assets/Assets.hpp" #include "assets/assets_util.hpp" +#include "coders/GLSLExtension.hpp" #include "content/Content.hpp" #include "engine/Engine.hpp" -#include "coders/GLSLExtension.hpp" -#include "frontend/LevelFrontend.hpp" #include "frontend/ContentGfxCache.hpp" +#include "frontend/LevelFrontend.hpp" +#include "graphics/commons/Model.hpp" +#include "graphics/core/Atlas.hpp" +#include "graphics/core/Batch3D.hpp" +#include "graphics/core/DrawContext.hpp" +#include "graphics/core/Font.hpp" +#include "graphics/core/Framebuffer.hpp" +#include "graphics/core/GBuffer.hpp" +#include "graphics/core/LineBatch.hpp" +#include "graphics/core/Mesh.hpp" +#include "graphics/core/PostProcessing.hpp" +#include "graphics/core/Shader.hpp" +#include "graphics/core/Shadows.hpp" +#include "graphics/core/Cubemap.hpp" +#include "graphics/core/Texture.hpp" #include "items/Inventory.hpp" #include "items/ItemDef.hpp" #include "items/ItemStack.hpp" @@ -25,32 +52,6 @@ #include "world/Level.hpp" #include "world/LevelEvents.hpp" #include "world/World.hpp" -#include "graphics/commons/Model.hpp" -#include "graphics/core/Atlas.hpp" -#include "graphics/core/Batch3D.hpp" -#include "graphics/core/DrawContext.hpp" -#include "graphics/core/LineBatch.hpp" -#include "graphics/core/Mesh.hpp" -#include "graphics/core/PostProcessing.hpp" -#include "graphics/core/Framebuffer.hpp" -#include "graphics/core/Shader.hpp" -#include "graphics/core/Texture.hpp" -#include "graphics/core/Font.hpp" -#include "graphics/core/Shadows.hpp" -#include "graphics/core/GBuffer.hpp" -#include "BlockWrapsRenderer.hpp" -#include "ParticlesRenderer.hpp" -#include "PrecipitationRenderer.hpp" -#include "HandsRenderer.hpp" -#include "NamedSkeletons.hpp" -#include "TextsRenderer.hpp" -#include "ChunksRenderer.hpp" -#include "DebugLinesRenderer.hpp" -#include "ModelBatch.hpp" -#include "Skybox.hpp" -#include "Emitter.hpp" -#include "TextNote.hpp" -#include "CloudsRenderer.hpp" #include #include @@ -58,8 +59,6 @@ #include #include -using namespace advanced_pipeline; - inline constexpr size_t BATCH3D_CAPACITY = 4096; inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000; @@ -105,8 +104,7 @@ WorldRenderer::WorldRenderer( [this](LevelEventType, Chunk* chunk) { chunksRenderer->unload(chunk); } ); skybox = std::make_unique( - settings.graphics.skyboxResolution.get(), - assets.require("skybox_gen") + settings.graphics.skyboxResolution.get(), assets ); const auto& content = level.content; @@ -160,7 +158,7 @@ void WorldRenderer::setupWorldShader( shader.uniform1i("u_debugNormals", false); shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime); shader.uniform2f("u_lightDir", skybox->getLightDir()); - shader.uniform1i("u_skybox", TARGET_SKYBOX); + shader.uniform1i("u_skybox", advanced_pipeline::TARGET_SKYBOX); auto indices = level.content.getIndices(); // Light emission when an emissive item is chosen @@ -179,6 +177,11 @@ void WorldRenderer::setupWorldShader( } } +float WorldRenderer::calcFogFactor() const { + const auto& settings = engine.getSettings(); + return 15.0f / static_cast(settings.chunks.loadDistance.get() - 2); +} + void WorldRenderer::renderOpaque( const DrawContext& ctx, const Camera& camera, @@ -187,14 +190,12 @@ void WorldRenderer::renderOpaque( ) { texts->render(ctx, camera, settings, hudVisible, false); - bool culling = engine.getSettings().graphics.frustumCulling.get(); - float fogFactor = - 15.0f / static_cast(settings.chunks.loadDistance.get() - 2); + float fogFactor = calcFogFactor(); auto& entityShader = assets.require("entity"); setupWorldShader(entityShader, camera, settings, fogFactor); - skybox->bind(); + bool culling = engine.getSettings().graphics.frustumCulling.get(); if (culling) { frustumCulling->update(camera.getProjView()); } @@ -213,25 +214,26 @@ void WorldRenderer::renderOpaque( auto& shader = assets.require("main"); auto& cloudsShader = assets.require("clouds"); - auto& linesShader = assets.require("lines"); setupWorldShader(shader, camera, settings, fogFactor); chunksRenderer->drawChunks(camera, shader); blockWraps->draw(ctx); - int cloudsQuality = settings.graphics.cloudsQuality.get(); - if (cloudsQuality > 0) { - setupWorldShader(cloudsShader, camera, settings, fogFactor); - cloudsRenderer->draw( - cloudsShader, weather, timer, fogFactor, camera, cloudsQuality - ); + if (level.environment.sky.clouds) { + int cloudsQuality = settings.graphics.cloudsQuality.get(); + if (cloudsQuality > 0) { + setupWorldShader(cloudsShader, camera, settings, fogFactor); + cloudsRenderer->draw( + cloudsShader, weather, timer, fogFactor, camera, cloudsQuality + ); + } } if (hudVisible) { + auto& linesShader = assets.require("lines"); renderLines(camera, linesShader, ctx); } - skybox->unbind(); } void WorldRenderer::renderBlockSelection() { @@ -286,21 +288,19 @@ void WorldRenderer::refreshSettings(Shader** shaders) { int shadowsQuality = graphics.shadowsQuality.get() * gbufferPipeline; shadowMapping->setQuality(shadowsQuality); - + CompileTimeShaderSettings currentSettings { gbufferPipeline, shadowsQuality != 0, - graphics.ssao.get() && gbufferPipeline - }; - if ( - prevCTShaderSettings.advancedRender != currentSettings.advancedRender || + graphics.ssao.get() && gbufferPipeline}; + if (prevCTShaderSettings.advancedRender != currentSettings.advancedRender || prevCTShaderSettings.shadows != currentSettings.shadows || - prevCTShaderSettings.ssao != currentSettings.ssao - ) { + prevCTShaderSettings.ssao != currentSettings.ssao) { std::vector defines; if (currentSettings.shadows) defines.emplace_back("ENABLE_SHADOWS"); if (currentSettings.ssao) defines.emplace_back("ENABLE_SSAO"); - if (currentSettings.advancedRender) defines.emplace_back("ADVANCED_RENDER"); + if (currentSettings.advancedRender) + defines.emplace_back("ADVANCED_RENDER"); for (size_t i = 0; shaders[i]; i++) { shaders[i]->recompile(defines); @@ -332,7 +332,8 @@ void WorldRenderer::renderFrame( auto& entityShader = assets.require("entity"); auto& cloudsShader = assets.require("clouds"); auto& translucentShader = assets.require("translucent"); - auto& deferredShader = assets.require("deferred_lighting").getShader(); + auto& deferredShader = + assets.require("deferred_lighting").getShader(); const auto& settings = engine.getSettings(); @@ -342,19 +343,19 @@ void WorldRenderer::renderFrame( &cloudsShader, &translucentShader, &deferredShader, - nullptr - }; + nullptr}; refreshSettings(affectedShaders); const auto& worldInfo = world->getInfo(); - + float clouds = weather.clouds(); clouds = glm::max(worldInfo.fog, clouds); float mie = 1.0f + glm::max(worldInfo.fog, clouds * 0.5f) * 2.0f; float random = rand() / static_cast(RAND_MAX); skybox->refresh( + level.environment, pctx, worldInfo.daytime, mie, @@ -373,20 +374,19 @@ void WorldRenderer::renderFrame( { DrawContext wctx = pctx.sub(); postProcessing.use(wctx, gbufferPipeline); - display::clearDepth(); /* Main opaque pass (GBuffer pass) */ { DrawContext ctx = wctx.sub(); ctx.setDepthTest(true); ctx.setCullFace(true); + ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap()); + ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); renderOpaque(ctx, camera, settings, hudVisible); } texts->render(pctx, camera, settings, hudVisible, true); } - skybox->bind(); - float fogFactor = - 15.0f / static_cast(settings.chunks.loadDistance.get() - 2); + float fogFactor = calcFogFactor(); if (gbufferPipeline) { deferredShader.use(); setupWorldShader(deferredShader, camera, settings, fogFactor); @@ -395,6 +395,7 @@ void WorldRenderer::renderFrame( { DrawContext ctx = pctx.sub(); ctx.setDepthTest(true); + ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); if (gbufferPipeline) { postProcessing.bindDepthBuffer(); @@ -403,7 +404,9 @@ void WorldRenderer::renderFrame( } // Background sky plane - skybox->draw(ctx, camera, assets, worldInfo.daytime, clouds); + skybox->draw( + level.environment, ctx, camera, worldInfo.daytime, clouds + ); auto& linesShader = assets.require("lines"); linesShader.use(); @@ -415,10 +418,12 @@ void WorldRenderer::renderFrame( linesShader.uniformMatrix("u_projview", projView); lineBatch->flush(); - skybox->bind(); + DrawContext wctx = ctx.sub(); + wctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap()); + wctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); // Translucent blocks { - auto sctx = ctx.sub(); + auto sctx = wctx.sub(); sctx.setCullFace(true); translucentShader.use(); setupWorldShader(translucentShader, camera, settings, fogFactor); @@ -429,44 +434,41 @@ void WorldRenderer::renderFrame( entityShader.use(); setupWorldShader(entityShader, camera, settings, fogFactor); - std::array weatherInstances {&weather.a, &weather.b}; + std::array weatherInstances { + &weather.a, &weather.b}; for (const auto& weather : weatherInstances) { float maxIntensity = weather->fall.maxIntensity; float zero = weather->fall.minOpacity; float one = weather->fall.maxOpacity; float t = (weather->intensity * (one - zero)) * maxIntensity + zero; entityShader.uniform1i("u_alphaClip", weather->fall.opaque); - entityShader.uniform1f("u_opacity", weather->fall.opaque ? t * t : t); + entityShader.uniform1f( + "u_opacity", weather->fall.opaque ? t * t : t + ); if (weather->intensity > 1.e-3f && !weather->fall.texture.empty()) { precipitation->render(camera, *weather); } } - skybox->unbind(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); } postProcessing.render(pctx, assets, timer, camera); - + if (player.currentCamera == player.fpCamera) { DrawContext ctx = pctx.sub(); ctx.setDepthTest(true); ctx.setCullFace(true); + ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap()); + ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); + display::clearDepth(); // prepare modified HUD camera Camera hudcam = camera; hudcam.far = 10.0f; hudcam.setFov(0.9f); hudcam.position = {}; - - hands->render(camera); - display::clearDepth(); setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f); - - skybox->bind(); - modelBatch->render(); - modelBatch->setLightsOffset(glm::vec3()); - skybox->unbind(); + hands->render(camera); } renderBlockOverlay(pctx); @@ -486,13 +488,12 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) { if (def.overlayTexture.empty()) { return; } - auto textureRegion = util::get_texture_region( - assets, def.overlayTexture, "blocks:notfound" - ); + auto textureRegion = + util::get_texture_region(assets, def.overlayTexture, "blocks:notfound"); DrawContext ctx = wctx.sub(); ctx.setDepthTest(false); ctx.setCullFace(false); - + auto& shader = assets.require("ui3d"); shader.use(); batch3d->begin(); diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index b9a893d16..c3e6b1669 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -99,6 +99,8 @@ class WorldRenderer { ); void refreshSettings(Shader** shaders); + + float calcFogFactor() const; public: std::unique_ptr particles; std::unique_ptr texts; diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 7a460c066..1dc791b19 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -25,7 +25,7 @@ const uint MIN_SURROUNDING = 9; ChunksController::ChunksController(Level& level) : level(level), generator(std::make_unique( - level.content.generators.require(level.getWorld()->getGenerator()), + level.content.generators.require(level.environment.generator), level.content, level.getWorld()->getSeed() )) {} diff --git a/src/logic/EngineController.cpp b/src/logic/EngineController.cpp index c3d775919..ec9ef0b2a 100644 --- a/src/logic/EngineController.cpp +++ b/src/logic/EngineController.cpp @@ -179,7 +179,7 @@ void EngineController::onMissingContent( if (engine.isHeadless()) { throw std::runtime_error( "missing content: " + - json::stringify(create_missing_content_report(report), true, " ") + util::quote(json::stringify(create_missing_content_report(report), true, " ")) ); } else { engine.setScreen(std::make_shared(engine)); @@ -268,7 +268,7 @@ inline uint64_t str2seed(const std::string& seedstr) { void EngineController::createWorld( const std::string& name, const std::string& seedstr, - const std::string& generatorID + const std::string& environment ) { uint64_t seed = str2seed(seedstr); @@ -279,17 +279,31 @@ void EngineController::createWorld( paths.setCurrentWorldFolder(folder); engine.getContentControl().loadContent(); }); + const auto& generators = engine.getContentControl().get()->generators; + + // generator-based environments must die in 1.0 + bool genBasedEnv = false; + if (auto genDef = generators.find(environment)) { + genBasedEnv = true; + } auto& contentControl = engine.getContentControl(); auto level = World::create( name, - generatorID, + genBasedEnv ? "" : environment, + genBasedEnv ? environment : "", folder, seed, engine.getSettings(), *contentControl.get(), contentControl.getContentPacks() ); + + // generator-based environment initialization + if (genBasedEnv) { + level->environment.generator = environment; + } + if (!engine.isHeadless()) { level->players->create(localPlayer); } diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 704949851..2d61e9af8 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -149,7 +149,7 @@ void LevelController::saveWorld() { world->wfile->createDirectories(); scripting::on_world_save(); level->onSave(); - level->getWorld()->write(level.get()); + level->getWorld()->write(*level); } void LevelController::onWorldQuit() { diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 3b3b4af59..bd0690523 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -125,7 +125,7 @@ static int l_is_night(lua::State* L) { } static int l_get_generator(lua::State* L) { - return lua::pushstring(L, require_world_info().generator); + return lua::pushstring(L, require_level().environment.generator); } static int l_get_chunk_data(lua::State* L) { diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index c0fe61744..44ea8859f 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -127,7 +127,7 @@ void Player::teleport(glm::vec3 position) { void Player::attemptToChooseSpawnpoint() { // looks bad to be here tbh const auto& generatorDef = - level.content.generators.require(level.getWorld()->getGenerator()); + level.content.generators.require(level.environment.generator); int minHeight = generatorDef.playerMinSpawnHeight; int maxHeight = generatorDef.playerMaxSpawnHeight; diff --git a/src/world/Environment.cpp b/src/world/Environment.cpp new file mode 100644 index 000000000..29c3ef476 --- /dev/null +++ b/src/world/Environment.cpp @@ -0,0 +1,49 @@ +#define VC_ENABLE_REFLECTION +#include "Environment.hpp" + +#include "data/dv.hpp" +#include "data/dv_util.hpp" + +dv::value Environment::serialize() const { + auto skySpritesList = dv::list(); + for (const auto& sprite : sky.sprites) { + skySpritesList.add(dv::object({ + {"texture", sprite.texture}, + {"phase", sprite.phase}, + {"distance", sprite.distance}, + {"emissive", sprite.emissive}, + {"altitude", sprite.altitude}, + })); + } + return dv::object({ + {"sky", dv::object({ + {"mode", SkyModeMeta.getNameString(sky.mode)}, + {"stars", sky.stars}, + {"clouds", sky.clouds}, + {"sprites", std::move(skySpritesList)}, + })}, + {"generator", generator} + }); +} + +void Environment::deserialize(const dv::value& src) { + if (auto skyItem = src.at("sky")) { + skyItem->at("mode").get(sky.mode, SkyModeMeta); + skyItem->at("stars").get(sky.stars); + skyItem->at("clouds").get(sky.clouds); + + if (auto skySpritesList = skyItem->at("sprites")) { + for (int i = 0; i < skySpritesList->size(); i++) { + SkySprite sprite; + const auto& item = (*skySpritesList.ptr)[i]; + item.at("texture").get(sprite.texture); + item.at("phase").get(sprite.phase); + item.at("distance").get(sprite.distance); + item.at("emissive").get(sprite.emissive); + item.at("altitude").get(sprite.altitude); + sky.sprites.push_back(std::move(sprite)); + } + } + } + src.at("generator").get(generator); +} diff --git a/src/world/Environment.hpp b/src/world/Environment.hpp new file mode 100644 index 000000000..c2efa828a --- /dev/null +++ b/src/world/Environment.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include "interfaces/Serializable.hpp" +#include "util/EnumMetadata.hpp" + +#include +#include + +enum class SkyMode { + NONE, SOLID, BOX +}; + +VC_ENUM_METADATA(SkyMode) + {"none", SkyMode::NONE}, + {"solid", SkyMode::SOLID}, + {"box", SkyMode::BOX}, +VC_ENUM_END + +struct SkySprite { + std::string texture; + float phase; + float distance; + bool emissive; + float altitude; +}; + +class Environment : public Serializable { +public: + struct { + SkyMode mode = SkyMode::BOX; + bool stars = true; + bool clouds = true; + // TODO: move defaults to config file + std::vector sprites { + SkySprite { + "misc/moon", + glm::pi() * 0.5f, + 4.0f, + false, + glm::pi() * 0.25f, + }, + SkySprite { + "misc/moon_flare", + glm::pi() * 0.5f, + 0.5f, + false, + glm::pi() * 0.25f, + }, + SkySprite { + "misc/sun", + glm::pi() * 1.5f, + 4.0f, + true, + glm::pi() * 0.25f, + }, + }; + } sky; + + std::string generator; + + Environment() = default; + + dv::value serialize() const override; + void deserialize(const dv::value& src) override; +private: +}; diff --git a/src/world/Level.cpp b/src/world/Level.cpp index e6d9000e1..9e40e90cd 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -31,6 +31,7 @@ Level::Level( entities(std::make_unique(*this)), players(std::make_unique(*this)), pathfinding(std::make_unique(*this)) { + const auto& worldInfo = world->getInfo(); auto& cameraIndices = content.getIndices(ResourceType::CAMERA); for (size_t i = 0; i < cameraIndices.size(); i++) { diff --git a/src/world/Level.hpp b/src/world/Level.hpp index 0dd3251fa..c8945a23c 100644 --- a/src/world/Level.hpp +++ b/src/world/Level.hpp @@ -6,6 +6,7 @@ #include #include "typedefs.hpp" +#include "Environment.hpp" class Content; class World; @@ -36,6 +37,7 @@ public: std::unique_ptr players; std::unique_ptr pathfinding; std::vector> cameras; // move somewhere? + Environment environment {}; Level( std::unique_ptr world, diff --git a/src/world/World.cpp b/src/world/World.cpp index 6508a4dab..2acc7edaa 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -65,12 +65,12 @@ void World::writeResources(const Content& content) { io::write_json(wfile->getResourcesFile(), root); } -void World::write(Level* level) { - level->chunks->saveAll(); - info.nextEntityId = level->entities->peekNextID(); +void World::write(Level& level) { + level.chunks->saveAll(); + info.nextEntityId = level.entities->peekNextID(); wfile->write(this, &content); - auto playerFile = level->players->serialize(); + auto playerFile = level.players->serialize(); io::write_json(wfile->getPlayerFile(), playerFile); writeResources(content); @@ -78,7 +78,8 @@ void World::write(Level* level) { std::unique_ptr World::create( const std::string& name, - const std::string& generator, + const std::string& environment, + const std::string& generatorOverride, const io::path& directory, uint64_t seed, EngineSettings& settings, @@ -87,7 +88,8 @@ std::unique_ptr World::create( ) { WorldInfo info {}; info.name = name; - info.generator = generator; + info.environment = environment; + info.explicitGenerator = generatorOverride; info.seed = seed; auto world = std::make_unique( info, @@ -101,7 +103,7 @@ std::unique_ptr World::create( logger.info() << "created world '" << name << "' (" << directory.string() << ")"; } - logger.info() << "world seed: " << seed << " generator: " << generator; + logger.info() << "world seed: " << seed << " environment: " << environment; return std::make_unique(std::move(world), content, settings); } @@ -122,16 +124,19 @@ std::unique_ptr World::load( << worldFilesPtr->getFolder().string() << ")"; logger.info() << "world version: " << info->major << "." << info->minor << " seed: " << info->seed - << " generator: " << info->generator; + << " environment: " << info->environment; - auto world = std::make_unique( + auto worldPtr = std::make_unique( info.value(), std::move(worldFilesPtr), content, packs ); + auto world = worldPtr.get(); auto& wfile = world->wfile; wfile->readResourcesData(content); - auto level = std::make_unique(std::move(world), content, settings); - + auto level = std::make_unique(std::move(worldPtr), content, settings); + if (world->getEnvironment().empty()) { + level->environment.generator = world->getInfo().explicitGenerator; + } io::path file = wfile->getPlayerFile(); if (!io::is_regular_file(file)) { logger.warning() << "player.json does not exists"; @@ -163,8 +168,8 @@ void World::setName(const std::string& name) { this->info.name = name; } -void World::setGenerator(const std::string& generator) { - this->info.generator = generator; +void World::setEnvironment(const std::string& environment) { + this->info.environment = environment; } bool World::hasPack(const std::string& id) const { @@ -186,8 +191,8 @@ uint64_t World::getSeed() const { return info.seed; } -std::string World::getGenerator() const { - return info.generator; +std::string World::getEnvironment() const { + return info.environment; } const std::vector& World::getPacks() const { @@ -196,7 +201,8 @@ const std::vector& World::getPacks() const { void WorldInfo::deserialize(const dv::value& root) { name = root["name"].asString(); - generator = root["generator"].asString(generator); + root.at("environment").get(environment); + root.at("generator").get(explicitGenerator); seed = root["seed"].asInteger(seed); if (root.has("version")) { @@ -226,7 +232,11 @@ dv::value WorldInfo::serialize() const { versionobj["minor"] = ENGINE_VERSION_MINOR; root["name"] = name; - root["generator"] = generator; + if (environment.empty()) { + root["generator"] = explicitGenerator; + } else { + root["environment"] = environment; + } root["seed"] = seed; auto& timeobj = root.object("time"); diff --git a/src/world/World.hpp b/src/world/World.hpp index 725b91823..14649fb1b 100644 --- a/src/world/World.hpp +++ b/src/world/World.hpp @@ -23,7 +23,8 @@ public: struct WorldInfo : public Serializable { std::string name; - std::string generator; + std::string environment; + std::string explicitGenerator; uint64_t seed; int64_t nextInventoryId = 1; int64_t nextPlayerId = 0; @@ -76,7 +77,7 @@ public: void updateTimers(float delta); /// @brief Write all unsaved level data to the world directory - void write(Level* level); + void write(Level& level); /// @brief Check world indices and generate ContentReport if convert required /// @param directory world directory @@ -98,7 +99,8 @@ public: /// @return Level instance containing World instance static std::unique_ptr create( const std::string& name, - const std::string& generator, + const std::string& environment, + const std::string& generatorOverride, const io::path& directory, uint64_t seed, EngineSettings& settings, @@ -123,7 +125,7 @@ public: void setName(const std::string& name); void setSeed(uint64_t seed); - void setGenerator(const std::string& generator); + void setEnvironment(const std::string& environment); /// @brief Check if world has content-pack installed /// @param id content-pack id @@ -137,7 +139,7 @@ public: uint64_t getSeed() const; /// @brief Get world generator id - std::string getGenerator() const; + std::string getEnvironment() const; bool isNameless() const { return info.name.empty();