blocky/playground/player.cc
2022-06-22 11:48:52 +09:00

162 lines
4.0 KiB
C++

#include <algorithm>
#include <cassert>
#include <string>
#include <vector>
#include <GL/glew.h>
#include <imgui.h>
#include "app.hh"
#include "input.hh"
namespace pg {
namespace {
class Player final : public App {
public:
static inline TypeInfo kType = TypeInfo::Create<Player>("Player");
static constexpr size_t kTexW = 1024;
static constexpr size_t kTexH = 1024;
Player() noexcept {
glGenTextures(1, &tex_);
glBindTexture(GL_TEXTURE_2D, tex_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
}
~Player() noexcept {
glDeleteTextures(1, &tex_);
}
void Update() noexcept override {
const auto em = ImGui::GetFontSize();
const auto id = "Player | "+
std::to_string(reinterpret_cast<uintptr_t>(this));
ImGui::SetNextWindowSize({32*em, 16*em}, ImGuiCond_Once);
if (ImGui::Begin(id.c_str())) {
bool mod = false;
ImGui::BeginGroup();
{
ImGui::PushItemWidth(4*em);
mod |= ImGui::DragInt("input_slot", &slot_, 1, 0, 1024);
mod |= ImGui::DragInt("time", &t_, 1, 0, 1024);
ImGui::Checkbox("auto increment", &auto_inc_);
if (auto_inc_) {
++t_; mod = true;
}
ImGui::PopItemWidth();
}
ImGui::EndGroup();
ImGui::SameLine();
ImVec2 img_min = {0, 0};
ImVec2 img_max = {0, 0};
if (mod) UpdateTex();
const auto wf = static_cast<float>(w_);
const auto hf = static_cast<float>(h_);
ImGui::BeginGroup();
{
if (w_ && h_) {
const auto tex = (void*) (uintptr_t) tex_;
const auto z = wf / ImGui::GetContentRegionAvail().x;
ImGui::Image(tex, {wf/z, hf/z}, {0, 0}, uv_);
img_min = ImGui::GetItemRectMin();
img_max = ImGui::GetItemRectMax();
}
}
ImGui::EndGroup();
if (msg_.size() > 0) {
ImGui::TextUnformatted(msg_.c_str());
} else if (w_ && h_) {
const auto m = ImGui::GetMousePos();
const auto xf = (m.x-img_min.x)/(img_max.x-img_min.x);
const auto yf = (m.y-img_min.y)/(img_max.y-img_min.y);
ImGui::Text("x=%f, y=%f, xf=%f, yf=%f", xf*wf, yf*hf, xf, yf);
} else {
ImGui::TextUnformatted("no image shown");
}
}
ImGui::End();
}
private:
std::string msg_;
int slot_ = 0;
int t_ = 0;
bool auto_inc_ = false;
GLsizei w_ = 0, h_ = 0;
ImVec2 uv_;
GLuint tex_;
GLsizei texw_ = 0, texh_ = 0;
void UpdateTex() noexcept
try {
msg_ = "";
auto data = Input::instance().slots(static_cast<size_t>(slot_));
if (data == nullptr) {
throw "missing slot";
}
if (static_cast<size_t>(t_) >= data->frames()) {
throw "time out of range";
}
const auto frame = data->Fetch(static_cast<size_t>(t_));
if (!frame.rgba) {
throw "got an empty frame";
}
w_ = static_cast<GLsizei>(frame.w);
h_ = static_cast<GLsizei>(frame.h);
glBindTexture(GL_TEXTURE_2D, tex_);
const auto ptexw = texw_;
const auto ptexh = texh_;
texw_ = std::max(texw_, NextPowerOf2(w_));
texh_ = std::max(texh_, NextPowerOf2(h_));
if (texw_ != ptexw || texh_ != ptexh) {
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGBA, texw_, texh_,
0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
}
glTexSubImage2D(
GL_TEXTURE_2D, 0, 0, 0, w_, h_,
GL_RGBA, GL_UNSIGNED_BYTE, frame.rgba);
uv_ = {
static_cast<float>(w_)/static_cast<float>(texw_),
static_cast<float>(h_)/static_cast<float>(texh_)};
glBindTexture(GL_TEXTURE_2D, 0);
assert(glGetError() == 0);
} catch (const char* msg) {
msg_ = msg;
}
template <typename I>
static I NextPowerOf2(I x) {
I y = 1;
while (y < x) y *= 2;
return y;
}
};
}
} // namespace pg