]> git.localhorst.tv Git - blobs.git/blob - src/app/states.cpp
varying material and schlick/fresnel
[blobs.git] / src / app / states.cpp
1 #include "MasterState.hpp"
2
3 #include "Application.hpp"
4 #include "../creature/Creature.hpp"
5 #include "../graphics/Viewport.hpp"
6 #include "../math/const.hpp"
7 #include "../world/Body.hpp"
8 #include "../world/Planet.hpp"
9 #include "../world/Simulation.hpp"
10 #include "../world/Sun.hpp"
11
12 #include <glm/gtx/vector_angle.hpp>
13 #include <glm/gtx/euler_angles.hpp>
14 #include <glm/gtx/rotate_vector.hpp>
15 #include <glm/gtx/transform.hpp>
16
17 #include <iostream>
18 #include <glm/gtx/io.hpp>
19
20
21 namespace blobs {
22 namespace app {
23
24 MasterState::MasterState(Assets &assets, world::Simulation &sim) noexcept
25 : State()
26 , assets(assets)
27 , sim(sim)
28 , cam(**sim.Suns().begin())
29 , cam_pos(0.0, 0.0, 1.0)
30 , cam_tgt_pos(0.0, 0.0, 1.0)
31 , cam_focus(0.0)
32 , cam_tgt_focus(0.0)
33 , cam_up(0.0, 1.0, 0.0)
34 , cam_tgt_up(0.0, 1.0, 0.0)
35 , cam_dist(5.0)
36 , cam_orient(PI * 0.375, PI * 0.25, 0.0)
37 , cam_dragging(false)
38 , shown_creature(nullptr)
39 , shown_body(nullptr)
40 , bp(assets)
41 , cp(assets)
42 , rp(sim)
43 , tp(sim)
44 , remain(0)
45 , thirds(0)
46 , paused(false) {
47         bp.ZIndex(10.0f);
48         cp.ZIndex(20.0f);
49         rp.ZIndex(30.0f);
50         tp.ZIndex(40.0f);
51 }
52
53 MasterState::~MasterState() noexcept {
54 }
55
56
57 void MasterState::Show(creature::Creature &c) noexcept {
58         cam.Reference(c.GetSituation().GetPlanet());
59         cam.TrackOrientation(true);
60         cam_orient.x = std::max(0.0, cam_orient.x);
61         cp.Show(c);
62         bp.Hide();
63         tp.SetBody(c.GetSituation().GetPlanet());
64         shown_creature = &c;
65         shown_body = nullptr;
66 }
67
68 void MasterState::Show(world::Body &b) noexcept {
69         cam.Reference(b);
70         cam.TrackOrientation(false);
71         bp.Show(b);
72         cp.Hide();
73         tp.SetBody(b);
74         shown_creature = nullptr;
75         shown_body = &b;
76 }
77
78
79 void MasterState::OnResize(int w, int h) {
80         assets.shaders.canvas.Activate();
81         assets.shaders.canvas.Resize(float(w), float(h));
82         assets.shaders.alpha_sprite.Activate();
83         assets.shaders.alpha_sprite.SetVP(glm::mat4(1.0f), glm::ortho(0.0f, float(w), float(h), 0.0f, 1.0e4f, -1.0e4f));
84
85         cam.Aspect(float(w), float(h));
86         assets.shaders.planet_surface.Activate();
87         assets.shaders.planet_surface.SetVP(cam.View(), cam.Projection());
88         assets.shaders.sun_surface.Activate();
89         assets.shaders.sun_surface.SetVP(cam.View(), cam.Projection());
90         assets.shaders.creature_skin.Activate();
91         assets.shaders.creature_skin.SetVP(cam.View(), cam.Projection());
92         assets.shaders.sky_box.Activate();
93         assets.shaders.sky_box.SetVP(cam.View() * cam.Universe(), cam.Projection());
94 }
95
96 void MasterState::OnUpdate(int dt) {
97         remain += dt;
98 #ifdef NDEBUG
99         int max_tick = 10;
100 #else
101         // one tick per frame when debugging so pausing execution doesn't result in more ticks
102         int max_tick = 1;
103 #endif
104         while (remain >= FrameMS() && max_tick > 0) {
105                 Tick();
106                 --max_tick;
107         }
108         if (max_tick == 0) {
109                 // drop remaining
110                 remain = 0;
111         }
112 }
113
114 void MasterState::Tick() {
115         constexpr double dt = 0.01666666666666666666666666666666;
116         if (!paused) {
117                 sim.Tick(dt);
118         }
119         remain -= FrameMS();
120         thirds = (thirds + 1) % 3;
121
122         // determine where camera should be
123         double actual_dist = cam_dist;
124         if (shown_creature) {
125                 cam_tgt_focus = shown_creature->GetSituation().Position();
126                 cam_tgt_up = glm::normalize(cam_tgt_focus);
127                 actual_dist += shown_creature->Size();
128         } else if (shown_body) {
129                 cam_tgt_focus = glm::dvec3(0.0);
130                 cam_tgt_up = glm::dvec3(0.0, 1.0, 0.0);
131                 actual_dist += shown_body->Radius();
132         }
133
134         glm::dvec3 dir(0.0, 0.0, -actual_dist);
135         glm::dvec3 ref_dir(glm::normalize(glm::cross(cam_tgt_up, glm::dvec3(-cam_tgt_up.z, cam_tgt_up.x, cam_tgt_up.y))));
136         dir =
137                 glm::dmat3(ref_dir, cam_tgt_up, glm::cross(ref_dir, cam_tgt_up))
138                 * glm::dmat3(glm::eulerAngleYX(-cam_orient.y, -cam_orient.x))
139                 * dir;
140         cam_tgt_up = glm::rotate(cam_tgt_up, cam_orient.z, glm::normalize(-dir));
141         cam_tgt_pos = cam_tgt_focus - dir;
142
143         // approach target location
144         glm::dvec3 cam_pos_diff(cam_tgt_pos - cam_pos);
145         if (glm::length2(cam_pos_diff) > 0.000001) {
146                 cam_pos += cam_pos_diff * 0.25;
147         } else {
148                 cam_pos = cam_tgt_pos;
149         }
150
151         glm::dvec3 cam_focus_diff(cam_tgt_focus - cam_focus);
152         if (glm::length2(cam_focus_diff) > 0.000001) {
153                 cam_focus += cam_focus_diff * 0.25;
154         } else {
155                 cam_focus = cam_tgt_focus;
156         }
157
158         double cam_up_diff = glm::angle(cam_up, cam_tgt_up);
159         if (cam_up_diff > 0.001) {
160                 cam_up = glm::rotate(cam_up, cam_up_diff * 0.25, glm::normalize(glm::cross(cam_up, cam_tgt_up)));
161         } else {
162                 cam_up = cam_tgt_up;
163         }
164 }
165
166 int MasterState::FrameMS() const noexcept {
167         return thirds == 0 ? 16 : 17;
168 }
169
170
171 void MasterState::OnKeyDown(const SDL_KeyboardEvent &e) {
172         if (e.keysym.sym == SDLK_p) {
173                 paused = !paused;
174         } else if (e.keysym.sym == SDLK_F1) {
175                 rp.Toggle();
176         }
177 }
178
179 void MasterState::OnMouseDown(const SDL_MouseButtonEvent &e) {
180         if (e.button == SDL_BUTTON_RIGHT) {
181                 SDL_SetRelativeMouseMode(SDL_TRUE);
182                 cam_dragging = true;
183         }
184 }
185
186 void MasterState::OnMouseUp(const SDL_MouseButtonEvent &e) {
187         if (e.button == SDL_BUTTON_LEFT) {
188                 glm::dmat4 inverse(glm::inverse(cam.Projection() * cam.View()));
189                 math::Ray ray(inverse * App().GetViewport().ShootPixel(e.x, e.y));
190
191                 shown_creature = nullptr;
192                 double closest_dist = std::numeric_limits<double>::infinity();
193                 for (creature::Creature *c : sim.LiveCreatures()) {
194                         glm::dvec3 normal(0.0);
195                         double dist = 0.0;
196                         if (Intersect(ray, c->CollisionBounds(), glm::dmat4(cam.Model(c->GetSituation().GetPlanet())) * c->CollisionTransform(), normal, dist)
197                                 && dist < closest_dist) {
198                                 shown_creature = c;
199                                 closest_dist = dist;
200                         }
201                 }
202
203                 shown_body = nullptr;
204                 for (world::Body *b : sim.Bodies()) {
205                         glm::dvec3 normal(0.0);
206                         double dist = 0.0;
207                         if (Intersect(ray, glm::dmat4(cam.Model(*b)) * b->CollisionBounds(), normal, dist) && dist < closest_dist) {
208                                 shown_creature = nullptr;
209                                 shown_body = b;
210                                 closest_dist = dist;
211                         }
212                 }
213
214                 if (shown_creature) {
215                         Show(*shown_creature);
216                 } else if (shown_body) {
217                         Show(*shown_body);
218                 } else {
219                         cp.Hide();
220                         bp.Hide();
221                 }
222         } else if (e.button == SDL_BUTTON_RIGHT) {
223                 SDL_SetRelativeMouseMode(SDL_FALSE);
224                 cam_dragging = false;
225         }
226 }
227
228 void MasterState::OnMouseMotion(const SDL_MouseMotionEvent &e) {
229         constexpr double pitch_scale = PI * 0.001;
230         constexpr double yaw_scale = PI * 0.002;
231         if (cam_dragging) {
232                 cam_orient.x = glm::clamp(cam_orient.x + double(e.yrel) * pitch_scale, shown_creature ? 0.0 : PI * -0.499, PI * 0.499);
233                 cam_orient.y = std::fmod(cam_orient.y + double(e.xrel) * yaw_scale, PI * 2.0);
234         }
235 }
236
237 void MasterState::OnMouseWheel(const SDL_MouseWheelEvent &e) {
238         constexpr double roll_scale = PI * 0.0625;
239         constexpr double zoom_scale = -1.0;
240         constexpr double zoom_base = 1.125;
241         cam_orient.z = glm::clamp(cam_orient.z + double(e.x) * roll_scale, PI * -0.5, PI * 0.5);
242         cam_dist = std::max(0.125, cam_dist * std::pow(zoom_base, double(e.y) * zoom_scale));
243 }
244
245 void MasterState::OnRender(graphics::Viewport &viewport) {
246         cam.LookAt(glm::vec3(cam_pos), glm::vec3(cam_focus), glm::vec3(cam_up));
247         assets.shaders.planet_surface.Activate();
248         assets.shaders.planet_surface.SetV(cam.View());
249         assets.shaders.planet_surface.SetAmbient(glm::vec3(0.04, 0.05, 0.06));
250         assets.shaders.sky_box.Activate();
251         assets.shaders.sky_box.SetV(cam.View() * cam.Universe());
252         assets.shaders.sun_surface.Activate();
253         assets.shaders.sun_surface.SetV(cam.View());
254         assets.shaders.creature_skin.Activate();
255         assets.shaders.creature_skin.SetV(cam.View());
256         assets.shaders.creature_skin.SetAmbient(glm::vec3(0.04, 0.05, 0.06));
257
258         int num_lights = 0;
259         for (auto sun : sim.Suns()) {
260                 glm::vec3 pos(cam.View() * cam.Model(*sun)[3]);
261                 assets.shaders.planet_surface.Activate();
262                 assets.shaders.planet_surface.SetLight(num_lights, pos, glm::vec3(sun->Color()), float(sun->Luminosity()));
263                 assets.shaders.creature_skin.Activate();
264                 assets.shaders.creature_skin.SetLight(num_lights, pos, glm::vec3(sun->Color()), float(sun->Luminosity()));
265                 ++num_lights;
266                 if (num_lights >= graphics::PlanetSurface::MAX_LIGHTS || num_lights >= graphics::CreatureSkin::MAX_LIGHTS) {
267                         break;
268                 }
269         }
270         for (auto planet : sim.Planets()) {
271                 // TODO: indirect light from planets, calculate strength and get color somehow
272                 glm::vec3 pos(cam.View() * cam.Model(*planet)[3]);
273                 glm::vec3 col(1.0f, 1.0f, 1.0f);
274                 float str = 10.0f;
275                 assets.shaders.planet_surface.Activate();
276                 assets.shaders.planet_surface.SetLight(num_lights, pos, col, str);
277                 assets.shaders.creature_skin.Activate();
278                 assets.shaders.creature_skin.SetLight(num_lights, pos, col, str);
279                 ++num_lights;
280                 if (num_lights >= graphics::PlanetSurface::MAX_LIGHTS || num_lights >= graphics::CreatureSkin::MAX_LIGHTS) {
281                         break;
282                 }
283         }
284         assets.shaders.planet_surface.Activate();
285         assets.shaders.planet_surface.SetNumLights(num_lights);
286         assets.shaders.creature_skin.Activate();
287         assets.shaders.creature_skin.SetNumLights(num_lights);
288
289         assets.shaders.planet_surface.Activate();
290         assets.shaders.planet_surface.SetTexture(assets.textures.tiles);
291         for (auto planet : sim.Planets()) {
292                 assets.shaders.planet_surface.SetM(cam.Model(*planet));
293                 planet->Draw(assets, viewport);
294         }
295
296         assets.shaders.sun_surface.Activate();
297         for (auto sun : sim.Suns()) {
298                 double sun_radius = sun->Radius();
299                 assets.shaders.sun_surface.SetM(
300                         cam.Model(*sun) * glm::scale(glm::vec3(sun_radius, sun_radius, sun_radius)));
301                 assets.shaders.sun_surface.SetLight(glm::vec3(sun->Color()), float(sun->Luminosity()));
302                 assets.shaders.sun_surface.Draw();
303         }
304
305         assets.shaders.creature_skin.Activate();
306         assets.shaders.creature_skin.SetTexture(assets.textures.skins);
307         // TODO: extend to nearby bodies as well
308         for (auto c : cam.Reference().Creatures()) {
309                 assets.shaders.creature_skin.SetM(cam.Model(cam.Reference()) * glm::mat4(c->LocalTransform()));
310                 assets.shaders.creature_skin.SetBaseColor(glm::vec3(c->BaseColor()));
311                 assets.shaders.creature_skin.SetHighlightColor(glm::vec4(c->HighlightColor()));
312                 c->Draw(viewport);
313         }
314
315         assets.shaders.sky_box.Activate();
316         assets.shaders.sky_box.SetTexture(assets.textures.sky);
317         assets.shaders.sky_box.Draw();
318
319         viewport.ClearDepth();
320         bp.Draw(viewport);
321         cp.Draw(viewport);
322         rp.Draw(viewport);
323         tp.Draw(viewport);
324 }
325
326 }
327 }