Various Random Edits...
This commit is contained in:
@@ -52,7 +52,9 @@ endif()
|
||||
|
||||
if (WIN32)
|
||||
ADD_LIBRARY(Editor2D STATIC ${EDITOR_SRC}
|
||||
include/Preferences.hpp)
|
||||
include/Preferences.hpp
|
||||
src/Utils.cpp
|
||||
include/NewMapDialog.hpp)
|
||||
endif()
|
||||
|
||||
set_target_properties(Editor2D PROPERTIES LINKER_LANGUAGE CXX)
|
||||
|
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
class Editor {
|
||||
public:
|
||||
protected:
|
||||
|
@@ -1,14 +1,18 @@
|
||||
#pragma once
|
||||
#include <ReWindow/types/Window.h>
|
||||
|
||||
#include <JUI/Widgets/Slider.hpp>
|
||||
#include "JGL/JGL.h"
|
||||
#include "JUI/Widgets/Image.hpp"
|
||||
#include "JUI/Widgets/Scene.hpp"
|
||||
#include "JUI/Widgets/UtilityBar.hpp"
|
||||
#include "JUI/Widgets/Window.hpp"
|
||||
// TODO: ColorPicker.hpp needs to include Slider.hpp
|
||||
#include "JUI/Widgets/Slider.hpp"
|
||||
#include <JUI/Widgets/CommandLine.hpp>
|
||||
|
||||
#include "NewMapDialog.hpp"
|
||||
#include "JUI/Widgets/ColorPicker.hpp"
|
||||
#include "Preferences.hpp"
|
||||
|
||||
#define GL_VER_MAJOR 2
|
||||
#define GL_VER_MINOR 1
|
||||
@@ -61,10 +65,15 @@ public:
|
||||
EditorCamera camera;
|
||||
|
||||
JUI::Scene* scene;
|
||||
JUI::CommandLine* console;
|
||||
JUI::Window* tileset_viewer;
|
||||
JUI::Rect* cell_indicator;
|
||||
JGL::Texture* test_tilesheet;
|
||||
|
||||
NewMapDialog* nmd = nullptr;
|
||||
|
||||
Preferences preferences;
|
||||
|
||||
JUI::Window* bg_color_tool_window = nullptr;
|
||||
JUI::ColorPicker* bg_color_tool = nullptr;
|
||||
|
||||
@@ -139,6 +148,10 @@ public:
|
||||
DeleteGrid();
|
||||
}
|
||||
|
||||
void LoadPreferences();
|
||||
|
||||
void SavePreferences();
|
||||
|
||||
/// Loads the test.lvl file into the editor.
|
||||
/// @note Placeholder until FileDialogs are added.
|
||||
void LoadTestFile();
|
||||
@@ -152,9 +165,10 @@ public:
|
||||
/// @note Placeholder until Tileset json is added.
|
||||
void LoadMisc();
|
||||
|
||||
|
||||
/// Creates a JUI widget that displays the Tileset, and lets you select a tile from it.
|
||||
JUI::Window* CreateTilesetViewerWindow(JUI::Widget* parent);
|
||||
void ParseCmdLineMessage(const std::string& message);
|
||||
void BindConsoleCallbacks();
|
||||
|
||||
/// Create all JUI elements required for this program.
|
||||
void CreateWidgets();
|
||||
|
@@ -2,6 +2,14 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <TileLayer.hpp>
|
||||
#include <JJX/JSON.hpp>
|
||||
#include <Colors.hpp>
|
||||
#include <Utils.hpp>
|
||||
|
||||
using namespace JJX;
|
||||
|
||||
const float REDACTED_EDITOR_LIB_VERSION = 1;
|
||||
|
||||
|
||||
class Level {
|
||||
public:
|
||||
@@ -9,9 +17,39 @@ public:
|
||||
std::string description;
|
||||
std::string author;
|
||||
std::vector<std::string> tags;
|
||||
int rows;
|
||||
int cols;
|
||||
|
||||
|
||||
|
||||
std::vector<TileLayer> tile_layers;
|
||||
|
||||
explicit Level(const json::value& data) {
|
||||
|
||||
}
|
||||
|
||||
json::value Serialize() const
|
||||
{
|
||||
json::object data;
|
||||
data["name"] = name;
|
||||
data["description"] = description;
|
||||
data["author"] = author;
|
||||
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);
|
||||
data["layers"] = json::array();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void Deserialize() const {
|
||||
|
||||
}
|
||||
|
||||
protected:
|
||||
private:
|
||||
|
21
include/NewMapDialog.hpp
Normal file
21
include/NewMapDialog.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Widgets/Window.hpp>
|
||||
|
||||
class NewMapDialog : public JUI::Window
|
||||
{
|
||||
public:
|
||||
Event<> OnSubmit;
|
||||
|
||||
NewMapDialog() : JUI::Window() {
|
||||
Name("NewMapDialog");
|
||||
|
||||
}
|
||||
|
||||
explicit NewMapDialog(Widget* parent) : NewMapDialog()
|
||||
{
|
||||
this->Parent(parent);
|
||||
}
|
||||
protected:
|
||||
private:
|
||||
};
|
@@ -2,23 +2,64 @@
|
||||
|
||||
#include <JJX/JSON.hpp>
|
||||
#include <Color4.hpp>
|
||||
#include <J3ML/LinearAlgebra/Vector2.hpp>
|
||||
|
||||
using namespace JJX;
|
||||
|
||||
struct RecentCameraState
|
||||
{
|
||||
Vector2 translation;
|
||||
float rotation;
|
||||
float scale;
|
||||
|
||||
json::value Serialize() const
|
||||
{
|
||||
json::object json;
|
||||
json["tx"] = translation.x;
|
||||
json["ty"] = translation.y;
|
||||
json["rot"] = rotation;
|
||||
json["scale"] = scale;
|
||||
return json;
|
||||
}
|
||||
|
||||
void Deserialize(const json::value& v)
|
||||
{
|
||||
translation.x = v["tx"].number.value_or(0.f);
|
||||
translation.y = v["ty"].number.value_or(0.f);
|
||||
rotation = v["rot"].number.value_or(0.f);
|
||||
scale = v["scale"].number.value_or(1.f);
|
||||
}
|
||||
};
|
||||
|
||||
class Preferences {
|
||||
public:
|
||||
|
||||
RecentCameraState recent_camera_state;
|
||||
|
||||
explicit Preferences() : recent_camera_state()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
explicit Preferences(const json::value& json)
|
||||
{
|
||||
|
||||
Deserialize(json);
|
||||
}
|
||||
|
||||
json::value Serialize() const
|
||||
{
|
||||
json::value json;
|
||||
json::object json;
|
||||
|
||||
json["camerastate"] = recent_camera_state.Serialize();
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
void Deserialize(const json::value& json)
|
||||
{
|
||||
recent_camera_state.Deserialize(json["camerastate"]);
|
||||
}
|
||||
|
||||
public:
|
||||
Color4 GridColor() const;
|
||||
|
||||
|
@@ -1,6 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <Color4.hpp>
|
||||
#include <J3ML/LinearAlgebra.hpp>
|
||||
#include <JJX/JSON.hpp>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace JsonConversions
|
||||
{
|
||||
using namespace JJX;
|
||||
Color4 parse_color(const JJX::json::value& v);
|
||||
|
||||
JJX::json::value deparse_color_to_hex(const Color4& color);
|
||||
|
||||
std::vector<std::string> parse_string_list(const JJX::json::value& v);
|
||||
|
||||
JJX::json::value deparse_string_list(const std::vector<std::string>& list);
|
||||
} // namespace JsonConversions
|
||||
|
||||
template <typename T>
|
||||
struct Lerped
|
||||
@@ -14,4 +32,8 @@ struct Lerped
|
||||
};
|
||||
|
||||
template <> inline void Lerped<float>::Step(float elapsed) { current = Math::Lerp(current, goal, rate*elapsed);}
|
||||
template <> inline void Lerped<Vector2>::Step(float elapsed) { current = Vector2::Lerp(current, goal, rate*elapsed);}
|
||||
template <> inline void Lerped<Vector2>::Step(float elapsed) { current = Vector2::Lerp(current, goal, rate*elapsed);}
|
||||
|
||||
|
||||
std::string read_file_contents(const std::filesystem::path& file_path);
|
||||
bool write_file_contents(const std::filesystem::path& file_path, const std::string& contents);
|
||||
|
@@ -1,5 +1,7 @@
|
||||
#include <EditorApp.hpp>
|
||||
|
||||
#include "NewMapDialog.hpp"
|
||||
|
||||
void EditorApp::PopulateQuads()
|
||||
{
|
||||
quads.reserve(tileset_width*tileset_height);
|
||||
@@ -26,24 +28,42 @@ EditorApp::EditorApp(): OpenGLWindow("Editor App", 1776, 1000, GL_VER_MAJOR, GL_
|
||||
camera.DefaultState();
|
||||
}
|
||||
|
||||
|
||||
std::string read_file_contents(const std::filesystem::path& file_path)
|
||||
void EditorApp::LoadPreferences()
|
||||
{
|
||||
std::ifstream input;
|
||||
input.open("test.lvl", std::ios::binary | std::ios::in);
|
||||
input.seekg(0, std::ios::end);
|
||||
int data_length = input.tellg();
|
||||
input.seekg(0, std::ios::beg);
|
||||
if (std::filesystem::exists("preferences.json"))
|
||||
{
|
||||
|
||||
char* buffer = new char[data_length];
|
||||
input.read(buffer, data_length);
|
||||
input.close();
|
||||
auto text = read_file_contents("preferences.json");
|
||||
|
||||
std::string result = std::string(buffer);
|
||||
delete[] buffer;
|
||||
return result;
|
||||
if (text.empty())
|
||||
return;
|
||||
|
||||
auto [data , errcode] = json::parse(text);
|
||||
|
||||
if (!errcode.empty())
|
||||
{
|
||||
std::cerr << "Problem!1!" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
preferences = Preferences(data);
|
||||
camera.translation.goal = preferences.recent_camera_state.translation;
|
||||
camera.rotation.goal = preferences.recent_camera_state.rotation;
|
||||
camera.scale.goal = preferences.recent_camera_state.scale;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorApp::SavePreferences()
|
||||
{
|
||||
|
||||
preferences.recent_camera_state.translation = camera.translation.goal;
|
||||
preferences.recent_camera_state.rotation = camera.rotation.goal;
|
||||
preferences.recent_camera_state.scale = camera.scale.goal;
|
||||
|
||||
write_file_contents("preferences.json", json::deparse(preferences.Serialize()));
|
||||
}
|
||||
|
||||
|
||||
void EditorApp::LoadTestFile()
|
||||
{
|
||||
if (!std::filesystem::exists("test_level"))
|
||||
@@ -157,15 +177,85 @@ JUI::Window* EditorApp::CreateTilesetViewerWindow(JUI::Widget* parent)
|
||||
return wind;
|
||||
}
|
||||
|
||||
namespace misc {
|
||||
std::vector<std::string> string_expand(const std::string& input, char delimiter = ' ');
|
||||
bool string_matches(const std::string& x, const std::vector<std::string>& v);
|
||||
|
||||
std::vector<std::string> misc::string_expand(const std::string& input, char delimiter)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
std::stringstream ss (input);
|
||||
std::string item;
|
||||
|
||||
while (getline(ss, item, delimiter)) {
|
||||
result.push_back(item);
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
auto misc::string_matches(const std::string &x, const std::vector<std::string> &v) -> bool {
|
||||
|
||||
return std::find(v.begin(), v.end(), x) != v.end();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void EditorApp::ParseCmdLineMessage(const std::string &message) {
|
||||
auto tokens = misc::string_expand(message);
|
||||
|
||||
if (tokens.size() == 0) {
|
||||
console->Log("No command input!", Colors::Red);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string cmd = tokens[0];
|
||||
|
||||
// Remove 0th element from tokens before passing to command delegates.
|
||||
tokens.erase(tokens.begin());
|
||||
|
||||
|
||||
|
||||
if (misc::string_matches(cmd, {"reset"})) {
|
||||
|
||||
}
|
||||
|
||||
console->Log(std::format("No such command: {}", cmd), Colors::Red);
|
||||
}
|
||||
|
||||
void EditorApp::BindConsoleCallbacks()
|
||||
{
|
||||
|
||||
// TODO: This parsing pattern is duplicated between at least 2 other projects at this point.
|
||||
// TODO: Move up into JUI or a separate package.
|
||||
console->OnInput += [this] (const std::string& cmd)
|
||||
{
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
void EditorApp::CreateWidgets()
|
||||
{
|
||||
scene = new JUI::Scene();
|
||||
|
||||
console = new JUI::CommandLine(scene);
|
||||
console->Close();
|
||||
BindConsoleCallbacks();
|
||||
|
||||
nmd = new NewMapDialog(scene);
|
||||
nmd->Close();
|
||||
|
||||
auto* topbar = new JUI::UtilityBar(scene);
|
||||
|
||||
auto* file = topbar->AddSubmenu("File");
|
||||
file->SetFont(JGL::Fonts::Jupiteroid);
|
||||
file->AddButton("New");
|
||||
|
||||
|
||||
auto* new_map = file->AddButton("New Map", [this] () mutable {
|
||||
nmd->Open();
|
||||
});
|
||||
|
||||
file->AddButton("Open");
|
||||
file->AddButton("Save", [this]{SaveTestFile();});
|
||||
file->AddButton("Save As");
|
||||
@@ -187,6 +277,9 @@ void EditorApp::CreateWidgets()
|
||||
view->AddSeparator(2_px);
|
||||
view->AddButton("Toggle Grid", [this]{ToggleGrid();});
|
||||
view->AddButton("Set Background Color", [this]{bg_color_tool_window->Toggle();});
|
||||
view->AddSeparator(2_px);
|
||||
view->AddButton("Console", [this]()mutable { console->Toggle();});
|
||||
view->AddButton("Tileset Viewer", [this]()mutable { tileset_viewer->Toggle();});
|
||||
|
||||
auto* level = topbar->AddSubmenu("Level");
|
||||
auto* layer = topbar->AddSubmenu("Layer");
|
||||
@@ -215,6 +308,8 @@ void EditorApp::ToggleGrid()
|
||||
|
||||
bool EditorApp::Open()
|
||||
{
|
||||
LoadPreferences();
|
||||
|
||||
if (!OpenGLWindow::Open()) return false;
|
||||
|
||||
auto size = GetSize();
|
||||
@@ -269,8 +364,8 @@ Vector2i EditorApp::GetGridCellFromMouse()
|
||||
|
||||
void EditorApp::CameraUpdate(float elapsed)
|
||||
{
|
||||
float move_rate = 40;
|
||||
float zoom_rate = 0.1f;
|
||||
float move_rate = 120;
|
||||
float zoom_rate = 0.2f;
|
||||
|
||||
if (IsKeyDown(Keys::LeftArrow))
|
||||
camera.translation.goal.x -= move_rate*elapsed;
|
||||
@@ -292,6 +387,11 @@ void EditorApp::CameraUpdate(float elapsed)
|
||||
|
||||
void EditorApp::SetTile(const Vector2i& cell, int tileID)
|
||||
{
|
||||
|
||||
if (cell.x < 0) return;
|
||||
|
||||
if (cell.y < 0) return;
|
||||
|
||||
// out of bounds horizontally
|
||||
if (cell.x > grid_rows) return;
|
||||
|
||||
@@ -303,6 +403,9 @@ void EditorApp::SetTile(const Vector2i& cell, int tileID)
|
||||
|
||||
void EditorApp::PlaceTileBrush(const Vector2i& cell)
|
||||
{
|
||||
if (cell.x < 0) return;
|
||||
|
||||
if (cell.y < 0) return;
|
||||
|
||||
// out of bounds horizontally
|
||||
if (cell.x > grid_rows) return;
|
||||
@@ -315,6 +418,10 @@ void EditorApp::PlaceTileBrush(const Vector2i& cell)
|
||||
|
||||
void EditorApp::EraseTile(const Vector2i& cell)
|
||||
{
|
||||
if (cell.x < 0) return;
|
||||
|
||||
if (cell.y < 0) return;
|
||||
|
||||
// out of bounds horizontally
|
||||
if (cell.x > grid_rows) return;
|
||||
|
||||
@@ -456,5 +563,6 @@ void EditorApp::Draw()
|
||||
|
||||
void EditorApp::OnClosing()
|
||||
{
|
||||
SavePreferences();
|
||||
SaveTestFile();
|
||||
}
|
||||
|
90
src/Utils.cpp
Normal file
90
src/Utils.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include <Colors.hpp>
|
||||
#include <Utils.hpp>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
|
||||
|
||||
Color4 JsonConversions::parse_color(const JJX::json::value& v)
|
||||
{
|
||||
if (v.type == json::value_type::string)
|
||||
return Color4::FromHex(v.string.value());
|
||||
else if (v.type == json::value_type::array)
|
||||
{
|
||||
auto color_array = v.as_array();
|
||||
int r = color_array[0].number.value();
|
||||
int g = color_array[1].number.value();
|
||||
int b = color_array[2].number.value();
|
||||
int a = 255;
|
||||
if (color_array.value::array.value().size() == 4)
|
||||
a = color_array[3].number.value();
|
||||
return Color4(r, g, b, a);
|
||||
}
|
||||
else if (v.type == json::value_type::object)
|
||||
{
|
||||
auto color_obj = v.operator std::map<std::string, json::value>();
|
||||
int r = color_obj["r"].number.value();
|
||||
int g = color_obj["g"].number.value();
|
||||
int b = color_obj["b"].number.value();
|
||||
|
||||
int a = 255;
|
||||
|
||||
if (color_obj.contains("a"))
|
||||
a = color_obj["a"].number.value();
|
||||
|
||||
return Color4(r, g, b, a);
|
||||
}
|
||||
return Colors::Transparent;
|
||||
}
|
||||
|
||||
JJX::json::value JsonConversions::deparse_color_to_hex(const Color4& color)
|
||||
{
|
||||
return JJX::json::string(color.ToHexAlpha());
|
||||
}
|
||||
|
||||
std::vector<std::string> JsonConversions::parse_string_list(const JJX::json::value& v)
|
||||
{
|
||||
// TODO: Log an error if the json value is an invalid type.
|
||||
if (v.type == json::value_type::string)
|
||||
{
|
||||
return {v.string.value()};
|
||||
}
|
||||
else if (v.type == json::value_type::array)
|
||||
{
|
||||
std::vector<std::string> retval;
|
||||
|
||||
for (auto& token : v.array.value())
|
||||
retval.push_back(token.string.value());
|
||||
return retval;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
JJX::json::value JsonConversions::deparse_string_list(const std::vector<std::string>& list)
|
||||
{
|
||||
json::array strlist;
|
||||
for (auto& str : list)
|
||||
strlist.push_back(str);
|
||||
|
||||
return strlist;
|
||||
}
|
||||
|
||||
std::string read_file_contents(const std::filesystem::path& file_path)
|
||||
{
|
||||
std::ifstream file (file_path);
|
||||
std::ostringstream sstr;
|
||||
sstr << file.rdbuf();
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
bool write_file_contents(const std::filesystem::path& file_path, const std::string& contents)
|
||||
{
|
||||
std::ofstream output(file_path);
|
||||
|
||||
if (output.is_open()) {
|
||||
output << contents;
|
||||
output.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
Reference in New Issue
Block a user