Merge pull request #721 from HifiExperiments/mirror

Mirrors + Portals
This commit is contained in:
ksuprynowicz 2024-01-13 11:18:00 +01:00 committed by GitHub
commit c73566d7f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
81 changed files with 1047 additions and 263 deletions

View file

@ -2479,6 +2479,7 @@ Application::Application(
copyViewFrustum(viewFrustum); copyViewFrustum(viewFrustum);
return viewFrustum.getPosition(); return viewFrustum.getPosition();
}); });
MirrorModeHelpers::setComputeMirrorViewOperator(EntityRenderer::computeMirrorViewOperator);
DependencyManager::get<UsersScriptingInterface>()->setKickConfirmationOperator([this] (const QUuid& nodeID, unsigned int banFlags) { userKickConfirmation(nodeID, banFlags); }); DependencyManager::get<UsersScriptingInterface>()->setKickConfirmationOperator([this] (const QUuid& nodeID, unsigned int banFlags) { userKickConfirmation(nodeID, banFlags); });

View file

@ -27,7 +27,7 @@ public:
using Config = SecondaryCameraJobConfig; using Config = SecondaryCameraJobConfig;
using JobModel = render::Job::ModelO<SecondaryCameraJob, RenderArgsPointer, Config>; using JobModel = render::Job::ModelO<SecondaryCameraJob, RenderArgsPointer, Config>;
SecondaryCameraJob() { SecondaryCameraJob() {
_cachedArgsPointer = std::make_shared<RenderArgs>(_cachedArgs); _cachedArgsPointer = std::make_shared<RenderArgs>();
_attachedEntityPropertyFlags += PROP_POSITION; _attachedEntityPropertyFlags += PROP_POSITION;
_attachedEntityPropertyFlags += PROP_ROTATION; _attachedEntityPropertyFlags += PROP_ROTATION;
} }
@ -203,7 +203,6 @@ public:
} }
protected: protected:
RenderArgs _cachedArgs;
RenderArgsPointer _cachedArgsPointer; RenderArgsPointer _cachedArgsPointer;
private: private:

View file

@ -3530,8 +3530,9 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON_LOOK_AT || bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON_LOOK_AT ||
qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON; qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON;
bool overrideAnim = _skeletonModel ? _skeletonModel->getRig().isPlayingOverrideAnimation() : false; bool overrideAnim = _skeletonModel ? _skeletonModel->getRig().isPlayingOverrideAnimation() : false;
bool isInMirror = renderArgs->_mirrorDepth > 0;
bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition()); bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition());
return !defaultMode || (!firstPerson && !insideHead) || (overrideAnim && !insideHead); return !defaultMode || isInMirror || (!firstPerson && !insideHead) || (overrideAnim && !insideHead);
} }
void MyAvatar::setRotationRecenterFilterLength(float length) { void MyAvatar::setRotationRecenterFilterLength(float length) {

View file

@ -9,6 +9,7 @@
// //
#include "RenderScriptingInterface.h" #include "RenderScriptingInterface.h"
#include <RenderCommonTask.h>
#include <ScriptEngineCast.h> #include <ScriptEngineCast.h>
#include "LightingModel.h" #include "LightingModel.h"
@ -79,14 +80,35 @@ void RenderScriptingInterface::setRenderMethod(RenderMethod renderMethod) {
emit settingsChanged(); emit settingsChanged();
} }
} }
void recursivelyUpdateMirrorRenderMethods(const QString& parentTaskName, int renderMethod, int depth) {
if (depth == RenderMirrorTask::MAX_MIRROR_DEPTH) {
return;
}
for (size_t mirrorIndex = 0; mirrorIndex < RenderMirrorTask::MAX_MIRRORS_PER_LEVEL; mirrorIndex++) {
std::string mirrorTaskString = parentTaskName.toStdString() + ".RenderMirrorView" + std::to_string(mirrorIndex) + "Depth" + std::to_string(depth) + ".DeferredForwardSwitch";
auto mirrorConfig = dynamic_cast<render::SwitchConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig(QString::fromStdString(mirrorTaskString)));
if (mirrorConfig) {
mirrorConfig->setBranch((int)renderMethod);
recursivelyUpdateMirrorRenderMethods(QString::fromStdString(mirrorTaskString) + (renderMethod == 1 ? ".RenderForwardTask" : ".RenderShadowsAndDeferredTask.RenderDeferredTask"),
renderMethod, depth + 1);
}
}
}
void RenderScriptingInterface::forceRenderMethod(RenderMethod renderMethod) { void RenderScriptingInterface::forceRenderMethod(RenderMethod renderMethod) {
_renderSettingLock.withWriteLock([&] { _renderSettingLock.withWriteLock([&] {
_renderMethod = (int)renderMethod; _renderMethod = (int)renderMethod;
_renderMethodSetting.set((int)renderMethod); _renderMethodSetting.set((int)renderMethod);
auto config = dynamic_cast<render::SwitchConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("RenderMainView.DeferredForwardSwitch")); QString configName = "RenderMainView.DeferredForwardSwitch";
auto config = dynamic_cast<render::SwitchConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig(configName));
if (config) { if (config) {
config->setBranch((int)renderMethod); config->setBranch((int)renderMethod);
recursivelyUpdateMirrorRenderMethods(configName + (renderMethod == RenderMethod::FORWARD ? ".RenderForwardTask" : ".RenderShadowsAndDeferredTask.RenderDeferredTask"),
(int)renderMethod, 0);
} }
}); });
} }

View file

@ -1120,7 +1120,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
batch.setModelTransform(textTransform); batch.setModelTransform(textTransform);
{ {
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderText"); PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderText");
displayNameRenderer->draw(batch, text_x, -text_y, glm::vec2(-1.0f), nameUTF8.data(), textColor, true, forward); displayNameRenderer->draw(batch, { nameUTF8.data(), textColor, { text_x, -text_y }, glm::vec2(-1.0f), forward });
} }
} }
} }

View file

@ -357,7 +357,7 @@ void OpenGLDisplayPlugin::customizeContext() {
auto presentThread = DependencyManager::get<PresentThread>(); auto presentThread = DependencyManager::get<PresentThread>();
Q_ASSERT(thread() == presentThread->thread()); Q_ASSERT(thread() == presentThread->thread());
getGLBackend()->setCameraCorrection(mat4(), mat4(), true); getGLBackend()->setCameraCorrection(mat4(), mat4(), true, true);
for (auto& cursorValue : _cursorsData) { for (auto& cursorValue : _cursorsData) {
auto& cursorData = cursorValue.second; auto& cursorData = cursorValue.second;
@ -701,7 +701,7 @@ void OpenGLDisplayPlugin::present(const std::shared_ptr<RefreshRateController>&
if (_currentFrame) { if (_currentFrame) {
auto correction = getViewCorrection(); auto correction = getViewCorrection();
getGLBackend()->setCameraCorrection(correction, _prevRenderView); getGLBackend()->setCameraCorrection(correction, _prevRenderView, true);
_prevRenderView = correction * _currentFrame->view; _prevRenderView = correction * _currentFrame->view;
{ {
withPresentThreadLock([&] { withPresentThreadLock([&] {

View file

@ -12,6 +12,7 @@
#include "RenderableEntityItem.h" #include "RenderableEntityItem.h"
#include <glm/gtx/transform.hpp>
#include <ObjectMotionState.h> #include <ObjectMotionState.h>
#include "RenderableShapeEntityItem.h" #include "RenderableShapeEntityItem.h"
@ -192,6 +193,10 @@ ItemKey EntityRenderer::getKey() {
builder.withSubMetaCulled(); builder.withSubMetaCulled();
} }
if (_mirrorMode == MirrorMode::MIRROR || (_mirrorMode == MirrorMode::PORTAL && !_portalExitID.isNull())) {
builder.withMirror();
}
if (!_visible) { if (!_visible) {
builder.withInvisible(); builder.withInvisible();
} }
@ -221,12 +226,113 @@ bool EntityRenderer::passesZoneOcclusionTest(const std::unordered_set<QUuid>& co
return true; return true;
} }
ItemID EntityRenderer::computeMirrorView(ViewFrustum& viewFrustum) const {
glm::vec3 inPropertiesPosition;
glm::quat inPropertiesRotation;
MirrorMode mirrorMode;
QUuid portalExitID;
withReadLock([&]{
inPropertiesPosition = _entity->getWorldPosition();
inPropertiesRotation = _entity->getWorldOrientation();
mirrorMode = _mirrorMode;
portalExitID = _portalExitID;
});
return computeMirrorViewOperator(viewFrustum, inPropertiesPosition, inPropertiesRotation, mirrorMode, portalExitID);
}
ItemID EntityRenderer::computeMirrorViewOperator(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation,
MirrorMode mirrorMode, const QUuid& portalExitID) {
glm::mat4 inToWorld = glm::translate(inPropertiesPosition) * glm::mat4_cast(inPropertiesRotation);
glm::mat4 worldToIn = glm::inverse(inToWorld);
glm::vec3 outPropertiesPosition = inPropertiesPosition;
glm::quat outPropertiesRotation = inPropertiesRotation;
glm::mat4 outToWorld = inToWorld;
bool foundPortalExit = false;
if (mirrorMode == MirrorMode::PORTAL && !portalExitID.isNull()) {
auto renderer = DependencyManager::get<EntityTreeRenderer>();
if (renderer) {
if (auto renderable = renderer->renderableForEntityId(portalExitID)) {
renderable->withReadLock([&] {
outPropertiesPosition = renderable->_entity->getWorldPosition();
outPropertiesRotation = renderable->_entity->getWorldOrientation();
});
outToWorld = glm::translate(outPropertiesPosition) * glm::mat4_cast(outPropertiesRotation);
foundPortalExit = true;
}
}
}
// get mirror camera position by reflecting main camera position's z coordinate in mirror space
glm::vec3 cameraPositionWorld = viewFrustum.getPosition();
glm::vec3 cameraPositionIn = vec3(worldToIn * vec4(cameraPositionWorld, 1.0f));
glm::vec3 mirrorCameraPositionIn = vec3(cameraPositionIn.x, cameraPositionIn.y, -cameraPositionIn.z);
if (foundPortalExit) {
// portals also flip over x
mirrorCameraPositionIn.x *= -1.0f;
}
glm::vec3 mirrorCameraPositionWorld = vec3(outToWorld * vec4(mirrorCameraPositionIn, 1.0f));
// get mirror camera rotation by reflecting main camera rotation in mirror space
// TODO: we are assuming here that UP is world y-axis
glm::quat mainCameraRotationWorld = viewFrustum.getOrientation();
glm::quat mainCameraRotationMirror = worldToIn * glm::mat4_cast(mainCameraRotationWorld);
glm::quat mirrorCameraRotationMirror = glm::quat(mainCameraRotationMirror.w, -mainCameraRotationMirror.x, -mainCameraRotationMirror.y, mainCameraRotationMirror.z) *
glm::angleAxis((float)M_PI, glm::vec3(0, 1, 0));
if (foundPortalExit) {
// portals also flip over x
mirrorCameraRotationMirror = glm::quat(mirrorCameraRotationMirror.w, mirrorCameraRotationMirror.x, -mirrorCameraRotationMirror.y, -mirrorCameraRotationMirror.z);
}
glm::quat mirrorCameraRotationWorld = outToWorld * glm::mat4_cast(mirrorCameraRotationMirror);
viewFrustum.setPosition(mirrorCameraPositionWorld);
viewFrustum.setOrientation(mirrorCameraRotationWorld);
// modify the near clip plane to be the XY plane of the mirror
// from: https://terathon.com/lengyel/Lengyel-Oblique.pdf
glm::mat4 view = viewFrustum.getView();
glm::mat4 projection = viewFrustum.getProjection();
//Find the camera-space 4D reflection plane vector
glm::vec3 cameraSpacePosition = glm::inverse(view) * glm::vec4(outPropertiesPosition, 1.0f);
glm::vec3 cameraSpaceNormal = glm::transpose(view) * (outPropertiesRotation * glm::vec4(0, 0, -1, 0));
glm::vec4 clipPlane = glm::vec4(cameraSpaceNormal, -glm::dot(cameraSpaceNormal, cameraSpacePosition));
// Make sure we pick the direction facing away from us
if (clipPlane.w > 0.0f) {
clipPlane *= -1.0f;
}
// Calculate the clip-space corner point opposite the clipping plane
// as (sign(clipPlane.x), sign(clipPlane.y), 1, 1) and
// transform it into camera space by multiplying it
// by the inverse of the projection matrix
glm::vec4 q;
q.x = (glm::sign(clipPlane.x) + projection[0][2]) / projection[0][0];
q.y = (glm::sign(clipPlane.y) + projection[1][2]) / projection[1][1];
q.z = -1.0f;
q.w = (1.0f + projection[2][2]) / projection[2][3];
// Calculate the scaled plane vector
glm::vec4 c = (2.0f / glm::dot(clipPlane, q)) * clipPlane;
// Replace the third row of the projection matrix
projection[0][2] = c.x;
projection[1][2] = c.y;
projection[2][2] = c.z + 1.0f;
projection[3][2] = c.w;
viewFrustum.setProjection(projection, true);
return foundPortalExit ? DependencyManager::get<EntityTreeRenderer>()->renderableIdForEntityId(portalExitID) : Item::INVALID_ITEM_ID;
}
void EntityRenderer::render(RenderArgs* args) { void EntityRenderer::render(RenderArgs* args) {
if (!isValidRenderItem()) { if (!isValidRenderItem()) {
return; return;
} }
if (_visible && (args->_renderMode != RenderArgs::RenderMode::DEFAULT_RENDER_MODE || !_cauterized)) { if (_visible && (!_cauterized || args->_renderMode != RenderArgs::RenderMode::DEFAULT_RENDER_MODE || args->_mirrorDepth > 0)) {
doRender(args); doRender(args);
} }
} }
@ -454,6 +560,8 @@ void EntityRenderer::doRenderUpdateAsynchronous(const EntityItemPointer& entity)
_canCastShadow = entity->getCanCastShadow(); _canCastShadow = entity->getCanCastShadow();
setCullWithParent(entity->getCullWithParent()); setCullWithParent(entity->getCullWithParent());
_cauterized = entity->getCauterized(); _cauterized = entity->getCauterized();
setMirrorMode(entity->getMirrorMode());
setPortalExitID(entity->getPortalExitID());
if (entity->needsZoneOcclusionUpdate()) { if (entity->needsZoneOcclusionUpdate()) {
entity->resetNeedsZoneOcclusionUpdate(); entity->resetNeedsZoneOcclusionUpdate();
_renderWithZones = entity->getRenderWithZones(); _renderWithZones = entity->getRenderWithZones();
@ -505,6 +613,10 @@ graphics::MaterialPointer EntityRenderer::getTopMaterial() {
} }
EntityRenderer::Pipeline EntityRenderer::getPipelineType(const graphics::MultiMaterial& materials) { EntityRenderer::Pipeline EntityRenderer::getPipelineType(const graphics::MultiMaterial& materials) {
if (_mirrorMode == MirrorMode::MIRROR || (_mirrorMode == MirrorMode::PORTAL && !_portalExitID.isNull())) {
return Pipeline::MIRROR;
}
if (materials.top().material && materials.top().material->isProcedural() && materials.top().material->isReady()) { if (materials.top().material && materials.top().material->isProcedural() && materials.top().material->isReady()) {
return Pipeline::PROCEDURAL; return Pipeline::PROCEDURAL;
} }

View file

@ -58,12 +58,13 @@ public:
enum class Pipeline { enum class Pipeline {
SIMPLE, SIMPLE,
MATERIAL, MATERIAL,
PROCEDURAL PROCEDURAL,
MIRROR
}; };
virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName); virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName);
virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName); virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName);
virtual graphics::MaterialPointer getTopMaterial(); virtual graphics::MaterialPointer getTopMaterial();
static Pipeline getPipelineType(const graphics::MultiMaterial& materials); Pipeline getPipelineType(const graphics::MultiMaterial& materials);
virtual gpu::TexturePointer getTexture() { return nullptr; } virtual gpu::TexturePointer getTexture() { return nullptr; }
virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); } virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); }
@ -74,6 +75,9 @@ public:
virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const override; virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const override;
virtual Item::Bound getBound(RenderArgs* args) override; virtual Item::Bound getBound(RenderArgs* args) override;
bool passesZoneOcclusionTest(const std::unordered_set<QUuid>& containingZones) const override; bool passesZoneOcclusionTest(const std::unordered_set<QUuid>& containingZones) const override;
ItemID computeMirrorView(ViewFrustum& viewFrustum) const override;
static ItemID computeMirrorViewOperator(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation,
MirrorMode mirrorMode, const QUuid& portalExitID);
protected: protected:
virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); }
@ -116,6 +120,8 @@ protected:
virtual void setIsVisibleInSecondaryCamera(bool value) { _isVisibleInSecondaryCamera = value; } virtual void setIsVisibleInSecondaryCamera(bool value) { _isVisibleInSecondaryCamera = value; }
virtual void setRenderLayer(RenderLayer value) { _renderLayer = value; } virtual void setRenderLayer(RenderLayer value) { _renderLayer = value; }
virtual void setCullWithParent(bool value) { _cullWithParent = value; } virtual void setCullWithParent(bool value) { _cullWithParent = value; }
virtual void setMirrorMode(MirrorMode value) { _mirrorMode = value; }
virtual void setPortalExitID(const QUuid& value) { _portalExitID = value; }
template<typename T> template<typename T>
std::shared_ptr<T> asTypedEntity() { return std::static_pointer_cast<T>(_entity); } std::shared_ptr<T> asTypedEntity() { return std::static_pointer_cast<T>(_entity); }
@ -151,6 +157,8 @@ protected:
BillboardMode _billboardMode { BillboardMode::NONE }; BillboardMode _billboardMode { BillboardMode::NONE };
bool _cauterized { false }; bool _cauterized { false };
bool _moving { false }; bool _moving { false };
MirrorMode _mirrorMode { MirrorMode::NONE };
QUuid _portalExitID;
Transform _renderTransform; Transform _renderTransform;
MaterialMap _materials; MaterialMap _materials;

View file

@ -263,8 +263,9 @@ void GizmoEntityRenderer::doRender(RenderArgs* args) {
bool wireframe = render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES; bool wireframe = render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES;
bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0;
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition(), true)); usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition(), true));
batch.setModelTransform(transform); batch.setModelTransform(transform);
Pipeline pipelineType = getPipelineType(materials); Pipeline pipelineType = getPipelineType(materials);

View file

@ -103,8 +103,9 @@ void GridEntityRenderer::doRender(RenderArgs* args) {
} else { } else {
transform.setTranslation(renderTransform.getTranslation()); transform.setTranslation(renderTransform.getTranslation());
} }
bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0;
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
batch->setModelTransform(transform); batch->setModelTransform(transform);
auto minCorner = glm::vec2(-0.5f, -0.5f); auto minCorner = glm::vec2(-0.5f, -0.5f);

View file

