From 7a25560bc0ee5bb1c76b926164186db2a16aa331 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 24 May 2025 19:28:24 +0300 Subject: [PATCH] add 'modelviewer' ui element --- src/graphics/core/Batch3D.cpp | 49 ++++++-- src/graphics/core/Batch3D.hpp | 5 +- src/graphics/core/Shader.cpp | 6 + src/graphics/core/Shader.hpp | 3 + src/graphics/ui/elements/ModelViewer.cpp | 150 +++++++++++++++++++++++ src/graphics/ui/elements/ModelViewer.hpp | 40 ++++++ src/graphics/ui/gui_xml.cpp | 19 +++ 7 files changed, 261 insertions(+), 11 deletions(-) create mode 100644 src/graphics/ui/elements/ModelViewer.cpp create mode 100644 src/graphics/ui/elements/ModelViewer.hpp diff --git a/src/graphics/core/Batch3D.cpp b/src/graphics/core/Batch3D.cpp index 43d161f6..327bb690 100644 --- a/src/graphics/core/Batch3D.cpp +++ b/src/graphics/core/Batch3D.cpp @@ -6,6 +6,11 @@ #include "typedefs.hpp" #include "maths/UVRegion.hpp" +namespace { + const glm::vec3 SUN_VECTOR(0.528265f, 0.833149f, -0.163704f); + const float DIRECTIONAL_LIGHT_FACTOR = 0.3f; +} + Batch3D::Batch3D(size_t capacity) : capacity(capacity) { @@ -31,20 +36,28 @@ void Batch3D::begin(){ } void Batch3D::vertex( - float x, float y, float z, float u, float v, - float r, float g, float b, float a + float x, + float y, + float z, + float u, + float v, + float r, + float g, + float b, + float a ) { buffer[index].position = {x, y, z}; - buffer[index].uv = {u, v}; + buffer[index].uv = { + u * region.getWidth() + region.u1, v * region.getHeight() + region.v1}; buffer[index].color = {r, g, b, a}; index++; } void Batch3D::vertex( - glm::vec3 coord, float u, float v, - float r, float g, float b, float a + glm::vec3 coord, float u, float v, float r, float g, float b, float a ) { buffer[index].position = coord; - buffer[index].uv = {u, v}; + buffer[index].uv = { + u * region.getWidth() + region.u1, v * region.getHeight() + region.v1}; buffer[index].color = {r, g, b, a}; index++; } @@ -54,7 +67,9 @@ void Batch3D::vertex( float r, float g, float b, float a ) { buffer[index].position = point; - buffer[index].uv = uvpoint; + buffer[index].uv = { + uvpoint.x * region.getWidth() + region.u1, + uvpoint.y * region.getHeight() + region.v1}; buffer[index].color = {r, g, b, a}; index++; } @@ -86,14 +101,18 @@ void Batch3D::face( } void Batch3D::texture(const Texture* new_texture){ - if (currentTexture == new_texture) + if (currentTexture == new_texture) { return; + } flush(); currentTexture = new_texture; - if (new_texture == nullptr) + if (new_texture == nullptr) { blank->bind(); - else + region = blank->getUVRegion(); + } else { new_texture->bind(); + region = currentTexture->getUVRegion(); + } } void Batch3D::sprite( @@ -240,6 +259,16 @@ void Batch3D::blockCube( cube((1.0f - size) * -0.5f, size, texfaces, tint, shading); } +void Batch3D::setRegion(UVRegion region) { + this->region = region; +} + +void Batch3D::vertex(const glm::vec3& pos, const glm::vec2& uv, const glm::vec3& norm) { + float d = glm::dot(glm::normalize(norm), SUN_VECTOR); + d = (1.0f - DIRECTIONAL_LIGHT_FACTOR) + d * DIRECTIONAL_LIGHT_FACTOR; + vertex(pos, uv, glm::vec4(d, d, d, 1.0f)); +} + void Batch3D::vertex( const glm::vec3& coord, const glm::vec2& uv, const glm::vec4& tint ) { diff --git a/src/graphics/core/Batch3D.hpp b/src/graphics/core/Batch3D.hpp index c3fb3775..88bec3e4 100644 --- a/src/graphics/core/Batch3D.hpp +++ b/src/graphics/core/Batch3D.hpp @@ -3,6 +3,7 @@ #include "typedefs.hpp" #include "commons.hpp" #include "MeshData.hpp" +#include "maths/UVRegion.hpp" #include #include @@ -32,8 +33,8 @@ class Batch3D : public Flushable { std::unique_ptr blank; size_t index; glm::vec4 tint {1.0f}; - const Texture* currentTexture; + UVRegion region {0.0f, 0.0f, 1.0f, 1.0f}; void vertex( float x, float y, float z, @@ -102,6 +103,8 @@ public: const glm::vec4& tint, bool shading = true ); + void setRegion(UVRegion region); + void vertex(const glm::vec3& pos, const glm::vec2& uv, const glm::vec3& norm); void vertex(const glm::vec3& pos, const glm::vec2& uv, const glm::vec4& tint); void point(const glm::vec3& pos, const glm::vec4& tint); void flush() override; diff --git a/src/graphics/core/Shader.cpp b/src/graphics/core/Shader.cpp index 122495ff..626d747b 100644 --- a/src/graphics/core/Shader.cpp +++ b/src/graphics/core/Shader.cpp @@ -16,6 +16,7 @@ namespace fs = std::filesystem; GLSLExtension* Shader::preprocessor = new GLSLExtension(); +Shader* Shader::used = nullptr; Shader::Shader(uint id) : id(id){ } @@ -25,6 +26,7 @@ Shader::~Shader(){ } void Shader::use(){ + used = this; glUseProgram(id); } @@ -131,3 +133,7 @@ std::unique_ptr Shader::create( } return std::make_unique(id); } + +Shader& Shader::getUsed() { + return *used; +} diff --git a/src/graphics/core/Shader.hpp b/src/graphics/core/Shader.hpp index 5c970d4f..18a14da7 100644 --- a/src/graphics/core/Shader.hpp +++ b/src/graphics/core/Shader.hpp @@ -10,6 +10,7 @@ class GLSLExtension; class Shader { + static Shader* used; uint id; std::unordered_map uniformLocations; @@ -43,4 +44,6 @@ public: const std::string& vertexSource, const std::string& fragmentSource ); + + static Shader& getUsed(); }; diff --git a/src/graphics/ui/elements/ModelViewer.cpp b/src/graphics/ui/elements/ModelViewer.cpp new file mode 100644 index 00000000..4f931839 --- /dev/null +++ b/src/graphics/ui/elements/ModelViewer.cpp @@ -0,0 +1,150 @@ +#include +#include "ModelViewer.hpp" + +#include "assets/Assets.hpp" +#include "assets/assets_util.hpp" +#include "graphics/commons/Model.hpp" +#include "graphics/core/Batch2D.hpp" +#include "graphics/core/Batch3D.hpp" +#include "graphics/core/Shader.hpp" +#include "graphics/core/Framebuffer.hpp" +#include "graphics/core/DrawContext.hpp" +#include "window/Window.hpp" +#include "../GUI.hpp" + +// TODO: remove +#include + +using namespace gui; + +ModelViewer::ModelViewer( + GUI& gui, const glm::vec2& size, const std::string& modelName +) + : Container(gui, size), + modelName(modelName), + camera(), + batch(std::make_unique(1024)), + fbo(std::make_unique(size.x, size.y)) { + camera.perspective = true; + camera.position = glm::vec3(2, 2, 2); +} + +ModelViewer::~ModelViewer() = default; + +void ModelViewer::setModel(const std::string& modelName) { + this->modelName = modelName; +} + +const std::string& ModelViewer::getModel() const { + return modelName; +} + +Camera& ModelViewer::getCamera() { + return camera; +} + +const Camera& ModelViewer::getCamera() const { + return camera; +} + +void ModelViewer::act(float delta) { + Container::act(delta); + + auto& input = gui.getInput(); + + if (!grabbing && hover && input.jclicked(Mousecode::BUTTON_3)) { + grabbing = true; + } + if (grabbing && input.clicked(Mousecode::BUTTON_3)) { + auto cursor = input.getCursor(); + if (input.pressed(Keycode::LEFT_SHIFT)) { + center -= camera.right * (cursor.delta.x / size.x) * distance; + center += camera.up * (cursor.delta.y / size.y) * distance; + } else { + rotation.x -= cursor.delta.x / size.x * glm::two_pi(); + rotation.y -= cursor.delta.y / size.y * glm::two_pi(); + rotation.y = glm::max( + -glm::half_pi(), glm::min(glm::half_pi(), rotation.y) + ); + } + } else if (grabbing) { + grabbing = false; + } + if (hover) { + distance *= 1.0f - 0.2f * input.getScroll(); + } + camera.rotation = glm::mat4(1.0f); + camera.rotate(rotation.y, rotation.x, rotation.z); + camera.position = center - camera.front * distance; +} + +void ModelViewer::draw(const DrawContext& pctx, const Assets& assets) { + camera.setAspectRatio(size.x / size.y); + camera.updateVectors(); + + auto model = assets.get(modelName); + if (model == nullptr) { + return; + } + auto& prevShader = Shader::getUsed(); + + fbo->resize(size.x, size.y); + { + glDisable(GL_SCISSOR_TEST); + auto ctx = pctx.sub(); + ctx.setFramebuffer(fbo.get()); + ctx.setViewport({size.x, size.y}); + ctx.setDepthTest(true); + display::clear(); + + auto& ui3dShader = assets.require("ui3d"); + ui3dShader.use(); + ui3dShader.uniformMatrix("u_apply", glm::mat4(1.0f)); + ui3dShader.uniformMatrix("u_projview", camera.getProjView()); + batch->begin(); + for (const auto& mesh : model->meshes) { + util::TextureRegion region; + if (!mesh.texture.empty() && mesh.texture[0] == '$') { + // todo: refactor + static std::array faces { + "blocks:dbg_north", + "blocks:dbg_south", + "blocks:dbg_top", + "blocks:dbg_bottom", + "blocks:dbg_east", + "blocks:dbg_west", + }; + region = util::get_texture_region( + assets, + faces.at(mesh.texture.at(1) - '0'), + "blocks:notfound" + ); + } else { + region = util::get_texture_region(assets, mesh.texture, "blocks:notfound"); + } + batch->texture(region.texture); + batch->setRegion(region.region); + for (const auto& vertex : mesh.vertices) { + batch->vertex(vertex.coord, vertex.uv, vertex.normal); + } + } + batch->flush(); + glEnable(GL_SCISSOR_TEST); + } + + auto pos = calcPos(); + prevShader.use(); + auto& batch2d = *pctx.getBatch2D(); + batch2d.texture(fbo->getTexture()); + batch2d.rect(pos.x, pos.y, size.x, size.y, 0.0f, 0.0f, 0.0f, UVRegion {}, false, true, glm::vec4{1.0f}); + + Container::draw(pctx, assets); +} + +void ModelViewer::setCenter(const glm::vec3& center) { + this->center = center; +} + +void ModelViewer::setRotation(const glm::vec3& euler) { + this->rotation = euler; +} diff --git a/src/graphics/ui/elements/ModelViewer.hpp b/src/graphics/ui/elements/ModelViewer.hpp new file mode 100644 index 00000000..37fa497e --- /dev/null +++ b/src/graphics/ui/elements/ModelViewer.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include "Container.hpp" +#include "window/Camera.hpp" + +class Batch3D; +class Framebuffer; + +namespace gui { + class ModelViewer : public Container { + private: + std::string modelName; + Camera camera; + std::unique_ptr batch; + std::unique_ptr fbo; + + glm::vec3 rotation {}; + glm::vec3 center {}; + float distance = 4.0f; + bool grabbing = false; + public: + ModelViewer(GUI& gui, const glm::vec2& size, const std::string& modelName); + + ~ModelViewer(); + + void setModel(const std::string& modelName); + const std::string& getModel() const; + + Camera& getCamera(); + const Camera& getCamera() const; + + void act(float delta) override; + void draw(const DrawContext& pctx, const Assets& assets) override; + + void setRotation(const glm::vec3& euler); + void setCenter(const glm::vec3& center); + }; +} diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index d393dbae..aaa5a269 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -19,6 +19,7 @@ #include "elements/Panel.hpp" #include "elements/TextBox.hpp" #include "elements/TrackBar.hpp" +#include "elements/ModelViewer.hpp" #include "engine/Engine.hpp" #include "frontend/locale.hpp" #include "frontend/menu.hpp" @@ -368,6 +369,23 @@ static std::shared_ptr read_split_box( return splitBox; } +static std::shared_ptr read_model_viewer( + UiXmlReader& reader, const xml::xmlelement& element +) { + auto model = element.attr("model", "").getText(); + auto viewer = std::make_shared( + reader.getGUI(), glm::vec2(), model + ); + read_container_impl(reader, element, *viewer); + if (element.has("center")) { + viewer->setCenter(element.attr("center").asVec3()); + } + if (element.has("cam-rotation")) { + viewer->setRotation(glm::radians(element.attr("cam-rotation").asVec3())); + } + return viewer; +} + static std::shared_ptr read_panel( UiXmlReader& reader, const xml::xmlelement& element ) { @@ -785,6 +803,7 @@ UiXmlReader::UiXmlReader(gui::GUI& gui, scriptenv&& env) : gui(gui), env(std::mo add("trackbar", read_track_bar); add("container", read_container); add("bindbox", read_input_bind_box); + add("modelviewer", read_model_viewer); add("inventory", read_inventory); }