Skip to content
Snippets Groups Projects
slim_blockchain_handler.cpp 10.09 KiB
#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;
			}
		}
	}
}