[nostalgia/core] Add support for drawing NostalgiaGraphics in SDL
This commit is contained in:
parent
79a1a6f896
commit
52c4744242
@ -17,8 +17,8 @@
|
|||||||
|
|
||||||
namespace nostalgia::core {
|
namespace nostalgia::core {
|
||||||
|
|
||||||
ox::Error init(Context *ctx);
|
[[nodiscard]] ox::Error init(Context *ctx);
|
||||||
|
|
||||||
ox::Error run();
|
[[nodiscard]] ox::Error run();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,11 @@
|
|||||||
|
|
||||||
namespace nostalgia::core {
|
namespace nostalgia::core {
|
||||||
|
|
||||||
|
enum class TileSheetSpace {
|
||||||
|
Background,
|
||||||
|
Sprite
|
||||||
|
};
|
||||||
|
|
||||||
using Color = uint16_t;
|
using Color = uint16_t;
|
||||||
|
|
||||||
struct NostalgiaPalette {
|
struct NostalgiaPalette {
|
||||||
@ -26,7 +31,7 @@ struct NostalgiaGraphic {
|
|||||||
static constexpr auto Fields = 4;
|
static constexpr auto Fields = 4;
|
||||||
uint8_t bpp = 0;
|
uint8_t bpp = 0;
|
||||||
ox::FileAddress defaultPalette;
|
ox::FileAddress defaultPalette;
|
||||||
ox::Vector<Color> pal;
|
NostalgiaPalette pal;
|
||||||
ox::Vector<uint8_t> tiles;
|
ox::Vector<uint8_t> tiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,9 +52,16 @@ ox::Error model(T *io, NostalgiaPalette *pal) {
|
|||||||
return OxError(0);
|
return OxError(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error initGfx(Context *ctx);
|
[[nodiscard]] ox::Error initGfx(Context *ctx);
|
||||||
|
|
||||||
ox::Error initConsole(Context *ctx);
|
[[nodiscard]] ox::Error shutdownGfx();
|
||||||
|
|
||||||
|
[[nodiscard]] ox::Error initConsole(Context *ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param section describes which section of the selected TileSheetSpace to use (e.g. MEM_PALLETE_BG[section])
|
||||||
|
*/
|
||||||
|
[[nodiscard]] ox::Error loadTileSheet(Context *ctx, TileSheetSpace tss, int section, ox::FileAddress tilesheet, ox::FileAddress palette = nullptr);
|
||||||
|
|
||||||
ox::Error loadTileSheet(Context *ctx, ox::FileAddress file);
|
ox::Error loadTileSheet(Context *ctx, ox::FileAddress file);
|
||||||
|
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
namespace nostalgia::core {
|
namespace nostalgia::core {
|
||||||
|
|
||||||
|
void draw();
|
||||||
|
|
||||||
ox::Error run() {
|
ox::Error run() {
|
||||||
for (auto running = true; running;) {
|
for (auto running = true; running;) {
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
@ -28,7 +30,7 @@ ox::Error run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SDL_Delay(1);
|
draw();
|
||||||
}
|
}
|
||||||
return OxError(0);
|
return OxError(0);
|
||||||
}
|
}
|
||||||
|
@ -6,25 +6,153 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
#include "../gfx.hpp"
|
#include <ox/mc/read.hpp>
|
||||||
|
|
||||||
|
#include <nostalgia/core/gfx.hpp>
|
||||||
|
|
||||||
namespace nostalgia::core {
|
namespace nostalgia::core {
|
||||||
|
|
||||||
static SDL_Window *window = nullptr;
|
static SDL_Window *window = nullptr;
|
||||||
|
static SDL_Renderer *renderer = nullptr;
|
||||||
|
|
||||||
|
static std::array<SDL_Texture*, 4> bgTextures;
|
||||||
|
static std::vector<std::vector<int>> bgTileMaps(128, std::vector<int>(128, 0));
|
||||||
|
|
||||||
|
[[nodiscard]] static ox::ValErr<ox::Vector<uint8_t>> readFile(Context *ctx, const ox::FileAddress &file) {
|
||||||
|
auto [stat, err] = ctx->rom->stat(file);
|
||||||
|
oxReturnError(err);
|
||||||
|
ox::Vector<uint8_t> buff(stat.size);
|
||||||
|
oxReturnError(ctx->rom->read(file, buff.data(), buff.size()));
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] ox::ValErr<T> readMC(Context *ctx, const ox::FileAddress &file) {
|
||||||
|
auto [buff, err] = readFile(ctx, file);
|
||||||
|
oxReturnError(err);
|
||||||
|
T t;
|
||||||
|
oxReturnError(ox::readMC(buff.data(), buff.size(), &t));
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
ox::Error initGfx(Context*) {
|
ox::Error initGfx(Context*) {
|
||||||
window = SDL_CreateWindow("nostalgia", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1024, 768, SDL_WINDOW_SHOWN);
|
window = SDL_CreateWindow("nostalgia", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1024, 768, SDL_WINDOW_SHOWN);
|
||||||
|
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||||
return OxError(window == nullptr);
|
return OxError(window == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ox::Error shutdownGfx() {
|
||||||
|
for (auto tex : bgTextures) {
|
||||||
|
SDL_DestroyTexture(tex);
|
||||||
|
}
|
||||||
|
SDL_DestroyRenderer(renderer);
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
return OxError(0);
|
||||||
|
}
|
||||||
|
|
||||||
ox::Error initConsole(Context*) {
|
ox::Error initConsole(Context*) {
|
||||||
return OxError(1);
|
return OxError(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error loadTileSheet(Context*, ox::FileAddress) {
|
SDL_Color createSDL_Color(Color nc) {
|
||||||
return OxError(1);
|
SDL_Color c;
|
||||||
|
// extract the color chanels and scale them up for a 24 bit color
|
||||||
|
c.r = ((nc & 0b0000000000011111) >> 0) * 8;
|
||||||
|
c.g = ((nc & 0b0000001111100000) >> 5) * 8;
|
||||||
|
c.b = ((nc & 0b0111110000000000) >> 10) * 8;
|
||||||
|
c.a = 1;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Palette *createSDL_Palette(const NostalgiaPalette &npal) {
|
||||||
|
auto pal = SDL_AllocPalette(npal.colors.size());
|
||||||
|
for (std::size_t i = 0; i < npal.colors.size(); ++i) {
|
||||||
|
pal->colors[i] = createSDL_Color(npal.colors[i]);
|
||||||
|
}
|
||||||
|
return pal;
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error loadTileSheet(Context *ctx,
|
||||||
|
TileSheetSpace tss,
|
||||||
|
int section,
|
||||||
|
ox::FileAddress tilesheetPath,
|
||||||
|
ox::FileAddress palettePath) {
|
||||||
|
auto [tilesheet, tserr] = readMC<NostalgiaGraphic>(ctx, tilesheetPath);
|
||||||
|
oxReturnError(tserr);
|
||||||
|
NostalgiaPalette palette;
|
||||||
|
if (palettePath) {
|
||||||
|
oxReturnError(readMC<NostalgiaPalette>(ctx, palettePath).get(&palette));
|
||||||
|
} else {
|
||||||
|
palette = tilesheet.pal;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto bytesPerTile = tilesheet.bpp == 8 ? 64 : 32;
|
||||||
|
const auto tiles = tilesheet.tiles.size() / bytesPerTile;
|
||||||
|
const int width = 8;
|
||||||
|
const int height = 8 * tiles;
|
||||||
|
const auto format = SDL_PIXELFORMAT_INDEX8;
|
||||||
|
auto surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 16, format);
|
||||||
|
auto sdlPalette = createSDL_Palette(palette);
|
||||||
|
SDL_SetSurfacePalette(surface, sdlPalette);
|
||||||
|
if (bytesPerTile == 1) {
|
||||||
|
SDL_memcpy(surface->pixels, tilesheet.tiles.data(), bytesPerTile * tiles);
|
||||||
|
} else {
|
||||||
|
for (std::size_t i = 0; i < tilesheet.tiles.size(); ++i) {
|
||||||
|
static_cast<uint8_t*>(surface->pixels)[i * 2 + 0] = tilesheet.tiles[i] & 0xF;
|
||||||
|
static_cast<uint8_t*>(surface->pixels)[i * 2 + 1] = tilesheet.tiles[i] >> 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||||
|
SDL_FreeSurface(surface);
|
||||||
|
SDL_FreePalette(sdlPalette);
|
||||||
|
|
||||||
|
if (tss == TileSheetSpace::Background) {
|
||||||
|
if (bgTextures[section]) {
|
||||||
|
SDL_DestroyTexture(bgTextures[section]);
|
||||||
|
}
|
||||||
|
bgTextures[section] = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OxError(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawBackground(std::size_t bg) {
|
||||||
|
//oxTrace("nostalgia::core::drawBackground") << "Drawing background";
|
||||||
|
SDL_Rect src = {}, dst = {};
|
||||||
|
src.x = 0;
|
||||||
|
src.w = 8;
|
||||||
|
src.h = 8;
|
||||||
|
dst.x = 0;
|
||||||
|
dst.y = 0;
|
||||||
|
dst.w = 64;
|
||||||
|
dst.h = 64;
|
||||||
|
const auto tex = bgTextures[bg];
|
||||||
|
if (tex) {
|
||||||
|
for (auto &m : bgTileMaps) {
|
||||||
|
for (auto t : m) {
|
||||||
|
src.y = t * 8;
|
||||||
|
SDL_RenderCopy(renderer, tex, &src, &dst);
|
||||||
|
dst.y += 64;
|
||||||
|
}
|
||||||
|
dst.x += 64;
|
||||||
|
dst.y = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() {
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
drawBackground(0);
|
||||||
|
drawBackground(1);
|
||||||
|
drawBackground(2);
|
||||||
|
drawBackground(3);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void puts(Context*, int, const char*) {
|
void puts(Context*, int, const char*) {
|
||||||
|
@ -75,7 +75,7 @@ namespace {
|
|||||||
|
|
||||||
QMap<QRgb, int> colors;
|
QMap<QRgb, int> colors;
|
||||||
auto ng = std::make_unique<core::NostalgiaGraphic>();
|
auto ng = std::make_unique<core::NostalgiaGraphic>();
|
||||||
ng->pal.resize(countColors(src, argTiles));
|
ng->pal.colors.resize(countColors(src, argTiles));
|
||||||
if (argBpp == 4) {
|
if (argBpp == 4) {
|
||||||
ng->tiles.resize(Pixels / 2);
|
ng->tiles.resize(Pixels / 2);
|
||||||
} else {
|
} else {
|
||||||
@ -112,7 +112,7 @@ namespace {
|
|||||||
// store colors in palette with the corresponding color id
|
// store colors in palette with the corresponding color id
|
||||||
for (auto key : colors.keys()) {
|
for (auto key : colors.keys()) {
|
||||||
auto colorId = colors[key];
|
auto colorId = colors[key];
|
||||||
ng->pal[colorId] = toGbaColor(key);
|
ng->pal.colors[colorId] = toGbaColor(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ng;
|
return ng;
|
||||||
|
@ -73,7 +73,7 @@ int ImportTilesheetWizardPalettePage::accept() {
|
|||||||
if (palette != PaletteOptions.indexOf(PaletteOption_Bundle)) {
|
if (palette != PaletteOptions.indexOf(PaletteOption_Bundle)) {
|
||||||
const auto outPath = PaletteDir + paletteName + FileExt_npal;
|
const auto outPath = PaletteDir + paletteName + FileExt_npal;
|
||||||
core::NostalgiaPalette pal;
|
core::NostalgiaPalette pal;
|
||||||
pal.colors = std::move(ng->pal);
|
pal = std::move(ng->pal);
|
||||||
auto [buff, err] = toBuffer(&pal);
|
auto [buff, err] = toBuffer(&pal);
|
||||||
oxReturnError(err);
|
oxReturnError(err);
|
||||||
oxReturnError(m_ctx->project->write(outPath, buff.data(), buff.size()));
|
oxReturnError(m_ctx->project->write(outPath, buff.data(), buff.size()));
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <ox/fs/fs.hpp>
|
||||||
#include <ox/std/units.hpp>
|
#include <ox/std/units.hpp>
|
||||||
#include <nostalgia/world/world.hpp>
|
#include <nostalgia/world/world.hpp>
|
||||||
|
|
||||||
@ -13,14 +14,16 @@ using namespace nostalgia::common;
|
|||||||
using namespace nostalgia::core;
|
using namespace nostalgia::core;
|
||||||
using namespace nostalgia::world;
|
using namespace nostalgia::world;
|
||||||
|
|
||||||
int run(ox::FileSystem *fs) {
|
ox::Error run(ox::FileSystem *fs) {
|
||||||
Context ctx;
|
Context ctx;
|
||||||
init(&ctx);
|
oxReturnError(init(&ctx));
|
||||||
ctx.rom = fs;
|
ctx.rom = fs;
|
||||||
Zone zone(&ctx, Bounds{0, 0, 40, 40}, "/TileSheets/GeneralWorld");
|
Zone zone;
|
||||||
|
oxReturnError(zone.init(&ctx, Bounds{0, 0, 40, 40}, "/TileSheets/Charset.ng", "/Palettes/Charset.npal"));
|
||||||
zone.draw(&ctx);
|
zone.draw(&ctx);
|
||||||
run();
|
oxReturnError(run());
|
||||||
return 0;
|
oxReturnError(shutdownGfx());
|
||||||
|
return OxError(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef OX_USE_STDLIB
|
#ifndef OX_USE_STDLIB
|
||||||
@ -32,8 +35,14 @@ extern "C" void _start() {
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
int main() {
|
int main(int argc, const char **argv) {
|
||||||
return run(nullptr);
|
if (argc > 1) {
|
||||||
|
ox::PassThroughFS fs(argv[1]);
|
||||||
|
auto err = run(&fs);
|
||||||
|
oxAssert(err, "Something went wrong...");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -13,11 +13,15 @@ namespace nostalgia::world {
|
|||||||
using namespace common;
|
using namespace common;
|
||||||
using namespace core;
|
using namespace core;
|
||||||
|
|
||||||
Zone::Zone(Context *ctx, Bounds bnds, ox::FileAddress tileSheet) {
|
ox::Error Zone::init(Context *ctx, Bounds bnds, ox::FileAddress tileSheet, ox::FileAddress palette) {
|
||||||
const auto size = bnds.width * bnds.height;
|
const auto size = bnds.width * bnds.height;
|
||||||
m_tiles = new Tile[size];
|
m_tiles = new Tile[size];
|
||||||
m_bounds = bnds;
|
m_bounds = bnds;
|
||||||
core::loadTileSheet(ctx, tileSheet);
|
return core::loadTileSheet(ctx, core::TileSheetSpace::Background, 0, tileSheet, palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
Zone::~Zone() {
|
||||||
|
delete[] m_tiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Zone::draw(Context *ctx) {
|
void Zone::draw(Context *ctx) {
|
||||||
|
@ -47,7 +47,11 @@ struct Zone {
|
|||||||
Tile *m_tiles = nullptr;
|
Tile *m_tiles = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Zone(core::Context *ctx, common::Bounds bnds, ox::FileAddress tileSheet);
|
Zone() = default;
|
||||||
|
|
||||||
|
~Zone();
|
||||||
|
|
||||||
|
[[nodiscard]] ox::Error init(core::Context *ctx, common::Bounds bnds, ox::FileAddress tileSheet, ox::FileAddress palette = {});
|
||||||
|
|
||||||
void draw(core::Context *ctx);
|
void draw(core::Context *ctx);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user