2 #include "Interface.hpp"
4 #include "../app/Assets.hpp"
5 #include "../app/FrameCounter.hpp"
6 #include "../app/init.hpp"
7 #include "../graphics/BlendedSprite.hpp"
8 #include "../graphics/DirectionalLighting.hpp"
9 #include "../graphics/Font.hpp"
10 #include "../model/shapes.hpp"
11 #include "../world/World.hpp"
17 #include <glm/gtc/matrix_transform.hpp>
18 #include <glm/gtx/io.hpp>
23 HUD::HUD(const BlockTypeRegistry &types, const Font &font)
28 , block_transform(1.0f)
31 , label_transform(1.0f)
32 , label_color{0xFF, 0xFF, 0xFF, 0xFF}
33 , block_visible(false)
35 , crosshair_transform(1.0f)
38 , projection(glm::ortho(0.0f, 1.0f, 1.0f, 0.0f, near, far))
40 block_transform = glm::translate(block_transform, glm::vec3(50.0f, 50.0f, 0.0f));
41 block_transform = glm::scale(block_transform, glm::vec3(50.0f));
42 block_transform = glm::rotate(block_transform, 3.5f, glm::vec3(1.0f, 0.0f, 0.0f));
43 block_transform = glm::rotate(block_transform, 0.35f, glm::vec3(0.0f, 1.0f, 0.0f));
45 crosshair.vertices = std::vector<glm::vec3>({
46 { -10.0f, 0.0f, 0.0f }, { 10.0f, 0.0f, 0.0f },
47 { 0.0f, -10.0f, 0.0f }, { 0.0f, 10.0f, 0.0f },
49 crosshair.indices = std::vector<OutlineModel::Index>({
52 crosshair.colors.resize(4, { 10.0f, 10.0f, 10.0f });
53 crosshair.Invalidate();
57 void HUD::Viewport(float width, float height) noexcept {
58 Viewport(0, 0, width, height);
61 void HUD::Viewport(float x, float y, float width, float height) noexcept {
62 projection = glm::ortho(x, width, height, y, near, far);
63 crosshair_transform = glm::translate(glm::vec3(width * 0.5f, height * 0.5f, 0.0f));
67 void HUD::Display(const Block &b) {
68 const BlockType &type = types.Get(b.type);
71 type.FillModel(block_buf, b.Transform());
72 block.Update(block_buf);
74 font.Render(type.label.c_str(), label_color, block_label);
75 glm::vec2 size(font.TextSize(type.label.c_str()));
76 label_sprite.LoadRect(size.x, size.y);
77 label_transform = glm::translate(glm::vec3(
78 std::max(5.0f, 50.0f - std::round(size.x * 0.5f)),
83 block_visible = type.visible;
87 void HUD::Render(DirectionalLighting &world_prog, BlendedSprite &sprite_prog) noexcept {
88 world_prog.Activate();
89 world_prog.SetLightDirection({ 1.0f, 3.0f, 5.0f });
90 // disable distance fog
91 world_prog.SetFogDensity(0.0f);
92 GLContext::ClearDepthBuffer();
94 GLContext::EnableInvertBlending();
95 world_prog.SetMVP(crosshair_transform, view, projection);
99 GLContext::DisableBlending();
100 world_prog.SetM(block_transform);
103 sprite_prog.Activate();
104 sprite_prog.SetMVP(label_transform, view, projection);
105 sprite_prog.SetTexture(block_label);
111 Interface::Interface(
112 const Config &config,
113 const Assets &assets,
114 const FrameCounter &counter,
118 , ctrl(world.Player())
119 , font(assets.LoadFont("DejaVuSans", 16))
120 , hud(world.BlockTypes(), font)
121 , aim{{ 0, 0, 0 }, { 0, 0, -1 }}
126 , outline_transform(1.0f)
127 , show_counter(false)
130 , counter_transform(1.0f)
131 , counter_color{0xFF, 0xFF, 0xFF, 0xFF}
139 hud.Viewport(960, 600);
140 hud.Display(selection);
144 void Interface::HandlePress(const SDL_KeyboardEvent &event) {
145 if (config.keyboard_disabled) return;
147 switch (event.keysym.sym) {
188 PrintSelectionInfo();
197 void Interface::HandleRelease(const SDL_KeyboardEvent &event) {
198 if (config.keyboard_disabled) return;
200 switch (event.keysym.sym) {
222 void Interface::FaceBlock() {
223 selection.SetFace(Block::Face((selection.GetFace() + 1) % Block::FACE_COUNT));
224 hud.Display(selection);
227 void Interface::TurnBlock() {
228 selection.SetTurn(Block::Turn((selection.GetTurn() + 1) % Block::TURN_COUNT));
229 hud.Display(selection);
232 void Interface::ToggleCollision() {
233 ctrl.Controlled().WorldCollidable(!ctrl.Controlled().WorldCollidable());
234 std::cout << "collision " << (ctrl.Controlled().WorldCollidable() ? "on" : "off") << std::endl;
237 void Interface::PrintBlockInfo() {
238 std::cout << std::endl;
240 std::cout << "not looking at any block" << std::endl;
241 Ray aim = ctrl.Aim();
242 std::cout << "aim ray: " << aim.orig << ", " << aim.dir << std::endl;
245 std::cout << "looking at block " << aim_block
246 << " " << Chunk::ToCoords(aim_block)
247 << " of chunk " << aim_chunk->Position()
249 Print(aim_chunk->BlockAt(aim_block));
252 void Interface::PrintChunkInfo() {
253 std::cout << std::endl;
255 std::cout << "not looking at any block" << std::endl;
258 std::cout << "looking at chunk "
259 << aim_chunk->Position()
262 std::cout << " neighbors:" << std::endl;
263 if (aim_chunk->HasNeighbor(Block::FACE_LEFT)) {
264 std::cout << " left " << aim_chunk->GetNeighbor(Block::FACE_LEFT).Position() << std::endl;
266 if (aim_chunk->HasNeighbor(Block::FACE_RIGHT)) {
267 std::cout << " right " << aim_chunk->GetNeighbor(Block::FACE_RIGHT).Position() << std::endl;
269 if (aim_chunk->HasNeighbor(Block::FACE_UP)) {
270 std::cout << " up " << aim_chunk->GetNeighbor(Block::FACE_UP).Position() << std::endl;
272 if (aim_chunk->HasNeighbor(Block::FACE_DOWN)) {
273 std::cout << " down " << aim_chunk->GetNeighbor(Block::FACE_DOWN).Position() << std::endl;
275 if (aim_chunk->HasNeighbor(Block::FACE_FRONT)) {
276 std::cout << " front " << aim_chunk->GetNeighbor(Block::FACE_FRONT).Position() << std::endl;
278 if (aim_chunk->HasNeighbor(Block::FACE_BACK)) {
279 std::cout << " back " << aim_chunk->GetNeighbor(Block::FACE_BACK).Position() << std::endl;
281 std::cout << std::endl;
284 void Interface::PrintLightInfo() {
286 << "light level " << world.PlayerChunk().GetLight(world.Player().Position())
287 << " at position " << world.Player().Position()
291 void Interface::PrintSelectionInfo() {
292 std::cout << std::endl;
296 void Interface::Print(const Block &block) {
297 std::cout << "type: " << block.type
298 << ", face: " << block.GetFace()
299 << ", turn: " << block.GetTurn()
303 void Interface::ToggleCounter() {
304 if ((show_counter = !show_counter)) {
309 void Interface::UpdateCounter() {
311 s << std::setprecision(3) << counter.AvgRunning() << "ms";
312 std::string text = s.str();
313 font.Render(text.c_str(), counter_color, counter_tex);
314 glm::vec2 size(font.TextSize(text.c_str()));
315 counter_sprite.LoadRect(size.x, size.y);
316 counter_transform = glm::translate(glm::vec3(
324 void Interface::Handle(const SDL_MouseMotionEvent &event) {
325 if (config.mouse_disabled) return;
326 ctrl.RotateYaw(event.xrel * config.yaw_sensitivity);
327 ctrl.RotatePitch(event.yrel * config.pitch_sensitivity);
330 void Interface::HandlePress(const SDL_MouseButtonEvent &event) {
331 if (config.mouse_disabled) return;
333 if (event.button == SDL_BUTTON_LEFT) {
335 remove_timer.Start();
336 } else if (event.button == SDL_BUTTON_MIDDLE) {
338 } else if (event.button == SDL_BUTTON_RIGHT) {
344 void Interface::HandleRelease(const SDL_MouseButtonEvent &event) {
345 if (config.mouse_disabled) return;
347 if (event.button == SDL_BUTTON_LEFT) {
349 } else if (event.button == SDL_BUTTON_RIGHT) {
354 void Interface::PickBlock() {
355 if (!aim_chunk) return;
356 selection = aim_chunk->BlockAt(aim_block);
357 hud.Display(selection);
360 void Interface::PlaceBlock() {
361 if (!aim_chunk) return;
362 Chunk *mod_chunk = aim_chunk;
363 glm::vec3 next_pos = Chunk::ToCoords(aim_block) + aim_normal;
364 if (!Chunk::InBounds(next_pos)) {
365 mod_chunk = &world.Next(*aim_chunk, aim_normal);
366 next_pos -= aim_normal * glm::vec3(Chunk::Extent());
368 mod_chunk->SetBlock(next_pos, selection);
369 mod_chunk->Invalidate();
372 void Interface::RemoveBlock() noexcept {
373 if (!aim_chunk) return;
374 aim_chunk->SetBlock(aim_block, remove);
375 aim_chunk->Invalidate();
379 void Interface::Handle(const SDL_MouseWheelEvent &event) {
380 if (config.mouse_disabled) return;
384 } else if (event.y > 0) {
389 void Interface::SelectNext() {
391 if (size_t(selection.type) >= world.BlockTypes().Size()) {
394 hud.Display(selection);
397 void Interface::SelectPrevious() {
399 if (selection.type <= 0) {
400 selection.type = world.BlockTypes().Size() - 1;
402 hud.Display(selection);
405 void Interface::Handle(const SDL_WindowEvent &event) noexcept {
406 if (event.event == SDL_WINDOWEVENT_RESIZED) {
407 hud.Viewport(event.data1, event.data2);
412 void Interface::Update(int dt) {
413 ctrl.Velocity(glm::vec3(fwd - rev) * config.move_velocity);
416 place_timer.Update(dt);
417 remove_timer.Update(dt);
422 if (remove_timer.Hit()) {
427 if (place_timer.Hit()) {
432 if (show_counter && counter.Changed()) {
437 void Interface::CheckAim() {
439 if (world.Intersection(aim, glm::mat4(1.0f), aim_chunk, aim_block, dist, aim_normal)) {
441 aim_chunk->Type(aim_chunk->BlockAt(aim_block)).FillOutlineModel(outline);
442 outline_transform = glm::scale(glm::vec3(1.0002f));
443 outline_transform *= aim_chunk->Transform(world.Player().ChunkCoords());
444 outline_transform *= aim_chunk->ToTransform(Chunk::ToPos(aim_block), aim_block);
451 void Interface::Render(DirectionalLighting &world_prog, BlendedSprite &sprite_prog) noexcept {
452 if (config.visual_disabled) return;
455 world_prog.Activate();
456 world_prog.SetM(outline_transform);
461 sprite_prog.Activate();
462 sprite_prog.SetM(counter_transform);
463 sprite_prog.SetTexture(counter_tex);
464 counter_sprite.Draw();
467 hud.Render(world_prog, sprite_prog);