#include #include #include #include #include #include #include "app.hh" #include "input.hh" namespace pg { namespace { class Player final : public App { public: static inline TypeInfo kType = TypeInfo::Create("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(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(w_); const auto hf = static_cast(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(slot_)); if (data == nullptr) { throw "missing slot"; } if (static_cast(t_) >= data->frames()) { throw "time out of range"; } const auto frame = data->Fetch(static_cast(t_)); if (!frame.rgba) { throw "got an empty frame"; } w_ = static_cast(frame.w); h_ = static_cast(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(w_)/static_cast(texw_), static_cast(h_)/static_cast(texh_)}; glBindTexture(GL_TEXTURE_2D, 0); assert(glGetError() == 0); } catch (const char* msg) { msg_ = msg; } template static I NextPowerOf2(I x) { I y = 1; while (y < x) y *= 2; return y; } }; } } // namespace pg