2023-06-20 04:33:09 +00:00
|
|
|
#ifndef STAR_WORLD_CAMERA_HPP
|
|
|
|
#define STAR_WORLD_CAMERA_HPP
|
|
|
|
|
|
|
|
#include "StarWorldGeometry.hpp"
|
|
|
|
#include "StarGameTypes.hpp"
|
2023-06-28 21:05:01 +00:00
|
|
|
#include "StarInterpolation.hpp"
|
2023-06-20 04:33:09 +00:00
|
|
|
|
|
|
|
namespace Star {
|
|
|
|
|
|
|
|
class WorldCamera {
|
|
|
|
public:
|
|
|
|
void setScreenSize(Vec2U screenSize);
|
|
|
|
Vec2U screenSize() const;
|
|
|
|
|
2023-06-28 21:05:01 +00:00
|
|
|
void setTargetPixelRatio(float targetPixelRatio);
|
|
|
|
void setPixelRatio(float pixelRatio);
|
|
|
|
float pixelRatio() const;
|
2023-06-20 04:33:09 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
// Returns the actual camera position.
|
|
|
|
void setCenterWorldPosition(Vec2F const& 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 const& worldCoord) const;
|
|
|
|
|
|
|
|
// Assumes top left corner of screen is (0, 0) in screen coordinates.
|
|
|
|
Vec2F screenToWorld(Vec2F const& 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;
|
|
|
|
|
2023-06-28 21:05:01 +00:00
|
|
|
void update(float dt);
|
|
|
|
|
2023-06-20 04:33:09 +00:00
|
|
|
private:
|
|
|
|
WorldGeometry m_worldGeometry;
|
|
|
|
Vec2U m_screenSize;
|
2023-06-28 21:05:01 +00:00
|
|
|
float m_pixelRatio = 1.0f;
|
|
|
|
float m_targetPixelRatio = 1.0f;
|
2023-06-20 04:33:09 +00:00
|
|
|
Vec2F m_worldCenter;
|
|
|
|
};
|
|
|
|
|
|
|
|
inline void WorldCamera::setScreenSize(Vec2U screenSize) {
|
|
|
|
m_screenSize = screenSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Vec2U WorldCamera::screenSize() const {
|
|
|
|
return m_screenSize;
|
|
|
|
}
|
|
|
|
|
2023-06-28 21:05:01 +00:00
|
|
|
inline void WorldCamera::setTargetPixelRatio(float targetPixelRatio) {
|
|
|
|
m_targetPixelRatio = targetPixelRatio;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void WorldCamera::setPixelRatio(float pixelRatio) {
|
|
|
|
m_pixelRatio = m_targetPixelRatio = pixelRatio;
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:05:01 +00:00
|
|
|
inline float WorldCamera::pixelRatio() const {
|
2023-06-20 04:33:09 +00:00
|
|
|
return m_pixelRatio;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void WorldCamera::setWorldGeometry(WorldGeometry geometry) {
|
2024-02-19 15:55:19 +00:00
|
|
|
m_worldGeometry = std::move(geometry);
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline WorldGeometry WorldCamera::worldGeometry() const {
|
|
|
|
return m_worldGeometry;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Vec2F WorldCamera::centerWorldPosition() const {
|
|
|
|
return Vec2F(m_worldCenter);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Vec2F WorldCamera::worldToScreen(Vec2F const& worldCoord) const {
|
|
|
|
Vec2F wrappedCoord = m_worldGeometry.nearestTo(Vec2F(m_worldCenter), worldCoord);
|
|
|
|
return Vec2F(
|
|
|
|
(wrappedCoord[0] - m_worldCenter[0]) * (TilePixels * m_pixelRatio) + m_screenSize[0] / 2.0,
|
|
|
|
(wrappedCoord[1] - m_worldCenter[1]) * (TilePixels * m_pixelRatio) + m_screenSize[1] / 2.0
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Vec2F WorldCamera::screenToWorld(Vec2F const& screen) const {
|
|
|
|
return Vec2F(
|
|
|
|
(screen[0] - m_screenSize[0] / 2.0) / (TilePixels * m_pixelRatio) + m_worldCenter[0],
|
|
|
|
(screen[1] - 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);
|
|
|
|
}
|
|
|
|
|
2023-06-28 21:05:01 +00:00
|
|
|
inline void WorldCamera::update(float dt) {
|
|
|
|
m_pixelRatio = lerp(exp(-20.0f * dt), m_targetPixelRatio, m_pixelRatio);
|
|
|
|
}
|
|
|
|
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|