@ -147,8 +147,9 @@ void ImageEntityRenderer::doRender(RenderArgs* args) {
gpu::Batch* batch = args->_batch; gpu::Batch* batch = args->_batch;
bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0;
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
float imageWidth = _texture->getWidth(); float imageWidth = _texture->getWidth();
float imageHeight = _texture->getHeight(); float imageHeight = _texture->getHeight();
@ -198,9 +199,11 @@ void ImageEntityRenderer::doRender(RenderArgs* args) {
procedural->prepare(*batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(transparent)); procedural->prepare(*batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(transparent));
} else if (pipelineType == Pipeline::SIMPLE) { } else if (pipelineType == Pipeline::SIMPLE) {
batch->setResourceTexture(0, _texture->getGPUTexture()); batch->setResourceTexture(0, _texture->getGPUTexture());
} else if (RenderPipelines::bindMaterials(materials, *batch, args->_renderMode, args->_enableTexturing)) { } else if (pipelineType == Pipeline::MATERIAL) {
if (RenderPipelines::bindMaterials(materials, *batch, args->_renderMode, args->_enableTexturing)) {
args->_details._materialSwitches++; args->_details._materialSwitches++;
} }
}
DependencyManager::get<GeometryCache>()->renderQuad( DependencyManager::get<GeometryCache>()->renderQuad(
*batch, glm::vec2(-0.5f), glm::vec2(0.5f), texCoordBottomLeft, texCoordTopRight, *batch, glm::vec2(-0.5f), glm::vec2(0.5f), texCoordBottomLeft, texCoordTopRight,

View file

@ -47,8 +47,9 @@ void LineEntityRenderer::doRender(RenderArgs* args) {
const auto& modelTransform = getModelTransform(); const auto& modelTransform = getModelTransform();
Transform transform = Transform(); Transform transform = Transform();
transform.setTranslation(modelTransform.getTranslation()); transform.setTranslation(modelTransform.getTranslation());
bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0;
transform.setRotation(BillboardModeHelpers::getBillboardRotation(modelTransform.getTranslation(), modelTransform.getRotation(), _billboardMode, transform.setRotation(BillboardModeHelpers::getBillboardRotation(modelTransform.getTranslation(), modelTransform.getRotation(), _billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
batch.setModelTransform(transform); batch.setModelTransform(transform);
if (_linePoints.size() > 1) { if (_linePoints.size() > 1) {
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, false, false, true, DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, false, false, true,

View file

@ -303,8 +303,9 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) {
proceduralRender = true; proceduralRender = true;
} }
bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0;
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
batch.setModelTransform(transform); batch.setModelTransform(transform);
if (!proceduralRender) { if (!proceduralRender) {

View file

@ -1274,6 +1274,8 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
_model->setBillboardMode(_billboardMode, scene); _model->setBillboardMode(_billboardMode, scene);
_model->setCullWithParent(_cullWithParent, scene); _model->setCullWithParent(_cullWithParent, scene);
_model->setRenderWithZones(_renderWithZones, scene); _model->setRenderWithZones(_renderWithZones, scene);
_model->setMirrorMode(_mirrorMode, scene);
_model->setPortalExitID(_portalExitID, scene);
}); });
if (didVisualGeometryRequestSucceed) { if (didVisualGeometryRequestSucceed) {
emit DependencyManager::get<scriptable::ModelProviderFactory>()-> emit DependencyManager::get<scriptable::ModelProviderFactory>()->
@ -1353,6 +1355,8 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
model->setBillboardMode(_billboardMode, scene); model->setBillboardMode(_billboardMode, scene);
model->setCullWithParent(_cullWithParent, scene); model->setCullWithParent(_cullWithParent, scene);
model->setRenderWithZones(_renderWithZones, scene); model->setRenderWithZones(_renderWithZones, scene);
model->setMirrorMode(_mirrorMode, scene);
model->setPortalExitID(_portalExitID, scene);
}); });
if (entity->blendshapesChanged()) { if (entity->blendshapesChanged()) {
@ -1466,6 +1470,18 @@ void ModelEntityRenderer::setCullWithParent(bool value) {
setKey(_didLastVisualGeometryRequestSucceed, _model); setKey(_didLastVisualGeometryRequestSucceed, _model);
} }
void ModelEntityRenderer::setMirrorMode(MirrorMode value) {
Parent::setMirrorMode(value);
// called within a lock so no need to lock for _model
setKey(_didLastVisualGeometryRequestSucceed, _model);
}
void ModelEntityRenderer::setPortalExitID(const QUuid& value) {
Parent::setPortalExitID(value);
// called within a lock so no need to lock for _model
setKey(_didLastVisualGeometryRequestSucceed, _model);
}
// NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items // NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items
void ModelEntityRenderer::doRender(RenderArgs* args) { void ModelEntityRenderer::doRender(RenderArgs* args) {
DETAILED_PROFILE_RANGE(render_detail, "MetaModelRender"); DETAILED_PROFILE_RANGE(render_detail, "MetaModelRender");

View file

@ -170,6 +170,8 @@ protected:
void setIsVisibleInSecondaryCamera(bool value) override; void setIsVisibleInSecondaryCamera(bool value) override;
void setRenderLayer(RenderLayer value) override; void setRenderLayer(RenderLayer value) override;
void setCullWithParent(bool value) override; void setCullWithParent(bool value) override;
void setMirrorMode(MirrorMode value) override;
void setPortalExitID(const QUuid& value) override;
private: private:
void animate(const TypedEntityPointer& entity, const ModelPointer& model); void animate(const TypedEntityPointer& entity, const ModelPointer& model);

View file

@ -325,8 +325,9 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) {
buildPipelines(); buildPipelines();
} }
bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0;
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
batch.setModelTransform(transform); batch.setModelTransform(transform);
batch.setPipeline(_pipelines[{args->_renderMethod, isTransparent()}]); batch.setPipeline(_pipelines[{args->_renderMethod, isTransparent()}]);

View file

@ -1860,8 +1860,9 @@ void PolyVoxEntityRenderer::doRender(RenderArgs* args) {
PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render");
gpu::Batch& batch = *args->_batch; gpu::Batch& batch = *args->_batch;
bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0;
glm::mat4 rotation = glm::mat4_cast(BillboardModeHelpers::getBillboardRotation(_position, _orientation, _billboardMode, glm::mat4 rotation = glm::mat4_cast(BillboardModeHelpers::getBillboardRotation(_position, _orientation, _billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
Transform transform(glm::translate(_position) * rotation * _lastVoxelToLocalMatrix); Transform transform(glm::translate(_position) * rotation * _lastVoxelToLocalMatrix);
batch.setModelTransform(transform); batch.setModelTransform(transform);

View file

@ -118,8 +118,9 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
bool wireframe = render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES; bool wireframe = render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES;
bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0;
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition(), usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition(),
_shape < entity::Shape::Cube || _shape > entity::Shape::Icosahedron)); _shape < entity::Shape::Cube || _shape > entity::Shape::Icosahedron));
batch.setModelTransform(transform); batch.setModelTransform(transform);
@ -157,7 +158,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
} }
} }
} else { } else {
if (RenderPipelines::bindMaterials(materials, batch, args->_renderMode, args->_enableTexturing)) { if (pipelineType == Pipeline::MATERIAL && RenderPipelines::bindMaterials(materials, batch, args->_renderMode, args->_enableTexturing)) {
args->_details._materialSwitches++; args->_details._materialSwitches++;
} }

View file

@ -164,8 +164,9 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
transform = _renderTransform; transform = _renderTransform;
}); });
bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0;
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
batch.setModelTransform(transform); batch.setModelTransform(transform);
Pipeline pipelineType = getPipelineType(materials); Pipeline pipelineType = getPipelineType(materials);
@ -180,7 +181,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
} }
auto geometryCache = DependencyManager::get<GeometryCache>(); auto geometryCache = DependencyManager::get<GeometryCache>();
if (pipelineType == Pipeline::SIMPLE) { if (pipelineType == Pipeline::SIMPLE || pipelineType == Pipeline::MIRROR) {
geometryCache->renderQuad(batch, glm::vec2(-0.5f), glm::vec2(0.5f), backgroundColor, _geometryID); geometryCache->renderQuad(batch, glm::vec2(-0.5f), glm::vec2(0.5f), backgroundColor, _geometryID);
} else { } else {
geometryCache->renderQuad(batch, glm::vec2(-0.5f), glm::vec2(0.5f), glm::vec2(0.0f), glm::vec2(1.0f), backgroundColor, _geometryID); geometryCache->renderQuad(batch, glm::vec2(-0.5f), glm::vec2(0.5f), glm::vec2(0.0f), glm::vec2(1.0f), backgroundColor, _geometryID);
@ -260,6 +261,10 @@ ItemKey entities::TextPayload::getKey() const {
builder.withInvisible(); builder.withInvisible();
} }
if (textRenderable->_mirrorMode == MirrorMode::MIRROR || (textRenderable->_mirrorMode == MirrorMode::PORTAL && !textRenderable->_portalExitID.isNull())) {
builder.withMirror();
}
return builder; return builder;
} }
} }
@ -311,6 +316,17 @@ bool entities::TextPayload::passesZoneOcclusionTest(const std::unordered_set<QUu
return false; return false;
} }
ItemID entities::TextPayload::computeMirrorView(ViewFrustum& viewFrustum) const {
auto entityTreeRenderer = DependencyManager::get<EntityTreeRenderer>();
if (entityTreeRenderer) {
auto renderable = entityTreeRenderer->renderableForEntityId(_entityID);
if (renderable) {
return renderable->computeMirrorView(viewFrustum);
}
}
return Item::INVALID_ITEM_ID;
}
void entities::TextPayload::render(RenderArgs* args) { void entities::TextPayload::render(RenderArgs* args) {
PerformanceTimer perfTimer("TextPayload::render"); PerformanceTimer perfTimer("TextPayload::render");
Q_ASSERT(args->_batch); Q_ASSERT(args->_batch);
@ -335,12 +351,15 @@ void entities::TextPayload::render(RenderArgs* args) {
glm::vec3 dimensions; glm::vec3 dimensions;
glm::vec4 textColor; glm::vec4 textColor;
bool mirror;
textRenderable->withReadLock([&] { textRenderable->withReadLock([&] {
transform = textRenderable->_renderTransform; transform = textRenderable->_renderTransform;
dimensions = textRenderable->_dimensions; dimensions = textRenderable->_dimensions;
float fadeRatio = textRenderable->_isFading ? Interpolate::calculateFadeRatio(textRenderable->_fadeStartTime) : 1.0f; float fadeRatio = textRenderable->_isFading ? Interpolate::calculateFadeRatio(textRenderable->_fadeStartTime) : 1.0f;
textColor = glm::vec4(textRenderable->_textColor, fadeRatio * textRenderable->_textAlpha); textColor = glm::vec4(textRenderable->_textColor, fadeRatio * textRenderable->_textAlpha);
mirror = textRenderable->_mirrorMode == MirrorMode::MIRROR || (textRenderable->_mirrorMode == MirrorMode::PORTAL && !textRenderable->_portalExitID.isNull());
}); });
bool forward = textRenderable->_renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD; bool forward = textRenderable->_renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD;
@ -352,8 +371,9 @@ void entities::TextPayload::render(RenderArgs* args) {
return; return;
} }
bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0;
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), textRenderable->_billboardMode, transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), textRenderable->_billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
float scale = textRenderable->_lineHeight / textRenderer->getFontSize(); float scale = textRenderable->_lineHeight / textRenderer->getFontSize();
transform.postTranslate(glm::vec3(-0.5, 0.5, 1.0f + EPSILON / dimensions.z)); transform.postTranslate(glm::vec3(-0.5, 0.5, 1.0f + EPSILON / dimensions.z));
@ -361,9 +381,8 @@ void entities::TextPayload::render(RenderArgs* args) {
batch.setModelTransform(transform); batch.setModelTransform(transform);
glm::vec2 bounds = glm::vec2(dimensions.x - (textRenderable->_leftMargin + textRenderable->_rightMargin), dimensions.y - (textRenderable->_topMargin + textRenderable->_bottomMargin)); glm::vec2 bounds = glm::vec2(dimensions.x - (textRenderable->_leftMargin + textRenderable->_rightMargin), dimensions.y - (textRenderable->_topMargin + textRenderable->_bottomMargin));
textRenderer->draw(batch, textRenderable->_leftMargin / scale, -textRenderable->_topMargin / scale, bounds / scale, scale, textRenderer->draw(batch, textRenderable->_font, { textRenderable->_text, textColor, effectColor, { textRenderable->_leftMargin / scale, -textRenderable->_topMargin / scale },
textRenderable->_text, textRenderable->_font, textColor, effectColor, textRenderable->_effectThickness, textRenderable->_effect, bounds / scale, scale, textRenderable->_effectThickness, textRenderable->_effect, textRenderable->_alignment, textRenderable->_unlit, forward, mirror });
textRenderable->_alignment, textRenderable->_unlit, forward);
} }
namespace render { namespace render {
@ -399,4 +418,11 @@ template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Poi
return false; return false;
} }
template <> ItemID payloadComputeMirrorView(const entities::TextPayload::Pointer& payload, ViewFrustum& viewFrustum) {
if (payload) {
return payload->computeMirrorView(viewFrustum);
}
return Item::INVALID_ITEM_ID;
}
} }

View file

@ -101,6 +101,7 @@ public:
ShapeKey getShapeKey() const; ShapeKey getShapeKey() const;
void render(RenderArgs* args); void render(RenderArgs* args);
bool passesZoneOcclusionTest(const std::unordered_set<QUuid>& containingZones) const; bool passesZoneOcclusionTest(const std::unordered_set<QUuid>& containingZones) const;
ItemID computeMirrorView(ViewFrustum& viewFrustum) const;
protected: protected:
QUuid _entityID; QUuid _entityID;
@ -117,6 +118,7 @@ namespace render {
template <> const ShapeKey shapeGetShapeKey(const entities::TextPayload::Pointer& payload); template <> const ShapeKey shapeGetShapeKey(const entities::TextPayload::Pointer& payload);
template <> void payloadRender(const entities::TextPayload::Pointer& payload, RenderArgs* args); template <> void payloadRender(const entities::TextPayload::Pointer& payload, RenderArgs* args);
template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Pointer& payload, const std::unordered_set<QUuid>& containingZones); template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Pointer& payload, const std::unordered_set<QUuid>& containingZones);
template <> ItemID payloadComputeMirrorView(const entities::TextPayload::Pointer& payload, ViewFrustum& viewFrustum);
} }
#endif // hifi_RenderableTextEntityItem_h #endif // hifi_RenderableTextEntityItem_h

View file

@ -320,8 +320,9 @@ void WebEntityRenderer::doRender(RenderArgs* args) {
batch.setResourceTexture(0, _texture); batch.setResourceTexture(0, _texture);
bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0;
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
batch.setModelTransform(transform); batch.setModelTransform(transform);
// Turn off jitter for these entities // Turn off jitter for these entities

View file

@ -105,6 +105,8 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_RENDER_WITH_ZONES; requestedProperties += PROP_RENDER_WITH_ZONES;
requestedProperties += PROP_BILLBOARD_MODE; requestedProperties += PROP_BILLBOARD_MODE;
requestedProperties += _grabProperties.getEntityProperties(params); requestedProperties += _grabProperties.getEntityProperties(params);
requestedProperties += PROP_MIRROR_MODE;
requestedProperties += PROP_PORTAL_EXIT_ID;
// Physics // Physics
requestedProperties += PROP_DENSITY; requestedProperties += PROP_DENSITY;
@ -305,6 +307,8 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
_grabProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, _grabProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
propertyFlags, propertiesDidntFit, propertyCount, appendState); propertyFlags, propertiesDidntFit, propertyCount, appendState);
}); });
APPEND_ENTITY_PROPERTY(PROP_MIRROR_MODE, (uint32_t)getMirrorMode());
APPEND_ENTITY_PROPERTY(PROP_PORTAL_EXIT_ID, getPortalExitID());
// Physics // Physics
APPEND_ENTITY_PROPERTY(PROP_DENSITY, getDensity()); APPEND_ENTITY_PROPERTY(PROP_DENSITY, getDensity());
@ -881,6 +885,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
bytesRead += bytesFromGrab; bytesRead += bytesFromGrab;
dataAt += bytesFromGrab; dataAt += bytesFromGrab;
}); });
READ_ENTITY_PROPERTY(PROP_MIRROR_MODE, MirrorMode, setMirrorMode);
READ_ENTITY_PROPERTY(PROP_PORTAL_EXIT_ID, QUuid, setPortalExitID);
READ_ENTITY_PROPERTY(PROP_DENSITY, float, setDensity); READ_ENTITY_PROPERTY(PROP_DENSITY, float, setDensity);
{ {
@ -1361,6 +1367,8 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire
withReadLock([&] { withReadLock([&] {
_grabProperties.getProperties(properties); _grabProperties.getProperties(properties);
}); });
COPY_ENTITY_PROPERTY_TO_PROPERTIES(mirrorMode, getMirrorMode);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(portalExitID, getPortalExitID);
// Physics // Physics
COPY_ENTITY_PROPERTY_TO_PROPERTIES(density, getDensity); COPY_ENTITY_PROPERTY_TO_PROPERTIES(density, getDensity);
@ -1499,6 +1507,8 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
bool grabPropertiesChanged = _grabProperties.setProperties(properties); bool grabPropertiesChanged = _grabProperties.setProperties(properties);
somethingChanged |= grabPropertiesChanged; somethingChanged |= grabPropertiesChanged;
}); });
SET_ENTITY_PROPERTY_FROM_PROPERTIES(mirrorMode, setMirrorMode);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(portalExitID, setPortalExitID);
// Physics // Physics
SET_ENTITY_PROPERTY_FROM_PROPERTIES(density, setDensity); SET_ENTITY_PROPERTY_FROM_PROPERTIES(density, setDensity);
@ -3553,3 +3563,29 @@ void EntityItem::setBillboardMode(BillboardMode value) {
_billboardMode = value; _billboardMode = value;
}); });
} }
MirrorMode EntityItem::getMirrorMode() const {
return resultWithReadLock<MirrorMode>([&] {
return _mirrorMode;
});
}
void EntityItem::setMirrorMode(MirrorMode value) {
withWriteLock([&] {
_needsRenderUpdate |= _mirrorMode != value;
_mirrorMode = value;
});
}
QUuid EntityItem::getPortalExitID() const {
return resultWithReadLock<QUuid>([&] {
return _portalExitID;
});
}
void EntityItem::setPortalExitID(const QUuid& value) {
withWriteLock([&] {
_needsRenderUpdate |= _portalExitID != value;
_portalExitID = value;
});
}

View file

@ -559,6 +559,12 @@ public:
BillboardMode getBillboardMode() const; BillboardMode getBillboardMode() const;
virtual bool getRotateForPicking() const { return false; } virtual bool getRotateForPicking() const { return false; }
MirrorMode getMirrorMode() const;
void setMirrorMode(MirrorMode value);
QUuid getPortalExitID() const;
void setPortalExitID(const QUuid& value);
signals: signals:
void spaceUpdate(std::pair<int32_t, glm::vec4> data); void spaceUpdate(std::pair<int32_t, glm::vec4> data);
@ -740,6 +746,9 @@ protected:
bool _cullWithParent { false }; bool _cullWithParent { false };
MirrorMode _mirrorMode { MirrorMode::NONE };
QUuid _portalExitID;
mutable bool _needsRenderUpdate { false }; mutable bool _needsRenderUpdate { false };
}; };

View file

