]> git.localhorst.tv Git - blank.git/blob - src/ui/ui.cpp
combine text handling stuff into a class
[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 , config(config)
109 , place_timer(256)
110 , remove_timer(256)
111 , remove(0)
112 , selection(1)
113 , fwd(0)
114 , rev(0) {
115         counter_text.Hide();
116         counter_text.Position(glm::vec3(-25.0f, 25.0f, 0.0f), Gravity::NORTH_EAST);
117         counter_text.Foreground(glm::vec4(1.0f));
118         counter_text.Background(glm::vec4(0.5f));
119         hud.Display(selection);
120 }
121
122
123 void Interface::HandlePress(const SDL_KeyboardEvent &event) {
124         if (config.keyboard_disabled) return;
125
126         switch (event.keysym.sym) {
127                 case SDLK_w:
128                         rev.z = 1;
129                         break;
130                 case SDLK_s:
131                         fwd.z = 1;
132                         break;
133                 case SDLK_a:
134                         rev.x = 1;
135                         break;
136                 case SDLK_d:
137                         fwd.x = 1;
138                         break;
139                 case SDLK_SPACE:
140                         fwd.y = 1;
141                         break;
142                 case SDLK_LSHIFT:
143                         rev.y = 1;
144                         break;
145
146                 case SDLK_q:
147                         FaceBlock();
148                         break;
149                 case SDLK_e:
150                         TurnBlock();
151                         break;
152
153                 case SDLK_n:
154                         ToggleCollision();
155                         break;
156
157                 case SDLK_b:
158                         PrintBlockInfo();
159                         break;
160                 case SDLK_c:
161                         PrintChunkInfo();
162                         break;
163                 case SDLK_l:
164                         PrintLightInfo();
165                         break;
166                 case SDLK_p:
167                         PrintSelectionInfo();
168                         break;
169
170                 case SDLK_F3:
171                         ToggleCounter();
172                         break;
173         }
174 }
175
176 void Interface::HandleRelease(const SDL_KeyboardEvent &event) {
177         if (config.keyboard_disabled) return;
178
179         switch (event.keysym.sym) {
180                 case SDLK_w:
181                         rev.z = 0;
182                         break;
183                 case SDLK_s:
184                         fwd.z = 0;
185                         break;
186                 case SDLK_a:
187                         rev.x = 0;
188                         break;
189                 case SDLK_d:
190                         fwd.x = 0;
191                         break;
192                 case SDLK_SPACE:
193                         fwd.y = 0;
194                         break;
195                 case SDLK_LSHIFT:
196                         rev.y = 0;
197                         break;
198         }
199 }
200
201 void Interface::FaceBlock() {
202         selection.SetFace(Block::Face((selection.GetFace() + 1) % Block::FACE_COUNT));
203         hud.Display(selection);
204 }
205
206 void Interface::TurnBlock() {
207         selection.SetTurn(Block::Turn((selection.GetTurn() + 1) % Block::TURN_COUNT));
208         hud.Display(selection);
209 }
210
211 void Interface::ToggleCollision() {
212         ctrl.Controlled().WorldCollidable(!ctrl.Controlled().WorldCollidable());
213         std::cout << "collision " << (ctrl.Controlled().WorldCollidable() ? "on" : "off") << std::endl;
214 }
215
216 void Interface::PrintBlockInfo() {
217         std::cout << std::endl;
218         if (!aim_chunk) {
219                 std::cout << "not looking at any block" << std::endl;
220                 Ray aim = ctrl.Aim();
221                 std::cout << "aim ray: " << aim.orig << ", " << aim.dir << std::endl;
222                 return;
223         }
224         std::cout << "looking at block " << aim_block
225                 << " " << Chunk::ToCoords(aim_block)
226                 << " of chunk " << aim_chunk->Position()
227                 << std::endl;
228         Print(aim_chunk->BlockAt(aim_block));
229 }
230
231 void Interface::PrintChunkInfo() {
232         std::cout << std::endl;
233         if (!aim_chunk) {
234                 std::cout << "not looking at any block" << std::endl;
235                 return;
236         }
237         std::cout << "looking at chunk "
238                 << aim_chunk->Position()
239                 << std::endl;
240
241         std::cout << "  neighbors:" << std::endl;
242         if (aim_chunk->HasNeighbor(Block::FACE_LEFT)) {
243                 std::cout << " left  " << aim_chunk->GetNeighbor(Block::FACE_LEFT).Position() << std::endl;
244         }
245         if (aim_chunk->HasNeighbor(Block::FACE_RIGHT)) {
246                 std::cout << " right " << aim_chunk->GetNeighbor(Block::FACE_RIGHT).Position() << std::endl;
247         }
248         if (aim_chunk->HasNeighbor(Block::FACE_UP)) {
249                 std::cout << " up    " << aim_chunk->GetNeighbor(Block::FACE_UP).Position() << std::endl;
250         }
251         if (aim_chunk->HasNeighbor(Block::FACE_DOWN)) {
252                 std::cout << " down  " << aim_chunk->GetNeighbor(Block::FACE_DOWN).Position() << std::endl;
253         }
254         if (aim_chunk->HasNeighbor(Block::FACE_FRONT)) {
255                 std::cout << " front " << aim_chunk->GetNeighbor(Block::FACE_FRONT).Position() << std::endl;
256         }
257         if (aim_chunk->HasNeighbor(Block::FACE_BACK)) {
258                 std::cout << " back  " << aim_chunk->GetNeighbor(Block::FACE_BACK).Position() << std::endl;
259         }
260         std::cout << std::endl;
261 }
262
263 void Interface::PrintLightInfo() {
264         std::cout
265                 << "light level " << world.PlayerChunk().GetLight(world.Player().Position())
266                 << " at position " << world.Player().Position()
267                 << std::endl;
268 }
269
270 void Interface::PrintSelectionInfo() {
271         std::cout << std::endl;
272         Print(selection);
273 }
274
275 void Interface::Print(const Block &block) {
276         std::cout << "type: " << block.type
277                 << ", face: " << block.GetFace()
278                 << ", turn: " << block.GetTurn()
279                 << std::endl;
280 }
281
282 void Interface::ToggleCounter() {
283         counter_text.Toggle();
284         if (counter_text.Visible()) {
285                 UpdateCounter();
286         }
287 }
288
289 void Interface::UpdateCounter() {
290         std::stringstream s;
291         s << std::setprecision(3) << counter.AvgRunning() << "ms";
292         std::string text = s.str();
293         counter_text.Set(font, text);
294 }
295
296
297 void Interface::Handle(const SDL_MouseMotionEvent &event) {
298         if (config.mouse_disabled) return;
299         ctrl.RotateYaw(event.xrel * config.yaw_sensitivity);
300         ctrl.RotatePitch(event.yrel * config.pitch_sensitivity);
301 }
302
303 void Interface::HandlePress(const SDL_MouseButtonEvent &event) {
304         if (config.mouse_disabled) return;
305
306         if (event.button == SDL_BUTTON_LEFT) {
307                 RemoveBlock();
308                 remove_timer.Start();
309         } else if (event.button == SDL_BUTTON_MIDDLE) {
310                 PickBlock();
311         } else if (event.button == SDL_BUTTON_RIGHT) {
312                 PlaceBlock();
313                 place_timer.Start();
314         }
315 }
316
317 void Interface::HandleRelease(const SDL_MouseButtonEvent &event) {
318         if (config.mouse_disabled) return;
319
320         if (event.button == SDL_BUTTON_LEFT) {
321                 remove_timer.Stop();
322         } else if (event.button == SDL_BUTTON_RIGHT) {
323                 place_timer.Stop();
324         }
325 }
326
327 void Interface::PickBlock() {
328         if (!aim_chunk) return;
329         selection = aim_chunk->BlockAt(aim_block);
330         hud.Display(selection);
331 }
332
333 void Interface::PlaceBlock() {
334         if (!aim_chunk) return;
335         Chunk *mod_chunk = aim_chunk;
336         glm::vec3 next_pos = Chunk::ToCoords(aim_block) + aim_normal;
337         if (!Chunk::InBounds(next_pos)) {
338                 mod_chunk = &world.Next(*aim_chunk, aim_normal);
339                 next_pos -= aim_normal * glm::vec3(Chunk::Extent());
340         }
341         mod_chunk->SetBlock(next_pos, selection);
342         mod_chunk->Invalidate();
343 }
344
345 void Interface::RemoveBlock() noexcept {
346         if (!aim_chunk) return;
347         aim_chunk->SetBlock(aim_block, remove);
348         aim_chunk->Invalidate();
349 }
350
351
352 void Interface::Handle(const SDL_MouseWheelEvent &event) {
353         if (config.mouse_disabled) return;
354
355         if (event.y < 0) {
356                 SelectNext();
357         } else if (event.y > 0) {
358                 SelectPrevious();
359         }
360 }
361
362 void Interface::SelectNext() {
363         ++selection.type;
364         if (size_t(selection.type) >= world.BlockTypes().Size()) {
365                 selection.type = 1;
366         }
367         hud.Display(selection);
368 }
369
370 void Interface::SelectPrevious() {
371         --selection.type;
372         if (selection.type <= 0) {
373                 selection.type = world.BlockTypes().Size() - 1;
374         }
375         hud.Display(selection);
376 }
377
378
379 void Interface::Update(int dt) {
380         ctrl.Velocity(glm::vec3(fwd - rev) * config.move_velocity);
381         ctrl.Update(dt);
382
383         place_timer.Update(dt);
384         remove_timer.Update(dt);
385
386         aim = ctrl.Aim();
387         CheckAim();
388
389         if (remove_timer.Hit()) {
390                 RemoveBlock();
391                 CheckAim();
392         }
393
394         if (place_timer.Hit()) {
395                 PlaceBlock();
396                 CheckAim();
397         }
398
399         if (counter_text.Visible() && counter.Changed()) {
400                 UpdateCounter();
401         }
402 }
403
404 void Interface::CheckAim() {
405         float dist;
406         if (world.Intersection(aim, glm::mat4(1.0f), aim_chunk, aim_block, dist, aim_normal)) {
407                 outline.Clear();
408                 aim_chunk->Type(aim_chunk->BlockAt(aim_block)).FillOutlineModel(outline);
409                 outline_transform = glm::scale(glm::vec3(1.0002f));
410                 outline_transform *= aim_chunk->Transform(world.Player().ChunkCoords());
411                 outline_transform *= aim_chunk->ToTransform(Chunk::ToPos(aim_block), aim_block);
412         } else {
413                 aim_chunk = nullptr;
414         }
415 }
416
417
418 void Interface::Render(Viewport &viewport) noexcept {
419         if (config.visual_disabled) return;
420
421         if (aim_chunk) {
422                 DirectionalLighting &world_prog = viewport.EntityProgram();
423                 world_prog.SetM(outline_transform);
424                 outline.Draw();
425         }
426
427         if (counter_text.Visible()) {
428                 counter_text.Render(viewport);
429         }
430
431         hud.Render(viewport);
432 }
433
434 }