diff --git a/src/coders/vcm.cpp b/src/coders/vcm.cpp index 5bf54ee99..73da37401 100644 --- a/src/coders/vcm.cpp +++ b/src/coders/vcm.cpp @@ -1,17 +1,19 @@ #include "vcm.hpp" -#include -#include -#include -#include - #include "xml.hpp" #include "util/stringutil.hpp" #include "graphics/commons/Model.hpp" #include "io/io.hpp" +#include +#include +#include +#include +#include + using namespace vcm; using namespace xml; +using namespace model; static const std::unordered_map side_indices { {"north", 0}, @@ -26,7 +28,87 @@ static bool to_boolean(const xml::Attribute& attr) { return attr.getText() != "off"; } -static void perform_rect(const xmlelement& root, model::Model& model) { +class ModelBuilder { +public: + ModelBuilder(Model& model) : model(model) {} + + void push(const glm::mat4& matrix) { + matrices.push_back(matrix); + calculateMatrix(); + } + + void pop() { + matrices.pop_back(); + calculateMatrix(); + } + + void addBox( + const std::string& texture, + bool shading, + const glm::vec3& pos, + const glm::vec3& size, + const UVRegion (&uvs)[6], + const bool enabledSides[6] + ) { + auto& mesh = model.addMesh(texture, shading); + mesh.addBox(pos, size, uvs, enabledSides, combined); + } + + void addTriangle( + const std::string& texture, + bool shading, + const glm::vec3& a, + const glm::vec3& b, + const glm::vec3& c, + const glm::vec3& norm, + const glm::vec2& uvA, + const glm::vec2& uvB, + const glm::vec2& uvC + ) { + auto& mesh = model.addMesh(texture, shading); + mesh.addTriangle( + combined * glm::vec4(a, 1.0f), + combined * glm::vec4(b, 1.0f), + combined * glm::vec4(c, 1.0f), + norm, + uvA, + uvB, + uvC + ); + } + + void addRect( + const std::string& texture, + bool shading, + const glm::vec3& pos, + const glm::vec3& right, + const glm::vec3& up, + const glm::vec3& norm, + const UVRegion& uv + ) { + auto& mesh = model.addMesh(texture, shading); + mesh.addRect(pos, right, up, norm, uv, combined); + } + + Model& getModel() { + return model; + } +private: + void calculateMatrix() { + combined = glm::mat4(1.0f); + for (const auto& matrix : matrices) { + combined *= matrix; + } + } + + Model& model; + std::vector matrices; + glm::mat4 combined {1.0f}; +}; + +static void perform_element(const xmlelement& root, ModelBuilder& builder); + +static void perform_rect(const xmlelement& root, ModelBuilder& builder) { auto from = root.attr("from").asVec3(); auto right = root.attr("right").asVec3(); auto up = root.attr("up").asVec3(); @@ -60,10 +142,11 @@ static void perform_rect(const xmlelement& root, model::Model& model) { from -= up; } std::string texture = root.attr("texture", "$0").getText(); - auto& mesh = model.addMesh(texture, shading); auto normal = glm::cross(glm::normalize(right), glm::normalize(up)); - mesh.addRect( + builder.addRect( + texture, + shading, from + right * 0.5f + up * 0.5f, right * 0.5f, up * 0.5f, @@ -72,7 +155,7 @@ static void perform_rect(const xmlelement& root, model::Model& model) { ); } -static void perform_triangle(const xmlelement& root, model::Model& model) { +static void perform_triangle(const xmlelement& root, ModelBuilder& builder) { auto pointA = root.attr("a").asVec3(); auto pointB = root.attr("b").asVec3(); auto pointC = root.attr("c").asVec3(); @@ -104,11 +187,10 @@ static void perform_triangle(const xmlelement& root, model::Model& model) { } std::string texture = root.attr("texture", "$0").getText(); - auto& mesh = model.addMesh(texture, shading); - mesh.addTriangle(pointA, pointB, pointC, normal, uvs[0], uvs[1], uvs[2]); + builder.addTriangle(texture, shading, pointA, pointB, pointC, normal, uvs[0], uvs[1], uvs[2]); } -static void perform_box(const xmlelement& root, model::Model& model) { +static void perform_box(const xmlelement& root, ModelBuilder& builder) { auto from = root.attr("from").asVec3(); auto to = root.attr("to").asVec3(); @@ -186,7 +268,7 @@ static void perform_box(const xmlelement& root, model::Model& model) { bool deleted[6] {}; if (root.has("delete")) { - // todo: replace by expression parsing + // todo: replace with expression parsing auto names = util::split(root.attr("delete").getText(), ','); for (auto& name : names) { util::trim(name); @@ -197,30 +279,37 @@ static void perform_box(const xmlelement& root, model::Model& model) { } } + builder.push(tsf); for (int i = 0; i < 6; i++) { if (deleted[i]) { continue; } bool enabled[6] {}; enabled[i] = true; - auto& mesh = model.addMesh(texfaces[i], shading); - mesh.addBox(center, halfsize, regions, enabled, tsf); + + builder.addBox(texfaces[i], shading, center, halfsize, regions, enabled); + } + builder.pop(); +} + +static void perform_element(const xmlelement& root, ModelBuilder& builder) { + auto tag = root.getTag(); + + if (tag == "rect") { + perform_rect(root, builder); + } else if (tag == "box") { + perform_box(root, builder); + } else if (tag == "tri") { + perform_triangle(root, builder); } } static std::unique_ptr load_model(const xmlelement& root) { - model::Model model; + Model model; + ModelBuilder builder(model); for (const auto& elem : root.getElements()) { - auto tag = elem->getTag(); - - if (tag == "rect") { - perform_rect(*elem, model); - } else if (tag == "box") { - perform_box(*elem, model); - } else if (tag == "tri") { - perform_triangle(*elem, model); - } + perform_element(*elem, builder); } return std::make_unique(std::move(model)); diff --git a/src/graphics/commons/Model.cpp b/src/graphics/commons/Model.cpp index e7c247868..8889400d4 100644 --- a/src/graphics/commons/Model.cpp +++ b/src/graphics/commons/Model.cpp @@ -1,6 +1,10 @@ +#define GLM_ENABLE_EXPERIMENTAL + #include "Model.hpp" #include +#include +#include using namespace model; @@ -71,6 +75,39 @@ void Mesh::addRect( vertices.push_back({pos-right+up, {uv.u1, uv.v2}, norm}); } +static glm::mat4 extract_rotation(const glm::mat4& matrix) { + glm::vec3 scale; + glm::quat rotation; + glm::vec3 translation; + glm::vec3 skew; + glm::vec4 perspective; + glm::decompose(matrix, scale, rotation, translation, skew, perspective); + return glm::mat4_cast(rotation); +} + +void Mesh::addRect( + const glm::vec3& pos, + const glm::vec3& right, + const glm::vec3& up, + const glm::vec3& norm, + const UVRegion& uv, + const glm::mat4& transform +) { + auto a = transform * glm::vec4(pos - right - up, 1.0f); + auto b = transform * glm::vec4(pos + right - up, 1.0f); + auto c = transform * glm::vec4(pos + right + up, 1.0f); + auto d = transform * glm::vec4(pos - right + up, 1.0f); + auto tsfnorm = extract_rotation(transform) * glm::vec4(norm, 1.0f); + + vertices.push_back({a, {uv.u1, uv.v1}, tsfnorm}); + vertices.push_back({b, {uv.u2, uv.v1}, tsfnorm}); + vertices.push_back({c, {uv.u2, uv.v2}, tsfnorm}); + + vertices.push_back({a, {uv.u1, uv.v1}, tsfnorm}); + vertices.push_back({c, {uv.u2, uv.v2}, tsfnorm}); + vertices.push_back({d, {uv.u1, uv.v2}, tsfnorm}); +} + void Mesh::addBox(const glm::vec3& pos, const glm::vec3& size) { UVRegion fullRegion (0, 0, 1, 1); addPlane(pos+Z*size, X*size, Y*size, Z, fullRegion); diff --git a/src/graphics/commons/Model.hpp b/src/graphics/commons/Model.hpp index 67618ed38..ac330c378 100644 --- a/src/graphics/commons/Model.hpp +++ b/src/graphics/commons/Model.hpp @@ -40,6 +40,14 @@ namespace model { const glm::vec3& norm, const UVRegion& region ); + void addRect( + const glm::vec3& pos, + const glm::vec3& right, + const glm::vec3& up, + const glm::vec3& norm, + const UVRegion& uv, + const glm::mat4& transform + ); void addTriangle( const glm::vec3& a, const glm::vec3& b,