From 98e2eb0beed4f57461956a10eeae014bf88a51c6 Mon Sep 17 00:00:00 2001 From: cflip Date: Sun, 23 Jan 2022 17:11:38 -0700 Subject: Simple tile editor --- bitmap.cpp | 53 ++++++++++++++++++++++++ bitmap.h | 22 ++++++++++ level.cpp | 54 +++++++++++++++++++++++++ level.h | 28 +++++++++++++ main.cpp | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tiles.png | Bin 0 -> 1419 bytes window.cpp | 61 ++++++++++++++++++++++++++++ window.h | 36 +++++++++++++++++ 8 files changed, 388 insertions(+) create mode 100644 bitmap.cpp create mode 100644 bitmap.h create mode 100644 level.cpp create mode 100644 level.h create mode 100644 main.cpp create mode 100644 tiles.png create mode 100644 window.cpp create mode 100644 window.h diff --git a/bitmap.cpp b/bitmap.cpp new file mode 100644 index 0000000..ccb5390 --- /dev/null +++ b/bitmap.cpp @@ -0,0 +1,53 @@ +#include "bitmap.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +Bitmap::Bitmap(const char* image) +{ + int width, height, bitDepth; + + uint8_t* buffer = stbi_load(image, &width, &height, &bitDepth, STBI_rgb_alpha); + if (!buffer) { + return; + } + + this->width = width; + this->height = height; + data = new uint32_t[width * height]; + + for (int i = 0; i < width * height; i++) { + uint8_t r = buffer[i * 4 + 0]; + uint8_t g = buffer[i * 4 + 1]; + uint8_t b = buffer[i * 4 + 2]; + uint8_t a = buffer[i * 4 + 3]; + + data[i] = (a << 24) | (r << 16) | (g << 8) | b; + } + + stbi_image_free(buffer); +} + +void Bitmap::clear(uint32_t colour) +{ + memset(data, colour, width * height * 4); +} + +void Bitmap::blit(Bitmap const& other, int xo, int yo, int xc, int yc, int w, int h) +{ + constexpr int MaskColour = 0xffff00ff; + + for (int y = 0; y < h; ++y) { + int yp = y + yo; + if (yp < 0 || yp >= height) continue; + + for (int x = 0; x < w; ++x) { + int xp = x + xo; + if (xp < 0 || xp >= width) continue; + + int src = other.data[(x + xc) + (y + yc) * other.width]; + if (src != MaskColour) + data[xp + yp * width] = src; + } + } +} \ No newline at end of file diff --git a/bitmap.h b/bitmap.h new file mode 100644 index 0000000..76f2f7a --- /dev/null +++ b/bitmap.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +class Bitmap { +public: + Bitmap(int width, int height) + : width(width), height(height) + { + data = new uint32_t[width * height]; + } + + Bitmap(const char* image); + + ~Bitmap() { delete[] data; } + + void clear(uint32_t colour); + void blit(Bitmap const& other, int x, int y, int xc, int yc, int w, int h); + + uint32_t* data; + int width, height; +}; \ No newline at end of file diff --git a/level.cpp b/level.cpp new file mode 100644 index 0000000..4df1d5a --- /dev/null +++ b/level.cpp @@ -0,0 +1,54 @@ +#include "level.h" + +Level::Level(int width, int height) + : m_width(width), m_height(height) +{ + m_tiles = new uint8_t[width * height]; + memset(m_tiles, 0, width * height); +} + +uint8_t Level::get(int x, int y) +{ + if (inBounds(x, y)) { + return m_tiles[x + y * m_width]; + } else { + return 0; + } +} + +void Level::set(int x, int y, uint8_t tile) +{ + if (inBounds(x, y)) + m_tiles[x + y * m_width] = tile; +} + +RailDirection ChooseDirection(Level& level, int x, int y) +{ + if (!level.inBounds(x, y)) return NorthSouth; + + bool n = level.get(x, y - 1) > 0; + bool e = level.get(x + 1, y) > 0; + bool s = level.get(x, y + 1) > 0; + bool w = level.get(x - 1, y) > 0; + + if ((n || s) && !(e || w)) { + return NorthSouth; + } + if ((e || w) && !(n || s)) { + return EastWest; + } + else if (s && e) { + return SouthEast; + } + else if (s && w) { + return SouthWest; + } + else if (n && w) { + return NorthWest; + } + else if (n && e) { + return NorthEast; + } + + return NorthSouth; +} \ No newline at end of file diff --git a/level.h b/level.h new file mode 100644 index 0000000..ae7ffe3 --- /dev/null +++ b/level.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +class Level { +public: + Level(int width, int height); + ~Level() { delete[] m_tiles; } + + uint8_t get(int x, int y); + void set(int x, int y, uint8_t tile); + bool inBounds(int x, int y) { return x >= 0 && x < m_width && y >= 0 && y < m_height; } +private: + int m_width, m_height; + uint8_t* m_tiles; +}; + +enum RailDirection { + NorthSouth = 1, + EastWest = 2, + SouthEast = 3, + SouthWest = 4, + NorthWest = 5, + NorthEast = 6, +}; + +RailDirection ChooseDirection(Level& level, int x, int y); \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..8af8922 --- /dev/null +++ b/main.cpp @@ -0,0 +1,134 @@ +#define SDL_MAIN_HANDLED +#include + +#include "window.h" +#include "bitmap.h" +#include "level.h" + +struct Point2D { + float x, y; +}; + +Point2D screenToTile(Point2D source) +{ + Point2D result; + + constexpr int TILE_SIZE = 24; + + float mx = source.x; + float my = source.y; + float xx = (mx / TILE_SIZE + my / (TILE_SIZE / 2.f)); + float yy = (my / (TILE_SIZE / 2.f) - (mx / TILE_SIZE)); + + result.x = floor(xx - 1.5f); // Magic numbers + result.y = floor(yy - 0.5f); + + return result; +} + +void DrawLevel(Bitmap& bitmap, Bitmap& tiles, Level& level, int xo, int yo) +{ + bitmap.clear(0xff224466); + + for (int y = 0; y < 32; ++y) { + for (int x = 0; x < 32; ++x) { + constexpr int TILE_SIZE = 24; + + auto tile = level.get(x, y); + + int xx = (x - y) * (TILE_SIZE / 2) - xo; + int yy = (x + y) * (TILE_SIZE / 4) - yo; + + int tx = (x + y) % 2; + int ty = 0; + + if (tile == NorthSouth) { + tx = 0; + ty = 2; + } + else if (tile == EastWest) { + tx = 1; + ty = 2; + } + else if (tile == SouthEast) { + tx = 0; + ty = 3; + } + else if (tile == SouthWest) { + tx = 3; + ty = 3; + } + else if (tile == NorthWest) { + tx = 1; + ty = 3; + } + else if (tile == NorthEast) { + tx = 2; + ty = 3; + } + + bitmap.blit(tiles, xx, yy, tx * 24, ty * 24, TILE_SIZE, TILE_SIZE); + } + } +} + +int main(int argc, char **argv) +{ + constexpr int Height = 240; + constexpr int Width = Height * 16 / 9; + constexpr int Scale = 3; + + Window window("Nonortho", Width, Height, Scale); + + Bitmap tiles("tiles.png"); + Level level(32, 32); + + Bitmap bitmap(Width, Height); + + int xOffs = 0, yOffs = 0; + int xDrag, yDrag; + bool isDragging = false; + + window.onMouseDown([&](int button, int x, int y) { + if (button == 1) { + float mx = x / Scale + xOffs; + float my = y / Scale + yOffs; + Point2D tilePos = screenToTile({ mx, my }); + + if (level.inBounds(tilePos.x, tilePos.y)) { + auto tile = level.get(tilePos.x, tilePos.y); + + if (tile > 0) tile = 0; + else tile = ChooseDirection(level, tilePos.x, tilePos.y); + + level.set(tilePos.x, tilePos.y, tile); + } + } else if (button == 2 && !isDragging) { + xDrag = x; + yDrag = y; + isDragging = true; + } + }); + + window.onMouseUp([&](int button, int x, int y) { + if (button == 2 && isDragging) + isDragging = false; + }); + + window.onMouseMove([&](int x, int y) { + if (isDragging) { + xOffs -= (x - xDrag) / Scale; + yOffs -= (y - yDrag) / Scale; + xDrag = x; + yDrag = y; + } + }); + + while (!window.shouldClose()) { + window.update(); + DrawLevel(bitmap, tiles, level, xOffs, yOffs); + window.draw(bitmap); + } + + return 0; +} \ No newline at end of file diff --git a/tiles.png b/tiles.png new file mode 100644 index 0000000..09707e2 Binary files /dev/null and b/tiles.png differ diff --git a/window.cpp b/window.cpp new file mode 100644 index 0000000..a907aba --- /dev/null +++ b/window.cpp @@ -0,0 +1,61 @@ +#include "window.h" + +#include +#include + +#include "bitmap.h" + +Window::Window(const char* title, int width, int height, int scale) + : m_width(width), m_height(height), m_scale(scale) +{ + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + fprintf(stderr, "Failed to initalize SDL: %s\n", SDL_GetError()); + exit(1); + } + + m_window = SDL_CreateWindow("Nonortho", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width * scale, height * scale, SDL_WINDOW_SHOWN); + m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED); + m_texture = SDL_CreateTexture(m_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, width, height); + + m_isRunning = true; +} + +Window::~Window() +{ + SDL_DestroyTexture(m_texture); + SDL_DestroyRenderer(m_renderer); + SDL_DestroyWindow(m_window); + SDL_Quit(); +} + +void Window::update() +{ + static SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + m_isRunning = false; + break; + case SDL_MOUSEBUTTONDOWN: + m_mouseDown(event.button.button, event.button.x, event.button.y); + break; + case SDL_MOUSEMOTION: + m_mouseMove(event.button.x, event.button.y); + break; + case SDL_MOUSEBUTTONUP: + m_mouseUp(event.button.button, event.button.x, event.button.y); + break; + } + } + + SDL_UpdateWindowSurface(m_window); + SDL_Delay(10); +} + +void Window::draw(Bitmap& bitmap) +{ + SDL_RenderClear(m_renderer); + SDL_UpdateTexture(m_texture, NULL, bitmap.data, m_width * 4); + SDL_RenderCopy(m_renderer, m_texture, NULL, NULL); + SDL_RenderPresent(m_renderer); +} \ No newline at end of file diff --git a/window.h b/window.h new file mode 100644 index 0000000..bdd1e91 --- /dev/null +++ b/window.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +class Bitmap; + +struct SDL_Window; +struct SDL_Renderer; +struct SDL_Texture; + +class Window { +public: + Window(const char* title, int width, int height, int scale); + ~Window(); + + void update(); + void draw(Bitmap&); + + bool shouldClose() { return !m_isRunning; } + + void onMouseDown(std::function callback) { m_mouseDown = callback; } + void onMouseUp(std::function callback) { m_mouseUp = callback; } + void onMouseMove(std::function callback) { m_mouseMove = callback; } +private: + bool m_isRunning; + + int m_width, m_height, m_scale; + + SDL_Window* m_window; + SDL_Renderer* m_renderer; + SDL_Texture* m_texture; + + std::function m_mouseDown; + std::function m_mouseUp; + std::function m_mouseMove; +}; \ No newline at end of file -- cgit v1.2.3