From e4f9bd03b776774a6d94807fe91d144fb815aaac Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 22 Jun 2024 20:24:35 +0300 Subject: [PATCH] add src/coders/obj --- src/coders/commons.cpp | 35 ++++++++ src/coders/commons.hpp | 3 + src/coders/json.cpp | 8 +- src/coders/obj.cpp | 124 ++++++++++++++++++++++++++ src/coders/obj.hpp | 19 ++++ src/data/dynamic.hpp | 10 +++ src/graphics/core/Model.cpp | 13 +++ src/graphics/core/Model.hpp | 5 ++ src/graphics/render/ModelBatch.cpp | 20 +++-- src/graphics/render/WorldRenderer.cpp | 26 +++--- src/graphics/render/WorldRenderer.hpp | 6 ++ 11 files changed, 241 insertions(+), 28 deletions(-) create mode 100644 src/coders/obj.cpp create mode 100644 src/coders/obj.hpp diff --git a/src/coders/commons.cpp b/src/coders/commons.cpp index 1bc111c2..d8d61873 100644 --- a/src/coders/commons.cpp +++ b/src/coders/commons.cpp @@ -163,6 +163,28 @@ void BasicParser::goBack(size_t count) { } } +void BasicParser::reset() { + pos = 0; +} + +char BasicParser::peekInLine() { + while (hasNext()) { + char next = source[pos]; + if (next == '\n') { + return next; + } + if (is_whitespace(next)) { + pos++; + } else { + break; + } + } + if (pos >= source.length()) { + throw error("unexpected end"); + } + return source[pos]; +} + char BasicParser::peek() { skipWhitespace(); if (pos >= source.length()) { @@ -226,6 +248,19 @@ int64_t BasicParser::parseSimpleInt(int base) { return value; } +dynamic::Value BasicParser::parseNumber() { + switch (peek()) { + case '-': + skip(1); + return parseNumber(-1); + case '+': + skip(1); + return parseNumber(1); + default: + return parseNumber(1); + } +} + dynamic::Value BasicParser::parseNumber(int sign) { char c = peek(); int base = 10; diff --git a/src/coders/commons.hpp b/src/coders/commons.hpp index f8f1b0e9..7b6cb3c2 100644 --- a/src/coders/commons.hpp +++ b/src/coders/commons.hpp @@ -87,9 +87,11 @@ protected: bool isNext(const std::string& substring); void expectNewLine(); void goBack(size_t count=1); + void reset(); int64_t parseSimpleInt(int base); dynamic::Value parseNumber(int sign); + dynamic::Value parseNumber(); std::string parseString(char chr, bool closeRequired=true); parsing_error error(const std::string& message); @@ -99,6 +101,7 @@ public: std::string parseName(); bool hasNext(); char peek(); + char peekInLine(); char peekNoJump(); char nextChar(); diff --git a/src/coders/json.cpp b/src/coders/json.cpp index 19b59726..dbe42b5a 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -209,9 +209,8 @@ std::unique_ptr Parser::parseList() { Value Parser::parseValue() { char next = peek(); - if (next == '-' || next == '+') { - pos++; - return parseNumber(next == '-' ? -1 : 1); + if (next == '-' || next == '+' || is_digit(next)) { + return parseNumber(); } if (is_identifier_start(next)) { std::string literal = parseName(); @@ -232,9 +231,6 @@ Value Parser::parseValue() { if (next == '[') { return List_sptr(parseList().release()); } - if (is_digit(next)) { - return parseNumber(1); - } if (next == '"' || next == '\'') { pos++; return parseString(next); diff --git a/src/coders/obj.cpp b/src/coders/obj.cpp new file mode 100644 index 00000000..3ecea677 --- /dev/null +++ b/src/coders/obj.cpp @@ -0,0 +1,124 @@ +#include "obj.hpp" + +#include "commons.hpp" +#include "../graphics/core/Model.hpp" + +using namespace model; + +class ObjParser : BasicParser { + std::vector coords {{0, 0, 0}}; + std::vector uvs {{0, 0}}; + std::vector normals {{0, 1, 0}}; + + void parseFace(Mesh& mesh) { + std::vector vertices; + while (hasNext()) { + auto c = peekInLine(); + if (c == '\n') { + break; + } else { + uint indices[3] {}; + uint i = 0; + do { + char next = peekInLine(); + if (is_digit(next)) { + indices[i] = parseSimpleInt(10); + if (peekInLine() == '/') { + pos++; + } + } else if (next == '/') { + pos++; + } else { + break; + } + } while (peekInLine() != '\n' && ++i < 3); + + vertices.push_back(Vertex { + coords[indices[0]], uvs[indices[1]], normals[indices[2]] + }); + } + } + if (peekInLine() != '\n' && hasNext()) { + skipLine(); + } + if (vertices.size() >= 3) { + for (size_t j = 0; j < vertices.size() - 2; j++) { + mesh.vertices.push_back(vertices[0]); + for (size_t i = 1; i < 3; i++) { + mesh.vertices.push_back(vertices[i + j]); + } + } + } + } +public: + ObjParser(const std::string_view file, const std::string_view src) : BasicParser(file, src) { + } + + std::unique_ptr parse() { + // first iteration - collecting vertex data + while (hasNext()) { + if (peek() == '#') { + skipLine(); + continue; + } + auto cmd = parseName(); + if (cmd == "v") { + float x = dynamic::as_number(parseNumber()); + float y = dynamic::as_number(parseNumber()); + float z = dynamic::as_number(parseNumber()); + coords.emplace_back(x, y, z); + } else if (cmd == "vt") { + float u = dynamic::as_number(parseNumber()); + float v = dynamic::as_number(parseNumber()); + uvs.emplace_back(u, v); + } else if (cmd == "vn") { + float x = dynamic::as_number(parseNumber()); + float y = dynamic::as_number(parseNumber()); + float z = dynamic::as_number(parseNumber()); + normals.emplace_back(x, y, z); + } else { + skipLine(); + } + } + // second iteration - building meshes + reset(); + + auto model = std::make_unique(); + std::string texture; + while (hasNext()) { + if (peek() != '#' && parseName() == "usemtl") { + skipWhitespace(); + texture = readUntil('\n'); + break; + } + skipLine(); + } + do { + Mesh* mesh = &model->addMesh(texture); + while (hasNext()) { + if (peek() == '#') { + skipLine(); + continue; + } + auto cmd = parseName(); + if (cmd == "usemtl") { + skipWhitespace(); + texture = readUntil('\n'); + mesh = &model->addMesh(texture); + break; + } else if (cmd == "f") { + parseFace(*mesh); + } + skipLine(); + } + } while(hasNext()); + model->clean(); + return model; + } +}; + +std::unique_ptr obj::parse( + const std::string_view file, const std::string_view src +) { + return ObjParser(file, src).parse(); +} diff --git a/src/coders/obj.hpp b/src/coders/obj.hpp new file mode 100644 index 00000000..64d0f4ce --- /dev/null +++ b/src/coders/obj.hpp @@ -0,0 +1,19 @@ +#ifndef CODERS_OBJ_HPP_ +#define CODERS_OBJ_HPP_ + +#include +#include + +/// Wavefont OBJ files parser + +namespace model { + struct Model; +} + +namespace obj { + std::unique_ptr parse( + const std::string_view file, const std::string_view src + ); +} + +#endif // CODERS_OBJ_HPP_ diff --git a/src/data/dynamic.hpp b/src/data/dynamic.hpp index ff0342df..5dc5dbed 100644 --- a/src/data/dynamic.hpp +++ b/src/data/dynamic.hpp @@ -3,6 +3,7 @@ #include "../typedefs.hpp" +#include #include #include #include @@ -47,6 +48,15 @@ namespace dynamic { std::holds_alternative(value); } + inline number_t as_number(const Value& value) { + if (auto num = std::get_if(&value)) { + return *num; + } else if (auto num = std::get_if(&value)) { + return *num; + } + return NAN; + } + class List { public: std::vector values; diff --git a/src/graphics/core/Model.cpp b/src/graphics/core/Model.cpp index 01d32973..78e15354 100644 --- a/src/graphics/core/Model.cpp +++ b/src/graphics/core/Model.cpp @@ -1,5 +1,7 @@ #include "Model.hpp" +#include + using namespace model; inline constexpr glm::vec3 X(1, 0, 0); @@ -26,3 +28,14 @@ void Mesh::addBox(glm::vec3 pos, glm::vec3 size) { addPlane(pos+X*size, -Z*size, Y*size, X); addPlane(pos-X*size, Z*size, Y*size, -X); } + + +void Model::clean() { + meshes.erase( + std::remove_if(meshes.begin(), meshes.end(), + [](const Mesh& mesh){ + return mesh.vertices.empty(); + }), + meshes.end() + ); +} diff --git a/src/graphics/core/Model.hpp b/src/graphics/core/Model.hpp index 45ea94b9..03d00826 100644 --- a/src/graphics/core/Model.hpp +++ b/src/graphics/core/Model.hpp @@ -23,10 +23,15 @@ namespace model { struct Model { std::vector meshes; + /// @brief Add mesh to the model + /// @param texture texture name + /// @return writeable Mesh Mesh& addMesh(const std::string& texture) { meshes.push_back({texture, {}}); return meshes[meshes.size()-1]; } + /// @brief Remove all empty meshes + void clean(); }; } diff --git a/src/graphics/render/ModelBatch.cpp b/src/graphics/render/ModelBatch.cpp index dc269012..4bc1be32 100644 --- a/src/graphics/render/ModelBatch.cpp +++ b/src/graphics/render/ModelBatch.cpp @@ -66,13 +66,19 @@ void ModelBatch::draw(const model::Model& model) { } else { blank->bind(); } - for (const auto& vert : mesh.vertices) { - auto norm = rotation * vert.normal; - float d = glm::dot(norm, SUN_VECTOR); - d = 0.8f + d * 0.2f; - - auto color = lights * d; - vertex(vert.coord, vert.uv, color); + for (size_t i = 0; i < mesh.vertices.size() / 3; i++) { + if (index + VERTEX_SIZE * 3 > capacity) { + flush(); + } + for (size_t j = 0; j < 3; j++) { + const auto& vert = mesh.vertices[i * 3 + j]; + auto norm = rotation * vert.normal; + float d = glm::dot(norm, SUN_VECTOR); + d = 0.8f + d * 0.2f; + + auto color = lights * d; + vertex(vert.coord, vert.uv, color); + } } flush(); } diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 7b3a6120..efb03d80 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -7,10 +7,12 @@ #include "../../assets/Assets.hpp" #include "../../content/Content.hpp" #include "../../engine.hpp" +#include "../../coders/obj.hpp" #include "../../frontend/LevelFrontend.hpp" #include "../../items/Inventory.hpp" #include "../../items/ItemDef.hpp" #include "../../items/ItemStack.hpp" +#include "../../files/files.hpp" #include "../../logic/PlayerController.hpp" #include "../../maths/FrustumCulling.hpp" #include "../../maths/voxmaths.hpp" @@ -70,6 +72,10 @@ WorldRenderer::WorldRenderer(Engine* engine, LevelFrontend* frontend, Player* pl settings.graphics.skyboxResolution.get(), assets->getShader("skybox_gen") ); + + auto name = "dingus.obj"; + auto text = files::read_string(fs::path(name)); + model = obj::parse(name, text); } WorldRenderer::~WorldRenderer() { @@ -194,23 +200,13 @@ void WorldRenderer::renderLevel( drawChunks(level->chunks.get(), camera, shader); - model::Model model {}; - auto& mesh = model.addMesh("gui/warning"); - mesh.addBox({}, glm::vec3(0.3f)); - mesh.addBox({}, glm::vec3(0.6f)); - - auto& mesh2 = model.addMesh("gui/error"); - mesh2.addBox({}, glm::vec3(0.7f)); - mesh2.addBox({}, glm::vec3(0.9f)); - float timer = static_cast(Window::time()); - assets->getTexture("gui/menubg")->bind(); shader->uniformMatrix("u_model", glm::mat4(1.0f)); - modelBatch->translate({0, 86, 0}); - modelBatch->scale(glm::vec3(glm::sin(timer*6)+1)); - modelBatch->rotate(glm::vec3(1, 0, 0), timer); - modelBatch->draw(model); - modelBatch->popMatrix(); + modelBatch->translate({0, 85, 0}); + //modelBatch->scale(glm::vec3(glm::sin(timer*6)+10)); + modelBatch->rotate(glm::vec3(0, 1, 0), timer*6); + modelBatch->draw(*model); + //modelBatch->popMatrix(); modelBatch->popMatrix(); modelBatch->popMatrix(); diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index 2f3517e1..b6b5dd27 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -26,6 +26,10 @@ class DrawContext; class ModelBatch; struct EngineSettings; +namespace model { + struct Model; +} + class WorldRenderer { Engine* engine; Level* level; @@ -36,6 +40,8 @@ class WorldRenderer { std::unique_ptr skybox; std::unique_ptr batch3d; std::unique_ptr modelBatch; + + std::unique_ptr model; bool drawChunk(size_t index, Camera* camera, Shader* shader, bool culling); void drawChunks(Chunks* chunks, Camera* camera, Shader* shader);