Symphony Of Empires
orbit_camera.hpp
Go to the documentation of this file.
1 // Symphony of Empires
2 // Copyright (C) 2021, Symphony of Empires contributors
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <https://www.gnu.org/licenses/>.
16 //
17 // ----------------------------------------------------------------------------
18 // Name:
19 // client/orbit_camera.hpp
20 //
21 // Abstract:
22 // Does some important stuff.
23 // ----------------------------------------------------------------------------
24 
25 #pragma once
26 
27 #include <glm/vec3.hpp>
28 #include <glm/glm.hpp>
29 #include <glm/gtc/matrix_transform.hpp>
30 #include <glm/gtc/type_ptr.hpp>
31 #include <glm/gtx/intersect.hpp>
32 
33 #include "eng3d/camera.hpp"
34 #include "eng3d/value_chase.hpp"
35 
36 namespace Eng3D {
37  class OrbitCamera: public Camera {
38  public:
39  float radius;
41  float zoom_dist;
42  glm::vec3 target;
44 
45  OrbitCamera(glm::vec2 _screen_size, glm::vec2 _map_size, float _radius)
46  : Camera(_screen_size, _map_size), radius{ _radius }
47  {
48  circumference = _radius * 2 * glm::pi<float>();
49  map_position = glm::vec3(glm::pi<float>(), glm::pi<float>() * 0.5f, radius * 1.5f);
51  }
52 
53  OrbitCamera(const Camera& _camera, float _radius)
54  : Camera(_camera), radius{ _radius }
55  {
56  circumference = radius * 2 * glm::pi<float>();
59  }
60 
61  void move(float x_dir, float y_dir, float z_dir) override {
62  float scale = glm::abs(target.z * map_size.x / 1e5);
63  float camera_radius = radius + map_position.z / (map_size.x / 2.f) * circumference * 0.5f;
64  target.x += x_dir * scale * radius / camera_radius;
65  target.y += y_dir * scale * radius / camera_radius;
66  target.z += z_dir * scale;
67  target.z = glm::clamp(target.z, radius - 60.f, map_size.x / 2.f);
68  }
69 
70  void update() override {
72  map_position.y = glm::clamp(map_position.y, 0.f, map_size.y);
73  map_position.z = glm::clamp(map_position.z, 0.f, map_size.x / 2.f);
74 
75  const glm::vec3 normalized_pos = map_position / glm::vec3(map_size.x, map_size.y, map_size.x / 2.f);
76  glm::vec2 radiance_pos;
77  constexpr float pi = glm::pi<float>();
78  radiance_pos.x = glm::mod(normalized_pos.x * 2.f * pi, 2.f * pi);
79  radiance_pos.y = glm::max(0.f, glm::min(pi, normalized_pos.y * pi));
80 
81  // float distance = radius - normalized_pos.z * circumference * 0.5f;
82  const float distance = radius + normalized_pos.z * circumference * 0.5f;
83  zoom_dist = normalized_pos.z * circumference * 0.5f;
84  world_position.x = distance * cos(radiance_pos.x) * sin(radiance_pos.y);
85  world_position.y = distance * sin(radiance_pos.x) * sin(radiance_pos.y);
86  world_position.z = distance * cos(radiance_pos.y);
87  };
88 
89  void set_pos(float x, float y) override {
90  map_position.x = glm::mod(x, map_size.x);
91  map_position.y = glm::clamp(y, 0.f, map_size.y);
93  this->update();
94  }
95 
96  glm::mat4 get_view() const override {
97  glm::vec3 look_at = glm::vec3(0);
98  glm::vec3 up_vector = glm::vec3(0.f, -1.f, 0.f);
99 
100  glm::vec3 normalized_pos = map_position;
101  normalized_pos.x = glm::mod(normalized_pos.x, map_size.x);
102  normalized_pos = normalized_pos / glm::vec3(map_size.x, map_size.y, radius);
103  glm::vec2 radiance_pos;
104  constexpr float pi = glm::pi<float>();
105  radiance_pos.x = glm::mod(normalized_pos.x * 2.f * pi, 2.f * pi);
106  radiance_pos.y = glm::max(0.f, glm::min(pi, normalized_pos.y * pi));
107  up_vector.x = -cos(radiance_pos.x) * cos(radiance_pos.y);
108  up_vector.y = -sin(radiance_pos.x) * cos(radiance_pos.y);
109  up_vector.z = sin(radiance_pos.y);
110  // glm::vec3 look = glm::normalize(world_position) * radius;
111  // if(zoom_dist > radius)
112  // look = -look;
113  // return glm::lookAt(world_position, look, up_vector);
114  return glm::lookAt(world_position, look_at, up_vector);
115  };
116 
117  glm::vec3 get_map_pos() const override {
118  glm::vec3 out_pos = map_position;
119  out_pos.x = glm::mod(out_pos.x, map_size.x);
120  return out_pos;
121  };
122 
123  bool get_cursor_map_pos(glm::ivec2 mouse_pos, glm::ivec2& out_pos) const override {
124  float mouse_x = mouse_pos.x;
125  float mouse_y = screen_size.y - 1.f - mouse_pos.y;
126 
127  const glm::mat4 view = get_view();
128  const glm::mat4 projection = get_projection();
129  const glm::vec3 world_space_near = glm::unProject(
130  glm::vec3(mouse_x, mouse_y, 0.f),
131  view, projection,
132  glm::vec4(0.f, 0.f, screen_size)
133  );
134  const glm::vec3 world_space_far = glm::unProject(
135  glm::vec3(mouse_x, mouse_y, 1.f),
136  view, projection,
137  glm::vec4(0.f, 0.f, screen_size)
138  );
139 
140  glm::vec3 ray_direction = world_space_far - world_space_near;
141  ray_direction = glm::normalize(ray_direction);
142 
143  float distance = 0.f;
144  bool hit = glm::intersectRaySphere(world_space_near, ray_direction, glm::vec3(0, 0, 0), radius * radius, distance);
145 
146  glm::vec3 intersection_point = world_space_near + ray_direction * distance;
147  constexpr float pi = glm::pi<float>();
148  float y_rad = glm::acos(intersection_point.z / radius);
149  float x_rad = glm::atan(intersection_point.y, intersection_point.x);
150  x_rad += x_rad < 0 ? 2.f * pi : 0.f;
151 
152  out_pos.x = map_size.x * x_rad / (2.f * pi);
153  out_pos.y = map_size.y * y_rad / (pi);
154  return hit;
155  };
156 
157  glm::vec3 get_tile_world_pos(glm::vec2 tile_pos) const override {
158  constexpr float pi = glm::pi<float>();
159  const glm::vec2 normalized_pos = tile_pos / map_size;
160  glm::vec2 radiance_pos;
161  radiance_pos.x = glm::mod(normalized_pos.x * 2.f * pi, 2.f * pi);
162  radiance_pos.y = glm::max(0.f, glm::min(pi, normalized_pos.y * pi));
163 
164  const float distance = radius;
165  glm::vec3 out_pos;
166  out_pos.x = distance * cos(radiance_pos.x) * sin(radiance_pos.y);
167  out_pos.y = distance * sin(radiance_pos.x) * sin(radiance_pos.y);
168  out_pos.z = distance * cos(radiance_pos.y);
169  return out_pos;
170  }
171  };
172 }
glm::vec2 screen_size
Definition: camera.hpp:38
virtual glm::mat4 get_projection() const
Get the projection matrix.
Definition: camera.hpp:91
glm::vec3 world_position
Definition: camera.hpp:37
glm::vec2 map_size
Definition: camera.hpp:39
glm::vec3 map_position
Definition: camera.hpp:36
void update() override
Update the movement of the camera. Used for smooth camera movement.
bool get_cursor_map_pos(glm::ivec2 mouse_pos, glm::ivec2 &out_pos) const override
Get the cursors position on the map.
OrbitCamera(const Camera &_camera, float _radius)
void move(float x_dir, float y_dir, float z_dir) override
Move the camera in the specified direction. Uses the map coordinate system, where the Z-axis is the m...
glm::vec3 get_tile_world_pos(glm::vec2 tile_pos) const override
Get the tiles world position.
glm::mat4 get_view() const override
Get the view matrix.
OrbitCamera(glm::vec2 _screen_size, glm::vec2 _map_size, float _radius)
glm::vec3 get_map_pos() const override
Get the map position of the camera.
ValueChase< glm::vec3 > chase
void set_pos(float x, float y) override
Set the map position of the camera.
constexpr T move_towards(T current, T target)
Definition: value_chase.hpp:18