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