]> git.localhorst.tv Git - blank.git/blob - src/ui/ui.cpp
fancy crosshair
[blank.git] / src / ui / ui.cpp
1 #include "HUD.hpp"
2 #include "Interface.hpp"
3
4 #include "../app/Assets.hpp"
5 #include "../app/init.hpp"
6 #include "../graphics/BlendedSprite.hpp"
7 #include "../graphics/DirectionalLighting.hpp"
8 #include "../graphics/Font.hpp"
9 #include "../model/shapes.hpp"
10 #include "../world/World.hpp"
11
12 #include <algorithm>
13 #include <cmath>
14 #include <iostream>
15 #include <glm/gtc/matrix_transform.hpp>
16 #include <glm/gtx/io.hpp>
17
18
19 namespace blank {
20
21 HUD::HUD(const BlockTypeRegistry &types, const Font &font)
22 : types(types)
23 , font(font)
24 , block()
25 , block_buf()
26 , block_transform(1.0f)
27 , block_label()
28 , label_sprite()
29 , label_transform(1.0f)
30 , label_color{0xFF, 0xFF, 0xFF, 0xFF}
31 , block_visible(false)
32 , crosshair()
33 , crosshair_transform(1.0f)
34 , near(100.0f)
35 , far(-100.0f)
36 , projection(glm::ortho(0.0f, 1.0f, 1.0f, 0.0f, near, far))
37 , view(1.0f) {
38         block_transform = glm::translate(block_transform, glm::vec3(50.0f, 50.0f, 0.0f));
39         block_transform = glm::scale(block_transform, glm::vec3(50.0f));
40         block_transform = glm::rotate(block_transform, 3.5f, glm::vec3(1.0f, 0.0f, 0.0f));
41         block_transform = glm::rotate(block_transform, 0.35f, glm::vec3(0.0f, 1.0f, 0.0f));
42
43         crosshair.vertices = std::vector<glm::vec3>({
44                 { -10.0f,   0.0f, 0.0f }, { 10.0f,  0.0f, 0.0f },
45                 {   0.0f, -10.0f, 0.0f }, {  0.0f, 10.0f, 0.0f },
46         });
47         crosshair.indices = std::vector<OutlineModel::Index>({
48                 0, 1, 2, 3
49         });
50         crosshair.colors.resize(4, { 10.0f, 10.0f, 10.0f });
51         crosshair.Invalidate();
52 }
53
54
55 void HUD::Viewport(float width, float height) noexcept {
56         Viewport(0, 0, width, height);
57 }
58
59 void HUD::Viewport(float x, float y, float width, float height) noexcept {
60         projection = glm::ortho(x, width, height, y, near, far);
61         crosshair_transform = glm::translate(glm::vec3(width * 0.5f, height * 0.5f, 0.0f));
62 }
63
64
65 void HUD::Display(const Block &b) {
66         const BlockType &type = types.Get(b.type);
67
68         block_buf.Clear();
69         type.FillModel(block_buf, b.Transform());
70         block.Update(block_buf);
71
72         font.Render(type.label.c_str(), label_color, block_label);
73         glm::vec2 size(font.TextSize(type.label.c_str()));
74         label_sprite.LoadRect(size.x, size.y);
75         label_transform = glm::translate(glm::vec3(
76                 std::max(5.0f, 50.0f - std::round(size.x * 0.5f)),
77                 70.0f + size.y,
78                 0.75f
79         ));
80
81         block_visible = type.visible;
82 }
83
84
85 void HUD::Render(DirectionalLighting &world_prog, BlendedSprite &sprite_prog) noexcept {
86         world_prog.Activate();
87         world_prog.SetLightDirection({ 1.0f, 3.0f, 5.0f });
88         // disable distance fog
89         world_prog.SetFogDensity(0.0f);
90         GLContext::ClearDepthBuffer();
91
92         GLContext::EnableInvertBlending();
93         world_prog.SetMVP(crosshair_transform, view, projection);
94         crosshair.Draw();
95
96         if (block_visible) {
97                 GLContext::DisableBlending();
98                 world_prog.SetM(block_transform);
99                 block.Draw();
100
101                 sprite_prog.Activate();
102                 sprite_prog.SetMVP(label_transform, view, projection);
103                 sprite_prog.SetTexture(block_label);
104                 label_sprite.Draw();
105         }
106 }
107
108
109 Interface::Interface(const Config &config, const Assets &assets, World &world)
110 : world(world)
111 , ctrl(world.Player())
112 , font(assets.LoadFont("DejaVuSans", 16))
113 , hud(world.BlockTypes(), font)
114 , aim{{ 0, 0, 0 }, { 0, 0, -1 }}
115 , aim_chunk(nullptr)
116 , aim_block(0)
117 , aim_normal()
118 , outline()
119 , outline_transform(1.0f)
120 , config(config)
121 , place_timer(256)
122 , remove_timer(256)
123 , remove(0)
124 , selection(1)
125 , fwd(0)
126 , rev(0) {
127         hud.Viewport(960, 600);
128         hud.Display(selection);
129 }
130
131
132 void Interface::HandlePress(const SDL_KeyboardEvent &event) {
133         if (config.keyboard_disabled) return;
134
135         switch (event.keysym.sym) {
136                 case SDLK_w:
137                         rev.z = 1;
138                         break;
139                 case SDLK_s:
140                         fwd.z = 1;
141                         break;
142                 case SDLK_a:
143                         rev.x = 1;
144                         break;
145                 case SDLK_d:
146                         fwd.x = 1;
147                         break;
148                 case SDLK_SPACE:
149                         fwd.y = 1;
150                         break;
151                 case SDLK_LSHIFT:
152                         rev.y = 1;
153                         break;
154
155                 case SDLK_q:
156                         FaceBlock();
157                         break;
158                 case SDLK_e:
159                         TurnBlock();
160                         break;
161
162                 case SDLK_n:
163                         ToggleCollision();
164                         break;
165
166                 case SDLK_b:
167                         PrintBlockInfo();
168                         break;
169                 case SDLK_c:
170                         PrintChunkInfo();
171                         break;
172                 case SDLK_l:
173                         PrintLightInfo();
174                         break;
175                 case SDLK_p:
176                         PrintSelectionInfo();
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         std::cout << "collision " << (ctrl.Controlled().WorldCollidable() ? "on" : "off") << std::endl;
219 }
220
221 void Interface::PrintBlockInfo() {
222         std::cout << std::endl;
223         if (!aim_chunk) {
224                 std::cout << "not looking at any block" << std::endl;
225                 Ray aim = ctrl.Aim();
226                 std::cout << "aim ray: " << aim.orig << ", " << aim.dir << std::endl;
227                 return;
228         }
229         std::cout << "looking at block " << aim_block
230                 << " " << Chunk::ToCoords(aim_block)
231                 << " of chunk " << aim_chunk->Position()
232                 << std::endl;
233         Print(aim_chunk->BlockAt(aim_block));
234 }
235
236 void Interface::PrintChunkInfo() {
237         std::cout << std::endl;
238         if (!aim_chunk) {
239                 std::cout << "not looking at any block" << std::endl;
240                 return;
241         }
242         std::cout << "looking at chunk "
243                 << aim_chunk->Position()
244                 << std::endl;
245
246         std::cout << "  neighbors:" << std::endl;
247         if (aim_chunk->HasNeighbor(Block::FACE_LEFT)) {
248                 std::cout << " left  " << aim_chunk->GetNeighbor(Block::FACE_LEFT).Position() << std::endl;
249         }
250         if (aim_chunk->HasNeighbor(Block::FACE_RIGHT)) {
251                 std::cout << " right " << aim_chunk->GetNeighbor(Block::FACE_RIGHT).Position() << std::endl;
252         }
253         if (aim_chunk->HasNeighbor(Block::FACE_UP)) {
254                 std::cout << " up    " << aim_chunk->GetNeighbor(Block::FACE_UP).Position() << std::endl;
255         }
256         if (aim_chunk->HasNeighbor(Block::FACE_DOWN)) {
257                 std::cout << " down  " << aim_chunk->GetNeighbor(Block::FACE_DOWN).Position() << std::endl;
258         }
259         if (aim_chunk->HasNeighbor(Block::FACE_FRONT)) {
260                 std::cout << " front " << aim_chunk->GetNeighbor(Block::FACE_FRONT).Position() << std::endl;
261         }
262         if (aim_chunk->HasNeighbor(Block::FACE_BACK)) {
263                 std::cout << " back  " << aim_chunk->GetNeighbor(Block::FACE_BACK).Position() << std::endl;
264         }
265         std::cout << std::endl;
266 }
267
268 void Interface::PrintLightInfo() {
269         std::cout
270                 << "light level " << world.PlayerChunk().GetLight(world.Player().Position())
271                 << " at position " << world.Player().Position()
272                 << std::endl;
273 }
274
275 void Interface::PrintSelectionInfo() {
276         std::cout << std::endl;
277         Print(selection);
278 }
279
280 void Interface::Print(const Block &block) {
281         std::cout << "type: " << block.type
282                 << ", face: " << block.GetFace()
283                 << ", turn: " << block.GetTurn()
284                 << std::endl;
285 }
286
287
288 void Interface::Handle(const SDL_MouseMotionEvent &event) {
289         if (config.mouse_disabled) return;
290         ctrl.RotateYaw(event.xrel * config.yaw_sensitivity);
291         ctrl.RotatePitch(event.yrel * config.pitch_sensitivity);
292 }
293
294 void Interface::HandlePress(const SDL_MouseButtonEvent &event) {
295         if (config.mouse_disabled) return;
296
297         if (event.button == SDL_BUTTON_LEFT) {
298                 RemoveBlock();
299                 remove_timer.Start();
300         } else if (event.button == SDL_BUTTON_MIDDLE) {
301                 PickBlock();
302         } else if (event.button == SDL_BUTTON_RIGHT) {
303                 PlaceBlock();
304                 place_timer.Start();
305         }
306 }
307
308 void Interface::HandleRelease(const SDL_MouseButtonEvent &event) {
309         if (config.mouse_disabled) return;
310
311         if (event.button == SDL_BUTTON_LEFT) {
312                 remove_timer.Stop();
313         } else if (event.button == SDL_BUTTON_RIGHT) {
314                 place_timer.Stop();
315         }
316 }
317
318 void Interface::PickBlock() {
319         if (!aim_chunk) return;
320         selection = aim_chunk->BlockAt(aim_block);
321         hud.Display(selection);
322 }
323
324 void Interface::PlaceBlock() {
325         if (!aim_chunk) return;
326         Chunk *mod_chunk = aim_chunk;
327         glm::vec3 next_pos = Chunk::ToCoords(aim_block) + aim_normal;
328         if (!Chunk::InBounds(next_pos)) {
329                 mod_chunk = &world.Next(*aim_chunk, aim_normal);
330                 next_pos -= aim_normal * glm::vec3(Chunk::Extent());
331         }
332         mod_chunk->SetBlock(next_pos, selection);
333         mod_chunk->Invalidate();
334 }
335
336 void Interface::RemoveBlock() noexcept {
337         if (!aim_chunk) return;
338         aim_chunk->SetBlock(aim_block, remove);
339         aim_chunk->Invalidate();
340 }
341
342
343 void Interface::Handle(const SDL_MouseWheelEvent &event) {
344         if (config.mouse_disabled) return;
345
346         if (event.y < 0) {
347                 SelectNext();
348         } else if (event.y > 0) {
349                 SelectPrevious();
350         }
351 }
352
353 void Interface::SelectNext() {
354         ++selection.type;
355         if (size_t(selection.type) >= world.BlockTypes().Size()) {
356                 selection.type = 1;
357         }
358         hud.Display(selection);
359 }
360
361 void Interface::SelectPrevious() {
362         --selection.type;
363         if (selection.type <= 0) {
364                 selection.type = world.BlockTypes().Size() - 1;
365         }
366         hud.Display(selection);
367 }
368
369 void Interface::Handle(const SDL_WindowEvent &event) noexcept {
370         if (event.event == SDL_WINDOWEVENT_RESIZED) {
371                 hud.Viewport(event.data1, event.data2);
372         }
373 }
374
375
376 void Interface::Update(int dt) {
377         ctrl.Velocity(glm::vec3(fwd - rev) * config.move_velocity);
378         ctrl.Update(dt);
379
380         place_timer.Update(dt);
381         remove_timer.Update(dt);
382
383         aim = ctrl.Aim();
384         CheckAim();
385
386         if (remove_timer.Hit()) {
387                 RemoveBlock();
388                 CheckAim();
389         }
390
391         if (place_timer.Hit()) {
392                 PlaceBlock();
393                 CheckAim();
394         }
395 }
396
397 void Interface::CheckAim() {
398         float dist;
399         if (world.Intersection(aim, glm::mat4(1.0f), aim_chunk, aim_block, dist, aim_normal)) {
400                 outline.Clear();
401                 aim_chunk->Type(aim_chunk->BlockAt(aim_block)).FillOutlineModel(outline);
402                 outline_transform = glm::scale(glm::vec3(1.0002f));
403                 outline_transform *= aim_chunk->Transform(world.Player().ChunkCoords());
404                 outline_transform *= aim_chunk->ToTransform(Chunk::ToPos(aim_block), aim_block);
405         } else {
406                 aim_chunk = nullptr;
407         }
408 }
409
410
411 void Interface::Render(DirectionalLighting &world_prog, BlendedSprite &sprite_prog) noexcept {
412         if (config.visual_disabled) return;
413
414         if (aim_chunk) {
415                 world_prog.Activate();
416                 world_prog.SetM(outline_transform);
417                 outline.Draw();
418         }
419
420         hud.Render(world_prog, sprite_prog);
421 }
422
423 }