#include <slim_blockchain_handler.hpp> #include <toolbox.hpp> #include <message.hpp> #include <enums.hpp> #include <algorithm> void SlimBlockchainHandler::update(Toolbox& tb) { bool skip_events = false; //can't be dragging if mstate changes if (previous_mstate_ != tb.m_state) { clear_blocks_(); skip_events = true; } //update tracked mstate previous_mstate_ = tb.m_state; /*Handle Messages*/ for (Message& m : tb.mailbox) { if (m.target == MESSAGETARGET::BLOCKCHAIN && !skip_events) { BLOCKCHAINMESSAGE message = std::get<BLOCKCHAINMESSAGE>(m.message); switch (message) { case BLOCKCHAINMESSAGE::CLEAR: clear_blocks_(); m.handled = true; skip_events = true; break; default: break; } } } /* //This is touchscreen-only //Uncomment on a touchscreen //Catch dangling Blocks, if no touches, then no dragging if (!skip_events && dragpairs_.size() > 0 && tb.current_touchIDs.size() == 0 && tb.events.size() == 0) { clear_blocks_(); skip_events = true; #ifndef NDEBUG std::cout << "Emergency Block-Release" << std::endl; #endif } */ if (tb.m_state == static_cast<int>(MSTATE::IMMEDIATE) && !skip_events) { /*Handle Events*/ for (Pevent& pev : tb.events) { /*Handle event differently depending on where it is*/ /*Event within wave window*/ if (in_wave_window_(tb, pev)) { switch (pev.type) { /*Finger down, place Block, add to dragpairs*/ case PEVENTTYPE::DOWN: blockchain_.push_back(EfficientBlock(tb.block_width, tb.block_height, pev.itcoord_x - tb.block_width / 2, tb.texture_h - (pev.itcoord_y + tb.block_height / 2))); dragpairs_.push_back({ &blockchain_.back(), pev.finger_id }); drawlist_dynamic_.push_back(blockchain_.back().xywh()); break; /*Finger up, find and remove the associated Block and dragpair*/ case PEVENTTYPE::UP: { auto released = std::find_if(dragpairs_.begin(), dragpairs_.end(), [=](auto dragpair) { return dragpair.second == pev.finger_id; }); if (released != dragpairs_.end()) { released->first->request_removal(); eraselist_dynamic_.push_back(released->first->xywh()); } dragpairs_.remove_if([=](auto dragpair) { return dragpair.second == pev.finger_id; }); break; } /*Finger moved, find and translate associated Block*/ case PEVENTTYPE::MOVE: { auto moved = std::find_if(dragpairs_.begin(), dragpairs_.end(), [=](auto dragpair) { return dragpair.second == pev.finger_id; }); if (moved != dragpairs_.end()) { eraselist_dynamic_.push_back(moved->first->xywh()); moved->first->translate(pev.itcoord_x - moved->first->width() / 2, tb.texture_h - (pev.itcoord_y + moved->first->height() / 2)); drawlist_dynamic_.push_back(moved->first->xywh()); } break; } } } /*Event outside wave window*/ else { switch (pev.type) { /*Finger down outside wave area, don't care*/ case PEVENTTYPE::DOWN: break; /*Finger up or moved outside wave area, if associated Block exists, clear it*/ case PEVENTTYPE::UP: case PEVENTTYPE::MOVE: { auto released = std::find_if(dragpairs_.begin(), dragpairs_.end(), [=](auto dragpair) { return dragpair.second == pev.finger_id; }); if (released != dragpairs_.end()) { released->first->request_removal(); eraselist_dynamic_.push_back(released->first->xywh()); } dragpairs_.remove_if([=](auto dragpair) { return dragpair.second == pev.finger_id; }); break; } } } } } /*Update Blocks*/ /*TODO: Can reupload blocks depending on need, if necessary*/ update_blocks_(tb, false); /*Toolbox developer region*/ tb.num_blocks = num_blocks(); } size_t SlimBlockchainHandler::num_blocks() { return blockchain_.size(); } void SlimBlockchainHandler::clear_blocks_() { for (EfficientBlock& b : blockchain_) { eraselist_dynamic_.push_back(b.xywh()); b.request_removal(); } dragpairs_.clear(); blockchain_.clear(); } void SlimBlockchainHandler::update_blocks_(const Toolbox& tb, bool reload_all) { find_dynamic_duplicates_(); if (drawlist_dynamic_.size() != 0 || drawlist_static_.size() != 0 || eraselist_dynamic_.size() != 0) { glBindVertexArray(vao_); glBindBuffer(GL_ARRAY_BUFFER, vbo_); glViewport(0, 0, tb.texture_w, tb.texture_h); glActiveTexture(GL_TEXTURE0); shader_drawblocks_.use(); } std::vector<float> vertices; /*Erase dynamic blocks*/ if (eraselist_dynamic_.size() != 0) { glBindFramebuffer(GL_FRAMEBUFFER, fbo_dynamic_); glBindTexture(GL_TEXTURE_2D, tb.tex_damp_clean); xywhs_to_vertices_(tb, eraselist_dynamic_duplicates_, eraselist_dynamic_, vertices); if (vertices.size() != 0) { glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_DYNAMIC_DRAW); /*Draw on damping*/ shader_drawblocks_.setFloat("color_multiplier", 1.f); for (size_t i = 0; i < eraselist_dynamic_.size(); ++i) { glDrawArrays(GL_TRIANGLES, 6 * i, 6); } } eraselist_dynamic_.clear(); } /*Draw static blocks*/ if (drawlist_static_.size() != 0) { std::cout << "DRAWING STATIC" << std::endl; glBindFramebuffer(GL_FRAMEBUFFER, fbo_static_); glBindTexture(GL_TEXTURE_2D, tb.tex_wave_clean); xywhs_to_vertices_(tb, std::vector<bool>(drawlist_static_.size(), false), drawlist_static_, vertices); if (vertices.size() != 0) { glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_DYNAMIC_DRAW); /*Draw on damping*/ shader_drawblocks_.setFloat("color_multiplier", 0.f); for (size_t i = 0; i < drawlist_static_.size(); ++i) { glDrawArrays(GL_TRIANGLES, 6 * i, 6); } /*Draw on wave*/ shader_drawblocks_.setFloat("color_multiplier", 1.f); glBindFramebuffer(GL_FRAMEBUFFER, fbo_wave_); for (size_t i = 0; i < drawlist_static_.size(); ++i) { glDrawArrays(GL_TRIANGLES, 6 * i, 6); } } drawlist_static_.clear(); } /*Draw dynamic blocks*/ if (drawlist_dynamic_.size() != 0) { glBindFramebuffer(GL_FRAMEBUFFER, fbo_dynamic_); glBindTexture(GL_TEXTURE_2D, tb.tex_wave_clean); xywhs_to_vertices_(tb, drawlist_dynamic_duplicates_, drawlist_dynamic_, vertices); if (vertices.size() != 0) { glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_DYNAMIC_DRAW); /*Draw on damping*/ shader_drawblocks_.setFloat("color_multiplier", 0.f); for (size_t i = 0; i < drawlist_dynamic_.size(); ++i) { glDrawArrays(GL_TRIANGLES, 6 * i, 6); } /*Draw on wave*/ shader_drawblocks_.setFloat("color_multiplier", 1.f); glBindFramebuffer(GL_FRAMEBUFFER, fbo_wave_); for (size_t i = 0; i < drawlist_dynamic_.size(); ++i) { glDrawArrays(GL_TRIANGLES, 6 * i, 6); } } drawlist_dynamic_.clear(); } /*Drawing finished, reset OpenGL state*/ glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); /*Clean up the blockchain*/ /* This doesn't work, because it invalidates all iterators/pointers, even in a list. See https://godbolt.org/z/GzxTExc44 blockchain_.erase(std::remove_if(blockchain_.begin(), blockchain_.end(), [](const Block& b) {return b.needs_removal(); }), blockchain_.end()); The following preserves iterator/pointer validity, which is what we need */ dragpairs_.remove_if([=](const std::pair<EfficientBlock*, SDL_FingerID>& dp) { return dp.first->needs_removal(); }); blockchain_.remove_if([=](const EfficientBlock& b) { return b.needs_removal(); }); } bool SlimBlockchainHandler::in_wave_window_(const Toolbox& tb, const Pevent& pev) const { return pev.fscoord_x < 1.f - tb.gui_pos; } void SlimBlockchainHandler::xywhs_to_vertices_(const Toolbox& tb, const std::vector<bool>& duplicates, const std::vector<glm::ivec4>& xywhs, std::vector<float>& vertices) const { float texwidth = tb.texture_w; float texheight = tb.texture_h; vertices.clear(); vertices.reserve(24 * xywhs.size()); for (size_t i = 0; i < xywhs.size(); ++i) { const glm::ivec4& xywh = xywhs[i]; if (duplicates[i]) { continue; } float x = xywh.r >= 0 ? xywh.r : 0; float y = xywh.g; float w = xywh.r >= 0 ? xywh.b : xywh.b + xywh.r; float h = xywh.a; /*Lower Left Corner*/ vertices.push_back(2. * (x) / texwidth - 1.f); //x vertices.push_back(2. * (y) / texheight - 1.f); //y vertices.push_back((x) / texwidth); //u vertices.push_back((y) / texheight); //v /*Top Right Corner*/ vertices.push_back(2. * (x + w) / texwidth - 1.f); //x vertices.push_back(2. * (y + h) / texheight - 1.f); //y vertices.push_back((x + w) / texwidth); //u vertices.push_back((y + h) / texheight); //v /*Bottom Right Corner*/ vertices.push_back(2. * (x + w) / texwidth - 1.f); //x vertices.push_back(2. * (y) / texheight - 1.f); //y vertices.push_back((x + w) / texwidth); //u vertices.push_back((y) / texheight); //v /*Lower Left Corner*/ vertices.push_back(2. * (x) / texwidth - 1.f); //x vertices.push_back(2. * (y) / texheight - 1.f); //y vertices.push_back((x) / texwidth); //u vertices.push_back((y) / texheight); //v /*Top Right Corner*/ vertices.push_back(2. * (x + w) / texwidth - 1.f); //x vertices.push_back(2. * (y + h) / texheight - 1.f); //y vertices.push_back((x + w) / texwidth); //u vertices.push_back((y + h) / texheight); //v /*Top Left Corner*/ vertices.push_back(2. * (x) / texwidth - 1.f); //x vertices.push_back(2. * (y + h) / texheight - 1.f); //y vertices.push_back((x) / texwidth); //u vertices.push_back((y + h) / texheight); //v } } void SlimBlockchainHandler::find_dynamic_duplicates_() { drawlist_dynamic_duplicates_ = std::vector<bool>(drawlist_dynamic_.size(), false); eraselist_dynamic_duplicates_ = std::vector<bool>(eraselist_dynamic_.size(), false); if (drawlist_dynamic_.size() == 0 || eraselist_dynamic_.size() == 0) { return; } for (size_t i_draw = 0; i_draw < drawlist_dynamic_.size(); ++i_draw) { const glm::ivec4 dr = drawlist_dynamic_[i_draw]; for (size_t i_erase = 0; i_erase < eraselist_dynamic_.size(); ++i_erase) { if (eraselist_dynamic_duplicates_[i_erase] == true) { continue; } const glm::ivec4 er = eraselist_dynamic_[i_erase]; if (dr.r == er.r && dr.g == er.g && dr.b == er.b && er.a == dr.a) { drawlist_dynamic_duplicates_[i_draw] = true; eraselist_dynamic_duplicates_[i_erase] = true; break; } } } }