diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index 9d19b8fb0f..4cfa4d6156 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -14,6 +14,7 @@ #include #include #include +#include using RenderArgsPointer = std::shared_ptr; @@ -48,6 +49,47 @@ public: _farClipPlaneDistance = config.farClipPlaneDistance; _textureWidth = config.textureWidth; _textureHeight = config.textureHeight; + _mirrorProjection = config.mirrorProjection; + } + + void setMirrorProjection(ViewFrustum& srcViewFrustum) { + if (_attachedEntityId.isNull()) { + qWarning() << "ERROR: Cannot set mirror projection for SecondaryCamera without an attachedEntityId set."; + return; + } + + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_attachedEntityId, + _attachedEntityPropertyFlags); + glm::vec3 mirrorPropertiesPosition = entityProperties.getPosition(); + glm::quat mirrorPropertiesRotation = entityProperties.getRotation(); + glm::vec3 mirrorPropertiesDimensions = entityProperties.getDimensions(); + glm::vec3 halfMirrorPropertiesDimensions = 0.5f * mirrorPropertiesDimensions; + + // setup mirror from world as inverse of world from mirror transformation using inverted x and z for mirrored image + // TODO: we are assuming here that UP is world y-axis + glm::mat4 worldFromMirrorRotation = glm::mat4_cast(mirrorPropertiesRotation) * glm::scale(vec3(-1.0f, 1.0f, -1.0f)); + glm::mat4 worldFromMirrorTranslation = glm::translate(mirrorPropertiesPosition); + glm::mat4 worldFromMirror = worldFromMirrorTranslation * worldFromMirrorRotation; + glm::mat4 mirrorFromWorld = glm::inverse(worldFromMirror); + + // get mirror camera position by reflecting main camera position's z coordinate in mirror space + glm::vec3 mainCameraPositionWorld = qApp->getCamera().getPosition(); + glm::vec3 mainCameraPositionMirror = vec3(mirrorFromWorld * vec4(mainCameraPositionWorld, 1.0f)); + glm::vec3 mirrorCameraPositionMirror = vec3(mainCameraPositionMirror.x, mainCameraPositionMirror.y, + -mainCameraPositionMirror.z); + glm::vec3 mirrorCameraPositionWorld = vec3(worldFromMirror * vec4(mirrorCameraPositionMirror, 1.0f)); + + // set frustum position to be mirrored camera and set orientation to mirror's adjusted rotation + glm::quat mirrorCameraOrientation = glm::quat_cast(worldFromMirrorRotation); + srcViewFrustum.setPosition(mirrorCameraPositionWorld); + srcViewFrustum.setOrientation(mirrorCameraOrientation); + + // build frustum using mirror space translation of mirrored camera + float nearClip = mirrorCameraPositionMirror.z + mirrorPropertiesDimensions.z * 2.0f; + glm::vec3 upperRight = halfMirrorPropertiesDimensions - mirrorCameraPositionMirror; + glm::vec3 bottomLeft = -halfMirrorPropertiesDimensions - mirrorCameraPositionMirror; + glm::mat4 frustum = glm::frustum(bottomLeft.x, upperRight.x, bottomLeft.y, upperRight.y, nearClip, _farClipPlaneDistance); + srcViewFrustum.setProjection(frustum); } void run(const render::RenderContextPointer& renderContext, RenderArgsPointer& cachedArgs) { @@ -71,15 +113,22 @@ public: }); auto srcViewFrustum = args->getViewFrustum(); - if (!_attachedEntityId.isNull()) { - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_attachedEntityId, _attachedEntityPropertyFlags); - srcViewFrustum.setPosition(entityProperties.getPosition()); - srcViewFrustum.setOrientation(entityProperties.getRotation()); + if (_mirrorProjection) { + setMirrorProjection(srcViewFrustum); } else { - srcViewFrustum.setPosition(_position); - srcViewFrustum.setOrientation(_orientation); + if (!_attachedEntityId.isNull()) { + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_attachedEntityId, + _attachedEntityPropertyFlags); + srcViewFrustum.setPosition(entityProperties.getPosition()); + srcViewFrustum.setOrientation(entityProperties.getRotation()); + } else { + srcViewFrustum.setPosition(_position); + srcViewFrustum.setOrientation(_orientation); + } + srcViewFrustum.setProjection(glm::perspective(glm::radians(_vFoV), + ((float)args->_viewport.z / (float)args->_viewport.w), + _nearClipPlaneDistance, _farClipPlaneDistance)); } - srcViewFrustum.setProjection(glm::perspective(glm::radians(_vFoV), ((float)args->_viewport.z / (float)args->_viewport.w), _nearClipPlaneDistance, _farClipPlaneDistance)); // Without calculating the bound planes, the secondary camera will use the same culling frustum as the main camera, // which is not what we want here. srcViewFrustum.calculate(); @@ -101,6 +150,7 @@ private: float _farClipPlaneDistance; int _textureWidth; int _textureHeight; + bool _mirrorProjection; EntityPropertyFlags _attachedEntityPropertyFlags; QSharedPointer _entityScriptingInterface; }; diff --git a/interface/src/SecondaryCamera.h b/interface/src/SecondaryCamera.h index a9b438ec6f..38d75c391e 100644 --- a/interface/src/SecondaryCamera.h +++ b/interface/src/SecondaryCamera.h @@ -35,6 +35,7 @@ class SecondaryCameraJobConfig : public render::Task::Config { // Exposes second Q_PROPERTY(float vFoV MEMBER vFoV NOTIFY dirty) // Secondary camera's vertical field of view. In degrees. Q_PROPERTY(float nearClipPlaneDistance MEMBER nearClipPlaneDistance NOTIFY dirty) // Secondary camera's near clip plane distance. In meters. Q_PROPERTY(float farClipPlaneDistance MEMBER farClipPlaneDistance NOTIFY dirty) // Secondary camera's far clip plane distance. In meters. + Q_PROPERTY(bool mirrorProjection MEMBER mirrorProjection NOTIFY dirty) // Flag to use attached mirror entity to build frustum for the mirror and set mirrored camera position/orientation. public: QUuid attachedEntityId; glm::vec3 position; @@ -44,6 +45,7 @@ public: float farClipPlaneDistance { DEFAULT_FAR_CLIP }; int textureWidth { TextureCache::DEFAULT_SPECTATOR_CAM_WIDTH }; int textureHeight { TextureCache::DEFAULT_SPECTATOR_CAM_HEIGHT }; + bool mirrorProjection { false }; SecondaryCameraJobConfig() : render::Task::Config(false) {} signals: