]> git.localhorst.tv Git - blank.git/blob - src/ui/ui.cpp
also show peak in frame counter
[blank.git] / src / ui / ui.cpp
1 #include "HUD.hpp"
2 #include "Interface.hpp"
3
4 #include "../app/Assets.hpp"
5 #include "../app/FrameCounter.hpp"
6 #include "../app/init.hpp"
7 #include "../graphics/Font.hpp"
8 #include "../graphics/Viewport.hpp"
9 #include "../model/shapes.hpp"
10 #include "../world/World.hpp"
11
12 #include <algorithm>
13 #include <cmath>
14 #include <iostream>
15 #include <sstream>
16 #include <glm/gtc/matrix_transform.hpp>
17 #include <glm/gtx/io.hpp>
18
19
20 namespace blank {
21
22 HUD::HUD(const BlockTypeRegistry &types, const Font &font)
23 : types(types)
24 , font(font)
25 , block()
26 , block_buf()
27 , block_transform(1.0f)
28 , block_label()
29 , block_visible(false)
30 , crosshair() {
31         block_transform = glm::translate(block_transform, glm::vec3(50.0f, 50.0f, 0.0f));
32         block_transform = glm::scale(block_transform, glm::vec3(50.0f));
33         block_transform = glm::rotate(block_transform, 3.5f, glm::vec3(1.0f, 0.0f, 0.0f));
34         block_transform = glm::rotate(block_transform, 0.35f, glm::vec3(0.0f, 1.0f, 0.0f));
35
36         crosshair.vertices = std::vector<glm::vec3>({
37                 { -10.0f,   0.0f, 0.0f }, { 10.0f,  0.0f, 0.0f },
38                 {   0.0f, -10.0f, 0.0f }, {  0.0f, 10.0f, 0.0f },
39         });
40         crosshair.indices = std::vector<OutlineModel::Index>({
41                 0, 1, 2, 3
42         });
43         crosshair.colors.resize(4, { 10.0f, 10.0f, 10.0f });
44         crosshair.Invalidate();
45
46         block_label.Position(
47                 glm::vec3(50.0f, 85.0f, 0.0f),
48                 Gravity::NORTH_WEST,
49                 Gravity::NORTH
50         );
51         block_label.Foreground(glm::vec4(1.0f));
52         block_label.Background(glm::vec4(0.5f));
53 }
54
55
56 void HUD::Display(const Block &b) {
57         const BlockType &type = types.Get(b.type);
58
59         block_buf.Clear();
60         type.FillModel(block_buf, b.Transform());
61         block.Update(block_buf);
62
63         block_label.Set(font, type.label);
64
65         block_visible = type.visible;
66 }
67
68
69 void HUD::Render(Viewport &viewport) noexcept {
70         viewport.ClearDepth();
71
72         DirectionalLighting &world_prog = viewport.HUDProgram();
73         world_prog.SetLightDirection({ 1.0f, 3.0f, 5.0f });
74         // disable distance fog
75         world_prog.SetFogDensity(0.0f);
76
77         viewport.EnableInvertBlending();
78         viewport.SetCursor(glm::vec3(0.0f), Gravity::CENTER);
79         world_prog.SetM(viewport.Cursor());
80         crosshair.Draw();
81
82         if (block_visible) {
83                 viewport.DisableBlending();
84                 world_prog.SetM(block_transform);
85                 block.Draw();
86                 block_label.Render(viewport);
87         }
88 }
89
90
91 Interface::Interface(
92         const Config &config,
93         const Assets &assets,
94         const FrameCounter &counter,
95         World &world)
96 : counter(counter)
97 , world(world)
98 , ctrl(world.Player())
99 , font(assets.LoadFont("DejaVuSans", 16))
100 , hud(world.BlockTypes(), font)
101 , aim{{ 0, 0, 0 }, { 0, 0, -1 }}
102 , aim_chunk(nullptr)
103 , aim_block(0)
104 , aim_normal()
105 , outline()
106 , outline_transform(1.0f)
107 , counter_text()
108 , messages(font)
109 , msg_timer(5000)
110 , config(config)
111 , place_timer(256)
112 , remove_timer(256)
113 , remove(0)
114 , selection(1)
115 , fwd(0)
116 , rev(0) {
117         counter_text.Hide();
118         counter_text.Position(glm::vec3(-25.0f, 25.0f, 0.0f), Gravity::NORTH_EAST);
119         counter_text.Foreground(glm::vec4(1.0f));
120         counter_text.Background(glm::vec4(0.5f));
121         messages.Position(glm::vec3(25.0f, -25.0f, 0.0f), Gravity::SOUTH_WEST);
122         messages.Foreground(glm::vec4(1.0f));
123         messages.Background(glm::vec4(0.5f));
124         hud.Display(selection);
125 }
126
127
128 void Interface::HandlePress(const SDL_KeyboardEvent &event) {
129         if (config.keyboard_disabled) return;
130
131         switch (event.keysym.sym) {
132                 case SDLK_w:
133                         rev.z = 1;
134                         break;
135                 case SDLK_s:
136                         fwd.z = 1;
137                         break;
138                 case SDLK_a:
139                         rev.x = 1;
140                         break;
141                 case SDLK_d:
142                         fwd.x = 1;
143                         break;
144                 case SDLK_SPACE:
145                         fwd.y = 1;
146                         break;
147                 case SDLK_LSHIFT:
148                         rev.y = 1;
149                         break;
150
151                 case SDLK_q:
152                         FaceBlock();
153                         break;
154                 case SDLK_e:
155                         TurnBlock();
156                         break;
157
158                 case SDLK_n:
159                         ToggleCollision();
160                         break;
161
162                 case SDLK_b:
163                         PrintBlockInfo();
164                         break;
165                 case SDLK_c:
166                         PrintChunkInfo();
167                         break;
168                 case SDLK_l:
169                         PrintLightInfo();
170                         break;
171                 case SDLK_p:
172                         PrintSelectionInfo();
173                         break;
174
175                 case SDLK_F3:
176                         ToggleCounter();
177                         break;
178         }
179 }
180
181 void Interface::HandleRelease(const SDL_KeyboardEvent &event) {
182         if (config.keyboard_disabled) return;
183
184         switch (event.keysym.sym) {
185                 case SDLK_w:
186                         rev.z = 0;
187                         break;
188                 case SDLK_s:
189                         fwd.z = 0;
190                         break;
191                 case SDLK_a:
192                         rev.x = 0;
193                         break;
194                 case SDLK_d:
195                         fwd.x = 0;
196                         break;
197                 case SDLK_SPACE:
198                         fwd.y = 0;
199                         break;
200                 case SDLK_LSHIFT:
201                         rev.y = 0;
202                         break;
203         }
204 }
205
206 void Interface::FaceBlock() {
207         selection.SetFace(Block::Face((selection.GetFace() + 1) % Block::FACE_COUNT));
208         hud.Display(selection);
209 }
210
211 void Interface::TurnBlock() {
212         selection.SetTurn(Block::Turn((selection.GetTurn() + 1) % Block::TURN_COUNT));
213         hud.Display(selection);
214 }
215
216 void Interface::ToggleCollision() {
217         ctrl.Controlled().WorldCollidable(!ctrl.Controlled().WorldCollidable());
218         if (ctrl.Controlled().WorldCollidable()) {
219                 PostMessage("collision on");
220         } else {
221                 PostMessage("collision off");
222         }
223 }
224
225 void Interface::PrintBlockInfo() {
226         std::cout << std::endl;
227         if (!aim_chunk) {
228                 PostMessage("not looking at any block");
229                 Ray aim = ctrl.Aim();
230                 std::stringstream s;
231                 s << "aim ray: " << aim.orig << ", " << aim.dir;
232                 PostMessage(s.str());
233                 return;
234         }
235         std::stringstream s;
236         s << "looking at block " << aim_block
237                 << " " << Chunk::ToCoords(aim_block)
238                 << " of chunk " << aim_chunk->Position()
239         ;
240         PostMessage(s.str());
241         Print(aim_chunk->BlockAt(aim_block));
242 }
243
244 void Interface::PrintChunkInfo() {
245         std::cout << std::endl;
246         if (!aim_chunk) {
247                 PostMessage("not looking at any block");
248                 return;
249         }
250         std::stringstream s;
251         s << "looking at chunk " << aim_chunk->Position();
252         PostMessage(s.str());
253
254         PostMessage("  neighbors:");
255         if (aim_chunk->HasNeighbor(Block::FACE_LEFT)) {
256                 s.str("");
257                 s << " left  " << aim_chunk->GetNeighbor(Block::FACE_LEFT).Position();
258                 PostMessage(s.str());
259         }
260         if (aim_chunk->HasNeighbor(Block::FACE_RIGHT)) {
261                 s.str("");
262                 s << " right " << aim_chunk->GetNeighbor(Block::FACE_RIGHT).Position();
263                 PostMessage(s.str());
264         }
265         if (aim_chunk->HasNeighbor(Block::FACE_UP)) {
266                 s.str("");
267                 s << " up    " << aim_chunk->GetNeighbor(Block::FACE_UP).Position();
268                 PostMessage(s.str());
269         }
270         if (aim_chunk->HasNeighbor(Block::FACE_DOWN)) {
271                 s.str("");
272                 s << " down  " << aim_chunk->GetNeighbor(Block::FACE_DOWN).Position();
273                 PostMessage(s.str());
274         }
275         if (aim_chunk->HasNeighbor(Block::FACE_FRONT)) {
276                 s.str("");
277                 s << " front " << aim_chunk->GetNeighbor(Block::FACE_FRONT).Position();
278                 PostMessage(s.str());
279         }
280         if (aim_chunk->HasNeighbor(Block::FACE_BACK)) {
281                 s.str("");
282                 s << " back  " << aim_chunk->GetNeighbor(Block::FACE_BACK).Position();
283                 PostMessage(s.str());
284         }
285         std::cout << std::endl;
286 }
287
288 void Interface::PrintLightInfo() {
289         std::stringstream s;
290         s
291                 << "light level " << world.PlayerChunk().GetLight(world.Player().Position())
292                 << " at position " << world.Player().Position()
293         ;
294         PostMessage(s.str());
295 }
296
297 void Interface::PrintSelectionInfo() {
298         std::cout << std::endl;
299         Print(selection);
300 }
301
302 void Interface::Print(const Block &block) {
303         std::stringstream s;
304         s << "type: " << block.type
305                 << ", face: " << block.GetFace()
306                 << ", turn: " << block.GetTurn()
307         ;
308         PostMessage(s.str());
309 }
310
311 void Interface::ToggleCounter() {
312         counter_text.Toggle();
313         if (counter_text.Visible()) {
314                 UpdateCounter();
315         }
316 }
317
318 void Interface::UpdateCounter() {
319         std::stringstream s;
320         s << std::setprecision(3) <<
321                 "avg: " << counter.Average().running << "ms, "
322                 "peak: " << counter.Peak().running << "ms";
323         std::string text = s.str();
324         counter_text.Set(font, text);
325 }
326
327
328 void Interface::Handle(const SDL_MouseMotionEvent &event) {
329         if (config.mouse_disabled) return;
330         ctrl.RotateYaw(event.xrel * config.yaw_sensitivity);
331         ctrl.RotatePitch(event.yrel * config.pitch_sensitivity);
332 }
333
334 void Interface::HandlePress(const SDL_MouseButtonEvent &event) {
335         if (config.mouse_disabled) return;
336
337         if (event.button == SDL_BUTTON_LEFT) {
338                 RemoveBlock();
339                 remove_timer.Start();
340         } else if (event.button == SDL_BUTTON_MIDDLE) {
341                 PickBlock();
342         } else if (event.button == SDL_BUTTON_RIGHT) {
343                 PlaceBlock();
344                 place_timer.Start();
345         }
346 }
347
348 void Interface::HandleRelease(const SDL_MouseButtonEvent &event) {
349         if (config.mouse_disabled) return;
350
351         if (event.button == SDL_BUTTON_LEFT) {
352                 remove_timer.Stop();
353         } else if (event.button == SDL_BUTTON_RIGHT) {
354                 place_timer.Stop();
355         }
356 }
357
358 void Interface::PickBlock() {
359         if (!aim_chunk) return;
360         selection = aim_chunk->BlockAt(aim_block);
361         hud.Display(selection);
362 }
363
364 void Interface::PlaceBlock() {
365         if (!aim_chunk) return;
366         Chunk *mod_chunk = aim_chunk;
367         glm::vec3 next_pos = Chunk::ToCoords(aim_block) + aim_normal;
368         if (!Chunk::InBounds(next_pos)) {
369                 mod_chunk = &world.Next(*aim_chunk, aim_normal);
370                 next_pos -= aim_normal * glm::vec3(Chunk::Extent());
371         }
372         mod_chunk->SetBlock(next_pos, selection);
373         mod_chunk->Invalidate();
374 }
375
376 void Interface::RemoveBlock() noexcept {
377         if (!aim_chunk) return;
378         aim_chunk->SetBlock(aim_block, remove);
379         aim_chunk->Invalidate();
380 }
381
382
383 void Interface::Handle(const SDL_MouseWheelEvent &event) {
384         if (config.mouse_disabled) return;
385
386         if (event.y < 0) {
387                 SelectNext();
388         } else if (event.y > 0) {
389                 SelectPrevious();
390         }
391 }
392
393 void Interface::SelectNext() {
394         ++selection.type;
395         if (size_t(selection.type) >= world.BlockTypes().Size()) {
396                 selection.type = 1;
397         }
398         hud.Display(selection);
399 }
400
401 void Interface::SelectPrevious() {
402         --selection.type;
403         if (selection.type <= 0) {
404                 selection.type = world.BlockTypes().Size() - 1;
405         }
406         hud.Display(selection);
407 }
408
409
410 void Interface::PostMessage(const char *msg) {
411         messages.PushLine(msg);
412         msg_timer.Reset();
413         msg_timer.Start();
414         std::cout << msg << std::endl;
415 }
416
417
418 void Interface::Update(int dt) {
419         ctrl.Velocity(glm::vec3(fwd - rev) * config.move_velocity);
420         ctrl.Update(dt);
421
422         msg_timer.Update(dt);
423         place_timer.Update(dt);
424         remove_timer.Update(dt);
425
426         aim = ctrl.Aim();
427         CheckAim();
428
429         if (msg_timer.HitOnce()) {
430                 msg_timer.Stop();
431         }
432
433         if (remove_timer.Hit()) {
434                 RemoveBlock();
435                 CheckAim();
436         }
437
438         if (place_timer.Hit()) {
439                 PlaceBlock();
440                 CheckAim();
441         }
442
443         if (counter_text.Visible() && counter.Changed()) {
444                 UpdateCounter();
445         }
446 }
447
448 void Interface::CheckAim() {
449         float dist;
450         if (world.Intersection(aim, glm::mat4(1.0f), aim_chunk, aim_block, dist, aim_normal)) {
451                 outline.Clear();
452                 aim_chunk->Type(aim_chunk->BlockAt(aim_block)).FillOutlineModel(outline);
453                 outline_transform = glm::scale(glm::vec3(1.0002f));
454                 outline_transform *= aim_chunk->Transform(world.Player().ChunkCoords());
455                 outline_transform *= aim_chunk->ToTransform(Chunk::ToPos(aim_block), aim_block);
456         } else {
457                 aim_chunk = nullptr;
458         }
459 }
460
461
462 void Interface::Render(Viewport &viewport) noexcept {
463         if (config.visual_disabled) return;
464
465         if (aim_chunk) {
466                 DirectionalLighting &world_prog = viewport.EntityProgram();
467                 world_prog.SetM(outline_transform);
468                 outline.Draw();
469         }
470
471         if (counter_text.Visible()) {
472                 counter_text.Render(viewport);
473         }
474
475         if (msg_timer.Running()) {
476                 messages.Render(viewport);
477         }
478
479         hud.Render(viewport);
480 }
481
482 }