Program Listing for File cube.hpp

Return to documentation for file (inexor/vulkan-renderer/world/cube.hpp)

#pragma once

#include "inexor/vulkan-renderer/world/indentation.hpp"

#include <glm/gtx/vector_angle.hpp>
#include <glm/vec3.hpp>

#include <array>
#include <functional>
#include <memory>
#include <optional>
#include <vector>

// Forward declaration
namespace inexor::vulkan_renderer::world {
class Cube;
} // namespace inexor::vulkan_renderer::world

// Forward declarations
namespace inexor::vulkan_renderer::io {
class ByteStream;
class NXOCParser;
} // namespace inexor::vulkan_renderer::io

void swap(inexor::vulkan_renderer::world::Cube &lhs, inexor::vulkan_renderer::world::Cube &rhs) noexcept;

namespace inexor::vulkan_renderer::world {

using Polygon = std::array<glm::vec3, 3>;

using PolygonCache = std::shared_ptr<std::vector<Polygon>>;

class Cube : public std::enable_shared_from_this<Cube> {
    friend void ::swap(Cube &lhs, Cube &rhs) noexcept;
    friend class io::NXOCParser;

public:
    static constexpr std::size_t SUB_CUBES{8};
    static constexpr std::size_t EDGES{12};
    enum class Type { EMPTY = 0b00u, SOLID = 0b01u, NORMAL = 0b10u, OCTANT = 0b11u };
    enum class NeighborAxis { X = 2, Y = 1, Z = 0 };
    enum class NeighborDirection { POSITIVE, NEGATIVE };

    struct RotationAxis {
        using ChildType = std::array<std::array<std::size_t, 4>, 2>;
        using EdgeType = std::array<std::array<std::size_t, 4>, 3>;
        using Type = std::pair<ChildType, EdgeType>;
        static constexpr Type X{{{{0, 1, 3, 2}, {4, 5, 7, 6}}}, {{{2, 4, 11, 1}, {5, 7, 8, 10}, {0, 9, 6, 3}}}};
        static constexpr Type Y{{{{0, 4, 5, 1}, {2, 6, 7, 3}}}, {{{0, 5, 9, 2}, {3, 8, 6, 11}, {1, 10, 7, 4}}}};
        static constexpr Type Z{{{{0, 2, 6, 4}, {1, 3, 7, 5}}}, {{{1, 3, 10, 0}, {4, 6, 7, 9}, {2, 11, 8, 5}}}};
    };

private:
    Type m_type{Type::EMPTY};
    float m_size{32};
    glm::vec3 m_position{0.0f, 0.0f, 0.0f};

    std::weak_ptr<Cube> m_parent{};

    std::uint8_t m_index_in_parent{};

    std::array<Indentation, Cube::EDGES> m_indentations;
    std::array<std::shared_ptr<Cube>, Cube::SUB_CUBES> m_children;

    mutable PolygonCache m_polygon_cache;
    mutable bool m_polygon_cache_valid{false};

    void remove_children();

    [[nodiscard]] std::shared_ptr<Cube> root();
    [[nodiscard]] std::array<glm::vec3, 8> vertices() const noexcept;

    template <int Rotations>
    void rotate(const RotationAxis::Type &axis);

public:
    Cube() = default;
    Cube(float size, const glm::vec3 &position);
    Cube(std::weak_ptr<Cube> parent, std::uint8_t index, float size, const glm::vec3 &position);
    Cube(const Cube &rhs) = delete;
    Cube(Cube &&rhs) noexcept;
    ~Cube() = default;
    Cube &operator=(Cube rhs);
    Cube &operator=(Cube &&) = delete;

    std::shared_ptr<Cube> operator[](std::size_t idx);
    std::shared_ptr<const Cube> operator[](std::size_t idx) const; // NOLINT

    [[nodiscard]] std::shared_ptr<Cube> clone() const;

    [[nodiscard]] bool is_root() const noexcept;
    [[nodiscard]] std::size_t grid_level() const noexcept;
    [[nodiscard]] std::size_t count_geometry_cubes() const noexcept;

    [[nodiscard]] glm::vec3 center() const noexcept {
        return m_position + 0.5f * m_size;
    }

    [[nodiscard]] glm::vec3 position() const noexcept {
        return m_position;
    }

    [[nodiscard]] std::array<glm::vec3, 2> bounding_box() const {
        return {m_position, {m_position.x + m_size, m_position.y + m_size, m_position.z + m_size}};
    }

    [[nodiscard]] float size() const noexcept {
        return m_size;
    }

    void set_type(Type new_type);
    [[nodiscard]] Type type() const noexcept;

    [[nodiscard]] const std::array<std::shared_ptr<Cube>, Cube::SUB_CUBES> &children() const;
    [[nodiscard]] std::array<Indentation, Cube::EDGES> indentations() const noexcept;

    void set_indent(std::uint8_t edge_id, Indentation indentation);
    void indent(std::uint8_t edge_id, bool positive_direction, std::uint8_t steps);

    void rotate(const RotationAxis::Type &axis, int rotations);

    void update_polygon_cache() const;
    void invalidate_polygon_cache() const;
    [[nodiscard]] std::vector<PolygonCache> polygons(bool update_invalid = false) const;

    [[nodiscard]] std::shared_ptr<Cube> neighbor(NeighborAxis axis, NeighborDirection direction);
};

std::shared_ptr<world::Cube> create_random_world(std::uint32_t max_depth, const glm::vec3 &position,
                                                 std::optional<std::uint32_t> seed = std::nullopt);

} // namespace inexor::vulkan_renderer::world