diff options
-rw-r--r-- | bitmap.cpp | 53 | ||||
-rw-r--r-- | bitmap.h | 22 | ||||
-rw-r--r-- | level.cpp | 54 | ||||
-rw-r--r-- | level.h | 28 | ||||
-rw-r--r-- | main.cpp | 134 | ||||
-rw-r--r-- | tiles.png | bin | 0 -> 1419 bytes | |||
-rw-r--r-- | window.cpp | 61 | ||||
-rw-r--r-- | window.h | 36 |
8 files changed, 388 insertions, 0 deletions
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 <cstdint> + +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 @@ -0,0 +1,28 @@ +#pragma once + +#include <cstdint> +#include <cstring> + +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 <cmath> + +#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 Binary files differnew file mode 100644 index 0000000..09707e2 --- /dev/null +++ b/tiles.png 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 <SDL2/SDL.h> +#include <cstdio> + +#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 <functional> + +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<void(int, int, int)> callback) { m_mouseDown = callback; } + void onMouseUp(std::function<void(int, int, int)> callback) { m_mouseUp = callback; } + void onMouseMove(std::function<void(int, int)> 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<void(int, int, int)> m_mouseDown; + std::function<void(int, int, int)> m_mouseUp; + std::function<void(int, int)> m_mouseMove; +};
\ No newline at end of file |