@ -424,6 +424,23 @@ void EntityItemProperties::setEntityHostTypeFromString(const QString& entityHost
} }
} }
inline void addMirrorMode(QHash<QString, MirrorMode>& lookup, MirrorMode mirrorMode) { lookup[MirrorModeHelpers::getNameForMirrorMode(mirrorMode)] = mirrorMode; }
const QHash<QString, MirrorMode> stringToMirrorModeLookup = [] {
QHash<QString, MirrorMode> toReturn;
addMirrorMode(toReturn, MirrorMode::NONE);
addMirrorMode(toReturn, MirrorMode::MIRROR);
addMirrorMode(toReturn, MirrorMode::PORTAL);
return toReturn;
}();
QString EntityItemProperties::getMirrorModeAsString() const { return MirrorModeHelpers::getNameForMirrorMode(_mirrorMode); }
void EntityItemProperties::setMirrorModeFromString(const QString& mirrorMode) {
auto mirrorModeItr = stringToMirrorModeLookup.find(mirrorMode.toLower());
if (mirrorModeItr != stringToMirrorModeLookup.end()) {
_mirrorMode = mirrorModeItr.value();
_mirrorModeChanged = true;
}
}
EntityPropertyFlags EntityItemProperties::getChangedProperties() const { EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
EntityPropertyFlags changedProperties; EntityPropertyFlags changedProperties;
@ -455,6 +472,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_RENDER_WITH_ZONES, renderWithZones); CHECK_PROPERTY_CHANGE(PROP_RENDER_WITH_ZONES, renderWithZones);
CHECK_PROPERTY_CHANGE(PROP_BILLBOARD_MODE, billboardMode); CHECK_PROPERTY_CHANGE(PROP_BILLBOARD_MODE, billboardMode);
changedProperties += _grab.getChangedProperties(); changedProperties += _grab.getChangedProperties();
CHECK_PROPERTY_CHANGE(PROP_MIRROR_MODE, mirrorMode);
CHECK_PROPERTY_CHANGE(PROP_PORTAL_EXIT_ID, portalExitID);
// Physics // Physics
CHECK_PROPERTY_CHANGE(PROP_DENSITY, density); CHECK_PROPERTY_CHANGE(PROP_DENSITY, density);
@ -825,6 +844,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* *
* @property {Entities.Grab} grab - The entity's grab-related properties. * @property {Entities.Grab} grab - The entity's grab-related properties.
* *
* @property {MirrorMode} mirrorMode="none" - If this entity should render as a mirror (reflecting the view of the camera),
* a portal (reflecting the view through its <code>portalExitID</code>), or normally.
* @property {Uuid} portalExitID=Uuid.NULL - The ID of the entity that should act as the portal exit if the <code>mirrorMode</code>
* is set to <code>portal</code>.
*
* @comment The different entity types have additional properties as follows: * @comment The different entity types have additional properties as follows:
* @see {@link Entities.EntityProperties-Box|EntityProperties-Box} * @see {@link Entities.EntityProperties-Box|EntityProperties-Box}
* @see {@link Entities.EntityProperties-Gizmo|EntityProperties-Gizmo} * @see {@link Entities.EntityProperties-Gizmo|EntityProperties-Gizmo}
@ -1616,6 +1640,8 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RENDER_WITH_ZONES, renderWithZones); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RENDER_WITH_ZONES, renderWithZones);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString());
_grab.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); _grab.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_MIRROR_MODE, mirrorMode, getMirrorModeAsString());
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PORTAL_EXIT_ID, portalExitID);
// Physics // Physics
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DENSITY, density); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DENSITY, density);
@ -2032,6 +2058,8 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h
COPY_PROPERTY_FROM_QSCRIPTVALUE(renderWithZones, qVectorQUuid, setRenderWithZones); COPY_PROPERTY_FROM_QSCRIPTVALUE(renderWithZones, qVectorQUuid, setRenderWithZones);
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(billboardMode, BillboardMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(billboardMode, BillboardMode);
_grab.copyFromScriptValue(object, namesSet, _defaultSettings); _grab.copyFromScriptValue(object, namesSet, _defaultSettings);
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(mirrorMode, MirrorMode);
COPY_PROPERTY_FROM_QSCRIPTVALUE(portalExitID, QUuid, setPortalExitID);
// Physics // Physics
COPY_PROPERTY_FROM_QSCRIPTVALUE(density, float, setDensity); COPY_PROPERTY_FROM_QSCRIPTVALUE(density, float, setDensity);
@ -2318,6 +2346,8 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(renderWithZones); COPY_PROPERTY_IF_CHANGED(renderWithZones);
COPY_PROPERTY_IF_CHANGED(billboardMode); COPY_PROPERTY_IF_CHANGED(billboardMode);
_grab.merge(other._grab); _grab.merge(other._grab);
COPY_PROPERTY_IF_CHANGED(mirrorMode);
COPY_PROPERTY_IF_CHANGED(portalExitID);
// Physics // Physics
COPY_PROPERTY_IF_CHANGED(density); COPY_PROPERTY_IF_CHANGED(density);
@ -2627,6 +2657,8 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, Grab, grab, ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, Grab, grab,
EquippableIndicatorOffset, equippableIndicatorOffset); EquippableIndicatorOffset, equippableIndicatorOffset);
} }
ADD_PROPERTY_TO_MAP(PROP_MIRROR_MODE, MirrorMode, mirrorMode, MirrorMode);
ADD_PROPERTY_TO_MAP(PROP_PORTAL_EXIT_ID, PortalExitID, portalExitID, QUuid);
// Physics // Physics
ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DENSITY, Density, density, float, ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DENSITY, Density, density, float,
@ -3090,6 +3122,8 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
_staticGrab.setProperties(properties); _staticGrab.setProperties(properties);
_staticGrab.appendToEditPacket(packetData, requestedProperties, propertyFlags, _staticGrab.appendToEditPacket(packetData, requestedProperties, propertyFlags,
propertiesDidntFit, propertyCount, appendState); propertiesDidntFit, propertyCount, appendState);
APPEND_ENTITY_PROPERTY(PROP_MIRROR_MODE, (uint32_t)properties.getMirrorMode());
APPEND_ENTITY_PROPERTY(PROP_PORTAL_EXIT_ID, properties.getPortalExitID());
// Physics // Physics
APPEND_ENTITY_PROPERTY(PROP_DENSITY, properties.getDensity()); APPEND_ENTITY_PROPERTY(PROP_DENSITY, properties.getDensity());
@ -3568,6 +3602,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RENDER_WITH_ZONES, QVector<QUuid>, setRenderWithZones); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RENDER_WITH_ZONES, QVector<QUuid>, setRenderWithZones);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode);
properties.getGrab().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); properties.getGrab().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MIRROR_MODE, MirrorMode, setMirrorMode);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PORTAL_EXIT_ID, QUuid, setPortalExitID);
// Physics // Physics
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DENSITY, float, setDensity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DENSITY, float, setDensity);
@ -3960,6 +3996,8 @@ void EntityItemProperties::markAllChanged() {
_renderWithZonesChanged = true; _renderWithZonesChanged = true;
_billboardModeChanged = true; _billboardModeChanged = true;
_grab.markAllChanged(); _grab.markAllChanged();
_mirrorModeChanged = true;
_portalExitIDChanged = true;
// Physics // Physics
_densityChanged = true; _densityChanged = true;
@ -4359,6 +4397,12 @@ QList<QString> EntityItemProperties::listChangedProperties() {
out += "billboardMode"; out += "billboardMode";
} }
getGrab().listChangedProperties(out); getGrab().listChangedProperties(out);
if (mirrorModeChanged()) {
out += "mirrorMode";
}
if (portalExitIDChanged()) {
out += "portalExitID";
}
// Physics // Physics
if (densityChanged()) { if (densityChanged()) {

View file

@ -70,6 +70,7 @@
#include "GizmoType.h" #include "GizmoType.h"
#include "TextEffect.h" #include "TextEffect.h"
#include "TextAlignment.h" #include "TextAlignment.h"
#include "MirrorMode.h"
class ScriptEngine; class ScriptEngine;
@ -204,6 +205,8 @@ public:
DEFINE_PROPERTY_REF(PROP_RENDER_WITH_ZONES, RenderWithZones, renderWithZones, QVector<QUuid>, QVector<QUuid>()); DEFINE_PROPERTY_REF(PROP_RENDER_WITH_ZONES, RenderWithZones, renderWithZones, QVector<QUuid>, QVector<QUuid>());
DEFINE_PROPERTY_REF_ENUM(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode, BillboardMode::NONE); DEFINE_PROPERTY_REF_ENUM(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode, BillboardMode::NONE);
DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup); DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup);
DEFINE_PROPERTY_REF_ENUM(PROP_MIRROR_MODE, MirrorMode, mirrorMode, MirrorMode, MirrorMode::NONE);
DEFINE_PROPERTY_REF(PROP_PORTAL_EXIT_ID, PortalExitID, portalExitID, QUuid, UNKNOWN_ENTITY_ID);
// Physics // Physics
DEFINE_PROPERTY(PROP_DENSITY, Density, density, float, ENTITY_ITEM_DEFAULT_DENSITY); DEFINE_PROPERTY(PROP_DENSITY, Density, density, float, ENTITY_ITEM_DEFAULT_DENSITY);

View file

@ -60,6 +60,8 @@ enum EntityPropertyList {
PROP_GRAB_EQUIPPABLE_INDICATOR_URL, PROP_GRAB_EQUIPPABLE_INDICATOR_URL,
PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE,
PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET,
PROP_MIRROR_MODE,
PROP_PORTAL_EXIT_ID,
// Physics // Physics
PROP_DENSITY, PROP_DENSITY,

View file

@ -81,6 +81,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::gl::GLBackend::do_disableContextViewCorrection), (&::gpu::gl::GLBackend::do_disableContextViewCorrection),
(&::gpu::gl::GLBackend::do_restoreContextViewCorrection), (&::gpu::gl::GLBackend::do_restoreContextViewCorrection),
(&::gpu::gl::GLBackend::do_setContextMirrorViewCorrection),
(&::gpu::gl::GLBackend::do_disableContextStereo), (&::gpu::gl::GLBackend::do_disableContextStereo),
(&::gpu::gl::GLBackend::do_restoreContextStereo), (&::gpu::gl::GLBackend::do_restoreContextStereo),
@ -348,6 +349,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) {
case Batch::COMMAND_setViewTransform: case Batch::COMMAND_setViewTransform:
case Batch::COMMAND_setProjectionTransform: case Batch::COMMAND_setProjectionTransform:
case Batch::COMMAND_setProjectionJitter: case Batch::COMMAND_setProjectionJitter:
case Batch::COMMAND_setContextMirrorViewCorrection:
{ {
CommandCall call = _commandCalls[(*command)]; CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset); (this->*(call))(batch, *offset);
@ -413,6 +415,7 @@ void GLBackend::renderPassDraw(const Batch& batch) {
case Batch::COMMAND_setProjectionJitter: case Batch::COMMAND_setProjectionJitter:
case Batch::COMMAND_setViewportTransform: case Batch::COMMAND_setViewportTransform:
case Batch::COMMAND_setDepthRangeTransform: case Batch::COMMAND_setDepthRangeTransform:
case Batch::COMMAND_setContextMirrorViewCorrection:
{ {
PROFILE_RANGE(render_gpu_gl_detail, "transform"); PROFILE_RANGE(render_gpu_gl_detail, "transform");
CommandCall call = _commandCalls[(*command)]; CommandCall call = _commandCalls[(*command)];
@ -619,6 +622,16 @@ void GLBackend::do_restoreContextViewCorrection(const Batch& batch, size_t param
_transform._viewCorrectionEnabled = true; _transform._viewCorrectionEnabled = true;
} }
void GLBackend::do_setContextMirrorViewCorrection(const Batch& batch, size_t paramOffset) {
bool prevMirrorViewCorrection = _transform._mirrorViewCorrection;
_transform._mirrorViewCorrection = batch._params[paramOffset]._uint != 0;
if (_transform._correction.correction != glm::mat4()) {
setCameraCorrection(_transform._mirrorViewCorrection ? _transform._flippedCorrection : _transform._unflippedCorrection, _transform._correction.prevView, false);
_transform._invalidView = true;
}
}
void GLBackend::do_disableContextStereo(const Batch& batch, size_t paramOffset) { void GLBackend::do_disableContextStereo(const Batch& batch, size_t paramOffset) {
} }
@ -997,17 +1010,31 @@ void GLBackend::recycle() const {
_textureManagement._transferEngine->manageMemory(); _textureManagement._transferEngine->manageMemory();
} }
void GLBackend::setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset) { void GLBackend::setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool primary, bool reset) {
auto invCorrection = glm::inverse(correction); auto invCorrection = glm::inverse(correction);
auto invPrevView = glm::inverse(prevRenderView); auto invPrevView = glm::inverse(prevRenderView);
_transform._correction.prevView = (reset ? Mat4() : prevRenderView); _transform._correction.prevView = (reset ? Mat4() : prevRenderView);
_transform._correction.prevViewInverse = (reset ? Mat4() : invPrevView); _transform._correction.prevViewInverse = (reset ? Mat4() : invPrevView);
_transform._correction.correction = correction; _transform._correction.correction = correction;
_transform._correction.correctionInverse = invCorrection; _transform._correction.correctionInverse = invCorrection;
if (!_inRenderTransferPass) {
_pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction); _pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction);
_pipeline._cameraCorrectionBuffer._buffer->flush(); _pipeline._cameraCorrectionBuffer._buffer->flush();
} }
if (primary) {
_transform._unflippedCorrection = _transform._correction.correction;
quat flippedRotation = glm::quat_cast(_transform._unflippedCorrection);
flippedRotation.y *= -1.0f;
flippedRotation.z *= -1.0f;
vec3 flippedTranslation = _transform._unflippedCorrection[3];
flippedTranslation.x *= -1.0f;
_transform._flippedCorrection = glm::translate(glm::mat4_cast(flippedRotation), flippedTranslation);
_transform._mirrorViewCorrection = false;
}
}
void GLBackend::syncProgram(const gpu::ShaderPointer& program) { void GLBackend::syncProgram(const gpu::ShaderPointer& program) {
gpu::gl::GLShader::sync(*this, *program); gpu::gl::GLShader::sync(*this, *program);
} }

View file

@ -121,7 +121,7 @@ public:
// Shutdown rendering and persist any required resources // Shutdown rendering and persist any required resources
void shutdown() override; void shutdown() override;
void setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset = false) override; void setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool primary, bool reset = false) override;
void render(const Batch& batch) final override; void render(const Batch& batch) final override;
// This call synchronize the Full Backend cache with the current GLState // This call synchronize the Full Backend cache with the current GLState
@ -211,6 +211,7 @@ public:
virtual void do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) final; virtual void do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) final;
virtual void do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) final; virtual void do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) final;
virtual void do_setContextMirrorViewCorrection(const Batch& batch, size_t paramOffset) final;
virtual void do_disableContextStereo(const Batch& batch, size_t paramOffset) final; virtual void do_disableContextStereo(const Batch& batch, size_t paramOffset) final;
virtual void do_restoreContextStereo(const Batch& batch, size_t paramOffset) final; virtual void do_restoreContextStereo(const Batch& batch, size_t paramOffset) final;
@ -433,6 +434,9 @@ protected:
Transform _view; Transform _view;
CameraCorrection _correction; CameraCorrection _correction;
bool _viewCorrectionEnabled{ true }; bool _viewCorrectionEnabled{ true };
mat4 _unflippedCorrection;
mat4 _flippedCorrection;
bool _mirrorViewCorrection{ false };
Mat4 _projection; Mat4 _projection;
Vec4i _viewport{ 0, 0, 1, 1 }; Vec4i _viewport{ 0, 0, 1, 1 };

View file

@ -464,6 +464,12 @@ void Batch::restoreContextViewCorrection() {
ADD_COMMAND(restoreContextViewCorrection); ADD_COMMAND(restoreContextViewCorrection);
} }
void Batch::setContextMirrorViewCorrection(bool shouldMirror) {
ADD_COMMAND(setContextMirrorViewCorrection);
uint mirrorFlag = shouldMirror ? 1 : 0;
_params.emplace_back(mirrorFlag);
}
void Batch::disableContextStereo() { void Batch::disableContextStereo() {
ADD_COMMAND(disableContextStereo); ADD_COMMAND(disableContextStereo);
} }

View file

@ -239,6 +239,7 @@ public:
void disableContextViewCorrection(); void disableContextViewCorrection();
void restoreContextViewCorrection(); void restoreContextViewCorrection();
void setContextMirrorViewCorrection(bool shouldMirror);
void disableContextStereo(); void disableContextStereo();
void restoreContextStereo(); void restoreContextStereo();
@ -340,6 +341,7 @@ public:
COMMAND_disableContextViewCorrection, COMMAND_disableContextViewCorrection,
COMMAND_restoreContextViewCorrection, COMMAND_restoreContextViewCorrection,
COMMAND_setContextMirrorViewCorrection,
COMMAND_disableContextStereo, COMMAND_disableContextStereo,
COMMAND_restoreContextStereo, COMMAND_restoreContextStereo,

View file

@ -66,7 +66,7 @@ public:
virtual void syncProgram(const gpu::ShaderPointer& program) = 0; virtual void syncProgram(const gpu::ShaderPointer& program) = 0;
virtual void recycle() const = 0; virtual void recycle() const = 0;
virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0; virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0;
virtual void setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset = false) {} virtual void setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool primary, bool reset = false) {}
virtual bool supportedTextureFormat(const gpu::Element& format) = 0; virtual bool supportedTextureFormat(const gpu::Element& format) = 0;

View file

@ -181,6 +181,7 @@ constexpr const char* COMMAND_NAMES[] = {
"disableContextViewCorrection", "disableContextViewCorrection",
"restoreContextViewCorrection", "restoreContextViewCorrection",
"setContextMirrorViewCorrection",
"disableContextStereo", "disableContextStereo",
"restoreContextStereo", "restoreContextStereo",

View file

@ -27,6 +27,7 @@
#define GRAPHICS_TEXTURE_MATERIAL_ROUGHNESS 4 #define GRAPHICS_TEXTURE_MATERIAL_ROUGHNESS 4
#define GRAPHICS_TEXTURE_MATERIAL_OCCLUSION 5 #define GRAPHICS_TEXTURE_MATERIAL_OCCLUSION 5
#define GRAPHICS_TEXTURE_MATERIAL_SCATTERING 6 #define GRAPHICS_TEXTURE_MATERIAL_SCATTERING 6
#define GRAPHICS_TEXTURE_MATERIAL_MIRROR 1 // Mirrors use albedo textures, but nothing else
// Make sure these match the ones in render-utils/ShaderConstants.h // Make sure these match the ones in render-utils/ShaderConstants.h
#define GRAPHICS_TEXTURE_SKYBOX 11 #define GRAPHICS_TEXTURE_SKYBOX 11
@ -59,6 +60,7 @@ enum Texture {
MaterialRoughness = GRAPHICS_TEXTURE_MATERIAL_ROUGHNESS, MaterialRoughness = GRAPHICS_TEXTURE_MATERIAL_ROUGHNESS,
MaterialOcclusion = GRAPHICS_TEXTURE_MATERIAL_OCCLUSION, MaterialOcclusion = GRAPHICS_TEXTURE_MATERIAL_OCCLUSION,
MaterialScattering = GRAPHICS_TEXTURE_MATERIAL_SCATTERING, MaterialScattering = GRAPHICS_TEXTURE_MATERIAL_SCATTERING,
MaterialMirror = GRAPHICS_TEXTURE_MATERIAL_MIRROR,
Skybox = GRAPHICS_TEXTURE_SKYBOX Skybox = GRAPHICS_TEXTURE_SKYBOX
}; };
} // namespace texture } // namespace texture

View file

@ -290,6 +290,7 @@ enum class EntityVersion : PacketVersion {
UserAgent, UserAgent,
AllBillboardMode, AllBillboardMode,
TextAlignment, TextAlignment,
Mirror,
// Add new versions above here // Add new versions above here
NUM_PACKET_TYPE, NUM_PACKET_TYPE,

View file

@ -42,6 +42,7 @@
#include "GizmoType.h" #include "GizmoType.h"
#include "TextEffect.h" #include "TextEffect.h"
#include "TextAlignment.h" #include "TextAlignment.h"
#include "MirrorMode.h"
#include "OctreeConstants.h" #include "OctreeConstants.h"
#include "OctreeElement.h" #include "OctreeElement.h"
@ -280,6 +281,7 @@ public:
static int unpackDataFromBytes(const unsigned char* dataBytes, GizmoType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, GizmoType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, TextEffect& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, TextEffect& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, TextAlignment& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, TextAlignment& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, MirrorMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result); static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result);
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result); static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result);
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::u8vec3& result); static int unpackDataFromBytes(const unsigned char* dataBytes, glm::u8vec3& result);

View file

@ -71,14 +71,15 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(const Transform
_cauterizedTransform = renderTransform; _cauterizedTransform = renderTransform;
} }
void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode) const { void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode, size_t mirrorDepth) const {
bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) && _enableCauterization; bool useCauterizedMesh = _enableCauterization && (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) &&
mirrorDepth == 0;
if (useCauterizedMesh) { if (useCauterizedMesh) {
if (_cauterizedClusterBuffer) { if (_cauterizedClusterBuffer) {
batch.setUniformBuffer(graphics::slot::buffer::Skinning, _cauterizedClusterBuffer); batch.setUniformBuffer(graphics::slot::buffer::Skinning, _cauterizedClusterBuffer);
} }
batch.setModelTransform(_cauterizedTransform); batch.setModelTransform(_cauterizedTransform);
} else { } else {
ModelMeshPartPayload::bindTransform(batch, transform, renderMode); ModelMeshPartPayload::bindTransform(batch, transform, renderMode, mirrorDepth);
} }
} }

View file

@ -25,7 +25,7 @@ public:
void updateTransformForCauterizedMesh(const Transform& modelTransform, const Model::MeshState& meshState, bool useDualQuaternionSkinning); void updateTransformForCauterizedMesh(const Transform& modelTransform, const Model::MeshState& meshState, bool useDualQuaternionSkinning);
void bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode) const override; void bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode, size_t mirrorDepth) const override;
void setEnableCauterization(bool enableCauterization) { _enableCauterization = enableCauterization; } void setEnableCauterization(bool enableCauterization) { _enableCauterization = enableCauterization; }

View file

