1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
#pragma once
#include "StarWorldGeometry.hpp"
#include "StarGameTypes.hpp"
#include "StarInterpolation.hpp"
namespace Star {
class WorldCamera {
public:
void setScreenSize(Vec2U screenSize);
Vec2U screenSize() const;
void setTargetPixelRatio(float targetPixelRatio);
void setPixelRatio(float pixelRatio);
float pixelRatio() const;
void setWorldGeometry(WorldGeometry geometry);
WorldGeometry worldGeometry() const;
// Set the camera center position (in world space) to as close to the given
// location as possible while keeping the screen within world bounds.
void setCenterWorldPosition(Vec2F position, bool force = false);
// Returns the actual camera position.
Vec2F centerWorldPosition() const;
// Transforms world coordinates into one set of screen coordinates. Since
// the world is non-euclidean, one world coordinate can transform to
// potentially an infinite number of screen coordinates. This will retrun
// the closest to the center of the screen.
Vec2F worldToScreen(Vec2F worldCoord) const;
// Assumes top left corner of screen is (0, 0) in screen coordinates.
Vec2F screenToWorld(Vec2F screen) const;
// Returns screen dimensions in world space.
RectF worldScreenRect() const;
// Returns tile dimensions of the tiles that overlap with the screen
RectI worldTileRect() const;
// Returns the position of the lower left corner of the lower left tile of
// worldTileRect, in screen coordinates.
Vec2F tileMinScreen() const;
void update(float dt);
private:
WorldGeometry m_worldGeometry;
Vec2U m_screenSize;
float m_pixelRatio = 1.0f;
float m_targetPixelRatio = 1.0f;
Vec2F m_worldCenter;
Vec2F m_rawWorldCenter;
};
inline void WorldCamera::setScreenSize(Vec2U screenSize) {
m_screenSize = screenSize;
}
inline Vec2U WorldCamera::screenSize() const {
return m_screenSize;
}
inline void WorldCamera::setTargetPixelRatio(float targetPixelRatio) {
m_targetPixelRatio = targetPixelRatio;
}
inline void WorldCamera::setPixelRatio(float pixelRatio) {
m_pixelRatio = m_targetPixelRatio = pixelRatio;
}
inline float WorldCamera::pixelRatio() const {
return m_pixelRatio;
}
inline void WorldCamera::setWorldGeometry(WorldGeometry geometry) {
m_worldGeometry = std::move(geometry);
}
inline WorldGeometry WorldCamera::worldGeometry() const {
return m_worldGeometry;
}
inline Vec2F WorldCamera::centerWorldPosition() const {
return Vec2F(m_worldCenter);
}
inline Vec2F WorldCamera::worldToScreen(Vec2F worldCoord) const {
Vec2F wrappedCoord = m_worldGeometry.nearestTo(Vec2F(m_worldCenter), worldCoord);
return Vec2F(
(wrappedCoord[0] - m_worldCenter[0]) * (TilePixels * m_pixelRatio) + (float)m_screenSize[0] / 2.0,
(wrappedCoord[1] - m_worldCenter[1]) * (TilePixels * m_pixelRatio) + (float)m_screenSize[1] / 2.0
);
}
inline Vec2F WorldCamera::screenToWorld(Vec2F screen) const {
return Vec2F(
(screen[0] - (float)m_screenSize[0] / 2.0) / (TilePixels * m_pixelRatio) + m_worldCenter[0],
(screen[1] - (float)m_screenSize[1] / 2.0) / (TilePixels * m_pixelRatio) + m_worldCenter[1]
);
}
inline RectF WorldCamera::worldScreenRect() const {
// screen dimensions in world space
float w = (float)m_screenSize[0] / (TilePixels * m_pixelRatio);
float h = (float)m_screenSize[1] / (TilePixels * m_pixelRatio);
return RectF::withSize(Vec2F(m_worldCenter[0] - w / 2, m_worldCenter[1] - h / 2), Vec2F(w, h));
}
inline RectI WorldCamera::worldTileRect() const {
RectF screen = worldScreenRect();
Vec2I min = Vec2I::floor(screen.min());
Vec2I size = Vec2I::ceil(Vec2F(m_screenSize) / (TilePixels * m_pixelRatio) + (screen.min() - Vec2F(min)));
return RectI::withSize(min, size);
}
inline Vec2F WorldCamera::tileMinScreen() const {
RectF screenRect = worldScreenRect();
RectI tileRect = worldTileRect();
return (Vec2F(tileRect.min()) - screenRect.min()) * (TilePixels * m_pixelRatio);
}
inline void WorldCamera::update(float dt) {
float newPixelRatio = lerp(exp(-20.0f * dt), m_targetPixelRatio, m_pixelRatio);
if (abs(newPixelRatio - m_targetPixelRatio) < 0.0125f)
newPixelRatio = m_targetPixelRatio;
if (m_pixelRatio != newPixelRatio) {
m_pixelRatio = newPixelRatio;
setCenterWorldPosition(m_rawWorldCenter, true);
}
}
}
|