Skip to content

Commit

Permalink
Add tileset IR factory function
Browse files Browse the repository at this point in the history
  • Loading branch information
albin-johansson committed Jul 18, 2024
1 parent c4f06e1 commit 9a734f4
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 67 deletions.
28 changes: 24 additions & 4 deletions source/core/inc/tactile/core/tile/tileset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ namespace tactile {
struct CTexture;
struct TilesetSpec;
class Registry;
class IRenderer;

namespace ir {
struct TilesetRef;
} // namespace ir

/**
* Represents an sequence of tile identifiers.
Expand Down Expand Up @@ -104,6 +109,23 @@ auto is_tileset(const Registry& registry, EntityID entity) -> bool;
[[nodiscard]]
auto make_tileset(Registry& registry, const TilesetSpec& spec) -> EntityID;

/**
* Creates a tileset instance from an intermediate representation.
*
* \pre The registry must feature a \c CTileCache context component.
*
* \param registry The associated registry.
* \param renderer The renderer to use for loading textures.
* \param ir_tileset_ref The intermediate tileset representation.
*
* \return
* A tileset entity identifier if successful; an error code otherwise.
*/
[[nodiscard]]
auto make_tileset(Registry& registry,
IRenderer& renderer,
const ir::TilesetRef& ir_tileset_ref) -> Result<EntityID>;

/**
* Initializes a tileset "instance".
*
Expand Down Expand Up @@ -223,8 +245,7 @@ auto find_tileset(const Registry& registry, TileID tile_id) -> EntityID;
* A tile index if successful; nothing otherwise.
*/
[[nodiscard]]
auto get_tile_index(const Registry& registry,
TileID tile_id) -> Maybe<TileIndex>;
auto get_tile_index(const Registry& registry, TileID tile_id) -> Maybe<TileIndex>;

/**
* Indicates whether the tiles in a tile range are available for use.
Expand All @@ -236,8 +257,7 @@ auto get_tile_index(const Registry& registry,
* True if the tile range is available; false otherwise.
*/
[[nodiscard]]
auto is_tile_range_available(const Registry& registry,
const TileRange& range) -> bool;
auto is_tile_range_available(const Registry& registry, const TileRange& range) -> bool;

/**
* Indicates whether a tile range contains a given tile identifier.
Expand Down
185 changes: 141 additions & 44 deletions source/core/src/tactile/core/tile/tileset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

#include "tactile/core/tile/tileset.hpp"

#include <system_error> // make_error_code, errc
#include <utility> // move

#include "tactile/base/io/save/ir.hpp"
#include "tactile/base/numeric/saturate_cast.hpp"
#include "tactile/base/numeric/vec_format.hpp"
#include "tactile/core/debug/assert.hpp"
Expand All @@ -18,9 +22,49 @@
#include "tactile/core/util/lookup.hpp"

namespace tactile {
inline namespace tileset {
namespace tileset {

void _create_tiles(Registry& registry, CTileset& tileset)
[[nodiscard]]
auto add_tileset_component(Registry& registry,
const EntityID tileset_id,
const Int2& texture_size,
const Int2& tile_size) -> Result<void>
{
if (tile_size.x() <= 0 || tile_size.y() <= 0) {
TACTILE_LOG_ERROR("Invalid tileset tile size: {}", tile_size);
return unexpected(std::make_error_code(std::errc::invalid_argument));
}

const MatrixExtent extent {
.rows = static_cast<MatrixExtent::value_type>(texture_size.y() / tile_size.y()),
.cols = static_cast<MatrixExtent::value_type>(texture_size.x() / tile_size.x()),
};

if (extent.rows <= 0 || extent.cols <= 0) {
TACTILE_LOG_ERROR("Invalid tileset extent: {}", extent);
return unexpected(std::make_error_code(std::errc::invalid_argument));
}

const auto tileset_entity = registry.make_entity();

auto& tileset = registry.add<CTileset>(tileset_id);
tileset.tile_size = tile_size;
tileset.extent = extent;

return kOK;
}

void add_viewport_component(Registry& registry,
const EntityID tileset_id,
const Int2& texture_size)
{
auto& viewport = registry.add<CViewport>(tileset_id);
viewport.pos = Float2 {0.0f, 0.0f};
viewport.size = vec_cast<Float2>(texture_size) * 0.5f;
viewport.scale = 1.0f;
}

void create_tiles(Registry& registry, CTileset& tileset)
{
const auto tile_count = tileset.extent.rows * tileset.extent.cols;
tileset.tiles.reserve(saturate_cast<usize>(tile_count));
Expand All @@ -33,6 +77,39 @@ void _create_tiles(Registry& registry, CTileset& tileset)
}
}

[[nodiscard]]
auto create_tiles(Registry& registry,
CTileset& tileset,
const ir::Tileset& ir_tileset) -> Result<void>
{
tileset.tiles.resize(saturate_cast<usize>(ir_tileset.tile_count), kInvalidEntity);

for (const auto& ir_tile : ir_tileset.tiles) {
const auto tile_id = make_tile(registry, ir_tile);
if (!tile_id.has_value()) {
return propagate_unexpected(tile_id);
}

const auto tile_index = saturate_cast<usize>(ir_tile.index);
if (tile_index >= tileset.tiles.size()) {
return unexpected(std::make_error_code(std::errc::result_out_of_range));
}

tileset.tiles[tile_index] = *tile_id;
}

TileIndex tile_index {0};
for (auto& tile_id : tileset.tiles) {
if (tile_id == kInvalidEntity) {
tile_id = make_tile(registry, tile_index);
}

++tile_index;
}

return kOK;
}

} // namespace tileset

auto is_tileset(const Registry& registry, const EntityID entity) -> bool
Expand All @@ -45,47 +122,74 @@ auto is_tileset(const Registry& registry, const EntityID entity) -> bool

auto make_tileset(Registry& registry, const TilesetSpec& spec) -> EntityID
{
const SetLogScope log_scope {"Tileset"};
const auto tileset_id = registry.make_entity();

if (spec.tile_size.x() <= 0 || spec.tile_size.y() <= 0) {
TACTILE_LOG_ERROR("Tried to create tileset with invalid tile size: {}",
spec.tile_size);
const auto add_tileset_result =
tileset::add_tileset_component(registry, tileset_id, spec.texture.size, spec.tile_size);
if (!add_tileset_result.has_value()) {
return kInvalidEntity;
}

const MatrixExtent extent {
.rows = static_cast<MatrixExtent::value_type>(spec.texture.size.y() /
spec.tile_size.y()),
.cols = static_cast<MatrixExtent::value_type>(spec.texture.size.x() /
spec.tile_size.x()),
};
tileset::add_viewport_component(registry, tileset_id, spec.texture.size);

if (extent.rows <= 0 || extent.cols <= 0) {
TACTILE_LOG_ERROR("Tried to create tileset with invalid extent: {}",
extent);
registry.add<CMeta>(tileset_id);
registry.add<CTexture>(tileset_id, spec.texture);

auto& tileset = registry.get<CTileset>(tileset_id);
tileset::create_tiles(registry, tileset);

TACTILE_ASSERT(is_tileset(registry, tileset_id));
TACTILE_ASSERT(tileset.extent.rows > 0);
TACTILE_ASSERT(tileset.extent.cols > 0);
return tileset_id;
}

auto make_tileset(Registry& registry,
IRenderer& renderer,
const ir::TilesetRef& ir_tileset_ref) -> Result<EntityID>
{
auto texture_result = load_texture(renderer, ir_tileset_ref.tileset.image_path);
if (!texture_result.has_value()) {
return propagate_unexpected(texture_result);
}

const auto tileset_id = registry.make_entity();
registry.add<CMeta>(tileset_id);

auto& texture = registry.add<CTexture>(tileset_id, std::move(*texture_result));

tileset::add_viewport_component(registry, tileset_id, texture.size);

const auto add_tileset_result =
tileset::add_tileset_component(registry,
tileset_id,
texture.size,
ir_tileset_ref.tileset.tile_size);
if (!add_tileset_result.has_value()) {
return kInvalidEntity;
}

const auto tileset_entity = registry.make_entity();
auto& tileset = registry.get<CTileset>(tileset_id);

auto& tileset = registry.add<CTileset>(tileset_entity);
tileset.tile_size = spec.tile_size;
tileset.extent = extent;
const auto create_tiles_result =
tileset::create_tiles(registry, tileset, ir_tileset_ref.tileset);
if (!create_tiles_result.has_value()) {
return propagate_unexpected(create_tiles_result);
}

auto& viewport = registry.add<CViewport>(tileset_entity);
viewport.pos = Float2 {0.0f, 0.0f};
viewport.size = vec_cast<Float2>(spec.texture.size) * 0.5f;
viewport.scale = 1.0f;
const auto init_instance_result =
init_tileset_instance(registry, tileset_id, ir_tileset_ref.first_tile_id);
if (!init_instance_result.has_value()) {
return propagate_unexpected(init_instance_result);
}

registry.add<CMeta>(tileset_entity);
registry.add<CTexture>(tileset_entity, spec.texture);
auto& tileset_instance = registry.get<CTilesetInstance>(tileset_id);
tileset_instance.is_embedded = ir_tileset_ref.tileset.is_embedded;

_create_tiles(registry, tileset);
convert_ir_metadata(registry, tileset_id, ir_tileset_ref.tileset.meta);

TACTILE_ASSERT(is_tileset(registry, tileset_entity));
TACTILE_ASSERT(tileset.extent.rows > 0);
TACTILE_ASSERT(tileset.extent.cols > 0);
return tileset_entity;
TACTILE_ASSERT(is_tileset(registry, tileset_id));
return tileset_id;
}

auto init_tileset_instance(Registry& registry,
Expand All @@ -96,8 +200,7 @@ auto init_tileset_instance(Registry& registry,
const SetLogScope log_scope {"Tileset"};

if (first_tile_id < TileID {1}) {
TACTILE_LOG_ERROR("Tried to use invalid first tile identifier: {}",
first_tile_id);
TACTILE_LOG_ERROR("Tried to use invalid first tile identifier: {}", first_tile_id);
return unexpected(make_error(GenericError::kInvalidParam));
}

Expand Down Expand Up @@ -125,8 +228,7 @@ auto init_tileset_instance(Registry& registry,
instance.is_embedded = false; // TODO

auto& tile_cache = registry.get<CTileCache>();
tile_cache.tileset_mapping.reserve(tile_cache.tileset_mapping.size() +
tileset.tiles.size());
tile_cache.tileset_mapping.reserve(tile_cache.tileset_mapping.size() + tileset.tiles.size());

for (int32 index = 0; index < tile_range.count; ++index) {
const TileID tile_id {tile_range.first_id + index};
Expand Down Expand Up @@ -159,8 +261,7 @@ void destroy_tileset(Registry& registry, const EntityID tileset_entity)
registry.destroy(tileset_entity);
}

auto copy_tileset(Registry& registry,
const EntityID old_tileset_entity) -> EntityID
auto copy_tileset(Registry& registry, const EntityID old_tileset_entity) -> EntityID
{
TACTILE_ASSERT(is_tileset(registry, old_tileset_entity));
const auto& old_meta = registry.get<CMeta>(old_tileset_entity);
Expand All @@ -179,8 +280,7 @@ auto copy_tileset(Registry& registry,
new_tileset.tiles.push_back(copy_tile(registry, old_tile_entity));
}

if (const auto* instance =
registry.find<CTilesetInstance>(old_tileset_entity)) {
if (const auto* instance = registry.find<CTilesetInstance>(old_tileset_entity)) {
registry.add<CTilesetInstance>(new_tileset_entity, *instance);
}

Expand Down Expand Up @@ -215,8 +315,7 @@ auto find_tileset(const Registry& registry, const TileID tile_id) -> EntityID
return kInvalidEntity;
}

auto get_tile_index(const Registry& registry,
const TileID tile_id) -> Maybe<TileIndex>
auto get_tile_index(const Registry& registry, const TileID tile_id) -> Maybe<TileIndex>
{
TACTILE_ASSERT(registry.has<CTileCache>());

Expand All @@ -230,8 +329,7 @@ auto get_tile_index(const Registry& registry,
return kNone;
}

auto is_tile_range_available(const Registry& registry,
const TileRange& range) -> bool
auto is_tile_range_available(const Registry& registry, const TileRange& range) -> bool
{
const auto first_tile = range.first_id;
const auto last_tile = range.first_id + range.count - 1;
Expand All @@ -240,8 +338,7 @@ auto is_tile_range_available(const Registry& registry,
return false;
}

for (const auto& [tileset_entity, instance] :
registry.each<CTilesetInstance>()) {
for (const auto& [tileset_entity, instance] : registry.each<CTilesetInstance>()) {
if (has_tile(instance.tile_range, first_tile) ||
has_tile(instance.tile_range, last_tile)) {
return false;
Expand Down
1 change: 1 addition & 0 deletions source/core/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,6 @@ target_link_libraries(tactile-core-test
PRIVATE
tactile::core
tactile::base_test_util
tactile::null_renderer
GTest::gtest
)
4 changes: 4 additions & 0 deletions source/core/test/inc/tactile/core/test/ir_comparison.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,8 @@ void compare_layer(const Registry& registry, EntityID layer_id, const ir::Layer&

void compare_tile(const Registry& registry, EntityID tile_id, const ir::Tile& ir_tile);

void compare_tileset(const Registry& registry,
EntityID tileset_id,
const ir::TilesetRef& ir_tileset_ref);

} // namespace tactile::test
Loading

0 comments on commit 9a734f4

Please sign in to comment.