@ -286,6 +286,7 @@ void PrepareDeferred::run(const RenderContextPointer& renderContext, const Input
outputs.edit0() = _deferredFramebuffer; outputs.edit0() = _deferredFramebuffer;
outputs.edit1() = _deferredFramebuffer->getLightingFramebuffer(); outputs.edit1() = _deferredFramebuffer->getLightingFramebuffer();
outputs.edit2() = _deferredFramebuffer->getDeferredFramebuffer();
gpu::doInBatch("PrepareDeferred::run", args->_context, [&](gpu::Batch& batch) { gpu::doInBatch("PrepareDeferred::run", args->_context, [&](gpu::Batch& batch) {
batch.enableStereo(false); batch.enableStereo(false);

View file

@ -78,8 +78,8 @@ class PrepareDeferred {
public: public:
// Inputs: primaryFramebuffer and lightingModel // Inputs: primaryFramebuffer and lightingModel
using Inputs = render::VaryingSet2 <gpu::FramebufferPointer, LightingModelPointer>; using Inputs = render::VaryingSet2 <gpu::FramebufferPointer, LightingModelPointer>;
// Output: DeferredFramebuffer, LightingFramebuffer // Output: DeferredFramebuffer, LightingFramebuffer, the framebuffer to be used for mirrors (same as DeferredFramebuffer)
using Outputs = render::VaryingSet2<DeferredFramebufferPointer, gpu::FramebufferPointer>; using Outputs = render::VaryingSet3<DeferredFramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer>;
using JobModel = render::Job::ModelIO<PrepareDeferred, Inputs, Outputs>; using JobModel = render::Job::ModelIO<PrepareDeferred, Inputs, Outputs>;

View file

@ -506,8 +506,9 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren
const auto jitter = inputs.getN<Inputs>(4); const auto jitter = inputs.getN<Inputs>(4);
// Prepare the ShapePipeline // Prepare the ShapePipeline
auto shapePlumber = std::make_shared<ShapePlumber>(); static ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
{ static std::once_flag once;
std::call_once(once, [] {
auto state = std::make_shared<gpu::State>(); auto state = std::make_shared<gpu::State>();
state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setDepthTest(true, true, gpu::LESS_EQUAL);
state->setColorWriteMask(false, false, false, false); state->setColorWriteMask(false, false, false, false);
@ -515,7 +516,7 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren
auto fadeEffect = DependencyManager::get<FadeEffect>(); auto fadeEffect = DependencyManager::get<FadeEffect>();
initZPassPipelines(*shapePlumber, state, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); initZPassPipelines(*shapePlumber, state, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter());
} });
auto sharedParameters = std::make_shared<HighlightSharedParameters>(); auto sharedParameters = std::make_shared<HighlightSharedParameters>();
const auto highlightSelectionNames = task.addJob<SelectionToHighlight>("SelectionToHighlight", sharedParameters); const auto highlightSelectionNames = task.addJob<SelectionToHighlight>("SelectionToHighlight", sharedParameters);

View file

@ -12,6 +12,7 @@
#include "MeshPartPayload.h" #include "MeshPartPayload.h"
#include <BillboardMode.h> #include <BillboardMode.h>
#include <MirrorMode.h>
#include <PerfStat.h> #include <PerfStat.h>
#include <DualQuaternion.h> #include <DualQuaternion.h>
#include <graphics/ShaderConstants.h> #include <graphics/ShaderConstants.h>
@ -188,7 +189,7 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) {
batch.setInputStream(0, _drawMesh->getVertexStream()); batch.setInputStream(0, _drawMesh->getVertexStream());
} }
void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode) const { void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode, size_t mirrorDepth) const {
if (_clusterBuffer) { if (_clusterBuffer) {
batch.setUniformBuffer(graphics::slot::buffer::Skinning, _clusterBuffer); batch.setUniformBuffer(graphics::slot::buffer::Skinning, _clusterBuffer);
} }
@ -220,6 +221,10 @@ void ModelMeshPartPayload::updateKey(const render::ItemKey& key) {
builder.withSubMetaCulled(); builder.withSubMetaCulled();
} }
if (_mirrorMode == MirrorMode::MIRROR || (_mirrorMode == MirrorMode::PORTAL && !_portalExitID.isNull())) {
builder.withMirror();
}
_itemKey = builder.build(); _itemKey = builder.build();
} }
@ -299,8 +304,9 @@ Item::Bound ModelMeshPartPayload::getBound(RenderArgs* args) const {
auto worldBound = _adjustedLocalBound; auto worldBound = _adjustedLocalBound;
auto parentTransform = _parentTransform; auto parentTransform = _parentTransform;
if (args) { if (args) {
bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0;
parentTransform.setRotation(BillboardModeHelpers::getBillboardRotation(parentTransform.getTranslation(), parentTransform.getRotation(), _billboardMode, parentTransform.setRotation(BillboardModeHelpers::getBillboardRotation(parentTransform.getTranslation(), parentTransform.getRotation(), _billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
} }
worldBound.transform(parentTransform); worldBound.transform(parentTransform);
return worldBound; return worldBound;
@ -313,18 +319,19 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
void ModelMeshPartPayload::render(RenderArgs* args) { void ModelMeshPartPayload::render(RenderArgs* args) {
PerformanceTimer perfTimer("ModelMeshPartPayload::render"); PerformanceTimer perfTimer("ModelMeshPartPayload::render");
if (!args || (args->_renderMode == RenderArgs::RenderMode::DEFAULT_RENDER_MODE && _cauterized)) { if (!args || (_cauterized && args->_renderMode == RenderArgs::RenderMode::DEFAULT_RENDER_MODE && args->_mirrorDepth == 0)) {
return; return;
} }
gpu::Batch& batch = *(args->_batch); gpu::Batch& batch = *(args->_batch);
Transform transform = _parentTransform; Transform transform = _parentTransform;
bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0;
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
Transform modelTransform = transform.worldTransform(_localTransform); Transform modelTransform = transform.worldTransform(_localTransform);
bindTransform(batch, modelTransform, args->_renderMode); bindTransform(batch, modelTransform, args->_renderMode, args->_mirrorDepth);
//Bind the index buffer and vertex buffer and Blend shapes if needed //Bind the index buffer and vertex buffer and Blend shapes if needed
bindMesh(batch); bindMesh(batch);
@ -346,7 +353,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
procedural->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, procedural->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created,
ProceduralProgramKey(outColor.a < 1.0f, _shapeKey.isDeformed(), _shapeKey.isDualQuatSkinned())); ProceduralProgramKey(outColor.a < 1.0f, _shapeKey.isDeformed(), _shapeKey.isDualQuatSkinned()));
batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a);
} else { } else if (!_itemKey.isMirror()) {
// apply material properties // apply material properties
if (RenderPipelines::bindMaterials(_drawMaterials, batch, args->_renderMode, args->_enableTexturing)) { if (RenderPipelines::bindMaterials(_drawMaterials, batch, args->_renderMode, args->_enableTexturing)) {
args->_details._materialSwitches++; args->_details._materialSwitches++;
@ -377,6 +384,12 @@ bool ModelMeshPartPayload::passesZoneOcclusionTest(const std::unordered_set<QUui
return true; return true;
} }
ItemID ModelMeshPartPayload::computeMirrorView(ViewFrustum& viewFrustum) const {
Transform transform = _parentTransform;
transform = transform.worldTransform(_localTransform);
return MirrorModeHelpers::computeMirrorView(viewFrustum, transform.getTranslation(), transform.getRotation(), _mirrorMode, _portalExitID);
}
void ModelMeshPartPayload::setBlendshapeBuffer(const std::unordered_map<int, gpu::BufferPointer>& blendshapeBuffers, const QVector<int>& blendedMeshSizes) { void ModelMeshPartPayload::setBlendshapeBuffer(const std::unordered_map<int, gpu::BufferPointer>& blendshapeBuffers, const QVector<int>& blendedMeshSizes) {
if (_meshIndex < blendedMeshSizes.length() && blendedMeshSizes.at(_meshIndex) == _meshNumVertices) { if (_meshIndex < blendedMeshSizes.length() && blendedMeshSizes.at(_meshIndex) == _meshNumVertices) {
auto blendshapeBuffer = blendshapeBuffers.find(_meshIndex); auto blendshapeBuffer = blendshapeBuffers.find(_meshIndex);
@ -426,4 +439,11 @@ template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Poin
} }
return false; return false;
} }
template <> ItemID payloadComputeMirrorView(const ModelMeshPartPayload::Pointer& payload, ViewFrustum& viewFrustum) {
if (payload) {
return payload->computeMirrorView(viewFrustum);
}
return Item::INVALID_ITEM_ID;
}
} }

View file

@ -37,7 +37,7 @@ public:
// ModelMeshPartPayload functions to perform render // ModelMeshPartPayload functions to perform render
void bindMesh(gpu::Batch& batch); void bindMesh(gpu::Batch& batch);
virtual void bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode) const; virtual void bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode, size_t mirrorDepth) const;
void drawCall(gpu::Batch& batch) const; void drawCall(gpu::Batch& batch) const;
void updateKey(const render::ItemKey& key); void updateKey(const render::ItemKey& key);
@ -58,7 +58,10 @@ public:
void setCullWithParent(bool value) { _cullWithParent = value; } void setCullWithParent(bool value) { _cullWithParent = value; }
void setRenderWithZones(const QVector<QUuid>& renderWithZones) { _renderWithZones = renderWithZones; } void setRenderWithZones(const QVector<QUuid>& renderWithZones) { _renderWithZones = renderWithZones; }
void setBillboardMode(BillboardMode billboardMode) { _billboardMode = billboardMode; } void setBillboardMode(BillboardMode billboardMode) { _billboardMode = billboardMode; }
void setMirrorMode(MirrorMode mirrorMode) { _mirrorMode = mirrorMode; }
void setPortalExitID(const QUuid& portalExitID) { _portalExitID = portalExitID; }
bool passesZoneOcclusionTest(const std::unordered_set<QUuid>& containingZones) const; bool passesZoneOcclusionTest(const std::unordered_set<QUuid>& containingZones) const;
render::ItemID computeMirrorView(ViewFrustum& viewFrustum) const;
void addMaterial(graphics::MaterialLayer material) { _drawMaterials.push(material); } void addMaterial(graphics::MaterialLayer material) { _drawMaterials.push(material); }
void removeMaterial(graphics::MaterialPointer material) { _drawMaterials.remove(material); } void removeMaterial(graphics::MaterialPointer material) { _drawMaterials.remove(material); }
@ -93,6 +96,8 @@ private:
bool _cullWithParent { false }; bool _cullWithParent { false };
QVector<QUuid> _renderWithZones; QVector<QUuid> _renderWithZones;
BillboardMode _billboardMode { BillboardMode::NONE }; BillboardMode _billboardMode { BillboardMode::NONE };
MirrorMode _mirrorMode { MirrorMode::NONE };
QUuid _portalExitID;
uint64_t _created; uint64_t _created;
Transform _localTransform; Transform _localTransform;
@ -107,6 +112,8 @@ namespace render {
template <> const ShapeKey shapeGetShapeKey(const ModelMeshPartPayload::Pointer& payload); template <> const ShapeKey shapeGetShapeKey(const ModelMeshPartPayload::Pointer& payload);
template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args); template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args);
template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Pointer& payload, const std::unordered_set<QUuid>& containingZones); template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Pointer& payload, const std::unordered_set<QUuid>& containingZones);
template <> ItemID payloadComputeMirrorView(const ModelMeshPartPayload::Pointer& payload, ViewFrustum& viewFrustum);
} }
#endif // hifi_MeshPartPayload_h #endif // hifi_MeshPartPayload_h

View file

@ -232,6 +232,8 @@ void Model::updateRenderItems() {
auto renderWithZones = self->getRenderWithZones(); auto renderWithZones = self->getRenderWithZones();
auto renderItemKeyGlobalFlags = self->getRenderItemKeyGlobalFlags(); auto renderItemKeyGlobalFlags = self->getRenderItemKeyGlobalFlags();
bool cauterized = self->isCauterized(); bool cauterized = self->isCauterized();
auto mirrorMode = self->getMirrorMode();
const QUuid& portalExitID = self->getPortalExitID();
render::Transaction transaction; render::Transaction transaction;
for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) { for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) {
@ -246,7 +248,7 @@ void Model::updateRenderItems() {
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, meshState, useDualQuaternionSkinning, transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, meshState, useDualQuaternionSkinning,
invalidatePayloadShapeKey, primitiveMode, billboardMode, renderItemKeyGlobalFlags, invalidatePayloadShapeKey, primitiveMode, billboardMode, renderItemKeyGlobalFlags,
cauterized, renderWithZones](ModelMeshPartPayload& data) { cauterized, renderWithZones, mirrorMode, portalExitID](ModelMeshPartPayload& data) {
if (useDualQuaternionSkinning) { if (useDualQuaternionSkinning) {
data.updateClusterBuffer(meshState.clusterDualQuaternions); data.updateClusterBuffer(meshState.clusterDualQuaternions);
data.computeAdjustedLocalBound(meshState.clusterDualQuaternions); data.computeAdjustedLocalBound(meshState.clusterDualQuaternions);
@ -260,6 +262,8 @@ void Model::updateRenderItems() {
data.setCauterized(cauterized); data.setCauterized(cauterized);
data.setRenderWithZones(renderWithZones); data.setRenderWithZones(renderWithZones);
data.setBillboardMode(billboardMode); data.setBillboardMode(billboardMode);
data.setMirrorMode(mirrorMode);
data.setPortalExitID(portalExitID);
data.updateKey(renderItemKeyGlobalFlags); data.updateKey(renderItemKeyGlobalFlags);
data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning); data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning);
}); });
@ -1065,6 +1069,46 @@ void Model::setRenderWithZones(const QVector<QUuid>& renderWithZones, const rend
} }
} }
void Model::setMirrorMode(MirrorMode mirrorMode, const render::ScenePointer& scene) {
if (_mirrorMode != mirrorMode) {
_mirrorMode = mirrorMode;
if (!scene) {
_needsFixupInScene = true;
return;
}
render::Transaction transaction;
auto renderItemsKey = _renderItemKeyGlobalFlags;
for (auto item : _modelMeshRenderItemIDs) {
transaction.updateItem<ModelMeshPartPayload>(item, [mirrorMode, renderItemsKey](ModelMeshPartPayload& data) {
data.setMirrorMode(mirrorMode);
data.updateKey(renderItemsKey);
});
}
scene->enqueueTransaction(transaction);
}
}
void Model::setPortalExitID(const QUuid& portalExitID, const render::ScenePointer& scene) {
if (_portalExitID != portalExitID) {
_portalExitID = portalExitID;
if (!scene) {
_needsFixupInScene = true;
return;
}
render::Transaction transaction;
auto renderItemsKey = _renderItemKeyGlobalFlags;
for (auto item : _modelMeshRenderItemIDs) {
transaction.updateItem<ModelMeshPartPayload>(item, [portalExitID, renderItemsKey](ModelMeshPartPayload& data) {
data.setPortalExitID(portalExitID);
data.updateKey(renderItemsKey);
});
}
scene->enqueueTransaction(transaction);
}
}
const render::ItemKey Model::getRenderItemKeyGlobalFlags() const { const render::ItemKey Model::getRenderItemKeyGlobalFlags() const {
return _renderItemKeyGlobalFlags; return _renderItemKeyGlobalFlags;
} }

View file

@ -41,6 +41,7 @@
#include "Rig.h" #include "Rig.h"
#include "PrimitiveMode.h" #include "PrimitiveMode.h"
#include "BillboardMode.h" #include "BillboardMode.h"
#include "MirrorMode.h"
// Use dual quaternion skinning! // Use dual quaternion skinning!
// Must match define in Skinning.slh // Must match define in Skinning.slh
@ -131,6 +132,12 @@ public:
void setRenderWithZones(const QVector<QUuid>& renderWithZones, const render::ScenePointer& scene = nullptr); void setRenderWithZones(const QVector<QUuid>& renderWithZones, const render::ScenePointer& scene = nullptr);
const QVector<QUuid>& getRenderWithZones() const { return _renderWithZones; } const QVector<QUuid>& getRenderWithZones() const { return _renderWithZones; }
void setMirrorMode(MirrorMode mirrorMode, const render::ScenePointer& scene = nullptr);
MirrorMode getMirrorMode() const { return _mirrorMode; }
void setPortalExitID(const QUuid& portalExitID, const render::ScenePointer& scene = nullptr);
const QUuid& getPortalExitID() const { return _portalExitID; }
// Access the current RenderItemKey Global Flags used by the model and applied to the render items representing the parts of the model. // Access the current RenderItemKey Global Flags used by the model and applied to the render items representing the parts of the model.
const render::ItemKey getRenderItemKeyGlobalFlags() const; const render::ItemKey getRenderItemKeyGlobalFlags() const;
@ -503,6 +510,8 @@ protected:
bool _cauterized { false }; bool _cauterized { false };
bool _cullWithParent { false }; bool _cullWithParent { false };
QVector<QUuid> _renderWithZones; QVector<QUuid> _renderWithZones;
MirrorMode _mirrorMode { MirrorMode::NONE };
QUuid _portalExitID;
bool shouldInvalidatePayloadShapeKey(int meshIndex); bool shouldInvalidatePayloadShapeKey(int meshIndex);

View file

@ -13,7 +13,10 @@
#include "render-utils/ShaderConstants.h" #include "render-utils/ShaderConstants.h"
#include "DeferredLightingEffect.h" #include "DeferredLightingEffect.h"
#include "FadeEffect.h"
#include "RenderUtilsLogging.h" #include "RenderUtilsLogging.h"
#include "RenderViewTask.h"
#include "StencilMaskPass.h"
namespace ru { namespace ru {
using render_utils::slot::texture::Texture; using render_utils::slot::texture::Texture;
@ -25,9 +28,11 @@ namespace gr {
using graphics::slot::buffer::Buffer; using graphics::slot::buffer::Buffer;
} }
using RenderArgsPointer = std::shared_ptr<RenderArgs>;
using namespace render; using namespace render;
extern void initForwardPipelines(ShapePlumber& plumber); extern void initForwardPipelines(ShapePlumber& plumber);
extern void initMirrorPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter, bool forward);
void BeginGPURangeTimer::run(const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer) { void BeginGPURangeTimer::run(const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer) {
timer = _gpuTimer; timer = _gpuTimer;
@ -45,10 +50,14 @@ void EndGPURangeTimer::run(const render::RenderContextPointer& renderContext, co
config->setGPUBatchRunTime(timer->getGPUAverage(), timer->getBatchAverage()); config->setGPUBatchRunTime(timer->getGPUAverage(), timer->getBatchAverage());
} }
render::ShapePlumberPointer DrawLayered3D::_shapePlumber = std::make_shared<ShapePlumber>();
DrawLayered3D::DrawLayered3D(bool opaque) : DrawLayered3D::DrawLayered3D(bool opaque) :
_shapePlumber(std::make_shared<ShapePlumber>()),
_opaquePass(opaque) { _opaquePass(opaque) {
static std::once_flag once;
std::call_once(once, [] {
initForwardPipelines(*_shapePlumber); initForwardPipelines(*_shapePlumber);
});
} }
void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs& inputs) { void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs& inputs) {
@ -262,3 +271,149 @@ void ResolveFramebuffer::run(const render::RenderContextPointer& renderContext,
} }
} }
class SetupMirrorTask {
public:
using Input = RenderMirrorTask::Inputs;
using Outputs = render::VaryingSet4<render::ItemBound, gpu::FramebufferPointer, RenderArgsPointer, glm::vec2>;
using JobModel = render::Job::ModelIO<SetupMirrorTask, Input, Outputs>;
SetupMirrorTask(size_t mirrorIndex, size_t depth) : _mirrorIndex(mirrorIndex), _depth(depth) {}
void run(const render::RenderContextPointer& renderContext, const Input& inputs, Outputs& outputs) {
auto args = renderContext->args;
auto items = inputs.get0();
if (items.empty() || _mirrorIndex > items.size() - 1) {
renderContext->taskFlow.abortTask();
return;
}
auto inputFramebuffer = inputs.get1();
if (!_mirrorFramebuffer || _mirrorFramebuffer->getWidth() != inputFramebuffer->getWidth() || _mirrorFramebuffer->getHeight() != inputFramebuffer->getHeight()) {
_mirrorFramebuffer.reset(gpu::Framebuffer::create("mirror" + _mirrorIndex, gpu::Element::COLOR_SRGBA_32, inputFramebuffer->getWidth(), inputFramebuffer->getHeight()));
}
render::ItemBound mirror = items[_mirrorIndex];
_cachedArgsPointer->_renderMode = args->_renderMode;
_cachedArgsPointer->_blitFramebuffer = args->_blitFramebuffer;
_cachedArgsPointer->_ignoreItem = args->_ignoreItem;
_cachedArgsPointer->_mirrorDepth = args->_mirrorDepth;
_cachedArgsPointer->_numMirrorFlips = args->_numMirrorFlips;
ViewFrustum srcViewFrustum = args->getViewFrustum();
ItemID portalExitID = args->_scene->getItem(mirror.id).computeMirrorView(srcViewFrustum);
args->_blitFramebuffer = _mirrorFramebuffer;
args->_ignoreItem = portalExitID != Item::INVALID_ITEM_ID ? portalExitID : mirror.id;
args->_mirrorDepth = _depth;
args->_numMirrorFlips += portalExitID != Item::INVALID_ITEM_ID ? 0 : 1;
gpu::doInBatch("SetupMirrorTask::run", args->_context, [&](gpu::Batch& batch) {
bool shouldMirror = args->_numMirrorFlips % 2 == (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE);
batch.setContextMirrorViewCorrection(shouldMirror);
});
// Without calculating the bound planes, the mirror will use the same culling frustum as the main camera,
// which is not what we want here.
srcViewFrustum.calculate();
args->pushViewFrustum(srcViewFrustum);
outputs.edit0() = mirror;
outputs.edit1() = inputFramebuffer;
outputs.edit2() = _cachedArgsPointer;
outputs.edit3() = inputs.get2();
}
protected:
gpu::FramebufferPointer _mirrorFramebuffer { nullptr };
RenderArgsPointer _cachedArgsPointer { std::make_shared<RenderArgs>() };
size_t _mirrorIndex;
size_t _depth;
};
class DrawMirrorTask {
public:
using Inputs = SetupMirrorTask::Outputs;
using JobModel = render::Job::ModelI<DrawMirrorTask, Inputs>;
DrawMirrorTask() {
static std::once_flag once;
std::call_once(once, [this] {
auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, true, gpu::LESS_EQUAL);
PrepareStencil::testMaskDrawShape(*state);
auto fadeEffect = DependencyManager::get<FadeEffect>();
initMirrorPipelines(*_forwardPipelines, state, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter(), true);
initMirrorPipelines(*_deferredPipelines, state, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter(), false);
});
}
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
auto args = renderContext->args;
auto mirror = inputs.get0();
auto framebuffer = inputs.get1();
auto cachedArgs = inputs.get2();
auto jitter = inputs.get3();
if (cachedArgs) {
args->_renderMode = cachedArgs->_renderMode;
}
args->popViewFrustum();
gpu::doInBatch("DrawMirrorTask::run", args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
if (cachedArgs) {
bool shouldMirror = cachedArgs->_numMirrorFlips % 2 == (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE);
batch.setContextMirrorViewCorrection(shouldMirror);
}
batch.setFramebuffer(framebuffer);
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
glm::mat4 projMat;
Transform viewMat;
args->getViewFrustum().evalProjectionMatrix(projMat);
args->getViewFrustum().evalViewTransform(viewMat);
batch.setProjectionTransform(projMat);
batch.setProjectionJitter(jitter.x, jitter.y);
batch.setViewTransform(viewMat);
batch.setResourceTexture(gr::Texture::MaterialMirror, args->_blitFramebuffer->getRenderBuffer(0));
renderShapes(renderContext, args->_renderMethod == render::Args::RenderMethod::FORWARD ? _forwardPipelines : _deferredPipelines, { mirror });
args->_batch = nullptr;
});
if (cachedArgs) {
// Restore the blit framebuffer after we've sampled from it
args->_blitFramebuffer = cachedArgs->_blitFramebuffer;
args->_ignoreItem = cachedArgs->_ignoreItem;
args->_mirrorDepth = cachedArgs->_mirrorDepth;
args->_numMirrorFlips = cachedArgs->_numMirrorFlips;
}
}
private:
static ShapePlumberPointer _forwardPipelines;
static ShapePlumberPointer _deferredPipelines;
};
ShapePlumberPointer DrawMirrorTask::_forwardPipelines = std::make_shared<ShapePlumber>();
ShapePlumberPointer DrawMirrorTask::_deferredPipelines = std::make_shared<ShapePlumber>();
void RenderMirrorTask::build(JobModel& task, const render::Varying& inputs, render::Varying& output, size_t mirrorIndex, render::CullFunctor cullFunctor, size_t depth) {
size_t nextDepth = depth + 1;
const auto setupOutput = task.addJob<SetupMirrorTask>("SetupMirror" + std::to_string(mirrorIndex) + "Depth" + std::to_string(depth), inputs, mirrorIndex, nextDepth);
task.addJob<RenderViewTask>("RenderMirrorView" + std::to_string(mirrorIndex) + "Depth" + std::to_string(depth), cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1, nextDepth);
task.addJob<DrawMirrorTask>("DrawMirrorTask" + std::to_string(mirrorIndex) + "Depth" + std::to_string(depth), setupOutput);
}

