]> git.localhorst.tv Git - blobs.git/blob - src/app/states.cpp
allow clicking celestial bodies
[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/transform.hpp>
13
14 #include <iostream>
15 #include <glm/gtx/io.hpp>
16
17
18 namespace blobs {
19 namespace app {
20
21 MasterState::MasterState(Assets &assets, world::Simulation &sim) noexcept
22 : State()
23 , assets(assets)
24 , sim(sim)
25 , cam(sim.Root())
26 , cam_dist(5.0)
27 , cam_tgt_dist(5.0)
28 , cam_orient(PI * 0.375, PI * 0.25, 0.0)
29 , cam_dragging(false)
30 , bp(assets)
31 , cp(assets)
32 , rp(sim)
33 , tp(sim)
34 , remain(0)
35 , thirds(0)
36 , paused(false) {
37         bp.ZIndex(10.0f);
38         cp.ZIndex(20.0f);
39         rp.ZIndex(30.0f);
40         tp.ZIndex(40.0f);
41 }
42
43 MasterState::~MasterState() noexcept {
44 }
45
46
47 void MasterState::OnResize(int w, int h) {
48         assets.shaders.canvas.Activate();
49         assets.shaders.canvas.Resize(float(w), float(h));
50         assets.shaders.alpha_sprite.Activate();
51         assets.shaders.alpha_sprite.SetVP(glm::mat4(1.0f), glm::ortho(0.0f, float(w), float(h), 0.0f, 1.0e4f, -1.0e4f));
52
53         cam.Aspect(float(w), float(h));
54         assets.shaders.planet_surface.Activate();
55         assets.shaders.planet_surface.SetVP(cam.View(), cam.Projection());
56         assets.shaders.sun_surface.Activate();
57         assets.shaders.sun_surface.SetVP(cam.View(), cam.Projection());
58         assets.shaders.creature_skin.Activate();
59         assets.shaders.creature_skin.SetVP(cam.View(), cam.Projection());
60 }
61
62 void MasterState::OnUpdate(int dt) {
63         remain += dt;
64 #ifdef NDEBUG
65         int max_tick = 10;
66 #else
67         // one tick per frame when debugging so pausing execution doesn't result in more ticks
68         int max_tick = 1;
69 #endif
70         while (remain >= FrameMS() && max_tick > 0) {
71                 Tick();
72                 --max_tick;
73         }
74 }
75
76 void MasterState::Tick() {
77         constexpr double dt = 0.01666666666666666666666666666666;
78         if (!paused) {
79                 sim.Tick(dt);
80         }
81         remain -= FrameMS();
82         thirds = (thirds + 1) % 3;
83
84         double cam_diff = cam_tgt_dist - cam_dist;
85         if (std::abs(cam_diff) > 0.001) {
86                 cam_dist += cam_diff * 0.25;
87         } else {
88                 cam_dist = cam_tgt_dist;
89         }
90 }
91
92 int MasterState::FrameMS() const noexcept {
93         return thirds == 0 ? 16 : 17;
94 }
95
96
97 void MasterState::OnKeyDown(const SDL_KeyboardEvent &e) {
98         if (e.keysym.sym == SDLK_p) {
99                 paused = !paused;
100         } else if (e.keysym.sym == SDLK_F1) {
101                 rp.Toggle();
102         }
103 }
104
105 void MasterState::OnMouseDown(const SDL_MouseButtonEvent &e) {
106         if (e.button == SDL_BUTTON_RIGHT) {
107                 SDL_SetRelativeMouseMode(SDL_TRUE);
108                 cam_dragging = true;
109         }
110 }
111
112 void MasterState::OnMouseUp(const SDL_MouseButtonEvent &e) {
113         if (e.button == SDL_BUTTON_LEFT) {
114                 glm::dmat4 inverse(glm::inverse(cam.Projection() * cam.View()));
115                 math::Ray ray(inverse * App().GetViewport().ShootPixel(e.x, e.y));
116
117                 creature::Creature *closest_creature = nullptr;
118                 double closest_dist = std::numeric_limits<double>::infinity();
119                 for (creature::Creature *c : sim.LiveCreatures()) {
120                         glm::dvec3 normal(0.0);
121                         double dist = 0.0;
122                         if (Intersect(ray, c->CollisionBounds(), glm::dmat4(cam.Model(c->GetSituation().GetPlanet())) * c->CollisionTransform(), normal, dist)
123                                 && dist < closest_dist) {
124                                 closest_creature = c;
125                                 closest_dist = dist;
126                         }
127                 }
128
129                 world::Body *closest_body = nullptr;
130                 for (world::Body *b : sim.Bodies()) {
131                         glm::dvec3 normal(0.0);
132                         double dist = 0.0;
133                         if (Intersect(ray, glm::dmat4(cam.Model(*b)) * b->CollisionBounds(), normal, dist) && dist < closest_dist) {
134                                 closest_creature = nullptr;
135                                 closest_body = b;
136                                 closest_dist = dist;
137                         }
138                 }
139
140                 if (closest_creature) {
141                         cp.Show(*closest_creature);
142                         bp.Hide();
143                 } else if (closest_body) {
144                         bp.Show(*closest_body);
145                         cp.Hide();
146                 } else {
147                         cp.Hide();
148                         bp.Hide();
149                 }
150         } else if (e.button == SDL_BUTTON_RIGHT) {
151                 SDL_SetRelativeMouseMode(SDL_FALSE);
152                 cam_dragging = false;
153         }
154 }
155
156 void MasterState::OnMouseMotion(const SDL_MouseMotionEvent &e) {
157         constexpr double pitch_scale = PI * 0.001;
158         constexpr double yaw_scale = PI * 0.002;
159         if (cam_dragging) {
160                 cam_orient.x = glm::clamp(cam_orient.x + double(e.yrel) * pitch_scale, 0.0, PI * 0.499);
161                 cam_orient.y = std::fmod(cam_orient.y + double(e.xrel) * yaw_scale, PI * 2.0);
162         }
163 }
164
165 void MasterState::OnMouseWheel(const SDL_MouseWheelEvent &e) {
166         constexpr double roll_scale = PI * 0.0625;
167         constexpr double zoom_scale = -1.0;
168         constexpr double zoom_base = 1.125;
169         cam_orient.z = glm::clamp(cam_orient.z + double(e.x) * roll_scale, PI * -0.5, PI * 0.5);
170         if (cp.Shown()) {
171                 cam_tgt_dist = std::max(cp.GetCreature().Size() * 2.0, cam_tgt_dist * std::pow(zoom_base, double(e.y) * zoom_scale));
172         } else {
173                 cam_tgt_dist = std::max(1.0, cam_tgt_dist * std::pow(zoom_base, double(e.y) * zoom_scale));
174         }
175 }
176
177 void MasterState::OnRender(graphics::Viewport &viewport) {
178         if (cp.Shown()) {
179                 cam.Radial(cp.GetCreature(), cam_dist, cam_orient);
180                 assets.shaders.planet_surface.Activate();
181                 assets.shaders.planet_surface.SetV(cam.View());
182                 assets.shaders.sun_surface.Activate();
183                 assets.shaders.sun_surface.SetV(cam.View());
184                 assets.shaders.creature_skin.Activate();
185                 assets.shaders.creature_skin.SetV(cam.View());
186         }
187
188         int num_lights = 0;
189         for (auto sun : sim.Suns()) {
190                 // TODO: source sun's light color and strength
191                 glm::vec3 pos(cam.View() * cam.Model(*sun)[3]);
192                 glm::vec3 col(1.0f, 1.0f, 1.0f);
193                 float str = 1.0e6f;
194                 assets.shaders.planet_surface.Activate();
195                 assets.shaders.planet_surface.SetLight(num_lights, pos, col, str);
196                 assets.shaders.creature_skin.Activate();
197                 assets.shaders.creature_skin.SetLight(num_lights, pos, col, str);
198                 ++num_lights;
199                 if (num_lights >= graphics::PlanetSurface::MAX_LIGHTS || num_lights >= graphics::CreatureSkin::MAX_LIGHTS) {
200                         break;
201                 }
202         }
203         for (auto planet : sim.Planets()) {
204                 // TODO: indirect light from planets, calculate strength and get color somehow
205                 glm::vec3 pos(cam.View() * cam.Model(*planet)[3]);
206                 glm::vec3 col(1.0f, 1.0f, 1.0f);
207                 float str = 10.0f;
208                 assets.shaders.planet_surface.Activate();
209                 assets.shaders.planet_surface.SetLight(num_lights, pos, col, str);
210                 assets.shaders.creature_skin.Activate();
211                 assets.shaders.creature_skin.SetLight(num_lights, pos, col, str);
212                 ++num_lights;
213                 if (num_lights >= graphics::PlanetSurface::MAX_LIGHTS || num_lights >= graphics::CreatureSkin::MAX_LIGHTS) {
214                         break;
215                 }
216         }
217         assets.shaders.planet_surface.Activate();
218         assets.shaders.planet_surface.SetNumLights(num_lights);
219         assets.shaders.creature_skin.Activate();
220         assets.shaders.creature_skin.SetNumLights(num_lights);
221
222         assets.shaders.planet_surface.Activate();
223         assets.shaders.planet_surface.SetTexture(assets.textures.tiles);
224         for (auto planet : sim.Planets()) {
225                 assets.shaders.planet_surface.SetM(cam.Model(*planet));
226                 planet->Draw(assets, viewport);
227         }
228
229         assets.shaders.sun_surface.Activate();
230         for (auto sun : sim.Suns()) {
231                 double sun_radius = sun->Radius();
232                 assets.shaders.sun_surface.SetM(
233                         cam.Model(*sun) * glm::scale(glm::vec3(sun_radius, sun_radius, sun_radius)));
234                 assets.shaders.sun_surface.SetLight(glm::vec3(1.0f, 1.0f, 1.0f), 1.0e6f);
235                 assets.shaders.sun_surface.Draw();
236         }
237
238         assets.shaders.creature_skin.Activate();
239         assets.shaders.creature_skin.SetTexture(assets.textures.skins);
240         // TODO: extend to nearby bodies as well
241         for (auto c : cam.Reference().Creatures()) {
242                 assets.shaders.creature_skin.SetM(cam.Model(cam.Reference()) * glm::mat4(c->LocalTransform()));
243                 assets.shaders.creature_skin.SetBaseColor(glm::vec3(c->BaseColor()));
244                 assets.shaders.creature_skin.SetHighlightColor(glm::vec4(c->HighlightColor()));
245                 c->Draw(viewport);
246         }
247
248         viewport.ClearDepth();
249         bp.Draw(viewport);
250         cp.Draw(viewport);
251         rp.Draw(viewport);
252         tp.Draw(viewport);
253 }
254
255 }
256 }