FFmpeg and sound support; refactor, etc.
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -14,3 +14,6 @@
|
|||||||
[submodule "external/lunasvg"]
|
[submodule "external/lunasvg"]
|
||||||
path = external/lunasvg
|
path = external/lunasvg
|
||||||
url = https://github.com/sammycage/lunasvg
|
url = https://github.com/sammycage/lunasvg
|
||||||
|
[submodule "external/SDL_mixer"]
|
||||||
|
path = external/SDL_mixer
|
||||||
|
url = https://github.com/libsdl-org/SDL_mixer
|
||||||
|
|||||||
1
external/SDL_mixer
vendored
Submodule
1
external/SDL_mixer
vendored
Submodule
Submodule external/SDL_mixer added at 8c516fcd2e
22
src/anm2.cpp
22
src/anm2.cpp
@@ -1356,12 +1356,28 @@ namespace anm2ed::anm2
|
|||||||
return unused;
|
return unused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> Anm2::event_names_get()
|
||||||
|
{
|
||||||
|
std::vector<std::string> names{};
|
||||||
|
for (auto& event : content.events | std::views::values)
|
||||||
|
names.push_back(event.name);
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> Anm2::animation_names_get()
|
||||||
|
{
|
||||||
|
std::vector<std::string> names{};
|
||||||
|
for (auto& animation : animations.items)
|
||||||
|
names.push_back(animation.name);
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> Anm2::spritesheet_names_get()
|
std::vector<std::string> Anm2::spritesheet_names_get()
|
||||||
{
|
{
|
||||||
std::vector<std::string> spritesheets{};
|
std::vector<std::string> names{};
|
||||||
for (auto& [id, spritesheet] : content.spritesheets)
|
for (auto& [id, spritesheet] : content.spritesheets)
|
||||||
spritesheets.push_back(std::format("#{} {}", id, spritesheet.path.c_str()));
|
names.push_back(std::format(SPRITESHEET_FORMAT, id, spritesheet.path.c_str()));
|
||||||
return spritesheets;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Anm2::bake(Reference reference, int interval, bool isRoundScale, bool isRoundRotation)
|
void Anm2::bake(Reference reference, int interval, bool isRoundScale, bool isRoundRotation)
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ namespace anm2ed::anm2
|
|||||||
constexpr auto NO_PATH = "(No Path)";
|
constexpr auto NO_PATH = "(No Path)";
|
||||||
constexpr auto LAYER_FORMAT = "#{} {} (Spritesheet: #{})";
|
constexpr auto LAYER_FORMAT = "#{} {} (Spritesheet: #{})";
|
||||||
constexpr auto NULL_FORMAT = "#{} {}";
|
constexpr auto NULL_FORMAT = "#{} {}";
|
||||||
constexpr auto SPRITESHEET_FORMAT = "#%d %s";
|
constexpr auto SPRITESHEET_FORMAT_C = "#%d %s";
|
||||||
|
constexpr auto SPRITESHEET_FORMAT = "#{} {}";
|
||||||
|
|
||||||
enum Type
|
enum Type
|
||||||
{
|
{
|
||||||
@@ -254,7 +255,9 @@ namespace anm2ed::anm2
|
|||||||
std::set<int> events_unused(Reference = REFERENCE_DEFAULT);
|
std::set<int> events_unused(Reference = REFERENCE_DEFAULT);
|
||||||
std::set<int> layers_unused(Reference = REFERENCE_DEFAULT);
|
std::set<int> layers_unused(Reference = REFERENCE_DEFAULT);
|
||||||
std::set<int> nulls_unused(Reference = REFERENCE_DEFAULT);
|
std::set<int> nulls_unused(Reference = REFERENCE_DEFAULT);
|
||||||
|
std::vector<std::string> animation_names_get();
|
||||||
std::vector<std::string> spritesheet_names_get();
|
std::vector<std::string> spritesheet_names_get();
|
||||||
|
std::vector<std::string> event_names_get();
|
||||||
void bake(Reference, int = 1, bool = true, bool = true);
|
void bake(Reference, int = 1, bool = true, bool = true);
|
||||||
void generate_from_grid(Reference, glm::ivec2, glm::ivec2, glm::ivec2, int, int, int);
|
void generate_from_grid(Reference, glm::ivec2, glm::ivec2, glm::ivec2, int, int, int);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
|
|
||||||
#include "math.h"
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
#include <glm/ext/matrix_clip_space.hpp>
|
#include <glm/ext/matrix_clip_space.hpp>
|
||||||
#include <glm/ext/matrix_transform.hpp>
|
#include <glm/ext/matrix_transform.hpp>
|
||||||
#include <glm/gtc/matrix_inverse.hpp>
|
#include <glm/gtc/matrix_inverse.hpp>
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
|
#include "math.h"
|
||||||
|
#include "texture.h"
|
||||||
|
|
||||||
using namespace glm;
|
using namespace glm;
|
||||||
using namespace anm2ed::shader;
|
using namespace anm2ed::shader;
|
||||||
|
|
||||||
@@ -283,6 +287,21 @@ namespace anm2ed::canvas
|
|||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> Canvas::pixels_get()
|
||||||
|
{
|
||||||
|
auto count = size.x * size.y * texture::CHANNELS;
|
||||||
|
std::vector<unsigned char> pixels(count);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||||
|
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||||
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||||
|
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||||
|
glReadPixels(0, 0, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
return pixels;
|
||||||
|
}
|
||||||
|
|
||||||
void Canvas::zoom_set(float& zoom, vec2& pan, vec2 focus, float step)
|
void Canvas::zoom_set(float& zoom, vec2& pan, vec2 focus, float step)
|
||||||
{
|
{
|
||||||
auto zoomFactor = math::percent_to_unit(zoom);
|
auto zoomFactor = math::percent_to_unit(zoom);
|
||||||
@@ -295,6 +314,19 @@ namespace anm2ed::canvas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec4 Canvas::pixel_read(vec2 position, vec2 framebufferSize)
|
||||||
|
{
|
||||||
|
uint8_t rgba[4]{};
|
||||||
|
|
||||||
|
glBindTexture(GL_READ_FRAMEBUFFER, fbo);
|
||||||
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||||
|
glReadPixels(position.x, framebufferSize.y - 1 - position.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, rgba);
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
return vec4(math::uint8_to_float(rgba[0]), math::uint8_to_float(rgba[1]), math::uint8_to_float(rgba[2]),
|
||||||
|
math::uint8_to_float(rgba[3]));
|
||||||
|
}
|
||||||
|
|
||||||
vec2 Canvas::position_translate(float& zoom, vec2& pan, vec2 position)
|
vec2 Canvas::position_translate(float& zoom, vec2& pan, vec2 position)
|
||||||
{
|
{
|
||||||
auto zoomFactor = math::percent_to_unit(zoom);
|
auto zoomFactor = math::percent_to_unit(zoom);
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ namespace anm2ed::canvas
|
|||||||
constexpr auto DASH_GAP = 1.0f;
|
constexpr auto DASH_GAP = 1.0f;
|
||||||
constexpr auto DASH_OFFSET = 1.0f;
|
constexpr auto DASH_OFFSET = 1.0f;
|
||||||
|
|
||||||
|
constexpr auto STEP = 1.0f;
|
||||||
|
constexpr auto STEP_FAST = 5.0f;
|
||||||
|
|
||||||
class Canvas
|
class Canvas
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -43,6 +46,7 @@ namespace anm2ed::canvas
|
|||||||
void framebuffer_set();
|
void framebuffer_set();
|
||||||
void framebuffer_resize_check();
|
void framebuffer_resize_check();
|
||||||
void size_set(glm::vec2);
|
void size_set(glm::vec2);
|
||||||
|
glm::vec4 pixel_read(glm::vec2, glm::vec2);
|
||||||
glm::mat4 transform_get(float = 100.0f, glm::vec2 = {});
|
glm::mat4 transform_get(float = 100.0f, glm::vec2 = {});
|
||||||
void axes_render(shader::Shader&, float, glm::vec2, glm::vec4 = glm::vec4(1.0f));
|
void axes_render(shader::Shader&, float, glm::vec2, glm::vec4 = glm::vec4(1.0f));
|
||||||
void grid_render(shader::Shader&, float, glm::vec2, glm::ivec2 = glm::ivec2(32, 32), glm::ivec2 = {},
|
void grid_render(shader::Shader&, float, glm::vec2, glm::ivec2 = glm::ivec2(32, 32), glm::ivec2 = {},
|
||||||
@@ -58,5 +62,6 @@ namespace anm2ed::canvas
|
|||||||
void zoom_set(float&, glm::vec2&, glm::vec2, float);
|
void zoom_set(float&, glm::vec2&, glm::vec2, float);
|
||||||
glm::vec2 position_translate(float&, glm::vec2&, glm::vec2);
|
glm::vec2 position_translate(float&, glm::vec2&, glm::vec2);
|
||||||
void set_to_rect(float& zoom, glm::vec2& pan, glm::vec4 rect);
|
void set_to_rect(float& zoom, glm::vec2& pan, glm::vec4 rect);
|
||||||
|
std::vector<unsigned char> pixels_get();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,15 @@
|
|||||||
|
|
||||||
namespace anm2ed::snapshots
|
namespace anm2ed::snapshots
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void textures_ensure(anm2::Anm2& anm2)
|
||||||
|
{
|
||||||
|
for (auto& [id, spritesheet] : anm2.content.spritesheets)
|
||||||
|
spritesheet.texture.ensure_pixels();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool SnapshotStack::is_empty()
|
bool SnapshotStack::is_empty()
|
||||||
{
|
{
|
||||||
return top == 0;
|
return top == 0;
|
||||||
@@ -31,6 +40,7 @@ namespace anm2ed::snapshots
|
|||||||
|
|
||||||
void Snapshots::push(const anm2::Anm2& anm2, anm2::Reference reference, const std::string& message)
|
void Snapshots::push(const anm2::Anm2& anm2, anm2::Reference reference, const std::string& message)
|
||||||
{
|
{
|
||||||
|
textures_ensure(const_cast<anm2::Anm2&>(anm2));
|
||||||
Snapshot snapshot = {anm2, reference, message};
|
Snapshot snapshot = {anm2, reference, message};
|
||||||
undoStack.push(snapshot);
|
undoStack.push(snapshot);
|
||||||
redoStack.clear();
|
redoStack.clear();
|
||||||
@@ -40,8 +50,10 @@ namespace anm2ed::snapshots
|
|||||||
{
|
{
|
||||||
if (auto current = undoStack.pop())
|
if (auto current = undoStack.pop())
|
||||||
{
|
{
|
||||||
|
textures_ensure(anm2);
|
||||||
Snapshot snapshot = {anm2, reference, message};
|
Snapshot snapshot = {anm2, reference, message};
|
||||||
redoStack.push(snapshot);
|
redoStack.push(snapshot);
|
||||||
|
textures_ensure(current->anm2);
|
||||||
anm2 = current->anm2;
|
anm2 = current->anm2;
|
||||||
reference = current->reference;
|
reference = current->reference;
|
||||||
message = current->message;
|
message = current->message;
|
||||||
@@ -52,8 +64,10 @@ namespace anm2ed::snapshots
|
|||||||
{
|
{
|
||||||
if (auto current = redoStack.pop())
|
if (auto current = redoStack.pop())
|
||||||
{
|
{
|
||||||
|
textures_ensure(anm2);
|
||||||
Snapshot snapshot = {anm2, reference, message};
|
Snapshot snapshot = {anm2, reference, message};
|
||||||
undoStack.push(snapshot);
|
undoStack.push(snapshot);
|
||||||
|
textures_ensure(current->anm2);
|
||||||
anm2 = current->anm2;
|
anm2 = current->anm2;
|
||||||
reference = current->reference;
|
reference = current->reference;
|
||||||
message = current->message;
|
message = current->message;
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ using namespace glm;
|
|||||||
|
|
||||||
namespace anm2ed::spritesheet_editor
|
namespace anm2ed::spritesheet_editor
|
||||||
{
|
{
|
||||||
|
constexpr auto PIVOT_COLOR = color::PINK;
|
||||||
|
|
||||||
SpritesheetEditor::SpritesheetEditor() : Canvas(vec2())
|
SpritesheetEditor::SpritesheetEditor() : Canvas(vec2())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -115,7 +117,7 @@ namespace anm2ed::spritesheet_editor
|
|||||||
|
|
||||||
auto pivotTransform =
|
auto pivotTransform =
|
||||||
transform * math::quad_model_get(canvas::PIVOT_SIZE, frame->crop + frame->pivot, PIVOT_SIZE * 0.5f);
|
transform * math::quad_model_get(canvas::PIVOT_SIZE, frame->crop + frame->pivot, PIVOT_SIZE * 0.5f);
|
||||||
texture_render(shaderTexture, resources.icons[icon::PIVOT].id, pivotTransform, color::RED);
|
texture_render(shaderTexture, resources.icons[icon::PIVOT].id, pivotTransform, PIVOT_COLOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +147,7 @@ namespace anm2ed::spritesheet_editor
|
|||||||
auto isUp = imgui::chord_repeating(ImGuiKey_UpArrow);
|
auto isUp = imgui::chord_repeating(ImGuiKey_UpArrow);
|
||||||
auto isDown = imgui::chord_repeating(ImGuiKey_DownArrow);
|
auto isDown = imgui::chord_repeating(ImGuiKey_DownArrow);
|
||||||
auto isMod = ImGui::IsKeyDown(ImGuiMod_Shift);
|
auto isMod = ImGui::IsKeyDown(ImGuiMod_Shift);
|
||||||
auto step = isMod ? step::FAST : step::NORMAL;
|
auto step = isMod ? canvas::STEP_FAST : canvas::STEP;
|
||||||
auto useTool = tool;
|
auto useTool = tool;
|
||||||
auto isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
auto isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
||||||
auto isMouseReleased = ImGui::IsMouseReleased(ImGuiMouseButton_Left);
|
auto isMouseReleased = ImGui::IsMouseReleased(ImGuiMouseButton_Left);
|
||||||
@@ -195,12 +197,26 @@ namespace anm2ed::spritesheet_editor
|
|||||||
case tool::ERASE:
|
case tool::ERASE:
|
||||||
{
|
{
|
||||||
if (!spritesheet) break;
|
if (!spritesheet) break;
|
||||||
if (isMouseClicked) document.snapshot(tool == tool::DRAW ? "Draw" : "Erase");
|
|
||||||
auto color = tool == tool::DRAW ? toolColor : vec4();
|
auto color = tool == tool::DRAW ? toolColor : vec4();
|
||||||
|
if (isMouseClicked) document.snapshot(tool == tool::DRAW ? "Draw" : "Erase");
|
||||||
if (isMouseDown) spritesheet->texture.pixel_line(ivec2(previousMousePos), ivec2(mousePos), color);
|
if (isMouseDown) spritesheet->texture.pixel_line(ivec2(previousMousePos), ivec2(mousePos), color);
|
||||||
if (isMouseReleased) document.change(change::FRAMES);
|
if (isMouseReleased) document.change(change::FRAMES);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case tool::COLOR_PICKER:
|
||||||
|
{
|
||||||
|
if (isMouseDown)
|
||||||
|
{
|
||||||
|
auto position = to_vec2(ImGui::GetMousePos());
|
||||||
|
toolColor = pixel_read(position, {settings.windowSize.x, settings.windowSize.y});
|
||||||
|
if (ImGui::BeginTooltip())
|
||||||
|
{
|
||||||
|
ImGui::ColorButton("##Color Picker Button", to_imvec4(toolColor));
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,20 @@ namespace anm2ed::texture
|
|||||||
return id != 0;
|
return id != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> Texture::pixels_get()
|
||||||
|
{
|
||||||
|
ensure_pixels();
|
||||||
|
return pixels;
|
||||||
|
}
|
||||||
|
|
||||||
void Texture::download()
|
void Texture::download()
|
||||||
{
|
{
|
||||||
pixels.resize(size.x * size.y * CHANNELS);
|
if (size.x <= 0 || size.y <= 0 || !is_valid()) return;
|
||||||
|
pixels.resize(static_cast<size_t>(size.x) * static_cast<size_t>(size.y) * CHANNELS);
|
||||||
glBindTexture(GL_TEXTURE_2D, id);
|
glBindTexture(GL_TEXTURE_2D, id);
|
||||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
|
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
isPixelsDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::upload(const uint8_t* data)
|
void Texture::upload(const uint8_t* data)
|
||||||
@@ -66,6 +74,7 @@ namespace anm2ed::texture
|
|||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
isPixelsDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture::Texture() = default;
|
Texture::Texture() = default;
|
||||||
@@ -91,6 +100,7 @@ namespace anm2ed::texture
|
|||||||
{
|
{
|
||||||
if (is_valid()) glDeleteTextures(1, &id);
|
if (is_valid()) glDeleteTextures(1, &id);
|
||||||
id = 0;
|
id = 0;
|
||||||
|
other.ensure_pixels();
|
||||||
size = other.size;
|
size = other.size;
|
||||||
filter = other.filter;
|
filter = other.filter;
|
||||||
channels = other.channels;
|
channels = other.channels;
|
||||||
@@ -110,6 +120,9 @@ namespace anm2ed::texture
|
|||||||
filter = other.filter;
|
filter = other.filter;
|
||||||
channels = other.channels;
|
channels = other.channels;
|
||||||
pixels = std::move(other.pixels);
|
pixels = std::move(other.pixels);
|
||||||
|
isPixelsDirty = other.isPixelsDirty;
|
||||||
|
other.isPixelsDirty = true;
|
||||||
|
if (!pixels.empty()) upload();
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -129,9 +142,15 @@ namespace anm2ed::texture
|
|||||||
upload(bitmap.data());
|
upload(bitmap.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Texture::Texture(const uint8_t* data, ivec2 size)
|
||||||
|
{
|
||||||
|
this->size = size;
|
||||||
|
upload(data);
|
||||||
|
}
|
||||||
|
|
||||||
Texture::Texture(const std::string& pngPath)
|
Texture::Texture(const std::string& pngPath)
|
||||||
{
|
{
|
||||||
if (const uint8* data = stbi_load(pngPath.c_str(), &size.x, &size.y, nullptr, CHANNELS); data)
|
if (const uint8_t* data = stbi_load(pngPath.c_str(), &size.x, &size.y, nullptr, CHANNELS); data)
|
||||||
{
|
{
|
||||||
upload(data);
|
upload(data);
|
||||||
stbi_image_free((void*)data);
|
stbi_image_free((void*)data);
|
||||||
@@ -140,6 +159,7 @@ namespace anm2ed::texture
|
|||||||
|
|
||||||
bool Texture::write_png(const std::string& path)
|
bool Texture::write_png(const std::string& path)
|
||||||
{
|
{
|
||||||
|
ensure_pixels();
|
||||||
return stbi_write_png(path.c_str(), size.x, size.y, CHANNELS, this->pixels.data(), size.x * CHANNELS);
|
return stbi_write_png(path.c_str(), size.x, size.y, CHANNELS, this->pixels.data(), size.x * CHANNELS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,6 +167,7 @@ namespace anm2ed::texture
|
|||||||
{
|
{
|
||||||
if (position.x < 0 || position.y < 0 || position.x >= size.x || position.y >= size.y) return;
|
if (position.x < 0 || position.y < 0 || position.x >= size.x || position.y >= size.y) return;
|
||||||
|
|
||||||
|
ensure_pixels();
|
||||||
uint8 rgba8[4] = {(uint8)float_to_uint8(color.r), (uint8)float_to_uint8(color.g), (uint8)float_to_uint8(color.b),
|
uint8 rgba8[4] = {(uint8)float_to_uint8(color.r), (uint8)float_to_uint8(color.g), (uint8)float_to_uint8(color.b),
|
||||||
(uint8)float_to_uint8(color.a)};
|
(uint8)float_to_uint8(color.a)};
|
||||||
|
|
||||||
@@ -154,14 +175,23 @@ namespace anm2ed::texture
|
|||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, position.x, position.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, rgba8);
|
glTexSubImage2D(GL_TEXTURE_2D, 0, position.x, position.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, rgba8);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
if (!pixels.empty())
|
||||||
|
{
|
||||||
|
auto index = (position.y * size.x + position.x) * CHANNELS;
|
||||||
|
pixels[index + 0] = rgba8[0];
|
||||||
|
pixels[index + 1] = rgba8[1];
|
||||||
|
pixels[index + 2] = rgba8[2];
|
||||||
|
pixels[index + 3] = rgba8[3];
|
||||||
|
isPixelsDirty = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
isPixelsDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::pixel_line(ivec2 start, ivec2 end, vec4 color)
|
void Texture::pixel_line(ivec2 start, ivec2 end, vec4 color)
|
||||||
{
|
{
|
||||||
auto plot = [&](ivec2 pos)
|
ensure_pixels();
|
||||||
{
|
auto plot = [&](ivec2 pos) { pixel_set(pos, color); };
|
||||||
pixel_set(pos, color);
|
|
||||||
};
|
|
||||||
|
|
||||||
int x0 = start.x;
|
int x0 = start.x;
|
||||||
int y0 = start.y;
|
int y0 = start.y;
|
||||||
@@ -192,6 +222,13 @@ namespace anm2ed::texture
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Texture::ensure_pixels() const
|
||||||
|
{
|
||||||
|
if (size.x <= 0 || size.y <= 0) return;
|
||||||
|
if (!pixels.empty() && !isPixelsDirty) return;
|
||||||
|
const_cast<Texture*>(this)->download();
|
||||||
|
}
|
||||||
|
|
||||||
void Texture::bind(GLuint unit)
|
void Texture::bind(GLuint unit)
|
||||||
{
|
{
|
||||||
glActiveTexture(GL_TEXTURE0 + unit);
|
glActiveTexture(GL_TEXTURE0 + unit);
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ namespace anm2ed::texture
|
|||||||
glm::ivec2 size{};
|
glm::ivec2 size{};
|
||||||
GLint filter = GL_NEAREST;
|
GLint filter = GL_NEAREST;
|
||||||
int channels{};
|
int channels{};
|
||||||
std::vector<uint8_t> pixels{};
|
mutable std::vector<uint8_t> pixels{};
|
||||||
|
mutable bool isPixelsDirty{true};
|
||||||
|
|
||||||
bool is_valid();
|
bool is_valid();
|
||||||
void download();
|
void download();
|
||||||
@@ -30,10 +31,13 @@ namespace anm2ed::texture
|
|||||||
Texture(Texture&&);
|
Texture(Texture&&);
|
||||||
Texture& operator=(const Texture&);
|
Texture& operator=(const Texture&);
|
||||||
Texture& operator=(Texture&&);
|
Texture& operator=(Texture&&);
|
||||||
|
Texture(const uint8_t*, glm::ivec2);
|
||||||
Texture(const char*, size_t, glm::ivec2);
|
Texture(const char*, size_t, glm::ivec2);
|
||||||
Texture(const std::string&);
|
Texture(const std::string&);
|
||||||
bool write_png(const std::string&);
|
bool write_png(const std::string&);
|
||||||
void pixel_set(glm::ivec2, glm::vec4);
|
void pixel_set(glm::ivec2, glm::vec4);
|
||||||
|
void ensure_pixels() const;
|
||||||
|
std::vector<uint8_t> pixels_get();
|
||||||
void pixel_line(glm::ivec2, glm::ivec2, glm::vec4);
|
void pixel_line(glm::ivec2, glm::ivec2, glm::vec4);
|
||||||
void bind(GLuint = 0);
|
void bind(GLuint = 0);
|
||||||
void unbind(GLuint = 0);
|
void unbind(GLuint = 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user