View file

@ -10,6 +10,8 @@
#define hifi_RenderCommonTask_h #define hifi_RenderCommonTask_h
#include <gpu/Pipeline.h> #include <gpu/Pipeline.h>
#include <render/CullTask.h>
#include "LightStage.h" #include "LightStage.h"
#include "HazeStage.h" #include "HazeStage.h"
#include "LightingModel.h" #include "LightingModel.h"
@ -71,7 +73,7 @@ public:
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
protected: protected:
render::ShapePlumberPointer _shapePlumber; static render::ShapePlumberPointer _shapePlumber;
int _maxDrawn; // initialized by Config int _maxDrawn; // initialized by Config
bool _opaquePass { true }; bool _opaquePass { true };
}; };
@ -152,4 +154,17 @@ protected:
render::Args::RenderMethod _method; render::Args::RenderMethod _method;
}; };
class RenderMirrorTask {
public:
using Inputs = render::VaryingSet3<render::ItemBounds, gpu::FramebufferPointer, glm::vec2>;
using JobModel = render::Task::ModelI<RenderMirrorTask, Inputs>;
RenderMirrorTask() {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& output, size_t mirrorIndex, render::CullFunctor cullFunctor, size_t depth);
static const size_t MAX_MIRROR_DEPTH { 3 };
static const size_t MAX_MIRRORS_PER_LEVEL { 3 };
};
#endif // hifi_RenderDeferredTask_h #endif // hifi_RenderDeferredTask_h

View file

@ -100,11 +100,14 @@ void RenderDeferredTask::configure(const Config& config) {
preparePrimaryBufferConfig->setResolutionScale(config.resolutionScale); preparePrimaryBufferConfig->setResolutionScale(config.resolutionScale);
} }
void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, size_t depth) {
auto fadeEffect = DependencyManager::get<FadeEffect>(); static auto fadeEffect = DependencyManager::get<FadeEffect>();
// Prepare the ShapePipelines // Prepare the ShapePipelines
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>(); static ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
static std::once_flag once;
std::call_once(once, [] {
initDeferredPipelines(*shapePlumber, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); initDeferredPipelines(*shapePlumber, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter());
});
const auto& inputs = input.get<Input>(); const auto& inputs = input.get<Input>();
@ -116,6 +119,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
// Extract opaques / transparents / lights / metas / layered / background // Extract opaques / transparents / lights / metas / layered / background
const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE]; const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE];
const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE];
const auto& mirrors = items[RenderFetchCullSortTask::MIRROR];
const auto& inFrontOpaque = items[RenderFetchCullSortTask::LAYER_FRONT_OPAQUE_SHAPE]; const auto& inFrontOpaque = items[RenderFetchCullSortTask::LAYER_FRONT_OPAQUE_SHAPE];
const auto& inFrontTransparent = items[RenderFetchCullSortTask::LAYER_FRONT_TRANSPARENT_SHAPE]; const auto& inFrontTransparent = items[RenderFetchCullSortTask::LAYER_FRONT_TRANSPARENT_SHAPE];
const auto& hudOpaque = items[RenderFetchCullSortTask::LAYER_HUD_OPAQUE_SHAPE]; const auto& hudOpaque = items[RenderFetchCullSortTask::LAYER_HUD_OPAQUE_SHAPE];
@ -139,7 +143,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
// Shadow Stage Frame // Shadow Stage Frame
const auto shadowFrame = shadowTaskOutputs[1]; const auto shadowFrame = shadowTaskOutputs[1];
fadeEffect->build(task, opaques); fadeEffect->build(task, opaques);
const auto jitter = task.addJob<JitterSample>("JitterCam"); const auto jitter = task.addJob<JitterSample>("JitterCam");
@ -154,6 +157,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
const auto prepareDeferredOutputs = task.addJob<PrepareDeferred>("PrepareDeferred", prepareDeferredInputs); const auto prepareDeferredOutputs = task.addJob<PrepareDeferred>("PrepareDeferred", prepareDeferredInputs);
const auto deferredFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(0); const auto deferredFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(0);
const auto lightingFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(1); const auto lightingFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(1);
const auto mirrorTargetFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(2);
// draw a stencil mask in hidden regions of the framebuffer. // draw a stencil mask in hidden regions of the framebuffer.
task.addJob<PrepareStencil>("PrepareStencil", scaledPrimaryFramebuffer); task.addJob<PrepareStencil>("PrepareStencil", scaledPrimaryFramebuffer);
@ -162,6 +166,13 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel, jitter).asVarying(); const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel, jitter).asVarying();
task.addJob<DrawStateSortDeferred>("DrawOpaqueDeferred", opaqueInputs, shapePlumber); task.addJob<DrawStateSortDeferred>("DrawOpaqueDeferred", opaqueInputs, shapePlumber);
if (depth < RenderMirrorTask::MAX_MIRROR_DEPTH) {
const auto mirrorInputs = RenderMirrorTask::Inputs(mirrors, mirrorTargetFramebuffer, jitter).asVarying();
for (size_t i = 0; i < RenderMirrorTask::MAX_MIRRORS_PER_LEVEL; i++) {
task.addJob<RenderMirrorTask>("RenderMirrorTask" + std::to_string(i) + "Depth" + std::to_string(depth), mirrorInputs, i, cullFunctor, depth);
}
}
// Opaque all rendered // Opaque all rendered
// Linear Depth Pass // Linear Depth Pass
@ -234,7 +245,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
// Lighting Buffer ready for tone mapping // Lighting Buffer ready for tone mapping
const auto toneMappingInputs = ToneMapAndResample::Input(lightingFramebuffer, destFramebuffer).asVarying(); const auto toneMappingInputs = ToneMapAndResample::Input(lightingFramebuffer, destFramebuffer).asVarying();
const auto toneMappedBuffer = task.addJob<ToneMapAndResample>("ToneMapping", toneMappingInputs); const auto toneMappedBuffer = task.addJob<ToneMapAndResample>("ToneMapping", toneMappingInputs, depth);
// Debugging task is happening in the "over" layer after tone mapping and just before HUD // Debugging task is happening in the "over" layer after tone mapping and just before HUD
{ // Debug the bounds of the rendered items, still look at the zbuffer { // Debug the bounds of the rendered items, still look at the zbuffer
@ -398,8 +409,12 @@ void RenderDeferredTaskDebug::build(JobModel& task, const render::Varying& input
// Status icon rendering job // Status icon rendering job
{ {
// Grab a texture map representing the different status icons and assign that to the drawStatusJob // Grab a texture map representing the different status icons and assign that to the drawStatusJob
static gpu::TexturePointer statusIconMap;
static std::once_flag once;
std::call_once(once, [] {
auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg";
auto statusIconMap = DependencyManager::get<TextureCache>()->getImageTexture(iconMapPath, image::TextureUsage::STRICT_TEXTURE); statusIconMap = DependencyManager::get<TextureCache>()->getImageTexture(iconMapPath, image::TextureUsage::STRICT_TEXTURE);
});
const auto drawStatusInputs = DrawStatus::Input(opaques, jitter).asVarying(); const auto drawStatusInputs = DrawStatus::Input(opaques, jitter).asVarying();
task.addJob<DrawStatus>("DrawStatus", drawStatusInputs, DrawStatus(statusIconMap)); task.addJob<DrawStatus>("DrawStatus", drawStatusInputs, DrawStatus(statusIconMap));
} }
@ -407,8 +422,6 @@ void RenderDeferredTaskDebug::build(JobModel& task, const render::Varying& input
const auto debugZoneInputs = DebugZoneLighting::Inputs(deferredFrameTransform, lightFrame, backgroundFrame).asVarying(); const auto debugZoneInputs = DebugZoneLighting::Inputs(deferredFrameTransform, lightFrame, backgroundFrame).asVarying();
task.addJob<DebugZoneLighting>("DrawZoneStack", debugZoneInputs); task.addJob<DebugZoneLighting>("DrawZoneStack", debugZoneInputs);
} }
} }
gpu::FramebufferPointer PreparePrimaryFramebuffer::createFramebuffer(const char* name, const glm::uvec2& frameSize) { gpu::FramebufferPointer PreparePrimaryFramebuffer::createFramebuffer(const char* name, const glm::uvec2& frameSize) {

View file

@ -144,7 +144,7 @@ public:
RenderDeferredTask(); RenderDeferredTask();
void configure(const Config& config); void configure(const Config& config);
void build(JobModel& task, const render::Varying& input, render::Varying& output); void build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, size_t depth);
private: private:
}; };

View file

@ -34,6 +34,8 @@
#include "RenderCommonTask.h" #include "RenderCommonTask.h"
#include "RenderHUDLayerTask.h" #include "RenderHUDLayerTask.h"
#include "RenderViewTask.h"
namespace ru { namespace ru {
using render_utils::slot::texture::Texture; using render_utils::slot::texture::Texture;
using render_utils::slot::buffer::Buffer; using render_utils::slot::buffer::Buffer;
@ -66,13 +68,16 @@ void RenderForwardTask::configure(const Config& config) {
preparePrimaryBufferConfig->setResolutionScale(config.resolutionScale); preparePrimaryBufferConfig->setResolutionScale(config.resolutionScale);
} }
void RenderForwardTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { void RenderForwardTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, size_t depth) {
task.addJob<SetRenderMethod>("SetRenderMethodTask", render::Args::FORWARD); task.addJob<SetRenderMethod>("SetRenderMethodTask", render::Args::FORWARD);
// Prepare the ShapePipelines // Prepare the ShapePipelines
auto fadeEffect = DependencyManager::get<FadeEffect>(); auto fadeEffect = DependencyManager::get<FadeEffect>();
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>(); static ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
static std::once_flag once;
std::call_once(once, [] {
initForwardPipelines(*shapePlumber); initForwardPipelines(*shapePlumber);
});
// Unpack inputs // Unpack inputs
const auto& inputs = input.get<Input>(); const auto& inputs = input.get<Input>();
@ -86,6 +91,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE]; const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE];
const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE];
const auto& metas = items[RenderFetchCullSortTask::META]; const auto& metas = items[RenderFetchCullSortTask::META];
const auto& mirrors = items[RenderFetchCullSortTask::MIRROR];
const auto& inFrontOpaque = items[RenderFetchCullSortTask::LAYER_FRONT_OPAQUE_SHAPE]; const auto& inFrontOpaque = items[RenderFetchCullSortTask::LAYER_FRONT_OPAQUE_SHAPE];
const auto& inFrontTransparent = items[RenderFetchCullSortTask::LAYER_FRONT_TRANSPARENT_SHAPE]; const auto& inFrontTransparent = items[RenderFetchCullSortTask::LAYER_FRONT_TRANSPARENT_SHAPE];
const auto& hudOpaque = items[RenderFetchCullSortTask::LAYER_HUD_OPAQUE_SHAPE]; const auto& hudOpaque = items[RenderFetchCullSortTask::LAYER_HUD_OPAQUE_SHAPE];
@ -107,7 +113,6 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
// First job, alter faded // First job, alter faded
fadeEffect->build(task, opaques); fadeEffect->build(task, opaques);
// GPU jobs: Start preparing the main framebuffer // GPU jobs: Start preparing the main framebuffer
const auto scaledPrimaryFramebuffer = task.addJob<PreparePrimaryFramebufferMSAA>("PreparePrimaryBufferForward"); const auto scaledPrimaryFramebuffer = task.addJob<PreparePrimaryFramebufferMSAA>("PreparePrimaryBufferForward");
@ -125,6 +130,16 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
const auto opaqueInputs = DrawForward::Inputs(opaques, lightingModel, hazeFrame).asVarying(); const auto opaqueInputs = DrawForward::Inputs(opaques, lightingModel, hazeFrame).asVarying();
task.addJob<DrawForward>("DrawOpaques", opaqueInputs, shapePlumber, true); task.addJob<DrawForward>("DrawOpaques", opaqueInputs, shapePlumber, true);
const auto nullJitter = Varying(glm::vec2(0.0f, 0.0f));
#ifndef Q_OS_ANDROID
if (depth < RenderMirrorTask::MAX_MIRROR_DEPTH) {
const auto mirrorInputs = RenderMirrorTask::Inputs(mirrors, scaledPrimaryFramebuffer, nullJitter).asVarying();
for (size_t i = 0; i < RenderMirrorTask::MAX_MIRRORS_PER_LEVEL; i++) {
task.addJob<RenderMirrorTask>("RenderMirrorTask" + std::to_string(i) + "Depth" + std::to_string(depth), mirrorInputs, i, cullFunctor, depth);
}
}
#endif
// Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job // Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job
const auto backgroundInputs = DrawBackgroundStage::Inputs(lightingModel, backgroundFrame, hazeFrame).asVarying(); const auto backgroundInputs = DrawBackgroundStage::Inputs(lightingModel, backgroundFrame, hazeFrame).asVarying();
task.addJob<DrawBackgroundStage>("DrawBackgroundForward", backgroundInputs); task.addJob<DrawBackgroundStage>("DrawBackgroundForward", backgroundInputs);
@ -134,7 +149,6 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
task.addJob<DrawForward>("DrawTransparents", transparentInputs, shapePlumber, false); task.addJob<DrawForward>("DrawTransparents", transparentInputs, shapePlumber, false);
// Layered // Layered
const auto nullJitter = Varying(glm::vec2(0.0f, 0.0f));
const auto inFrontOpaquesInputs = DrawLayered3D::Inputs(inFrontOpaque, lightingModel, hazeFrame, nullJitter).asVarying(); const auto inFrontOpaquesInputs = DrawLayered3D::Inputs(inFrontOpaque, lightingModel, hazeFrame, nullJitter).asVarying();
const auto inFrontTransparentsInputs = DrawLayered3D::Inputs(inFrontTransparent, lightingModel, hazeFrame, nullJitter).asVarying(); const auto inFrontTransparentsInputs = DrawLayered3D::Inputs(inFrontTransparent, lightingModel, hazeFrame, nullJitter).asVarying();
task.addJob<DrawLayered3D>("DrawInFrontOpaque", inFrontOpaquesInputs, true); task.addJob<DrawLayered3D>("DrawInFrontOpaque", inFrontOpaquesInputs, true);
@ -159,7 +173,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
const auto destFramebuffer = static_cast<gpu::FramebufferPointer>(nullptr); const auto destFramebuffer = static_cast<gpu::FramebufferPointer>(nullptr);
const auto toneMappingInputs = ToneMapAndResample::Input(resolvedFramebuffer, destFramebuffer).asVarying(); const auto toneMappingInputs = ToneMapAndResample::Input(resolvedFramebuffer, destFramebuffer).asVarying();
const auto toneMappedBuffer = task.addJob<ToneMapAndResample>("ToneMapping", toneMappingInputs); const auto toneMappedBuffer = task.addJob<ToneMapAndResample>("ToneMapping", toneMappingInputs, depth);
// HUD Layer // HUD Layer
const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(toneMappedBuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying(); const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(toneMappedBuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying();
task.addJob<RenderHUDLayerTask>("RenderHUDLayer", renderHUDLayerInputs); task.addJob<RenderHUDLayerTask>("RenderHUDLayer", renderHUDLayerInputs);

View file

@ -36,7 +36,7 @@ public:
RenderForwardTask() {} RenderForwardTask() {}
void configure(const Config& config); void configure(const Config& config);
void build(JobModel& task, const render::Varying& input, render::Varying& output); void build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, size_t depth);
}; };

View file

