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