Refactoring **everything**
This commit is contained in:
@@ -33,7 +33,7 @@ CPMAddPackage(NAME Event
|
||||
URL https://git.redacted.cc/josh/Event/archive/Release-12.zip)
|
||||
|
||||
CPMAddPackage(NAME jjx
|
||||
URL https://git.redacted.cc/josh/jjx/archive/Release-1.1.zip)
|
||||
URL https://git.redacted.cc/josh/jjx/archive/Release-1.2.zip)
|
||||
|
||||
CPMAddPackage(NAME J3ML
|
||||
URL https://git.redacted.cc/josh/j3ml/archive/3.4.5.zip)
|
||||
@@ -53,16 +53,12 @@ if (UNIX)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
ADD_LIBRARY(Editor2D STATIC ${EDITOR_SRC}
|
||||
include/Preferences.hpp
|
||||
src/Utils.cpp
|
||||
include/NewMapDialog.hpp
|
||||
src/Tileset.cpp
|
||||
src/Layer.cpp)
|
||||
ADD_LIBRARY(Editor2D STATIC ${EDITOR_SRC})
|
||||
endif()
|
||||
|
||||
set_target_properties(Editor2D PROPERTIES LINKER_LANGUAGE CXX)
|
||||
|
||||
# TODO: Separate dependency packages such that only bare-minimum required is compiled with the just the lib.
|
||||
target_include_directories(Editor2D PUBLIC
|
||||
${Event_SOURCE_DIR}/include
|
||||
${J3ML_SOURCE_DIR}/include
|
||||
|
8
include/App/Editor.hpp
Normal file
8
include/App/Editor.hpp
Normal file
@@ -0,0 +1,8 @@
|
||||
//
|
||||
// Created by josh on 5/29/2025.
|
||||
//
|
||||
|
||||
#ifndef EDITOR_HPP
|
||||
#define EDITOR_HPP
|
||||
|
||||
#endif //EDITOR_HPP
|
@@ -13,7 +13,11 @@
|
||||
#include "NewMapDialog.hpp"
|
||||
#include "JUI/Widgets/ColorPicker.hpp"
|
||||
#include "Preferences.hpp"
|
||||
#include <Level.hpp>
|
||||
#include <Data/Level.hpp>
|
||||
#include <Data/Tileset.hpp>
|
||||
#include <JUI/Widgets/Checkbox.hpp>
|
||||
#include <App/LayerView.hpp>
|
||||
#include <App/TilesetView.hpp>
|
||||
|
||||
#define GL_VER_MAJOR 2
|
||||
#define GL_VER_MINOR 1
|
||||
@@ -21,7 +25,10 @@
|
||||
using namespace ReWindow;
|
||||
using namespace JUI::UDimLiterals;
|
||||
|
||||
#include <EditorCamera.hpp>
|
||||
#include <App/EditorCamera.hpp>
|
||||
|
||||
// TODO: Support loading an entity_prefab.json file which defines template entities to copy.
|
||||
// TODO: Support sprites for these?
|
||||
|
||||
class Action {
|
||||
public:
|
||||
@@ -61,85 +68,55 @@ class EditHistoryLogWindow : public JUI::Window {
|
||||
public:
|
||||
};
|
||||
|
||||
class LayerView : public JUI::Window
|
||||
{
|
||||
public:
|
||||
LayerView() : JUI::Window()
|
||||
{
|
||||
this->SetTitle("Layers");
|
||||
Name("LayerView");
|
||||
}
|
||||
explicit LayerView(Widget* parent) : LayerView()
|
||||
{
|
||||
Parent(parent);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class EditorApp : public OpenGLWindow {
|
||||
public:
|
||||
#pragma region GUI Elements
|
||||
JUI::Scene* scene = nullptr;
|
||||
JUI::CommandLine* console = nullptr;
|
||||
JUI::Window* tileset_viewer = nullptr;
|
||||
JUI::Rect* cell_indicator = nullptr;
|
||||
NewMapDialog* nmd = nullptr;
|
||||
LayerView* layer_view = nullptr;
|
||||
JUI::Window* bg_color_tool_window = nullptr;
|
||||
JUI::ColorPicker* bg_color_tool = nullptr;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Map Data
|
||||
Level* loaded_level;
|
||||
Tileset* loaded_tileset;
|
||||
JGL::Texture* loaded_tilesheet;
|
||||
#pragma endregion
|
||||
EditorCamera camera;
|
||||
|
||||
JUI::Scene* scene;
|
||||
JUI::CommandLine* console;
|
||||
JUI::Window* tileset_viewer;
|
||||
JUI::Rect* cell_indicator;
|
||||
JGL::Texture* test_tilesheet;
|
||||
LayerView* layer_view;
|
||||
|
||||
NewMapDialog* nmd = nullptr;
|
||||
|
||||
Level loaded_level;
|
||||
|
||||
Preferences preferences;
|
||||
|
||||
JUI::Window* bg_color_tool_window = nullptr;
|
||||
JUI::ColorPicker* bg_color_tool = nullptr;
|
||||
|
||||
int focus_layer_index = 0;
|
||||
|
||||
Layer* GetLayer(int index)
|
||||
{
|
||||
return loaded_level->layers[index];
|
||||
}
|
||||
Layer* GetFocusLayer()
|
||||
{
|
||||
return loaded_level->layers[focus_layer_index];
|
||||
}
|
||||
|
||||
bool grid_overlay_enabled = true;
|
||||
Color4 grid_overlay_color = {255, 255, 255, 64};
|
||||
Color4 bg_color = Colors::Black;
|
||||
|
||||
|
||||
bool show_cell_pointer_outline = true;
|
||||
Color4 cell_pointer_outline_color = Colors::Blues::LightSteelBlue;
|
||||
float cell_pointer_outline_width = 1.f;
|
||||
|
||||
|
||||
int grid_pixel_width = 16;
|
||||
int grid_pixel_height = 16;
|
||||
|
||||
int tileset_width = 420;
|
||||
int tileset_height = 420;
|
||||
|
||||
int grid_rows = 128;
|
||||
int grid_cols = 128;
|
||||
|
||||
int** grid;
|
||||
|
||||
void InitGrid()
|
||||
{
|
||||
grid = new int*[grid_rows];
|
||||
|
||||
for (int i = 0; i < grid_rows; i++)
|
||||
grid[i] = new int[grid_cols];
|
||||
|
||||
for (int x = 0; x < grid_rows; x++) {
|
||||
for (int y = 0; y < grid_cols; y++) {
|
||||
grid[x][y] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteGrid()
|
||||
{
|
||||
for (int i = 0; i < grid_rows; i++)
|
||||
delete[] grid[i];
|
||||
|
||||
delete[] grid;
|
||||
}
|
||||
|
||||
int selected_quad = 0;
|
||||
|
||||
bool data_ready = false;
|
||||
|
||||
struct Quad {
|
||||
int x;
|
||||
int y;
|
||||
@@ -147,7 +124,7 @@ public:
|
||||
int h;
|
||||
};
|
||||
|
||||
std::vector<Quad> quads;
|
||||
//std::vector<Quad> quads;
|
||||
|
||||
/// Fill the tile-id lookup table with their respective partial-sprite bounding boxes.
|
||||
void PopulateQuads();
|
||||
@@ -165,7 +142,7 @@ public:
|
||||
EditorApp();
|
||||
~EditorApp() override
|
||||
{
|
||||
DeleteGrid();
|
||||
//DeleteGrid();
|
||||
}
|
||||
|
||||
void LoadPreferences();
|
||||
@@ -185,6 +162,7 @@ public:
|
||||
/// @note Placeholder until FileDialogs are added.
|
||||
/// @note Placeholder until Tileset json is added.
|
||||
void LoadMisc();
|
||||
void SizeCellIndicatorToTilesetSize();
|
||||
|
||||
/// Creates a JUI widget that displays the Tileset, and lets you select a tile from it.
|
||||
JUI::Window* CreateTilesetViewerWindow(JUI::Widget* parent);
|
||||
@@ -229,6 +207,47 @@ public:
|
||||
|
||||
void DrawTiles();
|
||||
|
||||
void DrawLayer(const Layer* layer) const
|
||||
{
|
||||
for (int gx = 0; gx < layer->rows; gx++) {
|
||||
for (int gy = 0; gy < layer->cols; gy++) {
|
||||
|
||||
auto quad_idx = layer->cells[gx][gy];
|
||||
|
||||
Vector2 pos(gx*layer->cell_width, gy*layer->cell_height);
|
||||
|
||||
//if ((gx+gy) % 2 == 0)
|
||||
//J2D::FillRect(Colors::Purples::Fuchsia, pos, {16, 16});
|
||||
|
||||
if (quad_idx < 0)
|
||||
continue;
|
||||
|
||||
auto quad = loaded_tileset->quads[quad_idx];
|
||||
|
||||
J2D::DrawPartialSprite(loaded_tilesheet, pos,
|
||||
Vector2(quad.x, quad.y), Vector2(quad.w, quad.h));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawLevel(const Level* level)
|
||||
{
|
||||
// TODO: Draw Per-Level Background Texture.
|
||||
|
||||
// TODO: Draw Per-Layer Background Texture.
|
||||
for (const auto* layer : level->layers)
|
||||
{
|
||||
DrawLayer(layer);
|
||||
}
|
||||
|
||||
// TODO: Support a handful of presets to represent entity shape types.
|
||||
for (auto entity : level->entities)
|
||||
{
|
||||
J2D::DrawPoint(entity->overlay_color, entity->x, entity->y, 1);
|
||||
J2D::DrawString(Colors::White, std::format("{} - {}", entity->type, entity->name), entity->x, entity->y, 1.f, 12);
|
||||
}
|
||||
}
|
||||
|
||||
void Draw();
|
||||
|
||||
// TODO: Closing the app nominally doesn't work on Linux.
|
||||
@@ -259,7 +278,7 @@ public:
|
||||
if (btn == JUI::MouseButton::Middle)
|
||||
{
|
||||
Vector2i cell = GetGridCellFromMouse();
|
||||
selected_quad = grid[cell.x][cell.y];
|
||||
selected_quad = loaded_level->layers[0]->cells[cell.x][cell.y];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +288,7 @@ public:
|
||||
|
||||
if (tileset_viewer->Content()->IsMouseInside()) {
|
||||
Vector2i cell = GetTilesetCellFromMouse();
|
||||
int index = CellToIndex(cell, tileset_width);
|
||||
int index = CellToIndex(cell, loaded_tileset->rows);
|
||||
selected_quad = index;
|
||||
|
||||
}
|
39
include/App/LayerView.hpp
Normal file
39
include/App/LayerView.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Widgets/Window.hpp>
|
||||
#include <JUI/Widgets/Button.hpp>
|
||||
#include <JUI/Widgets/ScrollingRect.hpp>
|
||||
#include <JUI/Widgets/ListLayout.hpp>
|
||||
#include <JUI/Widgets/Checkbox.hpp>
|
||||
|
||||
#include <Data/Level.hpp>
|
||||
|
||||
class LayerView : public JUI::Window
|
||||
{
|
||||
public:
|
||||
LayerView() : JUI::Window()
|
||||
{
|
||||
this->SetTitle("Layers");
|
||||
Name("LayerView");
|
||||
|
||||
scroller = new JUI::ScrollingRect(this->Content());
|
||||
|
||||
layout = new JUI::VerticalListLayout(scroller);
|
||||
|
||||
}
|
||||
explicit LayerView(Widget* parent) : LayerView()
|
||||
{
|
||||
Parent(parent);
|
||||
}
|
||||
void UpdateComponents(Level* level)
|
||||
{
|
||||
for (auto layer : level->layers)
|
||||
{
|
||||
//auto* rect = new JUI::Rect(layout);
|
||||
//auto* vis_chk = new JUI::Checkbox(rect);
|
||||
}
|
||||
}
|
||||
|
||||
JUI::ScrollingRect* scroller = nullptr;
|
||||
JUI::VerticalListLayout* layout = nullptr;
|
||||
};
|
2
include/App/TilesetView.hpp
Normal file
2
include/App/TilesetView.hpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
|
61
include/Data/Entity.hpp
Normal file
61
include/Data/Entity.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <JJX/json.hpp>
|
||||
#include <Color4.hpp>
|
||||
|
||||
using namespace JJX;
|
||||
|
||||
enum class EntityRepresentativeShape {
|
||||
Point, AABB, Sphere, OBB
|
||||
};
|
||||
|
||||
|
||||
/// @class Entity
|
||||
/// @brief Represents an object or interactive element placed on the map, not necessarily tied to the tile grid.
|
||||
/// This can include plater spawn points, enemies, pickups, triggers, boxes (my favorite), or any other
|
||||
/// game specific object that needs to be placed and configured in the level.
|
||||
class Entity
|
||||
{
|
||||
public:
|
||||
#pragma region json properties
|
||||
|
||||
std::string name;
|
||||
std::string type;
|
||||
float x;
|
||||
float y;
|
||||
float width;
|
||||
float height;
|
||||
float rotation;
|
||||
float flip_h;
|
||||
float flip_v;
|
||||
int z_index;
|
||||
Color4 overlay_color;
|
||||
json::value metadata;
|
||||
#pragma endregion
|
||||
Entity();
|
||||
Entity(const json::value& json)
|
||||
{
|
||||
Deserialize(json);
|
||||
}
|
||||
|
||||
|
||||
json::value Serialize() const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Deserialize(const json::value& json)
|
||||
{
|
||||
name = json["name"].as_string();
|
||||
type = json["type"].as_string();
|
||||
x = json["x"].number.value();
|
||||
y = json["y"].number.value();
|
||||
width = json["width"].number.value_or(0);
|
||||
height = json["height"].number.value_or(0);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
7
include/Data/Etc.hpp
Normal file
7
include/Data/Etc.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace LevelFormat
|
||||
{
|
||||
|
||||
};
|
5
include/Data/Format.hpp
Normal file
5
include/Data/Format.hpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include <Data/Entity.hpp>
|
||||
#include <Data/Layer.hpp>
|
||||
#include <Data/Tileset.hpp>
|
||||
#include <Data/Level.hpp>
|
||||
#include <Data/Tile.hpp>
|
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <Layer.hpp>
|
||||
#include <Data/Layer.hpp>
|
||||
#include <JJX/JSON.hpp>
|
||||
#include <Colors.hpp>
|
||||
#include <Utils.hpp>
|
||||
@@ -41,7 +41,10 @@ public:
|
||||
int cell_width;
|
||||
/// The height of the individual cells of this layer, measured in pixels.
|
||||
int cell_height;
|
||||
|
||||
/// The horizontal parallax scrolling factor for this layer.
|
||||
float parallax_x;
|
||||
/// The vertical parallax scrolling factor for this layer.
|
||||
float parallax_y;
|
||||
|
||||
bool visible;
|
||||
@@ -51,7 +54,7 @@ public:
|
||||
#pragma endregion
|
||||
|
||||
#pragma region binary data storage
|
||||
int** cells;
|
||||
int** cells = nullptr;
|
||||
#pragma endregion
|
||||
|
||||
|
||||
@@ -69,10 +72,7 @@ public:
|
||||
|
||||
|
||||
/// Destructor for Layer cleans up all dynamically allocated memory for the tile grid.
|
||||
~Layer()
|
||||
{
|
||||
DeleteGrid();
|
||||
}
|
||||
~Layer();
|
||||
|
||||
|
||||
/// Constructs a layer object from the JSON contained in the file at the given path.
|
||||
@@ -104,58 +104,26 @@ public:
|
||||
|
||||
void LoadFromDataBuffer(const int* buffer);
|
||||
|
||||
void WriteToDataBuffer(int* buffer)
|
||||
{
|
||||
//int* buffer = new int[rows*cols];
|
||||
for (int x = 0; x < rows; x++)
|
||||
{
|
||||
for (int y = 0; y < cols; y++)
|
||||
{
|
||||
int index = (x*cols + y);
|
||||
buffer[index] = cells[x][y];
|
||||
}
|
||||
}
|
||||
}
|
||||
void WriteToDataBuffer(int* buffer) const;
|
||||
|
||||
/// 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
|
||||
/// @return True if the binary data was written successfully, false otherwise.
|
||||
/// @see Save().
|
||||
bool WriteBinaryData(const std::filesystem::path& path) const;
|
||||
|
||||
bool Save();
|
||||
bool Load();
|
||||
|
||||
/// Reads tile grid data from a specified raw binary file.
|
||||
/// The file path should typically be stored in the layer's JSON metadata.
|
||||
/// @param path
|
||||
/// @return True if the binary data was loaded successfully, false otherwise.
|
||||
bool WriteBinaryData(const std::filesystem::path& path)
|
||||
{
|
||||
int* buffer = new int[rows*cols];
|
||||
WriteToDataBuffer(buffer);
|
||||
std::ofstream output;
|
||||
output.open(path, std::ios::out | std::ios::binary);
|
||||
output.write(reinterpret_cast<const char*>(buffer), rows * cols * sizeof(int));
|
||||
output.close();
|
||||
/// @see Load().
|
||||
bool ReadBinaryData(const std::filesystem::path& path);
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Save() { return WriteBinaryData(binary_path); }
|
||||
bool Load() { return ReadBinaryData(binary_path); }
|
||||
|
||||
bool ReadBinaryData(const std::filesystem::path& path)
|
||||
{
|
||||
std::ifstream input;
|
||||
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);
|
||||
|
||||
char* buffer = new char[data_length];
|
||||
input.read(buffer, data_length);
|
||||
input.close();
|
||||
|
||||
InitGrid();
|
||||
LoadFromDataBuffer(reinterpret_cast<int*>(buffer));
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
return true;
|
||||
}
|
||||
protected:
|
||||
bool grid_allocated = false;
|
||||
|
||||
};
|
127
include/Data/Level.hpp
Normal file
127
include/Data/Level.hpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <Data/Layer.hpp>
|
||||
#include <Data/Entity.hpp>
|
||||
#include <JJX/JSON.hpp>
|
||||
#include <Colors.hpp>
|
||||
#include <Utils.hpp>
|
||||
|
||||
#include "Tileset.hpp"
|
||||
|
||||
using namespace JJX;
|
||||
|
||||
const float REDACTED_EDITOR_LIB_VERSION = 1;
|
||||
|
||||
/// @class Level
|
||||
/// @brief Represents the complete game level, containing its metadata, tile layers, and entites.
|
||||
///
|
||||
/// This class serves as the primary data structure for loading, managing, and saving
|
||||
/// all components that constitute the playable game map. It centralizes information
|
||||
/// about the level's dimensions, graphical assets (tilesets), stuctural layers,
|
||||
/// and interactive objects (entities).
|
||||
class Level {
|
||||
public:
|
||||
/// The human-readable name of the level.
|
||||
/// This is typically displayed in level selection menus or editors.
|
||||
std::string name;
|
||||
/// A brief description of the level's content or purpose.
|
||||
/// Useful for level browsers or internal documentation.
|
||||
std::string description;
|
||||
/// The name or identifier of the author who created or last modified the level.
|
||||
std::string author;
|
||||
/// A collection of keywords or categories associated with the level.
|
||||
/// Can be used for filtering, searching, or thematic grouping (e.g., "forest", "puzzle").
|
||||
std::vector<std::string> tags;
|
||||
/// The height of the level grid in number of tiles (rows).
|
||||
// TODO: I mixed up rows and columns. I'm just going to leave it as the convention this system follows for now. -josh
|
||||
int rows;
|
||||
/// The width of the level grid in number of tiles (columns).
|
||||
int cols;
|
||||
/// A collection of pointers to the tile layers that compose the level's tile grid.
|
||||
/// Each layer typically represents a distinct plane of tiles (e.g., background, foreground, collisions).
|
||||
/// @warning The `Level` class is responsible for the ownership and lifecycle of these `Layer` objects
|
||||
/// if they are allocated dynamically. Consider using smart pointers like `std::unique_ptr<Layer>
|
||||
/// for automatic memory management,
|
||||
std::vector<Layer*> layers;
|
||||
/// A collection of pointers to the entities placed within the level.
|
||||
/// Entities represent interactive objects, characters, spawn points, or other dynamic elements.
|
||||
/// @warning Similar to `layers`, the `Level` class is responsible for the ownership and lifecycle
|
||||
/// of these `Entity` objects if they are dynamically allocated. Consider `std::unique_ptr<Entity>`.
|
||||
std::vector<Entity*> entities;
|
||||
/// The filesystem path to the primary tileset image or definition file used by this level.
|
||||
/// This path is typically relative to the level's main JSON file.
|
||||
std::string tileset_path;
|
||||
/// A generic JSON value for storing arbitrary, unstructured level metdata.
|
||||
/// This can include game-specific settings, editor-specific flags, or any data
|
||||
/// not explicitly covered by other `Level` members.
|
||||
json::value metadata;
|
||||
|
||||
#pragma region Member Functions
|
||||
/// The default constructor initializes all member variables to their default or empty states.
|
||||
/// The level will have zero dimensions, no layers, no entities, and empty metadata.
|
||||
Level();
|
||||
/// Destructor for the Level class.
|
||||
/// Responsible for deallocating all dynamically allocated `Layer` and `Entity` objects
|
||||
/// pointed to by the `layers` and `entities` vectors to prevent memory leaks.
|
||||
~Level();
|
||||
|
||||
/// Constructs a Level object by loading and parsing a level definition file from the given path.
|
||||
/// This constructor typically reads a JSON file containing the level's metadata, and
|
||||
/// may also initiate the loading of associated binary tile data or other assets. NOTE: Currently, we do not.
|
||||
/// @param path
|
||||
/// @throws std::runtime_error If the file cannot be opened, is malformed, or if associated data (like binary layers) is missing / invalid.
|
||||
explicit Level(const std::filesystem::path& path);
|
||||
|
||||
/// Constructs a Level object by deserializing its state from a pre-existing JSON value.
|
||||
/// This constructor assumes the JSON value is already loaded in memory and correct.
|
||||
/// It will parse the JSON to populate the level's members, including layers and entities.
|
||||
/// @param json
|
||||
/// @throws json::exception If the JSON structure is malformed.
|
||||
/// @see Deserialize() for the underlying parsing logic.
|
||||
explicit Level(const json::value& json);
|
||||
|
||||
/// Deserializes the Level object's state from a provided JSON value.
|
||||
/// This method takes a JSON object (typically parsed from a file) and populates
|
||||
/// the current `Level` instance's member variables, including its layers and entities.
|
||||
/// This is the core parsing logic for loading level data into memory.
|
||||
/// @param json
|
||||
/// @throws json::exception If the JSON structure is malformed.
|
||||
void Deserialize(const json::value& json);
|
||||
|
||||
/// Serializes the current Level object's state into a JSON value.
|
||||
/// This method converts all the level's metadata, layer properties, and entity data
|
||||
/// into a json object, ready for saving to a file or transmitting.
|
||||
/// @return a `json` value representing the complete level data.
|
||||
/// @see Level::Save() for writing the serialized JSON to a file.
|
||||
json::value Serialize() const;
|
||||
|
||||
|
||||
bool Load(const std::filesystem::path& path);
|
||||
bool Save(const std::filesystem::path& path);
|
||||
|
||||
Layer* CreateLayer(const std::string& name);
|
||||
Layer* GetLayer(int index);
|
||||
const Layer* GetLayer(int index) const;
|
||||
Layer* FindLayer(const std::string& name);
|
||||
const Layer* FindLayer(const std::string& name) const;
|
||||
bool RemoveLayer(const std::string& name);
|
||||
bool RemoveLayer(int index);
|
||||
bool RemoveLayer(Layer* layer);
|
||||
bool RemoveAllLayers();
|
||||
|
||||
Entity* CreateEntity(const std::string& name);
|
||||
Entity* GetEntity(const std::string& name);
|
||||
const Entity* GetEntity(const std::string& name) const;
|
||||
bool RemoveEntity(Entity* entity);
|
||||
|
||||
Tile GetTileData(int gid) const;
|
||||
Quad GetTileQuad(int gid) const;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
|
||||
|
3
include/Data/Tile.hpp
Normal file
3
include/Data/Tile.hpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
|
@@ -79,6 +79,22 @@ public:
|
||||
|
||||
#pragma endregion
|
||||
public:
|
||||
|
||||
static Tileset Template()
|
||||
{
|
||||
Tileset tileset;
|
||||
|
||||
tileset.author = "J. O'Leary";
|
||||
tileset.name = "Sample Tileset";
|
||||
tileset.file_path = "tileset.json";
|
||||
tileset.cols = 80;
|
||||
tileset.rows = 64;
|
||||
tileset.texture_path = "../megacommando.png";
|
||||
tileset.tex_width = 1024;
|
||||
tileset.tex_height = 1280;
|
||||
return tileset;
|
||||
}
|
||||
|
||||
/// Default constructor for Tileset .
|
||||
/// Initializes member variables to default or zero values.
|
||||
/// @see CreateDefault().
|
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
class Editor {
|
||||
public:
|
||||
protected:
|
||||
private:
|
||||
|
||||
|
||||
};
|
@@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
/// @class Entity
|
||||
/// @brief Represents an object or interactive element placed on the map, not necessarily tied to the tile grid.
|
||||
/// This can include plater spawn points, enemies, pickups, triggers, boxes (my favorite), or any other
|
||||
/// game specific object that needs to be placed and configured in the level.
|
||||
class Entity
|
||||
{
|
||||
public:
|
||||
#pragma region json properties
|
||||
|
||||
std::string name;
|
||||
std::string type;
|
||||
float x;
|
||||
float y;
|
||||
#pragma endregion
|
||||
Entity();
|
||||
Entity(const json::value& json);
|
||||
|
||||
|
||||
};
|
@@ -1,85 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <Layer.hpp>
|
||||
#include <JJX/JSON.hpp>
|
||||
#include <Colors.hpp>
|
||||
#include <Utils.hpp>
|
||||
|
||||
using namespace JJX;
|
||||
|
||||
const float REDACTED_EDITOR_LIB_VERSION = 1;
|
||||
|
||||
class Level {
|
||||
public:
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string author;
|
||||
std::vector<std::string> tags;
|
||||
int rows;
|
||||
int cols;
|
||||
std::vector<Layer> layers;
|
||||
std::string tileset_path;
|
||||
json::value metadata;
|
||||
|
||||
Level() {}
|
||||
|
||||
explicit Level(const std::filesystem::path& path)
|
||||
{
|
||||
auto [json, err] = json::parse(read_file_contents(path));
|
||||
Deserialize(json);
|
||||
}
|
||||
|
||||
explicit Level(const json::value& json) {
|
||||
Deserialize(json);
|
||||
}
|
||||
|
||||
void Deserialize(const json::value& json)
|
||||
{
|
||||
name = json["name"].as_string();
|
||||
description = json["description"].as_string();
|
||||
author = json["author"].as_string();
|
||||
tileset_path = json["tileset-path"].as_string();
|
||||
|
||||
auto layer_json_array = json["layers"].as_array();
|
||||
|
||||
for (auto& layer_json : layer_json_array)
|
||||
{
|
||||
layers.push_back(Layer(layer_json));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
json::value Serialize() const
|
||||
{
|
||||
json::object data;
|
||||
data["name"] = name;
|
||||
data["description"] = description;
|
||||
data["author"] = author;
|
||||
data["tileset-path"] = tileset_path;
|
||||
data["editor-version"] = REDACTED_EDITOR_LIB_VERSION;
|
||||
data["map-type"] = "OrthogonalTileMap";
|
||||
data["map-rows"] = (double)rows;
|
||||
data["map-cols"] = (double)cols;
|
||||
data["tile-width"] = 16.f;
|
||||
data["tile-height"] = 16.f;
|
||||
data["bgcolor"] = JsonConversions::deparse_color_to_hex(Colors::Black);
|
||||
data["tags"] = JsonConversions::deparse_string_list(tags);
|
||||
|
||||
json::array layers;
|
||||
for (auto& layer : this->layers)
|
||||
{
|
||||
layers.push_back(layer.Serialize());
|
||||
}
|
||||
data["layers"] = layers;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void Deserialize(const json::value& json) const {
|
||||
|
||||
}
|
||||
|
||||
protected:
|
||||
private:
|
||||
};
|
4
main.cpp
4
main.cpp
@@ -2,9 +2,9 @@
|
||||
// Created by dawsh on 5/12/25.
|
||||
//
|
||||
|
||||
#include <EditorApp.hpp>
|
||||
#include <App/EditorApp.hpp>
|
||||
|
||||
#include "ReWindow/Logger.h"
|
||||
#include "cmake-build-debug/_deps/rewindow-src/include/ReWindow/Logger.h"
|
||||
|
||||
int main() {
|
||||
|
||||
|
3
src/App/Editor.cpp
Normal file
3
src/App/Editor.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
//
|
||||
// Created by josh on 5/29/2025.
|
||||
//
|
@@ -1,16 +1,15 @@
|
||||
#include <EditorApp.hpp>
|
||||
#include <App/EditorApp.hpp>
|
||||
#include <App/NewMapDialog.hpp>
|
||||
|
||||
#include "NewMapDialog.hpp"
|
||||
|
||||
#include <Tileset.hpp>
|
||||
#include <Data/Format.hpp>
|
||||
|
||||
void EditorApp::PopulateQuads()
|
||||
{
|
||||
quads.reserve(tileset_width*tileset_height);
|
||||
for (int i = 0; i < tileset_width*tileset_height; i++) {
|
||||
Vector2i cell = IndexToCell(i, tileset_width);
|
||||
quads.push_back(Quad{cell.x*grid_pixel_width, cell.y*grid_pixel_height, grid_pixel_width, grid_pixel_height});
|
||||
}
|
||||
//quads.reserve(tileset_width*tileset_height);
|
||||
//for (int i = 0; i < tileset_width*tileset_height; i++) {
|
||||
// Vector2i cell = IndexToCell(i, tileset_width);
|
||||
// quads.push_back(Quad{cell.x*grid_pixel_width, cell.y*grid_pixel_height, grid_pixel_width, grid_pixel_height});
|
||||
//}
|
||||
}
|
||||
|
||||
Vector2i EditorApp::IndexToCell(int index, int width)
|
||||
@@ -125,23 +124,18 @@ void EditorApp::SavePreferences()
|
||||
void EditorApp::LoadTestFile()
|
||||
{
|
||||
|
||||
|
||||
if (!std::filesystem::exists("test_level"))
|
||||
std::filesystem::create_directory("test_level");
|
||||
|
||||
|
||||
LoadLevel("level.json");
|
||||
//if (!std::filesystem::exists("test_level/metadata.json"))
|
||||
|
||||
Level test_level = Level(std::filesystem::path("level.json"));
|
||||
grid_rows = test_level.layers[0].rows;
|
||||
grid_cols = test_level.layers[0].cols;
|
||||
//loaded_level = Level(std::filesystem::path("level.json"));
|
||||
//grid_rows = loaded_level->layers[0].rows;
|
||||
//grid_cols = loaded_level->layers[0].cols;
|
||||
|
||||
PopulateQuads();
|
||||
|
||||
InitGrid();
|
||||
//InitGrid();
|
||||
//std::string level_metadata_text = read_file_contents("test_level/metadata.json");
|
||||
|
||||
std::ifstream input;
|
||||
/*std::ifstream input;
|
||||
input.open("test.lvl", std::ios::binary | std::ios::in);
|
||||
input.seekg(0, std::ios::end);
|
||||
int data_length = input.tellg();
|
||||
@@ -164,13 +158,13 @@ void EditorApp::LoadTestFile()
|
||||
|
||||
//memcpy(grid, data, grid_rows * grid_cols * sizeof(int));
|
||||
|
||||
delete[] buffer;
|
||||
delete[] buffer;*/
|
||||
}
|
||||
|
||||
void EditorApp::SaveTestFile()
|
||||
{
|
||||
|
||||
int* buffer = new int[grid_rows*grid_cols];
|
||||
/*int* buffer = new int[grid_rows*grid_cols];
|
||||
for (int x = 0; x < grid_rows; x++)
|
||||
{
|
||||
for (int y = 0; y < grid_cols; y++)
|
||||
@@ -185,36 +179,36 @@ void EditorApp::SaveTestFile()
|
||||
output.write(reinterpret_cast<const char*>(buffer), grid_rows * grid_cols * sizeof(int));
|
||||
output.close();
|
||||
|
||||
delete[] buffer;
|
||||
delete[] buffer;*/
|
||||
}
|
||||
|
||||
|
||||
void EditorApp::LoadLevel(const std::filesystem::path& level_meta_path)
|
||||
{
|
||||
loaded_level = new Level(level_meta_path);
|
||||
|
||||
loaded_tileset = new Tileset(std::filesystem::path(loaded_level->tileset_path));
|
||||
|
||||
loaded_tilesheet = new JGL::Texture(loaded_tileset->texture_path);
|
||||
|
||||
for (auto layer : loaded_level->layers)
|
||||
layer->Load();
|
||||
|
||||
PopulateQuads();
|
||||
|
||||
layer_view->UpdateComponents(loaded_level);
|
||||
|
||||
data_ready = true;
|
||||
}
|
||||
void EditorApp::LoadMisc()
|
||||
{
|
||||
|
||||
|
||||
|
||||
Tileset tileset(std::filesystem::path("tileset.json"));
|
||||
|
||||
test_tilesheet = new JGL::Texture(tileset.texture_path);
|
||||
|
||||
|
||||
//auto texture_size = test_tilesheet->GetDimensions();
|
||||
tileset_width = tileset.rows;//texture_size.x / grid_pixel_width;
|
||||
tileset_height = tileset.cols;//texture_size.y / grid_pixel_height;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (std::filesystem::exists("test.lvl"))
|
||||
LoadTestFile();
|
||||
}
|
||||
|
||||
|
||||
void EditorApp::SizeCellIndicatorToTilesetSize()
|
||||
{
|
||||
cell_indicator->Size(JUI::UDim2::FromPixels(loaded_tileset->tile_width, loaded_tileset->tile_height));
|
||||
}
|
||||
|
||||
JUI::Window* EditorApp::CreateTilesetViewerWindow(JUI::Widget* parent)
|
||||
@@ -223,17 +217,17 @@ JUI::Window* EditorApp::CreateTilesetViewerWindow(JUI::Widget* parent)
|
||||
|
||||
auto* wind = new Window(parent);
|
||||
wind->SetTitle("Tileset Viewer");
|
||||
wind->Size(JUI::UDim2::FromPixels(test_tilesheet->GetDimensions().x, test_tilesheet->GetDimensions().y+20));
|
||||
wind->Size(JUI::UDim2::FromPixels(loaded_tilesheet->GetDimensions().x, loaded_tilesheet->GetDimensions().y+20));
|
||||
wind->SetResizable(false);
|
||||
|
||||
auto* img = new Image(wind->Content());
|
||||
img->Content(test_tilesheet);
|
||||
img->Content(loaded_tilesheet);
|
||||
img->FitImageToParent(false);
|
||||
|
||||
auto* grid_overlay = new JGL::RenderTarget(test_tilesheet->GetDimensions());
|
||||
auto* grid_overlay = new JGL::RenderTarget(loaded_tilesheet->GetDimensions());
|
||||
// TODO: supplying a translucent grid_overlay_color seems to not work?
|
||||
J2D::Begin(grid_overlay);
|
||||
DrawGrid(AABB2D({0,0}, Vector2(test_tilesheet->GetDimensions())), grid_overlay_color);
|
||||
DrawGrid(AABB2D({0,0}, Vector2(loaded_tilesheet->GetDimensions())), grid_overlay_color);
|
||||
J2D::End();
|
||||
|
||||
auto* grid_overlay_tex = new JGL::Texture(*grid_overlay->GetTexture());
|
||||
@@ -244,12 +238,13 @@ JUI::Window* EditorApp::CreateTilesetViewerWindow(JUI::Widget* parent)
|
||||
overlay->ZIndex(2);
|
||||
|
||||
cell_indicator = new JUI::Rect(wind->Content());
|
||||
cell_indicator->Size(UDim2::FromPixels(grid_pixel_width, grid_pixel_height));
|
||||
cell_indicator->BorderMode(JUI::BorderMode::Outline);
|
||||
cell_indicator->BGColor(Colors::Transparent);
|
||||
cell_indicator->BorderColor(Colors::Blues::CornflowerBlue);
|
||||
cell_indicator->BorderWidth(2);
|
||||
|
||||
SizeCellIndicatorToTilesetSize();
|
||||
|
||||
|
||||
return wind;
|
||||
}
|
||||
@@ -406,9 +401,15 @@ bool EditorApp::Open()
|
||||
// TODO: Delete when we update to the next release of JGL
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // NOTE: This MUST be called for text rendering to work properly!!!
|
||||
|
||||
// TODO: More sophisticated order-of-operations is required.
|
||||
// 1. Initialize elements of widgets that are independent of the level itself / the level depends on.
|
||||
// 2. Initialize the level.
|
||||
// 3. Initialize widgets that are dependent on the level itself.
|
||||
LoadMisc();
|
||||
CreateWidgets();
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -424,8 +425,8 @@ Vector2i EditorApp::GetTilesetCellFromMouse()
|
||||
Vector2 rel_mouse = mouse_v2 - rel;
|
||||
|
||||
return Vector2i(
|
||||
Math::Floor(rel_mouse.x/grid_pixel_width),
|
||||
Math::Floor(rel_mouse.y/grid_pixel_height));
|
||||
Math::Floor(rel_mouse.x/GetFocusLayer()->cell_width),
|
||||
Math::Floor(rel_mouse.y/GetFocusLayer()->cell_height));
|
||||
}
|
||||
|
||||
Vector2i EditorApp::GetGridCellFromMouse()
|
||||
@@ -438,8 +439,8 @@ Vector2i EditorApp::GetGridCellFromMouse()
|
||||
mouse_v2 = camera.ScreenToCameraSpace(mouse_v2);
|
||||
|
||||
return Vector2i(
|
||||
Math::Floor(mouse_v2.x/grid_pixel_width),
|
||||
Math::Floor(mouse_v2.y/grid_pixel_height));
|
||||
Math::Floor(mouse_v2.x/GetFocusLayer()->cell_width),
|
||||
Math::Floor(mouse_v2.y/GetFocusLayer()->cell_height));
|
||||
}
|
||||
|
||||
void EditorApp::CameraUpdate(float elapsed)
|
||||
@@ -471,12 +472,12 @@ int EditorApp::GetTile(const Vector2i& cell)
|
||||
if (cell.y < 0) return -2;
|
||||
|
||||
// out of bounds horizontally
|
||||
if (cell.x >= grid_rows) return -2;
|
||||
if (cell.x >= GetFocusLayer()->rows) return -2;
|
||||
|
||||
// out of bounds vertically
|
||||
if (cell.y >= grid_cols) return -2;
|
||||
if (cell.y >= GetFocusLayer()->cols) return -2;
|
||||
|
||||
return grid[cell.x][cell.y];
|
||||
return GetFocusLayer()->cells[cell.x][cell.y];
|
||||
}
|
||||
|
||||
int EditorApp::GetTile(int x, int y) { return GetTile({x, y}); }
|
||||
@@ -490,12 +491,12 @@ void EditorApp::SetTile(const Vector2i& cell, int tileID)
|
||||
if (cell.y < 0) return;
|
||||
|
||||
// out of bounds horizontally
|
||||
if (cell.x >= grid_rows) return;
|
||||
if (cell.x >= GetFocusLayer()->rows) return;
|
||||
|
||||
// out of bounds vertically
|
||||
if (cell.y >= grid_cols) return;
|
||||
if (cell.y >= GetFocusLayer()->cols) return;
|
||||
|
||||
grid[cell.x][cell.y] = tileID;
|
||||
GetFocusLayer()->cells[cell.x][cell.y] = tileID;
|
||||
}
|
||||
|
||||
void EditorApp::PlaceTileBrush(const Vector2i& cell)
|
||||
@@ -506,10 +507,10 @@ void EditorApp::PlaceTileBrush(const Vector2i& cell)
|
||||
if (cell.y < 0) return;
|
||||
|
||||
// out of bounds horizontally
|
||||
if (cell.x > grid_rows) return;
|
||||
if (cell.x > GetFocusLayer()->rows) return;
|
||||
|
||||
// out of bounds vertically
|
||||
if (cell.y > grid_cols) return;
|
||||
if (cell.y > GetFocusLayer()->cols) return;
|
||||
|
||||
|
||||
if (IsKeyDown(Keys::B))
|
||||
@@ -527,10 +528,10 @@ void EditorApp::EraseTile(const Vector2i& cell)
|
||||
if (cell.y < 0) return;
|
||||
|
||||
// out of bounds horizontally
|
||||
if (cell.x > grid_rows) return;
|
||||
if (cell.x > GetFocusLayer()->rows) return;
|
||||
|
||||
// out of bounds vertically
|
||||
if (cell.y > grid_cols) return;
|
||||
if (cell.y > GetFocusLayer()->cols) return;
|
||||
|
||||
SetTile(cell, -1);
|
||||
}
|
||||
@@ -548,25 +549,31 @@ void EditorApp::Update(float elapsed)
|
||||
scene->SetViewportSize(Vector2(vSize));
|
||||
scene->Update(elapsed);
|
||||
|
||||
bool mouse_hovering_ui_element = scene->IsMouseInsideDescendants();
|
||||
|
||||
if (tileset_viewer->IsMouseInside()) {
|
||||
if (tileset_viewer->Content()->IsMouseInside()) {
|
||||
cell_indicator->Visible(true);
|
||||
|
||||
Vector2 rel_mouse = Vector2(GetTilesetCellFromMouse());
|
||||
|
||||
rel_mouse.x *= grid_pixel_width;
|
||||
rel_mouse.y *= grid_pixel_height;
|
||||
rel_mouse.x *= loaded_tileset->tile_width;
|
||||
rel_mouse.y *= loaded_tileset->tile_height;
|
||||
|
||||
cell_indicator->Position(JUI::UDim2::FromPixels(rel_mouse.x, rel_mouse.y));
|
||||
}
|
||||
} else {
|
||||
|
||||
cell_indicator->Visible(false);
|
||||
|
||||
if (IsMouseButtonDown(MouseButtons::Left))
|
||||
PlaceTileBrush(GetGridCellFromMouse());
|
||||
if (!mouse_hovering_ui_element)
|
||||
{
|
||||
if (IsMouseButtonDown(MouseButtons::Left))
|
||||
PlaceTileBrush(GetGridCellFromMouse());
|
||||
|
||||
if (IsMouseButtonDown(MouseButtons::Right))
|
||||
EraseTile(GetGridCellFromMouse());
|
||||
if (IsMouseButtonDown(MouseButtons::Right))
|
||||
EraseTile(GetGridCellFromMouse());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,18 +582,20 @@ void EditorApp::DrawGrid(const AABB2D& bounds, const Color4& color)
|
||||
Vector2 viewport_topleft = bounds.minPoint;
|
||||
Vector2 viewport_bottomright = bounds.maxPoint;
|
||||
|
||||
int nearest_grid_left = Math::Floor(viewport_topleft.x / grid_pixel_width);
|
||||
int nearest_grid_right = Math::Floor(viewport_bottomright.x / grid_pixel_width);
|
||||
|
||||
|
||||
int nearest_grid_left = Math::Floor(viewport_topleft.x / loaded_tileset->tile_width);
|
||||
int nearest_grid_right = Math::Floor(viewport_bottomright.x / loaded_tileset->tile_width);
|
||||
for (int x = nearest_grid_left; x <= nearest_grid_right; x++) {
|
||||
auto top = Vector2(x * grid_pixel_width, viewport_topleft.y);
|
||||
auto bottom = Vector2(x * grid_pixel_width, viewport_bottomright.y);
|
||||
auto top = Vector2(x * loaded_tileset->tile_width, viewport_topleft.y);
|
||||
auto bottom = Vector2(x * loaded_tileset->tile_width, viewport_bottomright.y);
|
||||
JGL::J2D::DrawDashedLine(color, top, bottom, 4, 4, 1 / bounds.Width());
|
||||
}
|
||||
int nearest_grid_top = Math::Floor(viewport_topleft.y / grid_pixel_height);
|
||||
int nearest_grid_bottom = Math::Floor(viewport_bottomright.y / grid_pixel_height);
|
||||
int nearest_grid_top = Math::Floor(viewport_topleft.y / loaded_tileset->tile_height);
|
||||
int nearest_grid_bottom = Math::Floor(viewport_bottomright.y / loaded_tileset->tile_height);
|
||||
for (int y = nearest_grid_top; y <= nearest_grid_bottom; y++) {
|
||||
auto left = Vector2(viewport_topleft.x, y * grid_pixel_height);
|
||||
auto right = Vector2(viewport_bottomright.x, y * grid_pixel_height);
|
||||
auto left = Vector2(viewport_topleft.x, y * loaded_tileset->tile_height);
|
||||
auto right = Vector2(viewport_bottomright.x, y * loaded_tileset->tile_height);
|
||||
JGL::J2D::DrawDashedLine(color, left, right, 4, 4, 1 / bounds.Height());
|
||||
}
|
||||
}
|
||||
@@ -594,6 +603,11 @@ void EditorApp::DrawGrid(const AABB2D& bounds, const Color4& color)
|
||||
void EditorApp::DrawGrid(const Color4& color)
|
||||
{
|
||||
|
||||
auto grid_rows = GetFocusLayer()->rows;
|
||||
auto grid_cols = GetFocusLayer()->cols;
|
||||
auto grid_pixel_width = GetFocusLayer()->cell_width;
|
||||
auto grid_pixel_height = GetFocusLayer()->cell_height;
|
||||
|
||||
float sw = GetWidth();
|
||||
float sh = GetHeight();
|
||||
|
||||
@@ -604,15 +618,15 @@ void EditorApp::DrawCellPointerOutline()
|
||||
{
|
||||
Vector2 rel_mouse = Vector2(GetGridCellFromMouse());
|
||||
|
||||
rel_mouse.x *= grid_pixel_width;
|
||||
rel_mouse.y *= grid_pixel_height;
|
||||
rel_mouse.x *= GetFocusLayer()->cell_width;
|
||||
rel_mouse.y *= GetFocusLayer()->cell_height;
|
||||
|
||||
J2D::OutlineRect(cell_pointer_outline_color, rel_mouse, Vector2(grid_pixel_width, grid_pixel_height), cell_pointer_outline_width);
|
||||
J2D::OutlineRect(cell_pointer_outline_color, rel_mouse, Vector2( GetFocusLayer()->cell_width, GetFocusLayer()->cell_height), cell_pointer_outline_width);
|
||||
}
|
||||
|
||||
void EditorApp::DrawTiles()
|
||||
{
|
||||
for (int gx = 0; gx < grid_rows; gx++) {
|
||||
/*for (int gx = 0; gx < grid_rows; gx++) {
|
||||
for (int gy = 0; gy < grid_cols; gy++) {
|
||||
|
||||
auto quad_idx = grid[gx][gy];
|
||||
@@ -625,12 +639,12 @@ void EditorApp::DrawTiles()
|
||||
if (quad_idx < 0)
|
||||
continue;
|
||||
|
||||
auto quad = quads[quad_idx];
|
||||
//auto quad = quads[quad_idx];
|
||||
|
||||
J2D::DrawPartialSprite(test_tilesheet, pos,
|
||||
Vector2(quad.x, quad.y), Vector2(quad.w, quad.h));
|
||||
//J2D::DrawPartialSprite(test_tilesheet, pos,
|
||||
// Vector2(quad.x, quad.y), Vector2(quad.w, quad.h));
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void EditorApp::Draw()
|
||||
@@ -644,10 +658,14 @@ void EditorApp::Draw()
|
||||
glRotatef(camera.rotation.current, 0, 0, 1);
|
||||
glScalef(camera.scale.current, camera.scale.current, 1);
|
||||
|
||||
|
||||
if (grid_overlay_enabled)
|
||||
DrawGrid(grid_overlay_color);
|
||||
|
||||
DrawTiles();
|
||||
if (data_ready)
|
||||
DrawLevel(loaded_level);
|
||||
else
|
||||
J2D::DrawString(Colors::White, "Loading Level", 0, 0, 1, 24);
|
||||
|
||||
if (grid_overlay_enabled)
|
||||
DrawGrid(grid_overlay_color);
|
@@ -1,4 +1,4 @@
|
||||
#include <EditorCamera.hpp>
|
||||
#include <App/EditorCamera.hpp>
|
||||
|
||||
Matrix4x4 EditorCamera::CurrentTranslationMatrix() const
|
||||
{
|
1
src/Data/Entity.cpp
Normal file
1
src/Data/Entity.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include <Data/Entity.hpp>
|
@@ -1,4 +1,4 @@
|
||||
#include <Layer.hpp>
|
||||
#include <Data/Layer.hpp>
|
||||
|
||||
Layer::Layer(): rows(0), cols(0), cell_width(0), cell_height(0), parallax_x(0), parallax_y(0), cells(nullptr)
|
||||
{ }
|
||||
@@ -17,6 +17,11 @@ Layer::Layer(const json::value& json)
|
||||
Deserialize(json);
|
||||
}
|
||||
|
||||
Layer::~Layer()
|
||||
{
|
||||
DeleteGrid();
|
||||
}
|
||||
|
||||
Layer::Layer(const std::filesystem::path& path)
|
||||
{
|
||||
auto [json, err] = json::parse(read_file_contents(path));
|
||||
@@ -50,6 +55,9 @@ void Layer::Deserialize(const json::value& json)
|
||||
|
||||
void Layer::InitGrid()
|
||||
{
|
||||
if (grid_allocated)
|
||||
return; // TODO: Throw double allocation.
|
||||
|
||||
cells = new int*[rows];
|
||||
|
||||
for (int i = 0; i < rows; i++)
|
||||
@@ -60,14 +68,22 @@ void Layer::InitGrid()
|
||||
cells[x][y] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
grid_allocated = true;
|
||||
}
|
||||
|
||||
void Layer::DeleteGrid()
|
||||
{
|
||||
|
||||
if (!grid_allocated)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < rows; i++)
|
||||
delete[] cells[i];
|
||||
|
||||
delete[] cells;
|
||||
|
||||
grid_allocated = false;
|
||||
}
|
||||
|
||||
void Layer::Resize(int newWidth, int newHeight) {
|
||||
@@ -115,3 +131,56 @@ void Layer::LoadFromDataBuffer(const int* buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Layer::WriteToDataBuffer(int* buffer) const
|
||||
{
|
||||
//int* buffer = new int[rows*cols];
|
||||
for (int x = 0; x < rows; x++)
|
||||
{
|
||||
for (int y = 0; y < cols; y++)
|
||||
{
|
||||
int index = (x*cols + y);
|
||||
buffer[index] = cells[x][y];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Layer::WriteBinaryData(const std::filesystem::path& path) const
|
||||
{
|
||||
int* buffer = new int[rows*cols];
|
||||
WriteToDataBuffer(buffer);
|
||||
std::ofstream output;
|
||||
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;
|
||||
}
|
||||
|
||||
bool Layer::Save()
|
||||
{ return WriteBinaryData(binary_path); }
|
||||
|
||||
bool Layer::Load()
|
||||
{ return ReadBinaryData(binary_path); }
|
||||
|
||||
bool Layer::ReadBinaryData(const std::filesystem::path& path)
|
||||
{
|
||||
std::ifstream input;
|
||||
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);
|
||||
|
||||
char* buffer = new char[data_length];
|
||||
input.read(buffer, data_length);
|
||||
input.close();
|
||||
|
||||
InitGrid();
|
||||
LoadFromDataBuffer(reinterpret_cast<int*>(buffer));
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
return true;
|
||||
}
|
74
src/Data/Level.cpp
Normal file
74
src/Data/Level.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <Data/Level.hpp>
|
||||
|
||||
|
||||
Level::Level(): rows(0), cols(0)
|
||||
{
|
||||
}
|
||||
|
||||
Level::Level(const std::filesystem::path& path)
|
||||
{
|
||||
auto [json, err] = json::parse(read_file_contents(path));
|
||||
Deserialize(json);
|
||||
}
|
||||
|
||||
Level::Level(const json::value& json)
|
||||
{
|
||||
Deserialize(json);
|
||||
}
|
||||
|
||||
void Level::Deserialize(const json::value& json)
|
||||
{
|
||||
name = json["name"].as_string();
|
||||
description = json["description"].as_string();
|
||||
author = json["author"].as_string();
|
||||
tileset_path = json["tileset-path"].as_string();
|
||||
|
||||
auto layer_json_array = json["layers"].as_array();
|
||||
|
||||
for (auto layer_json : layer_json_array)
|
||||
{
|
||||
layers.emplace_back(new Layer(layer_json));
|
||||
}
|
||||
|
||||
auto entities_json_array = json["entities"].as_array();
|
||||
|
||||
for (auto& entity_json : entities_json_array)
|
||||
{
|
||||
entities.emplace_back(new Entity(entity_json));
|
||||
}
|
||||
}
|
||||
|
||||
json::value Level::Serialize() const
|
||||
{
|
||||
json::object data;
|
||||
data["name"] = name;
|
||||
data["description"] = description;
|
||||
data["author"] = author;
|
||||
data["tileset-path"] = tileset_path;
|
||||
data["editor-version"] = REDACTED_EDITOR_LIB_VERSION;
|
||||
data["map-type"] = "OrthogonalTileMap";
|
||||
data["map-rows"] = (double)rows;
|
||||
data["map-cols"] = (double)cols;
|
||||
data["tile-width"] = 16.f;
|
||||
data["tile-height"] = 16.f;
|
||||
data["bgcolor"] = JsonConversions::deparse_color_to_hex(Colors::Black);
|
||||
data["tags"] = JsonConversions::deparse_string_list(tags);
|
||||
|
||||
json::array layers_json;
|
||||
for (auto& layer : this->layers)
|
||||
{
|
||||
layers_json.push_back(layer->Serialize());
|
||||
}
|
||||
data["layers"] = layers_json;
|
||||
|
||||
|
||||
json::array entities_json;
|
||||
|
||||
for (auto& entity : this->entities)
|
||||
{
|
||||
entities_json.push_back(entity->Serialize());
|
||||
}
|
||||
data["entities"] = entities_json;
|
||||
|
||||
return data;
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#include <Tileset.hpp>
|
||||
#include <Data/Tileset.hpp>
|
||||
|
||||
Tileset::Tileset():
|
||||
tile_width(0),tile_height(0), tile_spacing(0),
|
Reference in New Issue
Block a user