@ -16,8 +16,8 @@ void CompositeHUD::run(const RenderContextPointer& renderContext, const gpu::Fra
assert(renderContext->args); assert(renderContext->args);
assert(renderContext->args->_context); assert(renderContext->args->_context);
// We do not want to render HUD elements in secondary camera // We do not want to render HUD elements in secondary camera or mirrors
if (nsightActive() || renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) { if (nsightActive() || renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE || renderContext->args->_mirrorDepth > 0) {
return; return;
} }

View file

@ -42,6 +42,7 @@ namespace gr {
void initDeferredPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); void initDeferredPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter);
void initForwardPipelines(ShapePlumber& plumber); void initForwardPipelines(ShapePlumber& plumber);
void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter);
void initMirrorPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter, bool forward);
void addPlumberPipeline(ShapePlumber& plumber, void addPlumberPipeline(ShapePlumber& plumber,
const ShapeKey& key, int programId, const ShapeKey& key, int programId,
@ -368,6 +369,45 @@ void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state, con
gpu::Shader::createProgram(model_shadow_fade_deformeddq), state, extraBatchSetter, itemSetter); gpu::Shader::createProgram(model_shadow_fade_deformeddq), state, extraBatchSetter, itemSetter);
} }
void initMirrorPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& extraBatchSetter, const render::ShapePipeline::ItemSetter& itemSetter, bool forward) {
using namespace shader::render_utils::program;
if (forward) {
shapePlumber.addPipeline(
ShapeKey::Filter::Builder().withoutDeformed().withoutFade(),
gpu::Shader::createProgram(model_shadow_mirror_forward), state);
shapePlumber.addPipeline(
ShapeKey::Filter::Builder().withDeformed().withoutDualQuatSkinned().withoutFade(),
gpu::Shader::createProgram(model_shadow_mirror_forward_deformed), state);
shapePlumber.addPipeline(
ShapeKey::Filter::Builder().withDeformed().withDualQuatSkinned().withoutFade(),
gpu::Shader::createProgram(model_shadow_mirror_forward_deformeddq), state);
} else {
shapePlumber.addPipeline(
ShapeKey::Filter::Builder().withoutDeformed().withoutFade(),
gpu::Shader::createProgram(model_shadow_mirror), state);
shapePlumber.addPipeline(
ShapeKey::Filter::Builder().withoutDeformed().withFade(),
gpu::Shader::createProgram(model_shadow_mirror_fade), state, extraBatchSetter, itemSetter);
shapePlumber.addPipeline(
ShapeKey::Filter::Builder().withDeformed().withoutDualQuatSkinned().withoutFade(),
gpu::Shader::createProgram(model_shadow_mirror_deformed), state);
shapePlumber.addPipeline(
ShapeKey::Filter::Builder().withDeformed().withoutDualQuatSkinned().withFade(),
gpu::Shader::createProgram(model_shadow_mirror_fade_deformed), state, extraBatchSetter, itemSetter);
shapePlumber.addPipeline(
ShapeKey::Filter::Builder().withDeformed().withDualQuatSkinned().withoutFade(),
gpu::Shader::createProgram(model_shadow_mirror_deformeddq), state);
shapePlumber.addPipeline(
ShapeKey::Filter::Builder().withDeformed().withDualQuatSkinned().withFade(),
gpu::Shader::createProgram(model_shadow_mirror_fade_deformeddq), state, extraBatchSetter, itemSetter);
}
}
bool RenderPipelines::bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, render::Args::RenderMode renderMode, bool enableTextures) { bool RenderPipelines::bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, render::Args::RenderMode renderMode, bool enableTextures) {
graphics::MultiMaterial multiMaterial; graphics::MultiMaterial multiMaterial;
multiMaterial.push(graphics::MaterialLayer(material, 0)); multiMaterial.push(graphics::MaterialLayer(material, 0));

View file

@ -46,15 +46,16 @@ void RenderShadowTask::configure(const Config& configuration) {
void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cameraCullFunctor, uint8_t tagBits, uint8_t tagMask) { void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cameraCullFunctor, uint8_t tagBits, uint8_t tagMask) {
// Prepare the ShapePipeline // Prepare the ShapePipeline
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>(); static ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
{ static std::once_flag once;
std::call_once(once, [] {
auto state = std::make_shared<gpu::State>(); auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_BACK); state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setDepthTest(true, true, gpu::LESS_EQUAL);
auto fadeEffect = DependencyManager::get<FadeEffect>(); auto fadeEffect = DependencyManager::get<FadeEffect>();
initZPassPipelines(*shapePlumber, state, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); initZPassPipelines(*shapePlumber, state, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter());
} });
const auto setupOutput = task.addJob<RenderShadowSetup>("ShadowSetup", input); const auto setupOutput = task.addJob<RenderShadowSetup>("ShadowSetup", input);
const auto queryResolution = setupOutput.getN<RenderShadowSetup::Output>(1); const auto queryResolution = setupOutput.getN<RenderShadowSetup::Output>(1);
const auto shadowFrame = setupOutput.getN<RenderShadowSetup::Output>(3); const auto shadowFrame = setupOutput.getN<RenderShadowSetup::Output>(3);

View file

@ -15,7 +15,7 @@
#include "RenderDeferredTask.h" #include "RenderDeferredTask.h"
#include "RenderForwardTask.h" #include "RenderForwardTask.h"
void RenderShadowsAndDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { void RenderShadowsAndDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask, size_t depth) {
task.addJob<SetRenderMethod>("SetRenderMethodTask", render::Args::DEFERRED); task.addJob<SetRenderMethod>("SetRenderMethodTask", render::Args::DEFERRED);
const auto items = input.getN<DeferredForwardSwitchJob::Input>(0); const auto items = input.getN<DeferredForwardSwitchJob::Input>(0);
@ -28,16 +28,16 @@ void RenderShadowsAndDeferredTask::build(JobModel& task, const render::Varying&
const auto shadowTaskOut = task.addJob<RenderShadowTask>("RenderShadowTask", shadowTaskIn, cullFunctor, tagBits, tagMask); const auto shadowTaskOut = task.addJob<RenderShadowTask>("RenderShadowTask", shadowTaskIn, cullFunctor, tagBits, tagMask);
const auto renderDeferredInput = RenderDeferredTask::Input(items, lightingModel, lightingStageFramesAndZones, shadowTaskOut).asVarying(); const auto renderDeferredInput = RenderDeferredTask::Input(items, lightingModel, lightingStageFramesAndZones, shadowTaskOut).asVarying();
task.addJob<RenderDeferredTask>("RenderDeferredTask", renderDeferredInput); task.addJob<RenderDeferredTask>("RenderDeferredTask", renderDeferredInput, cullFunctor, depth);
} }
void DeferredForwardSwitchJob::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { void DeferredForwardSwitchJob::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask, size_t depth) {
task.addBranch<RenderShadowsAndDeferredTask>("RenderShadowsAndDeferredTask", 0, input, cullFunctor, tagBits, tagMask); task.addBranch<RenderShadowsAndDeferredTask>("RenderShadowsAndDeferredTask", 0, input, cullFunctor, tagBits, tagMask, depth);
task.addBranch<RenderForwardTask>("RenderForwardTask", 1, input); task.addBranch<RenderForwardTask>("RenderForwardTask", 1, input, cullFunctor, depth);
} }
void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask, size_t depth) {
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, tagBits, tagMask); const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, tagBits, tagMask);
// Issue the lighting model, aka the big global settings for the view // Issue the lighting model, aka the big global settings for the view
@ -48,7 +48,7 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render:
#ifndef Q_OS_ANDROID #ifndef Q_OS_ANDROID
const auto deferredForwardIn = DeferredForwardSwitchJob::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); const auto deferredForwardIn = DeferredForwardSwitchJob::Input(items, lightingModel, lightingStageFramesAndZones).asVarying();
task.addJob<DeferredForwardSwitchJob>("DeferredForwardSwitch", deferredForwardIn, cullFunctor, tagBits, tagMask); task.addJob<DeferredForwardSwitchJob>("DeferredForwardSwitch", deferredForwardIn, cullFunctor, tagBits, tagMask, depth);
#else #else
const auto renderInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); const auto renderInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying();
task.addJob<RenderForwardTask>("RenderForwardTask", renderInput); task.addJob<RenderForwardTask>("RenderForwardTask", renderInput);

View file

@ -24,7 +24,7 @@ public:
RenderShadowsAndDeferredTask() {} RenderShadowsAndDeferredTask() {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask); void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask, size_t depth);
}; };
@ -36,7 +36,7 @@ public:
DeferredForwardSwitchJob() {} DeferredForwardSwitchJob() {}
void configure(const render::SwitchConfig& config) {} void configure(const render::SwitchConfig& config) {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask); void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask, size_t depth);
}; };
@ -47,7 +47,7 @@ public:
RenderViewTask() {} RenderViewTask() {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00); void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00, size_t depth = 0);
}; };

View file

@ -40,21 +40,18 @@ float TextRenderer3D::getFontSize() const {
return 0.0f; return 0.0f;
} }
void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, void TextRenderer3D::draw(gpu::Batch& batch, const Font::DrawProps& props) {
const QString& str, const glm::vec4& color, bool unlit, bool forward) {
if (_font) { if (_font) {
_font->drawString(batch, _drawInfo, str, color, glm::vec3(0.0f), 0, TextEffect::NO_EFFECT, TextAlignment::LEFT, { x, y }, bounds, 1.0f, unlit, forward); _font->drawString(batch, _drawInfo, props);
} }
} }
void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, float scale, void TextRenderer3D::draw(gpu::Batch& batch, const QString& font, const Font::DrawProps& props) {
const QString& str, const QString& font, const glm::vec4& color, const glm::vec3& effectColor,
float effectThickness, TextEffect effect, TextAlignment alignment, bool unlit, bool forward) {
if (font != _family) { if (font != _family) {
_family = font; _family = font;
_font = Font::load(_family); _font = Font::load(_family);
} }
if (_font) { if (_font) {
_font->drawString(batch, _drawInfo, str, color, effectColor, effectThickness, effect, alignment, { x, y }, bounds, scale, unlit, forward); _font->drawString(batch, _drawInfo, props);
} }
} }

View file

@ -27,11 +27,8 @@ public:
glm::vec2 computeExtent(const QString& str) const; glm::vec2 computeExtent(const QString& str) const;
float getFontSize() const; // Pixel size float getFontSize() const; // Pixel size
void draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, void draw(gpu::Batch& batch, const Font::DrawProps& props);
const QString& str, const glm::vec4& color, bool unlit, bool forward); void draw(gpu::Batch& batch, const QString& font, const Font::DrawProps& props);
void draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, float scale,
const QString& str, const QString& font, const glm::vec4& color, const glm::vec3& effectColor,
float effectThickness, TextEffect effect, TextAlignment alignment, bool unlit, bool forward);
private: private:
TextRenderer3D(const char* family); TextRenderer3D(const char* family);

View file

@ -25,9 +25,10 @@ using namespace shader::render_utils::program;
gpu::PipelinePointer ToneMapAndResample::_pipeline; gpu::PipelinePointer ToneMapAndResample::_pipeline;
gpu::PipelinePointer ToneMapAndResample::_mirrorPipeline; gpu::PipelinePointer ToneMapAndResample::_mirrorPipeline;
ToneMapAndResample::ToneMapAndResample() { ToneMapAndResample::ToneMapAndResample(size_t depth) {
Parameters parameters; Parameters parameters;
_parametersBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Parameters), (const gpu::Byte*) &parameters)); _parametersBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Parameters), (const gpu::Byte*) &parameters));
_depth = depth;
} }
void ToneMapAndResample::init() { void ToneMapAndResample::init() {
@ -95,7 +96,8 @@ void ToneMapAndResample::run(const RenderContextPointer& renderContext, const In
batch.setViewportTransform(destViewport); batch.setViewportTransform(destViewport);
batch.setProjectionTransform(glm::mat4()); batch.setProjectionTransform(glm::mat4());
batch.resetViewTransform(); batch.resetViewTransform();
batch.setPipeline(args->_renderMode == RenderArgs::MIRROR_RENDER_MODE ? _mirrorPipeline : _pipeline); bool shouldMirror = args->_numMirrorFlips >= (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE);
batch.setPipeline(shouldMirror ? _mirrorPipeline : _pipeline);
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(srcBufferSize, args->_viewport)); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(srcBufferSize, args->_viewport));
batch.setUniformBuffer(render_utils::slot::buffer::ToneMappingParams, _parametersBuffer); batch.setUniformBuffer(render_utils::slot::buffer::ToneMappingParams, _parametersBuffer);

View file

@ -49,11 +49,9 @@ signals:
class ToneMapAndResample { class ToneMapAndResample {
public: public:
ToneMapAndResample(); ToneMapAndResample(size_t depth);
virtual ~ToneMapAndResample() {} virtual ~ToneMapAndResample() {}
void render(RenderArgs* args, const gpu::TexturePointer& lightingBuffer, gpu::FramebufferPointer& destinationBuffer);
void setExposure(float exposure); void setExposure(float exposure);
float getExposure() const { return _parametersBuffer.get<Parameters>()._exposure; } float getExposure() const { return _parametersBuffer.get<Parameters>()._exposure; }
@ -76,6 +74,7 @@ protected:
gpu::FramebufferPointer _destinationFrameBuffer; gpu::FramebufferPointer _destinationFrameBuffer;
float _factor { 2.0f }; float _factor { 2.0f };
size_t _depth { 0 };
gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer); gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer);

View file

@ -36,7 +36,15 @@
<@include DeferredBufferWrite.slh@> <@include DeferredBufferWrite.slh@>
<@endif@> <@endif@>
<@else@> <@else@>
<@if HIFI_USE_MIRROR@>
<@if HIFI_USE_FORWARD@>
layout(location=0) out vec4 _fragColor0; layout(location=0) out vec4 _fragColor0;
<@else@>
<@include DeferredBufferWrite.slh@>
<@endif@>
<@else@>
layout(location=0) out vec4 _fragColor0;
<@endif@>
<@endif@> <@endif@>
<@if HIFI_USE_UNLIT@> <@if HIFI_USE_UNLIT@>
@ -45,6 +53,9 @@
<@if HIFI_USE_SHADOW or HIFI_USE_UNLIT@> <@if HIFI_USE_SHADOW or HIFI_USE_UNLIT@>
<$declareMaterialTextures(ALBEDO)$> <$declareMaterialTextures(ALBEDO)$>
<@if HIFI_USE_MIRROR@>
LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_MIRROR) uniform sampler2D mirrorMap;
<@endif@>
<@else@> <@else@>
<@if not HIFI_USE_LIGHTMAP@> <@if not HIFI_USE_LIGHTMAP@>
<@if HIFI_USE_NORMALMAP and HIFI_USE_TRANSLUCENT@> <@if HIFI_USE_NORMALMAP and HIFI_USE_TRANSLUCENT@>
@ -124,7 +135,14 @@ void main(void) {
<@endif@> <@endif@>
<@endif@> <@endif@>
<@if HIFI_USE_SHADOW@> <@if HIFI_USE_MIRROR@>
vec3 mirrorColor = texelFetch(mirrorMap, ivec2(gl_FragCoord.xy), 0).rgb;
<@if HIFI_USE_FORWARD@>
_fragColor0 = vec4(mirrorColor, 1.0);
<@else@>
packDeferredFragmentUnlit(vec3(1.0, 0.0, 0.0), 1.0, mirrorColor);
<@endif@>
<@elif HIFI_USE_SHADOW@>
_fragColor0 = vec4(1.0); _fragColor0 = vec4(1.0);
<@elif HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@> <@elif HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
_fragColor0 = vec4(albedo * isUnlitEnabled(), opacity); _fragColor0 = vec4(albedo * isUnlitEnabled(), opacity);

View file

@ -1 +1 @@
DEFINES (normalmap translucent:f unlit:f/lightmap:f)/shadow fade:f/forward:f deformed:v/deformeddq:v DEFINES (normalmap translucent:f unlit:f/lightmap:f)/(shadow mirror:f) fade:f/forward:f deformed:v/deformeddq:v

View file

@ -1 +1 @@
DEFINES (translucent unlit:f)/forward DEFINES (translucent unlit:f)/forward mirror:f

View file

@ -41,6 +41,12 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01;
#define _texCoord1 _texCoord01.zw #define _texCoord1 _texCoord01.zw
layout(location=RENDER_UTILS_ATTR_FADE1) flat in vec4 _glyphBounds; // we're reusing the fade texcoord locations here layout(location=RENDER_UTILS_ATTR_FADE1) flat in vec4 _glyphBounds; // we're reusing the fade texcoord locations here
<@if HIFI_USE_MIRROR@>
<@include graphics/ShaderConstants.h@>
LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_MIRROR) uniform sampler2D mirrorMap;
<@endif@>
void main() { void main() {
vec4 color = evalSDFSuperSampled(_texCoord0, _glyphBounds); vec4 color = evalSDFSuperSampled(_texCoord0, _glyphBounds);
@ -51,6 +57,11 @@ void main() {
} }
<@endif@> <@endif@>
<@if HIFI_USE_MIRROR@>
color.rgb = texelFetch(mirrorMap, ivec2(gl_FragCoord.xy), 0).rgb;
color.a = 1.0;
<@endif@>
<@if HIFI_USE_UNLIT@> <@if HIFI_USE_UNLIT@>
<@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@> <@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
_fragColor0 = vec4(color.rgb * isUnlitEnabled(), color.a); _fragColor0 = vec4(color.rgb * isUnlitEnabled(), color.a);

View file

@ -29,7 +29,7 @@
static std::mutex fontMutex; static std::mutex fontMutex;
std::map<std::tuple<bool, bool, bool>, gpu::PipelinePointer> Font::_pipelines; std::map<std::tuple<bool, bool, bool, bool>, gpu::PipelinePointer> Font::_pipelines;
gpu::Stream::FormatPointer Font::_format; gpu::Stream::FormatPointer Font::_format;
struct TextureVertex { struct TextureVertex {
@ -277,6 +277,7 @@ void Font::setupGPU() {
if (_pipelines.empty()) { if (_pipelines.empty()) {
using namespace shader::render_utils::program; using namespace shader::render_utils::program;
// transparent, unlit, forward
static const std::vector<std::tuple<bool, bool, bool, uint32_t>> keys = { static const std::vector<std::tuple<bool, bool, bool, uint32_t>> keys = {
std::make_tuple(false, false, false, sdf_text3D), std::make_tuple(true, false, false, sdf_text3D_translucent), std::make_tuple(false, false, false, sdf_text3D), std::make_tuple(true, false, false, sdf_text3D_translucent),
std::make_tuple(false, true, false, sdf_text3D_unlit), std::make_tuple(true, true, false, sdf_text3D_translucent_unlit), std::make_tuple(false, true, false, sdf_text3D_unlit), std::make_tuple(true, true, false, sdf_text3D_translucent_unlit),
@ -284,18 +285,23 @@ void Font::setupGPU() {
std::make_tuple(false, true, true, sdf_text3D_translucent_unlit/*sdf_text3D_unlit_forward*/), std::make_tuple(true, true, true, sdf_text3D_translucent_unlit/*sdf_text3D_translucent_unlit_forward*/) std::make_tuple(false, true, true, sdf_text3D_translucent_unlit/*sdf_text3D_unlit_forward*/), std::make_tuple(true, true, true, sdf_text3D_translucent_unlit/*sdf_text3D_translucent_unlit_forward*/)
}; };
for (auto& key : keys) { for (auto& key : keys) {
bool transparent = std::get<0>(key);
bool unlit = std::get<1>(key);
bool forward = std::get<2>(key);
auto state = std::make_shared<gpu::State>(); auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_BACK); state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, !std::get<0>(key), gpu::LESS_EQUAL); state->setDepthTest(true, !transparent, gpu::LESS_EQUAL);
state->setBlendFunction(std::get<0>(key), state->setBlendFunction(transparent,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
if (std::get<0>(key)) { if (transparent) {
PrepareStencil::testMask(*state); PrepareStencil::testMask(*state);
} else { } else {
PrepareStencil::testMaskDrawShape(*state); PrepareStencil::testMaskDrawShape(*state);
} }
_pipelines[std::make_tuple(std::get<0>(key), std::get<1>(key), std::get<2>(key))] = gpu::Pipeline::create(gpu::Shader::createProgram(std::get<3>(key)), state); _pipelines[std::make_tuple(transparent, unlit, forward, false)] = gpu::Pipeline::create(gpu::Shader::createProgram(std::get<3>(key)), state);
_pipelines[std::make_tuple(transparent, unlit, forward, true)] = gpu::Pipeline::create(gpu::Shader::createProgram(forward ? sdf_text3D_forward_mirror : sdf_text3D_mirror), state);
} }
// Sanity checks // Sanity checks
@ -444,32 +450,30 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
} }
} }
void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString& str, const glm::vec4& color, void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const DrawProps& props) {
const glm::vec3& effectColor, float effectThickness, TextEffect effect, TextAlignment alignment, if (!_loaded || props.str == "") {
const glm::vec2& origin, const glm::vec2& bounds, float scale, bool unlit, bool forward) {
if (!_loaded || str == "") {
return; return;
} }
int textEffect = (int)effect; int textEffect = (int)props.effect;
const int SHADOW_EFFECT = (int)TextEffect::SHADOW_EFFECT; const int SHADOW_EFFECT = (int)TextEffect::SHADOW_EFFECT;
// If we're switching to or from shadow effect mode, we need to rebuild the vertices // If we're switching to or from shadow effect mode, we need to rebuild the vertices
if (str != drawInfo.string || bounds != drawInfo.bounds || origin != drawInfo.origin || alignment != _alignment || if (props.str != drawInfo.string || props.bounds != drawInfo.bounds || props.origin != drawInfo.origin || props.alignment != _alignment ||
(drawInfo.params.effect != textEffect && (textEffect == SHADOW_EFFECT || drawInfo.params.effect == SHADOW_EFFECT)) || (drawInfo.params.effect != textEffect && (textEffect == SHADOW_EFFECT || drawInfo.params.effect == SHADOW_EFFECT)) ||
(textEffect == SHADOW_EFFECT && scale != _scale)) { (textEffect == SHADOW_EFFECT && props.scale != _scale)) {
_scale = scale; _scale = props.scale;
_alignment = alignment; _alignment = props.alignment;
buildVertices(drawInfo, str, origin, bounds, scale, textEffect == SHADOW_EFFECT, alignment); buildVertices(drawInfo, props.str, props.origin, props.bounds, props.scale, textEffect == SHADOW_EFFECT, props.alignment);
} }
setupGPU(); setupGPU();
if (!drawInfo.paramsBuffer || drawInfo.params.color != color || drawInfo.params.effectColor != effectColor || if (!drawInfo.paramsBuffer || drawInfo.params.color != props.color || drawInfo.params.effectColor != props.effectColor ||
drawInfo.params.effectThickness != effectThickness || drawInfo.params.effect != textEffect) { drawInfo.params.effectThickness != props.effectThickness || drawInfo.params.effect != textEffect) {
drawInfo.params.color = color; drawInfo.params.color = props.color;
drawInfo.params.effectColor = effectColor; drawInfo.params.effectColor = props.effectColor;
drawInfo.params.effectThickness = effectThickness; drawInfo.params.effectThickness = props.effectThickness;
drawInfo.params.effect = textEffect; drawInfo.params.effect = textEffect;
// need the gamma corrected color here // need the gamma corrected color here
@ -484,7 +488,7 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString
drawInfo.paramsBuffer->setSubData(0, sizeof(DrawParams), (const gpu::Byte*)&gpuDrawParams); drawInfo.paramsBuffer->setSubData(0, sizeof(DrawParams), (const gpu::Byte*)&gpuDrawParams);
} }
batch.setPipeline(_pipelines[std::make_tuple(color.a < 1.0f, unlit, forward)]); batch.setPipeline(_pipelines[std::make_tuple(props.color.a < 1.0f, props.unlit, props.forward, props.mirror)]);
batch.setInputFormat(_format); batch.setInputFormat(_format);
batch.setInputBuffer(0, drawInfo.verticesBuffer, 0, _format->getChannels().at(0)._stride); batch.setInputBuffer(0, drawInfo.verticesBuffer, 0, _format->getChannels().at(0)._stride);
batch.setResourceTexture(render_utils::slot::texture::TextFont, _texture); batch.setResourceTexture(render_utils::slot::texture::TextFont, _texture);

View file

@ -57,10 +57,30 @@ public:
glm::vec2 computeExtent(const QString& str) const; glm::vec2 computeExtent(const QString& str) const;
float getFontSize() const { return _fontSize; } float getFontSize() const { return _fontSize; }
struct DrawProps {
DrawProps(const QString& str, const glm::vec4& color, const glm::vec3& effectColor, const glm::vec2& origin, const glm::vec2& bounds,
float scale, float effectThickness, TextEffect effect, TextAlignment alignment, bool unlit, bool forward, bool mirror) :
str(str), color(color), effectColor(effectColor), origin(origin), bounds(bounds), scale(scale), effectThickness(effectThickness),
effect(effect), alignment(alignment), unlit(unlit), forward(forward), mirror(mirror) {}
DrawProps(const QString& str, const glm::vec4& color, const glm::vec2& origin, const glm::vec2& bounds, bool forward) :
str(str), color(color), origin(origin), bounds(bounds), forward(forward) {}
const QString& str;
const glm::vec4& color;
const glm::vec3& effectColor { glm::vec3(0.0f) };
const glm::vec2& origin;
const glm::vec2& bounds;
float scale { 1.0f };
float effectThickness { 0.0f };
TextEffect effect { TextEffect::NO_EFFECT };
TextAlignment alignment { TextAlignment::LEFT };
bool unlit = true;
bool forward;
bool mirror = false;
};
// Render string to batch // Render string to batch
void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const QString& str, const glm::vec4& color, void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const DrawProps& props);
const glm::vec3& effectColor, float effectThickness, TextEffect effect, TextAlignment alignment,
const glm::vec2& origin, const glm::vec2& bound, float scale, bool unlit, bool forward);
static Pointer load(const QString& family); static Pointer load(const QString& family);
@ -105,7 +125,7 @@ private:
gpu::TexturePointer _texture; gpu::TexturePointer _texture;
gpu::BufferStreamPointer _stream; gpu::BufferStreamPointer _stream;
static std::map<std::tuple<bool, bool, bool>, gpu::PipelinePointer> _pipelines; static std::map<std::tuple<bool, bool, bool, bool>, gpu::PipelinePointer> _pipelines;
static gpu::Stream::FormatPointer _format; static gpu::Stream::FormatPointer _format;
}; };

