[nostalgia/core/studio] Start tile sheet editor
This commit is contained in:
parent
69666a0b31
commit
8f21670439
@ -18,13 +18,14 @@ ox::Error initConsole(Context*) {
|
|||||||
return OxError(1);
|
return OxError(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error loadTileSheet(Context*, InodeId_t) {
|
ox::Error loadTileSheet(Context*,
|
||||||
|
TileSheetSpace,
|
||||||
|
int,
|
||||||
|
ox::FileAddress,
|
||||||
|
ox::FileAddress) {
|
||||||
return OxError(1);
|
return OxError(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void puts(Context*, int, const char*) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTile(Context*, int, int, int, uint8_t) {
|
void setTile(Context*, int, int, int, uint8_t) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,21 +1,25 @@
|
|||||||
|
|
||||||
add_library(
|
add_library(
|
||||||
NostalgiaCore-Studio SHARED
|
NostalgiaCore-Studio SHARED
|
||||||
imgconv.cpp
|
imgconv.cpp
|
||||||
import_tilesheet_wizard.cpp
|
import_tilesheet_wizard.cpp
|
||||||
new_tilesheet_wizard.cpp
|
new_tilesheet_wizard.cpp
|
||||||
plugin.cpp
|
plugin.cpp
|
||||||
|
tilesheeteditor.cpp
|
||||||
|
rsrc.qrc
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
NostalgiaCore-Studio
|
NostalgiaCore-Studio
|
||||||
Qt5::Core
|
Qt5::Core
|
||||||
Qt5::Widgets
|
Qt5::Widgets
|
||||||
|
Qt5::QuickWidgets
|
||||||
NostalgiaStudio
|
NostalgiaStudio
|
||||||
OxFS
|
OxFS
|
||||||
OxStd
|
OxStd
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(NostalgiaCore-Studio PRIVATE QT_QML_DEBUG)
|
||||||
|
|
||||||
install(
|
install(
|
||||||
TARGETS
|
TARGETS
|
||||||
NostalgiaCore-Studio
|
NostalgiaCore-Studio
|
||||||
|
22
src/nostalgia/core/studio/Pixel.qml
Normal file
22
src/nostalgia/core/studio/Pixel.qml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: pixel;
|
||||||
|
property int pixelNumber: index
|
||||||
|
color: sheetData.pixel(index)
|
||||||
|
width: parent.width / 8
|
||||||
|
height: parent.height / 8
|
||||||
|
border.color: '#717d7e'
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: (index % 8 + 1) + ', ' + Math.floor(index / 8 + 1)
|
||||||
|
font.family: 'Helvetica'
|
||||||
|
font.pointSize: 11
|
||||||
|
color: '#717d7e88'
|
||||||
|
visible: pixel.width > 42 // that's acutally not HGtG reference
|
||||||
|
anchors.horizontalCenter: pixel.horizontalCenter
|
||||||
|
anchors.bottom: pixel.bottom
|
||||||
|
}
|
||||||
|
}
|
36
src/nostalgia/core/studio/Tile.qml
Normal file
36
src/nostalgia/core/studio/Tile.qml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: tile;
|
||||||
|
property int tileNumber: -1
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
x: parent.width / 2 - tile.width / 2
|
||||||
|
y: parent.height / 2 - tile.height / 2
|
||||||
|
color: '#000000'
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
width: tile.width
|
||||||
|
height: tile.height
|
||||||
|
rows: 8
|
||||||
|
columns: 8
|
||||||
|
Repeater {
|
||||||
|
model: 64
|
||||||
|
Pixel {
|
||||||
|
pixelNumber: index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// place an outline Rectangle above the pixels
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
// make fill transparent
|
||||||
|
color: '#00000000'
|
||||||
|
border.color: '#000000'
|
||||||
|
border.width: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
src/nostalgia/core/studio/TileSheetEditor.qml
Normal file
28
src/nostalgia/core/studio/TileSheetEditor.qml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
import QtQuick 2.0
|
||||||
|
import 'qrc:/qml/'
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: tileSheetEditor
|
||||||
|
color: '#717d7e'
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
id: tileGrid
|
||||||
|
property int baseTileSize: Math.min(parent.width / tileGrid.columns, parent.height / tileGrid.rows)
|
||||||
|
width: tileGrid.columns * tileGrid.baseTileSize * 0.85
|
||||||
|
height: tileGrid.rows * tileGrid.baseTileSize * 0.85
|
||||||
|
anchors.horizontalCenter: tileSheetEditor.horizontalCenter
|
||||||
|
anchors.verticalCenter: tileSheetEditor.verticalCenter
|
||||||
|
rows: 2
|
||||||
|
columns: 3
|
||||||
|
Repeater {
|
||||||
|
model: tileGrid.rows * tileGrid.columns
|
||||||
|
Tile {
|
||||||
|
tileNumber: index
|
||||||
|
width: tileGrid.width / tileGrid.columns
|
||||||
|
height: tileGrid.height / tileGrid.rows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
15
src/nostalgia/core/studio/consts.hpp
Normal file
15
src/nostalgia/core/studio/consts.hpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2019 gtalent2@gmail.com
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace nostalgia::core {
|
||||||
|
|
||||||
|
constexpr auto PluginName = "NostalgiaCore";
|
||||||
|
|
||||||
|
}
|
@ -26,7 +26,7 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] int pointToIdx(int w, int x, int y) {
|
[[nodiscard]] int pointToIdx(int w, int x, int y) {
|
||||||
const auto colLength = 64;
|
constexpr auto colLength = 64;
|
||||||
const auto rowLength = (w / 8) * colLength;
|
const auto rowLength = (w / 8) * colLength;
|
||||||
const auto colStart = colLength * (x / 8);
|
const auto colStart = colLength * (x / 8);
|
||||||
const auto rowStart = rowLength * (y / 8);
|
const auto rowStart = rowLength * (y / 8);
|
||||||
@ -87,7 +87,7 @@ namespace {
|
|||||||
for (int x = 0; x < src.width(); x++) {
|
for (int x = 0; x < src.width(); x++) {
|
||||||
for (int y = 0; y < src.height(); y++) {
|
for (int y = 0; y < src.height(); y++) {
|
||||||
auto destI = pointToIdx(src.width(), x, y);
|
auto destI = pointToIdx(src.width(), x, y);
|
||||||
if (destI <= argTiles * 64) {
|
if (destI < argTiles * 64) {
|
||||||
const auto c = src.pixel(x, y);
|
const auto c = src.pixel(x, y);
|
||||||
// assign color a color id for the palette
|
// assign color a color id for the palette
|
||||||
if (!colors.contains(c)) {
|
if (!colors.contains(c)) {
|
||||||
|
@ -74,14 +74,12 @@ int ImportTilesheetWizardPalettePage::accept() {
|
|||||||
const auto outPath = PaletteDir + paletteName + FileExt_npal;
|
const auto outPath = PaletteDir + paletteName + FileExt_npal;
|
||||||
core::NostalgiaPalette pal;
|
core::NostalgiaPalette pal;
|
||||||
pal = std::move(ng->pal);
|
pal = std::move(ng->pal);
|
||||||
auto [buff, err] = toBuffer(&pal);
|
m_ctx->project->writeObj(outPath, &pal);
|
||||||
oxReturnError(err);
|
auto defaultPalette = outPath.toUtf8();
|
||||||
oxReturnError(m_ctx->project->write(outPath, buff.data(), buff.size()));
|
ng->defaultPalette = defaultPalette.data();
|
||||||
}
|
}
|
||||||
auto [buff, err] = toBuffer(ng.get());
|
m_ctx->project->writeObj(outPath, ng.get());
|
||||||
oxReturnError(err);
|
return 0;
|
||||||
oxReturnError(m_ctx->project->write(outPath, buff.data(), buff.size()));
|
|
||||||
return m_ctx->project->saveRomFs();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "new_tilesheet_wizard.hpp"
|
#include "new_tilesheet_wizard.hpp"
|
||||||
#include "import_tilesheet_wizard.hpp"
|
#include "import_tilesheet_wizard.hpp"
|
||||||
|
#include "tilesheeteditor.hpp"
|
||||||
|
|
||||||
#include "plugin.hpp"
|
#include "plugin.hpp"
|
||||||
|
|
||||||
@ -15,9 +16,6 @@ using namespace nostalgia::studio;
|
|||||||
|
|
||||||
namespace nostalgia::core {
|
namespace nostalgia::core {
|
||||||
|
|
||||||
Plugin::Plugin() {
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<studio::WizardMaker> Plugin::newWizards(const Context *ctx) {
|
QVector<studio::WizardMaker> Plugin::newWizards(const Context *ctx) {
|
||||||
return {
|
return {
|
||||||
{
|
{
|
||||||
@ -45,4 +43,15 @@ QVector<studio::WizardMaker> Plugin::importWizards(const studio::Context *ctx) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<studio::EditorMaker> Plugin::editors(const studio::Context *ctx) {
|
||||||
|
return {
|
||||||
|
{
|
||||||
|
{"ng"},
|
||||||
|
[ctx](QString path) {
|
||||||
|
return new TileSheetEditor(path, ctx, ctx->tabParent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,12 @@ class Plugin: public QObject, studio::Plugin {
|
|||||||
Q_INTERFACES(nostalgia::studio::Plugin)
|
Q_INTERFACES(nostalgia::studio::Plugin)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Plugin();
|
|
||||||
|
|
||||||
QVector<studio::WizardMaker> newWizards(const studio::Context *ctx) override;
|
QVector<studio::WizardMaker> newWizards(const studio::Context *ctx) override;
|
||||||
|
|
||||||
QVector<studio::WizardMaker> importWizards(const studio::Context *args) override;
|
QVector<studio::WizardMaker> importWizards(const studio::Context *args) override;
|
||||||
|
|
||||||
|
QVector<studio::EditorMaker> editors(const studio::Context *ctx) override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
7
src/nostalgia/core/studio/rsrc.qrc
Normal file
7
src/nostalgia/core/studio/rsrc.qrc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/qml">
|
||||||
|
<file>Pixel.qml</file>
|
||||||
|
<file>Tile.qml</file>
|
||||||
|
<file>TileSheetEditor.qml</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
131
src/nostalgia/core/studio/tilesheeteditor.cpp
Normal file
131
src/nostalgia/core/studio/tilesheeteditor.cpp
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2019 gtalent2@gmail.com
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QQmlContext>
|
||||||
|
#include <QQuickWidget>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QSplitter>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include <nostalgia/core/gfx.hpp>
|
||||||
|
|
||||||
|
#include "consts.hpp"
|
||||||
|
#include "tilesheeteditor.hpp"
|
||||||
|
|
||||||
|
namespace nostalgia::core {
|
||||||
|
|
||||||
|
uint32_t toColor32(Color nc) {
|
||||||
|
auto r = ((nc & 0b0000000000011111) >> 0) * 8;
|
||||||
|
auto g = ((nc & 0b0000001111100000) >> 5) * 8;
|
||||||
|
auto b = ((nc & 0b0111110000000000) >> 10) * 8;
|
||||||
|
auto a = 255;
|
||||||
|
return a | (b << 8) | (g << 16) | (r << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
QVector<uint32_t> toPixels(const NostalgiaGraphic *ng, const NostalgiaPalette *npal) {
|
||||||
|
if (!npal) {
|
||||||
|
npal = &ng->pal;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<uint32_t> out;
|
||||||
|
out.reserve(ng->tiles.size() * sizeof(Color));
|
||||||
|
|
||||||
|
if (ng->bpp == 8) {
|
||||||
|
for (std::size_t i = 0; i < ng->tiles.size(); i++) {
|
||||||
|
auto p = ng->tiles[i];
|
||||||
|
auto c = p < npal->colors.size() ? npal->colors[p] : 0;
|
||||||
|
out.push_back(toColor32(c));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (std::size_t i = 0; i < ng->tiles.size() * 2; i++) {
|
||||||
|
uint8_t p;
|
||||||
|
if (i & 1) {
|
||||||
|
p = ng->tiles[i / 2] >> 4;
|
||||||
|
} else {
|
||||||
|
p = ng->tiles[i / 2] & 0xF;
|
||||||
|
}
|
||||||
|
auto c = p < npal->colors.size() ? npal->colors[p] : 0;
|
||||||
|
out.push_back(toColor32(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
QVector<uint32_t> toPixels(const studio::Context *ctx, QString ngPath, QString palPath = "") {
|
||||||
|
auto ng = ctx->project->loadObj<NostalgiaGraphic>(ngPath);
|
||||||
|
std::unique_ptr<NostalgiaPalette> npal;
|
||||||
|
if (palPath == "" && ng->defaultPalette.type() == ox::FileAddressType::Path) {
|
||||||
|
palPath = ng->defaultPalette.getPath().value;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
npal = ctx->project->loadObj<NostalgiaPalette>(palPath);
|
||||||
|
qInfo() << "Opened palette" << palPath;
|
||||||
|
} catch (ox::Error) {
|
||||||
|
qWarning() << "Could not open palette" << palPath;
|
||||||
|
}
|
||||||
|
return toPixels(ng.get(), npal.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString SheetData::pixel(int index) {
|
||||||
|
return "#" + QString("%1").arg(QString::number(m_pixels[index], 16), 8, '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
void SheetData::updatePixels(const studio::Context *ctx, QString path) {
|
||||||
|
m_pixels = toPixels(ctx, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TileSheetEditor::TileSheetEditor(QString path, const studio::Context *ctx, QWidget *parent): QWidget(parent) {
|
||||||
|
m_ctx = ctx;
|
||||||
|
auto lyt = new QVBoxLayout(this);
|
||||||
|
m_splitter = new QSplitter(this);
|
||||||
|
auto canvas = new QQuickWidget(m_splitter);
|
||||||
|
lyt->addWidget(m_splitter);
|
||||||
|
m_splitter->addWidget(canvas);
|
||||||
|
m_splitter->addWidget(setupColorPicker(m_splitter));
|
||||||
|
m_splitter->setStretchFactor(0, 1);
|
||||||
|
m_sheetData.updatePixels(m_ctx, path);
|
||||||
|
canvas->rootContext()->setContextProperty("sheetData", &m_sheetData);
|
||||||
|
canvas->setSource(QUrl::fromLocalFile(":/qml/TileSheetEditor.qml"));
|
||||||
|
canvas->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||||
|
restoreState();
|
||||||
|
}
|
||||||
|
|
||||||
|
TileSheetEditor::~TileSheetEditor() {
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *TileSheetEditor::setupColorPicker(QWidget *parent) {
|
||||||
|
auto colorPicker = new QWidget(parent);
|
||||||
|
auto lyt = new QVBoxLayout(colorPicker);
|
||||||
|
m_colorPicker.palette = new QComboBox(colorPicker);
|
||||||
|
m_colorPicker.colorTable = new QTableWidget(colorPicker);
|
||||||
|
lyt->addWidget(m_colorPicker.palette);
|
||||||
|
lyt->addWidget(m_colorPicker.colorTable);
|
||||||
|
return colorPicker;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileSheetEditor::saveState() {
|
||||||
|
QSettings settings(m_ctx->orgName, PluginName);
|
||||||
|
settings.beginGroup("TileSheetEditor/state");
|
||||||
|
settings.setValue("m_splitter/state", m_splitter->saveState());
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileSheetEditor::restoreState() {
|
||||||
|
QSettings settings(m_ctx->orgName, PluginName);
|
||||||
|
settings.beginGroup("TileSheetEditor/state");
|
||||||
|
m_splitter->restoreState(settings.value("m_splitter/state").toByteArray());
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
63
src/nostalgia/core/studio/tilesheeteditor.hpp
Normal file
63
src/nostalgia/core/studio/tilesheeteditor.hpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2019 gtalent2@gmail.com
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QSplitter>
|
||||||
|
#include <QStringView>
|
||||||
|
#include <QTableWidget>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <nostalgia/studio/studio.hpp>
|
||||||
|
|
||||||
|
namespace nostalgia::core {
|
||||||
|
|
||||||
|
class SheetData: public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVector<uint32_t> m_pixels;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE QString pixel(int index);
|
||||||
|
|
||||||
|
void updatePixels(const studio::Context *ctx, QString path);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void refreshTileSheet();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class TileSheetEditor: public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
const studio::Context *m_ctx = nullptr;
|
||||||
|
SheetData m_sheetData;
|
||||||
|
QSplitter *m_splitter = nullptr;
|
||||||
|
struct {
|
||||||
|
QComboBox *palette = nullptr;
|
||||||
|
QTableWidget *colorTable = nullptr;
|
||||||
|
} m_colorPicker;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TileSheetEditor(QString path, const studio::Context *ctx, QWidget *parent);
|
||||||
|
|
||||||
|
virtual ~TileSheetEditor();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWidget *setupColorPicker(QWidget *widget);
|
||||||
|
|
||||||
|
void saveState();
|
||||||
|
|
||||||
|
void restoreState();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user