jubilant-funicular
Camera2D.cpp
1 #include <algorithm>
2 #include <numeric>
3 #include <vector>
4 #include <tuple>
5 
6 #include "nta/Camera2D.h"
7 
8 namespace nta {
10  m_center.x = m_center.y = 0;
11  m_dimensions.x = m_dimensions.y = 100;
12  m_orientation = 0;
13  }
14  Camera2D::Camera2D(crvec2 center) {
15  m_center.x = center.x;
16  m_center.y = center.y;
17  m_dimensions.x = m_dimensions.y = 100;
18  m_orientation = 0;
19  }
20  Camera2D::Camera2D(crvec2 center, crvec2 dimensions) {
21  m_center.x = center.x;
22  m_center.y = center.y;
23  m_dimensions.x = dimensions.x;
24  m_dimensions.y = dimensions.y;
25  m_orientation = 0;
26  }
27  Camera2D::Camera2D(crvec2 center, crvec2 dimensions, float orientation) :
28  m_center(center), m_dimensions(dimensions), m_orientation(orientation) {
29  }
32  }
33  // glm takes matrices in column-major order
34  glm::mat3 Camera2D::getTranslationMatrix() const {
35  return glm::mat3(
36  1, 0, 0,
37  0, 1, 0,
38  -m_center.x, -m_center.y, 1
39  );
40  }
41  glm::mat3 Camera2D::getRotationMatrix() const {
42  return glm::mat3(
43  glm::cos(m_orientation), glm::sin(m_orientation), 0,
44  -glm::sin(m_orientation), glm::cos(m_orientation), 0,
45  0, 0, 1
46  );
47  }
48  glm::mat3 Camera2D::getInverseRotationMatrix() const {
49  return glm::mat3(
50  glm::cos(m_orientation), -glm::sin(m_orientation), 0,
51  glm::sin(m_orientation), glm::cos(m_orientation), 0,
52  0, 0, 1
53  );
54  }
55  glm::mat3 Camera2D::getDilationMatrix() const {
56  return glm::mat3(
57  1.f/m_dimensions.x, 0, 0,
58  0, 1.f/m_dimensions.y, 0,
59  0, 0, 1
60  );
61  }
62  glm::mat3 Camera2D::getCameraMatrix() const {
63  return getDilationMatrix() * getRotationMatrix() * getTranslationMatrix();
64  }
65  glm::mat3 Camera2D::getInverseCameraMatrix() const {
66  return glm::inverse(getCameraMatrix());
67  }
68  glm::vec4 Camera2D::getBoundsCenter() const {
69  return glm::vec4(m_center, m_dimensions);
70  }
71  glm::vec4 Camera2D::getBoundsTopLeft() const {
72  return glm::vec4(m_center.x-m_dimensions.x, m_center.y+m_dimensions.y, 2.f*m_dimensions);
73  }
74  glm::vec2 Camera2D::getCenter() const {
75  return m_center;
76  }
77  glm::vec2 Camera2D::getTopLeft() const {
78  glm::vec2 axes = getRotatedDimensions();
79  return glm::vec2(m_center.x-axes.x,m_center.y+axes.y);
80  }
81  glm::vec2 Camera2D::getDimensions() const {
82  return m_dimensions;
83  }
84  glm::vec2 Camera2D::getRotatedDimensions() const {
85  glm::vec3 axes = getRotationMatrix() * glm::vec3(m_dimensions, 1.);
86  return glm::vec2(axes.x, axes.y);
87  }
88  float Camera2D::getOrientation() const {
89  return m_orientation;
90  }
91  std::tuple<glm::vec2, glm::vec2> Camera2D::getAxes() const {
92  auto mat = getRotationMatrix();
93  glm::vec3 e1 = mat * glm::vec3(1,0,1), e2 = mat * glm::vec3(0,1,1);
94  return std::make_tuple(glm::vec2(e1.x, e1.y), glm::vec2(e2.x, e2.y));
95  }
96  glm::vec2 Camera2D::mouseToGame(crvec2 mouse, crvec2 windowDimensions) const {
97  // [a,b] -> [0,b-a] -> [0,d-c] -> [c,d]
98  glm::vec2 screen(mouse.x, windowDimensions.y - mouse.y);
99  screen = 2.f*screen/windowDimensions - 1.f;
100  return screenToGame(screen);
101  }
102  glm::vec2 Camera2D::screenToGame(crvec2 screen) const {
103  glm::vec3 orig = getInverseCameraMatrix() * glm::vec3(screen, 1);
104  return glm::vec2(orig.x, orig.y);
105  }
106  bool Camera2D::inBounds(crvec2 pt) const {
107  glm::vec2 transformed_pt = getInverseRotationMatrix() * glm::vec3(pt - m_center, 1);
108  return -m_dimensions.x <= transformed_pt.x && transformed_pt.x <= m_dimensions.x &&
109  -m_dimensions.y <= transformed_pt.y && transformed_pt.y <= m_dimensions.y;
110  }
111  bool Camera2D::inBounds(float x, float y) const {
112  return inBounds(glm::vec2(x, y));
113  }
114  // There's gotta be a better way to do this
115  bool Camera2D::isVisible(const std::vector<glm::vec2>& polygon) const {
116  std::vector<glm::vec2> transformed_poly(polygon.size());
117  std::transform(polygon.begin(), polygon.end(), transformed_poly.begin(), [&](crvec2 pt) {
118  return getInverseRotationMatrix() * glm::vec3(pt - m_center, 1);
119  });
120 
121  std::vector<glm::vec2> normals(polygon.size()+2);
122  normals[0] = glm::vec2(1,0);
123  normals[1] = glm::vec2(0,1);
124  for (int i = 0; i < polygon.size(); i++) {
125  glm::vec2& p1 = transformed_poly[i];
126  glm::vec2& p2 = transformed_poly[(i+1)%polygon.size()];
127  glm::vec2 side = p2 - p1;
128 
129  normals[i+2] = glm::vec2(-side.y, side.x);
130  }
131 
132  glm::vec2 cam_pts[4] = {
133  glm::vec2(-m_dimensions.x, m_dimensions.y),
134  glm::vec2(m_dimensions.x, m_dimensions.y),
135  glm::vec2(m_dimensions.x, -m_dimensions.y),
136  glm::vec2(-m_dimensions.x, -m_dimensions.y)
137  };
138  for (const auto& n : normals) {
139  auto cam_minmax = std::accumulate(cam_pts, cam_pts+4, std::make_pair(FLT_MAX, FLT_MIN),
140  [&](std::pair<float, float> p, crvec2 pt) {
141  float proj = glm::dot(n, pt);
142  p.first = std::min(p.first, proj);
143  p.second = std::max(p.second, proj);
144  return p;
145  });
146  auto poly_minmax = std::accumulate(transformed_poly.begin(), transformed_poly.end(), std::make_pair(FLT_MAX, FLT_MIN),
147  [&](std::pair<float, float> p, crvec2 pt) {
148  float proj = glm::dot(n, pt);
149  p.first = std::min(p.first, proj);
150  p.second = std::max(p.second, proj);
151  return p;
152  });
153 
154  if (cam_minmax.first > poly_minmax.second ||
155  poly_minmax.first > cam_minmax.second) {
156  return false;
157  }
158  }
159  return true;
160  }
161  void Camera2D::setCenter(crvec2 center) {
162  m_center = center;
163  }
164  void Camera2D::setCenter(float x, float y) {
165  m_center.x = x;
166  m_center.y = y;
167  }
168  void Camera2D::setDimensions(crvec2 dimensions) {
169  m_dimensions = dimensions;
170  }
171  void Camera2D::setDimensions(float w, float h) {
172  m_dimensions.x = w;
173  m_dimensions.y = h;
174  }
175  void Camera2D::setOrientation(float t) {
176  m_orientation = t;
177  }
178  void Camera2D::translateCenter(crvec2 translation, bool move_along_axis) {
179  if (move_along_axis) {
180  m_center += translation;
181  } else {
182  glm::vec3 move = getInverseRotationMatrix() * glm::vec3(translation, 1.);
183  m_center += glm::vec2(move.x, move.y); // Probably an easier way to do this
184  }
185  }
186  void Camera2D::translateCenter(float dx, float dy, bool move_along_axis) {
187  translateCenter(glm::vec2(dx, dy), move_along_axis);
188  }
189  void Camera2D::scaleDimensions(crvec2 dilation) {
190  m_dimensions *= dilation;
191  }
192  void Camera2D::scaleDimensions(float dw, float dh) {
193  m_dimensions.x *= dw;
194  m_dimensions.y *= dh;
195  }
196  void Camera2D::rotate(float dt) {
197  m_orientation += dt;
198  }
199 }
nta::Camera2D::m_center
glm::vec2 m_center
center of the camera's view in world coordinates
Definition: Camera2D.h:16
nta::Camera2D::scaleDimensions
void scaleDimensions(crvec2 dilation)
scales the camera's field of view
Definition: Camera2D.cpp:189
nta::Camera2D::inBounds
bool inBounds(crvec2 pt) const
Returns true if this pt is contained within the camera.
Definition: Camera2D.cpp:106
nta::Camera2D::isVisible
bool isVisible(const std::vector< glm::vec2 > &polygon) const
Returns ture if this polygon would show up in the camera.
Definition: Camera2D.cpp:115
nta::Camera2D::screenToGame
glm::vec2 screenToGame(crvec2 screen) const
Definition: Camera2D.cpp:102
nta::Camera2D::Camera2D
Camera2D()
Definition: Camera2D.cpp:9
nta::Camera2D::getBoundsCenter
glm::vec4 getBoundsCenter() const
returns camera bounds in the given format
Definition: Camera2D.cpp:68
nta::Camera2D::rotate
void rotate(float dt)
rotates the camera
Definition: Camera2D.cpp:196
nta::Camera2D::~Camera2D
~Camera2D()
destructor
Definition: Camera2D.cpp:30
nta::Camera2D::getAxes
std::tuple< glm::vec2, glm::vec2 > getAxes() const
returns (normalized) axes aligned with the camera
Definition: Camera2D.cpp:91
nta::Camera2D::m_dimensions
glm::vec2 m_dimensions
half dimensions of camera's view in world coordinates
Definition: Camera2D.h:18
nta
Definition: Animation2D.h:6
nta::Camera2D::m_orientation
float m_orientation
rotation about axis orthogonal to the world
Definition: Camera2D.h:20
nta::Camera2D::translateCenter
void translateCenter(crvec2 translation, bool move_along_axes=false)
Definition: Camera2D.cpp:178
nta::Camera2D::getCameraMatrix
glm::mat3 getCameraMatrix() const
Definition: Camera2D.cpp:62
nta::Camera2D::mouseToGame
glm::vec2 mouseToGame(crvec2 mouse, crvec2 windowDimensions) const
converts mouse coordinates to world coordinates
Definition: Camera2D.cpp:96
nta::Camera2D::setCenter
void setCenter(crvec2 center)
sets the values of the camera's fields
Definition: Camera2D.cpp:161
nta::Camera2D::getCenter
glm::vec2 getCenter() const
returns the center, top left coordinate, and (half) dimensions of the camera's view
Definition: Camera2D.cpp:74