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