[nostalgia] Add implementation of single tile sprites on OpenGL
This commit is contained in:
		| @@ -61,6 +61,11 @@ class Context { | |||||||
| 	                                 unsigned cbb, | 	                                 unsigned cbb, | ||||||
| 	                                 const ox::FileAddress &tilesheetPath, | 	                                 const ox::FileAddress &tilesheetPath, | ||||||
| 	                                 const ox::FileAddress &palettePath) noexcept; | 	                                 const ox::FileAddress &palettePath) noexcept; | ||||||
|  | 	friend ox::Error loadSpriteTileSheet(Context *ctx, | ||||||
|  | 	                                     const ox::FileAddress &tilesheetAddr, | ||||||
|  | 	                                     const ox::FileAddress &paletteAddr) noexcept; | ||||||
|  | 	friend ox::Result<struct TileSheetData> loadTileSheet(Context *ctx, | ||||||
|  | 	                                                      const struct CompactTileSheet &tilesheetAddr) noexcept; | ||||||
| 	friend ox::Error run(Context *ctx) noexcept; | 	friend ox::Error run(Context *ctx) noexcept; | ||||||
| 	friend void shutdown(Context *ctx) noexcept; | 	friend void shutdown(Context *ctx) noexcept; | ||||||
| 	friend ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept; | 	friend ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept; | ||||||
| @@ -77,6 +82,15 @@ class Context { | |||||||
| 	friend constexpr void setKeyEventHandler(Context *ctx, KeyEventHandler h) noexcept; | 	friend constexpr void setKeyEventHandler(Context *ctx, KeyEventHandler h) noexcept; | ||||||
| 	friend constexpr KeyEventHandler keyEventHandler(Context *ctx) noexcept; | 	friend constexpr KeyEventHandler keyEventHandler(Context *ctx) noexcept; | ||||||
| 	friend void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept; | 	friend void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept; | ||||||
|  | 	friend void setSprite(Context *ctx, | ||||||
|  | 		                  unsigned idx, | ||||||
|  | 		                  int x, | ||||||
|  | 		                  int y, | ||||||
|  | 		                  unsigned tileIdx, | ||||||
|  | 		                  unsigned spriteShape, | ||||||
|  | 		                  unsigned spriteSize, | ||||||
|  | 		                  unsigned flipX) noexcept; | ||||||
|  | 	friend void hideSprite(Context *ctx, unsigned idx) noexcept; | ||||||
|  |  | ||||||
| 	public: | 	public: | ||||||
| 		ox::UniquePtr<ox::FileSystem> rom; | 		ox::UniquePtr<ox::FileSystem> rom; | ||||||
|   | |||||||
| @@ -253,15 +253,15 @@ void hideSprite(Context*, unsigned idx) noexcept { | |||||||
|  |  | ||||||
| void setSprite(Context*, | void setSprite(Context*, | ||||||
|                unsigned idx, |                unsigned idx, | ||||||
|                unsigned x, |                int x, | ||||||
|                unsigned y, |                int y, | ||||||
|                unsigned tileIdx, |                unsigned tileIdx, | ||||||
|                unsigned spriteShape, |                unsigned spriteShape, | ||||||
|                unsigned spriteSize, |                unsigned spriteSize, | ||||||
|                unsigned flipX) noexcept { |                unsigned flipX) noexcept { | ||||||
| 	oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow"); | 	oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow"); | ||||||
| 	GbaSpriteAttrUpdate oa; | 	GbaSpriteAttrUpdate oa; | ||||||
| 	oa.attr0 = static_cast<uint16_t>(y & ox::onMask<uint8_t>(0b111)) | 	oa.attr0 = static_cast<uint16_t>(y & ox::onMask<uint8_t>(0b111'1111)) | ||||||
| 	         | (static_cast<uint16_t>(1) << 10) // enable alpha | 	         | (static_cast<uint16_t>(1) << 10) // enable alpha | ||||||
| 	         | (static_cast<uint16_t>(spriteShape) << 14); | 	         | (static_cast<uint16_t>(spriteShape) << 14); | ||||||
| 	oa.attr1 = (static_cast<uint16_t>(x) & ox::onMask<uint8_t>(8)) | 	oa.attr1 = (static_cast<uint16_t>(x) & ox::onMask<uint8_t>(8)) | ||||||
|   | |||||||
| @@ -120,7 +120,7 @@ struct TileSheet { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		[[nodiscard]] | 		[[nodiscard]] | ||||||
| 		auto idx(const geo::Point &pt) const noexcept { | 		constexpr auto idx(const geo::Point &pt) const noexcept { | ||||||
| 			return ptToIdx(pt, columns); | 			return ptToIdx(pt, columns); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -275,6 +275,7 @@ struct TileSheet { | |||||||
| 		 * @param pBpp bits per pixel, need for knowing how to count the pixels | 		 * @param pBpp bits per pixel, need for knowing how to count the pixels | ||||||
| 		 * @return a count of the pixels in this sheet | 		 * @return a count of the pixels in this sheet | ||||||
| 		 */ | 		 */ | ||||||
|  | 		[[nodiscard]] | ||||||
| 		constexpr auto pixelCnt(int8_t pBpp) const noexcept { | 		constexpr auto pixelCnt(int8_t pBpp) const noexcept { | ||||||
| 			return pBpp == 4 ? pixels.size() * 2 : pixels.size(); | 			return pBpp == 4 ? pixels.size() * 2 : pixels.size(); | ||||||
| 		} | 		} | ||||||
| @@ -287,13 +288,9 @@ struct TileSheet { | |||||||
| 	SubSheet subsheet{"Root", 1, 1, bpp}; | 	SubSheet subsheet{"Root", 1, 1, bpp}; | ||||||
|  |  | ||||||
| 	constexpr TileSheet() noexcept = default; | 	constexpr TileSheet() noexcept = default; | ||||||
| 	inline TileSheet(const TileSheet &other) noexcept: | 	TileSheet(const TileSheet &other) noexcept = default; | ||||||
| 		bpp(other.bpp), |  | ||||||
| 		defaultPalette(other.defaultPalette), |  | ||||||
| 		subsheet(other.subsheet) { |  | ||||||
| 	} |  | ||||||
| 	inline TileSheet(TileSheet &&other) noexcept: | 	inline TileSheet(TileSheet &&other) noexcept: | ||||||
| 		bpp(std::move(other.bpp)), | 		bpp(other.bpp), | ||||||
| 		defaultPalette(std::move(other.defaultPalette)), | 		defaultPalette(std::move(other.defaultPalette)), | ||||||
| 		subsheet(std::move(other.subsheet)) { | 		subsheet(std::move(other.subsheet)) { | ||||||
| 	} | 	} | ||||||
| @@ -468,14 +465,24 @@ oxModelEnd() | |||||||
|  |  | ||||||
| struct Sprite { | struct Sprite { | ||||||
| 	unsigned idx = 0; | 	unsigned idx = 0; | ||||||
| 	unsigned x = 0; | 	int x = 0; | ||||||
| 	unsigned y = 0; | 	int y = 0; | ||||||
| 	unsigned tileIdx = 0; | 	unsigned tileIdx = 0; | ||||||
| 	unsigned spriteShape = 0; | 	unsigned spriteShape = 0; | ||||||
| 	unsigned spriteSize = 0; | 	unsigned spriteSize = 0; | ||||||
| 	unsigned flipX = 0; | 	unsigned flipX = 0; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | oxModelBegin(Sprite) | ||||||
|  | 	oxModelField(idx) | ||||||
|  | 	oxModelField(x) | ||||||
|  | 	oxModelField(y) | ||||||
|  | 	oxModelField(tileIdx) | ||||||
|  | 	oxModelField(spriteShape) | ||||||
|  | 	oxModelField(spriteSize) | ||||||
|  | 	oxModelField(flipX) | ||||||
|  | oxModelEnd() | ||||||
|  |  | ||||||
| ox::Error initGfx(Context *ctx) noexcept; | ox::Error initGfx(Context *ctx) noexcept; | ||||||
|  |  | ||||||
| void addCustomDrawer(Context *ctx, Drawer *cd) noexcept; | void addCustomDrawer(Context *ctx, Drawer *cd) noexcept; | ||||||
| @@ -526,7 +533,7 @@ void clearTileLayer(Context *ctx, unsigned bgIdx) noexcept; | |||||||
|  |  | ||||||
| void hideSprite(Context *ctx, unsigned) noexcept; | void hideSprite(Context *ctx, unsigned) noexcept; | ||||||
|  |  | ||||||
| void setSprite(Context *ctx, unsigned idx, unsigned x, unsigned y, unsigned tileIdx, | void setSprite(Context *ctx, unsigned idx, int x, int y, unsigned tileIdx, | ||||||
|                unsigned spriteShape = 0, unsigned spriteSize = 0, unsigned flipX = 0) noexcept; |                unsigned spriteShape = 0, unsigned spriteSize = 0, unsigned flipX = 0) noexcept; | ||||||
|  |  | ||||||
| void setSprite(Context *ctx, const Sprite &s) noexcept; | void setSprite(Context *ctx, const Sprite &s) noexcept; | ||||||
|   | |||||||
| @@ -32,15 +32,13 @@ ox::Error run(Context *ctx) noexcept { | |||||||
| 	while (!glfwWindowShouldClose(id->window)) { | 	while (!glfwWindowShouldClose(id->window)) { | ||||||
| 		glfwPollEvents(); | 		glfwPollEvents(); | ||||||
| 		const auto ticks = ticksMs(ctx); | 		const auto ticks = ticksMs(ctx); | ||||||
| 		if (id->eventHandler) { | 		if (id->wakeupTime <= ticks) { | ||||||
| 			if (id->wakeupTime <= ticks) { | 			 sleepTime = id->eventHandler(ctx); | ||||||
| 				 sleepTime = id->eventHandler(ctx); | 			 if (sleepTime >= 0) { | ||||||
| 				 if (sleepTime >= 0) { | 				  id->wakeupTime = ticks + static_cast<unsigned>(sleepTime); | ||||||
| 					  id->wakeupTime = ticks + static_cast<unsigned>(sleepTime); | 			 } else { | ||||||
| 				 } else { | 				  id->wakeupTime = ~uint64_t(0); | ||||||
| 					  id->wakeupTime = ~uint64_t(0); | 			 } | ||||||
| 				 } |  | ||||||
| 			} |  | ||||||
| 		} else { | 		} else { | ||||||
| 			sleepTime = 10; | 			sleepTime = 10; | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ namespace nostalgia::core { | |||||||
| struct GlfwImplData { | struct GlfwImplData { | ||||||
| 	struct GLFWwindow *window = nullptr; | 	struct GLFWwindow *window = nullptr; | ||||||
| 	int64_t startTime = 0; | 	int64_t startTime = 0; | ||||||
| 	UpdateHandler eventHandler = nullptr; | 	UpdateHandler eventHandler = [](Context*) -> int {return 0;}; | ||||||
| 	KeyEventHandler keyEventHandler = nullptr; | 	KeyEventHandler keyEventHandler = nullptr; | ||||||
| 	uint64_t wakeupTime = 0; | 	uint64_t wakeupTime = 0; | ||||||
| 	uint64_t keysDown = 0; | 	uint64_t keysDown = 0; | ||||||
|   | |||||||
| @@ -60,7 +60,6 @@ ox::Error loadBgTileSheet(Context*, | |||||||
| } | } | ||||||
|  |  | ||||||
| ox::Error loadSpriteTileSheet(Context*, | ox::Error loadSpriteTileSheet(Context*, | ||||||
|                               int, |  | ||||||
|                               const ox::FileAddress&, |                               const ox::FileAddress&, | ||||||
|                               const ox::FileAddress&) noexcept { |                               const ox::FileAddress&) noexcept { | ||||||
| 	return OxError(0); | 	return OxError(0); | ||||||
| @@ -91,8 +90,8 @@ void hideSprite(Context*, unsigned) noexcept { | |||||||
|  |  | ||||||
| void setSprite(Context*, | void setSprite(Context*, | ||||||
|                unsigned, |                unsigned, | ||||||
|                unsigned, |                int, | ||||||
|                unsigned, |                int, | ||||||
|                unsigned, |                unsigned, | ||||||
|                unsigned, |                unsigned, | ||||||
|                unsigned, |                unsigned, | ||||||
|   | |||||||
| @@ -17,39 +17,58 @@ ox::Error initConsole(Context *ctx) noexcept { | |||||||
| 	return loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr); | 	return loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr); | ||||||
| } | } | ||||||
|  |  | ||||||
| ox::Error loadBgTileSheet(Context *ctx, | struct TileSheetData { | ||||||
|                           unsigned cbb, | 	ox::Vector<uint32_t> pixels; | ||||||
|                           const ox::FileAddress &tilesheetPath, | 	int width = 0; | ||||||
|                           const ox::FileAddress &palettePath) noexcept { | 	int height = 0; | ||||||
| 	oxRequire(tilesheet, readObj<CompactTileSheet>(ctx, tilesheetPath)); | }; | ||||||
| 	oxRequire(palette, readObj<Palette>(ctx, palettePath ? palettePath : tilesheet->defaultPalette)); |  | ||||||
| 	const unsigned bytesPerTile = tilesheet->bpp == 8 ? 64 : 32; | ox::Result<TileSheetData> loadTileSheet(Context *ctx, const CompactTileSheet &tilesheet) noexcept { | ||||||
| 	const auto tiles = tilesheet->pixels.size() / bytesPerTile; | 	const unsigned bytesPerTile = tilesheet.bpp == 8 ? 64 : 32; | ||||||
|  | 	const auto tiles = tilesheet.pixels.size() / bytesPerTile; | ||||||
| 	constexpr int width = 8; | 	constexpr int width = 8; | ||||||
| 	const int height = 8 * static_cast<int>(tiles); | 	const int height = 8 * static_cast<int>(tiles); | ||||||
| 	ox::Vector<uint32_t> pixels; | 	ox::Vector<uint32_t> pixels; | ||||||
| 	if (bytesPerTile == 64) { // 8 BPP | 	if (bytesPerTile == 64) { // 8 BPP | ||||||
| 		pixels.resize(tilesheet->pixels.size()); | 		pixels.resize(tilesheet.pixels.size()); | ||||||
| 		for (std::size_t i = 0; i < tilesheet->pixels.size(); ++i) { | 		for (std::size_t i = 0; i < tilesheet.pixels.size(); ++i) { | ||||||
| 			pixels[i] = tilesheet->pixels[i]; | 			pixels[i] = tilesheet.pixels[i]; | ||||||
| 		} | 		} | ||||||
| 	} else { // 4 BPP | 	} else { // 4 BPP | ||||||
| 		pixels.resize(tilesheet->pixels.size() * 2); | 		pixels.resize(tilesheet.pixels.size() * 2); | ||||||
| 		for (std::size_t i = 0; i < tilesheet->pixels.size(); ++i) { | 		for (std::size_t i = 0; i < tilesheet.pixels.size(); ++i) { | ||||||
| 			pixels[i * 2 + 0] = tilesheet->pixels[i] & 0xF; | 			pixels[i * 2 + 0] = tilesheet.pixels[i] & 0xF; | ||||||
| 			pixels[i * 2 + 1] = tilesheet->pixels[i] >> 4; | 			pixels[i * 2 + 1] = tilesheet.pixels[i] >> 4; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	const auto rd = ctx->rendererData<void>(); | 	const auto rd = ctx->rendererData<void>(); | ||||||
| 	renderer::loadBgTexture(rd, cbb, pixels.data(), width, height); | 	renderer::loadSpriteTexture(rd, pixels.data(), width, height); | ||||||
| 	renderer::loadBgPalette(rd, *palette); | 	return TileSheetData{std::move(pixels), width, height}; | ||||||
| 	return OxError(0); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| ox::Error loadSpriteTileSheet(Context*, | ox::Error loadBgTileSheet(Context *ctx, | ||||||
|                               const ox::FileAddress&, |                           unsigned cbb, | ||||||
|                               const ox::FileAddress&) noexcept { |                           const ox::FileAddress &tilesheetAddr, | ||||||
| 	return OxError(0); |                           const ox::FileAddress &paletteAddr) noexcept { | ||||||
|  | 	oxRequire(tilesheet, readObj<CompactTileSheet>(ctx, tilesheetAddr)); | ||||||
|  | 	oxRequire(palette, readObj<Palette>(ctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette)); | ||||||
|  | 	oxRequire(tsd, loadTileSheet(ctx, *tilesheet)); | ||||||
|  | 	const auto rd = ctx->rendererData<void>(); | ||||||
|  | 	renderer::loadBgTexture(rd, cbb, tsd.pixels.data(), tsd.width, tsd.height); | ||||||
|  | 	renderer::loadBgPalette(rd, *palette); | ||||||
|  | 	return {}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ox::Error loadSpriteTileSheet(Context *ctx, | ||||||
|  |                               const ox::FileAddress &tilesheetAddr, | ||||||
|  |                               const ox::FileAddress &paletteAddr) noexcept { | ||||||
|  | 	oxRequire(tilesheet, readObj<CompactTileSheet>(ctx, tilesheetAddr)); | ||||||
|  | 	oxRequire(palette, readObj<Palette>(ctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette)); | ||||||
|  | 	oxRequire(tsd, loadTileSheet(ctx, *tilesheet)); | ||||||
|  | 	const auto rd = ctx->rendererData<void>(); | ||||||
|  | 	renderer::loadSpriteTexture(rd, tsd.pixels.data(), tsd.width, tsd.height); | ||||||
|  | 	renderer::loadSpritePalette(rd, *palette); | ||||||
|  | 	return {}; | ||||||
| } | } | ||||||
|  |  | ||||||
| void puts(Context *ctx, int column, int row, ox::CRStringView str) noexcept { | void puts(Context *ctx, int column, int row, ox::CRStringView str) noexcept { | ||||||
|   | |||||||
| @@ -20,6 +20,10 @@ void shutdown(Context *ctx, void *rendererData) noexcept; | |||||||
|  |  | ||||||
| void loadBgPalette(void *rendererData, const Palette &pal) noexcept; | void loadBgPalette(void *rendererData, const Palette &pal) noexcept; | ||||||
|  |  | ||||||
| void loadBgTexture(void *rendererData, unsigned cbb, void *pixels, int w, int h) noexcept; | void loadBgTexture(void *rendererData, unsigned cbb, const void *pixels, int w, int h) noexcept; | ||||||
|  |  | ||||||
|  | void loadSpritePalette(void *rendererData, const Palette &pal) noexcept; | ||||||
|  |  | ||||||
|  | void loadSpriteTexture(void *rendererData, const void *pixels, int w, int h) noexcept; | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -22,55 +22,96 @@ namespace renderer { | |||||||
| constexpr uint64_t TileRows = 128; | constexpr uint64_t TileRows = 128; | ||||||
| constexpr uint64_t TileColumns = 128; | constexpr uint64_t TileColumns = 128; | ||||||
| constexpr uint64_t TileCount = TileRows * TileColumns; | constexpr uint64_t TileCount = TileRows * TileColumns; | ||||||
|  | constexpr uint64_t SpriteCount = 128; | ||||||
| constexpr uint64_t BgVertexVboRows = 4; | constexpr uint64_t BgVertexVboRows = 4; | ||||||
| constexpr uint64_t BgVertexVboRowLength = 4; | constexpr uint64_t BgVertexVboRowLength = 4; | ||||||
| constexpr uint64_t BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength; | constexpr uint64_t BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength; | ||||||
| constexpr uint64_t BgVertexEboLength = 6; | constexpr uint64_t BgVertexEboLength = 6; | ||||||
|  | constexpr uint64_t SpriteVertexVboRows = 4; | ||||||
|  | constexpr uint64_t SpriteVertexVboRowLength = 5; | ||||||
|  | constexpr uint64_t SpriteVertexVboLength = SpriteVertexVboRows * SpriteVertexVboRowLength; | ||||||
|  | constexpr uint64_t SpriteVertexEboLength = 6; | ||||||
|  |  | ||||||
| struct CBB: public glutils::BufferSet { | struct CBB: public glutils::BufferSet { | ||||||
| 	bool updated = false; | 	bool updated = false; | ||||||
|  | 	constexpr CBB() noexcept { | ||||||
| 	CBB() noexcept { |  | ||||||
| 		vertices.resize(TileCount * BgVertexVboLength); | 		vertices.resize(TileCount * BgVertexVboLength); | ||||||
| 		elements.resize(TileCount * BgVertexEboLength); | 		elements.resize(TileCount * BgVertexEboLength); | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | struct SpriteBlockset: public glutils::BufferSet { | ||||||
|  | 	bool updated = false; | ||||||
|  | 	constexpr SpriteBlockset() noexcept { | ||||||
|  | 		vertices.resize(SpriteCount * SpriteVertexVboLength); | ||||||
|  | 		elements.resize(SpriteCount * SpriteVertexEboLength); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  |  | ||||||
| struct Background { | struct Background { | ||||||
| 	bool enabled = false; | 	bool enabled = false; | ||||||
| 	unsigned cbbIdx = 0; | 	unsigned cbbIdx = 0; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | struct Sprite { | ||||||
|  | 	bool enabled = false; | ||||||
|  | }; | ||||||
|  |  | ||||||
| struct GlImplData { | struct GlImplData { | ||||||
| 	glutils::GLProgram bgShader; | 	glutils::GLProgram bgShader; | ||||||
|  | 	glutils::GLProgram spriteShader; | ||||||
| 	int64_t prevFpsCheckTime = 0; | 	int64_t prevFpsCheckTime = 0; | ||||||
| 	uint64_t draws = 0; | 	uint64_t draws = 0; | ||||||
| 	ox::Array<CBB, 4> cbbs; | 	ox::Array<CBB, 4> cbbs; | ||||||
|  | 	SpriteBlockset spriteBlocks; | ||||||
|  | 	ox::Array<Sprite, 128> spriteStates; | ||||||
| 	ox::Array<Background, 4> backgrounds; | 	ox::Array<Background, 4> backgrounds; | ||||||
| 	static constexpr std::size_t ColorCnt = 256; |  | ||||||
| 	ox::Array<GLfloat, ColorCnt * 3> palette{}; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| constexpr const GLchar *bgvshad = R"( | constexpr ox::StringView bgvshadTmpl = R"( | ||||||
| 	{} | 	{} | ||||||
| 	in vec2 vTexCoord; | 	in vec2 vTexCoord; | ||||||
| 	in vec2 vPosition; | 	in vec2 vPosition; | ||||||
| 	out vec2 fTexCoord; | 	out vec2 fTexCoord; | ||||||
| 	uniform float vTileHeight; | 	uniform float vTileHeight; | ||||||
| 	void main() { | 	void main() { | ||||||
| 	    gl_Position = vec4(vPosition, 0.0, 1.0); | 		gl_Position = vec4(vPosition, 0.0, 1.0); | ||||||
| 	    fTexCoord = vTexCoord * vec2(1, vTileHeight); | 		fTexCoord = vTexCoord * vec2(1, vTileHeight); | ||||||
| 	})"; | 	})"; | ||||||
|  |  | ||||||
| constexpr const GLchar *bgfshad = R"( | constexpr ox::StringView bgfshadTmpl = R"( | ||||||
| 	{} | 	{} | ||||||
| 	out vec4 outColor; | 	out vec4 outColor; | ||||||
| 	in vec2 fTexCoord; | 	in vec2 fTexCoord; | ||||||
| 	uniform sampler2D image; | 	uniform sampler2D image; | ||||||
| 	uniform vec3 fPalette[256]; | 	uniform vec4 fPalette[256]; | ||||||
| 	void main() { | 	void main() { | ||||||
| 		int idx = int(texture(image, fTexCoord).rgb.r * 256); | 		int idx = int(texture(image, fTexCoord).rgb.r * 256); | ||||||
| 		outColor = vec4(fPalette[idx], 1.0); | 		outColor = fPalette[idx]; | ||||||
|  | 		//outColor = vec4(0.0, 0.7, 1.0, 1.0); | ||||||
|  | 	})"; | ||||||
|  |  | ||||||
|  | constexpr ox::StringView spritevshadTmpl = R"( | ||||||
|  | 	{} | ||||||
|  | 	in float vEnabled; | ||||||
|  | 	in vec2 vTexCoord; | ||||||
|  | 	in vec2 vPosition; | ||||||
|  | 	out vec2 fTexCoord; | ||||||
|  | 	uniform float vTileHeight; | ||||||
|  | 	void main() { | ||||||
|  | 		gl_Position = vec4(vPosition, 0.0, 1.0); | ||||||
|  | 		fTexCoord = vTexCoord * vec2(1, vTileHeight) * vec2(vEnabled, vEnabled); | ||||||
|  | 	})"; | ||||||
|  |  | ||||||
|  | constexpr ox::StringView spritefshadTmpl = R"( | ||||||
|  | 	{} | ||||||
|  | 	out vec4 outColor; | ||||||
|  | 	in vec2 fTexCoord; | ||||||
|  | 	uniform sampler2D image; | ||||||
|  | 	uniform vec4 fPalette[256]; | ||||||
|  | 	void main() { | ||||||
|  | 		int idx = int(texture(image, fTexCoord).rgb.r * 256); | ||||||
|  | 		outColor = fPalette[idx]; | ||||||
| 		//outColor = vec4(0.0, 0.7, 1.0, 1.0); | 		//outColor = vec4(0.0, 0.7, 1.0, 1.0); | ||||||
| 	})"; | 	})"; | ||||||
|  |  | ||||||
| @@ -79,8 +120,46 @@ static constexpr auto bgVertexRow(unsigned x, unsigned y) noexcept { | |||||||
| 	return y * TileRows + x; | 	return y * TileRows + x; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void setSpriteBufferObject(Context *ctx, | ||||||
| setTileBufferObject(Context *ctx, unsigned vi, float x, float y, int textureRow, float *vbo, GLuint *ebo) noexcept { |                                   unsigned vi, | ||||||
|  | 											 float enabled, | ||||||
|  |                                   float x, float y, | ||||||
|  |                                   unsigned textureRow, | ||||||
|  |                                   unsigned flipX, | ||||||
|  |                                   float *vbo, | ||||||
|  |                                   GLuint *ebo) noexcept { | ||||||
|  | 	// don't worry, this memcpy gets optimized to something much more ideal | ||||||
|  | 	const auto [sw, sh] = getScreenSize(ctx); | ||||||
|  | 	constexpr float ymod = 0.1f; | ||||||
|  | 	const auto xmod = ymod * static_cast<float>(sh) / static_cast<float>(sw); | ||||||
|  | 	x *= xmod; | ||||||
|  | 	y *= -ymod; | ||||||
|  | 	x -= 1.f; | ||||||
|  | 	y += 1.f - ymod; | ||||||
|  | 	const auto textureRowf = static_cast<float>(textureRow); | ||||||
|  | 	const float L = flipX ? 1 : 0; | ||||||
|  | 	const float R = flipX ? 0 : 1; | ||||||
|  | 	const ox::Array<float, SpriteVertexVboLength> vertices { | ||||||
|  | 		enabled,        x,        y, L, textureRowf + 1, // bottom left | ||||||
|  | 		enabled, x + xmod,        y, R, textureRowf + 1, // bottom right | ||||||
|  | 		enabled, x + xmod, y + ymod, R, textureRowf + 0, // top right | ||||||
|  | 		enabled,        x, y + ymod, L, textureRowf + 0, // top left | ||||||
|  | 	}; | ||||||
|  | 	memcpy(vbo, vertices.data(), sizeof(vertices)); | ||||||
|  | 	const ox::Array<GLuint, SpriteVertexEboLength> elms { | ||||||
|  | 		vi + 0, vi + 1, vi + 2, | ||||||
|  | 		vi + 2, vi + 3, vi + 0, | ||||||
|  | 	}; | ||||||
|  | 	memcpy(ebo, elms.data(), sizeof(elms)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void setTileBufferObject(Context *ctx, | ||||||
|  |                                 unsigned vi, | ||||||
|  |                                 float x, | ||||||
|  |                                 float y, | ||||||
|  |                                 unsigned textureRow, | ||||||
|  |                                 float *vbo, | ||||||
|  |                                 GLuint *ebo) noexcept { | ||||||
| 	// don't worry, this memcpy gets optimized to something much more ideal | 	// don't worry, this memcpy gets optimized to something much more ideal | ||||||
| 	const auto [sw, sh] = getScreenSize(ctx); | 	const auto [sw, sh] = getScreenSize(ctx); | ||||||
| 	constexpr float ymod = 2.0f / 20.0f; | 	constexpr float ymod = 2.0f / 20.0f; | ||||||
| @@ -89,11 +168,12 @@ setTileBufferObject(Context *ctx, unsigned vi, float x, float y, int textureRow, | |||||||
| 	y *= -ymod; | 	y *= -ymod; | ||||||
| 	x -= 1.0f; | 	x -= 1.0f; | ||||||
| 	y += 1.0f - ymod; | 	y += 1.0f - ymod; | ||||||
|  | 	const auto textureRowf = static_cast<float>(textureRow); | ||||||
| 	const ox::Array<float, BgVertexVboLength> vertices { | 	const ox::Array<float, BgVertexVboLength> vertices { | ||||||
| 		       x,        y, 0, static_cast<float>(textureRow + 1), // bottom left | 		       x,        y, 0, textureRowf + 1, // bottom left | ||||||
| 		x + xmod,        y, 1, static_cast<float>(textureRow + 1), // bottom right | 		x + xmod,        y, 1, textureRowf + 1, // bottom right | ||||||
| 		x + xmod, y + ymod, 1, static_cast<float>(textureRow + 0), // top right | 		x + xmod, y + ymod, 1, textureRowf + 0, // top right | ||||||
| 		       x, y + ymod, 0, static_cast<float>(textureRow + 0), // top left | 		       x, y + ymod, 0, textureRowf + 0, // top left | ||||||
| 	}; | 	}; | ||||||
| 	memcpy(vbo, vertices.data(), sizeof(vertices)); | 	memcpy(vbo, vertices.data(), sizeof(vertices)); | ||||||
| 	const ox::Array<GLuint, BgVertexEboLength> elms { | 	const ox::Array<GLuint, BgVertexEboLength> elms { | ||||||
| @@ -103,6 +183,14 @@ setTileBufferObject(Context *ctx, unsigned vi, float x, float y, int textureRow, | |||||||
| 	memcpy(ebo, elms.data(), sizeof(elms)); | 	memcpy(ebo, elms.data(), sizeof(elms)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void initSpriteBufferObjects(Context *ctx, glutils::BufferSet *bs) noexcept { | ||||||
|  | 	for (auto i = 0u; i < SpriteCount; ++i) { | ||||||
|  | 		auto vbo = &bs->vertices[i * static_cast<std::size_t>(SpriteVertexVboLength)]; | ||||||
|  | 		auto ebo = &bs->elements[i * static_cast<std::size_t>(SpriteVertexEboLength)]; | ||||||
|  | 		setSpriteBufferObject(ctx, i * SpriteVertexVboRows, 0, 0, 0, 0, false, vbo, ebo); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| static void initBackgroundBufferObjects(Context *ctx, glutils::BufferSet *bg) noexcept { | static void initBackgroundBufferObjects(Context *ctx, glutils::BufferSet *bg) noexcept { | ||||||
| 	for (auto x = 0u; x < TileColumns; ++x) { | 	for (auto x = 0u; x < TileColumns; ++x) { | ||||||
| 		for (auto y = 0u; y < TileRows; ++y) { | 		for (auto y = 0u; y < TileRows; ++y) { | ||||||
| @@ -114,6 +202,30 @@ static void initBackgroundBufferObjects(Context *ctx, glutils::BufferSet *bg) no | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void initSpritesBufferset(Context *ctx, GLuint shader, glutils::BufferSet *bs) noexcept { | ||||||
|  | 	// vao | ||||||
|  | 	bs->vao = glutils::generateVertexArrayObject(); | ||||||
|  | 	glBindVertexArray(bs->vao); | ||||||
|  | 	// vbo & ebo | ||||||
|  | 	bs->vbo = glutils::generateBuffer(); | ||||||
|  | 	bs->ebo = glutils::generateBuffer(); | ||||||
|  | 	initSpriteBufferObjects(ctx, bs); | ||||||
|  | 	glutils::sendVbo(*bs); | ||||||
|  | 	glutils::sendEbo(*bs); | ||||||
|  | 	// vbo layout | ||||||
|  | 	auto enabledAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vEnabled")); | ||||||
|  | 	glEnableVertexAttribArray(enabledAttr); | ||||||
|  | 	glVertexAttribPointer(enabledAttr, 1, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), nullptr); | ||||||
|  | 	auto posAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vPosition")); | ||||||
|  | 	glEnableVertexAttribArray(posAttr); | ||||||
|  | 	glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), | ||||||
|  | 	                      reinterpret_cast<void*>(1 * sizeof(float))); | ||||||
|  | 	auto texCoordAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vTexCoord")); | ||||||
|  | 	glEnableVertexAttribArray(texCoordAttr); | ||||||
|  | 	glVertexAttribPointer(texCoordAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), | ||||||
|  | 	                      reinterpret_cast<void*>(3 * sizeof(float))); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void initBackgroundBufferset(Context *ctx, GLuint shader, glutils::BufferSet *bg) noexcept { | static void initBackgroundBufferset(Context *ctx, GLuint shader, glutils::BufferSet *bg) noexcept { | ||||||
| 	// vao | 	// vao | ||||||
| 	bg->vao = glutils::generateVertexArrayObject(); | 	bg->vao = glutils::generateVertexArrayObject(); | ||||||
| @@ -134,7 +246,7 @@ static void initBackgroundBufferset(Context *ctx, GLuint shader, glutils::Buffer | |||||||
| 	                      reinterpret_cast<void*>(2 * sizeof(float))); | 	                      reinterpret_cast<void*>(2 * sizeof(float))); | ||||||
| } | } | ||||||
|  |  | ||||||
| static glutils::GLTexture loadTexture(GLsizei w, GLsizei h, void *pixels) noexcept { | static glutils::GLTexture loadTexture(GLsizei w, GLsizei h, const void *pixels) noexcept { | ||||||
| 	GLuint texId = 0; | 	GLuint texId = 0; | ||||||
| 	glGenTextures(1, &texId); | 	glGenTextures(1, &texId); | ||||||
| 	glutils::GLTexture tex(texId); | 	glutils::GLTexture tex(texId); | ||||||
| @@ -179,10 +291,9 @@ static void drawBackground(CBB *cbb) noexcept { | |||||||
| static void drawBackgrounds(GlImplData *id) noexcept { | static void drawBackgrounds(GlImplData *id) noexcept { | ||||||
| 	// load background shader and its uniforms | 	// load background shader and its uniforms | ||||||
| 	glUseProgram(id->bgShader); | 	glUseProgram(id->bgShader); | ||||||
| 	const auto uniformPalette = static_cast<GLint>(glGetUniformLocation(id->bgShader, "fPalette")); |  | ||||||
| 	const auto uniformTileHeight = static_cast<GLint>(glGetUniformLocation(id->bgShader, "vTileHeight")); | 	const auto uniformTileHeight = static_cast<GLint>(glGetUniformLocation(id->bgShader, "vTileHeight")); | ||||||
| 	glUniform3fv(uniformPalette, GlImplData::ColorCnt, id->palette.data()); | 	//glUniform3fv(uniformPalette, GlImplData::ColorCnt, id->palette.data()); | ||||||
| 	for (auto &bg : id->backgrounds) { | 	for (const auto &bg : id->backgrounds) { | ||||||
| 		if (bg.enabled) { | 		if (bg.enabled) { | ||||||
| 			auto &cbb = id->cbbs[bg.cbbIdx]; | 			auto &cbb = id->cbbs[bg.cbbIdx]; | ||||||
| 			const auto tileRows = cbb.tex.height / TileHeight; | 			const auto tileRows = cbb.tex.height / TileHeight; | ||||||
| @@ -192,16 +303,39 @@ static void drawBackgrounds(GlImplData *id) noexcept { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void drawSprites(GlImplData *id) noexcept { | ||||||
|  | 	glUseProgram(id->spriteShader); | ||||||
|  | 	auto &sb = id->spriteBlocks; | ||||||
|  | 	const auto uniformTileHeight = static_cast<GLint>(glGetUniformLocation(id->spriteShader, "vTileHeight")); | ||||||
|  | 	// update vbo | ||||||
|  | 	glBindVertexArray(sb.vao); | ||||||
|  | 	if (sb.updated) { | ||||||
|  | 		sb.updated = false; | ||||||
|  | 		glutils::sendVbo(sb); | ||||||
|  | 	} | ||||||
|  | 	// set vTileHeight uniform | ||||||
|  | 	const auto tileRows = sb.tex.height / TileHeight; | ||||||
|  | 	glUniform1f(uniformTileHeight, 1.0f / static_cast<float>(tileRows)); | ||||||
|  | 	// draw | ||||||
|  | 	glBindTexture(GL_TEXTURE_2D, sb.tex); | ||||||
|  | 	glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(sb.elements.size()), GL_UNSIGNED_INT, nullptr); | ||||||
|  | } | ||||||
|  |  | ||||||
| ox::Error init(Context *ctx, void **rendererData) noexcept { | ox::Error init(Context *ctx, void **rendererData) noexcept { | ||||||
| 	const auto vshad = ox::sfmt(bgvshad, glutils::GlslVersion); | 	glEnable(GL_BLEND); | ||||||
| 	const auto fshad = ox::sfmt(bgfshad, glutils::GlslVersion); | 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||||||
| 	oxRequireM(bgShader, glutils::buildShaderProgram(vshad.c_str(), fshad.c_str())); | 	const auto bgVshad = ox::sfmt(bgvshadTmpl, glutils::GlslVersion); | ||||||
| 	const auto id = new GlImplData; | 	const auto bgFshad = ox::sfmt(bgfshadTmpl, glutils::GlslVersion); | ||||||
|  | 	const auto spriteVshad = ox::sfmt(spritevshadTmpl, glutils::GlslVersion); | ||||||
|  | 	const auto spriteFshad = ox::sfmt(spritefshadTmpl, glutils::GlslVersion); | ||||||
|  | 	const auto id = ox::make<GlImplData>(); | ||||||
| 	*rendererData = id; | 	*rendererData = id; | ||||||
| 	id->bgShader = std::move(bgShader); | 	oxReturnError(glutils::buildShaderProgram(bgVshad.c_str(), bgFshad.c_str()).moveTo(&id->bgShader)); | ||||||
|  | 	oxReturnError(glutils::buildShaderProgram(spriteVshad.c_str(), spriteFshad.c_str()).moveTo(&id->spriteShader)); | ||||||
| 	for (auto &bg : id->cbbs) { | 	for (auto &bg : id->cbbs) { | ||||||
| 		initBackgroundBufferset(ctx, id->bgShader, &bg); | 		initBackgroundBufferset(ctx, id->bgShader, &bg); | ||||||
| 	} | 	} | ||||||
|  | 	initSpritesBufferset(ctx, id->spriteShader, &id->spriteBlocks); | ||||||
| 	ImGui_ImplOpenGL3_Init(glutils::GlslVersion); | 	ImGui_ImplOpenGL3_Init(glutils::GlslVersion); | ||||||
| 	return OxError(0); | 	return OxError(0); | ||||||
| } | } | ||||||
| @@ -211,20 +345,43 @@ void shutdown(Context*, void *rendererData) noexcept { | |||||||
| 	ox::safeDelete(id); | 	ox::safeDelete(id); | ||||||
| } | } | ||||||
|  |  | ||||||
| void loadBgPalette(void *rendererData, const Palette &pal) noexcept { | static void loadPalette(GLuint shaderPgrm, const Palette &pal, bool firstIsTransparent = false) noexcept { | ||||||
| 	const auto id = static_cast<GlImplData*>(rendererData); | 	static constexpr std::size_t ColorCnt = 256; | ||||||
|  | 	ox::Array<GLfloat, ColorCnt * 4> palette{}; | ||||||
| 	for (auto i = 0u; const auto c : pal.colors) { | 	for (auto i = 0u; const auto c : pal.colors) { | ||||||
| 		id->palette[i++] = redf(c); | 		palette[i++] = redf(c); | ||||||
| 		id->palette[i++] = greenf(c); | 		palette[i++] = greenf(c); | ||||||
| 		id->palette[i++] = bluef(c); | 		palette[i++] = bluef(c); | ||||||
|  | 		palette[i++] = 255; | ||||||
| 	} | 	} | ||||||
|  | 	if (firstIsTransparent) { | ||||||
|  | 		palette[3] = 0; | ||||||
|  | 	} | ||||||
|  | 	glUseProgram(shaderPgrm); | ||||||
|  | 	const auto uniformPalette = static_cast<GLint>(glGetUniformLocation(shaderPgrm, "fPalette")); | ||||||
|  | 	glUniform4fv(uniformPalette, ColorCnt, palette.data()); | ||||||
| } | } | ||||||
|  |  | ||||||
| void loadBgTexture(void *rendererData, unsigned cbbIdx, void *pixels, int w, int h) noexcept { | void loadBgPalette(void *rendererData, const Palette &pal) noexcept { | ||||||
|  | 	const auto id = static_cast<GlImplData*>(rendererData); | ||||||
|  | 	loadPalette(id->bgShader, pal); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void loadSpritePalette(void *rendererData, const Palette &pal) noexcept { | ||||||
|  | 	const auto id = static_cast<GlImplData*>(rendererData); | ||||||
|  | 	loadPalette(id->spriteShader, pal, true); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void loadBgTexture(void *rendererData, unsigned cbbIdx, const void *pixels, int w, int h) noexcept { | ||||||
| 	oxTracef("nostalgia::core::gfx::gl", "loadBgTexture: { cbbIdx: {}, w: {}, h: {} }", cbbIdx, w, h); | 	oxTracef("nostalgia::core::gfx::gl", "loadBgTexture: { cbbIdx: {}, w: {}, h: {} }", cbbIdx, w, h); | ||||||
| 	const auto id = static_cast<GlImplData*>(rendererData); | 	const auto id = static_cast<GlImplData*>(rendererData); | ||||||
| 	auto &tex = id->cbbs[cbbIdx].tex; | 	id->cbbs[cbbIdx].tex = loadTexture(w, h, pixels); | ||||||
| 	tex = loadTexture(w, h, pixels); | } | ||||||
|  |  | ||||||
|  | void loadSpriteTexture(void *rendererData, const void *pixels, int w, int h) noexcept { | ||||||
|  | 	oxTracef("nostalgia::core::gfx::gl", "loadSpriteTexture: { w: {}, h: {} }", w, h); | ||||||
|  | 	const auto id = static_cast<GlImplData*>(rendererData); | ||||||
|  | 	id->spriteBlocks.tex = loadTexture(w, h, pixels); | ||||||
| } | } | ||||||
|  |  | ||||||
| } | } | ||||||
| @@ -275,6 +432,7 @@ void draw(Context *ctx) noexcept { | |||||||
| 	glClear(GL_COLOR_BUFFER_BIT); | 	glClear(GL_COLOR_BUFFER_BIT); | ||||||
| 	// render | 	// render | ||||||
| 	renderer::drawBackgrounds(id); | 	renderer::drawBackgrounds(id); | ||||||
|  | 	renderer::drawSprites(id); | ||||||
| 	for (const auto cd : ctx->drawers) { | 	for (const auto cd : ctx->drawers) { | ||||||
| 		cd->draw(ctx); | 		cd->draw(ctx); | ||||||
| 	} | 	} | ||||||
| @@ -291,17 +449,31 @@ void clearTileLayer(Context *ctx, unsigned bgIdx) noexcept { | |||||||
| 	bg.updated = true; | 	bg.updated = true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void hideSprite(Context*, unsigned) noexcept { | void hideSprite(Context *ctx, unsigned idx) noexcept { | ||||||
|  | 	auto &id = *ctx->rendererData<renderer::GlImplData>(); | ||||||
|  | 	auto vbo = &id.spriteBlocks.vertices[idx * renderer::SpriteVertexVboLength]; | ||||||
|  | 	auto ebo = &id.spriteBlocks.elements[idx * renderer::SpriteVertexEboLength]; | ||||||
|  | 	renderer::setSpriteBufferObject(ctx, idx * renderer::SpriteVertexVboRows, 0, | ||||||
|  | 	                                0, 0, 0, false, vbo, ebo); | ||||||
|  | 	id.spriteBlocks.updated = true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void setSprite(Context*, | void setSprite(Context *ctx, | ||||||
|                unsigned, |                unsigned idx, | ||||||
|                unsigned, |                int x, | ||||||
|                unsigned, |                int y, | ||||||
|                unsigned, |                unsigned tileIdx, | ||||||
|                unsigned, |                [[maybe_unused]] unsigned spriteShape, | ||||||
|                unsigned, |                [[maybe_unused]] unsigned spriteSize, | ||||||
|                unsigned) noexcept { |                unsigned flipX) noexcept { | ||||||
|  | 	const auto uX = static_cast<unsigned>(x) % 255; | ||||||
|  | 	const auto uY = static_cast<unsigned>(y) % 127; | ||||||
|  | 	auto &id = *ctx->rendererData<renderer::GlImplData>(); | ||||||
|  | 	auto vbo = &id.spriteBlocks.vertices[idx * renderer::SpriteVertexVboLength]; | ||||||
|  | 	auto ebo = &id.spriteBlocks.elements[idx * renderer::SpriteVertexEboLength]; | ||||||
|  | 	renderer::setSpriteBufferObject(ctx, idx * renderer::SpriteVertexVboRows, 1, | ||||||
|  | 	                                static_cast<float>(uX) / 8, static_cast<float>(uY) / 8, tileIdx, flipX, vbo, ebo); | ||||||
|  | 	id.spriteBlocks.updated = true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept { | void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept { | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
| namespace nostalgia::core { | namespace nostalgia::core { | ||||||
|  |  | ||||||
| ox::Result<char*> loadRom(ox::CRStringView path) noexcept { | ox::Result<char*> loadRom(ox::CRStringView path) noexcept { | ||||||
| 	std::ifstream file(toStdStringView(path), std::ios::binary | std::ios::ate); | 	std::ifstream file(std::string(toStdStringView(path)), std::ios::binary | std::ios::ate); | ||||||
| 	if (!file.good()) { | 	if (!file.good()) { | ||||||
| 		oxErrorf("Could not find ROM file: {}", path); | 		oxErrorf("Could not find ROM file: {}", path); | ||||||
| 		return OxError(1, "Could not find ROM file"); | 		return OxError(1, "Could not find ROM file"); | ||||||
|   | |||||||
| @@ -12,14 +12,14 @@ static int spriteY = 64; | |||||||
| static int updateHandler(core::Context *ctx) noexcept { | static int updateHandler(core::Context *ctx) noexcept { | ||||||
| 	int xmod = 0; | 	int xmod = 0; | ||||||
| 	int ymod = 0; | 	int ymod = 0; | ||||||
| 	if (core::buttonDown(ctx, core::GamePad_Right)) { | 	if (core::buttonDown(ctx, core::Alpha_D) || core::buttonDown(ctx, core::GamePad_Right)) { | ||||||
| 		xmod = 2; | 		xmod = 2; | ||||||
| 	} else if (core::buttonDown(ctx, core::GamePad_Left)) { | 	} else if (core::buttonDown(ctx, core::Alpha_A) || core::buttonDown(ctx, core::GamePad_Left)) { | ||||||
| 		xmod = -2; | 		xmod = -2; | ||||||
| 	} | 	} | ||||||
| 	if (core::buttonDown(ctx, core::GamePad_Down)) { | 	if (core::buttonDown(ctx, core::Alpha_S) || core::buttonDown(ctx, core::GamePad_Down)) { | ||||||
| 		ymod = 2; | 		ymod = 2; | ||||||
| 	} else if (core::buttonDown(ctx, core::GamePad_Up)) { | 	} else if (core::buttonDown(ctx, core::Alpha_W) || core::buttonDown(ctx, core::GamePad_Up)) { | ||||||
| 		ymod = -2; | 		ymod = -2; | ||||||
| 	} | 	} | ||||||
| 	if (!xmod && !ymod) { | 	if (!xmod && !ymod) { | ||||||
| @@ -27,10 +27,10 @@ static int updateHandler(core::Context *ctx) noexcept { | |||||||
| 	} | 	} | ||||||
| 	spriteX += xmod; | 	spriteX += xmod; | ||||||
| 	spriteY += ymod; | 	spriteY += ymod; | ||||||
| 	constexpr auto s = "nostalgia"; | 	constexpr ox::StringView s = "nostalgia"; | ||||||
| 	for (unsigned i = 0; s[i]; ++i) { | 	for (unsigned i = 0; s[i]; ++i) { | ||||||
| 		const auto c = static_cast<unsigned>(s[i] - ('a' - 1)); | 		const auto c = static_cast<unsigned>(s[i] - ('a' - 1)); | ||||||
| 		core::setSprite(ctx, i, static_cast<unsigned>(spriteX) + 8 * (i + 1), static_cast<unsigned>(spriteY), c); | 		core::setSprite(ctx, i, spriteX + 8 * (static_cast<int>(i) + 1), spriteY, c); | ||||||
| 	} | 	} | ||||||
| 	return 16; | 	return 16; | ||||||
| } | } | ||||||
| @@ -53,4 +53,4 @@ ox::Error run(ox::UniquePtr<ox::FileSystem> fs) noexcept { | |||||||
| 	core::setUpdateHandler(ctx.get(), updateHandler); | 	core::setUpdateHandler(ctx.get(), updateHandler); | ||||||
| 	core::setKeyEventHandler(ctx.get(), keyEventHandler); | 	core::setKeyEventHandler(ctx.get(), keyEventHandler); | ||||||
| 	return core::run(ctx.get()); | 	return core::run(ctx.get()); | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user