View file

@ -159,6 +159,10 @@ namespace render {
bool _takingSnapshot { false }; bool _takingSnapshot { false };
StencilMaskMode _stencilMaskMode { StencilMaskMode::NONE }; StencilMaskMode _stencilMaskMode { StencilMaskMode::NONE };
std::function<void(gpu::Batch&)> _stencilMaskOperator; std::function<void(gpu::Batch&)> _stencilMaskOperator;
ItemID _ignoreItem { 0 };
size_t _mirrorDepth { 0 };
size_t _numMirrorFlips { 0 };
}; };
} }

View file

@ -82,7 +82,7 @@ void FetchNonspatialItems::run(const RenderContextPointer& renderContext, const
outItems.reserve(items.size()); outItems.reserve(items.size());
for (auto& id : items) { for (auto& id : items) {
auto& item = scene->getItem(id); auto& item = scene->getItem(id);
if (filter.test(item.getKey()) && item.passesZoneOcclusionTest(CullTest::_containingZones)) { if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && item.passesZoneOcclusionTest(CullTest::_containingZones)) {
outItems.emplace_back(ItemBound(id, item.getBound(renderContext->args))); outItems.emplace_back(ItemBound(id, item.getBound(renderContext->args)));
} }
} }
@ -190,7 +190,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
PerformanceTimer perfTimer("insideFitItems"); PerformanceTimer perfTimer("insideFitItems");
for (auto id : inSelection.insideItems) { for (auto id : inSelection.insideItems) {
auto& item = scene->getItem(id); auto& item = scene->getItem(id);
if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) {
ItemBound itemBound(id, item.getBound(args)); ItemBound itemBound(id, item.getBound(args));
outItems.emplace_back(itemBound); outItems.emplace_back(itemBound);
if (item.getKey().isMetaCullGroup()) { if (item.getKey().isMetaCullGroup()) {
@ -205,7 +205,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
PerformanceTimer perfTimer("insideSmallItems"); PerformanceTimer perfTimer("insideSmallItems");
for (auto id : inSelection.insideSubcellItems) { for (auto id : inSelection.insideSubcellItems) {
auto& item = scene->getItem(id); auto& item = scene->getItem(id);
if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) {
ItemBound itemBound(id, item.getBound(args)); ItemBound itemBound(id, item.getBound(args));
outItems.emplace_back(itemBound); outItems.emplace_back(itemBound);
if (item.getKey().isMetaCullGroup()) { if (item.getKey().isMetaCullGroup()) {
@ -220,7 +220,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
PerformanceTimer perfTimer("partialFitItems"); PerformanceTimer perfTimer("partialFitItems");
for (auto id : inSelection.partialItems) { for (auto id : inSelection.partialItems) {
auto& item = scene->getItem(id); auto& item = scene->getItem(id);
if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) {
ItemBound itemBound(id, item.getBound(args)); ItemBound itemBound(id, item.getBound(args));
outItems.emplace_back(itemBound); outItems.emplace_back(itemBound);
if (item.getKey().isMetaCullGroup()) { if (item.getKey().isMetaCullGroup()) {
@ -235,7 +235,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
PerformanceTimer perfTimer("partialSmallItems"); PerformanceTimer perfTimer("partialSmallItems");
for (auto id : inSelection.partialSubcellItems) { for (auto id : inSelection.partialSubcellItems) {
auto& item = scene->getItem(id); auto& item = scene->getItem(id);
if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) {
ItemBound itemBound(id, item.getBound(args)); ItemBound itemBound(id, item.getBound(args));
outItems.emplace_back(itemBound); outItems.emplace_back(itemBound);
if (item.getKey().isMetaCullGroup()) { if (item.getKey().isMetaCullGroup()) {
@ -252,7 +252,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
PerformanceTimer perfTimer("insideFitItems"); PerformanceTimer perfTimer("insideFitItems");
for (auto id : inSelection.insideItems) { for (auto id : inSelection.insideItems) {
auto& item = scene->getItem(id); auto& item = scene->getItem(id);
if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) {
ItemBound itemBound(id, item.getBound(args)); ItemBound itemBound(id, item.getBound(args));
outItems.emplace_back(itemBound); outItems.emplace_back(itemBound);
if (item.getKey().isMetaCullGroup()) { if (item.getKey().isMetaCullGroup()) {
@ -267,7 +267,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
PerformanceTimer perfTimer("insideSmallItems"); PerformanceTimer perfTimer("insideSmallItems");
for (auto id : inSelection.insideSubcellItems) { for (auto id : inSelection.insideSubcellItems) {
auto& item = scene->getItem(id); auto& item = scene->getItem(id);
if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) {
ItemBound itemBound(id, item.getBound(args)); ItemBound itemBound(id, item.getBound(args));
if (test.solidAngleTest(itemBound.bound)) { if (test.solidAngleTest(itemBound.bound)) {
outItems.emplace_back(itemBound); outItems.emplace_back(itemBound);
@ -284,7 +284,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
PerformanceTimer perfTimer("partialFitItems"); PerformanceTimer perfTimer("partialFitItems");
for (auto id : inSelection.partialItems) { for (auto id : inSelection.partialItems) {
auto& item = scene->getItem(id); auto& item = scene->getItem(id);
if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) {
ItemBound itemBound(id, item.getBound(args)); ItemBound itemBound(id, item.getBound(args));
if (test.frustumTest(itemBound.bound)) { if (test.frustumTest(itemBound.bound)) {
outItems.emplace_back(itemBound); outItems.emplace_back(itemBound);
@ -301,7 +301,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
PerformanceTimer perfTimer("partialSmallItems"); PerformanceTimer perfTimer("partialSmallItems");
for (auto id : inSelection.partialSubcellItems) { for (auto id : inSelection.partialSubcellItems) {
auto& item = scene->getItem(id); auto& item = scene->getItem(id);
if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) {
ItemBound itemBound(id, item.getBound(args)); ItemBound itemBound(id, item.getBound(args));
if (test.frustumTest(itemBound.bound) && test.solidAngleTest(itemBound.bound)) { if (test.frustumTest(itemBound.bound) && test.solidAngleTest(itemBound.bound)) {
outItems.emplace_back(itemBound); outItems.emplace_back(itemBound);
@ -325,73 +325,6 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
std::static_pointer_cast<Config>(renderContext->jobConfig)->numItems = (int)outItems.size(); std::static_pointer_cast<Config>(renderContext->jobConfig)->numItems = (int)outItems.size();
} }
void CullShapeBounds::run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
assert(renderContext->args);
assert(renderContext->args->hasViewFrustum());
RenderArgs* args = renderContext->args;
const auto& inShapes = inputs.get0();
const auto& cullFilter = inputs.get1();
const auto& boundsFilter = inputs.get2();
ViewFrustumPointer antiFrustum;
auto& outShapes = outputs.edit0();
auto& outBounds = outputs.edit1();
if (!inputs[3].isNull()) {
antiFrustum = inputs.get3();
}
outShapes.clear();
outBounds = AABox();
if (!cullFilter.selectsNothing() || !boundsFilter.selectsNothing()) {
auto& details = args->_details.edit(_detailType);
CullTest test(_cullFunctor, args, details, antiFrustum);
auto scene = args->_scene;
for (auto& inItems : inShapes) {
auto key = inItems.first;
auto outItems = outShapes.find(key);
if (outItems == outShapes.end()) {
outItems = outShapes.insert(std::make_pair(key, ItemBounds{})).first;
outItems->second.reserve(inItems.second.size());
}
details._considered += (int)inItems.second.size();
if (antiFrustum == nullptr) {
for (auto& item : inItems.second) {
if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound)) {
const auto shapeKey = scene->getItem(item.id).getKey();
if (cullFilter.test(shapeKey)) {
outItems->second.emplace_back(item);
}
if (boundsFilter.test(shapeKey)) {
outBounds += item.bound;
}
}
}
} else {
for (auto& item : inItems.second) {
if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound) && test.antiFrustumTest(item.bound)) {
const auto shapeKey = scene->getItem(item.id).getKey();
if (cullFilter.test(shapeKey)) {
outItems->second.emplace_back(item);
}
if (boundsFilter.test(shapeKey)) {
outBounds += item.bound;
}
}
}
}
details._rendered += (int)outItems->second.size();
}
for (auto& items : outShapes) {
items.second.shrink_to_fit();
}
}
}
void ApplyCullFunctorOnItemBounds::run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { void ApplyCullFunctorOnItemBounds::run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
assert(renderContext->args); assert(renderContext->args);
assert(renderContext->args->hasViewFrustum()); assert(renderContext->args->hasViewFrustum());

View file

@ -121,29 +121,6 @@ namespace render {
void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems); void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems);
}; };
class CullShapeBounds {
public:
using Inputs = render::VaryingSet4<ShapeBounds, ItemFilter, ItemFilter, ViewFrustumPointer>;
using Outputs = render::VaryingSet2<ShapeBounds, AABox>;
using JobModel = Job::ModelIO<CullShapeBounds, Inputs, Outputs>;
CullShapeBounds(CullFunctor cullFunctor, RenderDetails::Type type) :
_cullFunctor{ cullFunctor },
_detailType(type) {}
CullShapeBounds(CullFunctor cullFunctor) :
_cullFunctor{ cullFunctor } {
}
void run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
private:
CullFunctor _cullFunctor;
RenderDetails::Type _detailType{ RenderDetails::OTHER };
};
class ApplyCullFunctorOnItemBounds { class ApplyCullFunctorOnItemBounds {
public: public:
using Inputs = render::VaryingSet2<ItemBounds, ViewFrustumPointer>; using Inputs = render::VaryingSet2<ItemBounds, ViewFrustumPointer>;

View file

@ -160,4 +160,11 @@ namespace render {
} }
return payload->passesZoneOcclusionTest(containingZones); return payload->passesZoneOcclusionTest(containingZones);
} }
template <> ItemID payloadComputeMirrorView(const PayloadProxyInterface::Pointer& payload, ViewFrustum& viewFrustum) {
if (!payload) {
return Item::INVALID_ITEM_ID;
}
return payload->computeMirrorView(viewFrustum);
}
} }

View file

@ -110,6 +110,8 @@ public:
FIRST_LAYER_BIT, // 8 Exclusive Layers (encoded in 3 bits) available to organize the items in layers, an item can only belong to ONE layer FIRST_LAYER_BIT, // 8 Exclusive Layers (encoded in 3 bits) available to organize the items in layers, an item can only belong to ONE layer
LAST_LAYER_BIT = FIRST_LAYER_BIT + NUM_LAYER_BITS, LAST_LAYER_BIT = FIRST_LAYER_BIT + NUM_LAYER_BITS,
MIRROR,
__SMALLER, // Reserved bit for spatialized item to indicate that it is smaller than expected in the cell in which it belongs (probably because it overlaps over several smaller cells) __SMALLER, // Reserved bit for spatialized item to indicate that it is smaller than expected in the cell in which it belongs (probably because it overlaps over several smaller cells)
NUM_FLAGS, // Not a valid flag NUM_FLAGS, // Not a valid flag
@ -162,6 +164,7 @@ public:
Builder& withoutMetaCullGroup() { _flags.reset(META_CULL_GROUP); return (*this); } Builder& withoutMetaCullGroup() { _flags.reset(META_CULL_GROUP); return (*this); }
Builder& withSubMetaCulled() { _flags.set(SUB_META_CULLED); return (*this); } Builder& withSubMetaCulled() { _flags.set(SUB_META_CULLED); return (*this); }
Builder& withoutSubMetaCulled() { _flags.reset(SUB_META_CULLED); return (*this); } Builder& withoutSubMetaCulled() { _flags.reset(SUB_META_CULLED); return (*this); }
Builder& withMirror() { _flags.set(MIRROR); return (*this); }
Builder& withTag(Tag tag) { _flags.set(FIRST_TAG_BIT + tag); return (*this); } Builder& withTag(Tag tag) { _flags.set(FIRST_TAG_BIT + tag); return (*this); }
// Set ALL the tags in one call using the Tag bits // Set ALL the tags in one call using the Tag bits
@ -205,6 +208,9 @@ public:
bool isSubMetaCulled() const { return _flags[SUB_META_CULLED]; } bool isSubMetaCulled() const { return _flags[SUB_META_CULLED]; }
void setSubMetaCulled(bool metaCulled) { (metaCulled ? _flags.set(SUB_META_CULLED) : _flags.reset(SUB_META_CULLED)); } void setSubMetaCulled(bool metaCulled) { (metaCulled ? _flags.set(SUB_META_CULLED) : _flags.reset(SUB_META_CULLED)); }
bool isNotMirror() const { return !_flags[MIRROR]; }
bool isMirror() const { return _flags[MIRROR]; }
bool isTag(Tag tag) const { return _flags[FIRST_TAG_BIT + tag]; } bool isTag(Tag tag) const { return _flags[FIRST_TAG_BIT + tag]; }
uint8_t getTagBits() const { return ((_flags.to_ulong() & KEY_TAG_BITS_MASK) >> FIRST_TAG_BIT); } uint8_t getTagBits() const { return ((_flags.to_ulong() & KEY_TAG_BITS_MASK) >> FIRST_TAG_BIT); }
@ -276,6 +282,9 @@ public:
Builder& withoutSubMetaCulled() { _value.reset(ItemKey::SUB_META_CULLED); _mask.set(ItemKey::SUB_META_CULLED); return (*this); } Builder& withoutSubMetaCulled() { _value.reset(ItemKey::SUB_META_CULLED); _mask.set(ItemKey::SUB_META_CULLED); return (*this); }
Builder& withSubMetaCulled() { _value.set(ItemKey::SUB_META_CULLED); _mask.set(ItemKey::SUB_META_CULLED); return (*this); } Builder& withSubMetaCulled() { _value.set(ItemKey::SUB_META_CULLED); _mask.set(ItemKey::SUB_META_CULLED); return (*this); }
Builder& withoutMirror() { _value.reset(ItemKey::MIRROR); _mask.set(ItemKey::MIRROR); return (*this); }
Builder& withMirror() { _value.set(ItemKey::MIRROR); _mask.set(ItemKey::MIRROR); return (*this); }
Builder& withoutTag(ItemKey::Tag tagIndex) { _value.reset(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); } Builder& withoutTag(ItemKey::Tag tagIndex) { _value.reset(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); }
Builder& withTag(ItemKey::Tag tagIndex) { _value.set(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); } Builder& withTag(ItemKey::Tag tagIndex) { _value.set(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); }
// Set ALL the tags in one call using the Tag bits and the Tag bits touched // Set ALL the tags in one call using the Tag bits and the Tag bits touched
@ -292,6 +301,7 @@ public:
static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); } static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); }
static Builder light() { return Builder().withTypeLight(); } static Builder light() { return Builder().withTypeLight(); }
static Builder meta() { return Builder().withTypeMeta(); } static Builder meta() { return Builder().withTypeMeta(); }
static Builder mirror() { return Builder().withMirror(); }
static Builder background() { return Builder().withViewSpace().withLayer(ItemKey::LAYER_BACKGROUND); } static Builder background() { return Builder().withViewSpace().withLayer(ItemKey::LAYER_BACKGROUND); }
static Builder nothing() { return Builder().withNothing(); } static Builder nothing() { return Builder().withNothing(); }
}; };
@ -440,6 +450,8 @@ public:
virtual bool passesZoneOcclusionTest(const std::unordered_set<QUuid>& containingZones) const = 0; virtual bool passesZoneOcclusionTest(const std::unordered_set<QUuid>& containingZones) const = 0;
virtual ItemID computeMirrorView(ViewFrustum& viewFrustum) const = 0;
~PayloadInterface() {} ~PayloadInterface() {}
// Status interface is local to the base class // Status interface is local to the base class
@ -493,6 +505,8 @@ public:
bool passesZoneOcclusionTest(const std::unordered_set<QUuid>& containingZones) const { return _payload->passesZoneOcclusionTest(containingZones); } bool passesZoneOcclusionTest(const std::unordered_set<QUuid>& containingZones) const { return _payload->passesZoneOcclusionTest(containingZones); }
ItemID computeMirrorView(ViewFrustum& viewFrustum) const { return _payload->computeMirrorView(viewFrustum); }
// Access the status // Access the status
const StatusPointer& getStatus() const { return _payload->getStatus(); } const StatusPointer& getStatus() const { return _payload->getStatus(); }
@ -547,6 +561,9 @@ template <class T> uint32_t metaFetchMetaSubItems(const std::shared_ptr<T>& payl
// Allows payloads to determine if they should render or not, based on the zones that contain the current camera // Allows payloads to determine if they should render or not, based on the zones that contain the current camera
template <class T> bool payloadPassesZoneOcclusionTest(const std::shared_ptr<T>& payloadData, const std::unordered_set<QUuid>& containingZones) { return true; } template <class T> bool payloadPassesZoneOcclusionTest(const std::shared_ptr<T>& payloadData, const std::unordered_set<QUuid>& containingZones) { return true; }
// Mirror Interface
template <class T> ItemID payloadComputeMirrorView(const std::shared_ptr<T>& payloadData, ViewFrustum& viewFrustum) { return Item::INVALID_ITEM_ID; }
// THe Payload class is the real Payload to be used // THe Payload class is the real Payload to be used
// THis allow anything to be turned into a Payload as long as the required interface functions are available // THis allow anything to be turned into a Payload as long as the required interface functions are available
// When creating a new kind of payload from a new "stuff" class then you need to create specialized version for "stuff" // When creating a new kind of payload from a new "stuff" class then you need to create specialized version for "stuff"
@ -573,6 +590,8 @@ public:
virtual bool passesZoneOcclusionTest(const std::unordered_set<QUuid>& containingZones) const override { return payloadPassesZoneOcclusionTest<T>(_data, containingZones); } virtual bool passesZoneOcclusionTest(const std::unordered_set<QUuid>& containingZones) const override { return payloadPassesZoneOcclusionTest<T>(_data, containingZones); }
virtual ItemID computeMirrorView(ViewFrustum& viewFrustum) const override { return payloadComputeMirrorView<T>(_data, viewFrustum); }
protected: protected:
DataPointer _data; DataPointer _data;
@ -628,6 +647,7 @@ public:
virtual void render(RenderArgs* args) = 0; virtual void render(RenderArgs* args) = 0;
virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const = 0; virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const = 0;
virtual bool passesZoneOcclusionTest(const std::unordered_set<QUuid>& containingZones) const = 0; virtual bool passesZoneOcclusionTest(const std::unordered_set<QUuid>& containingZones) const = 0;
virtual ItemID computeMirrorView(ViewFrustum& viewFrustum) const = 0;
// FIXME: this isn't the best place for this since it's only used for ModelEntities, but currently all Entities use PayloadProxyInterface // FIXME: this isn't the best place for this since it's only used for ModelEntities, but currently all Entities use PayloadProxyInterface
virtual void handleBlendedVertices(int blendshapeNumber, const QVector<BlendshapeOffset>& blendshapeOffsets, virtual void handleBlendedVertices(int blendshapeNumber, const QVector<BlendshapeOffset>& blendshapeOffsets,
@ -640,6 +660,7 @@ template <> void payloadRender(const PayloadProxyInterface::Pointer& payload, Re
template <> uint32_t metaFetchMetaSubItems(const PayloadProxyInterface::Pointer& payload, ItemIDs& subItems); template <> uint32_t metaFetchMetaSubItems(const PayloadProxyInterface::Pointer& payload, ItemIDs& subItems);
template <> const ShapeKey shapeGetShapeKey(const PayloadProxyInterface::Pointer& payload); template <> const ShapeKey shapeGetShapeKey(const PayloadProxyInterface::Pointer& payload);
template <> bool payloadPassesZoneOcclusionTest(const PayloadProxyInterface::Pointer& payload, const std::unordered_set<QUuid>& containingZones); template <> bool payloadPassesZoneOcclusionTest(const PayloadProxyInterface::Pointer& payload, const std::unordered_set<QUuid>& containingZones);
template <> ItemID payloadComputeMirrorView(const PayloadProxyInterface::Pointer& payload, ViewFrustum& viewFrustum);
typedef Item::PayloadPointer PayloadPointer; typedef Item::PayloadPointer PayloadPointer;
typedef std::vector<PayloadPointer> Payloads; typedef std::vector<PayloadPointer> Payloads;

View file

@ -35,18 +35,20 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin
const auto nonspatialSelection = task.addJob<FetchNonspatialItems>("FetchLayeredSelection", nonspatialFilter); const auto nonspatialSelection = task.addJob<FetchNonspatialItems>("FetchLayeredSelection", nonspatialFilter);
// Multi filter visible items into different buckets // Multi filter visible items into different buckets
const int NUM_SPATIAL_FILTERS = 4; const int NUM_SPATIAL_FILTERS = 5;
const int NUM_NON_SPATIAL_FILTERS = 3; const int NUM_NON_SPATIAL_FILTERS = 3;
const int OPAQUE_SHAPE_BUCKET = 0; const int OPAQUE_SHAPE_BUCKET = 0;
const int TRANSPARENT_SHAPE_BUCKET = 1; const int TRANSPARENT_SHAPE_BUCKET = 1;
const int LIGHT_BUCKET = 2; const int LIGHT_BUCKET = 2;
const int META_BUCKET = 3; const int META_BUCKET = 3;
const int MIRROR_BUCKET = 4;
const int BACKGROUND_BUCKET = 2; const int BACKGROUND_BUCKET = 2;
MultiFilterItems<NUM_SPATIAL_FILTERS>::ItemFilterArray spatialFilters = { { MultiFilterItems<NUM_SPATIAL_FILTERS>::ItemFilterArray spatialFilters = { {
ItemFilter::Builder::opaqueShape(), ItemFilter::Builder::opaqueShape().withoutMirror(),
ItemFilter::Builder::transparentShape(), ItemFilter::Builder::transparentShape(),
ItemFilter::Builder::light(), ItemFilter::Builder::light(),
ItemFilter::Builder::meta() ItemFilter::Builder::meta().withoutMirror(),
ItemFilter::Builder::mirror()
} }; } };
MultiFilterItems<NUM_NON_SPATIAL_FILTERS>::ItemFilterArray nonspatialFilters = { { MultiFilterItems<NUM_NON_SPATIAL_FILTERS>::ItemFilterArray nonspatialFilters = { {
ItemFilter::Builder::opaqueShape(), ItemFilter::Builder::opaqueShape(),
@ -65,6 +67,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin
const auto transparents = task.addJob<DepthSortItems>("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); const auto transparents = task.addJob<DepthSortItems>("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false));
const auto lights = filteredSpatialBuckets[LIGHT_BUCKET]; const auto lights = filteredSpatialBuckets[LIGHT_BUCKET];
const auto metas = filteredSpatialBuckets[META_BUCKET]; const auto metas = filteredSpatialBuckets[META_BUCKET];
const auto mirrors = task.addJob<DepthSortItems>("DepthSortMirrors", filteredSpatialBuckets[MIRROR_BUCKET]);
const auto background = filteredNonspatialBuckets[BACKGROUND_BUCKET]; const auto background = filteredNonspatialBuckets[BACKGROUND_BUCKET];
@ -76,7 +79,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin
task.addJob<ClearContainingZones>("ClearContainingZones"); task.addJob<ClearContainingZones>("ClearContainingZones");
output = Output(BucketList{ opaques, transparents, lights, metas, output = Output(BucketList{ opaques, transparents, lights, metas, mirrors,
filteredLayeredOpaque.getN<FilterLayeredItems::Outputs>(0), filteredLayeredTransparent.getN<FilterLayeredItems::Outputs>(0), filteredLayeredOpaque.getN<FilterLayeredItems::Outputs>(0), filteredLayeredTransparent.getN<FilterLayeredItems::Outputs>(0),
filteredLayeredOpaque.getN<FilterLayeredItems::Outputs>(1), filteredLayeredTransparent.getN<FilterLayeredItems::Outputs>(1), filteredLayeredOpaque.getN<FilterLayeredItems::Outputs>(1), filteredLayeredTransparent.getN<FilterLayeredItems::Outputs>(1),
background }, spatialSelection); background }, spatialSelection);

View file

@ -23,6 +23,7 @@ public:
TRANSPARENT_SHAPE, TRANSPARENT_SHAPE,
LIGHT, LIGHT,
META, META,
MIRROR,
LAYER_FRONT_OPAQUE_SHAPE, LAYER_FRONT_OPAQUE_SHAPE,
LAYER_FRONT_TRANSPARENT_SHAPE, LAYER_FRONT_TRANSPARENT_SHAPE,
LAYER_HUD_OPAQUE_SHAPE, LAYER_HUD_OPAQUE_SHAPE,

View file

@ -167,7 +167,8 @@ void UpsampleToBlitFramebuffer::run(const RenderContextPointer& renderContext, c
batch.setViewportTransform(viewport); batch.setViewportTransform(viewport);
batch.setProjectionTransform(glm::mat4()); batch.setProjectionTransform(glm::mat4());
batch.resetViewTransform(); batch.resetViewTransform();
batch.setPipeline(args->_renderMode == RenderArgs::MIRROR_RENDER_MODE ? _mirrorPipeline : _pipeline); bool shouldMirror = args->_numMirrorFlips >= (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE);
batch.setPipeline(shouldMirror ? _mirrorPipeline : _pipeline);
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport)); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport));
batch.setResourceTexture(0, sourceFramebuffer->getRenderBuffer(0)); batch.setResourceTexture(0, sourceFramebuffer->getRenderBuffer(0));

View file

@ -73,7 +73,7 @@ namespace render {
using Input = gpu::FramebufferPointer; using Input = gpu::FramebufferPointer;
using JobModel = Job::ModelIO<UpsampleToBlitFramebuffer, Input, gpu::FramebufferPointer>; using JobModel = Job::ModelIO<UpsampleToBlitFramebuffer, Input, gpu::FramebufferPointer>;
UpsampleToBlitFramebuffer() {} UpsampleToBlitFramebuffer(size_t depth) : _depth(depth) {}
void run(const RenderContextPointer& renderContext, const Input& input, gpu::FramebufferPointer& resampledFrameBuffer); void run(const RenderContextPointer& renderContext, const Input& input, gpu::FramebufferPointer& resampledFrameBuffer);
@ -81,6 +81,8 @@ namespace render {
static gpu::PipelinePointer _pipeline; static gpu::PipelinePointer _pipeline;
static gpu::PipelinePointer _mirrorPipeline; static gpu::PipelinePointer _mirrorPipeline;
size_t _depth;
}; };
} }

View file

@ -0,0 +1,36 @@
//
// Created by HifiExperiments on 3/14/22.
// Copyright 2022 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "MirrorMode.h"
const char* MirrorModeNames[] = {
"none",
"mirror",
"portal"
};
static const size_t MIRROR_MODE_NAMES = (sizeof(MirrorModeNames) / sizeof(MirrorModeNames[0]));
std::function<uint32_t(ViewFrustum&, const glm::vec3&, const glm::quat&, MirrorMode, const QUuid&)> MirrorModeHelpers::_computeMirrorViewOperator =
[](ViewFrustum&, const glm::vec3&, const glm::quat&, MirrorMode, const QUuid&) { return 0; };
QString MirrorModeHelpers::getNameForMirrorMode(MirrorMode mode) {
if (((int)mode <= 0) || ((int)mode >= (int)MIRROR_MODE_NAMES)) {
mode = (MirrorMode)0;
}
return MirrorModeNames[(int)mode];
}
void MirrorModeHelpers::setComputeMirrorViewOperator(std::function<uint32_t(ViewFrustum&, const glm::vec3&, const glm::quat&, MirrorMode, const QUuid&)> computeMirrorViewOperator) {
_computeMirrorViewOperator = computeMirrorViewOperator;
}
uint32_t MirrorModeHelpers::computeMirrorView(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation,
MirrorMode mirrorMode, const QUuid& portalExitID) {
return _computeMirrorViewOperator(viewFrustum, inPropertiesPosition, inPropertiesRotation, mirrorMode, portalExitID);
}

View file

@ -0,0 +1,51 @@
//
// Created by HifiExperiments on 3/14/22.
// Copyright 2022 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_MirrorMode_h
#define hifi_MirrorMode_h
#include <functional>
#include "QString"
#include "ViewFrustum.h"
/*@jsdoc
* <p>If an entity is rendered as a mirror, a portal, or normally.</p>
* <table>
* <thead>
* <tr><th>Value</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>"none"</code></td><td>The entity will render normally.</td></tr>
* <tr><td><code>"mirror"</code></td><td>The entity will render as a mirror.</td></tr>
* <tr><td><code>"portal"</code></td><td>The entity will render as a portal.</td></tr>
* </tbody>
* </table>
* @typedef {string} MirrorMode
*/
enum class MirrorMode {
NONE = 0,
MIRROR,
PORTAL
};
class MirrorModeHelpers {
public:
static QString getNameForMirrorMode(MirrorMode mode);
static void setComputeMirrorViewOperator(std::function<uint32_t(ViewFrustum&, const glm::vec3&, const glm::quat&, MirrorMode, const QUuid&)> computeMirrorViewOperator);
static uint32_t computeMirrorView(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation,
MirrorMode mirrorMode, const QUuid& portalExitID);
private:
static std::function<uint32_t(ViewFrustum&, const glm::vec3&, const glm::quat&, MirrorMode, const QUuid&)> _computeMirrorViewOperator;
};
#endif // hifi_MirrorMode_h

View file

@ -53,7 +53,7 @@ static const glm::vec4 NDC_VALUES[NUM_FRUSTUM_CORNERS] = {
glm::vec4(-1.0f, 1.0f, 1.0f, 1.0f), glm::vec4(-1.0f, 1.0f, 1.0f, 1.0f),
}; };
void ViewFrustum::setProjection(const glm::mat4& projection) { void ViewFrustum::setProjection(const glm::mat4& projection, bool isOblique) {
_projection = projection; _projection = projection;
glm::mat4 inverseProjection = glm::inverse(projection); glm::mat4 inverseProjection = glm::inverse(projection);
@ -63,6 +63,9 @@ void ViewFrustum::setProjection(const glm::mat4& projection) {
_corners[i] /= _corners[i].w; _corners[i] /= _corners[i].w;
} }
// HACK: these calculations aren't correct for our oblique mirror frustums, but we can just reuse the values from the original
// frustum since these values are only used on the CPU.
if (!isOblique) {
// compute frustum properties // compute frustum properties
_nearClip = -_corners[BOTTOM_LEFT_NEAR].z; _nearClip = -_corners[BOTTOM_LEFT_NEAR].z;
_farClip = -_corners[BOTTOM_LEFT_FAR].z; _farClip = -_corners[BOTTOM_LEFT_FAR].z;
@ -74,6 +77,8 @@ void ViewFrustum::setProjection(const glm::mat4& projection) {
_height = _corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_RIGHT_NEAR].y; _height = _corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_RIGHT_NEAR].y;
_width = _corners[TOP_RIGHT_NEAR].x - _corners[TOP_LEFT_NEAR].x; _width = _corners[TOP_RIGHT_NEAR].x - _corners[TOP_LEFT_NEAR].x;
} }
_isOblique = isOblique;
}
void ViewFrustum::setProjection(float cameraFov, float cameraAspectRatio, float cameraNearClip, float cameraFarClip) { void ViewFrustum::setProjection(float cameraFov, float cameraAspectRatio, float cameraNearClip, float cameraFarClip) {
setProjection(glm::perspective(glm::radians(cameraFov), cameraAspectRatio, cameraNearClip, cameraFarClip)); setProjection(glm::perspective(glm::radians(cameraFov), cameraAspectRatio, cameraNearClip, cameraFarClip));
@ -109,12 +114,24 @@ void ViewFrustum::calculate() {
// the function set3Points assumes that the points are given in counter clockwise order, assume you // the function set3Points assumes that the points are given in counter clockwise order, assume you
// are inside the frustum, facing the plane. Start with any point, and go counter clockwise for // are inside the frustum, facing the plane. Start with any point, and go counter clockwise for
// three consecutive points // three consecutive points
if (!_isOblique) {
_planes[TOP_PLANE].set3Points(_cornersWorld[TOP_RIGHT_NEAR], _cornersWorld[TOP_LEFT_NEAR], _cornersWorld[TOP_LEFT_FAR]); _planes[TOP_PLANE].set3Points(_cornersWorld[TOP_RIGHT_NEAR], _cornersWorld[TOP_LEFT_NEAR], _cornersWorld[TOP_LEFT_FAR]);
_planes[BOTTOM_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[BOTTOM_RIGHT_FAR]); _planes[BOTTOM_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[BOTTOM_RIGHT_FAR]);
_planes[LEFT_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[BOTTOM_LEFT_FAR], _cornersWorld[TOP_LEFT_FAR]); _planes[LEFT_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[BOTTOM_LEFT_FAR], _cornersWorld[TOP_LEFT_FAR]);
_planes[RIGHT_PLANE].set3Points(_cornersWorld[BOTTOM_RIGHT_FAR], _cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[TOP_RIGHT_FAR]); _planes[RIGHT_PLANE].set3Points(_cornersWorld[BOTTOM_RIGHT_FAR], _cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[TOP_RIGHT_FAR]);
_planes[NEAR_PLANE].set3Points(_cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[TOP_LEFT_NEAR]); _planes[NEAR_PLANE].set3Points(_cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[TOP_LEFT_NEAR]);
_planes[FAR_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_FAR], _cornersWorld[BOTTOM_RIGHT_FAR], _cornersWorld[TOP_RIGHT_FAR]); _planes[FAR_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_FAR], _cornersWorld[BOTTOM_RIGHT_FAR], _cornersWorld[TOP_RIGHT_FAR]);
} else {
Corners near = getCorners(_nearClip);
Corners far = getCorners(_farClip);
_planes[TOP_PLANE].set3Points(near.topRight, near.topLeft, far.topLeft);
_planes[BOTTOM_PLANE].set3Points(near.bottomLeft, near.bottomRight, far.bottomRight);
_planes[LEFT_PLANE].set3Points(near.bottomLeft, far.bottomLeft, far.topLeft);
_planes[RIGHT_PLANE].set3Points(far.bottomRight, near.bottomRight, far.topRight);
_planes[NEAR_PLANE].set3Points(near.bottomRight, near.bottomLeft, near.topLeft);
_planes[FAR_PLANE].set3Points(far.bottomLeft, far.bottomRight, far.topRight);
}
// Also calculate our projection matrix in case people want to project points... // Also calculate our projection matrix in case people want to project points...
// Projection matrix : Field of View, ratio, display range : near to far // Projection matrix : Field of View, ratio, display range : near to far

View file

@ -47,7 +47,7 @@ public:
const glm::vec3& getRight() const { return _right; } const glm::vec3& getRight() const { return _right; }
// setters for lens attributes // setters for lens attributes
void setProjection(const glm::mat4 & projection); void setProjection(const glm::mat4& projection, bool isOblique = false);
void setProjection(float cameraFov, float cameraAspectRatio, float cameraNearClip, float cameraFarClip); void setProjection(float cameraFov, float cameraAspectRatio, float cameraNearClip, float cameraFarClip);
void setFocalLength(float focalLength) { _focalLength = focalLength; } void setFocalLength(float focalLength) { _focalLength = focalLength; }
bool isPerspective() const; bool isPerspective() const;
@ -103,7 +103,6 @@ public:
bool pointIntersectsFrustum(const glm::vec3& point) const; bool pointIntersectsFrustum(const glm::vec3& point) const;
bool sphereIntersectsFrustum(const glm::vec3& center, float radius) const; bool sphereIntersectsFrustum(const glm::vec3& center, float radius) const;
bool cubeIntersectsFrustum(const AACube& box) const;
bool boxIntersectsFrustum(const AABox& box) const; bool boxIntersectsFrustum(const AABox& box) const;
bool boxInsideFrustum(const AABox& box) const; bool boxInsideFrustum(const AABox& box) const;
@ -175,6 +174,8 @@ private:
float _nearClip { DEFAULT_NEAR_CLIP }; float _nearClip { DEFAULT_NEAR_CLIP };
float _farClip { DEFAULT_FAR_CLIP }; float _farClip { DEFAULT_FAR_CLIP };
bool _isOblique { false };
const char* debugPlaneName (int plane) const; const char* debugPlaneName (int plane) const;
// Used to project points // Used to project points

View file

@ -503,6 +503,12 @@
"grab.triggerable": { "grab.triggerable": {
"tooltip": "If enabled, the collider on this entity is used for triggering events." "tooltip": "If enabled, the collider on this entity is used for triggering events."
}, },
"mirrorMode": {
"tooltip": "If this entity should render normally, or as a \"Mirror\" or \"Portal\""
},
"portalExitID": {
"tooltip": "If this entity is a portal, what entity it should use as its exit."
},
"cloneable": { "cloneable": {
"tooltip": "If enabled, this entity can be duplicated." "tooltip": "If enabled, this entity can be duplicated."
}, },

View file

@ -127,6 +127,22 @@ const GROUPS = [
}, },
propertyID: "billboardMode", propertyID: "billboardMode",
}, },
{
label: "Mirror Mode",
type: "dropdown",
options: {
none: "None",
mirror: "Mirror",
portal: "Portal"
},
propertyID: "mirrorMode",
},
{
label: "Portal Exit",
type: "string",
propertyID: "portalExitID",
showPropertyRule: { "mirrorMode": "portal" },
},
{ {
label: "Render With Zones", label: "Render With Zones",
type: "multipleZonesSelection", type: "multipleZonesSelection",

View file

@ -122,7 +122,7 @@ void RenderThread::renderFrame(gpu::FramePointer& frame) {
if (_correction != glm::mat4()) { if (_correction != glm::mat4()) {
std::unique_lock<std::mutex> lock(_frameLock); std::unique_lock<std::mutex> lock(_frameLock);
if (_correction != glm::mat4()) { if (_correction != glm::mat4()) {
_backend->setCameraCorrection(_correction, _activeFrame->view); _backend->setCameraCorrection(_correction, _activeFrame->view, true);
//_prevRenderView = _correction * _activeFrame->view; //_prevRenderView = _correction * _activeFrame->view;
} }
} }