Test Implemetation of Flood Fill.
This commit is contained in:
@@ -55,7 +55,8 @@ if (WIN32)
|
||||
include/Preferences.hpp
|
||||
src/Utils.cpp
|
||||
include/NewMapDialog.hpp
|
||||
src/Tileset.cpp)
|
||||
src/Tileset.cpp
|
||||
src/Layer.cpp)
|
||||
endif()
|
||||
|
||||
set_target_properties(Editor2D PROPERTIES LINKER_LANGUAGE CXX)
|
||||
|
@@ -140,6 +140,7 @@ public:
|
||||
/// This provides a mapping from a 1D flat array to a 2D grid.
|
||||
/// @return The {x,y} coordinate of the grid-cell that maps to the given index.
|
||||
Vector2i IndexToCell(int index, int width);
|
||||
void FloodFill(int x, int y, int tileid);
|
||||
|
||||
/// This provides a mapping from a 2D grid to a 1D flat array.
|
||||
/// @return The {x,y} coordinate of the grid-cell that maps to the given index.
|
||||
@@ -187,6 +188,9 @@ public:
|
||||
Vector2i GetGridCellFromMouse();
|
||||
|
||||
void CameraUpdate(float elapsed);
|
||||
int GetTile(const Vector2i& cell);
|
||||
int GetTile(int x, int y);
|
||||
void SetTile(int x, int y, int tileID);
|
||||
|
||||
void SetTile(const Vector2i& cell, int tileID);
|
||||
|
||||
|
@@ -6,6 +6,19 @@
|
||||
#include <Colors.hpp>
|
||||
#include <Utils.hpp>
|
||||
|
||||
using namespace JJX;
|
||||
|
||||
/// @class Layer
|
||||
/// @brief Represents a single plane of tiles within a level.
|
||||
///
|
||||
/// A layer stores the grid of tile IDs and properties specific to that layer,
|
||||
/// such as its dimensions, visibility, opacity, and parallax scroll factors.
|
||||
/// The actual tile data is stored in a separate binary file for efficiency.
|
||||
/// Our current chosen format is sequential signed 32-bit integers, one for each tile ID.
|
||||
///
|
||||
/// TODO: Make this configurable
|
||||
///
|
||||
/// TODO: Support endianness management
|
||||
class Layer
|
||||
{
|
||||
public:
|
||||
@@ -17,19 +30,34 @@ public:
|
||||
int cell_height;
|
||||
int** cells;
|
||||
|
||||
float parallax_x;
|
||||
float parallax_y;
|
||||
|
||||
/// The default constructor for Layer initializes member variables to default, zero values.
|
||||
Layer() {}
|
||||
|
||||
/// This constructor creates an empty layer of a given size, and cell-size.
|
||||
Layer(int rows, int cols, int cell_width, int cell_height)
|
||||
{
|
||||
this->rows = rows;
|
||||
this->cols = cols;
|
||||
this->cell_width = cell_width;
|
||||
this->cell_height = cell_height;
|
||||
|
||||
InitGrid();
|
||||
}
|
||||
|
||||
/// Constructs a Layer object by deserializing metadata from a JSON object.
|
||||
/// The binary tile data will need to be loaded separately using `ReadFromFile()`
|
||||
explicit Layer(const json::value& json)
|
||||
{
|
||||
Deserialize(json);
|
||||
}
|
||||
|
||||
|
||||
/// Destructor for Layer cleans up
|
||||
~Layer() = default;
|
||||
|
||||
explicit Layer(const std::filesystem::path& path)
|
||||
{
|
||||
auto [json, err] = json::parse(read_file_contents(path));
|
||||
@@ -54,6 +82,11 @@ public:
|
||||
{
|
||||
name = json["name"].as_string();
|
||||
binary_path = json["binary-path"].as_string();
|
||||
rows = json["rows"].number.value();
|
||||
cols = json["cols"].number.value();
|
||||
cell_width = json["cell-width"].number.value();
|
||||
cell_height = json["cell-height"].number.value();
|
||||
|
||||
}
|
||||
|
||||
void InitGrid()
|
||||
@@ -78,6 +111,17 @@ public:
|
||||
delete[] cells;
|
||||
}
|
||||
|
||||
|
||||
/// Resizes the layer's dimensions and reinitializes the tile data.
|
||||
/// Existing tile data will be lost or truncated.
|
||||
///
|
||||
void Resize(int newWidth, int newHeight)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LoadFromDataBuffer(const int* buffer)
|
||||
{
|
||||
for (int x = 0; x < rows; x++)
|
||||
@@ -103,23 +147,30 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WriteToFile()
|
||||
/// Writes the current tile grid data to a specified raw binary file.
|
||||
/// The file path should typically be stored in the layer's JSON metadata.
|
||||
/// @param path
|
||||
bool WriteBinaryData(const std::filesystem::path& path)
|
||||
{
|
||||
int* buffer = new int[rows*cols];
|
||||
WriteToDataBuffer(buffer);
|
||||
std::ofstream output;
|
||||
output.open(binary_path, std::ios::out | std::ios::binary);
|
||||
output.open(path, std::ios::out | std::ios::binary);
|
||||
output.write(reinterpret_cast<const char*>(buffer), rows * cols * sizeof(int));
|
||||
output.close();
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReadFromFile()
|
||||
bool Save() { return WriteBinaryData(binary_path); }
|
||||
bool Load() { return ReadBinaryData(binary_path); }
|
||||
|
||||
bool ReadBinaryData(const std::filesystem::path& path)
|
||||
{
|
||||
std::ifstream input;
|
||||
input.open(binary_path, std::ios::binary | std::ios::in);
|
||||
input.open(path, std::ios::binary | std::ios::in);
|
||||
input.seekg(0, std::ios::end);
|
||||
int data_length = input.tellg();
|
||||
input.seekg(0, std::ios::beg);
|
||||
@@ -128,12 +179,13 @@ public:
|
||||
input.read(buffer, data_length);
|
||||
input.close();
|
||||
|
||||
InitGrid();
|
||||
LoadFromDataBuffer(reinterpret_cast<int*>(buffer));
|
||||
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
~Layer() { }
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@@ -10,7 +10,6 @@
|
||||
#include <J3ML/LinearAlgebra.hpp>
|
||||
#include "JGL/types/Texture.h"
|
||||
|
||||
|
||||
using namespace JJX;
|
||||
|
||||
struct Quad {
|
||||
@@ -66,28 +65,25 @@ public:
|
||||
/// The number of full columns of tiles in the texture atlast.
|
||||
/// @note This might be redundant if `tiles_per_row` and `tile_count` are used.
|
||||
int cols;
|
||||
///
|
||||
std::vector<json::value> tile_metadata;
|
||||
|
||||
std::vector<Tile> tiles;
|
||||
#pragma endregion
|
||||
#pragma region computed fields
|
||||
/// The total number of tiles defined in this tileset.
|
||||
/// @note This is calculated from `rows * cols` or by iterating
|
||||
int tile_count;
|
||||
/// The number of tiles that fit horizontally in a single row of the texture atlast, taking into account `tile_width` and `tile_spacing`.
|
||||
int tiles_per_row;
|
||||
|
||||
|
||||
std::vector<Quad> quads;
|
||||
|
||||
std::vector<Tile> tiles;
|
||||
|
||||
#pragma endregion
|
||||
public:
|
||||
/// Default constructor for Tileset.
|
||||
/// Default constructor for Tileset .
|
||||
/// Initializes member variables to default or zero values.
|
||||
/// @see CreateDefault().
|
||||
Tileset()
|
||||
{
|
||||
Tileset();
|
||||
|
||||
}
|
||||
/// This constructor is used for the initial definition of a Tileset. i.e. From Editor->Tileset->New
|
||||
Tileset(const std::string& name, const std::filesystem::path& texture_path, int tex_width, int tex_height, int tile_width, int tile_height, int tile_spacing);
|
||||
|
||||
@@ -104,18 +100,15 @@ public:
|
||||
void Deserialize(const json::value& json);
|
||||
|
||||
/// Serializes the current Tileset object's data into a JSON object.
|
||||
///
|
||||
/// @return a JJX::json::value object representing the tileset's data.
|
||||
json::value Serialize() const;
|
||||
|
||||
|
||||
/// Creates and returns a default Tileset object with predefined values.
|
||||
/// This can be useful for initializing a new, empty tileset in the editor.
|
||||
static Tileset CreateDefault() {
|
||||
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/// Converts a 1D tile index to 2D cell coordinates (row, column) within the tileset grid.
|
||||
/// The row and column are 0-based.
|
||||
/// @param index The 0-based 1D index of the tile.
|
||||
/// @warning If the index is out of bounds, the returned coordinates might be invalid (e.g. negative)
|
||||
Vector2i IndexToCell(int index) const;
|
||||
|
||||
inline int CellToIndex(int cx, int cy);
|
||||
|
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/// A Texture atlas that maps to a series of unique tiles with associated metadata.
|
||||
class Tilesheet {
|
||||
public:
|
||||
Tilesheet(JGL::Texture* texture, int tile_width, int tile_height) {
|
||||
|
||||
}
|
||||
};
|
22
libtest.cpp
22
libtest.cpp
@@ -3,12 +3,6 @@
|
||||
#include <Entity.hpp>
|
||||
#include <Layer.hpp>
|
||||
|
||||
|
||||
void CreateSampleLayer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CreateSampleLevel()
|
||||
{
|
||||
Level level;
|
||||
@@ -39,20 +33,28 @@ int main()
|
||||
if (!std::filesystem::exists("level.json"))
|
||||
CreateSampleLevel();
|
||||
|
||||
|
||||
Tileset tileset = Tileset(std::filesystem::path("tileset.json"));
|
||||
|
||||
Tile specialized_tile;
|
||||
specialized_tile.name = "Special Tile";
|
||||
specialized_tile.id = 9999;
|
||||
specialized_tile.quad = {0,0,16,16};
|
||||
json::object meta;
|
||||
meta["color"] = json::string("#FFAAFF");
|
||||
specialized_tile.metadata = meta;
|
||||
|
||||
tileset.tiles.push_back(specialized_tile);
|
||||
|
||||
Level level = Level(std::filesystem::path("level.json"));
|
||||
|
||||
Layer layer(128, 128, 16, 16);
|
||||
layer.binary_path = "test.lvl";
|
||||
layer.ReadFromFile();
|
||||
|
||||
layer.ReadBinaryData();
|
||||
|
||||
level.layers.push_back(layer);
|
||||
|
||||
|
||||
write_file_contents("level.json", json::deparse(level.Serialize()));
|
||||
write_file_contents("tileset.json", json::deparse(tileset.Serialize()));
|
||||
|
||||
std::cout << "Completed all tests" << std::endl;
|
||||
|
||||
|
@@ -20,6 +20,24 @@ Vector2i EditorApp::IndexToCell(int index, int width)
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
|
||||
void EditorApp::FloodFill(int x, int y, int tileid)
|
||||
{
|
||||
|
||||
if (GetTile(x, y) == -1)
|
||||
{
|
||||
SetTile(x, y, tileid);
|
||||
if (GetTile(x-1, y) == -1) FloodFill(x-1, y, tileid);
|
||||
if (GetTile(x+1, y) == -1) FloodFill(x+1, y, tileid);
|
||||
if (GetTile(x, y-1) == -1) FloodFill(x, y-1, tileid);
|
||||
if (GetTile(x, y+1) == -1) FloodFill(x, y+1, tileid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
int EditorApp::CellToIndex(Vector2i cell, int width)
|
||||
{
|
||||
return cell.y*width + cell.x;
|
||||
@@ -400,6 +418,23 @@ void EditorApp::CameraUpdate(float elapsed)
|
||||
|
||||
}
|
||||
|
||||
int EditorApp::GetTile(const Vector2i& cell)
|
||||
{
|
||||
if (cell.x < 0) return -2;
|
||||
if (cell.y < 0) return -2;
|
||||
|
||||
// out of bounds horizontally
|
||||
if (cell.x > grid_rows) return -2;
|
||||
|
||||
// out of bounds vertically
|
||||
if (cell.y > grid_cols) return -2;
|
||||
|
||||
return grid[cell.x][cell.y];
|
||||
}
|
||||
|
||||
int EditorApp::GetTile(int x, int y) { return GetTile({x, y}); }
|
||||
void EditorApp::SetTile(int x, int y, int tileID) { SetTile({x, y}, tileID);}
|
||||
|
||||
void EditorApp::SetTile(const Vector2i& cell, int tileID)
|
||||
{
|
||||
|
||||
@@ -418,6 +453,7 @@ void EditorApp::SetTile(const Vector2i& cell, int tileID)
|
||||
|
||||
void EditorApp::PlaceTileBrush(const Vector2i& cell)
|
||||
{
|
||||
|
||||
if (cell.x < 0) return;
|
||||
|
||||
if (cell.y < 0) return;
|
||||
@@ -428,7 +464,11 @@ void EditorApp::PlaceTileBrush(const Vector2i& cell)
|
||||
// out of bounds vertically
|
||||
if (cell.y > grid_cols) return;
|
||||
|
||||
SetTile(cell, selected_quad);
|
||||
|
||||
if (IsKeyDown(Keys::B))
|
||||
FloodFill(cell.x, cell.y, selected_quad);
|
||||
else
|
||||
SetTile(cell, selected_quad);
|
||||
}
|
||||
|
||||
void EditorApp::EraseTile(const Vector2i& cell)
|
||||
|
1
src/Layer.cpp
Normal file
1
src/Layer.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include <Layer.hpp>
|
@@ -1,7 +1,13 @@
|
||||
#include <Tileset.hpp>
|
||||
|
||||
Tileset::Tileset():
|
||||
tile_width(0),tile_height(0), tile_spacing(0),
|
||||
tex_width(0), tex_height(0), rows(0), cols(0),
|
||||
tile_count(0), tiles_per_row(0)
|
||||
{ }
|
||||
|
||||
Tileset::Tileset(const std::string& name, const std::filesystem::path& texture_path, int tex_width, int tex_height, int tile_width, int tile_height,
|
||||
int tile_spacing)
|
||||
int tile_spacing)
|
||||
{
|
||||
this->name = name;
|
||||
this->file_path = std::format("{}.tileset", name);
|
||||
@@ -60,11 +66,15 @@ void Tileset::Deserialize(const json::value& json)
|
||||
this->tex_width = (int)json["texture-width"].number.value();
|
||||
this->tex_height = (int)json["texture-height"].number.value();
|
||||
|
||||
/*auto tilemeta = json["tiledata"].as_array();
|
||||
for (json::value& v : tilemeta)
|
||||
if (json.as_object().contains("tiles"))
|
||||
{
|
||||
this->tile_metadata.push_back(v);
|
||||
}*/
|
||||
auto tilemeta = json["tiles"].as_array();
|
||||
for (json::value& v : tilemeta)
|
||||
{
|
||||
// this->tiles.push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this->rows = J3ML::Math::Floor(this->tex_width / this->tile_width);
|
||||
this->cols = J3ML::Math::Floor(this->tex_height / this->tile_height);
|
||||
@@ -104,7 +114,7 @@ json::value Tileset::Serialize() const
|
||||
|
||||
tileset["quads"] = quad_array;*/
|
||||
|
||||
/*json::array tiles;
|
||||
json::array tiles;
|
||||
for (auto& tile : this->tiles)
|
||||
{
|
||||
json::object tile_json;
|
||||
@@ -119,7 +129,7 @@ json::value Tileset::Serialize() const
|
||||
tile_json["quad"] = quad;
|
||||
tiles.push_back(tile_json);
|
||||
}
|
||||
tileset["tiles"] = tiles;*/
|
||||
tileset["tiles"] = tiles;
|
||||
|
||||
return tileset;
|
||||
}
|
||||
|
Reference in New Issue
Block a user