Botcraft 1.21.11
Loading...
Searching...
No Matches
RenderingManager.cpp
Go to the documentation of this file.
2
3#include <glad/glad.h>
4#include <GLFW/glfw3.h>
5
6#include <glm/glm.hpp>
7#include <glm/gtc/type_ptr.hpp>
8
9#ifdef USE_IMGUI
10#include <imgui.h>
11#include <imgui_impl_glfw.h>
12#include <imgui_impl_opengl3.h>
13#endif
14
15
24
28
32
35
38
39const std::vector<float> color_day({ 0.6f, 0.85f, 0.9f });
40const std::vector<float> color_night({0.1f, 0.1f, 0.1f});
41
42namespace Botcraft
43{
44 namespace Renderer
45 {
46 RenderingManager::RenderingManager(std::shared_ptr<World> world_, std::shared_ptr<InventoryManager> inventory_manager_,
47 std::shared_ptr<EntityManager> entity_manager_,
48 const unsigned int& window_width, const unsigned int& window_height,
49 const unsigned int section_height_, const bool headless)
50 {
51 world = world_;
52 inventory_manager = inventory_manager_;
53 entity_manager = entity_manager_;
54 local_player = nullptr;
55
56 for (int i = 0; i < is_key_pressed.size(); ++i)
57 {
58 is_key_pressed[i] = false;
59 }
60
61 inventory_open = false;
62 behaviour_open = false;
63 behaviour_renderer = std::make_unique<BehaviourRenderer>();
64
65 mouse_last_x = window_width / 2.0f;
66 mouse_last_y = window_height / 2.0f;
67 first_mouse = true;
68
69 deltaTime = 0.0f;
70 lastFrameTime = 0.0f;
71
72 current_window_width = window_width;
73 current_window_height = window_height;
74 has_proj_changed = true;
75
76 MouseCallback = [](double, double) {};
77 KeyboardCallback = [](std::array<bool, static_cast<int>(KEY_CODE::NUMBER_OF_KEYS)>, double) {};
78
79 world_renderer = std::make_unique<WorldRenderer>(section_height_);
80
81 take_screenshot = false;
82
83 day_time = 0.0f;
84
85 running = true;
86 rendering_thread = std::thread(&RenderingManager::Run, this, headless);
88 }
89
91 {
92 MouseCallback = [](double, double) {};
93 KeyboardCallback = [](std::array<bool, static_cast<int>(KEY_CODE::NUMBER_OF_KEYS)>, double) {};
94
95 running = false;
96
97 condition_update.notify_all();
98 if (thread_updating_renderable.joinable())
99 {
101 }
102
103 if (rendering_thread.joinable())
104 {
105 rendering_thread.join();
106 }
107 }
108
109 void RenderingManager::Run(const bool headless)
110 {
111 Logger::GetInstance().RegisterThread("RenderingLoop");
112 if (!Init(headless))
113 {
114 LOG_ERROR("Can't init rendering manager");
115 return;
116 }
117
118 my_shader->Use();
119 float real_fps = 1.0f;
120
121 while (!glfwWindowShouldClose(window))
122 {
123 double currentFrame = glfwGetTime();
124 auto start = std::chrono::steady_clock::now();
125
126 //Max 60 FPS
127 auto end = start + std::chrono::microseconds(1000000 / 60);
128
129 deltaTime = currentFrame - lastFrameTime;
130 lastFrameTime = currentFrame;
131
133
135 {
136#ifdef USE_IMGUI
137 ImGui_ImplOpenGL3_NewFrame();
138 ImGui_ImplGlfw_NewFrame();
139 ImGui::NewFrame();
140
141 {
142 if (local_player == nullptr)
143 {
144 local_player = entity_manager->GetLocalPlayer();
145 }
146 ImGui::SetNextWindowPos(ImVec2(0, 0));
147 ImGui::SetNextWindowSize(ImVec2(290, 70));
148 const Vector3<double> position = local_player == nullptr ? Vector3<double>(0.0) : local_player->GetPosition();
149 ImGui::Begin("Position", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse);
150 ImGui::Text("%f, %f, %f", position.x, position.y, position.z);
151 ImGui::Text("Yaw: %f || ", local_player == nullptr ? 0.0 : local_player->GetYaw());
152 ImGui::SameLine();
153 ImGui::Text("Pitch: %f", local_player == nullptr ? 0.0 : local_player->GetPitch());
154 ImGui::End();
155 }
156 {
157 ImGui::SetNextWindowPos(ImVec2(0, 75));
158 ImGui::SetNextWindowSize(ImVec2(290, 70));
159 ImGui::Begin("Targeted cube", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse);
160 Position raycasted_pos;
161 Position raycasted_normal;
162 const Blockstate* raycasted_blockstate =
163 world->Raycast(Vector3<double>(world_renderer->GetCamera()->GetPosition().x, world_renderer->GetCamera()->GetPosition().y, world_renderer->GetCamera()->GetPosition().z),
164 Vector3<double>(world_renderer->GetCamera()->GetFront().x, world_renderer->GetCamera()->GetFront().y, world_renderer->GetCamera()->GetFront().z),
165 6.0f, raycasted_pos, raycasted_normal);
166 if (raycasted_blockstate != nullptr)
167 {
168 ImGui::Text("Watching block at %i, %i, %i", raycasted_pos.x, raycasted_pos.y, raycasted_pos.z);
169 ImGui::Text("Block: %s", raycasted_blockstate->GetName().c_str());
170 if (ImGui::IsItemHovered(ImGuiHoveredFlags_::ImGuiHoveredFlags_AllowWhenDisabled))
171 {
172 ImGui::BeginTooltip();
173
174 ImGui::Text("Air: "); ImGui::SameLine(); raycasted_blockstate->IsAir() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
175 ImGui::Text("Solid: "); ImGui::SameLine(); raycasted_blockstate->IsSolid() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
176 ImGui::Text("Transparent: "); ImGui::SameLine(); raycasted_blockstate->IsTransparent() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
177 ImGui::Text("Lava: "); ImGui::SameLine(); raycasted_blockstate->IsLava() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
178 ImGui::Text("Water: "); ImGui::SameLine(); raycasted_blockstate->IsWater() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
179 ImGui::Text("Waterlogged: "); ImGui::SameLine(); raycasted_blockstate->IsWaterlogged() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
180 ImGui::Text("Fluid Falling: "); ImGui::SameLine(); raycasted_blockstate->IsFluidFalling() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
181 ImGui::Text("Climbable: "); ImGui::SameLine(); raycasted_blockstate->IsClimbable() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
182 ImGui::Text("Hazardous: "); ImGui::SameLine(); raycasted_blockstate->IsHazardous() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
183 ImGui::Text("Slime: "); ImGui::SameLine(); raycasted_blockstate->IsSlime() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
184 ImGui::Text("Bed: "); ImGui::SameLine(); raycasted_blockstate->IsBed() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
185 ImGui::Text("Soul Sand: "); ImGui::SameLine(); raycasted_blockstate->IsSoulSand() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
186 ImGui::Text("Honey: "); ImGui::SameLine(); raycasted_blockstate->IsHoney() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
187 ImGui::Text("Scaffolding: "); ImGui::SameLine(); raycasted_blockstate->IsScaffolding() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
188 ImGui::Text("Cobweb: "); ImGui::SameLine(); raycasted_blockstate->IsCobweb() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
189 ImGui::Text("UpBubbleColumn: "); ImGui::SameLine(); raycasted_blockstate->IsUpBubbleColumn() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
190 ImGui::Text("DownBubbleColumn: "); ImGui::SameLine(); raycasted_blockstate->IsDownBubbleColumn() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
191 ImGui::Text("Berry Bush: "); ImGui::SameLine(); raycasted_blockstate->IsBerryBush() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
192 ImGui::Text("Powder Snow: "); ImGui::SameLine(); raycasted_blockstate->IsPowderSnow() ? ImGui::TextColored(ImVec4(0.0, 1.0, 0.0, 1.0), "1") : ImGui::TextColored(ImVec4(1.0, 0.0, 0.0, 1.0), "0");
193 ImGui::Text("Fluid Height: "); ImGui::SameLine(); ImGui::TextUnformatted(std::to_string(raycasted_blockstate->GetFluidHeight()).data());
194 ImGui::Text("Hardness: "); ImGui::SameLine(); ImGui::TextUnformatted(std::to_string(raycasted_blockstate->GetHardness()).data());
195 ImGui::Text("Friction: "); ImGui::SameLine(); ImGui::TextUnformatted(std::to_string(raycasted_blockstate->GetFriction()).data());
196
197 ImGui::EndTooltip();
198 }
199 }
200 else
201 {
202 ImGui::Text("Watching block at");
203 ImGui::Text("Block: ");
204 }
205 ImGui::End();
206 }
207#endif
208 const float current_day_time = day_time;
209 std::vector<float> current_color(3);
210 for (int i = 0; i < 3; ++i)
211 {
212 current_color[i] = 2.0f * ((0.5f - std::abs(current_day_time - 0.5f)) * color_day[i] + (0.5f - std::min(std::abs(1.0f - current_day_time), current_day_time)) * color_night[i]);
213 }
214 glClearColor(current_color[0], current_color[1], current_color[2], 1.0f);
215 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
216
217 //Change view matrix
218 world_renderer->UpdateViewMatrix();
219
221 {
222 glm::mat4 projection = glm::perspective(glm::radians(45.0f), current_window_width / static_cast<float>(current_window_height), 0.1f, 200.0f);
223 my_shader->SetMat4("projection", projection);
224 world_renderer->SetCameraProjection(projection);
225 has_proj_changed = false;
226 }
227
228 world_renderer->UpdateFaces();
229
230 my_shader->Use();
231
232 //Draw all faces
233 world_renderer->UseAtlasTextureGL();
234
235#ifdef USE_IMGUI
236 int num_chunks, num_rendered_chunks, num_entities, num_rendered_entities, num_faces, num_rendered_faces;
237 world_renderer->RenderFaces(&num_chunks, &num_rendered_chunks, &num_entities, &num_rendered_entities, &num_faces, &num_rendered_faces);
238 {
239 ImGui::SetNextWindowPos(ImVec2(static_cast<float>(current_window_width), 0.0f), 0, ImVec2(1.0f, 0.0f));
240 ImGui::SetNextWindowSize(ImVec2(180.0f, 170.0f));
241 ImGui::Begin("Rendering", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse);
242 ImGui::Text("Lim. FPS: %.1f (%.2fms)", 1.0 / deltaTime, deltaTime * 1000.0);
243 ImGui::Text("Real FPS: %.1f (%.2fms)", 1.0 / real_fps, real_fps * 1000.0);
244 ImGui::Text("Loaded sections: %i", num_chunks);
245 ImGui::Text("Rendered sections: %i", num_rendered_chunks);
246 ImGui::Text("Num entities: %i", num_entities);
247 ImGui::Text("Rendered entities: %i", num_rendered_entities);
248 ImGui::Text("Loaded faces: %i", num_faces);
249 ImGui::Text("Rendered faces: %i", num_rendered_faces);
250 ImGui::End();
251 }
252#else
253 world_renderer->RenderFaces();
254#endif
255
256 glBindVertexArray(0);
257 glBindTexture(GL_TEXTURE_2D, 0);
258
259#ifdef USE_IMGUI
260 // Draw the behaviour if it's open
261 if (behaviour_open)
262 {
263 const int blackboard_width = static_cast<int>((static_cast<float>(current_window_width) - 30.0f) / 4.0f);
264
265 ImGui::SetNextWindowPos(ImVec2(15.0f, 15.0f), 0, ImVec2(0.0f, 0.0f));
266 ImGui::SetNextWindowSize(ImVec2(blackboard_width, current_window_height - 30.0f));
267 ImGui::Begin("Blackboard", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoCollapse);
268 behaviour_renderer->RenderBlackboard();
269 ImGui::End();
270
271 ImGui::SetNextWindowPos(ImVec2(15.0f + blackboard_width, 15.0f), 0, ImVec2(0.0f, 0.0f));
272 ImGui::SetNextWindowSize(ImVec2(current_window_width - blackboard_width - 30.0f, current_window_height - 30.0f));
273 ImGui::Begin("Behaviour", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse);
274 behaviour_renderer->RenderNodes();
275 ImGui::End();
276 }
277
278 // Draw the inventory if it's open
279 if (inventory_open)
280 {
281 ImGui::SetNextWindowPos(ImVec2(static_cast<float>(current_window_width), static_cast<float>(current_window_height)), 0, ImVec2(1.0f, 1.0f));
282 ImGui::SetNextWindowSize(ImVec2(300.0f, current_window_height - 175.0f));
283 ImGui::Begin("Inventory", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse);
284 if (inventory_manager && inventory_manager->GetPlayerInventory())
285 {
286 const std::map<short, ProtocolCraft::Slot>& slots = inventory_manager->GetPlayerInventory()->GetSlots();
287 for (short i = 0; i <= Window::INVENTORY_OFFHAND_INDEX; ++i)
288 {
289 auto it = slots.find(i);
290 if (it == slots.end())
291 {
292 continue;
293 }
295 {
296 ImGui::Text("Crafting output");
297 }
299 {
300 ImGui::Text("Crafting input");
301 }
302 else if (i == Window::INVENTORY_ARMOR_START)
303 {
304 ImGui::Text("Equiped Armor");
305 }
307 {
308 ImGui::Text("Inventory");
309 }
310 else if (i == Window::INVENTORY_HOTBAR_START)
311 {
312 ImGui::Text("Hotbar");
313 }
315 {
316 ImGui::Text("Offhand");
317 }
318 const std::string name = AssetsManager::getInstance().Items().at(it->second.GetItemId())->GetName();
319 if (name != "minecraft:air")
320 {
321 ImGui::Text(std::string(" (%i) " + name + " (x%i)").c_str(), i, it->second.GetItemCount());
322 }
323 }
324 }
325 ImGui::End();
326 }
327
328
329 ImGui::Render();
330
331 // Render ImGui
332 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
333#endif
334 }
335
336 glfwSwapBuffers(window);
337 glfwPollEvents();
338
339 {
340 std::scoped_lock<std::mutex> lock(screenshot_mutex);
341 if (take_screenshot)
342 {
343 std::vector<unsigned char> pixels(current_window_height * current_window_width * 3);
344 glReadPixels(0, 0, current_window_width, current_window_height, GL_RGB, GL_UNSIGNED_BYTE, pixels.data());
345
347 take_screenshot = false;
348 }
349 }
350
351 real_fps = static_cast<float>(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count() / 1e6);
352
353 //Wait to have 60 FPS
355 }
356
357 world_renderer.reset();
358 my_shader.reset();
359
360#ifdef USE_IMGUI
361 behaviour_renderer->CleanUp();
362 // ImGui cleaning
363 ImGui_ImplOpenGL3_Shutdown();
364 ImGui_ImplGlfw_Shutdown();
365 ImGui::DestroyContext();
366#endif
367
368 glfwDestroyWindow(window);
369 glfwTerminate();
370 }
371
373 {
374 glfwSetWindowShouldClose(window, true);
375 running = false;
376 }
377
378 void RenderingManager::SetMouseCallback(std::function<void(double, double)> callback)
379 {
380 MouseCallback = callback;
381 }
382
383 void RenderingManager::SetKeyboardCallback(std::function<void(std::array<bool, static_cast<int>(KEY_CODE::NUMBER_OF_KEYS)>, double)> callback)
384 {
385 KeyboardCallback = callback;
386 }
387
388 void RenderingManager::Screenshot(const std::string& path)
389 {
390 std::scoped_lock<std::mutex> lock(screenshot_mutex);
391 screenshot_path = path;
392 take_screenshot = true;
393 }
394
396 {
397 behaviour_renderer->SetCurrentBehaviourTree(root);
398 }
399
401 {
402 behaviour_renderer->ResetBehaviourState();
403 }
404
406 {
407 behaviour_renderer->BehaviourStartTick();
408 }
409
410 void RenderingManager::BehaviourEndTick(const bool b) const
411 {
412 behaviour_renderer->BehaviourEndTick(b);
413 }
414
415 void RenderingManager::BehaviourTickChild(const size_t i) const
416 {
417 behaviour_renderer->BehaviourTickChild(i);
418 }
419
421 {
422 return behaviour_renderer->IsBehaviourPaused();
423 }
424
426 {
427 behaviour_renderer->ResetBlackboard();
428 }
429
430 void RenderingManager::UpdateBlackboardValue(const std::string& key, const std::any& value) const
431 {
432 behaviour_renderer->UpdateBlackboardValue(key, value);
433 }
434
435 void RenderingManager::RemoveBlackboardValue(const std::string& key) const
436 {
437 behaviour_renderer->RemoveBlackboardValue(key);
438 }
439
440 bool RenderingManager::Init(const bool headless)
441 {
442 glfwInit();
443 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
444 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
445 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
446#ifdef __APPLE__
447 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
448#endif
449
450 if (headless)
451 {
452 glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
453 }
454
455 window = glfwCreateWindow(current_window_width, current_window_height, "RenderingManager", NULL, NULL);
456 if (window == NULL)
457 {
458 LOG_ERROR("Failed to create GLFW window");
459 glfwTerminate();
460 return false;
461 }
462 glfwMakeContextCurrent(window);
463 //set the user pointer of the window to this object to pass it to the callbacks
464 glfwSetWindowUserPointer(window, this);
465 glfwSetFramebufferSizeCallback(window, &RenderingManager::ResizeCallback);
466 glfwSetCursorPosCallback(window, &RenderingManager::InternalMouseCallback);
467
468 glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
469
470 // glad: load all OpenGL function pointers
471 // ---------------------------------------
472 if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
473 {
474 LOG_ERROR("Failed to initialize GLAD");
475 return false;
476 }
477
478#ifdef USE_IMGUI
479 // imgui: setup context
480 // ---------------------------------------
481 IMGUI_CHECKVERSION();
482 ImGui::CreateContext();
483
484 behaviour_renderer->Init();
485
486 ImGuiIO& io = ImGui::GetIO();
487 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
488
489 // Style
490 ImGui::StyleColorsDark();
491
492 // Setup platform/renderer
493 ImGui_ImplGlfw_InitForOpenGL(window, true);
494 ImGui_ImplOpenGL3_Init("#version 330");
495#endif
496
497 my_shader = std::make_unique<Shader>();
498
499 glEnable(GL_DEPTH_TEST);
500 //glEnable(GL_CULL_FACE);
501 glEnable(GL_BLEND);
502 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
503
504 //Set an uniform buffer for view matrix
505 unsigned int uniform_view_block_index = glGetUniformBlockIndex(my_shader->Program(), "MatriceView");
506 glUniformBlockBinding(my_shader->Program(), uniform_view_block_index, 0);
507
508 world_renderer->InitGL();
509
510 return true;
511 }
512
513 void RenderingManager::ResizeCallback(GLFWwindow *window, int width, int height)
514 {
515 RenderingManager *this_object = static_cast<RenderingManager*>(glfwGetWindowUserPointer(window));
516 this_object->current_window_width = width;
517 this_object->current_window_height = height;
518 glViewport(0, 0, width, height);
519 this_object->has_proj_changed = true;
520 }
521
522 void RenderingManager::InternalMouseCallback(GLFWwindow *window, double xpos, double ypos)
523 {
524 RenderingManager *this_object = static_cast<RenderingManager*>(glfwGetWindowUserPointer(window));
525
526 if (this_object->first_mouse)
527 {
528 this_object->mouse_last_x = xpos;
529 this_object->mouse_last_y = ypos;
530 this_object->first_mouse = false;
531 }
532
533 double xoffset = xpos - this_object->mouse_last_x;
534 double yoffset = this_object->mouse_last_y - ypos;
535
536 this_object->mouse_last_x = xpos;
537 this_object->mouse_last_y = ypos;
538
539 if (this_object->inventory_open || this_object->behaviour_open)
540 {
541 return;
542 }
543
544 this_object->MouseCallback(xoffset, yoffset);
545 }
546
548 {
549 is_key_pressed[static_cast<int>(KEY_CODE::FORWARD)] = glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS;
550 is_key_pressed[static_cast<int>(KEY_CODE::BACKWARD)] = glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS;
551 is_key_pressed[static_cast<int>(KEY_CODE::RIGHT)] = glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS;
552 is_key_pressed[static_cast<int>(KEY_CODE::LEFT)] = glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS;
553 is_key_pressed[static_cast<int>(KEY_CODE::SPACE)] = glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS;
554 is_key_pressed[static_cast<int>(KEY_CODE::SHIFT)] = glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS;
555 is_key_pressed[static_cast<int>(KEY_CODE::CTRL)] = glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS;
556 is_key_pressed[static_cast<int>(KEY_CODE::ESC)] = glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS;
557 is_key_pressed[static_cast<int>(KEY_CODE::MOUSE_LEFT)] = glfwGetKey(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS;
558 const bool is_inventory_key_pressed = glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS;
559
560 // Toggle inventory if E was not pressed during previous frame and is during this one
561 const bool toggle_inventory = (!is_key_pressed[static_cast<int>(KEY_CODE::INVENTORY)] && is_inventory_key_pressed);
562 // Save current value just like others
563 is_key_pressed[static_cast<int>(KEY_CODE::INVENTORY)] = is_inventory_key_pressed;
564
565
566 const bool is_behaviour_key_pressed = glfwGetKey(window, GLFW_KEY_B) == GLFW_PRESS;
567
568 // Toggle behaviour if B was not pressed during previous frame and is during this one
569 const bool toggle_behaviour = (!is_key_pressed[static_cast<int>(KEY_CODE::BEHAVIOUR)] && is_behaviour_key_pressed);
570 // Save current value just like others
571 is_key_pressed[static_cast<int>(KEY_CODE::BEHAVIOUR)] = is_behaviour_key_pressed;
572
573#ifdef USE_IMGUI
574 if (toggle_inventory)
575 {
577 }
578
579 if (toggle_behaviour)
580 {
582 }
583
585 {
586 glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
587 }
588 else
589 {
590 glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
591 }
592#endif
593
594 if (glfwGetKey(window, GLFW_KEY_F2) == GLFW_PRESS)
595 {
596 std::scoped_lock<std::mutex> lock(screenshot_mutex);
597 screenshot_path = "screenshot.png";
598 take_screenshot = true;
599 }
600
602 }
603
604 void RenderingManager::AddChunkToUpdate(const int x, const int z)
605 {
606 const std::vector<Position> chunk_pos = { {Position(x, 0, z), Position(x - 1, 0, z),
607 Position(x + 1, 0 , z), Position(x, 0, z - 1), Position(x, 0, z + 1)} };
608
609 std::lock_guard<std::mutex> guard_rendering(mutex_updating);
610 for (int i = 0; i < chunk_pos.size(); ++i)
611 {
612 chunks_to_udpate.insert(chunk_pos[i]);
613 }
614 condition_update.notify_all();
615 }
616
618 {
619 std::lock_guard<std::mutex> guard_rendering(mutex_updating);
620 entities_to_update.insert(id);
621 condition_update.notify_all();
622 }
623
624 void RenderingManager::SetPosOrientation(const double x_, const double y_, const double z_, const float yaw_, const float pitch_)
625 {
626 if (world_renderer)
627 {
628 world_renderer->SetPosOrientation(x_, y_, z_, yaw_, pitch_);
629 }
630 }
631
633 {
634 Logger::GetInstance().RegisterThread("RenderingDataUpdate");
635 while (running)
636 {
637 {
638 std::unique_lock<std::mutex> lck(mutex_updating);
639 condition_update.wait(lck);
640 }
641
642 while (!chunks_to_udpate.empty())
643 {
644 Position pos;
645 mutex_updating.lock();
646 if (!chunks_to_udpate.empty())
647 {
648 auto posIterator = chunks_to_udpate.begin();
649 pos = *posIterator;
650 chunks_to_udpate.erase(posIterator);
651 }
652 mutex_updating.unlock();
653
654 std::optional<Botcraft::Chunk> chunk;
655 // Get the new values in the world
656 bool has_chunk_been_modified = world->HasChunkBeenModified(pos.x, pos.z);
657 if (has_chunk_been_modified)
658 {
659 chunk = world->ResetChunkModificationState(pos.x, pos.z);
660 }
661
662 if (has_chunk_been_modified)
663 {
664 world_renderer->UpdateChunk(pos.x, pos.z, chunk);
665 }
666
667 // If we left the game, we don't need to process
668 // the rest of the data, just discard them
669 if (!running)
670 {
671 mutex_updating.lock();
672 chunks_to_udpate.clear();
673 mutex_updating.unlock();
674 break;
675 }
676 }
677
678 while (!entities_to_update.empty())
679 {
680 int entity_id = -1;
681 mutex_updating.lock();
682 if (!entities_to_update.empty())
683 {
684 auto eid_it = entities_to_update.begin();
685 entity_id = *eid_it;
686 entities_to_update.erase(eid_it);
687 }
688 mutex_updating.unlock();
689
690 std::vector<Face> faces;
691 // Get the new values
692 std::shared_ptr<Botcraft::Entity> entity = entity_manager->GetEntity(entity_id);
693 bool should_update = false;
694 if (entity == nullptr)
695 {
696 should_update = true;
697 }
698 else if (!entity->GetAreRenderedFacesUpToDate())
699 {
700 faces = entity->GetFaces(true);
701 should_update = true;
702 }
703
704 if (should_update)
705 {
706 world_renderer->UpdateEntity(entity_id, faces);
707 }
708
709 // If we left the game, we don't need to process
710 // the rest of the data, just discard them
711 if (!running)
712 {
713 mutex_updating.lock();
714 entities_to_update.clear();
715 mutex_updating.unlock();
716 break;
717 }
718 }
719 }
720 }
721
722 /*
723 * Packet handling methods
724 */
725
727 {
728 Position chunk_coords = Botcraft::Chunk::BlockCoordsToChunkCoords(msg.GetPos());
729 AddChunkToUpdate(chunk_coords.x, chunk_coords.z);
730 }
731
733 {
734#if PROTOCOL_VERSION < 737 /* < 1.16.2 */
735 AddChunkToUpdate(msg.GetChunkX(), msg.GetChunkZ());
736#else
737 AddChunkToUpdate(msg.GetSectionPos() >> 42, msg.GetSectionPos() << 22 >> 42);
738#endif
739 }
740
742 {
743#if PROTOCOL_VERSION < 764 /* < 1.20.2 */
744 AddChunkToUpdate(msg.GetX(), msg.GetZ());
745#else
746 AddChunkToUpdate(msg.GetPos().GetX(), msg.GetPos().GetZ());
747#endif
748 }
749
750#if PROTOCOL_VERSION < 757 /* < 1.18 */
751 void RenderingManager::Handle(ProtocolCraft::ClientboundLevelChunkPacket& msg)
752#else
754#endif
755 {
756 AddChunkToUpdate(msg.GetX(), msg.GetZ());
757 }
758
763
764#if PROTOCOL_VERSION < 759 /* < 1.19 */
765 void RenderingManager::Handle(ProtocolCraft::ClientboundAddMobPacket& msg)
766 {
767 AddEntityToUpdate(msg.GetEntityId());
768 }
769#endif
770
771#if PROTOCOL_VERSION < 721 /* < 1.16 */
772 void RenderingManager::Handle(ProtocolCraft::ClientboundAddGlobalEntityPacket& msg)
773 {
774 AddEntityToUpdate(msg.GetEntityId());
775 }
776#endif
777
778#if PROTOCOL_VERSION < 764 /* < 1.20.2 */
779 void RenderingManager::Handle(ProtocolCraft::ClientboundAddPlayerPacket& msg)
780 {
781 AddEntityToUpdate(msg.GetEntityId());
782 }
783#endif
784
789
790#if PROTOCOL_VERSION < 755 /* < 1.17 */
791 void RenderingManager::Handle(ProtocolCraft::ClientboundMoveEntityPacket& msg)
792 {
793 AddEntityToUpdate(msg.GetEntityId());
794 }
795#endif
796
801
806
811
813 {
814 std::shared_ptr<LocalPlayer> local_player = entity_manager->GetLocalPlayer();
815 if (local_player != nullptr && local_player->GetEntityID() == msg.GetEntityId())
816 {
817 return;
818 }
819 AddEntityToUpdate(msg.GetEntityId());
820 }
821
822#if PROTOCOL_VERSION == 755 /* 1.17 */
823 void RenderingManager::Handle(ProtocolCraft::ClientboundRemoveEntityPacket& msg)
824 {
825 AddEntityToUpdate(msg.GetEntityId());
826 }
827#else
829 {
830 for (auto id: msg.GetEntityIds())
831 {
833 }
834 }
835#endif
836
838 {
839 day_time = ((msg.GetDayTime() + 6000) % 24000) / 24000.0f;
840 }
841
846 } // Renderer
847} // Botcraft
#define LOG_ERROR(osstream)
Definition Logger.hpp:45
const std::vector< float > color_day({ 0.6f, 0.85f, 0.9f })
const std::vector< float > color_night({0.1f, 0.1f, 0.1f})
const std::unordered_map< ItemId, std::unique_ptr< Item > > & Items() const
static AssetsManager & getInstance()
bool IsHazardous() const
bool IsBerryBush() const
const std::string & GetName() const
bool IsCobweb() const
bool IsDownBubbleColumn() const
bool IsTransparent() const
bool IsClimbable() const
float GetHardness() const
bool IsFluidFalling() const
bool IsPowderSnow() const
bool IsUpBubbleColumn() const
bool IsWaterlogged() const
float GetFriction() const
float GetFluidHeight() const
Get fluid height for this block.
bool IsScaffolding() const
bool IsSoulSand() const
static Position BlockCoordsToChunkCoords(const Position &pos)
Definition Chunk.cpp:73
void RegisterThread(const std::string &name)
Register the current thread in the map.
Definition Logger.cpp:104
static Logger & GetInstance()
Definition Logger.cpp:36
void BehaviourTickChild(const size_t i) const
RenderingManager(std::shared_ptr< World > world_, std::shared_ptr< InventoryManager > inventory_manager_, std::shared_ptr< EntityManager > entity_manager_, const unsigned int &window_width, const unsigned int &window_height, const unsigned int section_height_=16, const bool headless=false)
std::unordered_set< int > entities_to_update
std::unique_ptr< BehaviourRenderer > behaviour_renderer
void InternalProcessInput(GLFWwindow *window)
void SetKeyboardCallback(std::function< void(std::array< bool, static_cast< int >(KEY_CODE::NUMBER_OF_KEYS)>, double)> callback)
std::function< void(double, double)> MouseCallback
void Screenshot(const std::string &path)
std::shared_ptr< LocalPlayer > local_player
void AddChunkToUpdate(const int x, const int z)
void SetCurrentBehaviourTree(const BaseNode *root) const
static void ResizeCallback(GLFWwindow *window, int width, int height)
std::unique_ptr< WorldRenderer > world_renderer
std::unordered_set< Position > chunks_to_udpate
std::shared_ptr< EntityManager > entity_manager
void RemoveBlackboardValue(const std::string &key) const
static void InternalMouseCallback(GLFWwindow *window, double xpos, double ypos)
void UpdateBlackboardValue(const std::string &key, const std::any &value) const
void SetMouseCallback(std::function< void(double, double)> callback)
void BehaviourEndTick(const bool b) const
virtual void Handle(ProtocolCraft::ClientboundBlockUpdatePacket &msg) override
std::function< void(std::array< bool, static_cast< int >(KEY_CODE::NUMBER_OF_KEYS)>, double)> KeyboardCallback
std::shared_ptr< InventoryManager > inventory_manager
std::array< bool, static_cast< int >(KEY_CODE::NUMBER_OF_KEYS)> is_key_pressed
void SetPosOrientation(const double x_, const double y_, const double z_, const float yaw_, const float pitch_)
static constexpr short INVENTORY_HOTBAR_START
Definition Window.hpp:26
static constexpr short INVENTORY_CRAFTING_INPUT_START
Definition Window.hpp:19
static constexpr short INVENTORY_ARMOR_START
Definition Window.hpp:20
static constexpr short INVENTORY_CRAFTING_OUTPUT_INDEX
Definition Window.hpp:18
static constexpr short INVENTORY_STORAGE_START
Definition Window.hpp:25
static constexpr short INVENTORY_OFFHAND_INDEX
Definition Window.hpp:27
void WriteImage(const std::string &path, const int height, const int width, const int depth, const unsigned char *data, const bool vertical_revert=true)
void SleepUntil(const std::chrono::steady_clock::time_point &end)
Vector3< int > Position
Definition Vector3.hpp:294