[nostalgia/core/studio] Add pixel outline to tile sheet editor

This commit is contained in:
Gary Talent 2022-01-29 21:53:48 -06:00
parent 99550b60ee
commit f6be36741c
4 changed files with 81 additions and 61 deletions

View File

@ -86,6 +86,16 @@ constexpr uint8_t blue32(Color32 c) noexcept {
return (c & 0x00ff0000) >> 16; return (c & 0x00ff0000) >> 16;
} }
[[nodiscard]]
constexpr Color32 color32(uint8_t r, uint8_t g, uint8_t b) noexcept {
return static_cast<Color32>(r | (g << 8) | (b << 16));
}
[[nodiscard]]
constexpr Color32 color32(float r, float g, float b) noexcept {
return static_cast<Color32>(static_cast<uint8_t>(r * 255) | (static_cast<uint8_t>(g * 255) << 8) | (static_cast<uint8_t>(b * 255) << 16));
}
[[nodiscard]] [[nodiscard]]
constexpr float redf(Color16 c) noexcept { constexpr float redf(Color16 c) noexcept {

View File

@ -4,6 +4,8 @@
#pragma once #pragma once
#include <ox/model/def.hpp>
#include <nostalgia/common/bounds.hpp> #include <nostalgia/common/bounds.hpp>
#include <nostalgia/core/gfx.hpp> #include <nostalgia/core/gfx.hpp>
#include <nostalgia/glutils/glutils.hpp> #include <nostalgia/glutils/glutils.hpp>
@ -20,7 +22,7 @@ enum class CommandId {
ModPixel = 2, ModPixel = 2,
UpdateDimension = 3, UpdateDimension = 3,
InsertTile = 4, InsertTile = 4,
ClipboardPaste = 4, ClipboardPaste = 5,
}; };
enum class TileSheetTool: int { enum class TileSheetTool: int {
@ -49,20 +51,16 @@ struct PixelChunk {
int size = 0; int size = 0;
}; };
template<typename T> oxModelBegin(PixelChunk)
constexpr ox::Error model(T *io, PixelChunk *c) noexcept { oxModelField(pt)
io->template setTypeInfo<PixelChunk>(); oxModelField(size)
oxReturnError(io->field("pt", &c->pt)); oxModelEnd()
oxReturnError(io->field("size", &c->size));
return OxError(0);
}
struct TileSheetClipboard { struct TileSheetClipboard {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard"; static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard";
static constexpr auto TypeVersion = 1; static constexpr auto TypeVersion = 1;
template<typename T> oxModelFriend(TileSheetClipboard);
friend ox::Error model(T*, TileSheetClipboard*);
protected: protected:
ox::Vector<int> m_pixels; ox::Vector<int> m_pixels;
@ -158,4 +156,4 @@ class TileSheetEditor: public studio::Editor {
}; };
} }

View File

@ -15,24 +15,23 @@ ox::Error TileSheetGrid::buildShader() noexcept {
} }
void TileSheetGrid::draw(bool update) noexcept { void TileSheetGrid::draw(bool update) noexcept {
glLineWidth(3);
glUseProgram(m_shader); glUseProgram(m_shader);
glBindVertexArray(m_bufferSet.vao); glBindVertexArray(m_bufferSet.vao);
if (update) { if (update) {
glutils::sendVbo(m_bufferSet); glutils::sendVbo(m_bufferSet);
} }
glDrawElements(GL_POINTS, static_cast<GLsizei>(m_bufferSet.elements.size()), GL_UNSIGNED_INT, nullptr); glDrawArrays(GL_POINTS, 0, m_bufferSet.vertices.size() / VertexVboRowLength);
} }
void TileSheetGrid::initBufferSet(const NostalgiaGraphic &img) noexcept { void TileSheetGrid::initBufferSet(const NostalgiaGraphic &img) noexcept {
// vao // vao
m_bufferSet.vao = glutils::generateVertexArrayObject(); m_bufferSet.vao = glutils::generateVertexArrayObject();
glBindVertexArray(m_bufferSet.vao); glBindVertexArray(m_bufferSet.vao);
// vbo & ebo // vbo
m_bufferSet.vbo = glutils::generateBuffer(); m_bufferSet.vbo = glutils::generateBuffer();
m_bufferSet.ebo = glutils::generateBuffer();
setBufferObjects(img, &m_bufferSet); setBufferObjects(img, &m_bufferSet);
glutils::sendVbo(m_bufferSet); glutils::sendVbo(m_bufferSet);
glutils::sendEbo(m_bufferSet);
// vbo layout // vbo layout
const auto pt1Attr = static_cast<GLuint>(glGetAttribLocation(m_shader, "vPt1")); const auto pt1Attr = static_cast<GLuint>(glGetAttribLocation(m_shader, "vPt1"));
glEnableVertexAttribArray(pt1Attr); glEnableVertexAttribArray(pt1Attr);
@ -41,41 +40,54 @@ void TileSheetGrid::initBufferSet(const NostalgiaGraphic &img) noexcept {
glEnableVertexAttribArray(pt2Attr); glEnableVertexAttribArray(pt2Attr);
glVertexAttribPointer(pt2Attr, 2, GL_FLOAT, GL_FALSE, VertexVboRowLength * sizeof(float), glVertexAttribPointer(pt2Attr, 2, GL_FLOAT, GL_FALSE, VertexVboRowLength * sizeof(float),
reinterpret_cast<void*>(2 * sizeof(float))); reinterpret_cast<void*>(2 * sizeof(float)));
const auto colorAttr = static_cast<GLuint>(glGetAttribLocation(m_shader, "vColor"));
glEnableVertexAttribArray(colorAttr);
glVertexAttribPointer(colorAttr, 3, GL_FLOAT, GL_FALSE, VertexVboRowLength * sizeof(float),
reinterpret_cast<void*>(4 * sizeof(float)));
} }
void TileSheetGrid::setBufferObject(unsigned vertexRow, common::Point pt1, common::Point pt2, float *vbo, GLuint *ebo) noexcept { void TileSheetGrid::setBufferObject(common::Point pt1, common::Point pt2, Color32 c, float *vbo, const ImVec2 &pixSize) noexcept {
const auto ps = pixelSize(); const auto x1 = static_cast<float>(pt1.x) * pixSize.x - 1.f;
const auto x1 = static_cast<float>(pt1.x) * ps.x; const auto y1 = 1.f - static_cast<float>(pt1.y) * pixSize.y;
const auto y1 = static_cast<float>(pt1.y) * ps.y; const auto x2 = static_cast<float>(pt2.x) * pixSize.x - 1.f;
const auto x2 = static_cast<float>(pt2.x) * ps.x; const auto y2 = 1.f - static_cast<float>(pt2.y) * pixSize.y;
const auto y2 = static_cast<float>(pt2.y) * ps.y; // don't worry, this memcpy gets optimized to something much more ideal
// don't worry, these memcpys gets optimized to something much more ideal const float vertices[VertexVboLength] = {x1, y1, x2, y2, redf(c), greenf(c), bluef(c)};
const float vertices[VertexVboLength] = {x1, y1, x2, y2};
memcpy(vbo, vertices, sizeof(vertices)); memcpy(vbo, vertices, sizeof(vertices));
const GLuint elms[VertexEboLength] = {vertexRow};
memcpy(ebo, elms, sizeof(elms));
} }
void TileSheetGrid::setBufferObjects(const NostalgiaGraphic &img, glutils::BufferSet *bg) noexcept { void TileSheetGrid::setBufferObjects(const NostalgiaGraphic &img, glutils::BufferSet *bg) noexcept {
const auto set = [bg](unsigned i, common::Point pt1, common::Point pt2) { const auto pixSize = pixelSize();
const auto set = [bg, pixSize](unsigned i, common::Point pt1, common::Point pt2, Color32 c) {
const auto vbo = &bg->vertices[i * VertexVboLength]; const auto vbo = &bg->vertices[i * VertexVboLength];
const auto ebo = &bg->elements[i * VertexEboLength]; setBufferObject(pt1, pt2, c, vbo, pixSize);
setBufferObject(i * VertexVboRows, pt1, pt2, vbo, ebo);
}; };
// set buffer lengths // set buffer length
const auto width = img.columns * TileWidth; const auto width = img.columns * TileWidth;
const auto height = img.rows * TileHeight; const auto height = img.rows * TileHeight;
const auto tiles = static_cast<unsigned>(width * height); const auto pixelCnt = static_cast<unsigned>(width * height);
m_bufferSet.vertices.resize(tiles * VertexVboLength); const auto tileCnt = static_cast<unsigned>(img.columns * img.rows);
m_bufferSet.elements.resize(tiles * VertexEboLength); m_bufferSet.vertices.resize((tileCnt + pixelCnt) * VertexVboLength);
// set buffers // set buffer
auto i = 0ull; auto i = 0ull;
for (auto x = 0; x < img.columns; ++x) { // pixel outlines
set(0, {x, 0}, {x, img.rows}); constexpr auto pixOutlineColor = color32(0.4431f, 0.4901f, 0.4941f);
for (auto x = 0; x < img.columns * TileWidth + 1; ++x) {
set(i, {x, 0}, {x, img.rows * TileHeight}, pixOutlineColor);
++i; ++i;
} }
for (auto y = 0; y < img.rows; ++y) { for (auto y = 0; y < img.rows * TileHeight + 1; ++y) {
set(0, {0, y}, {img.columns, y}); set(i, {0, y}, {img.columns * TileWidth, y}, pixOutlineColor);
++i;
}
// tile outlines
constexpr auto tileOutlineColor = color32(0.f, 0.f, 0.f);
for (auto x = 0; x < img.columns * TileWidth + 1; x += TileWidth) {
set(i, {x, 0}, {x, img.rows * TileHeight}, tileOutlineColor);
++i;
}
for (auto y = 0; y < img.rows * TileHeight + 1; y += TileHeight) {
set(i, {0, y}, {img.columns * TileWidth, y}, tileOutlineColor);
++i; ++i;
} }
} }

View File

@ -17,45 +17,45 @@ class TileSheetGrid {
private: private:
static constexpr auto VertexVboRows = 1; static constexpr auto VertexVboRows = 1;
static constexpr auto VertexVboRowLength = 4; static constexpr auto VertexVboRowLength = 7;
static constexpr auto VertexVboLength = VertexVboRows * VertexVboRowLength; static constexpr auto VertexVboLength = VertexVboRows * VertexVboRowLength;
static constexpr auto VertexEboLength = 1;
static constexpr auto VShad = R"( static constexpr auto VShad = R"glsl(
{} {}
in vec2 vPt1; in vec2 vPt1;
in vec2 vPt2; in vec2 vPt2;
out VS_OUT { in vec3 vColor;
vec2 pt1; out vec2 gPt2;
vec2 pt2; out vec3 gColor;
} vs_out;
void main() { void main() {
vs_out.pt1 = vPt1; gColor = vColor;
vs_out.pt2 = vPt2;
gl_Position = vec4(vPt1, 0.0, 1.0); gl_Position = vec4(vPt1, 0.0, 1.0);
})"; gPt2 = vPt2;
})glsl";
static constexpr auto FShad = R"( static constexpr auto FShad = R"glsl(
{} {}
in vec3 fColor; in vec3 fColor;
out vec4 outColor; out vec4 outColor;
void main() { void main() {
outColor = vec4(0.4431, 0.4901, 0.4941, 1.0); outColor = vec4(fColor, 1);
})"; //outColor = vec4(0.4431, 0.4901, 0.4941, 1.0);
})glsl";
static constexpr auto GShad = R"glsl( static constexpr auto GShad = R"glsl(
{} {}
in VS_OUT { layout(points) in;
vec2 pt1; layout(line_strip, max_vertices = 9) out;
vec2 pt2; in vec3 gColor[];
} gs_in[]; in vec2 gPt2[];
layout(line_strip, max_vertices = 2) out; out vec3 fColor;
void main() { void main() {
//gl_Position = vec4(gs_in[0].pt1, 0, 0); fColor = gColor[0];
//EmitVertex(); gl_Position = gl_in[0].gl_Position;
//gl_Position = vec4(gs_in[0].pt2, 0, 0); EmitVertex();
//EmitVertex(); gl_Position = vec4(gPt2[0], 0, 1);
//EndPrimitive(); EmitVertex();
EndPrimitive();
})glsl"; })glsl";
glutils::GLProgram m_shader; glutils::GLProgram m_shader;
@ -69,7 +69,7 @@ class TileSheetGrid {
void initBufferSet(const NostalgiaGraphic &img) noexcept; void initBufferSet(const NostalgiaGraphic &img) noexcept;
private: private:
static void setBufferObject(unsigned vertexRow, common::Point pt1, common::Point pt2, float *vbo, GLuint *ebo) noexcept; static void setBufferObject(common::Point pt1, common::Point pt2, Color32 c, float *vbo, const ImVec2 &pixSize) noexcept;
void setBufferObjects(const NostalgiaGraphic &img, glutils::BufferSet *bg) noexcept; void setBufferObjects(const NostalgiaGraphic &img, glutils::BufferSet *bg) noexcept;