35BoardManager::BoardManager(flecs::world ecs, std::weak_ptr<NetworkManager> network_manager, std::shared_ptr<IdentityManager> identity_manager, std::shared_ptr<DirectoryWindow> map_directory, std::shared_ptr<DirectoryWindow> marker_directory) :
36 ecs(ecs), camera(), identity_manager(identity_manager), currentTool(
Tool::
MOVE), mouse_start_screen_pos({0, 0}), mouse_start_world_pos({0, 0}), mouse_current_world_pos({0, 0}), marker_directory(marker_directory), map_directory(map_directory), network_manager(network_manager)
39 std::filesystem::path map_path = std::filesystem::path(map_directory->directoryPath);
40 std::filesystem::path base_path = map_path.parent_path();
41 std::filesystem::path marker_directory_path = base_path /
"Markers";
43 marker_directory->directoryName =
"MarkerDiretory";
44 marker_directory->directoryPath = marker_directory_path.string();
45 marker_directory->startMonitoring();
46 marker_directory->generateTextureIDs();
206 ImGuiWindowFlags toolbar_child_flags =
207 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoMove |
208 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoTitleBar |
209 ImGuiWindowFlags_AlwaysAutoResize;
211 auto panel_color = ImVec4(0.18f, 0.22f, 0.27f, 1.00f);
212 auto bg_color = ImVec4(0.12f, 0.14f, 0.17f, 1.00f);
213 auto old_bg_color = ImVec4(0.2f, 0.3f, 0.4f, 1.0f);
215 auto green_light_color = ImVec4(0.55f, 0.95f, 0.65f, 1.00f);
216 auto green_mid_color = ImVec4(0.20f, 0.75f, 0.35f, 1.00f);
217 auto green_dark_color = ImVec4(0.12f, 0.45f, 0.22f, 1.00f);
218 auto mint_color = ImVec4(0.50f, 0.95f, 0.85f, 1.00f);
220 auto blue_light_color = ImVec4(0.55f, 0.75f, 1.00f, 1.00f);
221 auto blue_mid_color = ImVec4(0.28f, 0.50f, 0.92f, 1.00f);
222 auto blue_dark_color = ImVec4(0.15f, 0.30f, 0.55f, 1.00f);
224 auto purple_color = ImVec4(0.70f, 0.45f, 0.95f, 1.00f);
225 auto violet_color = ImVec4(0.55f, 0.35f, 0.85f, 1.00f);
226 auto mid_purple_color = ImVec4(0.45f, 0.28f, 0.70f, 1.00f);
228 auto orange_color = ImVec4(0.98f, 0.60f, 0.20f, 1.00f);
229 auto amber_color = ImVec4(1.00f, 0.78f, 0.25f, 1.00f);
230 auto yellow_color = ImVec4(0.95f, 0.90f, 0.30f, 1.00f);
232 auto button_toggled_color = amber_color;
233 auto button_toggled_hover = orange_color;
234 auto button_toggled_active = yellow_color;
236 auto button_untoggled_color = blue_mid_color;
237 auto button_untoggled_hover = blue_light_color;
238 auto button_untoggled_active = yellow_color;
240 auto button_popup_color = green_dark_color;
241 auto button_popup_hover = green_mid_color;
242 auto button_popup_active = mint_color;
244 ImGui::SetCursorPos(window_position);
246 ImVec2 toolbar_size = ImVec2(0, 0);
251 throw std::exception(
"[BoardManager] NetworkManager Expired");
253 ImGuiIO& io = ImGui::GetIO();
258 static bool s_spaceHoldActive =
false;
262 auto setTool = [&](
Tool t)
274 if (ImGui::IsKeyPressed(ImGuiKey_Space))
276 if (!s_spaceHoldActive)
278 s_spaceHoldActive =
true;
286 if (s_spaceHoldActive && ImGui::IsKeyReleased(ImGuiKey_Space))
288 s_spaceHoldActive =
false;
293 if (!s_spaceHoldActive)
295 if (ImGui::IsKeyPressed(ImGuiKey_M))
297 if (isGM && ImGui::IsKeyPressed(ImGuiKey_F))
299 if (isGM && ImGui::IsKeyPressed(ImGuiKey_S))
302 if (ImGui::IsKeyPressed(ImGuiKey_C))
305 if (isGM && ImGui::IsKeyPressed(ImGuiKey_G))
308 if (ImGui::IsKeyPressed(ImGuiKey_Q))
314 ImGui::PushStyleColor(ImGuiCol_WindowBg, panel_color);
315 ImGui::BeginChild(
"ToolbarChild", toolbar_size,
false, toolbar_child_flags);
318 ImGui::PushStyleColor(ImGuiCol_Button, purple_color);
319 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, violet_color);
320 ImGui::PushStyleColor(ImGuiCol_ButtonActive, mid_purple_color);
321 if (ImGui::Button(
"Prev.(Q)", ImVec2(65, 40)))
325 ImGui::PopStyleColor(3);
329 ImGui::PushStyleColor(ImGuiCol_Button,
currentTool ==
Tool::MOVE ? button_toggled_color : button_untoggled_color);
330 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
currentTool ==
Tool::MOVE ? button_toggled_hover : button_untoggled_hover);
331 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
currentTool ==
Tool::MOVE ? button_toggled_active : button_untoggled_active);
332 if (ImGui::Button(
" Move (M)\n(Hold Space)", ImVec2(100, 40)))
336 ImGui::PopStyleColor(3);
342 ImGui::PushStyleColor(ImGuiCol_Button,
currentTool ==
Tool::FOG ? button_toggled_color : button_untoggled_color);
343 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
currentTool ==
Tool::FOG ? button_toggled_hover : button_untoggled_hover);
344 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
currentTool ==
Tool::FOG ? button_toggled_active : button_untoggled_active);
345 if (ImGui::Button(
"Fog (F)", ImVec2(100, 40)))
349 ImGui::PopStyleColor(3);
353 ImGui::PushStyleColor(ImGuiCol_Button,
currentTool ==
Tool::SELECT ? button_toggled_color : button_untoggled_color);
354 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
currentTool ==
Tool::SELECT ? button_toggled_hover : button_untoggled_hover);
355 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
currentTool ==
Tool::SELECT ? button_toggled_active : button_untoggled_active);
356 if (ImGui::Button(
"Select (S)", ImVec2(100, 40)))
360 ImGui::PopStyleColor(3);
365 ImGui::PushStyleColor(ImGuiCol_Button, button_popup_color);
366 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, button_popup_hover);
367 ImGui::PushStyleColor(ImGuiCol_ButtonActive, button_popup_active);
368 if (ImGui::Button(
"Camera Settings (C)", ImVec2(150, 40)))
372 ImGui::PopStyleColor(3);
377 ImGui::PushStyleColor(ImGuiCol_Button, button_popup_color);
378 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, button_popup_hover);
379 ImGui::PushStyleColor(ImGuiCol_ButtonActive, button_popup_active);
380 if (ImGui::Button(
"Grid Settings (G)", ImVec2(150, 40)))
384 ImGui::PopStyleColor(3);
387 ImGui::PopStyleColor();
411 throw std::exception(
"[BoardManager] Network Manager expired!!");
414 if (texture->textureID != 0)
422 glm::mat4 board_model = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f));
423 board_model = glm::scale(board_model, glm::vec3(size->width, size->height, 1.0f));
426 shader.SetUniformMat4f(
"projection", projection);
427 shader.SetUniformMat4f(
"view", viewMatrix);
428 shader.SetUniformMat4f(
"model", board_model);
429 shader.SetUniform1f(
"u_Alpha", 1.0f);
430 shader.SetUniform1i(
"u_UseTexture", 1);
431 shader.SetUniform1i(
"u_Texture", 0);
434 GLCall(glActiveTexture(GL_TEXTURE0));
435 GLCall(glBindTexture(GL_TEXTURE_2D, texture->textureID));
437 renderer.Draw(va, ib, shader);
444 grid_shader.SetUniformMat4f(
"projection", projection);
445 grid_shader.SetUniformMat4f(
"view", viewMatrix);
446 grid_shader.SetUniformMat4f(
"model", board_model);
447 grid_shader.SetUniform1i(
"grid_type", grid->is_hex ? 1 : 0);
448 grid_shader.SetUniform1f(
"cell_size", grid->cell_size);
449 grid_shader.SetUniform2f(
"grid_offset", grid->offset.x, grid->offset.y);
450 grid_shader.SetUniform1f(
"opacity", grid->opacity);
451 grid_shader.Unbind();
453 renderer.Draw(va, ib, grid_shader);
462 if (texture_marker->textureID != 0) {
465 const Size* size_marker = child.get<
Size>();
467 glm::mat4 marker_model = glm::translate(glm::mat4(1.0f), glm::vec3(position_marker->x, position_marker->y, 0.0f));
468 marker_model = glm::scale(marker_model, glm::vec3(size_marker->width, size_marker->height, 1.0f));
472 if (!visibility_marker->isVisible) {
483 shader.SetUniformMat4f(
"projection", projection);
484 shader.SetUniformMat4f(
"view", viewMatrix);
485 shader.SetUniformMat4f(
"model", marker_model);
486 shader.SetUniform1f(
"u_Alpha", alpha);
487 shader.SetUniform1i(
"u_Texture", 0);
488 shader.SetUniform1i(
"u_UseTexture", 1);
491 GLCall(glActiveTexture(GL_TEXTURE0));
492 GLCall(glBindTexture(GL_TEXTURE_2D, texture_marker->textureID));
494 renderer.Draw(va, ib, shader);
502 const Size* size_marker = child.get<
Size>();
504 glm::mat4 fog_model = glm::translate(glm::mat4(1.0f), glm::vec3(position_marker->x, position_marker->y, 0.0f));
505 fog_model = glm::scale(fog_model, glm::vec3(size_marker->width, size_marker->height, 1.0f));
509 if (!visibility_marker->isVisible) {
527 shader.SetUniformMat4f(
"projection", projection);
528 shader.SetUniformMat4f(
"view", viewMatrix);
529 shader.SetUniformMat4f(
"model", fog_model);
530 shader.SetUniform1f(
"u_Alpha", alpha);
531 shader.SetUniform1i(
"u_UseTexture", 0);
534 renderer.Draw(va, ib, shader);
589 static constexpr uint64_t TS_BITS = 42;
590 static constexpr uint64_t NODE_BITS = 10;
591 static constexpr uint64_t SEQ_BITS = 12;
593 static constexpr uint64_t TS_MASK = (1ULL << TS_BITS) - 1;
594 static constexpr uint64_t NODE_MASK = (1ULL << NODE_BITS) - 1;
595 static constexpr uint64_t SEQ_MASK = (1ULL << SEQ_BITS) - 1;
598 static constexpr int64_t EPOCH_MS = 1577836800000LL;
602 static const uint16_t NODE_ID = []() -> uint16_t
605 if (
const char* cn = std::getenv(
"COMPUTERNAME"); cn && *cn)
607 else if (
const char* hn = std::getenv(
"HOSTNAME"); hn && *hn)
611 std::random_device rd;
612 basis = std::to_string(rd());
614 uint64_t h = std::hash<std::string>{}(basis);
616 h ^= (h >> 33) ^ (h >> 17) ^ (h >> 9);
617 return static_cast<uint16_t
>(h) & NODE_MASK;
621 static std::atomic<uint64_t> lastMs{0};
622 static std::atomic<uint16_t> seq{0};
625 const auto nowEpoch = std::chrono::time_point_cast<std::chrono::milliseconds>(
626 std::chrono::system_clock::now())
629 uint64_t nowMs = (nowEpoch >= EPOCH_MS) ?
static_cast<uint64_t
>(nowEpoch - EPOCH_MS) : 0ULL;
631 uint64_t last = lastMs.load(std::memory_order_relaxed);
635 uint16_t s =
static_cast<uint16_t
>(seq.fetch_add(1, std::memory_order_relaxed)) & SEQ_MASK;
641 std::this_thread::yield();
642 const auto now2 = std::chrono::time_point_cast<std::chrono::milliseconds>(
643 std::chrono::system_clock::now())
646 nowMs = (now2 >= EPOCH_MS) ?
static_cast<uint64_t
>(now2 - EPOCH_MS) : 0ULL;
647 }
while (nowMs == last);
648 lastMs.store(nowMs, std::memory_order_relaxed);
649 seq.store(0, std::memory_order_relaxed);
653 return ((nowMs & TS_MASK) << (NODE_BITS + SEQ_BITS)) | ((
static_cast<uint64_t
>(NODE_ID) & NODE_MASK) << SEQ_BITS) | (
static_cast<uint64_t
>(s) & SEQ_MASK);
658 lastMs.store(nowMs, std::memory_order_relaxed);
659 seq.store(0, std::memory_order_relaxed);
660 return ((nowMs & TS_MASK) << (NODE_BITS + SEQ_BITS)) | ((
static_cast<uint64_t
>(NODE_ID) & NODE_MASK) << SEQ_BITS) | 0ULL;
1187 auto entity_at_mouse = flecs::entity();
1189 ecs.each([&](flecs::entity entity,
const Position& entity_pos,
const Size& entity_size)
1193 glm::vec2 world_position = mouse_position;
1197 bool withinXBounds = (world_position.x >= (entity_pos.x - entity_size.width / 2)) &&
1198 (world_position.x <= (entity_pos.x + entity_size.width / 2));
1200 bool withinYBounds = (world_position.y >= (entity_pos.y - entity_size.height / 2)) &&
1201 (world_position.y <= (entity_pos.y + entity_size.height / 2));
1203 if (withinXBounds && withinYBounds) {
1204 entity_at_mouse = entity;
1211 if (entity_at_mouse.is_valid())
1212 return entity_at_mouse;
1215 ecs.each([&](flecs::entity entity,
const Position& entity_pos,
const Size& entity_size)
1218 glm::vec2 world_position = mouse_position;
1222 bool withinXBounds = (world_position.x >= (entity_pos.x - entity_size.width / 2)) &&
1223 (world_position.x <= (entity_pos.x + entity_size.width / 2));
1225 bool withinYBounds = (world_position.y >= (entity_pos.y - entity_size.height / 2)) &&
1226 (world_position.y <= (entity_pos.y + entity_size.height / 2));
1228 if (withinXBounds && withinYBounds) {
1229 entity_at_mouse = entity;
1233 return entity_at_mouse;
1335 bool is_hovered =
false;
1337 ImVec2 mousePos = ImGui::GetMousePos();
1338 ImGui::SetNextWindowPos(mousePos, ImGuiCond_Appearing);
1339 ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.2f, 0.3f, 0.4f, 1.0f));
1340 ImGui::Begin(
"EditEntity", &
showEditWindow, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove);
1343 is_hovered = ImGui::IsWindowHovered();
1344 auto is_popup_open =
false;
1353 ImGui::BeginGroup();
1354 if (ImGui::Button(
"+ Size"))
1356 if (nm && boardEnt.is_valid())
1358 size->width = size->width * 1.1;
1359 size->height = size->height * 1.1;
1371 if (ImGui::Button(
"- Size"))
1373 if (nm && boardEnt.is_valid())
1375 size->width = size->width * 0.90;
1376 size->height = size->height * 0.90;
1390 auto vis_temp = visibility->isVisible;
1391 if (ImGui::Checkbox(
"Visible", &vis_temp))
1393 if (nm && boardEnt.is_valid())
1395 visibility->isVisible = vis_temp;
1411 if (ImGui::Button(
"Delete"))
1413 ImGui::OpenPopup(
"Confirm Delete");
1414 is_popup_open =
true;
1417 if (ImGui::IsPopupOpen(
"Confirm Delete"))
1418 is_popup_open =
true;
1419 if (ImGui::BeginPopupModal(
"Confirm Delete",
nullptr, ImGuiWindowFlags_AlwaysAutoResize))
1421 ImGui::Text(
"Are you sure you want to delete this entity?");
1424 if (ImGui::Button(
"Yes", ImVec2(120, 0)))
1428 if (nm && boardEnt.is_valid())
1442 ImGui::CloseCurrentPopup();
1445 if (ImGui::Button(
"No", ImVec2(120, 0)))
1447 ImGui::CloseCurrentPopup();
1454 ImGui::Text(
"Invalid entity or missing components!");
1464 ImGui::TextUnformatted(
"Owner");
1473 std::vector<Opt> options;
1474 options.push_back(Opt{
"",
"(none)"});
1477 std::set<std::string> seenUids;
1480 for (
auto& [peerId, link] : nm->getPeers())
1484 auto uidOpt = idm->uniqueForPeer(peerId);
1485 if (!uidOpt || uidOpt->empty())
1488 const std::string& uid = *uidOpt;
1489 if (!seenUids.insert(uid).second)
1493 std::string label = idm->usernameForUnique(uid);
1495 label = uid.substr(0, std::min<size_t>(8, uid.size()));
1496 options.push_back(Opt{uid, label});
1501 if (idm && !mc->ownerUniqueId.empty() && !seenUids.count(mc->ownerUniqueId))
1503 std::string label = idm->usernameForUnique(mc->ownerUniqueId);
1505 label = mc->ownerUniqueId.substr(0, std::min<size_t>(8, mc->ownerUniqueId.size()));
1506 options.push_back(Opt{mc->ownerUniqueId, label});
1510 int selectedIndex = 0;
1511 for (
int i = 1; i < (int)options.size(); ++i)
1512 if (options[i].uid == mc->ownerUniqueId)
1519 static int ownerPage = 0;
1520 const int rowsPerPage = 6;
1521 const int totalRows = (int)options.size();
1522 const int totalPages = (totalRows + rowsPerPage - 1) / rowsPerPage;
1523 ownerPage = std::clamp(ownerPage, 0, std::max(0, totalPages - 1));
1524 auto ToggleRow = [&](
const char* label,
bool selected,
int id) ->
bool
1529 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.20f, 0.50f, 0.80f, 1.0f));
1530 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.25f, 0.55f, 0.85f, 1.0f));
1531 ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.15f, 0.45f, 0.75f, 1.0f));
1535 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.25f, 0.28f, 0.32f, 1.0f));
1536 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.30f, 0.33f, 0.38f, 1.0f));
1537 ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.22f, 0.25f, 0.29f, 1.0f));
1539 bool clicked = ImGui::Button(label, ImVec2(-FLT_MIN, 0));
1540 ImGui::PopStyleColor(3);
1545 const int start = ownerPage * rowsPerPage;
1546 const int end = std::min(start + rowsPerPage, totalRows);
1549 for (
int i = start; i < end; ++i)
1551 const bool isSel = (selectedIndex == i);
1552 const char* label = options[i].label.c_str();
1553 if (ToggleRow(label, isSel, i))
1558 const std::string prevOwnerUid = mc->ownerUniqueId;
1559 mc->ownerUniqueId = options[i].uid;
1560 mc->ownerPeerUsername.clear();
1561 if (idm && !mc->ownerUniqueId.empty())
1562 mc->ownerPeerUsername = idm->usernameForUnique(mc->ownerUniqueId);
1568 if (prevOwnerUid != mc->ownerUniqueId)
1569 nm->clearDragState(mid);
1576 if (boardEnt.is_valid())
1586 ImGui::BeginDisabled(ownerPage <= 0);
1587 if (ImGui::Button(
"< Prev"))
1589 ImGui::EndDisabled();
1591 ImGui::Text(
"Page %d / %d", ownerPage + 1, std::max(1, totalPages));
1593 ImGui::BeginDisabled(ownerPage >= totalPages - 1);
1594 if (ImGui::Button(
"Next >"))
1596 ImGui::EndDisabled();
1600 bool flagsChanged =
false;
1601 flagsChanged |= ImGui::Checkbox(
"Allow all players to move", &mc->allowAllPlayersMove);
1602 flagsChanged |= ImGui::Checkbox(
"Locked (players cannot move)", &mc->locked);
1603 if (flagsChanged && nm)
1606 if (boardEnt.is_valid())
1612 ImGui::PopStyleColor();
1614 if (!is_hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !is_popup_open)
1674 auto mouse_pos = ImGui::GetMousePos();
1675 ImGui::SetNextWindowPos(ImVec2(mouse_pos.x, mouse_pos.y + ImGui::GetFrameHeightWithSpacing()), ImGuiCond_Appearing);
1676 ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.1f, 0.2f, 1.0f));
1677 ImGui::Begin(
"Grid", &
showGridSettings, ImGuiWindowFlags_AlwaysAutoResize);
1679 ImGui::PopStyleColor();
1682 bool changed =
false;
1687 changed |= ImGui::Checkbox(
"Visible", &grid->visible);
1688 changed |= ImGui::Checkbox(
"Snap to Grid", &grid->snap_to_grid);
1692 changed |= ImGui::SliderFloat(
"Cell Size", &grid->cell_size, 10.0f, 200.0f);
1694 if (ImGui::Button(
"-##size"))
1696 grid->cell_size = grid->cell_size - 0.01f;
1700 if (ImGui::Button(
"+##size"))
1702 grid->cell_size = grid->cell_size + 0.01f;
1706 ImGui::Text(
"Grid Offset");
1707 changed |= ImGui::SliderFloat(
"Offset X", &grid->offset.x, -500.0f, 500.0f);
1709 if (ImGui::Button(
"-##offsetx"))
1711 grid->offset.x = grid->offset.x - 0.01f;
1715 if (ImGui::Button(
"+##offsetx"))
1717 grid->offset.x = grid->offset.x + 0.01f;
1720 changed |= ImGui::SliderFloat(
"Offset Y", &grid->offset.y, -500.0f, 500.0f);
1723 if (ImGui::Button(
"-##offsety"))
1725 grid->offset.y = grid->offset.y - 0.01f;
1729 if (ImGui::Button(
"+##offsety"))
1731 grid->offset.y = grid->offset.y + 0.01f;
1736 if (ImGui::Button(
"Reset Offset"))
1738 grid->offset = glm::vec2(0.0f);
1744 if (grid->snap_to_grid)
1757 ImGui::Text(
"Active board entity does not have a Grid component.");
1779 if (!bytes || sizeBytes == 0)
1781 std::cerr <<
"LoadTextureFromMemory: empty buffer\n";
1786 stbi_set_flip_vertically_on_load(0);
1788 int width = 0, height = 0, nrChannels = 0;
1789 uint8_t* data = stbi_load_from_memory(bytes, (
int)sizeBytes, &width, &height, &nrChannels, 4);
1792 std::cerr <<
"LoadTextureFromMemory: decode failed\n";
1797 glGenTextures(1, &tex);
1798 glBindTexture(GL_TEXTURE_2D, tex);
1800 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height,
1801 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
1803 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1804 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1805 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1806 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1808 stbi_image_free(data);