Repository navigation
Expand file tree
/
Copy pathCameraComponent.cpp
More file actions
131 lines (114 loc) · 4.7 KB
/
Copy pathCameraComponent.cpp
File metadata and controls
131 lines (114 loc) · 4.7 KB
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
#include "CameraComponent.h"
#include <deki/Object.h>
#include <deki/Engine.h>
#include <deki/ICamera.h>
#include <deki/ScreenScale.h>
#include <deki/ComponentInterfaceAdapters.h>
namespace DekiRendering
{
// Registers the ICamera adapter so the editor can use FindInterface<ICamera>().
static struct CameraInterfaceRegistrar
{
CameraInterfaceRegistrar()
{
Deki::ComponentInterfaceAdapters::Register(
Deki::ICamera::kInterfaceID, ::Deki::TypeId<CameraComponent>(),
[](Deki::Component* c) -> void* { return static_cast<Deki::ICamera*>(static_cast<CameraComponent*>(c)); });
}
} s_CameraInterfaceReg;
CameraComponent::CameraComponent()
{
}
float CameraComponent::GetPixelsPerMeter(int bufferWidth, int bufferHeight) const
{
if (m_FixedPixelsPerMeter > 0.0f)
{
return m_FixedPixelsPerMeter;
}
(void)bufferWidth; // the height is fixed; the width follows the screen
return Deki::OrthoPixelsPerMeter(bufferHeight, orthoHeight, pixelPerfect,
Deki::EngineSettings::Global().pixelsPerMeter);
}
float CameraComponent::GetPositionX() const
{
Deki::Object* owner = GetOwner();
return owner ? owner->GetWorldX() : 0.0f;
}
float CameraComponent::GetPositionY() const
{
Deki::Object* owner = GetOwner();
return owner ? owner->GetWorldY() : 0.0f;
}
float CameraComponent::GetVisibleWidth(int32_t bufferWidth, int32_t bufferHeight) const
{
const float ppm = GetPixelsPerMeter(bufferWidth, bufferHeight);
return (ppm > 0.0f) ? (static_cast<float>(bufferWidth) / ppm) : 0.0f;
}
float CameraComponent::GetVisibleHeight(int32_t bufferWidth, int32_t bufferHeight) const
{
const float ppm = GetPixelsPerMeter(bufferWidth, bufferHeight);
return (ppm > 0.0f) ? (static_cast<float>(bufferHeight) / ppm) : 0.0f;
}
FrameCamera CameraComponent::CaptureFrameCamera(int screenWidth, int screenHeight) const
{
// World: meters, centre origin, Y up. Screen: top-left origin, Y down.
// The camera position is the world point that maps to the screen centre.
FrameCamera fc;
fc.ppm = GetPixelsPerMeter(screenWidth, screenHeight);
fc.camX = GetPositionX();
fc.camY = GetPositionY();
fc.halfW = static_cast<float>(screenWidth) * 0.5f;
fc.halfH = static_cast<float>(screenHeight) * 0.5f;
// Pixel Perfect: the camera sits on the art-pixel grid and the centre on a
// whole screen pixel, so every art pixel covers the same block of screen
// pixels however the camera moves. The scene view's fixed scale is not a
// screen and is not snapped.
const float art = Deki::EngineSettings::Global().pixelsPerMeter;
if (pixelPerfect && m_FixedPixelsPerMeter <= 0.0f && art > 0.0f && fc.ppm > 0.0f)
{
fc.camX = std::round(fc.camX * art) / art;
fc.camY = std::round(fc.camY * art) / art;
fc.halfW = std::floor(fc.halfW);
fc.halfH = std::floor(fc.halfH);
fc.snapStep = static_cast<int32_t>(std::lround(fc.ppm / art));
}
fc.valid = fc.ppm > 0.0f;
return fc;
}
Deki::Mat4 CameraComponent::GetProjectionMatrix(int bufferWidth, int bufferHeight) const
{
if (bufferWidth <= 0 || bufferHeight <= 0)
{
return Deki::Mat4::Identity();
}
if (projection == Deki::ProjectionMode::Perspective)
{
constexpr float kDegToRad = 3.14159265358979f / 180.0f;
return Deki::Mat4::Perspective(fieldOfView * kDegToRad,
static_cast<float>(bufferWidth) / static_cast<float>(bufferHeight), nearPlane,
farPlane);
}
const float ppm = GetPixelsPerMeter(bufferWidth, bufferHeight);
if (ppm <= 0.0f)
{
return Deki::Mat4::Identity();
}
const float halfW = (static_cast<float>(bufferWidth) * 0.5f) / ppm;
const float halfH = (static_cast<float>(bufferHeight) * 0.5f) / ppm;
return Deki::Mat4::Ortho(-halfW, halfW, -halfH, halfH, -1.0f, 1.0f);
}
void CameraComponent::WorldToScreen(float worldX, float worldY, int screenWidth, int screenHeight, float& screenX,
float& screenY) const
{
CaptureFrameCamera(screenWidth, screenHeight).WorldToScreen(worldX, worldY, screenX, screenY);
}
void CameraComponent::ScreenToWorld(float screenX, float screenY, int screenWidth, int screenHeight, float& worldX,
float& worldY) const
{
// Inverse of WorldToScreen, through the same snapshot.
const FrameCamera fc = CaptureFrameCamera(screenWidth, screenHeight);
const float inv = (fc.ppm > 0.0f) ? (1.0f / fc.ppm) : 0.0f;
worldX = (screenX - fc.halfW) * inv + fc.camX;
worldY = -(screenY - fc.halfH) * inv + fc.camY; // screen Y down -> world Y up
}
} // namespace DekiRendering