]> git.localhorst.tv Git - blank.git/blob - src/app.cpp
world class for multiple chunks
[blank.git] / src / app.cpp
1 #include "app.hpp"
2
3 #include "geometry.hpp"
4
5 #include <iostream>
6 #include <stdexcept>
7
8
9 namespace blank {
10
11 Application::Application()
12 : init_sdl()
13 , init_img()
14 , init_gl()
15 , window()
16 , ctx(window.CreateContext())
17 , init_glew()
18 , program()
19 , move_velocity(0.003f)
20 , pitch_sensitivity(-0.0025f)
21 , yaw_sensitivity(-0.001f)
22 , cam()
23 , world()
24 , outline()
25 , outline_visible(false)
26 , outline_transform(1.0f)
27 , light_position(17.0f, 17.0f, 17.0f)
28 , light_color(1.0f, 1.0f, 1.0f)
29 , light_power(250.0f)
30 , m_handle(0)
31 , v_handle(0)
32 , mv_handle(0)
33 , mvp_handle(0)
34 , light_position_handle(0)
35 , light_color_handle(0)
36 , light_power_handle(0)
37 , running(false)
38 , front(false)
39 , back(false)
40 , left(false)
41 , right(false)
42 , up(false)
43 , down(false)
44 , place(false)
45 , remove(false)
46 , pick(false)
47 , remove_id(0)
48 , place_id(1) {
49         GLContext::EnableVSync();
50         GLContext::EnableDepthTest();
51         GLContext::EnableBackfaceCulling();
52         program.LoadShader(
53                 GL_VERTEX_SHADER,
54                 "#version 330 core\n"
55                 "layout(location = 0) in vec3 vtx_position;\n"
56                 "layout(location = 1) in vec3 vtx_color;\n"
57                 "layout(location = 2) in vec3 vtx_normal;\n"
58                 "uniform mat4 M;\n"
59                 "uniform mat4 V;\n"
60                 "uniform mat4 MV;\n"
61                 "uniform mat4 MVP;\n"
62                 "uniform vec3 light_position;\n"
63                 "out vec3 frag_color;\n"
64                 "out vec3 vtx_world;\n"
65                 "out vec3 normal;\n"
66                 "out vec3 eye;\n"
67                 "out vec3 light_direction;\n"
68                 "void main() {\n"
69                         "vec4 v = vec4(vtx_position, 1);\n"
70                         "gl_Position = MVP * v;\n"
71                         "vtx_world = (M * v).xyz;\n"
72                         "vec3 vtx_camera = (MV * v).xyz;\n"
73                         "eye = vec3(0, 0, 0) - vtx_camera;\n"
74                         "vec3 light_camera = (V * v).xyz;\n"
75                         "light_direction = light_position + eye;\n"
76                         "normal = (MV * vec4(vtx_normal, 0)).xyz;\n"
77                         "frag_color = vtx_color;\n"
78                 "}\n"
79         );
80         program.LoadShader(
81                 GL_FRAGMENT_SHADER,
82                 "#version 330 core\n"
83                 "in vec3 frag_color;\n"
84                 "in vec3 vtx_world;\n"
85                 "in vec3 normal;\n"
86                 "in vec3 eye;\n"
87                 "in vec3 light_direction;\n"
88                 "uniform mat4 MV;\n"
89                 "uniform vec3 light_position;\n"
90                 "uniform vec3 light_color;\n"
91                 "uniform float light_power;\n"
92                 "out vec3 color;\n"
93                 "void main() {\n"
94                         "vec3 ambient = vec3(0.1, 0.1, 0.1) * frag_color;\n"
95                         "vec3 specular = vec3(0.3, 0.3, 0.3);\n"
96                         "float distance = length(light_position - vtx_world);\n"
97                         "vec3 n = normalize(normal);\n"
98                         "vec3 l = normalize(light_direction);\n"
99                         "float cos_theta = clamp(dot(n, l), 0, 1);\n"
100                         "vec3 E = normalize(eye);\n"
101                         "vec3 R = reflect(-l, n);\n"
102                         "float cos_alpha = clamp(dot(E, R), 0, 1);\n"
103                         "color = ambient"
104                                 " + frag_color * light_color * light_power * cos_theta / (distance * distance)"
105                                 " + specular * light_color * light_power * pow(cos_alpha, 5) / (distance * distance);\n"
106                 "}\n"
107         );
108         program.Link();
109         if (!program.Linked()) {
110                 program.Log(std::cerr);
111                 throw std::runtime_error("link program");
112         }
113
114         GLuint VertexArrayID;
115         glGenVertexArrays(1, &VertexArrayID);
116         glBindVertexArray(VertexArrayID);
117
118         cam.Position(glm::vec3(0, 4, 4));
119
120         world.Generate();
121
122         outline.vertices = std::vector<glm::vec3>({
123                 { 0.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f },
124                 { 1.0f, 0.0f, 0.0f }, { 1.0f, 1.0f, 0.0f },
125                 { 1.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f },
126                 { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 0.0f },
127                 { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f },
128                 { 1.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 1.0f },
129                 { 1.0f, 1.0f, 0.0f }, { 1.0f, 1.0f, 1.0f },
130                 { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 1.0f },
131                 { 0.0f, 0.0f, 1.0f }, { 1.0f, 0.0f, 1.0f },
132                 { 1.0f, 0.0f, 1.0f }, { 1.0f, 1.0f, 1.0f },
133                 { 1.0f, 1.0f, 1.0f }, { 0.0f, 1.0f, 1.0f },
134                 { 0.0f, 1.0f, 1.0f }, { 0.0f, 0.0f, 1.0f },
135         });
136         outline.colors.resize(24, { -1, -1, -1 });
137         outline.Invalidate();
138
139         m_handle = program.UniformLocation("M");
140         v_handle = program.UniformLocation("V");
141         mv_handle = program.UniformLocation("MV");
142         mvp_handle = program.UniformLocation("MVP");
143         light_position_handle = program.UniformLocation("light_position");
144         light_color_handle = program.UniformLocation("light_color");
145         light_power_handle = program.UniformLocation("light_power");
146
147         glClearColor(0.0, 0.0, 0.0, 1.0);
148 }
149
150
151 void Application::Run() {
152         running = true;
153         Uint32 last = SDL_GetTicks();
154         window.GrabMouse();
155         while (running) {
156                 Uint32 now = SDL_GetTicks();
157                 int delta = now - last;
158                 Loop(delta);
159                 last = now;
160         }
161 }
162
163 void Application::Loop(int dt) {
164         HandleEvents();
165         Update(dt);
166         Render();
167 }
168
169
170 void Application::HandleEvents() {
171         SDL_Event event;
172         while (SDL_PollEvent(&event)) {
173                 switch (event.type) {
174                         case SDL_KEYDOWN:
175                         case SDL_KEYUP:
176                                 switch (event.key.keysym.sym) {
177                                         case SDLK_w:
178                                                 front = event.key.state == SDL_PRESSED;
179                                                 break;
180                                         case SDLK_s:
181                                                 back = event.key.state == SDL_PRESSED;
182                                                 break;
183                                         case SDLK_a:
184                                                 left = event.key.state == SDL_PRESSED;
185                                                 break;
186                                         case SDLK_d:
187                                                 right = event.key.state == SDL_PRESSED;
188                                                 break;
189                                         case SDLK_q:
190                                                 up = event.key.state == SDL_PRESSED;
191                                                 break;
192                                         case SDLK_e:
193                                                 down = event.key.state == SDL_PRESSED;
194                                                 break;
195                                 }
196                                 break;
197                         case SDL_MOUSEBUTTONDOWN:
198                                 if (event.button.button == 1) {
199                                         // left
200                                         remove = true;
201                                 } else if (event.button.button == 2) {
202                                         // middle
203                                         pick = true;
204                                 } else if (event.button.button == 3) {
205                                         // right
206                                         place = true;
207                                 }
208                                 break;
209                         case SDL_MOUSEMOTION:
210                                 cam.RotateYaw(event.motion.xrel * yaw_sensitivity);
211                                 cam.RotatePitch(event.motion.yrel * pitch_sensitivity);
212                                 break;
213                         case SDL_QUIT:
214                                 running = false;
215                                 break;
216                         case SDL_WINDOWEVENT:
217                                 switch (event.window.event) {
218                                         case SDL_WINDOWEVENT_RESIZED:
219                                                 cam.Viewport(event.window.data1, event.window.data2);
220                                                 break;
221                                         default:
222                                                 break;
223                                 }
224                                 break;
225                         default:
226                                 break;
227                 }
228         }
229 }
230
231 void Application::Update(int dt) {
232         glm::vec3 vel;
233         if (right && !left) {
234                 vel.x = move_velocity;
235         } else if (left && !right) {
236                 vel.x = -move_velocity;
237         }
238         if (up && !down) {
239                 vel.y = move_velocity;
240         } else if (down && !up) {
241                 vel.y = -move_velocity;
242         }
243         if (back && !front) {
244                 vel.z = move_velocity;
245         } else if (front && !back) {
246                 vel.z = -move_velocity;
247         }
248         cam.OrientationVelocity(vel);
249
250         cam.Update(dt);
251
252         Ray aim = cam.Aim();
253         Chunk *chunk;
254         int blkid;
255         float dist;
256         glm::vec3 normal;
257         if (world.Intersection(aim, glm::mat4(1.0f), &chunk, &blkid, &dist, &normal)) {
258                 glm::vec3 pos = Chunk::ToCoords(blkid);
259                 outline_visible = true;
260                 outline_transform = glm::translate(chunk->Transform(), pos);
261         } else {
262                 outline_visible = false;
263         }
264
265         if (pick) {
266                 if (chunk) {
267                         place_id = chunk->BlockAt(blkid).type->id;
268                 }
269                 pick = false;
270         }
271         if (remove) {
272                 if (chunk) {
273                         chunk->BlockAt(blkid).type = world.BlockTypes()[remove_id];
274                         chunk->Invalidate();
275                 }
276                 remove = false;
277         }
278         if (place) {
279                 if (chunk) {
280                         Chunk *mod_chunk = chunk;
281                         glm::vec3 next_pos = Chunk::ToCoords(blkid) + normal;
282                         if (!Chunk::InBounds(next_pos)) {
283                                 mod_chunk = &world.Next(*chunk, normal);
284                                 next_pos -= normal * Chunk::Extent();
285                         }
286                         mod_chunk->BlockAt(next_pos).type = world.BlockTypes()[place_id];
287                         mod_chunk->Invalidate();
288                 }
289                 place = false;
290         }
291 }
292
293 void Application::Render() {
294         GLContext::Clear();
295
296         program.Use();
297
298         glUniformMatrix4fv(v_handle, 1, GL_FALSE, &cam.View()[0][0]);
299         glUniform3f(light_position_handle, light_position.x, light_position.y, light_position.z);
300         glUniform3f(light_color_handle, light_color.x, light_color.y, light_color.z);
301         glUniform1f(light_power_handle, light_power);
302
303         for (Chunk &chunk : world.LoadedChunks()) {
304                 glm::mat4 m(chunk.Transform());
305                 glm::mat4 mv(cam.View() * m);
306                 glm::mat4 mvp(cam.MakeMVP(m));
307                 glUniformMatrix4fv(m_handle, 1, GL_FALSE, &m[0][0]);
308                 glUniformMatrix4fv(mv_handle, 1, GL_FALSE, &mv[0][0]);
309                 glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
310                 chunk.Draw();
311         }
312
313         if (outline_visible) {
314                 glm::mat4 m(outline_transform);
315                 glm::mat4 mv(cam.View() * outline_transform);
316                 glm::mat4 mvp(cam.MakeMVP(outline_transform));
317                 glUniformMatrix4fv(m_handle, 1, GL_FALSE, &m[0][0]);
318                 glUniformMatrix4fv(mv_handle, 1, GL_FALSE, &mv[0][0]);
319                 glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
320                 outline.Draw();
321         }
322
323         window.Flip();
324 }
325
326 }