1#include "OrbitingCameraController.h"
3#include "Bridge/SceneFunctions.h"
8#include "ViewContext.h"
10#include "Editor/IEditor.h"
12#include "Components/Core/TransformComponent.h"
13#include "Components/Core/SceneComponent.h"
14#include "Components/Core/CameraComponent.h"
16#include "Input/KeyboardMapping.h"
17#include "Input/InputManager.h"
19#include "Renderer/CustomRenderer/ImguiRenderer.h"
21#include "Scene/RayPick.h"
22#include "Systems/Core/CameraSystem.h"
24#include "Utilities/CameraUtils.h"
26#include "Foundation/Platform/Mouse.h"
27#include "Foundation/Platform/Keyboard.h"
29#include <glm/vec2.hpp>
30#include <glm/vec3.hpp>
31#include <glm/vec4.hpp>
32#include <glm/mat4x4.hpp>
33#include <glm/ext/quaternion_float.hpp>
34#include <glm/gtx/compatibility.hpp>
41Cogs::Core::OrbitingCameraController::OrbitingCameraController()
46void Cogs::Core::OrbitingCameraController::registerType()
49 {
"initialize", &OrbitingCameraController::initialize },
50 {
"update", &OrbitingCameraController::update },
57 .setRange(-glm::pi<float>(), glm::pi<float>()),
59 .setRange(-2.0f * glm::pi<float>(), 2.0f * glm::pi<float>()),
62 .setRange(-glm::pi<float>(), glm::pi<float>()),
64 .setRange(-glm::pi<float>(), glm::pi<float>()),
74 DynamicComponent::registerDerivedType<OrbitingCameraController>().setFields(fields).setMethods(methods);
78void Cogs::Core::OrbitingCameraController::initialize(Context* ctx)
82 if (
auto camera = getComponent<CameraComponent>(); camera) {
83 for (ViewContext* v : context->getViews()) {
84 if (v->getCameraComponent() == camera) {
90 view = context->getDefaultView();
94void Cogs::Core::OrbitingCameraController::handleSeek(
float x,
float y)
96 if (
auto camera = getComponent<CameraComponent>(); camera) {
97 float viewportX = x - camera->viewportOrigin.x;
98 float viewportY = context->renderer->getSize().y - (int)camera->viewportOrigin.y - (y - (
int)camera->viewportOrigin.y) - 1;
99 if (viewportX >= 0 && viewportY >= 0 && viewportX < camera->viewportSize.x && viewportY < camera->viewportSize.y) {
100 std::vector<RayPicking::RayPickHit> picked;
101 context->rayPicking->pickCamera(context, *camera, glm::vec2(viewportX, viewportY), std::numeric_limits<float>::max(),
103 if (!picked.empty()) {
104 cameraTarget = context->transformSystem->worldFromEngineCoords(picked.front().position);
110void Cogs::Core::OrbitingCameraController::handleRotate(
float x0,
float y0,
float x1,
float y1,
bool first)
113 horizontalAngleStart = horizontalAngle;
114 verticalAngleStart = verticalAngle;
116 horizontalAngle = fmodf(horizontalAngleStart - (x1 - x0) * 0.005f, 2.0f * glm::pi<float>());
117 verticalAngle = glm::clamp(verticalAngleStart - (y1 - y0) * 0.005f, minVerticalAngle, maxVerticalAngle);
120void Cogs::Core::OrbitingCameraController::handleTranslate(
float x0,
float y0,
float x1,
float y1,
bool first)
122 if (first) { cameraTargetStart = cameraTarget; }
123 const CameraData& cameraData = context->cameraSystem->getData(getComponent<CameraComponent>());
126 glm::vec4 cameraTargetView = cameraData.viewMatrix * glm::vec4(context->transformSystem->engineFromWorldCoords(cameraTarget), 1.f);
127 if (cameraTargetView.z < 0.f) {
130 glm::vec4 cameraTargetClip = cameraData.projectionMatrix * cameraTargetView;
133 glm::vec2 viewportScale = (2.f * cameraTargetClip.w) / cameraData.viewportSize;
134 glm::vec2 p0 = viewportScale * (glm::vec2(x0, y0) - cameraData.viewportOrigin) - glm::vec2(1.f);
135 glm::vec2 p1 = viewportScale * (glm::vec2(x1, y1) - cameraData.viewportOrigin) - glm::vec2(1.f);
138 glm::vec4 ah = cameraData.inverseViewProjectionMatrix * glm::vec4(p0.x, -p0.y, cameraTargetClip.z, cameraTargetClip.w);
139 glm::vec3 a = (1.f / ah.w) * glm::vec3(ah);
141 glm::vec4 bh = cameraData.inverseViewProjectionMatrix * glm::vec4(p1.x, -p1.y, cameraTargetClip.z, cameraTargetClip.w);
142 glm::vec3 b = (1.f / bh.w) * glm::vec3(bh);
145 glm::dvec3 diff = glm::dvec3(b) - glm::dvec3(a);
146 if (glm::all(glm::isfinite(diff)) && glm::length(diff) != 0) {
147 cameraTarget = cameraTargetStart - diff;
152void Cogs::Core::OrbitingCameraController::handleDolly(
float delta,
bool first)
154 if (first) { distanceStart = distance; }
155 distance = glm::clamp(distanceStart * expf(-delta), minDistance, maxDistance);
158void Cogs::Core::OrbitingCameraController::handleZoom(
float delta,
bool first)
160 if (CameraComponent* camera = getComponent<CameraComponent>(); camera !=
nullptr) {
161 if (first) { fovStart = camera->fieldOfView; }
162 camera->fieldOfView = glm::clamp(fovStart * expf(-delta), minFOV, maxFOV);
166void Cogs::Core::OrbitingCameraController::update()
168 if(!enabled || view ==
nullptr)
return;
172 bool shiftIsDown = view->refKeyboard().isKeyDown(Key::Shift);
173 bool sIsDown = view->refKeyboard().isKeyDown(Key::S);
174 const std::vector<Gesture>& gestures = view->refGestures().getGestures();
175 for (
const Gesture& g : gestures) {
182 if (sIsDown) { handleSeek(g.tap.coord.x, g.tap.coord.y); }
185 handleSeek(g.doubleTap.coord.x, g.doubleTap.coord.y);
188 if (shiftIsDown) handleZoom(0.12f * g.wheel.delta,
true);
189 else handleDolly(0.12f * g.wheel.delta,
true);
192 switch (g.drag.button) {
193 case MouseButton::Middle: handleTranslate(g.drag.startCoord.x, g.drag.startCoord.y, g.drag.currCoord.x, g.drag.currCoord.y, started);
break;
194 case MouseButton::Left: handleRotate(g.drag.startCoord.x, g.drag.startCoord.y, g.drag.currCoord.x, g.drag.currCoord.y, started);
break;
195 case MouseButton::Right:
200 dragStartCoordY = g.drag.startCoord.y;
202 if (shiftIsDown != prevDragWithShift) {
203 prevDragWithShift = shiftIsDown;
204 dragStartCoordY = g.drag.currCoord.y;
208 if (shiftIsDown) handleZoom(0.01f * (g.drag.currCoord.y - dragStartCoordY), started);
209 else handleDolly(0.01f * (g.drag.currCoord.y - dragStartCoordY), started);
216 handleTranslate(g.pan.midStartCoord.x, g.pan.midStartCoord.y, g.pan.midCurrCoord.x, g.pan.midCurrCoord.y, started);
219 handleDolly(g.pinch.scale - 1.f, started);
229 glm::quat horizontalRotation = glm::angleAxis(horizontalAngle, glm::vec3(0, 0, 1));
230 glm::quat verticalRotation = glm::angleAxis(verticalAngle, glm::vec3(1, 0, 0));
231 glm::quat rotation = horizontalRotation * verticalRotation;
233 TransformComponent* transform = getComponent<TransformComponent>();
235 const glm::vec3 cameraDirection = rotation * glm::vec3(0, 0, 1);
236 const glm::dvec3 cameraPos = cameraTarget + glm::dvec3(distance *cameraDirection);
237 if (moveCamera && glm::all(glm::isfinite(cameraPos))) {
246 if (transform->coordinates != cameraPos || transform->position != glm::vec3(0.f)) {
247 transform->coordinates = cameraPos;
248 transform->position = glm::vec3(0.f);
249 transform->setChanged();
256 while (root && root->parent) {
257 root = root->parent.resolveComponent<SceneComponent>();
260 const TransformComponent* rootTransform = root->getComponent<TransformComponent>();
264 glm::vec3 cameraPosInRoot = glm::vec3(cameraPos - rootTransform->coordinates);
269 if (transform->position != cameraPosInRoot) {
270 transform->position = cameraPosInRoot;
271 transform->setChanged();
280 cameraLook = -cameraDirection;
281 const glm::vec3 cameraUp = rotation * glm::vec3(0, 1, 0);
282 const glm::quat cameraRot = glm::quat_cast(glm::inverse(glm::lookAt(cameraDirection, glm::vec3(0.f), cameraUp)));
283 if (cameraRot != transform->rotation) {
284 transform->rotation = cameraRot;
285 transform->setChanged();
static bool isUsingMouse()
Tests whether any ImGui control is being interacted with, or if the mouse is over an ImGui window or ...
float horizontalAngle
Current camera angle in horizontal direction [-TwoPI,+TwoPI] (glm::roll(transformComponent->rotation)...
float minDistance
Minimal distance from camera position to camera target (zoom).
float maxFOV
Maximal field of view.
float maxDistance
Maximal distance from camera position to camera target (zoom).
bool enabled
Navigation is only active when this flag is set.
float maxVerticalAngle
Maximum camera angle in vertical direction [-PI,+PI].
float minFOV
Minimal field of view.
float distance
Distance from camera target to camera.
glm::vec3 cameraLook
camera looking direction. Updated when navigating the camers.
bool seek
Deprecated Seeking flag - not used.
glm::dvec3 cameraTarget
Camera target. Define centre of camera rotation.
bool moveCamera
Set to false to disable controller moving the camera and only apply rotate and zoom....
float minVerticalAngle
Minimum camera angle in vertical direction [-PI,+PI].
float verticalAngle
Current camera angle in vertical direction [-PI,+PI] (glm::pitch(transformComponent->rotation))
Field definition describing a single data member of a data structure.
static FieldWrapper< Field, FieldType > create(const Name &name, FieldType ClassType::*field)
Creates a new field instance, returning a wrapper for type safe continuation style setup.
Simple method definition.
@ Closest
Return just the closest hit.
@ None
No flags specified,.
Contains reflection support.
Handle to a Component instance.
ComponentType * resolveComponent() const
@ Press
A long press, a press that was too long to be a tap, events on press start and end.
@ Pinch
Two-finger movement where the two fingers comes closer or farther away, see pinchThreshold,...
@ Tap
A short press, see tapMaxDuration, single-fire event.
@ Swipe
Swift pointer movement with button pressed or touch, see swipeMaxDuration, mouseVelocityThreshold and...
@ Rotate
Two-finger rotation movement where fingers are roughly equidistant, see rotateThreshold,...
@ DoubleTap
Two taps in quick succession, see doubleTapTreshold, single-fire event.
@ Wheel
Movemement of the mouse wheel, single-fire event.
@ Drag
Pointer movement with a button pressed or touch, see mouseMoveThreshold and touchMoveThreshold,...
@ Hover
Mouse pointer hovers over window without any buttons pressed, single-fire event.
@ Pan
Two-finger touch movement where fingers are roughly equidistant, see mouseMoveThreshold and touchMove...
@ Ended
Gesture ended or single-fire events, used by all gestures.
@ Started
Gesture started, used by: Press, Drag, Swipe, Pan, Rotate, Pinch.