[jasper/world/studio] Add map tile selection
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
add_library(
|
add_library(
|
||||||
JasperWorld-Studio
|
JasperWorld-Studio
|
||||||
|
maptilehighlighter.cpp
|
||||||
studiomodule.cpp
|
studiomodule.cpp
|
||||||
worldeditor/commands/commands.hpp
|
worldeditor/commands/commands.hpp
|
||||||
)
|
)
|
||||||
|
143
src/jasper/modules/world/src/studio/maptilehighlighter.cpp
Normal file
143
src/jasper/modules/world/src/studio/maptilehighlighter.cpp
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "maptilehighlighter.hpp"
|
||||||
|
|
||||||
|
namespace jasper::world {
|
||||||
|
|
||||||
|
const glutils::ProgramSource MapTileHighlighter::s_programSrc = {
|
||||||
|
.shaderParams = {
|
||||||
|
{
|
||||||
|
.len = 2,
|
||||||
|
.name = ox::String("vPosition"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.len = 1,
|
||||||
|
.name = ox::String("vSelection"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.vertShader = ox::sfmt(R"(
|
||||||
|
{}
|
||||||
|
in vec2 vPosition;
|
||||||
|
in float vSelection;
|
||||||
|
out float fSelection;
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(vPosition, 0.0, 1.0);
|
||||||
|
fSelection = vSelection;
|
||||||
|
})", ncore::gl::GlslVersion),
|
||||||
|
.fragShader = ox::sfmt(R"(
|
||||||
|
{}
|
||||||
|
in float fSelection;
|
||||||
|
out vec4 outColor;
|
||||||
|
void main() {
|
||||||
|
outColor = vec4(0.0, 0.7, 1.0, 0.4) * fSelection;
|
||||||
|
})", ncore::gl::GlslVersion),
|
||||||
|
};
|
||||||
|
|
||||||
|
MapTileHighlighter::MapTileHighlighter() {
|
||||||
|
m_bufferSet.vao = glutils::generateVertexArrayObject();
|
||||||
|
m_bufferSet.vbo = glutils::generateBuffer();
|
||||||
|
m_bufferSet.ebo = glutils::generateBuffer();
|
||||||
|
glBindVertexArray(m_bufferSet.vao);
|
||||||
|
sendVbo(m_bufferSet);
|
||||||
|
sendEbo(m_bufferSet);
|
||||||
|
m_shader = glutils::buildShaderProgram(s_programSrc).unwrapThrow();
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error MapTileHighlighter::setup(ox::Size const&sz) noexcept {
|
||||||
|
m_subsheetTilesWidth = sz.width;
|
||||||
|
m_subsheetTilesHeight = sz.height;
|
||||||
|
initGlBuffers();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapTileHighlighter::draw() noexcept {
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
glUseProgram(m_shader);
|
||||||
|
glBindVertexArray(m_bufferSet.vao);
|
||||||
|
sendVbo(m_bufferSet);
|
||||||
|
sendEbo(m_bufferSet);
|
||||||
|
auto const elmCnt = static_cast<GLsizei>(m_bufferSet.elements.size());
|
||||||
|
glDrawElements(GL_TRIANGLES, elmCnt, GL_UNSIGNED_INT, nullptr);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
glUseProgram(0);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error MapTileHighlighter::setTileHighlight(ox::Point const&addr, bool const hl) noexcept {
|
||||||
|
if (addr.x >= m_subsheetTilesWidth || addr.y >= m_subsheetTilesHeight) {
|
||||||
|
return OxError(1, "tile addr out of bounds");
|
||||||
|
}
|
||||||
|
auto const vboLength = static_cast<size_t>(s_programSrc.rowLen) * 4;
|
||||||
|
auto constexpr eboLength = 6;
|
||||||
|
auto const [x, y] = addr;
|
||||||
|
auto const i = static_cast<size_t>(m_subsheetTilesWidth * y + x);
|
||||||
|
auto const vbo = &m_bufferSet.vertices[i * vboLength];
|
||||||
|
auto const ebo = &m_bufferSet.elements[i * eboLength];
|
||||||
|
setPixelBufferObject(
|
||||||
|
static_cast<uint_t>(i * 4),
|
||||||
|
static_cast<float>(x),
|
||||||
|
static_cast<float>(y),
|
||||||
|
hl,
|
||||||
|
vbo,
|
||||||
|
ebo);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int MapTileHighlighter::scale() noexcept {
|
||||||
|
return s_scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapTileHighlighter::setPixelBufferObject(
|
||||||
|
uint_t vertexRow,
|
||||||
|
float x, float y,
|
||||||
|
bool const selected,
|
||||||
|
float *vbo,
|
||||||
|
GLuint *ebo) noexcept {
|
||||||
|
auto constexpr xmod = static_cast<float>(ncore::TileWidth) / 240.f * 4;
|
||||||
|
auto constexpr ymod = static_cast<float>(ncore::TileHeight) / 160.f * 4;
|
||||||
|
x *= xmod;
|
||||||
|
y *= -ymod;
|
||||||
|
x -= 1.0f;
|
||||||
|
y += 1.0f - ymod;
|
||||||
|
auto const selection = 1.f * static_cast<float>(selected);
|
||||||
|
// don't worry, these memcpys gets optimized to something much more ideal
|
||||||
|
std::array const vertices{
|
||||||
|
x, y, selection, // bottom left
|
||||||
|
x + xmod, y, selection, // bottom right
|
||||||
|
x + xmod, y + ymod, selection, // top right
|
||||||
|
x, y + ymod, selection, // top left
|
||||||
|
};
|
||||||
|
memcpy(vbo, vertices.data(), sizeof(vertices));
|
||||||
|
ox::Array<GLuint, 6> const elms{
|
||||||
|
vertexRow + 0, vertexRow + 1, vertexRow + 2,
|
||||||
|
vertexRow + 2, vertexRow + 3, vertexRow + 0,
|
||||||
|
};
|
||||||
|
memcpy(ebo, elms.data(), sizeof(elms));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapTileHighlighter::initGlBuffers() noexcept {
|
||||||
|
auto const vboLength = static_cast<size_t>(s_programSrc.rowLen) * 4;
|
||||||
|
auto constexpr eboLength = 6;
|
||||||
|
auto const tileCnt =
|
||||||
|
static_cast<size_t>(m_subsheetTilesWidth) * static_cast<size_t>(m_subsheetTilesHeight);
|
||||||
|
m_bufferSet.vertices.resize(tileCnt * vboLength);
|
||||||
|
m_bufferSet.elements.resize(tileCnt * eboLength);
|
||||||
|
for (int32_t y = 0; y < m_subsheetTilesHeight; ++y) {
|
||||||
|
for (int32_t x = 0; x < m_subsheetTilesWidth; ++x) {
|
||||||
|
std::ignore = setTileHighlight({x, y}, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glUseProgram(m_shader);
|
||||||
|
glBindVertexArray(m_bufferSet.vao);
|
||||||
|
sendVbo(m_bufferSet);
|
||||||
|
sendEbo(m_bufferSet);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
src/jasper/modules/world/src/studio/maptilehighlighter.hpp
Normal file
50
src/jasper/modules/world/src/studio/maptilehighlighter.hpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glutils/glutils.hpp>
|
||||||
|
|
||||||
|
#include <studio/context.hpp>
|
||||||
|
#include <nostalgia/core/core.hpp>
|
||||||
|
|
||||||
|
#include <jasper/world/worldobject.hpp>
|
||||||
|
|
||||||
|
namespace jasper::world {
|
||||||
|
|
||||||
|
namespace ncore = nostalgia::core;
|
||||||
|
|
||||||
|
class MapTileHighlighter {
|
||||||
|
private:
|
||||||
|
static const glutils::ProgramSource s_programSrc;
|
||||||
|
static constexpr int s_scale = 5;
|
||||||
|
glutils::GLProgram m_shader;
|
||||||
|
glutils::BufferSet m_bufferSet;
|
||||||
|
int32_t m_subsheetTilesWidth{};
|
||||||
|
int32_t m_subsheetTilesHeight{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
MapTileHighlighter();
|
||||||
|
|
||||||
|
ox::Error setup(ox::Size const&sz) noexcept;
|
||||||
|
|
||||||
|
void draw() noexcept;
|
||||||
|
|
||||||
|
ox::Error setTileHighlight(ox::Point const&addr, bool hl) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static int scale() noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void setPixelBufferObject(
|
||||||
|
uint_t vertexRow,
|
||||||
|
float x, float y,
|
||||||
|
bool selected,
|
||||||
|
float *vbo,
|
||||||
|
GLuint *ebo) noexcept;
|
||||||
|
|
||||||
|
void initGlBuffers() noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
|
#include <ox/std/conv.hpp>
|
||||||
#include <ox/std/ignore.hpp>
|
#include <ox/std/ignore.hpp>
|
||||||
|
|
||||||
#include <keel/media.hpp>
|
#include <keel/media.hpp>
|
||||||
@ -47,11 +48,11 @@ static WorldDoc makeValid(WorldDoc doc) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static ox::Vec2 dropPos(ox::Vec2 dropPos) noexcept {
|
static ox::Vec2 fbPos(ox::Vec2 fbPos) noexcept {
|
||||||
auto const winPos = ImGui::GetWindowPos();
|
auto const winPos = ImGui::GetWindowPos();
|
||||||
dropPos.x -= winPos.x;
|
fbPos.x -= winPos.x;
|
||||||
dropPos.y -= winPos.y;
|
fbPos.y -= winPos.y;
|
||||||
return dropPos;
|
return fbPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
@ -70,6 +71,22 @@ constexpr ox::Point fbPtToTileAddr(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr ox::Point fbPtToTileAddr(
|
||||||
|
ox::Point const&fbPt,
|
||||||
|
ox::Vec2 const&mapSz) noexcept {
|
||||||
|
return fbPtToTileAddr(static_cast<ox::Vec2>(fbPt), mapSz);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr studio::Selection fbPtToTileAddr(
|
||||||
|
studio::Selection sel,
|
||||||
|
ox::Vec2 const&mapSz) noexcept {
|
||||||
|
sel.a = fbPtToTileAddr(sel.a, mapSz);
|
||||||
|
sel.b = fbPtToTileAddr(sel.b, mapSz);
|
||||||
|
return sel;
|
||||||
|
}
|
||||||
|
|
||||||
WorldEditorImGui::WorldEditorImGui(studio::StudioContext &sctx, ox::StringView path):
|
WorldEditorImGui::WorldEditorImGui(studio::StudioContext &sctx, ox::StringView path):
|
||||||
Editor(path),
|
Editor(path),
|
||||||
m_sctx(sctx),
|
m_sctx(sctx),
|
||||||
@ -218,33 +235,59 @@ void WorldEditorImGui::drawWorldView() noexcept {
|
|||||||
paneSize,
|
paneSize,
|
||||||
ImVec2(0, 1),
|
ImVec2(0, 1),
|
||||||
ImVec2(xScale, 1 - yScale));
|
ImVec2(xScale, 1 - yScale));
|
||||||
std::ignore = ig::dragDropTarget([&, this] {
|
std::ignore = ig::dragDropTarget([this, fbPaneScale] {
|
||||||
oxRequire(objId, ig::getDragDropPayload<WorldTileDragDrop>("WorldTile"));
|
return handleDrop(fbPaneScale);
|
||||||
auto const&io = ImGui::GetIO();
|
});
|
||||||
auto const dropPos = world::dropPos(ox::Vec2{io.MousePos});
|
handleSelection(static_cast<ox::Size>(ox::Vec2(paneSize)), fbPaneScale);
|
||||||
auto const viewSz = m_view.drawSize();
|
}
|
||||||
auto const tileAddr = fbPtToTileAddr(
|
|
||||||
dropPos,
|
void WorldEditorImGui::handleSelection(ox::Size const&paneSz, float fbPaneScale) noexcept {
|
||||||
ox::Vec2{
|
auto const&io = ImGui::GetIO();
|
||||||
|
if (io.MouseDown[0]) {
|
||||||
|
auto const fbPos = world::fbPos(ox::Vec2{io.MousePos});
|
||||||
|
auto constexpr inside = [](auto val, int min, int max) {
|
||||||
|
auto const v = static_cast<int>(val);
|
||||||
|
return v < max && v > min;
|
||||||
|
};
|
||||||
|
auto const startSel = io.MouseClicked[0]
|
||||||
|
&& inside(fbPos.x, 0, paneSz.width)
|
||||||
|
&& inside(fbPos.y, 0, paneSz.height);
|
||||||
|
m_selection.updateCursorPoint(fbPos, startSel);
|
||||||
|
auto const scaledViewSz = static_cast<ox::Vec2>(m_view.drawSize()) * fbPaneScale;
|
||||||
|
if (m_selection.selectionOngoing()) {
|
||||||
|
m_view.setSelection(fbPtToTileAddr(m_selection.selection(), scaledViewSz));
|
||||||
|
}
|
||||||
|
} else if (io.MouseReleased[0]) {
|
||||||
|
m_selection.finishSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error WorldEditorImGui::handleDrop(float fbPaneScale) noexcept {
|
||||||
|
oxRequire(objId, ig::getDragDropPayload<WorldTileDragDrop>("WorldTile"));
|
||||||
|
auto const&io = ImGui::GetIO();
|
||||||
|
auto const fbPos = world::fbPos(ox::Vec2{io.MousePos});
|
||||||
|
auto const viewSz = m_view.drawSize();
|
||||||
|
auto const tileAddr = fbPtToTileAddr(
|
||||||
|
fbPos,
|
||||||
|
ox::Vec2{
|
||||||
static_cast<float>(viewSz.width) * fbPaneScale,
|
static_cast<float>(viewSz.width) * fbPaneScale,
|
||||||
static_cast<float>(viewSz.height) * fbPaneScale});
|
static_cast<float>(viewSz.height) * fbPaneScale});
|
||||||
if (tileAddr.x < m_doc.columns && tileAddr.y < m_doc.rows) {
|
if (tileAddr.x < m_doc.columns && tileAddr.y < m_doc.rows) {
|
||||||
std::ignore = pushCommand<ModifyTilesCommand>(
|
std::ignore = pushCommand<ModifyTilesCommand>(
|
||||||
m_doc,
|
m_doc,
|
||||||
m_worldStatic,
|
m_worldStatic,
|
||||||
m_objCache,
|
m_objCache,
|
||||||
ox::Vector<ModifyTilesCommand::Mod>{
|
ox::Vector<ModifyTilesCommand::Mod>{
|
||||||
{
|
{
|
||||||
.layer = m_activeLayer,
|
.layer = m_activeLayer,
|
||||||
.tileAddr = tileAddr,
|
.tileAddr = tileAddr,
|
||||||
.objId = objId.objId,
|
.objId = objId.objId,
|
||||||
.setId = objId.setId,
|
.setId = objId.setId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
oxReturnError(loadWorldStatic(m_objCache, m_doc).moveTo(m_worldStatic));
|
oxReturnError(loadWorldStatic(m_objCache, m_doc).moveTo(m_worldStatic));
|
||||||
return ox::Error{};
|
return ox::Error{};
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error WorldEditorImGui::addObjSet(ox::StringView path) noexcept {
|
ox::Error WorldEditorImGui::addObjSet(ox::StringView path) noexcept {
|
||||||
@ -278,7 +321,8 @@ ox::Error WorldEditorImGui::loadObjectSets() noexcept {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error WorldEditorImGui::undoStackChanged(studio::UndoCommand const*) {
|
ox::Error WorldEditorImGui::undoStackChanged(studio::UndoCommand const*cmd) {
|
||||||
|
auto const clearSelection = dynamic_cast<EditWorldSizeCommand const*>(cmd) != nullptr;
|
||||||
oxReturnError(m_view.setupWorld());
|
oxReturnError(m_view.setupWorld());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,7 @@ namespace jasper::world {
|
|||||||
class WorldEditorImGui: public studio::Editor {
|
class WorldEditorImGui: public studio::Editor {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Selection {
|
studio::SelectionTracker m_selection;
|
||||||
ox::Point begin, end;
|
|
||||||
};
|
|
||||||
ox::Optional<Selection> m_selection;
|
|
||||||
uint8_t m_activeLayer{};
|
uint8_t m_activeLayer{};
|
||||||
studio::StudioContext &m_sctx;
|
studio::StudioContext &m_sctx;
|
||||||
studio::ig::FilePicker m_objSetPicker{
|
studio::ig::FilePicker m_objSetPicker{
|
||||||
@ -74,6 +71,10 @@ class WorldEditorImGui: public studio::Editor {
|
|||||||
|
|
||||||
void drawWorldView() noexcept;
|
void drawWorldView() noexcept;
|
||||||
|
|
||||||
|
void handleSelection(ox::Size const&paneSz, float fbPaneScale) noexcept;
|
||||||
|
|
||||||
|
ox::Error handleDrop(float fbPaneScale) noexcept;
|
||||||
|
|
||||||
ox::Error addObjSet(ox::StringView path) noexcept;
|
ox::Error addObjSet(ox::StringView path) noexcept;
|
||||||
|
|
||||||
void rmObjSet() noexcept;
|
void rmObjSet() noexcept;
|
||||||
|
@ -16,6 +16,12 @@ WorldEditorView::WorldEditorView(turbine::Context &tctx, WorldStatic const&world
|
|||||||
|
|
||||||
ox::Error WorldEditorView::setupWorld() noexcept {
|
ox::Error WorldEditorView::setupWorld() noexcept {
|
||||||
glutils::resizeInitFrameBuffer(m_frameBuffer, ncore::gl::drawSize(m_scale));
|
glutils::resizeInitFrameBuffer(m_frameBuffer, ncore::gl::drawSize(m_scale));
|
||||||
|
if (m_columns != m_worldStatic.columns || m_rows != m_worldStatic.rows) {
|
||||||
|
oxReturnError(m_highlighter.setup({m_worldStatic.columns, m_worldStatic.rows}));
|
||||||
|
m_columns = m_worldStatic.columns;
|
||||||
|
m_rows = m_worldStatic.rows;
|
||||||
|
m_selection.reset();
|
||||||
|
}
|
||||||
return m_world.setupDisplay(*m_cctx);
|
return m_world.setupDisplay(*m_cctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,6 +34,19 @@ void WorldEditorView::draw(ox::Size const&targetSz) noexcept {
|
|||||||
}
|
}
|
||||||
glutils::FrameBufferBind const frameBufferBind(m_frameBuffer);
|
glutils::FrameBufferBind const frameBufferBind(m_frameBuffer);
|
||||||
ncore::gl::draw(*m_cctx, m_scale);
|
ncore::gl::draw(*m_cctx, m_scale);
|
||||||
|
m_highlighter.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldEditorView::setSelection(studio::Selection const&sel) noexcept {
|
||||||
|
if (m_selection) {
|
||||||
|
studio::iterateSelection(*m_selection, [this](int32_t x, int32_t y) {
|
||||||
|
std::ignore = m_highlighter.setTileHighlight({x, y}, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
m_selection.emplace(sel);
|
||||||
|
studio::iterateSelection(*m_selection, [this](int32_t x, int32_t y) {
|
||||||
|
std::ignore = m_highlighter.setTileHighlight({x, y}, true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
glutils::FrameBuffer const&WorldEditorView::framebuffer() const noexcept {
|
glutils::FrameBuffer const&WorldEditorView::framebuffer() const noexcept {
|
||||||
@ -38,4 +57,32 @@ ox::Size WorldEditorView::drawSize() const noexcept {
|
|||||||
return ncore::gl::drawSize(m_scale);
|
return ncore::gl::drawSize(m_scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WorldEditorView::setPixelBufferObject(
|
||||||
|
unsigned vertexRow,
|
||||||
|
float x, float y,
|
||||||
|
bool const selected,
|
||||||
|
float *vbo,
|
||||||
|
GLuint *ebo) const noexcept {
|
||||||
|
auto constexpr xmod = static_cast<float>(ncore::TileWidth) / 240.f * 4;
|
||||||
|
auto constexpr ymod = static_cast<float>(ncore::TileHeight) / 160.f * 4;
|
||||||
|
x *= xmod;
|
||||||
|
y *= -ymod;
|
||||||
|
x -= 1.0f;
|
||||||
|
y += 1.0f - ymod;
|
||||||
|
auto const selection = 1.f * static_cast<float>(selected);
|
||||||
|
// don't worry, these memcpys gets optimized to something much more ideal
|
||||||
|
std::array const vertices{
|
||||||
|
x, y, selection, // bottom left
|
||||||
|
x + xmod, y, selection, // bottom right
|
||||||
|
x + xmod, y + ymod, selection, // top right
|
||||||
|
x, y + ymod, selection, // top left
|
||||||
|
};
|
||||||
|
memcpy(vbo, vertices.data(), sizeof(vertices));
|
||||||
|
ox::Array<GLuint, 6> const elms{
|
||||||
|
vertexRow + 0, vertexRow + 1, vertexRow + 2,
|
||||||
|
vertexRow + 2, vertexRow + 3, vertexRow + 0,
|
||||||
|
};
|
||||||
|
memcpy(ebo, elms.data(), sizeof(elms));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,15 @@
|
|||||||
|
|
||||||
#include <glutils/glutils.hpp>
|
#include <glutils/glutils.hpp>
|
||||||
|
|
||||||
|
#include <studio/studio.hpp>
|
||||||
|
|
||||||
#include <nostalgia/core/context.hpp>
|
#include <nostalgia/core/context.hpp>
|
||||||
#include <nostalgia/core/gfx.hpp>
|
#include <nostalgia/core/gfx.hpp>
|
||||||
|
|
||||||
#include <jasper/world/world.hpp>
|
#include <jasper/world/world.hpp>
|
||||||
|
|
||||||
|
#include "../maptilehighlighter.hpp"
|
||||||
|
|
||||||
namespace jasper::world {
|
namespace jasper::world {
|
||||||
|
|
||||||
namespace ncore = nostalgia::core;
|
namespace ncore = nostalgia::core;
|
||||||
@ -19,10 +24,13 @@ class WorldEditorView {
|
|||||||
private:
|
private:
|
||||||
ncore::ContextUPtr m_cctx;
|
ncore::ContextUPtr m_cctx;
|
||||||
WorldStatic const&m_worldStatic;
|
WorldStatic const&m_worldStatic;
|
||||||
|
int m_columns{}, m_rows{};
|
||||||
World m_world;
|
World m_world;
|
||||||
glutils::FrameBuffer m_frameBuffer;
|
glutils::FrameBuffer m_frameBuffer;
|
||||||
int m_scale = 1;
|
int m_scale = 1;
|
||||||
ox::Size m_scaleSz = ncore::gl::drawSize(m_scale);
|
ox::Size m_scaleSz = ncore::gl::drawSize(m_scale);
|
||||||
|
ox::Optional<studio::Selection> m_selection;
|
||||||
|
MapTileHighlighter m_highlighter;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WorldEditorView(turbine::Context &ctx, WorldStatic const&worldStatic);
|
WorldEditorView(turbine::Context &ctx, WorldStatic const&worldStatic);
|
||||||
@ -31,6 +39,8 @@ class WorldEditorView {
|
|||||||
|
|
||||||
void draw(ox::Size const&targetSz) noexcept;
|
void draw(ox::Size const&targetSz) noexcept;
|
||||||
|
|
||||||
|
void setSelection(studio::Selection const&sel) noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
glutils::FrameBuffer const&framebuffer() const noexcept;
|
glutils::FrameBuffer const&framebuffer() const noexcept;
|
||||||
|
|
||||||
@ -40,6 +50,14 @@ class WorldEditorView {
|
|||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
ox::Size drawSize() const noexcept;
|
ox::Size drawSize() const noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setPixelBufferObject(
|
||||||
|
unsigned vertexRow,
|
||||||
|
float x, float y,
|
||||||
|
bool const selected,
|
||||||
|
float *vbo,
|
||||||
|
GLuint *ebo) const noexcept;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user