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.pngBinary files differ new 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 | 
