Fleshing out code generally.

This commit is contained in:
2025-05-20 03:26:29 -05:00
parent 5c18aa9a05
commit 8daabe4259
10 changed files with 657 additions and 316 deletions

View File

@@ -1 +1,410 @@
#include <EditorApp.hpp>
#include <EditorApp.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});
}
}
Vector2i EditorApp::IndexToCell(int index, int width)
{
int x = index % width;
int y = index / width;
return {x, y};
}
int EditorApp::CellToIndex(Vector2i cell, int width)
{
return cell.y*width + cell.x;
}
EditorApp::EditorApp(): OpenGLWindow("Editor App", 1776, 1000, GL_VER_MAJOR, GL_VER_MINOR)
{
camera.DefaultState();
}
void EditorApp::LoadTestFile()
{
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);
char* buffer = new char[data_length];
input.read(buffer, data_length);
input.close();
auto* data = reinterpret_cast<int*>(buffer);
memcpy(grid, data, grid_rows * grid_cols * sizeof(int));
}
void EditorApp::SaveTestFile()
{
std::ofstream output;
output.open("test.lvl", std::ios::out | std::ios::binary);
output.write(reinterpret_cast<const char*>(&grid[0][0]), grid_rows * grid_cols * sizeof(int));
output.close();
}
void EditorApp::LoadMisc()
{
test_tilesheet = new JGL::Texture("../megacommando.png");
auto texture_size = test_tilesheet->GetDimensions();
tileset_width = texture_size.x / grid_pixel_width;
tileset_height = texture_size.y / grid_pixel_height;
PopulateQuads();
for (int x = 0; x < grid_rows; x++) {
for (int y = 0; y < grid_cols; y++) {
grid[x][y] = -1;
}
}
if (std::filesystem::exists("test.lvl"))
LoadTestFile();
}
JUI::Window* EditorApp::CreateTilesetViewerWindow(JUI::Widget* parent)
{
using namespace JUI;
auto* wind = new Window(parent);
wind->SetTitle("Tileset Viewer");
wind->Size(JUI::UDim2::FromPixels(test_tilesheet->GetDimensions().x, test_tilesheet->GetDimensions().y+20));
wind->SetResizable(false);
auto* img = new Image(wind->Content());
img->Content(test_tilesheet);
img->FitImageToParent(false);
auto* grid_overlay = new JGL::RenderTarget(test_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);
J2D::End();
auto* grid_overlay_tex = new JGL::Texture(*grid_overlay->GetTexture());
auto* overlay = new JUI::Image(wind->Content());
overlay->Content(grid_overlay_tex);
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);
return wind;
}
void EditorApp::CreateWidgets()
{
scene = new JUI::Scene();
auto* topbar = new JUI::UtilityBar(scene);
auto* file = topbar->AddSubmenu("File");
file->SetFont(JGL::Fonts::Jupiteroid);
file->AddButton("New");
file->AddButton("Open");
file->AddButton("Save", [this]{SaveTestFile();});
file->AddButton("Save As");
file->AddSeparator(2_px);
file->AddButton("About");
file->AddButton("Preferences");
auto* edit = topbar->AddSubmenu("Edit");
edit->AddButton("Undo");
edit->AddButton("Redo");
edit->AddButton("Copy");
edit->AddSeparator(2_px);
edit->AddButton("Paste");
edit->AddButton("Cut Selection");
auto* view = topbar->AddSubmenu("View");
view->AddButton("Zoom In");
view->AddButton("Zoom Out");
view->AddSeparator(2_px);
view->AddButton("Toggle Grid", [this]{ToggleGrid();});
view->AddButton("Set Background Color", [this]{bg_color_tool_window->Toggle();});
auto* level = topbar->AddSubmenu("Level");
auto* layer = topbar->AddSubmenu("Layer");
layer->AddButton("New");
layer->AddButton("Open from File");
layer->AddButton("Duplicate Selected");
layer->AddButton("Delete Selected");
layer->AddButton("Edit Selected");
layer->AddButton("Export Layer");
tileset_viewer = CreateTilesetViewerWindow(scene);
bg_color_tool_window = new JUI::Window(scene);
bg_color_tool_window->Close();
bg_color_tool = new JUI::ColorPicker(bg_color_tool_window->Content());
bg_color_tool->OnColorValueChanged += [this] (Color4 new_color) mutable { bg_color = new_color; };
}
void EditorApp::ToggleGrid()
{
grid_overlay_enabled = !grid_overlay_enabled;
}
bool EditorApp::Open()
{
if (!OpenGLWindow::Open()) return false;
auto size = GetSize();
auto vec_size = Vector2i(size.x, size.y);
if (!JGL::Init(vec_size, 0.f, 1.f))
return false;
JGL::Update(vec_size);
glClearColor(0.f, 0.f, 0.f, 0.f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
// 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!!!
LoadMisc();
CreateWidgets();
return true;
}
Vector2i EditorApp::GetTilesetCellFromMouse()
{
auto maus = GetMouseCoordinates();
Vector2 mouse_v2(maus.x, maus.y);
Vector2 rel = tileset_viewer->Content()->GetAbsolutePosition();
Vector2 rel_mouse = mouse_v2 - rel;
return Vector2i(
Math::Floor(rel_mouse.x/grid_pixel_width),
Math::Floor(rel_mouse.y/grid_pixel_height));
}
Vector2i EditorApp::GetGridCellFromMouse()
{
auto maus = GetMouseCoordinates();
Vector2 mouse_v2(maus.x, maus.y);
mouse_v2 = camera.ScreenToCameraSpace(mouse_v2);
return Vector2i(
Math::Floor(mouse_v2.x/grid_pixel_width),
Math::Floor(mouse_v2.y/grid_pixel_height));
}
void EditorApp::CameraUpdate(float elapsed)
{
float move_rate = 40;
float zoom_rate = 0.1f;
if (IsKeyDown(Keys::LeftArrow))
camera.translation.goal.x -= move_rate*elapsed;
if (IsKeyDown(Keys::RightArrow))
camera.translation.goal.x += move_rate*elapsed;
if (IsKeyDown(Keys::UpArrow))
camera.translation.goal.y -= move_rate*elapsed;
if (IsKeyDown(Keys::DownArrow))
camera.translation.goal.y += move_rate*elapsed;
if (IsKeyDown(Keys::Minus))
camera.scale.goal -= zoom_rate*elapsed;
if (IsKeyDown(Keys::Equals))
camera.scale.goal += zoom_rate*elapsed;
camera.Update(elapsed);
}
void EditorApp::SetTile(const Vector2i& cell, int tileID)
{
// out of bounds horizontally
if (cell.x > grid_rows) return;
// out of bounds vertically
if (cell.y > grid_cols) return;
grid[cell.x][cell.y] = tileID;
}
void EditorApp::PlaceTileBrush(const Vector2i& cell)
{
// out of bounds horizontally
if (cell.x > grid_rows) return;
// out of bounds vertically
if (cell.y > grid_cols) return;
SetTile(cell, selected_quad);
}
void EditorApp::EraseTile(const Vector2i& cell)
{
// out of bounds horizontally
if (cell.x > grid_rows) return;
// out of bounds vertically
if (cell.y > grid_cols) return;
SetTile(cell, -1);
}
void EditorApp::Update(float elapsed)
{
CameraUpdate(elapsed);
auto size = GetSize();
Vector2i vSize = Vector2i(size.x, size.y);
camera.screenSize.x = vSize.x;
camera.screenSize.y = vSize.y;
JGL::Update(vSize);
scene->SetViewportSize(Vector2(vSize));
scene->Update(elapsed);
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;
cell_indicator->Position(JUI::UDim2::FromPixels(rel_mouse.x, rel_mouse.y));
}
} else {
cell_indicator->Visible(false);
if (IsMouseButtonDown(MouseButtons::Left))
PlaceTileBrush(GetGridCellFromMouse());
if (IsMouseButtonDown(MouseButtons::Right))
EraseTile(GetGridCellFromMouse());
}
}
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);
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);
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);
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);
JGL::J2D::DrawDashedLine(color, left, right, 4, 4, 1 / bounds.Height());
}
}
void EditorApp::DrawGrid(const Color4& color)
{
float sw = GetWidth();
float sh = GetHeight();
DrawGrid({{0,0}, {sw, sh}}, color);
}
void EditorApp::DrawCellPointerOutline()
{
Vector2 rel_mouse = Vector2(GetGridCellFromMouse());
rel_mouse.x *= grid_pixel_width;
rel_mouse.y *= grid_pixel_height;
J2D::OutlineRect(cell_pointer_outline_color, rel_mouse, Vector2(grid_pixel_width, grid_pixel_height), cell_pointer_outline_width);
}
void EditorApp::DrawTiles()
{
for (int gx = 0; gx < grid_rows; gx++) {
for (int gy = 0; gy < grid_cols; gy++) {
auto quad_idx = grid[gx][gy];
if (quad_idx < 0)
continue;
auto quad = quads[quad_idx];
Vector2 pos(gx*grid_pixel_width, gy*grid_pixel_height);
J2D::DrawPartialSprite(test_tilesheet, pos,
Vector2(quad.x, quad.y), Vector2(quad.w, quad.h));
}
}
}
void EditorApp::Draw()
{
glClearColor(bg_color.RN(), bg_color.GN(), bg_color.BN(), 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
J2D::Begin();
glPushMatrix();
glTranslatef(-camera.translation.current.x, -camera.translation.current.y, 0);
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 (grid_overlay_enabled)
DrawGrid(grid_overlay_color);
DrawCellPointerOutline();
glPopMatrix();
auto maus = GetMouseCoordinates();
Vector2 mouse_v2(maus.x, maus.y);
auto bruhbruh = camera.CameraToScreenSpace(camera.ScreenToCameraSpace(mouse_v2));
J2D::DrawPoint(Colors::White, bruhbruh, 4);
J2D::End();
scene->Draw();
}
void EditorApp::OnClosing()
{
SaveTestFile();
}

28
src/EditorCamera.cpp Normal file
View File

@@ -0,0 +1,28 @@
#include <EditorCamera.hpp>
Matrix4x4 EditorCamera::CurrentTranslationMatrix() const
{
return Matrix4x4::FromTranslation({translation.current.x, translation.current.y, 0});
}
Matrix4x4 EditorCamera::GoalTranslationMatrix() const
{
return Matrix4x4::FromTranslation({translation.goal.x, translation.goal.y, 0});
}
Matrix4x4 EditorCamera::CurrentRotationMatrix() const
{
// TODO: Which axis?
return Matrix4x4::RotateX(rotation.current);
}
Matrix4x4 EditorCamera::GoalRotationMatrix() const
{
// TODO: Which axis?
return Matrix4x4::RotateX(rotation.goal);
}
Matrix4x4 EditorCamera::CurrentScaleMatrix() const
{
return Matrix4x4::Scale({scale.current, scale.current, 1});
}