diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c2faf8494a..a950030965 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5585,7 +5585,35 @@ void Application::updateSecondaryCameraViewFrustum() { } ViewFrustum secondaryViewFrustum; - if (camera->mirrorProjection && !camera->attachedEntityId.isNull()) { + if (camera->portalProjection && !camera->attachedEntityId.isNull()) { + auto entityScriptingInterface = DependencyManager::get(); + auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId); + glm::vec3 mirrorPropertiesPosition = entityProperties.getPosition(); + glm::quat mirrorPropertiesRotation = entityProperties.getRotation(); + glm::vec3 mirrorPropertiesDimensions = entityProperties.getDimensions(); + glm::vec3 halfMirrorPropertiesDimensions = 0.5f * mirrorPropertiesDimensions; + + 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); + + glm::vec3 mainCameraPositionWorld = 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)); + + glm::quat mirrorCameraOrientation = glm::quat_cast(worldFromMirrorRotation); + secondaryViewFrustum.setPosition(mirrorCameraPositionWorld); + secondaryViewFrustum.setOrientation(mirrorCameraOrientation); + + 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, camera->farClipPlaneDistance); + secondaryViewFrustum.setProjection(frustum); + } else if (camera->mirrorProjection && !camera->attachedEntityId.isNull()) { auto entityScriptingInterface = DependencyManager::get(); auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId); glm::vec3 mirrorPropertiesPosition = entityProperties.getPosition(); diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index f99a373259..6d60b6ba43 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -40,7 +40,50 @@ public: _farClipPlaneDistance = config.farClipPlaneDistance; _textureWidth = config.textureWidth; _textureHeight = config.textureHeight; - _mirrorProjection = config.mirrorProjection; + _mirrorProjection = config.mirrorProjection; + _portalProjection = config.portalProjection; + } + + void setPortalProjection(ViewFrustum& srcViewFrustum) { + if (_attachedEntityId.isNull()) { + qWarning() << "ERROR: Cannot set mirror projection for SecondaryCamera without an attachedEntityId set."; + return; + } + EntityItemPointer attachedEntity = qApp->getEntities()->getTree()->findEntityByID(_attachedEntityId); + + if (!attachedEntity) { + qWarning() << "ERROR: Cannot get EntityItemPointer for _attachedEntityId."; + return; + } + + glm::vec3 mirrorPropertiesPosition = attachedEntity->getWorldPosition(); + glm::quat mirrorPropertiesRotation = attachedEntity->getWorldOrientation(); + glm::vec3 mirrorPropertiesDimensions = attachedEntity->getScaledDimensions(); + glm::vec3 halfMirrorPropertiesDimensions = 0.5f * mirrorPropertiesDimensions; + + glm::mat4 worldFromMirrorRotation = glm::mat4_cast(mirrorPropertiesRotation); + glm::mat4 worldFromMirrorTranslation = glm::translate(mirrorPropertiesPosition); + glm::mat4 worldFromMirror = worldFromMirrorTranslation * worldFromMirrorRotation; + glm::mat4 mirrorFromWorld = glm::inverse(worldFromMirror); + + + 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 setMirrorProjection(ViewFrustum& srcViewFrustum) { @@ -109,7 +152,17 @@ public: auto srcViewFrustum = args->getViewFrustum(); if (_mirrorProjection) { - setMirrorProjection(srcViewFrustum); + if (_portalProjection) { + qWarning() << "ERROR: You can't set both _portalProjection and _mirrorProjection"; + } else { + setMirrorProjection(srcViewFrustum); + } + } else if (_portalProjection) { + if (_mirrorProjection) { + qWarning() << "ERROR: You can't set both _portalProjection and _mirrorProjection"; + } else { + setPortalProjection(srcViewFrustum); + } } else { if (!_attachedEntityId.isNull()) { EntityItemPointer attachedEntity = qApp->getEntities()->getTree()->findEntityByID(_attachedEntityId); @@ -149,6 +202,7 @@ private: int _textureWidth; int _textureHeight; bool _mirrorProjection; + bool _portalProjection; EntityPropertyFlags _attachedEntityPropertyFlags; }; diff --git a/interface/src/SecondaryCamera.h b/interface/src/SecondaryCamera.h index 3c8540c081..7382c2322e 100644 --- a/interface/src/SecondaryCamera.h +++ b/interface/src/SecondaryCamera.h @@ -26,6 +26,7 @@ class SecondaryCameraJobConfig : public render::Task::Config { // Exposes second 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. + Q_PROPERTY(bool portalProjection MEMBER portalProjection NOTIFY dirty) // Flag to use attached portal entity to build frustum for the portal and set portal camera position/orientation. public: QUuid attachedEntityId; glm::vec3 position; @@ -36,6 +37,7 @@ public: int textureWidth { TextureCache::DEFAULT_SPECTATOR_CAM_WIDTH }; int textureHeight { TextureCache::DEFAULT_SPECTATOR_CAM_HEIGHT }; bool mirrorProjection { false }; + bool portalProjection { false }; SecondaryCameraJobConfig() : render::Task::Config(false) {} signals: