From 89e6cbbfa4b05e16589f6034f8be32aa41a0bd0a Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Thu, 3 Mar 2022 22:28:05 -0800 Subject: [PATCH] mirrors wip --- interface/src/SecondaryCamera.cpp | 3 +- .../scripting/RenderScriptingInterface.cpp | 24 ++- .../src/RenderableEntityItem.cpp | 75 ++++++++++ .../src/RenderableEntityItem.h | 10 +- .../src/RenderableModelEntityItem.cpp | 20 +++ .../src/RenderableModelEntityItem.h | 2 + .../src/RenderableShapeEntityItem.cpp | 2 +- libraries/entities/src/EntityItem.cpp | 36 +++++ libraries/entities/src/EntityItem.h | 9 ++ .../entities/src/EntityItemProperties.cpp | 44 ++++++ libraries/entities/src/EntityItemProperties.h | 3 + libraries/entities/src/EntityPropertyFlags.h | 2 + .../graphics/src/graphics/ShaderConstants.h | 2 + libraries/networking/src/udt/PacketHeaders.h | 1 + libraries/octree/src/OctreePacketData.h | 2 + .../render-utils/src/HighlightEffect.cpp | 7 +- .../render-utils/src/MeshPartPayload.cpp | 13 +- libraries/render-utils/src/MeshPartPayload.h | 7 + libraries/render-utils/src/Model.cpp | 45 +++++- libraries/render-utils/src/Model.h | 9 ++ .../render-utils/src/RenderCommonTask.cpp | 137 +++++++++++++++++- libraries/render-utils/src/RenderCommonTask.h | 17 ++- .../render-utils/src/RenderDeferredTask.cpp | 30 ++-- .../render-utils/src/RenderDeferredTask.h | 2 +- .../render-utils/src/RenderForwardTask.cpp | 24 ++- .../render-utils/src/RenderForwardTask.h | 2 +- .../render-utils/src/RenderPipelines.cpp | 40 +++++ .../render-utils/src/RenderShadowTask.cpp | 7 +- libraries/render-utils/src/RenderViewTask.cpp | 14 +- libraries/render-utils/src/RenderViewTask.h | 6 +- libraries/render-utils/src/model.slf | 22 ++- .../render-utils/src/render-utils/model.slp | 2 +- libraries/render/src/render/Item.cpp | 7 + libraries/render/src/render/Item.h | 23 ++- .../src/render/RenderFetchCullSortTask.cpp | 11 +- .../src/render/RenderFetchCullSortTask.h | 1 + libraries/shared/src/MirrorMode.cpp | 25 ++++ libraries/shared/src/MirrorMode.h | 40 +++++ .../create/assets/data/createAppTooltips.json | 12 +- .../html/js/entityProperties.js | 15 ++ 40 files changed, 699 insertions(+), 54 deletions(-) create mode 100644 libraries/shared/src/MirrorMode.cpp create mode 100644 libraries/shared/src/MirrorMode.h diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index 704d7963e7..130b8c77ea 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -27,7 +27,7 @@ public: using Config = SecondaryCameraJobConfig; using JobModel = render::Job::ModelO; SecondaryCameraJob() { - _cachedArgsPointer = std::make_shared(_cachedArgs); + _cachedArgsPointer = std::make_shared(); _attachedEntityPropertyFlags += PROP_POSITION; _attachedEntityPropertyFlags += PROP_ROTATION; } @@ -203,7 +203,6 @@ public: } protected: - RenderArgs _cachedArgs; RenderArgsPointer _cachedArgsPointer; private: diff --git a/interface/src/scripting/RenderScriptingInterface.cpp b/interface/src/scripting/RenderScriptingInterface.cpp index 12814aa6b6..47f772b4bc 100644 --- a/interface/src/scripting/RenderScriptingInterface.cpp +++ b/interface/src/scripting/RenderScriptingInterface.cpp @@ -9,6 +9,7 @@ // #include "RenderScriptingInterface.h" +#include #include #include "LightingModel.h" @@ -79,14 +80,35 @@ void RenderScriptingInterface::setRenderMethod(RenderMethod renderMethod) { 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(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) { _renderSettingLock.withWriteLock([&] { _renderMethod = (int)renderMethod; _renderMethodSetting.set((int)renderMethod); - auto config = dynamic_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("RenderMainView.DeferredForwardSwitch")); + QString configName = "RenderMainView.DeferredForwardSwitch"; + auto config = dynamic_cast(qApp->getRenderEngine()->getConfiguration()->getConfig(configName)); if (config) { config->setBranch((int)renderMethod); + + recursivelyUpdateMirrorRenderMethods(configName + (renderMethod == RenderMethod::FORWARD ? ".RenderForwardTask" : ".RenderShadowsAndDeferredTask.RenderDeferredTask"), + (int)renderMethod, 0); } }); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 212baa6634..108016a939 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -12,6 +12,7 @@ #include "RenderableEntityItem.h" +#include #include #include "RenderableShapeEntityItem.h" @@ -192,6 +193,10 @@ ItemKey EntityRenderer::getKey() { builder.withSubMetaCulled(); } + if (_mirrorMode != MirrorMode::NONE) { + builder.withMirror(); + } + if (!_visible) { builder.withInvisible(); } @@ -221,6 +226,70 @@ bool EntityRenderer::passesZoneOcclusionTest(const std::unordered_set& co return true; } +void EntityRenderer::computeMirrorView(ViewFrustum& viewFrustum) const { + //glm::vec3 mirrorPosition; + //glm::quat mirrorRotation; + //withReadLock([&]{ + // mirrorPosition = _entity->getWorldPosition(); + // mirrorRotation = _entity->getWorldOrientation(); + //}); + + //glm::mat4 mirrorToWorld = glm::translate(mirrorPosition) * glm::mat4_cast(mirrorRotation); + //glm::mat4 worldToMirror = glm::inverse(mirrorToWorld); + + //// get mirror camera position by reflecting main camera position's z coordinate in mirror space + //glm::vec3 cameraPosition = viewFrustum.getPosition(); + //glm::quat cameraRotation = viewFrustum.getOrientation(); + //glm::vec3 localCameraPosition = glm::vec3(worldToMirror * glm::vec4(cameraPosition, 1.0f)); + //localCameraPosition.z *= -1.0f; + //glm::quat localCameraRotation = worldToMirror * glm::mat4_cast(cameraRotation); + //glm::vec3 localCameraRotationAngles = glm::eulerAngles(localCameraRotation); + //localCameraRotationAngles.y = M_PI - localCameraRotationAngles.y; + + //viewFrustum.setPosition(mirrorToWorld * glm::vec4(localCameraPosition, 1.0f)); + //viewFrustum.setOrientation(mirrorToWorld * glm::mat4_cast(glm::quat(localCameraRotationAngles))); + + glm::vec3 mirrorPropertiesPosition; + glm::quat mirrorPropertiesRotation; + glm::vec3 mirrorPropertiesDimensions; + withReadLock([&]{ + mirrorPropertiesPosition = _entity->getWorldPosition(); + mirrorPropertiesRotation = _entity->getWorldOrientation(); + mirrorPropertiesDimensions = _entity->getScaledDimensions(); + }); + + glm::vec3 halfMirrorPropertiesDimensions = 0.5f * mirrorPropertiesDimensions; + + glm::mat4 worldFromMirrorRotation = glm::mat4_cast(mirrorPropertiesRotation); + glm::mat4 worldFromMirrorTranslation = glm::translate(mirrorPropertiesPosition); + glm::mat4 worldFromMirror = worldFromMirrorTranslation * worldFromMirrorRotation; + glm::mat4 mirrorFromWorld = glm::inverse(worldFromMirror); + + // get mirror camera position by reflecting main camera position's z coordinate in mirror space + glm::vec3 mainCameraPositionWorld = viewFrustum.getPosition(); + glm::vec3 mainCameraPositionMirror = vec3(mirrorFromWorld * vec4(mainCameraPositionWorld, 1.0f)); + glm::vec3 mirrorCameraPositionMirror = vec3(mainCameraPositionMirror.x, mainCameraPositionMirror.y, + -mainCameraPositionMirror.z); + glm::vec3 mirrorCameraPositionWorld = vec3(worldFromMirror * vec4(mirrorCameraPositionMirror, 1.0f)); + + // 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::mat4 mainCameraRotationMirror = mirrorFromWorld * glm::mat4_cast(mainCameraRotationWorld); + glm::mat4 mirrorCameraRotationMirror = mainCameraRotationMirror;// * glm::scale(vec3(-1.0f, 1.0f, -1.0f)); + glm::quat mirrorCameraRotationWorld = worldFromMirror * mirrorCameraRotationMirror; + + viewFrustum.setPosition(mirrorCameraPositionWorld); + viewFrustum.setOrientation(mirrorCameraRotationWorld); + + // build frustum using mirror space translation of mirrored camera + //float nearClip = mirrorCameraPositionMirror.z + mirrorPropertiesDimensions.z * 2.0f; + //glm::vec3 upperRight = halfMirrorPropertiesDimensions - mirrorCameraPositionMirror; + //glm::vec3 bottomLeft = -halfMirrorPropertiesDimensions - mirrorCameraPositionMirror; + //glm::mat4 frustum = glm::frustum(bottomLeft.x, upperRight.x, bottomLeft.y, upperRight.y, nearClip, viewFrustum.getFarClip()); + //viewFrustum.setProjection(frustum); +} + void EntityRenderer::render(RenderArgs* args) { if (!isValidRenderItem()) { return; @@ -454,6 +523,8 @@ void EntityRenderer::doRenderUpdateAsynchronous(const EntityItemPointer& entity) _canCastShadow = entity->getCanCastShadow(); setCullWithParent(entity->getCullWithParent()); _cauterized = entity->getCauterized(); + setMirrorMode(entity->getMirrorMode()); + setPortalExitID(entity->getPortalExitID()); if (entity->needsZoneOcclusionUpdate()) { entity->resetNeedsZoneOcclusionUpdate(); _renderWithZones = entity->getRenderWithZones(); @@ -505,6 +576,10 @@ graphics::MaterialPointer EntityRenderer::getTopMaterial() { } EntityRenderer::Pipeline EntityRenderer::getPipelineType(const graphics::MultiMaterial& materials) { + if (_mirrorMode != MirrorMode::NONE) { + return Pipeline::MIRROR; + } + if (materials.top().material && materials.top().material->isProcedural() && materials.top().material->isReady()) { return Pipeline::PROCEDURAL; } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 3caeef0713..bfec4743f3 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -58,12 +58,13 @@ public: enum class Pipeline { SIMPLE, MATERIAL, - PROCEDURAL + PROCEDURAL, + MIRROR }; virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName); virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName); virtual graphics::MaterialPointer getTopMaterial(); - static Pipeline getPipelineType(const graphics::MultiMaterial& materials); + Pipeline getPipelineType(const graphics::MultiMaterial& materials); virtual gpu::TexturePointer getTexture() { return nullptr; } virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); } @@ -74,6 +75,7 @@ public: virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const override; virtual Item::Bound getBound(RenderArgs* args) override; bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const override; + void computeMirrorView(ViewFrustum& viewFrustum) const override; protected: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } @@ -116,6 +118,8 @@ protected: virtual void setIsVisibleInSecondaryCamera(bool value) { _isVisibleInSecondaryCamera = value; } virtual void setRenderLayer(RenderLayer value) { _renderLayer = value; } virtual void setCullWithParent(bool value) { _cullWithParent = value; } + virtual void setMirrorMode(MirrorMode value) { _mirrorMode = value; } + virtual void setPortalExitID(const QUuid& value) { _portalExitID = value; } template std::shared_ptr asTypedEntity() { return std::static_pointer_cast(_entity); } @@ -151,6 +155,8 @@ protected: BillboardMode _billboardMode { BillboardMode::NONE }; bool _cauterized { false }; bool _moving { false }; + MirrorMode _mirrorMode { MirrorMode::NONE }; + QUuid _portalExitID; Transform _renderTransform; MaterialMap _materials; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 8ed3f84609..aa84280c74 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1085,6 +1085,10 @@ void ModelEntityRenderer::setKey(bool didVisualGeometryRequestSucceed, const Mod builder.withSubMetaCulled(); } + if (_mirrorMode != MirrorMode::NONE) { + builder.withMirror(); + } + if (didVisualGeometryRequestSucceed) { _itemKey = builder.build(); } else { @@ -1274,6 +1278,8 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint _model->setBillboardMode(_billboardMode, scene); _model->setCullWithParent(_cullWithParent, scene); _model->setRenderWithZones(_renderWithZones, scene); + _model->setMirrorMode(_mirrorMode, scene); + _model->setPortalExitID(_portalExitID, scene); }); if (didVisualGeometryRequestSucceed) { emit DependencyManager::get()-> @@ -1353,6 +1359,8 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint model->setBillboardMode(_billboardMode, scene); model->setCullWithParent(_cullWithParent, scene); model->setRenderWithZones(_renderWithZones, scene); + model->setMirrorMode(_mirrorMode, scene); + model->setPortalExitID(_portalExitID, scene); }); if (entity->blendshapesChanged()) { @@ -1466,6 +1474,18 @@ void ModelEntityRenderer::setCullWithParent(bool value) { 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 void ModelEntityRenderer::doRender(RenderArgs* args) { DETAILED_PROFILE_RANGE(render_detail, "MetaModelRender"); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index f394d389f5..425d082f01 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -170,6 +170,8 @@ protected: void setIsVisibleInSecondaryCamera(bool value) override; void setRenderLayer(RenderLayer value) override; void setCullWithParent(bool value) override; + void setMirrorMode(MirrorMode value) override; + void setPortalExitID(const QUuid& value) override; private: void animate(const TypedEntityPointer& entity, const ModelPointer& model); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index a6fee03311..b8954f2ced 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -157,7 +157,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { } } } 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++; } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index b63f0d91ab..34217d5bdf 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -105,6 +105,8 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_RENDER_WITH_ZONES; requestedProperties += PROP_BILLBOARD_MODE; requestedProperties += _grabProperties.getEntityProperties(params); + requestedProperties += PROP_MIRROR_MODE; + requestedProperties += PROP_PORTAL_EXIT_ID; // Physics requestedProperties += PROP_DENSITY; @@ -305,6 +307,8 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet _grabProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); }); + APPEND_ENTITY_PROPERTY(PROP_MIRROR_MODE, (uint32_t)getMirrorMode()); + APPEND_ENTITY_PROPERTY(PROP_PORTAL_EXIT_ID, getPortalExitID()); // Physics APPEND_ENTITY_PROPERTY(PROP_DENSITY, getDensity()); @@ -881,6 +885,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef bytesRead += 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); { @@ -1361,6 +1367,8 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire withReadLock([&] { _grabProperties.getProperties(properties); }); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(mirrorMode, getMirrorMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(portalExitID, getPortalExitID); // Physics COPY_ENTITY_PROPERTY_TO_PROPERTIES(density, getDensity); @@ -1499,6 +1507,8 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { bool grabPropertiesChanged = _grabProperties.setProperties(properties); somethingChanged |= grabPropertiesChanged; }); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(mirrorMode, setMirrorMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(portalExitID, setPortalExitID); // Physics SET_ENTITY_PROPERTY_FROM_PROPERTIES(density, setDensity); @@ -3553,3 +3563,29 @@ void EntityItem::setBillboardMode(BillboardMode value) { _billboardMode = value; }); } + +MirrorMode EntityItem::getMirrorMode() const { + return resultWithReadLock([&] { + return _mirrorMode; + }); +} + +void EntityItem::setMirrorMode(MirrorMode value) { + withWriteLock([&] { + _needsRenderUpdate |= _mirrorMode != value; + _mirrorMode = value; + }); +} + +QUuid EntityItem::getPortalExitID() const { + return resultWithReadLock([&] { + return _portalExitID; + }); +} + +void EntityItem::setPortalExitID(const QUuid& value) { + withWriteLock([&] { + _needsRenderUpdate |= _portalExitID != value; + _portalExitID = value; + }); +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index ec84b0ccb2..32f6dccf37 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -559,6 +559,12 @@ public: BillboardMode getBillboardMode() const; virtual bool getRotateForPicking() const { return false; } + MirrorMode getMirrorMode() const; + void setMirrorMode(MirrorMode value); + + QUuid getPortalExitID() const; + void setPortalExitID(const QUuid& value); + signals: void spaceUpdate(std::pair data); @@ -740,6 +746,9 @@ protected: bool _cullWithParent { false }; + MirrorMode _mirrorMode { MirrorMode::NONE }; + QUuid _portalExitID; + mutable bool _needsRenderUpdate { false }; }; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index f543169401..4d21e040f5 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -424,6 +424,23 @@ void EntityItemProperties::setEntityHostTypeFromString(const QString& entityHost } } +inline void addMirrorMode(QHash& lookup, MirrorMode mirrorMode) { lookup[MirrorModeHelpers::getNameForMirrorMode(mirrorMode)] = mirrorMode; } +const QHash stringToMirrorModeLookup = [] { + QHash 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 changedProperties; @@ -455,6 +472,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_RENDER_WITH_ZONES, renderWithZones); CHECK_PROPERTY_CHANGE(PROP_BILLBOARD_MODE, billboardMode); changedProperties += _grab.getChangedProperties(); + CHECK_PROPERTY_CHANGE(PROP_MIRROR_MODE, mirrorMode); + CHECK_PROPERTY_CHANGE(PROP_PORTAL_EXIT_ID, portalExitID); // Physics 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 {MirrorMode} mirrorMode="none" - If this entity should render as a mirror (reflecting the view of the camera), + * a portal (reflecting the view through its portalExitID), or normally. + * @property {Uuid} portalExitID=Uuid.NULL - The ID of the entity that should act as the portal exit if the mirrorMode + * is set to portal. + * * @comment The different entity types have additional properties as follows: * @see {@link Entities.EntityProperties-Box|EntityProperties-Box} * @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_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); _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 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_ENUM(billboardMode, BillboardMode); _grab.copyFromScriptValue(object, namesSet, _defaultSettings); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(mirrorMode, MirrorMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE(portalExitID, QUuid, setPortalExitID); // Physics 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(billboardMode); _grab.merge(other._grab); + COPY_PROPERTY_IF_CHANGED(mirrorMode); + COPY_PROPERTY_IF_CHANGED(portalExitID); // Physics 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, EquippableIndicatorOffset, equippableIndicatorOffset); } + ADD_PROPERTY_TO_MAP(PROP_MIRROR_MODE, MirrorMode, mirrorMode, MirrorMode); + ADD_PROPERTY_TO_MAP(PROP_PORTAL_EXIT_ID, PortalExitID, portalExitID, QUuid); // Physics ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DENSITY, Density, density, float, @@ -3090,6 +3122,8 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticGrab.setProperties(properties); _staticGrab.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_MIRROR_MODE, (uint32_t)properties.getMirrorMode()); + APPEND_ENTITY_PROPERTY(PROP_PORTAL_EXIT_ID, properties.getPortalExitID()); // Physics 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, setRenderWithZones); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); 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 READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DENSITY, float, setDensity); @@ -3960,6 +3996,8 @@ void EntityItemProperties::markAllChanged() { _renderWithZonesChanged = true; _billboardModeChanged = true; _grab.markAllChanged(); + _mirrorModeChanged = true; + _portalExitIDChanged = true; // Physics _densityChanged = true; @@ -4359,6 +4397,12 @@ QList EntityItemProperties::listChangedProperties() { out += "billboardMode"; } getGrab().listChangedProperties(out); + if (mirrorModeChanged()) { + out += "mirrorMode"; + } + if (portalExitIDChanged()) { + out += "portalExitID"; + } // Physics if (densityChanged()) { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 283d14c4cc..3e07a75616 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -70,6 +70,7 @@ #include "GizmoType.h" #include "TextEffect.h" #include "TextAlignment.h" +#include "MirrorMode.h" class ScriptEngine; @@ -204,6 +205,8 @@ public: DEFINE_PROPERTY_REF(PROP_RENDER_WITH_ZONES, RenderWithZones, renderWithZones, QVector, QVector()); DEFINE_PROPERTY_REF_ENUM(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode, BillboardMode::NONE); 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 DEFINE_PROPERTY(PROP_DENSITY, Density, density, float, ENTITY_ITEM_DEFAULT_DENSITY); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index e0b5a04094..c0cbb3f9a1 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -60,6 +60,8 @@ enum EntityPropertyList { PROP_GRAB_EQUIPPABLE_INDICATOR_URL, PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, + PROP_MIRROR_MODE, + PROP_PORTAL_EXIT_ID, // Physics PROP_DENSITY, diff --git a/libraries/graphics/src/graphics/ShaderConstants.h b/libraries/graphics/src/graphics/ShaderConstants.h index 3a614d26cd..8fd0df31f0 100644 --- a/libraries/graphics/src/graphics/ShaderConstants.h +++ b/libraries/graphics/src/graphics/ShaderConstants.h @@ -27,6 +27,7 @@ #define GRAPHICS_TEXTURE_MATERIAL_ROUGHNESS 4 #define GRAPHICS_TEXTURE_MATERIAL_OCCLUSION 5 #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 #define GRAPHICS_TEXTURE_SKYBOX 11 @@ -59,6 +60,7 @@ enum Texture { MaterialRoughness = GRAPHICS_TEXTURE_MATERIAL_ROUGHNESS, MaterialOcclusion = GRAPHICS_TEXTURE_MATERIAL_OCCLUSION, MaterialScattering = GRAPHICS_TEXTURE_MATERIAL_SCATTERING, + MaterialMirror = GRAPHICS_TEXTURE_MATERIAL_MIRROR, Skybox = GRAPHICS_TEXTURE_SKYBOX }; } // namespace texture diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 3badfdf418..51739e0f77 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -290,6 +290,7 @@ enum class EntityVersion : PacketVersion { UserAgent, AllBillboardMode, TextAlignment, + Mirror, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 583f090942..30b826aaec 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -42,6 +42,7 @@ #include "GizmoType.h" #include "TextEffect.h" #include "TextAlignment.h" +#include "MirrorMode.h" #include "OctreeConstants.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, 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, 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::vec3& result); static int unpackDataFromBytes(const unsigned char* dataBytes, glm::u8vec3& result); diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index 5a8b09b018..b4aced626d 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -506,8 +506,9 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren const auto jitter = inputs.getN(4); // Prepare the ShapePipeline - auto shapePlumber = std::make_shared(); - { + static ShapePlumberPointer shapePlumber = std::make_shared(); + static std::once_flag once; + std::call_once(once, [] { auto state = std::make_shared(); state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setColorWriteMask(false, false, false, false); @@ -515,7 +516,7 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren auto fadeEffect = DependencyManager::get(); initZPassPipelines(*shapePlumber, state, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); - } + }); auto sharedParameters = std::make_shared(); const auto highlightSelectionNames = task.addJob("SelectionToHighlight", sharedParameters); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 8ba5be54e6..8d9d31390d 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -12,6 +12,7 @@ #include "MeshPartPayload.h" #include +#include #include #include #include @@ -216,7 +217,7 @@ void ModelMeshPartPayload::updateKey(const render::ItemKey& key) { builder.withTransparent(); } - if (_cullWithParent) { + if (_cullWithParent || _mirrorMode != MirrorMode::NONE) { builder.withSubMetaCulled(); } @@ -377,6 +378,10 @@ bool ModelMeshPartPayload::passesZoneOcclusionTest(const std::unordered_set& blendshapeBuffers, const QVector& blendedMeshSizes) { if (_meshIndex < blendedMeshSizes.length() && blendedMeshSizes.at(_meshIndex) == _meshNumVertices) { auto blendshapeBuffer = blendshapeBuffers.find(_meshIndex); @@ -426,4 +431,10 @@ template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Poin } return false; } + +template <> void payloadComputeMirrorView(const ModelMeshPartPayload::Pointer& payload, ViewFrustum& viewFrustum) { + if (payload) { + payload->computeMirrorView(viewFrustum); + } +} } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 1a3a898582..3084d8ae01 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -58,7 +58,10 @@ public: void setCullWithParent(bool value) { _cullWithParent = value; } void setRenderWithZones(const QVector& renderWithZones) { _renderWithZones = renderWithZones; } 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& containingZones) const; + void computeMirrorView(ViewFrustum& viewFrustum) const; void addMaterial(graphics::MaterialLayer material) { _drawMaterials.push(material); } void removeMaterial(graphics::MaterialPointer material) { _drawMaterials.remove(material); } @@ -93,6 +96,8 @@ private: bool _cullWithParent { false }; QVector _renderWithZones; BillboardMode _billboardMode { BillboardMode::NONE }; + MirrorMode _mirrorMode { MirrorMode::NONE }; + QUuid _portalExitID; uint64_t _created; Transform _localTransform; @@ -107,6 +112,8 @@ namespace render { template <> const ShapeKey shapeGetShapeKey(const ModelMeshPartPayload::Pointer& payload); template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args); template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Pointer& payload, const std::unordered_set& containingZones); + template <> void payloadComputeMirrorView(const ModelMeshPartPayload::Pointer& payload, ViewFrustum& viewFrustum); + } #endif // hifi_MeshPartPayload_h diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index eabcabc7e5..9be811a7a9 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -232,6 +232,8 @@ void Model::updateRenderItems() { auto renderWithZones = self->getRenderWithZones(); auto renderItemKeyGlobalFlags = self->getRenderItemKeyGlobalFlags(); bool cauterized = self->isCauterized(); + auto mirrorMode = self->getMirrorMode(); + const QUuid& portalExitID = self->getPortalExitID(); render::Transaction transaction; for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) { @@ -246,7 +248,7 @@ void Model::updateRenderItems() { transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, invalidatePayloadShapeKey, primitiveMode, billboardMode, renderItemKeyGlobalFlags, - cauterized, renderWithZones](ModelMeshPartPayload& data) { + cauterized, renderWithZones, mirrorMode, portalExitID](ModelMeshPartPayload& data) { if (useDualQuaternionSkinning) { data.updateClusterBuffer(meshState.clusterDualQuaternions); data.computeAdjustedLocalBound(meshState.clusterDualQuaternions); @@ -260,6 +262,8 @@ void Model::updateRenderItems() { data.setCauterized(cauterized); data.setRenderWithZones(renderWithZones); data.setBillboardMode(billboardMode); + data.setMirrorMode(mirrorMode); + data.setPortalExitID(portalExitID); data.updateKey(renderItemKeyGlobalFlags); data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning); }); @@ -1065,6 +1069,45 @@ void Model::setRenderWithZones(const QVector& 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(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(item, [portalExitID, renderItemsKey](ModelMeshPartPayload& data) { + data.setPortalExitID(portalExitID); + }); + } + scene->enqueueTransaction(transaction); + } +} + const render::ItemKey Model::getRenderItemKeyGlobalFlags() const { return _renderItemKeyGlobalFlags; } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 17b7df9b23..63a96f7253 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -41,6 +41,7 @@ #include "Rig.h" #include "PrimitiveMode.h" #include "BillboardMode.h" +#include "MirrorMode.h" // Use dual quaternion skinning! // Must match define in Skinning.slh @@ -131,6 +132,12 @@ public: void setRenderWithZones(const QVector& renderWithZones, const render::ScenePointer& scene = nullptr); const QVector& 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. const render::ItemKey getRenderItemKeyGlobalFlags() const; @@ -503,6 +510,8 @@ protected: bool _cauterized { false }; bool _cullWithParent { false }; QVector _renderWithZones; + MirrorMode _mirrorMode { MirrorMode::NONE }; + QUuid _portalExitID; bool shouldInvalidatePayloadShapeKey(int meshIndex); diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index 7cf7f1129f..f41ed6b21f 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -13,7 +13,10 @@ #include "render-utils/ShaderConstants.h" #include "DeferredLightingEffect.h" +#include "FadeEffect.h" #include "RenderUtilsLogging.h" +#include "RenderViewTask.h" +#include "StencilMaskPass.h" namespace ru { using render_utils::slot::texture::Texture; @@ -25,9 +28,11 @@ namespace gr { using graphics::slot::buffer::Buffer; } +using RenderArgsPointer = std::shared_ptr; using namespace render; 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) { timer = _gpuTimer; @@ -45,10 +50,14 @@ void EndGPURangeTimer::run(const render::RenderContextPointer& renderContext, co config->setGPUBatchRunTime(timer->getGPUAverage(), timer->getBatchAverage()); } +render::ShapePlumberPointer DrawLayered3D::_shapePlumber = std::make_shared(); + DrawLayered3D::DrawLayered3D(bool opaque) : - _shapePlumber(std::make_shared()), _opaquePass(opaque) { - initForwardPipelines(*_shapePlumber); + static std::once_flag once; + std::call_once(once, [] { + initForwardPipelines(*_shapePlumber); + }); } void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs& inputs) { @@ -262,3 +271,127 @@ void ResolveFramebuffer::run(const render::RenderContextPointer& renderContext, } } +class SetupMirrorTask { +public: + using Input = RenderMirrorTask::Inputs; + using Outputs = render::VaryingSet4; + using JobModel = render::Job::ModelIO; + + SetupMirrorTask(size_t mirrorIndex) : _mirrorIndex(mirrorIndex) {} + + 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())); + } + + _cachedArgsPointer->_renderMode = args->_renderMode; + _cachedArgsPointer->_blitFramebuffer = args->_blitFramebuffer; + args->_renderMode = RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE; + args->_blitFramebuffer = _mirrorFramebuffer; + + render::ItemBound mirror = items[_mirrorIndex]; + ViewFrustum srcViewFrustum = args->getViewFrustum(); + args->_scene->getItem(mirror.id).computeMirrorView(srcViewFrustum); + + // 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() }; + size_t _mirrorIndex; + +}; + +class DrawMirrorTask { +public: + using Inputs = SetupMirrorTask::Outputs; + using JobModel = render::Job::ModelI; + + DrawMirrorTask() { + static std::once_flag once; + std::call_once(once, [this] { + auto state = std::make_shared(); + state->setCullMode(gpu::State::CULL_BACK); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + PrepareStencil::testMaskDrawShape(*state); + + auto fadeEffect = DependencyManager::get(); + 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; + + 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; + }); + + // Restore the blit framebuffer after we've sampled from it + if (cachedArgs) { + args->_blitFramebuffer = cachedArgs->_blitFramebuffer; + } + } + +private: + static ShapePlumberPointer _forwardPipelines; + static ShapePlumberPointer _deferredPipelines; +}; + +ShapePlumberPointer DrawMirrorTask::_forwardPipelines = std::make_shared(); +ShapePlumberPointer DrawMirrorTask::_deferredPipelines = std::make_shared(); + + void RenderMirrorTask::build(JobModel& task, const render::Varying& inputs, render::Varying& output, size_t mirrorIndex, render::CullFunctor cullFunctor, size_t depth) { + const auto setupOutput = task.addJob("SetupMirror" + std::to_string(mirrorIndex) + "Depth" + std::to_string(depth), inputs, mirrorIndex); + + task.addJob("RenderMirrorView" + std::to_string(mirrorIndex) + "Depth" + std::to_string(depth), cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1, depth + 1); + + task.addJob("DrawMirrorTask" + std::to_string(mirrorIndex) + "Depth" + std::to_string(depth), setupOutput); + } diff --git a/libraries/render-utils/src/RenderCommonTask.h b/libraries/render-utils/src/RenderCommonTask.h index 15d6ff9895..cada3cb6ea 100644 --- a/libraries/render-utils/src/RenderCommonTask.h +++ b/libraries/render-utils/src/RenderCommonTask.h @@ -10,6 +10,8 @@ #define hifi_RenderCommonTask_h #include +#include + #include "LightStage.h" #include "HazeStage.h" #include "LightingModel.h" @@ -71,7 +73,7 @@ public: void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); protected: - render::ShapePlumberPointer _shapePlumber; + static render::ShapePlumberPointer _shapePlumber; int _maxDrawn; // initialized by Config bool _opaquePass { true }; }; @@ -152,4 +154,17 @@ protected: render::Args::RenderMethod _method; }; +class RenderMirrorTask { +public: + using Inputs = render::VaryingSet3; + using JobModel = render::Task::ModelI; + + 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 diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index c506f22bc7..80157f9a42 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -100,11 +100,14 @@ void RenderDeferredTask::configure(const Config& config) { preparePrimaryBufferConfig->setResolutionScale(config.resolutionScale); } -void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { - auto fadeEffect = DependencyManager::get(); +void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, size_t depth) { + static auto fadeEffect = DependencyManager::get(); // Prepare the ShapePipelines - ShapePlumberPointer shapePlumber = std::make_shared(); - initDeferredPipelines(*shapePlumber, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); + static ShapePlumberPointer shapePlumber = std::make_shared(); + static std::once_flag once; + std::call_once(once, [] { + initDeferredPipelines(*shapePlumber, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); + }); const auto& inputs = input.get(); @@ -116,6 +119,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren // Extract opaques / transparents / lights / metas / layered / background const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_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& inFrontTransparent = items[RenderFetchCullSortTask::LAYER_FRONT_TRANSPARENT_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 const auto shadowFrame = shadowTaskOutputs[1]; - fadeEffect->build(task, opaques); const auto jitter = task.addJob("JitterCam"); @@ -162,6 +165,13 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel, jitter).asVarying(); task.addJob("DrawOpaqueDeferred", opaqueInputs, shapePlumber); + if (depth < RenderMirrorTask::MAX_MIRROR_DEPTH) { + const auto mirrorInputs = RenderMirrorTask::Inputs(mirrors, scaledPrimaryFramebuffer, jitter).asVarying(); + for (size_t i = 0; i < RenderMirrorTask::MAX_MIRRORS_PER_LEVEL; i++) { + task.addJob("RenderMirrorTask" + std::to_string(i) + "Depth" + std::to_string(depth), mirrorInputs, i, cullFunctor, depth); + } + } + // Opaque all rendered // Linear Depth Pass @@ -398,8 +408,12 @@ void RenderDeferredTaskDebug::build(JobModel& task, const render::Varying& input // Status icon rendering job { // Grab a texture map representing the different status icons and assign that to the drawStatusJob - auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; - auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath, image::TextureUsage::STRICT_TEXTURE); + static gpu::TexturePointer statusIconMap; + static std::once_flag once; + std::call_once(once, [] { + auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; + statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath, image::TextureUsage::STRICT_TEXTURE); + }); const auto drawStatusInputs = DrawStatus::Input(opaques, jitter).asVarying(); task.addJob("DrawStatus", drawStatusInputs, DrawStatus(statusIconMap)); } @@ -407,8 +421,6 @@ void RenderDeferredTaskDebug::build(JobModel& task, const render::Varying& input const auto debugZoneInputs = DebugZoneLighting::Inputs(deferredFrameTransform, lightFrame, backgroundFrame).asVarying(); task.addJob("DrawZoneStack", debugZoneInputs); } - - } gpu::FramebufferPointer PreparePrimaryFramebuffer::createFramebuffer(const char* name, const glm::uvec2& frameSize) { diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 969094488e..5fc9580981 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -144,7 +144,7 @@ public: RenderDeferredTask(); 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: }; diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index e34db755b7..12af18e44b 100644 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -34,6 +34,8 @@ #include "RenderCommonTask.h" #include "RenderHUDLayerTask.h" +#include "RenderViewTask.h" + namespace ru { using render_utils::slot::texture::Texture; using render_utils::slot::buffer::Buffer; @@ -66,13 +68,16 @@ void RenderForwardTask::configure(const Config& config) { 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("SetRenderMethodTask", render::Args::FORWARD); // Prepare the ShapePipelines auto fadeEffect = DependencyManager::get(); - ShapePlumberPointer shapePlumber = std::make_shared(); - initForwardPipelines(*shapePlumber); + static ShapePlumberPointer shapePlumber = std::make_shared(); + static std::once_flag once; + std::call_once(once, [] { + initForwardPipelines(*shapePlumber); + }); // Unpack inputs const auto& inputs = input.get(); @@ -86,6 +91,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE]; const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; const auto& metas = items[RenderFetchCullSortTask::META]; + const auto& mirrors = items[RenderFetchCullSortTask::MIRROR]; const auto& inFrontOpaque = items[RenderFetchCullSortTask::LAYER_FRONT_OPAQUE_SHAPE]; const auto& inFrontTransparent = items[RenderFetchCullSortTask::LAYER_FRONT_TRANSPARENT_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 fadeEffect->build(task, opaques); - // GPU jobs: Start preparing the main framebuffer const auto scaledPrimaryFramebuffer = task.addJob("PreparePrimaryBufferForward"); @@ -125,6 +130,16 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto opaqueInputs = DrawForward::Inputs(opaques, lightingModel, hazeFrame).asVarying(); task.addJob("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" + 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 const auto backgroundInputs = DrawBackgroundStage::Inputs(lightingModel, backgroundFrame, hazeFrame).asVarying(); task.addJob("DrawBackgroundForward", backgroundInputs); @@ -134,7 +149,6 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend task.addJob("DrawTransparents", transparentInputs, shapePlumber, false); // Layered - const auto nullJitter = Varying(glm::vec2(0.0f, 0.0f)); const auto inFrontOpaquesInputs = DrawLayered3D::Inputs(inFrontOpaque, lightingModel, hazeFrame, nullJitter).asVarying(); const auto inFrontTransparentsInputs = DrawLayered3D::Inputs(inFrontTransparent, lightingModel, hazeFrame, nullJitter).asVarying(); task.addJob("DrawInFrontOpaque", inFrontOpaquesInputs, true); diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index 6833e42449..de3a6dd205 100644 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -36,7 +36,7 @@ public: RenderForwardTask() {} 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); }; diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index ed9fb326c4..b5a88b0ba9 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -42,6 +42,7 @@ namespace gr { void initDeferredPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); void initForwardPipelines(ShapePlumber& plumber); 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, 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); } +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) { graphics::MultiMaterial multiMaterial; multiMaterial.push(graphics::MaterialLayer(material, 0)); diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index dbfa23a143..9fabad5fd0 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -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) { // Prepare the ShapePipeline - ShapePlumberPointer shapePlumber = std::make_shared(); - { + static ShapePlumberPointer shapePlumber = std::make_shared(); + static std::once_flag once; + std::call_once(once, [] { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); state->setDepthTest(true, true, gpu::LESS_EQUAL); auto fadeEffect = DependencyManager::get(); initZPassPipelines(*shapePlumber, state, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); - } + }); const auto setupOutput = task.addJob("ShadowSetup", input); const auto queryResolution = setupOutput.getN(1); const auto shadowFrame = setupOutput.getN(3); diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index 93a3ff2d67..b9320b6ad3 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -15,7 +15,7 @@ #include "RenderDeferredTask.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("SetRenderMethodTask", render::Args::DEFERRED); const auto items = input.getN(0); @@ -28,16 +28,16 @@ void RenderShadowsAndDeferredTask::build(JobModel& task, const render::Varying& const auto shadowTaskOut = task.addJob("RenderShadowTask", shadowTaskIn, cullFunctor, tagBits, tagMask); const auto renderDeferredInput = RenderDeferredTask::Input(items, lightingModel, lightingStageFramesAndZones, shadowTaskOut).asVarying(); - task.addJob("RenderDeferredTask", renderDeferredInput); + task.addJob("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) { - task.addBranch("RenderShadowsAndDeferredTask", 0, input, cullFunctor, tagBits, 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", 0, input, cullFunctor, tagBits, tagMask, depth); - task.addBranch("RenderForwardTask", 1, input); + task.addBranch("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("FetchCullSort", cullFunctor, tagBits, tagMask); // 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 const auto deferredForwardIn = DeferredForwardSwitchJob::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); - task.addJob("DeferredForwardSwitch", deferredForwardIn, cullFunctor, tagBits, tagMask); + task.addJob("DeferredForwardSwitch", deferredForwardIn, cullFunctor, tagBits, tagMask, depth); #else const auto renderInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); task.addJob("RenderForwardTask", renderInput); diff --git a/libraries/render-utils/src/RenderViewTask.h b/libraries/render-utils/src/RenderViewTask.h index cdb56a2189..139d00125e 100644 --- a/libraries/render-utils/src/RenderViewTask.h +++ b/libraries/render-utils/src/RenderViewTask.h @@ -24,7 +24,7 @@ public: 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() {} 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() {} - 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); }; diff --git a/libraries/render-utils/src/model.slf b/libraries/render-utils/src/model.slf index 98abc29d8c..2f80fbde99 100644 --- a/libraries/render-utils/src/model.slf +++ b/libraries/render-utils/src/model.slf @@ -36,7 +36,15 @@ <@include DeferredBufferWrite.slh@> <@endif@> <@else@> - layout(location=0) out vec4 _fragColor0; + <@if HIFI_USE_MIRROR@> + <@if HIFI_USE_FORWARD@> + layout(location=0) out vec4 _fragColor0; + <@else@> + <@include DeferredBufferWrite.slh@> + <@endif@> + <@else@> + layout(location=0) out vec4 _fragColor0; + <@endif@> <@endif@> <@if HIFI_USE_UNLIT@> @@ -45,6 +53,9 @@ <@if HIFI_USE_SHADOW or HIFI_USE_UNLIT@> <$declareMaterialTextures(ALBEDO)$> + <@if HIFI_USE_MIRROR@> + LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_MIRROR) uniform sampler2D mirrorMap; + <@endif@> <@else@> <@if not HIFI_USE_LIGHTMAP@> <@if HIFI_USE_NORMALMAP and HIFI_USE_TRANSLUCENT@> @@ -124,7 +135,14 @@ void main(void) { <@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); <@elif HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@> _fragColor0 = vec4(albedo * isUnlitEnabled(), opacity); diff --git a/libraries/render-utils/src/render-utils/model.slp b/libraries/render-utils/src/render-utils/model.slp index b63ec898eb..a3c28631e9 100644 --- a/libraries/render-utils/src/render-utils/model.slp +++ b/libraries/render-utils/src/render-utils/model.slp @@ -1 +1 @@ -DEFINES (normalmap translucent:f unlit:f/lightmap:f)/shadow fade:f/forward:f deformed:v/deformeddq:v \ No newline at end of file +DEFINES (normalmap translucent:f unlit:f/lightmap:f)/(shadow mirror:f) fade:f/forward:f deformed:v/deformeddq:v \ No newline at end of file diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index 369f227566..23e5570736 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -160,4 +160,11 @@ namespace render { } return payload->passesZoneOcclusionTest(containingZones); } + + template <> void payloadComputeMirrorView(const PayloadProxyInterface::Pointer& payload, ViewFrustum& viewFrustum) { + if (!payload) { + return; + } + payload->computeMirrorView(viewFrustum); + } } diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 5952be8a84..c2f03b4e05 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -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 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) NUM_FLAGS, // Not a valid flag @@ -162,6 +164,7 @@ public: Builder& withoutMetaCullGroup() { _flags.reset(META_CULL_GROUP); return (*this); } Builder& withSubMetaCulled() { _flags.set(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); } // Set ALL the tags in one call using the Tag bits @@ -205,6 +208,9 @@ public: bool isSubMetaCulled() const { return _flags[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]; } uint8_t getTagBits() const { return ((_flags.to_ulong() & KEY_TAG_BITS_MASK) >> FIRST_TAG_BIT); } @@ -274,7 +280,10 @@ public: Builder& withMetaCullGroup() { _value.set(ItemKey::META_CULL_GROUP); _mask.set(ItemKey::META_CULL_GROUP); 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& withTag(ItemKey::Tag tagIndex) { _value.set(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); } @@ -292,6 +301,7 @@ public: static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); } static Builder light() { return Builder().withTypeLight(); } static Builder meta() { return Builder().withTypeMeta(); } + static Builder mirror() { return Builder().withMirror(); } static Builder background() { return Builder().withViewSpace().withLayer(ItemKey::LAYER_BACKGROUND); } static Builder nothing() { return Builder().withNothing(); } }; @@ -440,6 +450,8 @@ public: virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const = 0; + virtual void computeMirrorView(ViewFrustum& viewFrustum) const = 0; + ~PayloadInterface() {} // Status interface is local to the base class @@ -493,6 +505,8 @@ public: bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const { return _payload->passesZoneOcclusionTest(containingZones); } + void computeMirrorView(ViewFrustum& viewFrustum) const { _payload->computeMirrorView(viewFrustum); } + // Access the status const StatusPointer& getStatus() const { return _payload->getStatus(); } @@ -547,6 +561,9 @@ template uint32_t metaFetchMetaSubItems(const std::shared_ptr& payl // Allows payloads to determine if they should render or not, based on the zones that contain the current camera template bool payloadPassesZoneOcclusionTest(const std::shared_ptr& payloadData, const std::unordered_set& containingZones) { return true; } +// Mirror Interface +template void payloadComputeMirrorView(const std::shared_ptr& payloadData, ViewFrustum& viewFrustum) { return; } + // 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 // 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& containingZones) const override { return payloadPassesZoneOcclusionTest(_data, containingZones); } + virtual void computeMirrorView(ViewFrustum& viewFrustum) const override { return payloadComputeMirrorView(_data, viewFrustum); } + protected: DataPointer _data; @@ -628,6 +647,7 @@ public: virtual void render(RenderArgs* args) = 0; virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const = 0; virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const = 0; + virtual void 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 virtual void handleBlendedVertices(int blendshapeNumber, const QVector& blendshapeOffsets, @@ -640,6 +660,7 @@ template <> void payloadRender(const PayloadProxyInterface::Pointer& payload, Re template <> uint32_t metaFetchMetaSubItems(const PayloadProxyInterface::Pointer& payload, ItemIDs& subItems); template <> const ShapeKey shapeGetShapeKey(const PayloadProxyInterface::Pointer& payload); template <> bool payloadPassesZoneOcclusionTest(const PayloadProxyInterface::Pointer& payload, const std::unordered_set& containingZones); +template <> void payloadComputeMirrorView(const PayloadProxyInterface::Pointer& payload, ViewFrustum& viewFrustum); typedef Item::PayloadPointer PayloadPointer; typedef std::vector Payloads; diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index b2656a597f..3bdaee25c6 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -35,18 +35,20 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin const auto nonspatialSelection = task.addJob("FetchLayeredSelection", nonspatialFilter); // 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 OPAQUE_SHAPE_BUCKET = 0; const int TRANSPARENT_SHAPE_BUCKET = 1; const int LIGHT_BUCKET = 2; const int META_BUCKET = 3; + const int MIRROR_BUCKET = 4; const int BACKGROUND_BUCKET = 2; MultiFilterItems::ItemFilterArray spatialFilters = { { - ItemFilter::Builder::opaqueShape(), + ItemFilter::Builder::opaqueShape().withoutMirror(), ItemFilter::Builder::transparentShape(), ItemFilter::Builder::light(), - ItemFilter::Builder::meta() + ItemFilter::Builder::meta().withoutMirror(), + ItemFilter::Builder::mirror() } }; MultiFilterItems::ItemFilterArray nonspatialFilters = { { ItemFilter::Builder::opaqueShape(), @@ -65,6 +67,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin const auto transparents = task.addJob("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); const auto lights = filteredSpatialBuckets[LIGHT_BUCKET]; const auto metas = filteredSpatialBuckets[META_BUCKET]; + const auto mirrors = task.addJob("DepthSortMirrors", filteredSpatialBuckets[MIRROR_BUCKET]); const auto background = filteredNonspatialBuckets[BACKGROUND_BUCKET]; @@ -76,7 +79,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin task.addJob("ClearContainingZones"); - output = Output(BucketList{ opaques, transparents, lights, metas, + output = Output(BucketList{ opaques, transparents, lights, metas, mirrors, filteredLayeredOpaque.getN(0), filteredLayeredTransparent.getN(0), filteredLayeredOpaque.getN(1), filteredLayeredTransparent.getN(1), background }, spatialSelection); diff --git a/libraries/render/src/render/RenderFetchCullSortTask.h b/libraries/render/src/render/RenderFetchCullSortTask.h index 0b475614a1..9823c2acdf 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -23,6 +23,7 @@ public: TRANSPARENT_SHAPE, LIGHT, META, + MIRROR, LAYER_FRONT_OPAQUE_SHAPE, LAYER_FRONT_TRANSPARENT_SHAPE, LAYER_HUD_OPAQUE_SHAPE, diff --git a/libraries/shared/src/MirrorMode.cpp b/libraries/shared/src/MirrorMode.cpp new file mode 100644 index 0000000000..53a51b1c1c --- /dev/null +++ b/libraries/shared/src/MirrorMode.cpp @@ -0,0 +1,25 @@ +// +// 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])); + +QString MirrorModeHelpers::getNameForMirrorMode(MirrorMode mode) { + if (((int)mode <= 0) || ((int)mode >= (int)MIRROR_MODE_NAMES)) { + mode = (MirrorMode)0; + } + + return MirrorModeNames[(int)mode]; +} diff --git a/libraries/shared/src/MirrorMode.h b/libraries/shared/src/MirrorMode.h new file mode 100644 index 0000000000..38af190070 --- /dev/null +++ b/libraries/shared/src/MirrorMode.h @@ -0,0 +1,40 @@ +// +// 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 "QString" + +/*@jsdoc + *

If an entity is rendered as a mirror, a portal, or normally.

+ * + * + * + * + * + * + * + * + * + *
ValueDescription
"none"The entity will render normally.
"mirror"The entity will render as a mirror.
"portal"The entity will render as a portal.
+ * @typedef {string} MirrorMode + */ + +enum class MirrorMode { + NONE = 0, + MIRROR, + PORTAL +}; + +class MirrorModeHelpers { +public: + static QString getNameForMirrorMode(MirrorMode mode); +}; + +#endif // hifi_MirrorMode_h diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index 9f6c3ef278..57cb3d1bc8 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -215,7 +215,7 @@ }, "imageAlpha": { "tooltip": "The opacity of the image between 0.0 fully transparent and 1.0 completely opaque." - }, + }, "emissive": { "tooltip": "If enabled, the image will display at full brightness." }, @@ -236,7 +236,7 @@ }, "showKeyboardFocusHighlight": { "tooltip": "If enabled, highlights when it has keyboard focus." - }, + }, "isEmitting": { "tooltip": "If enabled, then particles are emitted." }, @@ -503,6 +503,12 @@ "grab.triggerable": { "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": { "tooltip": "If enabled, this entity can be duplicated." }, @@ -606,7 +612,7 @@ }, "useBackground": { "tooltip": "If disabled, this web entity will support a transparent background for the webpage and its elements if the CSS property of 'background-color' on the 'body' is set with transparency." - }, + }, "maxFPS": { "tooltip": "The FPS at which to render the web entity. Higher values will have a performance impact." }, diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index e16b6ca653..ff28925b96 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -131,6 +131,21 @@ const GROUPS = [ label: "Render With Zones", type: "multipleZonesSelection", propertyID: "renderWithZones", + }, + { + label: "Mirror Mode", + type: "dropdown", + options: { + none: "None", + mirror: "Mirror", + portal: "Portal" + }, + propertyID: "mirrorMode", + }, + { + label: "Portal Exit", + type: "string", + propertyID: "portalExitID", } ] },