diff --git a/headers/3rdparty/ecs.hpp/ecs.hpp b/headers/3rdparty/ecs.hpp/ecs.hpp deleted file mode 100644 index faa1969b..00000000 --- a/headers/3rdparty/ecs.hpp/ecs.hpp +++ /dev/null @@ -1,2544 +0,0 @@ -/******************************************************************************* - * This file is part of the "https://github.com/blackmatov/ecs.hpp" - * For conditions of distribution and use, see copyright notice in LICENSE.md - * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) - ******************************************************************************/ - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// ----------------------------------------------------------------------------- -// -// config -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - class entity; - class const_entity; - - template < typename T > - class component; - template < typename T > - class const_component; - - class prototype; - - class system; - class registry; - - class entity_filler; - class registry_filler; - - using family_id = std::uint16_t; - using entity_id = std::uint32_t; - using priority_t = std::int32_t; - - constexpr std::size_t entity_id_index_bits = 22u; - constexpr std::size_t entity_id_version_bits = 10u; - - static_assert( - std::is_unsigned::value, - "ecs_hpp (family_id must be an unsigned integer)"); - - static_assert( - std::is_unsigned::value, - "ecs_hpp (entity_id must be an unsigned integer)"); - - static_assert( - entity_id_index_bits > 0u && - entity_id_version_bits > 0u && - sizeof(entity_id) == (entity_id_index_bits + entity_id_version_bits) / 8u, - "ecs_hpp (invalid entity id index and version bits)"); -} - -// ----------------------------------------------------------------------------- -// -// utilities -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - namespace detail - { - // - // as_const - // - - template < typename T > - constexpr std::add_const_t& as_const(T& t) noexcept { - return t; - } - - // - // hash_combine - // - - constexpr std::size_t hash_combine(std::size_t l, std::size_t r) noexcept { - return l ^ (r + 0x9e3779b9 + (l << 6) + (l >> 2)); - } - - // - // tuple_tail - // - - namespace impl - { - template < typename T, typename... Ts, std::size_t... Is > - std::tuple tuple_tail_impl( - std::tuple&& t, - std::index_sequence iseq) - { - (void)iseq; - return std::make_tuple(std::move(std::get(t))...); - } - - template < typename T, typename... Ts, std::size_t... Is > - std::tuple tuple_tail_impl( - const std::tuple& t, - std::index_sequence iseq) - { - (void)iseq; - return std::make_tuple(std::get(t)...); - } - } - - template < typename T, typename... Ts > - std::tuple tuple_tail(std::tuple&& t) { - return impl::tuple_tail_impl( - std::move(t), - std::make_index_sequence()); - } - - template < typename T, typename... Ts > - std::tuple tuple_tail(const std::tuple& t) { - return impl::tuple_tail_impl( - t, - std::make_index_sequence()); - } - - // - // tuple_contains - // - - namespace impl - { - template < size_t I, typename V, typename... Ts > - std::enable_if_t - tuple_contains_impl(const std::tuple& t, const V& v) { - (void)t; - (void)v; - return false; - } - - template < size_t I, typename V, typename... Ts > - std::enable_if_t - tuple_contains_impl(const std::tuple& t, const V& v) { - if ( std::get(t) == v ) { - return true; - } - return tuple_contains_impl(t, v); - } - } - - template < typename V, typename... Ts > - bool tuple_contains(const std::tuple& t, const V& v) { - return impl::tuple_contains_impl<0>(t, v); - } - - // - // tiny_tuple_apply - // - - namespace impl - { - template < typename F, typename Tuple, std::size_t... I > - void tiny_tuple_apply_impl(F&& f, Tuple&& args, std::index_sequence) { - std::forward(f)(std::get(std::forward(args))...); - } - } - - template < typename F, typename Tuple > - void tiny_tuple_apply(F&& f, Tuple&& args) { - impl::tiny_tuple_apply_impl( - std::forward(f), - std::forward(args), - std::make_index_sequence>::value>()); - } - - // - // next_capacity_size - // - - inline std::size_t next_capacity_size( - std::size_t cur_size, - std::size_t min_size, - std::size_t max_size) - { - if ( min_size > max_size ) { - throw std::length_error("ecs_hpp::next_capacity_size"); - } - if ( cur_size >= max_size / 2u ) { - return max_size; - } - return std::max(cur_size * 2u, min_size); - } - - // - // entity_id index/version - // - - constexpr std::size_t entity_id_index_mask = (1u << entity_id_index_bits) - 1u; - constexpr std::size_t entity_id_version_mask = (1u << entity_id_version_bits) - 1u; - - constexpr inline entity_id entity_id_index(entity_id id) noexcept { - return id & entity_id_index_mask; - } - - constexpr inline entity_id entity_id_version(entity_id id) noexcept { - return (id >> entity_id_index_bits) & entity_id_version_mask; - } - - constexpr inline entity_id entity_id_join(entity_id index, entity_id version) noexcept { - return index | (version << entity_id_index_bits); - } - - constexpr inline entity_id upgrade_entity_id(entity_id id) noexcept { - return entity_id_join( - entity_id_index(id), - entity_id_version(id) + 1u); - } - } -} - -// ----------------------------------------------------------------------------- -// -// detail::type_family -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - namespace detail - { - template < typename Void = void > - class type_family_base { - static_assert( - std::is_void::value, - "unexpected internal error"); - protected: - static family_id last_id_; - }; - - template < typename T > - class type_family final : public type_family_base<> { - public: - static family_id id() noexcept { - static family_id self_id = ++last_id_; - assert(self_id > 0u && "ecs_hpp::family_id overflow"); - return self_id; - } - }; - - template < typename Void > - family_id type_family_base::last_id_ = 0u; - } -} - -// ----------------------------------------------------------------------------- -// -// detail::sparse_indexer -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - namespace detail - { - template < typename T - , bool = std::is_unsigned::value - && sizeof(T) <= sizeof(std::size_t) > - struct sparse_unsigned_indexer { - std::size_t operator()(const T v) const noexcept { - return static_cast(v); - } - }; - - template < typename T > - struct sparse_unsigned_indexer {}; - - template < typename T > - struct sparse_indexer final - : public sparse_unsigned_indexer {}; - } -} - -// ----------------------------------------------------------------------------- -// -// detail::sparse_set -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - namespace detail - { - template < typename T - , typename Indexer = sparse_indexer > - class sparse_set final { - public: - using iterator = typename std::vector::iterator; - using const_iterator = typename std::vector::const_iterator; - public: - static_assert( - noexcept(std::declval()(std::declval())), - "unsupported sparse_set indexer"); - static_assert( - std::is_nothrow_destructible::value && - std::is_nothrow_move_assignable::value && - noexcept(std::declval() == std::declval()), - "unsupported sparse_set value type"); - public: - iterator begin() noexcept { - return dense_.begin(); - } - - iterator end() noexcept { - return dense_.end(); - } - - const_iterator begin() const noexcept { - return dense_.begin(); - } - - const_iterator end() const noexcept { - return dense_.end(); - } - - const_iterator cbegin() const noexcept { - return dense_.cbegin(); - } - - const_iterator cend() const noexcept { - return dense_.cend(); - } - public: - sparse_set(const Indexer& indexer = Indexer()) - : indexer_(indexer) {} - - sparse_set(const sparse_set& other) = default; - sparse_set& operator=(const sparse_set& other) = default; - - sparse_set(sparse_set&& other) noexcept = default; - sparse_set& operator=(sparse_set&& other) noexcept = default; - - void swap(sparse_set& other) noexcept { - using std::swap; - swap(dense_, other.dense_); - swap(sparse_, other.sparse_); - } - - template < typename UT > - bool insert(UT&& v) { - if ( has(v) ) { - return false; - } - const std::size_t vi = indexer_(v); - if ( vi >= sparse_.size() ) { - sparse_.resize(next_capacity_size( - sparse_.size(), vi + 1u, sparse_.max_size())); - } - dense_.push_back(std::forward(v)); - sparse_[vi] = dense_.size() - 1u; - return true; - } - - template < typename... Args > - bool emplace(Args&&... args) { - return insert(T(std::forward(args)...)); - } - - bool unordered_erase(const T& v) noexcept { - if ( !has(v) ) { - return false; - } - const std::size_t vi = indexer_(v); - const std::size_t dense_index = sparse_[vi]; - if ( dense_index != dense_.size() - 1 ) { - dense_[dense_index] = std::move(dense_.back()); - sparse_[indexer_(dense_[dense_index])] = dense_index; - } - dense_.pop_back(); - return true; - } - - void clear() noexcept { - dense_.clear(); - } - - bool has(const T& v) const noexcept { - const std::size_t vi = indexer_(v); - return vi < sparse_.size() - && sparse_[vi] < dense_.size() - && dense_[sparse_[vi]] == v; - } - - const_iterator find(const T& v) const noexcept { - return has(v) - ? begin() + sparse_[indexer_(v)] - : end(); - } - - std::size_t get_dense_index(const T& v) const { - const auto p = find_dense_index(v); - if ( p.second ) { - return p.first; - } - throw std::logic_error("ecs_hpp::sparse_set (value not found)"); - } - - std::pair find_dense_index(const T& v) const noexcept { - return has(v) - ? std::make_pair(sparse_[indexer_(v)], true) - : std::make_pair(std::size_t(-1), false); - } - - bool empty() const noexcept { - return dense_.empty(); - } - - std::size_t size() const noexcept { - return dense_.size(); - } - - std::size_t memory_usage() const noexcept { - return dense_.capacity() * sizeof(dense_[0]) - + sparse_.capacity() * sizeof(sparse_[0]); - } - private: - Indexer indexer_; - std::vector dense_; - std::vector sparse_; - }; - - template < typename T - , typename Indexer > - void swap( - sparse_set& l, - sparse_set& r) noexcept - { - l.swap(r); - } - } -} - -// ----------------------------------------------------------------------------- -// -// detail::sparse_map -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - namespace detail - { - template < typename K - , typename T - , typename Indexer = sparse_indexer > - class sparse_map final { - public: - using iterator = typename std::vector::iterator; - using const_iterator = typename std::vector::const_iterator; - public: - static_assert( - std::is_nothrow_destructible::value && - std::is_nothrow_move_assignable::value, - "unsupported sparse_map value type"); - public: - iterator begin() noexcept { - return keys_.begin(); - } - - iterator end() noexcept { - return keys_.end(); - } - - const_iterator begin() const noexcept { - return keys_.begin(); - } - - const_iterator end() const noexcept { - return keys_.end(); - } - - const_iterator cbegin() const noexcept { - return keys_.cbegin(); - } - - const_iterator cend() const noexcept { - return keys_.cend(); - } - public: - sparse_map(const Indexer& indexer = Indexer()) - : keys_(indexer) {} - - sparse_map(const sparse_map& other) = default; - sparse_map& operator=(const sparse_map& other) = default; - - sparse_map(sparse_map&& other) noexcept = default; - sparse_map& operator=(sparse_map&& other) noexcept = default; - - void swap(sparse_map& other) noexcept { - using std::swap; - swap(keys_, other.keys_); - swap(values_, other.values_); - } - - template < typename UK, typename UT > - bool insert(UK&& k, UT&& v) { - if ( keys_.has(k) ) { - return false; - } - values_.push_back(std::forward(v)); - try { - return keys_.insert(std::forward(k)); - } catch (...) { - values_.pop_back(); - throw; - } - } - - template < typename UK, typename... Args > - bool emplace(UK&& k, Args&&... args) { - if ( keys_.has(k) ) { - return false; - } - values_.emplace_back(std::forward(args)...); - try { - return keys_.insert(std::forward(k)); - } catch (...) { - values_.pop_back(); - throw; - } - } - - template < typename UK, typename UT > - bool insert_or_assign(UK&& k, UT&& v) { - if ( keys_.has(k) ) { - get(k) = std::forward(v); - return false; - } else { - insert(std::forward(k), std::forward(v)); - return true; - } - } - - bool unordered_erase(const K& k) noexcept { - const auto value_index_p = keys_.find_dense_index(k); - if ( !value_index_p.second ) { - return false; - } - if ( value_index_p.first != values_.size() - 1 ) { - values_[value_index_p.first] = std::move(values_.back()); - } - values_.pop_back(); - keys_.unordered_erase(k); - return true; - } - - void clear() noexcept { - keys_.clear(); - values_.clear(); - } - - bool has(const K& k) const noexcept { - return keys_.has(k); - } - - T& get(const K& k) { - return values_[keys_.get_dense_index(k)]; - } - - const T& get(const K& k) const { - return values_[keys_.get_dense_index(k)]; - } - - T* find(const K& k) noexcept { - const auto value_index_p = keys_.find_dense_index(k); - return value_index_p.second - ? &values_[value_index_p.first] - : nullptr; - } - - const T* find(const K& k) const noexcept { - const auto value_index_p = keys_.find_dense_index(k); - return value_index_p.second - ? &values_[value_index_p.first] - : nullptr; - } - - bool empty() const noexcept { - return values_.empty(); - } - - std::size_t size() const noexcept { - return values_.size(); - } - - std::size_t memory_usage() const noexcept { - return keys_.memory_usage() - + values_.capacity() * sizeof(values_[0]); - } - private: - sparse_set keys_; - std::vector values_; - }; - - template < typename K - , typename T - , typename Indexer > - void swap( - sparse_map& l, - sparse_map& r) noexcept - { - l.swap(r); - } - } -} - -// ----------------------------------------------------------------------------- -// -// detail::entity_id_indexer -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - namespace detail - { - struct entity_id_indexer final { - std::size_t operator()(entity_id id) const noexcept { - return entity_id_index(id); - } - }; - } -} - -// ----------------------------------------------------------------------------- -// -// detail::component_storage -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - namespace detail - { - class component_storage_base { - public: - virtual ~component_storage_base() = default; - virtual bool remove(entity_id id) noexcept = 0; - virtual bool has(entity_id id) const noexcept = 0; - virtual void clone(entity_id from, entity_id to) = 0; - virtual std::size_t memory_usage() const noexcept = 0; - }; - - template < typename T, bool E = std::is_empty::value > - class component_storage final : public component_storage_base { - public: - component_storage(registry& owner) - : owner_(owner) {} - - template < typename... Args > - T& assign(entity_id id, Args&&... args) { - components_.insert_or_assign(id, T(std::forward(args)...)); - return components_.get(id); - } - - bool exists(entity_id id) const noexcept { - return components_.has(id); - } - - bool remove(entity_id id) noexcept override { - return components_.unordered_erase(id); - } - - std::size_t remove_all() noexcept { - const std::size_t count = components_.size(); - components_.clear(); - return count; - } - - T* find(entity_id id) noexcept { - return components_.find(id); - } - - const T* find(entity_id id) const noexcept { - return components_.find(id); - } - - std::size_t count() const noexcept { - return components_.size(); - } - - bool has(entity_id id) const noexcept override { - return components_.has(id); - } - - void clone(entity_id from, entity_id to) override { - const T* c = components_.find(from); - if ( c ) { - components_.insert_or_assign(to, *c); - } - } - - template < typename F > - void for_each_component(F&& f) { - for ( const entity_id id : components_ ) { - f(id, components_.get(id)); - } - } - - template < typename F > - void for_each_component(F&& f) const { - for ( const entity_id id : components_ ) { - f(id, components_.get(id)); - } - } - - std::size_t memory_usage() const noexcept override { - return components_.memory_usage(); - } - private: - registry& owner_; - detail::sparse_map components_; - }; - - template < typename T > - class component_storage final : public component_storage_base { - public: - component_storage(registry& owner) - : owner_(owner) {} - - template < typename... Args > - T& assign(entity_id id, Args&&...) { - components_.insert(id); - return empty_value_; - } - - bool exists(entity_id id) const noexcept { - return components_.has(id); - } - - bool remove(entity_id id) noexcept override { - return components_.unordered_erase(id); - } - - std::size_t remove_all() noexcept { - const std::size_t count = components_.size(); - components_.clear(); - return count; - } - - T* find(entity_id id) noexcept { - return components_.has(id) - ? &empty_value_ - : nullptr; - } - - const T* find(entity_id id) const noexcept { - return components_.has(id) - ? &empty_value_ - : nullptr; - } - - std::size_t count() const noexcept { - return components_.size(); - } - - bool has(entity_id id) const noexcept override { - return components_.has(id); - } - - void clone(entity_id from, entity_id to) override { - if ( components_.has(from) ) { - components_.insert(to); - } - } - - template < typename F > - void for_each_component(F&& f) { - for ( const entity_id id : components_ ) { - f(id, empty_value_); - } - } - - template < typename F > - void for_each_component(F&& f) const { - for ( const entity_id id : components_ ) { - f(id, empty_value_); - } - } - - std::size_t memory_usage() const noexcept override { - return components_.memory_usage(); - } - private: - registry& owner_; - static T empty_value_; - detail::sparse_set components_; - }; - - template < typename T > - T component_storage::empty_value_; - } -} - -// ----------------------------------------------------------------------------- -// -// entity -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - class entity final { - public: - entity(registry& owner) noexcept; - entity(registry& owner, entity_id id) noexcept; - - entity(const entity&) = default; - entity& operator=(const entity&) = default; - - entity(entity&&) noexcept = default; - entity& operator=(entity&&) noexcept = default; - - registry& owner() noexcept; - const registry& owner() const noexcept; - - entity_id id() const noexcept; - - entity clone() const; - void destroy() noexcept; - bool valid() const noexcept; - - template < typename T, typename... Args > - T& assign_component(Args&&... args); - - template < typename T > - bool remove_component(); - - template < typename T > - bool exists_component() const noexcept; - - std::size_t remove_all_components() noexcept; - - template < typename T > - T& get_component(); - - template < typename T > - const T& get_component() const; - - template < typename T > - T* find_component() noexcept; - - template < typename T > - const T* find_component() const noexcept; - - template < typename... Ts > - std::tuple get_components(); - template < typename... Ts > - std::tuple get_components() const; - - template < typename... Ts > - std::tuple find_components() noexcept; - template < typename... Ts > - std::tuple find_components() const noexcept; - - std::size_t component_count() const noexcept; - private: - registry* owner_{nullptr}; - entity_id id_{0u}; - }; - - bool operator<(const entity& l, const entity& r) noexcept; - - bool operator==(const entity& l, const entity& r) noexcept; - bool operator==(const entity& l, const const_entity& r) noexcept; - - bool operator!=(const entity& l, const entity& r) noexcept; - bool operator!=(const entity& l, const const_entity& r) noexcept; -} - -namespace std -{ - template <> - struct hash final { - std::size_t operator()(const ecs_hpp::entity& ent) const noexcept { - return ecs_hpp::detail::hash_combine( - std::hash()(&ent.owner()), - std::hash()(ent.id())); - } - }; -} - -// ----------------------------------------------------------------------------- -// -// const_entity -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - class const_entity final { - public: - const_entity(const entity& ent) noexcept; - - const_entity(const registry& owner) noexcept; - const_entity(const registry& owner, entity_id id) noexcept; - - const_entity(const const_entity&) = default; - const_entity& operator=(const const_entity&) = default; - - const_entity(const_entity&&) noexcept = default; - const_entity& operator=(const_entity&&) noexcept = default; - - const registry& owner() const noexcept; - entity_id id() const noexcept; - - bool valid() const noexcept; - - template < typename T > - bool exists_component() const noexcept; - - template < typename T > - const T& get_component() const; - - template < typename T > - const T* find_component() const noexcept; - - template < typename... Ts > - std::tuple get_components() const; - - template < typename... Ts > - std::tuple find_components() const noexcept; - - std::size_t component_count() const noexcept; - private: - const registry* owner_{nullptr}; - entity_id id_{0u}; - }; - - bool operator<(const const_entity& l, const const_entity& r) noexcept; - - bool operator==(const const_entity& l, const entity& r) noexcept; - bool operator==(const const_entity& l, const const_entity& r) noexcept; - - bool operator!=(const const_entity& l, const entity& r) noexcept; - bool operator!=(const const_entity& l, const const_entity& r) noexcept; -} - -namespace std -{ - template <> - struct hash final { - std::size_t operator()(const ecs_hpp::const_entity& ent) const noexcept { - return ecs_hpp::detail::hash_combine( - std::hash()(&ent.owner()), - std::hash()(ent.id())); - } - }; -} - -// ----------------------------------------------------------------------------- -// -// component -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - template < typename T > - class component final { - public: - component(const entity& owner) noexcept; - - component(const component&) = default; - component& operator=(const component&) = default; - - component(component&&) noexcept = default; - component& operator=(component&&) noexcept = default; - - entity& owner() noexcept; - const entity& owner() const noexcept; - - bool remove(); - bool exists() const noexcept; - - template < typename... Args > - T& assign(Args&&... args); - - T& get(); - const T& get() const; - - T* find() noexcept; - const T* find() const noexcept; - - T& operator*(); - const T& operator*() const; - - T* operator->() noexcept; - const T* operator->() const noexcept; - - explicit operator bool() const noexcept; - private: - entity owner_; - }; - - template < typename T > - bool operator<(const component& l, const component& r) noexcept; - - template < typename T > - bool operator==(const component& l, const component& r) noexcept; - template < typename T > - bool operator==(const component& l, const const_component& r) noexcept; - - template < typename T > - bool operator!=(const component& l, const component& r) noexcept; - template < typename T > - bool operator!=(const component& l, const const_component& r) noexcept; -} - -namespace std -{ - template < typename T > - struct hash> { - std::size_t operator()(const ecs_hpp::component& comp) const noexcept { - return std::hash()(comp.owner()); - } - }; -} - -// ----------------------------------------------------------------------------- -// -// const_component -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - template < typename T > - class const_component final { - public: - const_component(const component& comp) noexcept; - const_component(const const_entity& owner) noexcept; - - const_component(const const_component&) = default; - const_component& operator=(const const_component&) = default; - - const_component(const_component&&) noexcept = default; - const_component& operator=(const_component&&) noexcept = default; - - const const_entity& owner() const noexcept; - - bool exists() const noexcept; - - const T& get() const; - const T* find() const noexcept; - - const T& operator*() const; - const T* operator->() const noexcept; - explicit operator bool() const noexcept; - private: - const_entity owner_; - }; - - template < typename T > - bool operator<(const const_component& l, const const_component& r) noexcept; - - template < typename T > - bool operator==(const const_component& l, const component& r) noexcept; - template < typename T > - bool operator==(const const_component& l, const const_component& r) noexcept; - - template < typename T > - bool operator!=(const const_component& l, const component& r) noexcept; - template < typename T > - bool operator!=(const const_component& l, const const_component& r) noexcept; -} - -namespace std -{ - template < typename T > - struct hash> { - std::size_t operator()(const ecs_hpp::const_component& comp) const noexcept { - return std::hash()(comp.owner()); - } - }; -} - -// ----------------------------------------------------------------------------- -// -// prototype -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - namespace detail - { - class applier_base; - using applier_uptr = std::unique_ptr; - - class applier_base { - public: - virtual ~applier_base() = default; - virtual applier_uptr clone() const = 0; - virtual void apply_to_entity(entity& ent, bool override) const = 0; - }; - - template < typename T > - class typed_applier : public applier_base { - public: - virtual void apply_to_component(T& component) const = 0; - }; - - template < typename T, typename... Args > - class typed_applier_with_args final : public typed_applier { - public: - typed_applier_with_args(std::tuple&& args); - typed_applier_with_args(const std::tuple& args); - applier_uptr clone() const override; - void apply_to_entity(entity& ent, bool override) const override; - void apply_to_component(T& component) const override; - private: - std::tuple args_; - }; - } - - class prototype final { - public: - prototype() = default; - ~prototype() noexcept = default; - - prototype(const prototype& other); - prototype& operator=(const prototype& other); - - prototype(prototype&& other) noexcept; - prototype& operator=(prototype&& other) noexcept; - - void clear() noexcept; - bool empty() const noexcept; - void swap(prototype& other) noexcept; - - template < typename T > - bool has_component() const noexcept; - - template < typename T, typename... Args > - prototype& component(Args&&... args) &; - template < typename T, typename... Args > - prototype&& component(Args&&... args) &&; - - prototype& merge_with(const prototype& other, bool override) &; - prototype&& merge_with(const prototype& other, bool override) &&; - - template < typename T > - bool apply_to_component(T& component) const; - void apply_to_entity(entity& ent, bool override) const; - private: - detail::sparse_map< - family_id, - detail::applier_uptr> appliers_; - }; - - void swap(prototype& l, prototype& r) noexcept; -} - -// ----------------------------------------------------------------------------- -// -// system -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - class system { - public: - virtual ~system() = default; - virtual void process(registry& owner) = 0; - }; -} - -// ----------------------------------------------------------------------------- -// -// registry -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - class registry final { - private: - class uentity { - public: - uentity(registry& owner, entity_id id) noexcept; - - uentity(entity_id ent) noexcept; - uentity(entity ent) noexcept; - - operator entity_id() const noexcept; - operator entity() const noexcept; - operator const_entity() const noexcept; - - entity_id id() const noexcept; - registry* owner() noexcept; - const registry* owner() const noexcept; - - bool check_owner(const registry* owner) const noexcept; - private: - entity_id id_{0u}; - registry* owner_{nullptr}; - }; - - class const_uentity { - public: - const_uentity(const registry& owner, entity_id id) noexcept; - - const_uentity(entity_id ent) noexcept; - const_uentity(entity ent) noexcept; - const_uentity(const_entity ent) noexcept; - const_uentity(const uentity& ent) noexcept; - - operator entity_id() const noexcept; - operator const_entity() const noexcept; - - entity_id id() const noexcept; - const registry* owner() const noexcept; - - bool check_owner(const registry* owner) const noexcept; - private: - entity_id id_{0u}; - const registry* owner_{nullptr}; - }; - public: - registry() = default; - ~registry() noexcept = default; - - registry(const registry& other) = delete; - registry& operator=(const registry& other) = delete; - - registry(registry&& other) noexcept = default; - registry& operator=(registry&& other) noexcept = default; - - entity wrap_entity(const const_uentity& ent) noexcept; - const_entity wrap_entity(const const_uentity& ent) const noexcept; - - template < typename T > - component wrap_component(const const_uentity& ent) noexcept; - template < typename T > - const_component wrap_component(const const_uentity& ent) const noexcept; - - entity create_entity(); - entity create_entity(const prototype& proto); - entity create_entity(const const_uentity& proto); - - void destroy_entity(const uentity& ent) noexcept; - bool valid_entity(const const_uentity& ent) const noexcept; - - template < typename T, typename... Args > - T& assign_component(const uentity& ent, Args&&... args); - - template < typename T > - bool remove_component(const uentity& ent); - - template < typename T > - bool exists_component(const const_uentity& ent) const noexcept; - - std::size_t remove_all_components(const uentity& ent) noexcept; - - template < typename T > - std::size_t remove_all_components() noexcept; - - template < typename T > - T& get_component(const uentity& ent); - template < typename T > - const T& get_component(const const_uentity& ent) const; - - template < typename T > - T* find_component(const uentity& ent) noexcept; - template < typename T > - const T* find_component(const const_uentity& ent) const noexcept; - - template < typename... Ts > - std::tuple get_components(const uentity& ent); - template < typename... Ts > - std::tuple get_components(const const_uentity& ent) const; - - template < typename... Ts > - std::tuple find_components(const uentity& ent) noexcept; - template < typename... Ts > - std::tuple find_components(const const_uentity& ent) const noexcept; - - template < typename T > - std::size_t component_count() const noexcept; - std::size_t entity_count() const noexcept; - std::size_t entity_component_count(const const_uentity& ent) const noexcept; - - template < typename F > - void for_each_entity(F&& f); - template < typename F > - void for_each_entity(F&& f) const; - - template < typename T, typename F > - void for_each_component(F&& f); - template < typename T, typename F > - void for_each_component(F&& f) const; - - template < typename... Ts, typename F > - void for_joined_components(F&& f); - template < typename... Ts, typename F > - void for_joined_components(F&& f) const; - - template < typename T, typename... Args > - void add_system(priority_t priority, Args&&... args); - - void process_all_systems(); - void process_systems_above(priority_t min); - void process_systems_below(priority_t max); - void process_systems_in_range(priority_t min, priority_t max); - - struct memory_usage_info { - std::size_t entities{0u}; - std::size_t components{0u}; - }; - memory_usage_info memory_usage() const noexcept; - - template < typename T > - std::size_t component_memory_usage() const noexcept; - private: - template < typename T > - detail::component_storage* find_storage_() noexcept; - - template < typename T > - const detail::component_storage* find_storage_() const noexcept; - - template < typename T > - detail::component_storage& get_or_create_storage_(); - - template < typename T - , typename... Ts - , typename F - , std::size_t I - , std::size_t... Is > - void for_joined_components_impl_( - F&& f, - std::index_sequence iseq); - - template < typename T - , typename... Ts - , typename F - , std::size_t I - , std::size_t... Is > - void for_joined_components_impl_( - F&& f, - std::index_sequence iseq) const; - - template < typename T - , typename... Ts - , typename F - , typename Ss - , typename... Cs > - void for_joined_components_impl_( - const uentity& e, - const F& f, - const Ss& ss, - Cs&... cs); - - template < typename T - , typename... Ts - , typename F - , typename Ss - , typename... Cs > - void for_joined_components_impl_( - const const_uentity& e, - const F& f, - const Ss& ss, - const Cs&... cs) const; - - template < typename F, typename... Cs > - void for_joined_components_impl_( - const uentity& e, - const F& f, - const std::tuple<>& ss, - Cs&... cs); - - template < typename F, typename... Cs > - void for_joined_components_impl_( - const const_uentity& e, - const F& f, - const std::tuple<>& ss, - const Cs&... cs) const; - private: - entity_id last_entity_id_{0u}; - std::vector free_entity_ids_; - detail::sparse_set entity_ids_; - - using storage_uptr = std::unique_ptr; - detail::sparse_map storages_; - - using system_uptr = std::unique_ptr; - std::vector> systems_; - }; -} - -// ----------------------------------------------------------------------------- -// -// fillers -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - class entity_filler final { - public: - entity_filler(entity& entity) noexcept - : entity_(entity) {} - - template < typename T, typename... Args > - entity_filler& component(Args&&... args) { - entity_.assign_component(std::forward(args)...); - return *this; - } - private: - entity& entity_; - }; - - class registry_filler final { - public: - registry_filler(registry& registry) noexcept - : registry_(registry) {} - - template < typename T, typename... Args > - registry_filler& system(priority_t priority, Args&&... args) { - registry_.add_system( - priority, - std::forward(args)...); - return *this; - } - private: - registry& registry_; - }; -} - -// ----------------------------------------------------------------------------- -// -// entity impl -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - inline entity::entity(registry& owner) noexcept - : owner_(&owner) {} - - inline entity::entity(registry& owner, entity_id id) noexcept - : owner_(&owner) - , id_(id) {} - - inline registry& entity::owner() noexcept { - return *owner_; - } - - inline const registry& entity::owner() const noexcept { - return *owner_; - } - - inline entity_id entity::id() const noexcept { - return id_; - } - - inline entity entity::clone() const { - return (*owner_).create_entity(id_); - } - - inline void entity::destroy() noexcept { - (*owner_).destroy_entity(id_); - } - - inline bool entity::valid() const noexcept { - return detail::as_const(*owner_).valid_entity(id_); - } - - template < typename T, typename... Args > - T& entity::assign_component(Args&&... args) { - return (*owner_).assign_component( - id_, - std::forward(args)...); - } - - template < typename T > - bool entity::remove_component() { - return (*owner_).remove_component(id_); - } - - template < typename T > - bool entity::exists_component() const noexcept { - return detail::as_const(*owner_).exists_component(id_); - } - - inline std::size_t entity::remove_all_components() noexcept { - return (*owner_).remove_all_components(id_); - } - - template < typename T > - T& entity::get_component() { - return (*owner_).get_component(id_); - } - - template < typename T > - const T& entity::get_component() const { - return detail::as_const(*owner_).get_component(id_); - } - - template < typename T > - T* entity::find_component() noexcept { - return (*owner_).find_component(id_); - } - - template < typename T > - const T* entity::find_component() const noexcept { - return detail::as_const(*owner_).find_component(id_); - } - - template < typename... Ts > - std::tuple entity::get_components() { - return (*owner_).get_components(id_); - } - - template < typename... Ts > - std::tuple entity::get_components() const { - return detail::as_const(*owner_).get_components(id_); - } - - template < typename... Ts > - std::tuple entity::find_components() noexcept { - return (*owner_).find_components(id_); - } - - template < typename... Ts > - std::tuple entity::find_components() const noexcept { - return detail::as_const(*owner_).find_components(id_); - } - - inline std::size_t entity::component_count() const noexcept { - return detail::as_const(*owner_).entity_component_count(id_); - } - - inline bool operator<(const entity& l, const entity& r) noexcept { - return (&l.owner() < &r.owner()) - || (&l.owner() == &r.owner() && l.id() < r.id()); - } - - inline bool operator==(const entity& l, const entity& r) noexcept { - return &l.owner() == &r.owner() - && l.id() == r.id(); - } - - inline bool operator==(const entity& l, const const_entity& r) noexcept { - return &l.owner() == &r.owner() - && l.id() == r.id(); - } - - inline bool operator!=(const entity& l, const entity& r) noexcept { - return !(l == r); - } - - inline bool operator!=(const entity& l, const const_entity& r) noexcept { - return !(l == r); - } -} - -// ----------------------------------------------------------------------------- -// -// const_entity impl -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - inline const_entity::const_entity(const entity& ent) noexcept - : owner_(&ent.owner()) - , id_(ent.id()) {} - - inline const_entity::const_entity(const registry& owner) noexcept - : owner_(&owner) {} - - inline const_entity::const_entity(const registry& owner, entity_id id) noexcept - : owner_(&owner) - , id_(id) {} - - inline const registry& const_entity::owner() const noexcept { - return *owner_; - } - - inline entity_id const_entity::id() const noexcept { - return id_; - } - - inline bool const_entity::valid() const noexcept { - return (*owner_).valid_entity(id_); - } - - template < typename T > - bool const_entity::exists_component() const noexcept { - return (*owner_).exists_component(id_); - } - - template < typename T > - const T& const_entity::get_component() const { - return (*owner_).get_component(id_); - } - - template < typename T > - const T* const_entity::find_component() const noexcept { - return (*owner_).find_component(id_); - } - - template < typename... Ts > - std::tuple const_entity::get_components() const { - return (*owner_).get_components(id_); - } - - template < typename... Ts > - std::tuple const_entity::find_components() const noexcept { - return (*owner_).find_components(id_); - } - - inline std::size_t const_entity::component_count() const noexcept { - return (*owner_).entity_component_count(id_); - } - - inline bool operator<(const const_entity& l, const const_entity& r) noexcept { - return (&l.owner() < &r.owner()) - || (&l.owner() == &r.owner() && l.id() < r.id()); - } - - inline bool operator==(const const_entity& l, const entity& r) noexcept { - return &l.owner() == &r.owner() - && l.id() == r.id(); - } - - inline bool operator==(const const_entity& l, const const_entity& r) noexcept { - return &l.owner() == &r.owner() - && l.id() == r.id(); - } - - inline bool operator!=(const const_entity& l, const entity& r) noexcept { - return !(l == r); - } - - inline bool operator!=(const const_entity& l, const const_entity& r) noexcept { - return !(l == r); - } -} - -// ----------------------------------------------------------------------------- -// -// component impl -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - template < typename T > - component::component(const entity& owner) noexcept - : owner_(owner) {} - - template < typename T > - entity& component::owner() noexcept { - return owner_; - } - - template < typename T > - const entity& component::owner() const noexcept { - return owner_; - } - - template < typename T > - bool component::remove() { - return owner_.remove_component(); - } - - template < typename T > - bool component::exists() const noexcept { - return owner_.exists_component(); - } - - template < typename T > - template < typename... Args > - T& component::assign(Args&&... args) { - return owner_.assign_component( - std::forward(args)...); - } - - template < typename T > - T& component::get() { - return owner_.get_component(); - } - - template < typename T > - const T& component::get() const { - return detail::as_const(owner_).template get_component(); - } - - template < typename T > - T* component::find() noexcept { - return owner_.find_component(); - } - - template < typename T > - const T* component::find() const noexcept { - return detail::as_const(owner_).template find_component(); - } - - template < typename T > - T& component::operator*() { - return get(); - } - - template < typename T > - const T& component::operator*() const { - return get(); - } - - template < typename T > - T* component::operator->() noexcept { - return find(); - } - - template < typename T > - const T* component::operator->() const noexcept { - return find(); - } - - template < typename T > - component::operator bool() const noexcept { - return exists(); - } - - template < typename T > - bool operator<(const component& l, const component& r) noexcept { - return l.owner() < r.owner(); - } - - template < typename T > - bool operator==(const component& l, const component& r) noexcept { - return l.owner() == r.owner(); - } - - template < typename T > - bool operator==(const component& l, const const_component& r) noexcept { - return l.owner() == r.owner(); - } - - template < typename T > - bool operator!=(const component& l, const component& r) noexcept { - return !(l == r); - } - - template < typename T > - bool operator!=(const component& l, const const_component& r) noexcept { - return !(l == r); - } -} - -// ----------------------------------------------------------------------------- -// -// const_component impl -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - template < typename T > - const_component::const_component(const component& comp) noexcept - : owner_(comp.owner()) {} - - template < typename T > - const_component::const_component(const const_entity& owner) noexcept - : owner_(owner) {} - - template < typename T > - const const_entity& const_component::owner() const noexcept { - return owner_; - } - - template < typename T > - bool const_component::exists() const noexcept { - return detail::as_const(owner_).template exists_component(); - } - - template < typename T > - const T& const_component::get() const { - return detail::as_const(owner_).template get_component(); - } - - template < typename T > - const T* const_component::find() const noexcept { - return detail::as_const(owner_).template find_component(); - } - - template < typename T > - const T& const_component::operator*() const { - return get(); - } - - template < typename T > - const T* const_component::operator->() const noexcept { - return find(); - } - - template < typename T > - const_component::operator bool() const noexcept { - return exists(); - } - - template < typename T > - bool operator<(const const_component& l, const const_component& r) noexcept { - return l.owner() < r.owner(); - } - - template < typename T > - bool operator==(const const_component& l, const component& r) noexcept { - return l.owner() == r.owner(); - } - - template < typename T > - bool operator==(const const_component& l, const const_component& r) noexcept { - return l.owner() == r.owner(); - } - - template < typename T > - bool operator!=(const const_component& l, const component& r) noexcept { - return !(l == r); - } - - template < typename T > - bool operator!=(const const_component& l, const const_component& r) noexcept { - return !(l == r); - } -} - -// ----------------------------------------------------------------------------- -// -// prototype impl -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - namespace detail - { - template < typename T, typename... Args > - typed_applier_with_args::typed_applier_with_args(std::tuple&& args) - : args_(std::move(args)) {} - - template < typename T, typename... Args > - typed_applier_with_args::typed_applier_with_args(const std::tuple& args) - : args_(args) {} - - template < typename T, typename... Args > - applier_uptr typed_applier_with_args::clone() const { - return std::make_unique(args_); - } - - template < typename T, typename... Args > - void typed_applier_with_args::apply_to_entity(entity& ent, bool override) const { - detail::tiny_tuple_apply([&ent, override](const Args&... args){ - if ( override || !ent.exists_component() ) { - ent.assign_component(args...); - } - }, args_); - } - - template < typename T, typename... Args > - void typed_applier_with_args::apply_to_component(T& component) const { - detail::tiny_tuple_apply([&component](const Args&... args){ - component = T(args...); - }, args_); - } - } - - inline prototype::prototype(const prototype& other) { - for ( const family_id family : other.appliers_ ) { - appliers_.insert(family, other.appliers_.get(family)->clone()); - } - } - - inline prototype& prototype::operator=(const prototype& other) { - if ( this != &other ) { - prototype p(other); - swap(p); - } - return *this; - } - - inline prototype::prototype(prototype&& other) noexcept - : appliers_(std::move(other.appliers_)) {} - - inline prototype& prototype::operator=(prototype&& other) noexcept { - if ( this != &other ) { - swap(other); - other.clear(); - } - return *this; - } - - inline void prototype::clear() noexcept { - appliers_.clear(); - } - - inline bool prototype::empty() const noexcept { - return appliers_.empty(); - } - - inline void prototype::swap(prototype& other) noexcept { - using std::swap; - swap(appliers_, other.appliers_); - } - - template < typename T > - bool prototype::has_component() const noexcept { - const auto family = detail::type_family::id(); - return appliers_.has(family); - } - - template < typename T, typename... Args > - prototype& prototype::component(Args&&... args) & { - using applier_t = detail::typed_applier_with_args< - T, - std::decay_t...>; - auto applier = std::make_unique( - std::make_tuple(std::forward(args)...)); - const auto family = detail::type_family::id(); - appliers_.insert_or_assign(family, std::move(applier)); - return *this; - } - - template < typename T, typename... Args > - prototype&& prototype::component(Args&&... args) && { - component(std::forward(args)...); - return std::move(*this); - } - - inline prototype& prototype::merge_with(const prototype& other, bool override) & { - for ( const auto family : other.appliers_ ) { - if ( override || !appliers_.has(family) ) { - appliers_.insert_or_assign( - family, - other.appliers_.get(family)->clone()); - } - } - return *this; - } - - inline prototype&& prototype::merge_with(const prototype& other, bool override) && { - merge_with(other, override); - return std::move(*this); - } - - template < typename T > - bool prototype::apply_to_component(T& component) const { - const auto family = detail::type_family::id(); - const auto applier_base_ptr = appliers_.find(family); - if ( !applier_base_ptr ) { - return false; - } - using applier_t = detail::typed_applier; - const auto applier = static_cast(applier_base_ptr->get()); - applier->apply_to_component(component); - return true; - } - - inline void prototype::apply_to_entity(entity& ent, bool override) const { - for ( const auto family : appliers_ ) { - appliers_.get(family)->apply_to_entity(ent, override); - } - } - - inline void swap(prototype& l, prototype& r) noexcept { - l.swap(r); - } -} - -// ----------------------------------------------------------------------------- -// -// registry impl -// -// ----------------------------------------------------------------------------- - -namespace ecs_hpp -{ - // - // registry::uentity - // - - inline registry::uentity::uentity(registry& owner, entity_id id) noexcept - : id_(id) - , owner_(&owner) {} - - inline registry::uentity::uentity(entity_id ent) noexcept - : id_(ent) {} - - inline registry::uentity::uentity(entity ent) noexcept - : id_(ent.id()) - , owner_(&ent.owner()) {} - - inline registry::uentity::operator entity_id() const noexcept { - return id_; - } - - inline registry::uentity::operator entity() const noexcept { - assert(owner_); - return {*owner_, id_}; - } - - inline registry::uentity::operator const_entity() const noexcept { - assert(owner_); - return {*owner_, id_}; - } - - inline entity_id registry::uentity::id() const noexcept { - return id_; - } - - inline registry* registry::uentity::owner() noexcept { - return owner_; - } - - inline const registry* registry::uentity::owner() const noexcept { - return owner_; - } - - inline bool registry::uentity::check_owner(const registry* owner) const noexcept { - return !owner_ || owner_ == owner; - } - - // - // registry::const_uentity - // - - inline registry::const_uentity::const_uentity(const registry& owner, entity_id id) noexcept - : id_(id) - , owner_(&owner) {} - - inline registry::const_uentity::const_uentity(entity_id ent) noexcept - : id_(ent) {} - - inline registry::const_uentity::const_uentity(entity ent) noexcept - : id_(ent.id()) - , owner_(&ent.owner()) {} - - inline registry::const_uentity::const_uentity(const_entity ent) noexcept - : id_(ent.id()) - , owner_(&ent.owner()) {} - - inline registry::const_uentity::const_uentity(const uentity& ent) noexcept - : id_(ent.id()) - , owner_(ent.owner()) {} - - inline registry::const_uentity::operator entity_id() const noexcept { - return id_; - } - - inline registry::const_uentity::operator const_entity() const noexcept { - assert(owner_); - return {*owner_, id_}; - } - - inline entity_id registry::const_uentity::id() const noexcept { - return id_; - } - - inline const registry* registry::const_uentity::owner() const noexcept { - return owner_; - } - - inline bool registry::const_uentity::check_owner(const registry* owner) const noexcept { - return !owner_ || owner_ == owner; - } - - // - // registry - // - - inline entity registry::wrap_entity(const const_uentity& ent) noexcept { - return {*this, ent.id()}; - } - - inline const_entity registry::wrap_entity(const const_uentity& ent) const noexcept { - return {*this, ent.id()}; - } - - template < typename T > - component registry::wrap_component(const const_uentity& ent) noexcept { - return {wrap_entity(ent)}; - } - - template < typename T > - const_component registry::wrap_component(const const_uentity& ent) const noexcept { - return {wrap_entity(ent)}; - } - - inline entity registry::create_entity() { - if ( !free_entity_ids_.empty() ) { - const auto free_ent_id = free_entity_ids_.back(); - const auto new_ent_id = detail::upgrade_entity_id(free_ent_id); - entity_ids_.insert(new_ent_id); - free_entity_ids_.pop_back(); - return wrap_entity(new_ent_id); - - } - if ( last_entity_id_ >= detail::entity_id_index_mask ) { - throw std::logic_error("ecs_hpp::registry (entity index overlow)"); - } - if ( free_entity_ids_.capacity() <= entity_ids_.size() ) { - // ensure free entity ids capacity for safe (noexcept) entity destroying - free_entity_ids_.reserve(detail::next_capacity_size( - free_entity_ids_.capacity(), - entity_ids_.size() + 1, - free_entity_ids_.max_size())); - } - entity_ids_.insert(last_entity_id_ + 1); - return wrap_entity(++last_entity_id_); - } - - inline entity registry::create_entity(const prototype& proto) { - auto ent = create_entity(); - try { - proto.apply_to_entity(ent, true); - } catch (...) { - destroy_entity(ent); - throw; - } - return ent; - } - - inline entity registry::create_entity(const const_uentity& proto) { - assert(valid_entity(proto)); - entity ent = create_entity(); - try { - for ( const auto family : storages_ ) { - storages_.get(family)->clone(proto, ent.id()); - } - } catch (...) { - destroy_entity(ent); - throw; - } - return ent; - } - - inline void registry::destroy_entity(const uentity& ent) noexcept { - assert(valid_entity(ent)); - remove_all_components(ent); - if ( entity_ids_.unordered_erase(ent) ) { - assert(free_entity_ids_.size() < free_entity_ids_.capacity()); - free_entity_ids_.push_back(ent); - } - } - - inline bool registry::valid_entity(const const_uentity& ent) const noexcept { - assert(ent.check_owner(this)); - return entity_ids_.has(ent); - } - - template < typename T, typename... Args > - T& registry::assign_component(const uentity& ent, Args&&... args) { - assert(valid_entity(ent)); - return get_or_create_storage_().assign( - ent, - std::forward(args)...); - } - - template < typename T > - bool registry::remove_component(const uentity& ent) { - assert(valid_entity(ent)); - detail::component_storage* storage = find_storage_(); - return storage - ? storage->remove(ent) - : false; - } - - template < typename T > - bool registry::exists_component(const const_uentity& ent) const noexcept { - assert(valid_entity(ent)); - const detail::component_storage* storage = find_storage_(); - return storage - ? storage->exists(ent) - : false; - } - - inline std::size_t registry::remove_all_components(const uentity& ent) noexcept { - assert(valid_entity(ent)); - std::size_t removed_count = 0u; - for ( const auto family : storages_ ) { - if ( storages_.get(family)->remove(ent) ) { - ++removed_count; - } - } - return removed_count; - } - - template < typename T > - std::size_t registry::remove_all_components() noexcept { - detail::component_storage* storage = find_storage_(); - return storage - ? storage->remove_all() - : 0u; - } - - template < typename T > - T& registry::get_component(const uentity& ent) { - assert(valid_entity(ent)); - T* component = find_component(ent); - if ( component ) { - return *component; - } - throw std::logic_error("ecs_hpp::registry (component not found)"); - } - - template < typename T > - const T& registry::get_component(const const_uentity& ent) const { - assert(valid_entity(ent)); - const T* component = find_component(ent); - if ( component ) { - return *component; - } - throw std::logic_error("ecs_hpp::registry (component not found)"); - } - - template < typename T > - T* registry::find_component(const uentity& ent) noexcept { - assert(valid_entity(ent)); - detail::component_storage* storage = find_storage_(); - return storage - ? storage->find(ent) - : nullptr; - } - - template < typename T > - const T* registry::find_component(const const_uentity& ent) const noexcept { - assert(valid_entity(ent)); - const detail::component_storage* storage = find_storage_(); - return storage - ? storage->find(ent) - : nullptr; - } - - template < typename... Ts > - std::tuple registry::get_components(const uentity& ent) { - assert(valid_entity(ent)); - return std::make_tuple(std::ref(get_component(ent))...); - } - - template < typename... Ts > - std::tuple registry::get_components(const const_uentity& ent) const { - assert(valid_entity(ent)); - return std::make_tuple(std::cref(get_component(ent))...); - } - - template < typename... Ts > - std::tuple registry::find_components(const uentity& ent) noexcept { - assert(valid_entity(ent)); - return std::make_tuple(find_component(ent)...); - } - - template < typename... Ts > - std::tuple registry::find_components(const const_uentity& ent) const noexcept { - assert(valid_entity(ent)); - return std::make_tuple(find_component(ent)...); - } - - template < typename T > - std::size_t registry::component_count() const noexcept { - const detail::component_storage* storage = find_storage_(); - return storage - ? storage->count() - : 0u; - } - - inline std::size_t registry::entity_count() const noexcept { - return entity_ids_.size(); - } - - inline std::size_t registry::entity_component_count(const const_uentity& ent) const noexcept { - assert(valid_entity(ent)); - std::size_t component_count = 0u; - for ( const auto family : storages_ ) { - if ( storages_.get(family)->has(ent) ) { - ++component_count; - } - } - return component_count; - } - - template < typename F > - void registry::for_each_entity(F&& f) { - for ( const auto id : entity_ids_ ) { - f({*this, id}); - } - } - - template < typename F > - void registry::for_each_entity(F&& f) const { - for ( const auto id : entity_ids_ ) { - f({*this, id}); - } - } - - template < typename T, typename F > - void registry::for_each_component(F&& f) { - detail::component_storage* storage = find_storage_(); - if ( storage ) { - storage->for_each_component([this, &f](const entity_id e, T& t){ - f(uentity{*this, e}, t); - }); - } - } - - template < typename T, typename F > - void registry::for_each_component(F&& f) const { - const detail::component_storage* storage = find_storage_(); - if ( storage ) { - storage->for_each_component([this, &f](const entity_id e, const T& t){ - f(const_uentity{*this, e}, t); - }); - } - } - - template < typename... Ts, typename F > - void registry::for_joined_components(F&& f) { - for_joined_components_impl_( - std::forward(f), - std::make_index_sequence()); - } - - template < typename... Ts, typename F > - void registry::for_joined_components(F&& f) const { - for_joined_components_impl_( - std::forward(f), - std::make_index_sequence()); - } - - template < typename T, typename... Args > - void registry::add_system(priority_t priority, Args&&... args) { - auto iter = std::upper_bound( - systems_.begin(), systems_.end(), priority, - [](priority_t pr, const auto& r){ - return pr < r.first; - }); - systems_.emplace( - iter, - priority, - std::make_unique(std::forward(args)...)); - } - - inline void registry::process_all_systems() { - process_systems_in_range( - std::numeric_limits::min(), - std::numeric_limits::max()); - } - - inline void registry::process_systems_above(priority_t min) { - process_systems_in_range( - min, - std::numeric_limits::max()); - } - - inline void registry::process_systems_below(priority_t max) { - process_systems_in_range( - std::numeric_limits::min(), - max); - } - - inline void registry::process_systems_in_range(priority_t min, priority_t max) { - const auto first = std::lower_bound( - systems_.begin(), systems_.end(), min, - [](const auto& p, priority_t pr) noexcept { - return p.first < pr; - }); - for ( auto iter = first; iter != systems_.end() && iter->first <= max; ++iter ) { - iter->second->process(*this); - } - } - - inline registry::memory_usage_info registry::memory_usage() const noexcept { - memory_usage_info info; - info.entities += free_entity_ids_.capacity() * sizeof(free_entity_ids_[0]); - info.entities += entity_ids_.memory_usage(); - for ( const auto family : storages_ ) { - info.components += storages_.get(family)->memory_usage(); - } - return info; - } - - template < typename T > - std::size_t registry::component_memory_usage() const noexcept { - const detail::component_storage* storage = find_storage_(); - return storage - ? storage->memory_usage() - : 0u; - } - - template < typename T > - detail::component_storage* registry::find_storage_() noexcept { - const auto family = detail::type_family::id(); - using raw_storage_ptr = detail::component_storage*; - const storage_uptr* storage_uptr_ptr = storages_.find(family); - return storage_uptr_ptr && *storage_uptr_ptr - ? static_cast(storage_uptr_ptr->get()) - : nullptr; - } - - template < typename T > - const detail::component_storage* registry::find_storage_() const noexcept { - const auto family = detail::type_family::id(); - using raw_storage_ptr = const detail::component_storage*; - const storage_uptr* storage_uptr_ptr = storages_.find(family); - return storage_uptr_ptr && *storage_uptr_ptr - ? static_cast(storage_uptr_ptr->get()) - : nullptr; - } - - template < typename T > - detail::component_storage& registry::get_or_create_storage_() { - detail::component_storage* storage = find_storage_(); - if ( storage ) { - return *storage; - } - const auto family = detail::type_family::id(); - storages_.emplace( - family, - std::make_unique>(*this)); - return *static_cast*>( - storages_.get(family).get()); - } - - template < typename T - , typename... Ts - , typename F - , std::size_t I - , std::size_t... Is > - void registry::for_joined_components_impl_( - F&& f, - std::index_sequence iseq) - { - (void)iseq; - const auto ss = std::make_tuple(find_storage_()...); - if ( !detail::tuple_contains(ss, nullptr) ) { - for_each_component([this, &f, &ss](const uentity& e, T& t) { - for_joined_components_impl_(e, f, ss, t); - }); - } - } - - template < typename T - , typename... Ts - , typename F - , std::size_t I - , std::size_t... Is > - void registry::for_joined_components_impl_( - F&& f, - std::index_sequence iseq) const - { - (void)iseq; - const auto ss = std::make_tuple(find_storage_()...); - if ( !detail::tuple_contains(ss, nullptr) ) { - for_each_component([this, &f, &ss](const const_uentity& e, const T& t) { - detail::as_const(*this).for_joined_components_impl_(e, f, ss, t); - }); - } - } - - template < typename T - , typename... Ts - , typename F - , typename Ss - , typename... Cs > - void registry::for_joined_components_impl_( - const uentity& e, - const F& f, - const Ss& ss, - Cs&... cs) - { - T* c = std::get<0>(ss)->find(e); - if ( c ) { - for_joined_components_impl_( - e, - f, - detail::tuple_tail(ss), - cs..., - *c); - } - } - - template < typename T - , typename... Ts - , typename F - , typename Ss - , typename... Cs > - void registry::for_joined_components_impl_( - const const_uentity& e, - const F& f, - const Ss& ss, - const Cs&... cs) const - { - const T* c = std::get<0>(ss)->find(e); - if ( c ) { - for_joined_components_impl_( - e, - f, - detail::tuple_tail(ss), - cs..., - *c); - } - } - - template < typename F, typename... Cs > - void registry::for_joined_components_impl_( - const uentity& e, - const F& f, - const std::tuple<>& ss, - Cs&... cs) - { - (void)ss; - f(e, cs...); - } - - template < typename F, typename... Cs > - void registry::for_joined_components_impl_( - const const_uentity& e, - const F& f, - const std::tuple<>& ss, - const Cs&... cs) const - { - (void)ss; - f(e, cs...); - } -} diff --git a/headers/enduro2d/base/_base.hpp b/headers/enduro2d/base/_base.hpp index 850bfdd0..01afedf1 100644 --- a/headers/enduro2d/base/_base.hpp +++ b/headers/enduro2d/base/_base.hpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include diff --git a/headers/enduro2d/utils/font.hpp b/headers/enduro2d/utils/font.hpp index cd447e20..c33679a7 100644 --- a/headers/enduro2d/utils/font.hpp +++ b/headers/enduro2d/utils/font.hpp @@ -9,49 +9,31 @@ #include "_utils.hpp" #include "buffer.hpp" +#include "buffer_view.hpp" #include "streams.hpp" namespace e2d { class font final { public: - struct info_data { - str face; - u32 size{0}; - }; - - struct common_data { - u32 line{0}; - u32 base{0}; - u32 pages{0}; + struct font_info { + str atlas_file; v2u atlas_size; + u32 font_size{0}; + u32 line_height{0}; + u32 glyph_ascent{0}; }; - struct page_data { - u32 id{0}; - str file; + struct glyph_info { + v2i offset; + b2u tex_rect; + i32 advance{0}; }; - struct char_data { - u32 id{0}; - b2hi rect; - v2hi offset; - i16 advance{0}; - u16 page{0}; - u16 chnl{0}; - }; - - struct kerning_data { - std::pair chars{0,0}; - i32 amount{0}; - }; - - struct data { - info_data info; - common_data common; - flat_set pages; - flat_map chars; + struct content { + font_info info; flat_map kernings; + flat_map glyphs; }; public: font() = default; @@ -62,60 +44,43 @@ namespace e2d font(const font& other); font& operator=(const font& other); - font(data&& data) noexcept; - font(const data& data); + font(content&& content) noexcept; + font(const content& content); font& assign(font&& other) noexcept; font& assign(const font& other); - font& assign(data&& data) noexcept; - font& assign(const data& data); + font& assign(content&& content) noexcept; + font& assign(const content& content); void swap(font& other) noexcept; void clear() noexcept; bool empty() const noexcept; - const info_data& info() const noexcept; - const common_data& common() const noexcept; - const flat_set& pages() const noexcept; - const flat_map& chars() const noexcept; + const font_info& info() const noexcept; const flat_map& kernings() const noexcept; + const flat_map& glyphs() const noexcept; - const page_data* find_page(u32 id) const noexcept; - const char_data* find_char(u32 id) const noexcept; i32 get_kerning(u32 first, u32 second) const noexcept; + const glyph_info* find_glyph(u32 code_point) const noexcept; private: - data data_; + content content_; }; void swap(font& l, font& r) noexcept; bool operator==(const font& l, const font& r) noexcept; bool operator!=(const font& l, const font& r) noexcept; - bool operator<(u32 l, const font::page_data& r) noexcept; - bool operator<(const font::page_data& l, u32 r) noexcept; - bool operator<(const font::page_data& l, const font::page_data& r) noexcept; - - bool operator<(u32 l, const font::char_data& r) noexcept; - bool operator<(const font::char_data& l, u32 r) noexcept; - bool operator<(const font::char_data& l, const font::char_data& r) noexcept; - - bool operator<(const std::pair& l, const font::kerning_data& r) noexcept; - bool operator<(const font::kerning_data& l, const std::pair& r) noexcept; - bool operator<(const font::kerning_data& l, const font::kerning_data& r) noexcept; - - bool operator==(const font::info_data& l, const font::info_data& r) noexcept; - bool operator==(const font::common_data& l, const font::common_data& r) noexcept; - bool operator==(const font::page_data& l, const font::page_data& r) noexcept; - bool operator==(const font::char_data& l, const font::char_data& r) noexcept; - bool operator==(const font::kerning_data& l, const font::kerning_data& r) noexcept; + bool operator==(const font::font_info& l, const font::font_info& r) noexcept; + bool operator==(const font::glyph_info& l, const font::glyph_info& r) noexcept; + bool operator==(const font::content& l, const font::content& r) noexcept; } namespace e2d::fonts { bool try_load_font( font& dst, - const buffer& src) noexcept; + buffer_view src) noexcept; bool try_load_font( font& dst, diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 3cac8bb9..70c5e8db 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -36,5 +36,4 @@ add_e2d_sample(02) add_e2d_sample(03) add_e2d_sample(04) add_e2d_sample(05) -add_e2d_sample(07) diff --git a/samples/sources/sample_07/sample_07.cpp b/samples/sources/sample_07/sample_07.cpp deleted file mode 100644 index 2b4e4bb8..00000000 --- a/samples/sources/sample_07/sample_07.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************* - * This file is part of the "Enduro2D" - * For conditions of distribution and use, see copyright notice in LICENSE.md - * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) - ******************************************************************************/ - -#include "../common.hpp" -using namespace e2d; - -namespace -{ - class game_system final : public ecs::system { - public: - void process(ecs::registry& owner) override { - E2D_UNUSED(owner); - const keyboard& k = the().keyboard(); - - if ( k.is_key_just_released(keyboard_key::f12) ) { - the().toggle_visible(!the().visible()); - } - - if ( k.is_key_just_released(keyboard_key::escape) ) { - the().set_should_close(true); - } - - if ( k.is_key_pressed(keyboard_key::lsuper) && k.is_key_just_released(keyboard_key::enter) ) { - the().toggle_fullscreen(!the().fullscreen()); - } - } - }; - - class camera_system final : public ecs::system { - public: - void process(ecs::registry& owner) override { - owner.for_joined_components( - [](const ecs::const_entity&, camera& cam){ - if ( !cam.target() ) { - cam.viewport( - the().real_size()); - cam.projection(math::make_orthogonal_lh_matrix4( - the().real_size().cast_to(), 0.f, 1000.f)); - } - }); - } - }; - - class game final : public starter::application { - public: - bool initialize() final { - return create_scene() - && create_systems(); - } - private: - bool create_scene() { - auto scene_prefab_res = the().load_asset("scene_bmf_prefab.json"); - auto scene_go = scene_prefab_res - ? the().instantiate(scene_prefab_res->content()) - : nullptr; - return !!scene_go; - } - bool create_systems() { - ecs::registry_filler(the().registry()) - .system(world::priority_update) - .system(world::priority_pre_render); - return true; - } - }; -} - -int e2d_main(int argc, char *argv[]) { - const auto starter_params = starter::parameters( - engine::parameters("sample_07", "enduro2d") - .timer_params(engine::timer_parameters() - .maximal_framerate(100))); - modules::initialize(argc, argv, starter_params).start(); - modules::shutdown(); - return 0; -} diff --git a/sources/enduro2d/high/systems/label_system.cpp b/sources/enduro2d/high/systems/label_system.cpp index 363f4c5d..bad2a4c7 100644 --- a/sources/enduro2d/high/systems/label_system.cpp +++ b/sources/enduro2d/high/systems/label_system.cpp @@ -38,20 +38,20 @@ namespace f32 get_top_pos(const label& l, u32 strings_count) { const auto& f = l.font()->content(); - f32 label_height = f.common().line * strings_count; + f32 label_height = f.info().line_height * strings_count; f32 top{0}; switch ( l.valign() ) { - case label::valigns::top : + case label::valigns::top: top = label_height; break; - case label::valigns::center : + case label::valigns::center: top = 0.5f * label_height; break; - case label::valigns::bottom : + case label::valigns::bottom: top = 0; break; - case label::valigns::baseline : - top = label_height - (f.common().line - f.common().base); + case label::valigns::baseline: + top = label_height - (f.info().line_height - f.info().glyph_ascent); break; default: E2D_ASSERT_MSG(false,"label_system: uncknow vertical align flag"); @@ -94,8 +94,7 @@ namespace e2d return; } const auto& f = l.font()->content(); - auto common = f.common(); - v2f texture_size = common.atlas_size.cast_to(); + v2f texture_size = f.info().atlas_size.cast_to(); if ( l.width() != 0 ) { f32 word_width{0}; @@ -108,13 +107,13 @@ namespace e2d string_width = 0; last_space = -1; } else { - auto data = f.find_char(ch); - if ( data ) { + const font::glyph_info* glyph = f.find_glyph(ch); + if ( glyph ) { prev_char != 0 - ? kerning = f.get_kerning(prev_char, data->id) + ? kerning = f.get_kerning(prev_char, ch) : kerning = 0; prev_char = ch; - f32 char_width = data->advance + kerning; + f32 char_width = glyph->advance + kerning; if ( ch == ' ' ) { if ( string_width + word_width < l.width() ) { if ( string_width + word_width + char_width < l.width() ) { @@ -166,13 +165,13 @@ namespace e2d prev_char = 0; for ( size_t i = 0, string_ind = 0; i < text.size(); i++ ) { if ( text[i] != '\n' ) { - auto data = f.find_char(text[i]); - if ( data ) { + const font::glyph_info* glyph = f.find_glyph(text[i]); + if ( glyph ) { letters_size++; prev_char != 0 - ? kerning = f.get_kerning(prev_char, data->id) + ? kerning = f.get_kerning(prev_char, text[i]) : kerning = 0; - strings_width[string_ind] += data->advance + kerning; + strings_width[string_ind] += glyph->advance + kerning; } } else { string_ind++; @@ -190,49 +189,49 @@ namespace e2d for ( size_t i = 0, str_ind = 0; i < text.size(); i++ ) { if ( text[i] == '\n' ) { str_ind++; - y_pos -= common.line; + y_pos -= f.info().line_height; x_pos = get_x_pos(l, strings_width[str_ind]); prev_char = 0; continue; } - auto data = f.find_char(text[i]); - if ( data ) { - offset = data->offset.cast_to(); + const font::glyph_info* glyph = f.find_glyph(text[i]); + if ( glyph ) { + offset = glyph->offset.cast_to(); prev_char != 0 - ? kerning = f.get_kerning(prev_char, data->id) + ? kerning = f.get_kerning(prev_char, text[i]) : kerning = 0; offset.x += kerning; - prev_char = data->id; + prev_char = text[i]; size_t start_vertices = letters_counter * 4; vertices[start_vertices] = v3f( x_pos + offset.x, - y_pos - offset.y - data->rect.size.y, + y_pos - offset.y - glyph->tex_rect.size.y, 0); vertices[start_vertices + 1] = v3f( x_pos + offset.x, y_pos - offset.y, 0); vertices[start_vertices + 2] = v3f( - x_pos + data->rect.size.x + offset.x, + x_pos + glyph->tex_rect.size.x + offset.x, y_pos - offset.y, 0); vertices[start_vertices + 3] = v3f( - x_pos + data->rect.size.x + offset.x, - y_pos - offset.y - data->rect.size.y, + x_pos + glyph->tex_rect.size.x + offset.x, + y_pos - offset.y - glyph->tex_rect.size.y, 0); uvs[start_vertices] = v2f( - data->rect.position.x / texture_size.x, - data->rect.position.y / texture_size.y); + glyph->tex_rect.position.x / texture_size.x, + glyph->tex_rect.position.y / texture_size.y); uvs[start_vertices + 1] = v2f( - data->rect.position.x / texture_size.x, - (data->rect.position.y + data->rect.size.y) / texture_size.y); + glyph->tex_rect.position.x / texture_size.x, + (glyph->tex_rect.position.y + glyph->tex_rect.size.y) / texture_size.y); uvs[start_vertices + 2] = v2f( - (data->rect.position.x + data->rect.size.x) / texture_size.x, - (data->rect.position.y + data->rect.size.y) / texture_size.y); + (glyph->tex_rect.position.x + glyph->tex_rect.size.x) / texture_size.x, + (glyph->tex_rect.position.y + glyph->tex_rect.size.y) / texture_size.y); uvs[start_vertices + 3] = v2f( - (data->rect.position.x + data->rect.size.x) / texture_size.x, - data->rect.position.y / texture_size.y); + (glyph->tex_rect.position.x + glyph->tex_rect.size.x) / texture_size.x, + glyph->tex_rect.position.y / texture_size.y); colors[start_vertices] = l.tint(); colors[start_vertices + 1] = l.tint(); @@ -247,7 +246,7 @@ namespace e2d indices[start_indices + 4] = start_vertices + 3; indices[start_indices + 5] = start_vertices; - x_pos += data->advance + kerning; + x_pos += glyph->advance + kerning; letters_counter++; } } @@ -270,8 +269,7 @@ namespace e2d if ( r.materials().size() > 0 && r.materials().front()->content().properties().sampler_count() == 0 ) { - str page_file = f.find_page(0)->file; - auto texture_p = the().load_asset(page_file); + auto texture_p = the().load_asset(f.info().atlas_file); if ( !texture_p ) { return; } diff --git a/sources/enduro2d/utils/font.cpp b/sources/enduro2d/utils/font.cpp index b148a0f5..aae2a38a 100644 --- a/sources/enduro2d/utils/font.cpp +++ b/sources/enduro2d/utils/font.cpp @@ -4,228 +4,15 @@ * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) ******************************************************************************/ -#include -#include +#include "font_impl/font_impl.hpp" namespace { using namespace e2d; - using namespace e2d::strings; - class bmfont_loading_exception : public exception { - const char* what() const noexcept override { - return "bmfont loading exception"; - } - }; - - inline u64 make_kerning_key(u32 first, u32 second) noexcept { + u64 make_kerning_key(u32 first, u32 second) noexcept { return (static_cast(first) << 32) | static_cast(second); } - - str_view read_tag(str_view buf, u32& pos) { - str_view res; - auto end = buf.find(' ', pos); - if ( end != str_view::npos ) { - res = buf.substr(pos, end - pos); - pos = end; - } - - return res; - } - - bool read_key(str_view buf, u32& pos, str& key) { - auto end = buf.find('=', pos); - if ( end != str_view::npos ) { - auto start = buf.rfind(' ', end); - if ( start != str_view::npos ) { - start++; - key = buf.substr(start, end - start); - pos = end + 1; - return true; - } - } - - return false; - } - - i32 read_int(str_view buf, u32& pos) { - i32 res{0}; - auto end = buf.find(' ', pos); - if ( end == str_view::npos ) { - end = buf.find("\r\n", pos); - if ( end == str_view::npos ) { - end = buf.find('\n', pos); - } - } - - if ( end == str_view::npos ) { - throw bmfont_loading_exception(); - } else { - if ( !try_parse(buf.substr(pos, end - pos), res) ) { - throw bmfont_loading_exception(); - } - pos = end; - } - - return res; - } - - str_view read_string(const str_view& buf, u32& pos) { - str_view res; - auto start = buf.find('"', pos); - if ( start == str_view::npos ) { - throw bmfont_loading_exception(); - } else { - start++; - auto end = buf.find('"', start); - if ( end == str_view::npos ) { - throw bmfont_loading_exception(); - } else { - res = buf.substr(start, end - start); - pos = end + 1; - } - } - - return res; - } - - font::data load_font_data(str_view content) { - u32 pos{0}; - str_view line; - str_view tag; - str key; - font::data data; - vector chars; - vector kernings; - - chars.reserve(120); - kernings.reserve(120); - size_t start_line{0}; - size_t end_line = content.find('\n', start_line); - while ( end_line != str_view::npos ) { - pos = 0; - line = content.substr(start_line, end_line - start_line + 1); - if ( !line.empty() ) { - tag = read_tag(line, pos); - if ( tag == "info" ) { - // info face="Arial-Black" size=32 bold=0 italic=0 charset="" - // unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=2,2 - while ( read_key(line, pos, key) ) { - if ( key == "face" ) { - data.info.face = read_string(line, pos); - } else if ( key == "size" ) { - data.info.size = read_int(line, pos); - } - } - } else if ( tag == "common" ) { - // common lineHeight=54 base=35 scaleW=512 scaleH=512 pages=1 packed=0 - while ( read_key(line, pos, key) ) { - if ( key == "lineHeight" ) { - data.common.line = read_int(line, pos); - } else if ( key == "base" ) { - data.common.base = read_int(line, pos); - } else if ( key == "scaleW" ) { - data.common.atlas_size.x = read_int(line, pos); - } else if ( key == "scaleH" ) { - data.common.atlas_size.y = read_int(line, pos); - } else if ( key == "pages" ) { - data.common.pages = read_int(line, pos); - } - } - } else if ( tag == "page" ) { - // page id=0 file="arial.png" - font::page_data page_data; - while ( read_key(line, pos, key) ) { - if ( key == "id" ) { - page_data.id = read_int(line, pos); - } else if ( key == "file" ) { - page_data.file = read_string(line, pos); - } - } - data.pages.insert(std::move(page_data)); - } else if ( tag == "chars" ) { - // chars count=95 - while ( read_key(line, pos, key) ) { - if ( key == "count" ) { - chars.reserve(read_int(line, pos)); - } - } - } else if ( tag == "char" ) { - // char id=123 x=2 y=2 width=38 height=54 - // xoffset=0 yoffset=-3 xadvance=12 page=0 chnl=0 - font::char_data c; - while ( read_key(line, pos, key) ) { - if ( key == "id" ) { - c.id = read_int(line, pos); - } else if ( key == "x" ) { - c.rect.position.x = read_int(line, pos); - } else if ( key == "y" ) { - c.rect.position.y = read_int(line, pos); - } else if ( key == "width" ) { - c.rect.size.x = read_int(line, pos); - } else if ( key == "height" ) { - c.rect.size.y = read_int(line, pos); - c.rect.position.y = - data.common.atlas_size.y - - c.rect.position.y - - c.rect.size.y; - } else if ( key == "xoffset" ) { - c.offset.x = read_int(line, pos); - } else if ( key == "yoffset" ) { - c.offset.y = read_int(line, pos); - } else if ( key == "xadvance" ) { - c.advance = read_int(line, pos); - } else if ( key == "page" ) { - c.page = read_int(line, pos); - } else if ( key == "chnl" ) { - c.chnl = read_int(line, pos); - } - } - chars.push_back(std::move(c)); - } else if ( tag == "kernings" ) { - while ( read_key(line, pos, key) ) { - if ( key == "count" ) { - kernings.reserve(read_int(line, pos)); - } - } - } else if ( tag == "kerning" ) { - font::kerning_data k; - while ( read_key(line, pos, key) ) { - if ( key == "first" ) { - k.chars.first = read_int(line, pos); - } else if ( key == "second" ) { - k.chars.second = read_int(line, pos); - } else if ( key == "amount" ) { - k.amount = read_int(line, pos); - } - } - kernings.push_back(std::move(k)); - } - } - start_line = content.find_first_not_of(' ', end_line + 1); - end_line = content.find('\n', start_line); - } - - if (chars.empty()) { - throw bmfont_loading_exception(); - } - - data.chars.reserve(chars.size()); - for ( size_t i = 0; i < chars.size(); i++ ) { - data.chars.insert(std::make_pair(chars[i].id, chars[i])); - } - - data.kernings.reserve(kernings.size()); - for ( size_t i = 0; i < kernings.size(); i++ ) { - auto& k = kernings[i]; - data.kernings.insert( - std::make_pair( - make_kerning_key(k.chars.first, k.chars.second), - k.amount)); - } - - return data; - } } namespace e2d @@ -246,12 +33,12 @@ namespace e2d return assign(other); } - font::font(data&& data) noexcept { - assign(std::move(data)); + font::font(content&& content) noexcept { + assign(std::move(content)); } - font::font(const data& data) { - assign(data); + font::font(const content& content) { + assign(content); } font& font::assign(font&& other) noexcept { @@ -264,79 +51,62 @@ namespace e2d font& font::assign(const font& other) { if ( this != &other ) { - data_ = other.data_; + content_ = other.content_; } return *this; } - font& font::assign(data&& data) noexcept { - data_ = std::move(data); + font& font::assign(content&& content) noexcept { + content_ = std::move(content); return *this; } - font& font::assign(const data& data) { - data_ = data; + font& font::assign(const content& content) { + content_ = content; return *this; } void font::swap(font& other) noexcept { using std::swap; - swap(data_.info, other.data_.info); - swap(data_.common, other.data_.common); - swap(data_.pages, other.data_.pages); - swap(data_.chars, other.data_.chars); - swap(data_.kernings, other.data_.kernings); + swap(content_.info, other.content_.info); + swap(content_.kernings, other.content_.kernings); + swap(content_.glyphs, other.content_.glyphs); } void font::clear() noexcept { - data_ = data(); + content_ = content(); } bool font::empty() const noexcept { - return data_.pages.empty(); + return !content_.info.font_size; } - const font::info_data& font::info() const noexcept { - return data_.info; - } - - const font::common_data& font::common() const noexcept { - return data_.common; - } - - const flat_set& font::pages() const noexcept { - return data_.pages; - } - - const flat_map& font::chars() const noexcept { - return data_.chars; + const font::font_info& font::info() const noexcept { + return content_.info; } const flat_map& font::kernings() const noexcept { - return data_.kernings; + return content_.kernings; } - const font::page_data* font::find_page(u32 id) const noexcept { - const auto iter = data_.pages.find(id); - return iter != data_.pages.end() - ? &*iter - : nullptr; - } - - const font::char_data* font::find_char(u32 id) const noexcept { - const auto iter = data_.chars.find(id); - return iter != data_.chars.end() - ? &iter->second - : nullptr; + const flat_map& font::glyphs() const noexcept { + return content_.glyphs; } i32 font::get_kerning(u32 first, u32 second) const noexcept { - u64 key = make_kerning_key(first, second); - const auto iter = data_.kernings.find(key); - return iter != data_.kernings.end() + const u64 key = make_kerning_key(first, second); + const auto iter = content_.kernings.find(key); + return iter != content_.kernings.end() ? iter->second : 0; } + + const font::glyph_info* font::find_glyph(u32 code_point) const noexcept { + const auto iter = content_.glyphs.find(code_point); + return iter != content_.glyphs.end() + ? &iter->second + : nullptr; + } } namespace e2d @@ -347,98 +117,52 @@ namespace e2d bool operator==(const font& l, const font& r) noexcept { return l.info() == r.info() - && l.common() == r.common() - && l.pages() == r.pages() - && l.chars() == r.chars() - && l.kernings() == r.kernings(); + && l.kernings() == r.kernings() + && l.glyphs() == r.glyphs(); } bool operator!=(const font& l, const font& r) noexcept { return !(l == r); } - bool operator<(u32 l, const font::page_data& r) noexcept { - return l < r.id; + bool operator==(const font::font_info& l, const font::font_info& r) noexcept { + return l.atlas_file == r.atlas_file + && l.atlas_size == r.atlas_size + && l.font_size == r.font_size + && l.line_height == r.line_height + && l.glyph_ascent == r.glyph_ascent; } - bool operator<(const font::page_data& l, u32 r) noexcept { - return l.id < r; + bool operator==(const font::glyph_info& l, const font::glyph_info& r) noexcept { + return l.offset == r.offset + && l.tex_rect == r.tex_rect + && l.advance == r.advance; } - bool operator<(const font::page_data& l, const font::page_data& r) noexcept { - return l.id < r.id; - } - - bool operator<(u32 l, const font::char_data& r) noexcept { - return l < r.id; - } - - bool operator<(const font::char_data& l, u32 r) noexcept { - return l.id < r; - } - - bool operator<(const font::char_data& l, const font::char_data& r) noexcept { - return l.id < r.id; - } - - bool operator<(const std::pair& l, const font::kerning_data& r) noexcept { - return l < r.chars; - } - - bool operator<(const font::kerning_data& l, const std::pair& r) noexcept { - return l.chars < r; - } - - bool operator<(const font::kerning_data& l, const font::kerning_data& r) noexcept { - return l.chars < r.chars; - } - - bool operator==(const font::info_data& l, const font::info_data& r) noexcept { - return l.face == r.face - && l.size == r.size; - } - - bool operator==(const font::common_data& l, const font::common_data& r) noexcept { - return l.line == r.line - && l.base == r.base - && l.pages == r.pages - && l.atlas_size == r.atlas_size; - } - - bool operator==(const font::page_data& l, const font::page_data& r) noexcept { - return l.id == r.id - && l.file == r.file; - } - - bool operator==(const font::char_data& l, const font::char_data& r) noexcept { - return l.id == r.id - && l.rect == r.rect - && l.offset == r.offset - && l.advance == r.advance - && l.page == r.page - && l.chnl == r.chnl; - } - - bool operator==(const font::kerning_data& l, const font::kerning_data& r) noexcept { - return l.chars == r.chars - && l.amount == r.amount; + bool operator==(const font::content& l, const font::content& r) noexcept { + return l.info == r.info + && l.kernings == r.kernings + && l.glyphs == r.glyphs; } } namespace e2d::fonts { - bool try_load_font(font& dst, const buffer& src) noexcept { + bool try_load_font( + font& dst, + buffer_view src) noexcept + { try { - dst = load_font_data(str_view{ - reinterpret_cast(src.data()), - src.size()}); - return true; + return impl::load_font_bmfont(dst, src); } catch (...) { return false; } } - bool try_load_font(font& dst, const input_stream_uptr& src) noexcept { + bool try_load_font( + font& dst, + const input_stream_uptr& src) noexcept + { buffer file_data; return streams::try_read_tail(file_data, src) && try_load_font(dst, file_data); diff --git a/sources/enduro2d/utils/font_impl/font_impl.hpp b/sources/enduro2d/utils/font_impl/font_impl.hpp new file mode 100644 index 00000000..12a8e5bd --- /dev/null +++ b/sources/enduro2d/utils/font_impl/font_impl.hpp @@ -0,0 +1,17 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace e2d::fonts::impl +{ + bool load_font_bmfont(font& dst, buffer_view src); +} diff --git a/sources/enduro2d/utils/font_impl/font_reader_bmfont.cpp b/sources/enduro2d/utils/font_impl/font_reader_bmfont.cpp new file mode 100644 index 00000000..f88bc8ed --- /dev/null +++ b/sources/enduro2d/utils/font_impl/font_reader_bmfont.cpp @@ -0,0 +1,359 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include "font_impl.hpp" + +namespace +{ + using namespace e2d; + + bool read_line(str_view content, std::size_t& pos, str_view& line) { + if ( pos > content.size() ) { + return false; + } + + auto end_of_line = content.find('\n', pos); + if ( end_of_line == str_view::npos ) { + end_of_line = content.size(); + } + + line = content.substr(pos, end_of_line - pos); + if ( !line.empty() && line.back() == '\r' ) { + line.remove_suffix(1u); + } + + pos = end_of_line + 1u; + return true; + } + + bool read_tag(str_view line, std::size_t& pos, str_view& tag) { + const auto start = line.find_first_not_of(' ', pos); + if ( start == str_view::npos ) { + return false; + } + const auto end = line.find(' ', start); + if ( end == str_view::npos || end == line.size() ) { + return false; + } + tag = line.substr(start, end - start); + pos = end + 1; + return true; + } + + bool read_key(str_view line, std::size_t& pos, str_view& key) { + const auto eq = line.find('=', pos); + if ( eq == str_view::npos || eq == 0u || eq == line.size() ) { + return false; + } + const auto last = line.find_last_not_of(' ', eq - 1u); + if ( last == str_view::npos ) { + return false; + } + const auto prestart = line.rfind(' ', last); + const auto start = prestart == str_view::npos + ? 0u + : prestart + 1u; + key = line.substr(start, last - prestart); + pos = eq + 1; + return true; + } + + template < typename T > + bool read_value(str_view line, std::size_t& pos, T& value) { + const auto start = line.find_first_not_of(' ', pos); + if ( start == str_view::npos ) { + return false; + } + auto end = line.find(' ', start); + if ( end == str_view::npos ) { + end = line.size(); + } + if ( !strings::try_parse(line.substr(start, end - start), value) ) { + return false; + } + pos = end + 1; + return true; + } + + bool read_value(str_view line, std::size_t& pos, str& value) { + const auto start = line.find("\"", pos); + if ( start == str_view::npos || start == line.size() ) { + return false; + } + const auto end = line.find("\"", start + 1u); + if ( end == str_view::npos ) { + return false; + } + value = line.substr(start + 1, end - start - 1u); + pos = end + 1; + return true; + } + + // info face="Arial-Black" size=32 bold=0 italic=0 charset="" + // unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=2,2 + bool parse_info_line(str_view line, font::content& font_content) { + str_view key; + std::size_t pos = 0u; + while ( read_key(line, pos, key) ) { + if ( key == "size" ) { + if ( !read_value(line, pos, font_content.info.font_size) ) { + return false; + } + } + } + return true; + } + + // common lineHeight=54 base=35 scaleW=512 scaleH=512 pages=1 packed=0 + bool parse_common_line(str_view line, font::content& font_content) { + str_view key; + std::size_t pos = 0u; + while ( read_key(line, pos, key) ) { + if ( key == "lineHeight" ) { + if ( !read_value(line, pos, font_content.info.line_height) ) { + return false; + } + } else if ( key == "base" ) { + if ( !read_value(line, pos, font_content.info.glyph_ascent) ) { + return false; + } + } else if ( key == "scaleW" ) { + if ( !read_value(line, pos, font_content.info.atlas_size.x) ) { + return false; + } + } else if ( key == "scaleH" ) { + if ( !read_value(line, pos, font_content.info.atlas_size.y) ) { + return false; + } + } else if ( key == "pages" ) { + u32 pages = 0u; + if ( !read_value(line, pos, pages) || pages > 1u ) { + return false; + } + } + } + return true; + } + + // page id=0 file="arial.png" + bool parse_page_line(str_view line, font::content& font_content) { + str_view key; + std::size_t pos = 0u; + while ( read_key(line, pos, key) ) { + if ( key == "id" ) { + u32 id = 0u; + if ( !read_value(line, pos, id) || id > 0 ) { + return false; + } + } else if ( key == "file" ) { + if ( !read_value(line, pos, font_content.info.atlas_file) ) { + return false; + } + } + } + return true; + } + + // chars count=95 + bool parse_chars_line(str_view line, font::content& font_content) { + str_view key; + std::size_t pos = 0u; + while ( read_key(line, pos, key) ) { + if ( key == "count" ) { + std::size_t count = 0u; + if ( !read_value(line, pos, count) ) { + return false; + } + font_content.glyphs.reserve(count); + } + } + return true; + } + + // char id=123 x=2 y=2 width=38 height=54 + // xoffset=0 yoffset=-3 xadvance=12 page=0 chnl=0 + bool parse_char_line(str_view line, font::content& font_content) { + u32 code_point = 0; + font::glyph_info glyph; + + str_view key; + std::size_t pos = 0u; + while ( read_key(line, pos, key) ) { + if ( key == "id" ) { + if ( !read_value(line, pos, code_point) ) { + return false; + } + } else if ( key == "x" ) { + if ( !read_value(line, pos, glyph.tex_rect.position.x) ) { + return false; + } + } else if ( key == "y" ) { + if ( !read_value(line, pos, glyph.tex_rect.position.y) ) { + return false; + } + } else if ( key == "width" ) { + if ( !read_value(line, pos, glyph.tex_rect.size.x) ) { + return false; + } + } else if ( key == "height" ) { + if ( !read_value(line, pos, glyph.tex_rect.size.y) ) { + return false; + } + } else if ( key == "xoffset" ) { + if ( !read_value(line, pos, glyph.offset.x) ) { + return false; + } + } else if ( key == "yoffset" ) { + if ( !read_value(line, pos, glyph.offset.y) ) { + return false; + } + } else if ( key == "xadvance" ) { + if ( !read_value(line, pos, glyph.advance) ) { + return false; + } + } else if ( key == "page" ) { + u32 page = 0; + if ( !read_value(line, pos, page) || page > 0 ) { + return false; + } + } + } + + font_content.glyphs.insert_or_assign( + code_point, + std::move(glyph)); + + return true; + } + + // kernings count=343 + bool parse_kernings_line(str_view line, font::content& font_content) { + str_view key; + std::size_t pos = 0u; + while ( read_key(line, pos, key) ) { + if ( key == "count" ) { + std::size_t count = 0u; + if ( !read_value(line, pos, count) ) { + return false; + } + font_content.kernings.reserve(count); + } + } + return true; + } + + // kerning first=1043 second=1071 amount=-1 + bool parse_kerning_line(str_view line, font::content& font_content) { + u32 first = 0; + u32 second = 0; + i32 amount = 0; + + str_view key; + std::size_t pos = 0u; + while ( read_key(line, pos, key) ) { + if ( key == "first" ) { + if ( !read_value(line, pos, first) ) { + return false; + } + } else if ( key == "second" ) { + if ( !read_value(line, pos, second) ) { + return false; + } + } else if ( key == "amount" ) { + if ( !read_value(line, pos, amount) ) { + return false; + } + } + } + + font_content.kernings.insert_or_assign( + (static_cast(first) << 32) | static_cast(second), + amount); + + return true; + } +} + +namespace e2d::fonts::impl +{ + bool load_font_bmfont(font& dst, buffer_view src) { + const auto text_src = str_view{ + reinterpret_cast(src.data()), + src.size()}; + + font::content font_content; + + str_view line; + std::size_t pos = 0u; + while ( read_line(text_src, pos, line) ) { + str_view tag; + std::size_t tag_pos = 0u; + if ( !read_tag(line, tag_pos, tag) ) { + continue; + } + + line.remove_prefix(tag_pos); + + if ( tag == "info" ) { + if ( !parse_info_line(line, font_content) ) { + return false; + } + } else if ( tag == "common" ) { + if ( !parse_common_line(line, font_content) ) { + return false; + } + } else if ( tag == "page" ) { + if ( !parse_page_line(line, font_content) ) { + return false; + } + } else if ( tag == "chars" ) { + if ( !parse_chars_line(line, font_content) ) { + return false; + } + } else if ( tag == "char" ) { + if ( !parse_char_line(line, font_content) ) { + return false; + } + } else if ( tag == "kernings" ) { + if ( !parse_kernings_line(line, font_content) ) { + return false; + } + } else if ( tag == "kerning" ) { + if ( !parse_kerning_line(line, font_content) ) { + return false; + } + } + } + + for ( auto& glyph : font_content.glyphs ) { + glyph.second.tex_rect.position.y = + font_content.info.atlas_size.y - + glyph.second.tex_rect.position.y - + glyph.second.tex_rect.size.y; + } + + bool info_valid = !font_content.info.atlas_file.empty() + && font_content.info.atlas_size.x > 0u + && font_content.info.atlas_size.y > 0u + && font_content.info.font_size > 0u + && font_content.info.line_height > 0u + && font_content.info.font_size > 0u + && font_content.info.glyph_ascent > 0u; + + bool glyphs_valid = true; + for ( const auto& glyph : font_content.glyphs ) { + const v2u& mst = glyph.second.tex_rect.position + glyph.second.tex_rect.size; + glyphs_valid = glyphs_valid && math::inside(b2u(font_content.info.atlas_size), mst); + } + + if ( !info_valid || !glyphs_valid ) { + return false; + } + + dst = font(font_content); + return true; + } +} diff --git a/untests/sources/untests_utils/font.cpp b/untests/sources/untests_utils/font.cpp index d8f23981..65baf767 100644 --- a/untests/sources/untests_utils/font.cpp +++ b/untests/sources/untests_utils/font.cpp @@ -10,13 +10,15 @@ using namespace e2d; namespace { const char* const mini_fnt = R"fnt( - info face="Arial" size=50 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=0,0 - common lineHeight=58 base=49 scaleW=512 scaleH=512 pages=1 packed=0 + info face ="Arial" size= 50 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=0,0 + common lineHeight =58 base= 49 scaleW = 512 scaleH=512 pages=1 packed=0 page id=0 file="arial.png" + chars count=3 char id=32 x=342 y=38 width=0 height=0 xoffset=0 yoffset=46 xadvance=14 page=0 chnl=0 char id=33 x=448 y=386 width=10 height=40 xoffset=5 yoffset=10 xadvance=14 page=0 chnl=0 char id=34 x=394 y=306 width=18 height=18 xoffset=3 yoffset=8 xadvance=18 page=0 chnl=0 + kernings count=2 kerning first=1059 second=1081 amount=-1 kerning first=1043 second=1071 amount=-1 @@ -39,32 +41,22 @@ TEST_CASE("font") { REQUIRE(fonts::try_load_font(f2, make_memory_stream(buffer(mini_fnt, std::strlen(mini_fnt))))); REQUIRE(f == f2); - REQUIRE(f.info().face == "Arial"); - REQUIRE(f.info().size == 50); + REQUIRE(f.info().atlas_file == "arial.png"); + REQUIRE(f.info().atlas_size == v2u(512,512)); + REQUIRE(f.info().font_size == 50); + REQUIRE(f.info().line_height == 58); + REQUIRE(f.info().glyph_ascent == 49); - REQUIRE(f.common().line == 58); - REQUIRE(f.common().base == 49); - REQUIRE(f.common().pages == 1); - REQUIRE(f.common().atlas_size == v2u(512,512)); - - REQUIRE(f.pages().size() == 1); - REQUIRE(f.find_page(0)); - REQUIRE(f.find_page(0)->id == 0); - REQUIRE(f.find_page(0)->file == "arial.png"); - - REQUIRE(f.chars().size() == 3); - REQUIRE_FALSE(f.find_char(100500)); - REQUIRE(f.find_char(33)); + REQUIRE(f.glyphs().size() == 3); + REQUIRE_FALSE(f.find_glyph(100500)); + REQUIRE(f.find_glyph(33)); { // char id=33 x=448 y=386 width=10 height=40 xoffset=5 yoffset=10 xadvance=14 page=0 chnl=0 - font::char_data c; - c.id = 33; - c.rect = b2hi(448, 86, 10, 40); - c.offset = v2hi(5, 10); + font::glyph_info c; + c.offset = v2i(5, 10); + c.tex_rect = b2u(448, 86, 10, 40); c.advance = 14; - c.page = 0; - c.chnl = 0; - REQUIRE(*f.find_char(33) == c); + REQUIRE(*f.find_glyph(33) == c); } REQUIRE(f.kernings().size() == 2);