From 89e6cbbfa4b05e16589f6034f8be32aa41a0bd0a Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Thu, 3 Mar 2022 22:28:05 -0800 Subject: [PATCH 001/109] 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", } ] }, From 10de1288c2690baa5d1472695085bf3d305c0694 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 12 Nov 2023 23:04:18 -0800 Subject: [PATCH 002/109] fix view + projection, texture flipping, billboarding --- interface/src/avatar/MyAvatar.cpp | 3 +- .../src/RenderableEntityItem.cpp | 74 ++++++++++--------- .../src/RenderableGizmoEntityItem.cpp | 3 +- .../src/RenderableGridEntityItem.cpp | 3 +- .../src/RenderableImageEntityItem.cpp | 3 +- .../src/RenderableLineEntityItem.cpp | 3 +- .../src/RenderableMaterialEntityItem.cpp | 3 +- .../src/RenderablePolyLineEntityItem.cpp | 3 +- .../src/RenderablePolyVoxEntityItem.cpp | 3 +- .../src/RenderableShapeEntityItem.cpp | 3 +- .../src/RenderableTextEntityItem.cpp | 6 +- .../src/RenderableWebEntityItem.cpp | 3 +- .../src/CauterizedMeshPartPayload.cpp | 7 +- .../src/CauterizedMeshPartPayload.h | 2 +- .../render-utils/src/MeshPartPayload.cpp | 12 +-- libraries/render-utils/src/MeshPartPayload.h | 2 +- .../render-utils/src/RenderCommonTask.cpp | 21 ++++-- .../render-utils/src/RenderDeferredTask.cpp | 2 +- .../render-utils/src/RenderForwardTask.cpp | 2 +- .../render-utils/src/RenderHUDLayerTask.cpp | 4 +- .../src/ToneMapAndResampleTask.cpp | 6 +- .../render-utils/src/ToneMapAndResampleTask.h | 7 +- libraries/render/src/render/Args.h | 3 + libraries/render/src/render/CullTask.cpp | 18 ++--- libraries/render/src/render/ResampleTask.cpp | 3 +- libraries/render/src/render/ResampleTask.h | 4 +- 26 files changed, 118 insertions(+), 85 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 58c86e5e8a..32116343bf 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3530,8 +3530,9 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON_LOOK_AT || qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON; bool overrideAnim = _skeletonModel ? _skeletonModel->getRig().isPlayingOverrideAnimation() : false; + bool isInMirror = renderArgs->_mirrorDepth > 0; bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition()); - return !defaultMode || (!firstPerson && !insideHead) || (overrideAnim && !insideHead); + return !defaultMode || isInMirror || (!firstPerson && !insideHead) || (overrideAnim && !insideHead); } void MyAvatar::setRotationRecenterFilterLength(float length) { diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 108016a939..1fc9ed791f 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -227,39 +227,13 @@ bool EntityRenderer::passesZoneOcclusionTest(const std::unordered_set& co } 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; @@ -275,19 +249,47 @@ void EntityRenderer::computeMirrorView(ViewFrustum& viewFrustum) const { // 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; + glm::quat mainCameraRotationMirror = mirrorFromWorld * 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)); + glm::quat mirrorCameraRotationWorld = worldFromMirror * glm::mat4_cast(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); + // 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(mirrorPropertiesPosition, 1.0f); + glm::vec3 cameraSpaceNormal = glm::transpose(view) * (worldFromMirrorRotation * glm::vec4(0, 0, -1, 0)); + glm::vec4 clipPlane = glm::vec4(cameraSpaceNormal, -glm::dot(cameraSpaceNormal, cameraSpacePosition)); + 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); } void EntityRenderer::render(RenderArgs* args) { @@ -295,7 +297,7 @@ void EntityRenderer::render(RenderArgs* args) { 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); } } diff --git a/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp b/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp index 42df1e2888..10ae144334 100644 --- a/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp @@ -263,8 +263,9 @@ void GizmoEntityRenderer::doRender(RenderArgs* args) { 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, - args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition(), true)); + usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition(), true)); batch.setModelTransform(transform); Pipeline pipelineType = getPipelineType(materials); diff --git a/libraries/entities-renderer/src/RenderableGridEntityItem.cpp b/libraries/entities-renderer/src/RenderableGridEntityItem.cpp index e374fe29c0..3f40218d46 100644 --- a/libraries/entities-renderer/src/RenderableGridEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableGridEntityItem.cpp @@ -103,8 +103,9 @@ void GridEntityRenderer::doRender(RenderArgs* args) { } else { 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, - args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); batch->setModelTransform(transform); auto minCorner = glm::vec2(-0.5f, -0.5f); diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index 9592a3e57f..4c18653d4f 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -147,8 +147,9 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { 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, - args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); float imageWidth = _texture->getWidth(); float imageHeight = _texture->getHeight(); diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index a36cdde212..1117c97c75 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -47,8 +47,9 @@ void LineEntityRenderer::doRender(RenderArgs* args) { const auto& modelTransform = getModelTransform(); Transform transform = Transform(); 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, - args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); batch.setModelTransform(transform); if (_linePoints.size() > 1) { DependencyManager::get()->bindSimpleProgram(batch, false, false, false, false, true, diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index b8f829f4ba..fe44c41094 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -303,8 +303,9 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) { proceduralRender = true; } + bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; 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); if (!proceduralRender) { diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index aca501985a..81f4c5fcb4 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -325,8 +325,9 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) { buildPipelines(); } + bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; 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.setPipeline(_pipelines[{args->_renderMethod, isTransparent()}]); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 8331e016fd..26091a1ed4 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1860,8 +1860,9 @@ void PolyVoxEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); 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, - 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); batch.setModelTransform(transform); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index b8954f2ced..82350f54bf 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -118,8 +118,9 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { 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, - 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)); batch.setModelTransform(transform); diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 2858e12afd..a3ccf78593 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -164,8 +164,9 @@ void TextEntityRenderer::doRender(RenderArgs* args) { transform = _renderTransform; }); + bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; 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); Pipeline pipelineType = getPipelineType(materials); @@ -352,8 +353,9 @@ void entities::TextPayload::render(RenderArgs* args) { return; } + bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; 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(); transform.postTranslate(glm::vec3(-0.5, 0.5, 1.0f + EPSILON / dimensions.z)); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index c98bfe7f63..77f6fe99f6 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -320,8 +320,9 @@ void WebEntityRenderer::doRender(RenderArgs* args) { 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, - args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); batch.setModelTransform(transform); // Turn off jitter for these entities diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index dc1ffb7b67..78652bfb09 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -71,14 +71,15 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(const Transform _cauterizedTransform = renderTransform; } -void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode) const { - bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) && _enableCauterization; +void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode, size_t mirrorDepth) const { + bool useCauterizedMesh = _enableCauterization && (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) && + mirrorDepth == 0; if (useCauterizedMesh) { if (_cauterizedClusterBuffer) { batch.setUniformBuffer(graphics::slot::buffer::Skinning, _cauterizedClusterBuffer); } batch.setModelTransform(_cauterizedTransform); } else { - ModelMeshPartPayload::bindTransform(batch, transform, renderMode); + ModelMeshPartPayload::bindTransform(batch, transform, renderMode, mirrorDepth); } } diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index 430f41fc08..cef7b6d9b5 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -25,7 +25,7 @@ public: 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; } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 8d9d31390d..ad749b6d10 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -189,7 +189,7 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { 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) { batch.setUniformBuffer(graphics::slot::buffer::Skinning, _clusterBuffer); } @@ -300,8 +300,9 @@ Item::Bound ModelMeshPartPayload::getBound(RenderArgs* args) const { auto worldBound = _adjustedLocalBound; auto parentTransform = _parentTransform; if (args) { + bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; 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); return worldBound; @@ -314,18 +315,19 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { void ModelMeshPartPayload::render(RenderArgs* args) { 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; } gpu::Batch& batch = *(args->_batch); Transform transform = _parentTransform; + bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; 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); - bindTransform(batch, modelTransform, args->_renderMode); + bindTransform(batch, modelTransform, args->_renderMode, args->_mirrorDepth); //Bind the index buffer and vertex buffer and Blend shapes if needed bindMesh(batch); diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 3084d8ae01..6e0bb91a30 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -37,7 +37,7 @@ public: // ModelMeshPartPayload functions to perform render 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 updateKey(const render::ItemKey& key); diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index f41ed6b21f..a3355ea805 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -277,7 +277,7 @@ public: using Outputs = render::VaryingSet4; using JobModel = render::Job::ModelIO; - SetupMirrorTask(size_t mirrorIndex) : _mirrorIndex(mirrorIndex) {} + 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; @@ -293,12 +293,17 @@ public: _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; - args->_renderMode = RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE; - args->_blitFramebuffer = _mirrorFramebuffer; + _cachedArgsPointer->_ignoreItem = args->_ignoreItem; + _cachedArgsPointer->_mirrorDepth = args->_mirrorDepth; + + args->_blitFramebuffer = _mirrorFramebuffer; + args->_ignoreItem = mirror.id; + args->_mirrorDepth = _depth; - render::ItemBound mirror = items[_mirrorIndex]; ViewFrustum srcViewFrustum = args->getViewFrustum(); args->_scene->getItem(mirror.id).computeMirrorView(srcViewFrustum); @@ -317,6 +322,7 @@ protected: gpu::FramebufferPointer _mirrorFramebuffer { nullptr }; RenderArgsPointer _cachedArgsPointer { std::make_shared() }; size_t _mirrorIndex; + size_t _depth; }; @@ -377,6 +383,8 @@ public: // Restore the blit framebuffer after we've sampled from it if (cachedArgs) { args->_blitFramebuffer = cachedArgs->_blitFramebuffer; + args->_ignoreItem = cachedArgs->_ignoreItem; + args->_mirrorDepth = cachedArgs->_mirrorDepth; } } @@ -389,9 +397,10 @@ ShapePlumberPointer DrawMirrorTask::_forwardPipelines = 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); + size_t nextDepth = depth + 1; + const auto setupOutput = task.addJob("SetupMirror" + std::to_string(mirrorIndex) + "Depth" + std::to_string(depth), inputs, mirrorIndex, nextDepth); - 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("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" + std::to_string(mirrorIndex) + "Depth" + std::to_string(depth), setupOutput); } diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 80157f9a42..92208450ef 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -244,7 +244,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren // Lighting Buffer ready for tone mapping const auto toneMappingInputs = ToneMapAndResample::Input(lightingFramebuffer, destFramebuffer).asVarying(); - const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs); + const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs, depth); // 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 diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 12af18e44b..722ba2248c 100644 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -173,7 +173,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto destFramebuffer = static_cast(nullptr); const auto toneMappingInputs = ToneMapAndResample::Input(resolvedFramebuffer, destFramebuffer).asVarying(); - const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs); + const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs, depth); // HUD Layer const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(toneMappedBuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying(); task.addJob("RenderHUDLayer", renderHUDLayerInputs); diff --git a/libraries/render-utils/src/RenderHUDLayerTask.cpp b/libraries/render-utils/src/RenderHUDLayerTask.cpp index 743e59eebc..8fee3d57bc 100644 --- a/libraries/render-utils/src/RenderHUDLayerTask.cpp +++ b/libraries/render-utils/src/RenderHUDLayerTask.cpp @@ -16,8 +16,8 @@ void CompositeHUD::run(const RenderContextPointer& renderContext, const gpu::Fra assert(renderContext->args); assert(renderContext->args->_context); - // We do not want to render HUD elements in secondary camera - if (nsightActive() || renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) { + // We do not want to render HUD elements in secondary camera or mirrors + if (nsightActive() || renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE || renderContext->args->_mirrorDepth > 0) { return; } diff --git a/libraries/render-utils/src/ToneMapAndResampleTask.cpp b/libraries/render-utils/src/ToneMapAndResampleTask.cpp index 10312f7f2e..6ac5142e45 100644 --- a/libraries/render-utils/src/ToneMapAndResampleTask.cpp +++ b/libraries/render-utils/src/ToneMapAndResampleTask.cpp @@ -25,9 +25,10 @@ using namespace shader::render_utils::program; gpu::PipelinePointer ToneMapAndResample::_pipeline; gpu::PipelinePointer ToneMapAndResample::_mirrorPipeline; -ToneMapAndResample::ToneMapAndResample() { +ToneMapAndResample::ToneMapAndResample(size_t depth) { Parameters parameters; _parametersBuffer = gpu::BufferView(std::make_shared(sizeof(Parameters), (const gpu::Byte*) ¶meters)); + _depth = depth; } void ToneMapAndResample::init() { @@ -95,7 +96,8 @@ void ToneMapAndResample::run(const RenderContextPointer& renderContext, const In batch.setViewportTransform(destViewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); - batch.setPipeline(args->_renderMode == RenderArgs::MIRROR_RENDER_MODE ? _mirrorPipeline : _pipeline); + bool shouldMirror = _depth % 2 == (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); + batch.setPipeline(shouldMirror ? _mirrorPipeline : _pipeline); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(srcBufferSize, args->_viewport)); batch.setUniformBuffer(render_utils::slot::buffer::ToneMappingParams, _parametersBuffer); diff --git a/libraries/render-utils/src/ToneMapAndResampleTask.h b/libraries/render-utils/src/ToneMapAndResampleTask.h index 1c7ef2cf48..3a812cf445 100644 --- a/libraries/render-utils/src/ToneMapAndResampleTask.h +++ b/libraries/render-utils/src/ToneMapAndResampleTask.h @@ -49,11 +49,9 @@ signals: class ToneMapAndResample { public: - ToneMapAndResample(); + ToneMapAndResample(size_t depth); virtual ~ToneMapAndResample() {} - void render(RenderArgs* args, const gpu::TexturePointer& lightingBuffer, gpu::FramebufferPointer& destinationBuffer); - void setExposure(float exposure); float getExposure() const { return _parametersBuffer.get()._exposure; } @@ -75,7 +73,8 @@ protected: gpu::FramebufferPointer _destinationFrameBuffer; - float _factor{ 2.0f }; + float _factor { 2.0f }; + size_t _depth { 0 }; gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer); diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index d09b0d2f2f..60daa35729 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -159,6 +159,9 @@ namespace render { bool _takingSnapshot { false }; StencilMaskMode _stencilMaskMode { StencilMaskMode::NONE }; std::function _stencilMaskOperator; + + ItemID _ignoreItem { 0 }; + size_t _mirrorDepth { 0 }; }; } diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 039cf01c86..aeb7572746 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -82,7 +82,7 @@ void FetchNonspatialItems::run(const RenderContextPointer& renderContext, const outItems.reserve(items.size()); for (auto& id : items) { 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))); } } @@ -190,7 +190,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideFitItems"); for (auto id : inSelection.insideItems) { 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)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -205,7 +205,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideSmallItems"); for (auto id : inSelection.insideSubcellItems) { 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)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -220,7 +220,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialFitItems"); for (auto id : inSelection.partialItems) { 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)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -235,7 +235,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialSmallItems"); for (auto id : inSelection.partialSubcellItems) { 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)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -252,7 +252,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideFitItems"); for (auto id : inSelection.insideItems) { 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)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -267,7 +267,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideSmallItems"); for (auto id : inSelection.insideSubcellItems) { 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)); if (test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); @@ -284,7 +284,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialFitItems"); for (auto id : inSelection.partialItems) { 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)); if (test.frustumTest(itemBound.bound)) { outItems.emplace_back(itemBound); @@ -301,7 +301,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialSmallItems"); for (auto id : inSelection.partialSubcellItems) { 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)); if (test.frustumTest(itemBound.bound) && test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); diff --git a/libraries/render/src/render/ResampleTask.cpp b/libraries/render/src/render/ResampleTask.cpp index b868c53542..5206767bd2 100644 --- a/libraries/render/src/render/ResampleTask.cpp +++ b/libraries/render/src/render/ResampleTask.cpp @@ -167,7 +167,8 @@ void UpsampleToBlitFramebuffer::run(const RenderContextPointer& renderContext, c batch.setViewportTransform(viewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); - batch.setPipeline(args->_renderMode == RenderArgs::MIRROR_RENDER_MODE ? _mirrorPipeline : _pipeline); + bool shouldMirror = _depth % 2 == (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); + batch.setPipeline(shouldMirror ? _mirrorPipeline : _pipeline); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport)); batch.setResourceTexture(0, sourceFramebuffer->getRenderBuffer(0)); diff --git a/libraries/render/src/render/ResampleTask.h b/libraries/render/src/render/ResampleTask.h index 92f720c843..bf1e535949 100644 --- a/libraries/render/src/render/ResampleTask.h +++ b/libraries/render/src/render/ResampleTask.h @@ -73,7 +73,7 @@ namespace render { using Input = gpu::FramebufferPointer; using JobModel = Job::ModelIO; - UpsampleToBlitFramebuffer() {} + UpsampleToBlitFramebuffer(size_t depth) : _depth(depth) {} void run(const RenderContextPointer& renderContext, const Input& input, gpu::FramebufferPointer& resampledFrameBuffer); @@ -81,6 +81,8 @@ namespace render { static gpu::PipelinePointer _pipeline; static gpu::PipelinePointer _mirrorPipeline; + + size_t _depth; }; } From 97c4184db1620a06dbb46b6dcc0468ca5612885b Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Mon, 13 Nov 2023 00:13:35 -0800 Subject: [PATCH 003/109] wip portals --- .../src/RenderableEntityItem.cpp | 54 ++++++++++++------- .../src/RenderableModelEntityItem.cpp | 2 +- .../render-utils/src/MeshPartPayload.cpp | 2 +- libraries/render-utils/src/Model.cpp | 7 +-- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 1fc9ed791f..b80b39c08c 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -193,7 +193,7 @@ ItemKey EntityRenderer::getKey() { builder.withSubMetaCulled(); } - if (_mirrorMode != MirrorMode::NONE) { + if (_mirrorMode == MirrorMode::MIRROR || (_mirrorMode == MirrorMode::PORTAL && !_portalExitID.isNull())) { builder.withMirror(); } @@ -227,32 +227,50 @@ bool EntityRenderer::passesZoneOcclusionTest(const std::unordered_set& co } void EntityRenderer::computeMirrorView(ViewFrustum& viewFrustum) const { - glm::vec3 mirrorPropertiesPosition; - glm::quat mirrorPropertiesRotation; + glm::vec3 inPropertiesPosition; + glm::quat inPropertiesRotation; + MirrorMode mirrorMode; + QUuid portalExitID; withReadLock([&]{ - mirrorPropertiesPosition = _entity->getWorldPosition(); - mirrorPropertiesRotation = _entity->getWorldOrientation(); + inPropertiesPosition = _entity->getWorldPosition(); + inPropertiesRotation = _entity->getWorldOrientation(); + mirrorMode = _mirrorMode; + portalExitID = _portalExitID; }); - glm::mat4 worldFromMirrorRotation = glm::mat4_cast(mirrorPropertiesRotation); - glm::mat4 worldFromMirrorTranslation = glm::translate(mirrorPropertiesPosition); - glm::mat4 worldFromMirror = worldFromMirrorTranslation * worldFromMirrorRotation; - glm::mat4 mirrorFromWorld = glm::inverse(worldFromMirror); + glm::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; + if (mirrorMode == MirrorMode::PORTAL && !portalExitID.isNull()) { + auto renderer = DependencyManager::get(); + 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); + } + } + } // 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)); + glm::vec3 cameraPositionWorld = viewFrustum.getPosition(); + glm::vec3 cameraPositionIn = vec3(worldToIn * vec4(cameraPositionWorld, 1.0f)); + glm::vec3 mirrorCameraPositionIn = vec3(cameraPositionIn.x, cameraPositionIn.y, -cameraPositionIn.z); + 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 = mirrorFromWorld * glm::mat4_cast(mainCameraRotationWorld); + 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)); - glm::quat mirrorCameraRotationWorld = worldFromMirror * glm::mat4_cast(mirrorCameraRotationMirror); + glm::quat mirrorCameraRotationWorld = outToWorld * glm::mat4_cast(mirrorCameraRotationMirror); viewFrustum.setPosition(mirrorCameraPositionWorld); viewFrustum.setOrientation(mirrorCameraRotationWorld); @@ -263,8 +281,8 @@ void EntityRenderer::computeMirrorView(ViewFrustum& viewFrustum) const { glm::mat4 projection = viewFrustum.getProjection(); //Find the camera-space 4D reflection plane vector - glm::vec3 cameraSpacePosition = glm::inverse(view) * glm::vec4(mirrorPropertiesPosition, 1.0f); - glm::vec3 cameraSpaceNormal = glm::transpose(view) * (worldFromMirrorRotation * glm::vec4(0, 0, -1, 0)); + 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)); if (clipPlane.w > 0.0f) { clipPlane *= -1.0f; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index aa84280c74..ae3f261d48 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1085,7 +1085,7 @@ void ModelEntityRenderer::setKey(bool didVisualGeometryRequestSucceed, const Mod builder.withSubMetaCulled(); } - if (_mirrorMode != MirrorMode::NONE) { + if (_mirrorMode == MirrorMode::MIRROR || (_mirrorMode == MirrorMode::PORTAL && !_portalExitID.isNull())) { builder.withMirror(); } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index ad749b6d10..de88381468 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -217,7 +217,7 @@ void ModelMeshPartPayload::updateKey(const render::ItemKey& key) { builder.withTransparent(); } - if (_cullWithParent || _mirrorMode != MirrorMode::NONE) { + if (_cullWithParent) { builder.withSubMetaCulled(); } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 9be811a7a9..066e8cb1d8 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1078,11 +1078,9 @@ void Model::setMirrorMode(MirrorMode mirrorMode, const render::ScenePointer& sce } render::Transaction transaction; - auto renderItemsKey = _renderItemKeyGlobalFlags; for (auto item : _modelMeshRenderItemIDs) { - transaction.updateItem(item, [mirrorMode, renderItemsKey](ModelMeshPartPayload& data) { + transaction.updateItem(item, [mirrorMode](ModelMeshPartPayload& data) { data.setMirrorMode(mirrorMode); - data.updateKey(renderItemsKey); }); } scene->enqueueTransaction(transaction); @@ -1098,9 +1096,8 @@ void Model::setPortalExitID(const QUuid& portalExitID, const render::ScenePointe } render::Transaction transaction; - auto renderItemsKey = _renderItemKeyGlobalFlags; for (auto item : _modelMeshRenderItemIDs) { - transaction.updateItem(item, [portalExitID, renderItemsKey](ModelMeshPartPayload& data) { + transaction.updateItem(item, [portalExitID](ModelMeshPartPayload& data) { data.setPortalExitID(portalExitID); }); } From 4806f901b4a5be00bba601d11c57774aa78b1fe7 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Thu, 16 Nov 2023 22:24:13 -0800 Subject: [PATCH 004/109] wip --- .../src/RenderableEntityItem.cpp | 3 +- libraries/render/src/render/CullTask.cpp | 67 ------------------- libraries/render/src/render/CullTask.h | 23 ------- libraries/shared/src/ViewFrustum.cpp | 5 ++ libraries/shared/src/ViewFrustum.h | 4 +- 5 files changed, 10 insertions(+), 92 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index b80b39c08c..d0891d16b1 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -308,6 +308,7 @@ void EntityRenderer::computeMirrorView(ViewFrustum& viewFrustum) const { projection[3][2] = c.w; viewFrustum.setProjection(projection); + viewFrustum.setIsOblique(true); } void EntityRenderer::render(RenderArgs* args) { @@ -596,7 +597,7 @@ graphics::MaterialPointer EntityRenderer::getTopMaterial() { } EntityRenderer::Pipeline EntityRenderer::getPipelineType(const graphics::MultiMaterial& materials) { - if (_mirrorMode != MirrorMode::NONE) { + if (_mirrorMode == MirrorMode::MIRROR || (_mirrorMode == MirrorMode::PORTAL && !_portalExitID.isNull())) { return Pipeline::MIRROR; } diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index aeb7572746..cd69114bbf 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -325,73 +325,6 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, std::static_pointer_cast(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) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 9a7466223d..9e214fd988 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -121,29 +121,6 @@ namespace render { void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems); }; - class CullShapeBounds { - public: - using Inputs = render::VaryingSet4; - using Outputs = render::VaryingSet2; - using JobModel = Job::ModelIO; - - 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 { public: using Inputs = render::VaryingSet2; diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index e925ef960d..3ed42df59b 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -208,6 +208,11 @@ bool ViewFrustum::sphereIntersectsFrustum(const glm::vec3& center, float radius) } bool ViewFrustum::boxIntersectsFrustum(const AABox& box) const { + // FIXME: remove once we fix culling + if (_isOblique) { + return true; + } + // only check against frustum for(int i = 0; i < NUM_FRUSTUM_PLANES; i++) { const glm::vec3& normal = _planes[i].getNormal(); diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index 9c80538e60..a81c646673 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -51,6 +51,7 @@ public: void setProjection(float cameraFov, float cameraAspectRatio, float cameraNearClip, float cameraFarClip); void setFocalLength(float focalLength) { _focalLength = focalLength; } bool isPerspective() const; + void setIsOblique(bool isOblique) { _isOblique = isOblique; } // getters for lens attributes const glm::mat4& getProjection() const { return _projection; } @@ -103,7 +104,6 @@ public: bool pointIntersectsFrustum(const glm::vec3& point) const; bool sphereIntersectsFrustum(const glm::vec3& center, float radius) const; - bool cubeIntersectsFrustum(const AACube& box) const; bool boxIntersectsFrustum(const AABox& box) const; bool boxInsideFrustum(const AABox& box) const; @@ -175,6 +175,8 @@ private: float _nearClip { DEFAULT_NEAR_CLIP }; float _farClip { DEFAULT_FAR_CLIP }; + bool _isOblique { false }; + const char* debugPlaneName (int plane) const; // Used to project points From 56860bea993af6251e84f23842aa5da6c120f519 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 18 Nov 2023 00:25:09 -0800 Subject: [PATCH 005/109] fix cpu frustum culling (hacky?) --- .../src/RenderableEntityItem.cpp | 3 +- libraries/shared/src/ViewFrustum.cpp | 56 +++++++++++-------- libraries/shared/src/ViewFrustum.h | 3 +- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index d0891d16b1..1fef0debdb 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -307,8 +307,7 @@ void EntityRenderer::computeMirrorView(ViewFrustum& viewFrustum) const { projection[2][2] = c.z + 1.0f; projection[3][2] = c.w; - viewFrustum.setProjection(projection); - viewFrustum.setIsOblique(true); + viewFrustum.setProjection(projection, true); } void EntityRenderer::render(RenderArgs* args) { diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index 3ed42df59b..daa08b5dbc 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -53,7 +53,7 @@ static const glm::vec4 NDC_VALUES[NUM_FRUSTUM_CORNERS] = { 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; glm::mat4 inverseProjection = glm::inverse(projection); @@ -63,16 +63,21 @@ void ViewFrustum::setProjection(const glm::mat4& projection) { _corners[i] /= _corners[i].w; } - // compute frustum properties - _nearClip = -_corners[BOTTOM_LEFT_NEAR].z; - _farClip = -_corners[BOTTOM_LEFT_FAR].z; - _aspectRatio = (_corners[TOP_RIGHT_NEAR].x - _corners[BOTTOM_LEFT_NEAR].x) / - (_corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_LEFT_NEAR].y); - glm::vec4 top = inverseProjection * vec4(0.0f, 1.0f, -1.0f, 1.0f); - top /= top.w; - _fieldOfView = abs(glm::degrees(2.0f * abs(glm::angle(vec3(0.0f, 0.0f, -1.0f), glm::normalize(vec3(top)))))); - _height = _corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_RIGHT_NEAR].y; - _width = _corners[TOP_RIGHT_NEAR].x - _corners[TOP_LEFT_NEAR].x; + // 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 + _nearClip = -_corners[BOTTOM_LEFT_NEAR].z; + _farClip = -_corners[BOTTOM_LEFT_FAR].z; + _aspectRatio = (_corners[TOP_RIGHT_NEAR].x - _corners[BOTTOM_LEFT_NEAR].x) / + (_corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_LEFT_NEAR].y); + glm::vec4 top = inverseProjection * vec4(0.0f, 1.0f, -1.0f, 1.0f); + top /= top.w; + _fieldOfView = abs(glm::degrees(2.0f * abs(glm::angle(vec3(0.0f, 0.0f, -1.0f), glm::normalize(vec3(top)))))); + _height = _corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_RIGHT_NEAR].y; + _width = _corners[TOP_RIGHT_NEAR].x - _corners[TOP_LEFT_NEAR].x; + } + _isOblique = isOblique; } void ViewFrustum::setProjection(float cameraFov, float cameraAspectRatio, float cameraNearClip, float cameraFarClip) { @@ -109,12 +114,24 @@ void ViewFrustum::calculate() { // 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 // three consecutive points - _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[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[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]); + if (!_isOblique) { + _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[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[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]); + } 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... // Projection matrix : Field of View, ratio, display range : near to far @@ -208,11 +225,6 @@ bool ViewFrustum::sphereIntersectsFrustum(const glm::vec3& center, float radius) } bool ViewFrustum::boxIntersectsFrustum(const AABox& box) const { - // FIXME: remove once we fix culling - if (_isOblique) { - return true; - } - // only check against frustum for(int i = 0; i < NUM_FRUSTUM_PLANES; i++) { const glm::vec3& normal = _planes[i].getNormal(); diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index a81c646673..fa66a0a87e 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -47,11 +47,10 @@ public: const glm::vec3& getRight() const { return _right; } // 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 setFocalLength(float focalLength) { _focalLength = focalLength; } bool isPerspective() const; - void setIsOblique(bool isOblique) { _isOblique = isOblique; } // getters for lens attributes const glm::mat4& getProjection() const { return _projection; } From 093cf2977802394f6f4ffb7ad1ba6bc40ac5e55a Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 18 Nov 2023 18:41:08 -0800 Subject: [PATCH 006/109] fix mirrors in deferred --- libraries/render-utils/src/DeferredLightingEffect.cpp | 1 + libraries/render-utils/src/DeferredLightingEffect.h | 4 ++-- libraries/render-utils/src/RenderDeferredTask.cpp | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 8d7fc345ac..3eb5924d19 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -286,6 +286,7 @@ void PrepareDeferred::run(const RenderContextPointer& renderContext, const Input outputs.edit0() = _deferredFramebuffer; outputs.edit1() = _deferredFramebuffer->getLightingFramebuffer(); + outputs.edit2() = _deferredFramebuffer->getDeferredFramebuffer(); gpu::doInBatch("PrepareDeferred::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 4779376410..058e0a4cbf 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -78,8 +78,8 @@ class PrepareDeferred { public: // Inputs: primaryFramebuffer and lightingModel using Inputs = render::VaryingSet2 ; - // Output: DeferredFramebuffer, LightingFramebuffer - using Outputs = render::VaryingSet2; + // Output: DeferredFramebuffer, LightingFramebuffer, the framebuffer to be used for mirrors (same as DeferredFramebuffer) + using Outputs = render::VaryingSet3; using JobModel = render::Job::ModelIO; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 92208450ef..fd10452dfa 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -157,6 +157,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto prepareDeferredOutputs = task.addJob("PrepareDeferred", prepareDeferredInputs); const auto deferredFramebuffer = prepareDeferredOutputs.getN(0); const auto lightingFramebuffer = prepareDeferredOutputs.getN(1); + const auto mirrorTargetFramebuffer = prepareDeferredOutputs.getN(2); // draw a stencil mask in hidden regions of the framebuffer. task.addJob("PrepareStencil", scaledPrimaryFramebuffer); @@ -166,7 +167,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawOpaqueDeferred", opaqueInputs, shapePlumber); if (depth < RenderMirrorTask::MAX_MIRROR_DEPTH) { - const auto mirrorInputs = RenderMirrorTask::Inputs(mirrors, scaledPrimaryFramebuffer, jitter).asVarying(); + const auto mirrorInputs = RenderMirrorTask::Inputs(mirrors, mirrorTargetFramebuffer, 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); } From a630a6f9c905de7144c577bccf11e4067e40f8c3 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 18 Nov 2023 23:04:17 -0800 Subject: [PATCH 007/109] mirrors on models + text --- interface/src/Application.cpp | 1 + .../src/avatars-renderer/Avatar.cpp | 2 +- .../src/RenderableEntityItem.cpp | 5 ++ .../src/RenderableEntityItem.h | 2 + .../src/RenderableModelEntityItem.cpp | 4 -- .../src/RenderableTextEntityItem.cpp | 30 ++++++++++-- .../src/RenderableTextEntityItem.h | 2 + .../render-utils/src/MeshPartPayload.cpp | 10 +++- libraries/render-utils/src/Model.cpp | 8 +++- libraries/render-utils/src/TextRenderer3D.cpp | 11 ++--- libraries/render-utils/src/TextRenderer3D.h | 9 ++-- .../src/render-utils/sdf_text3D.slp | 2 +- libraries/render-utils/src/sdf_text3D.slf | 14 +++++- libraries/render-utils/src/text/Font.cpp | 46 ++++++++++--------- libraries/render-utils/src/text/Font.h | 28 +++++++++-- libraries/shared/src/MirrorMode.cpp | 11 +++++ libraries/shared/src/MirrorMode.h | 11 +++++ 17 files changed, 142 insertions(+), 54 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 37204b0f9b..3e8e811c32 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2479,6 +2479,7 @@ Application::Application( copyViewFrustum(viewFrustum); return viewFrustum.getPosition(); }); + MirrorModeHelpers::setComputeMirrorViewOperator(EntityRenderer::computeMirrorViewOperator); DependencyManager::get()->setKickConfirmationOperator([this] (const QUuid& nodeID, unsigned int banFlags) { userKickConfirmation(nodeID, banFlags); }); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index b2d6a6260b..790b45843c 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1120,7 +1120,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const batch.setModelTransform(textTransform); { 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 }); } } } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 1fef0debdb..aec9f092b9 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -237,7 +237,11 @@ void EntityRenderer::computeMirrorView(ViewFrustum& viewFrustum) const { mirrorMode = _mirrorMode; portalExitID = _portalExitID; }); + computeMirrorViewOperator(viewFrustum, inPropertiesPosition, inPropertiesRotation, mirrorMode, portalExitID); +} +void 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); @@ -284,6 +288,7 @@ void EntityRenderer::computeMirrorView(ViewFrustum& viewFrustum) const { 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; } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index bfec4743f3..fceba6f0e6 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -76,6 +76,8 @@ public: virtual Item::Bound getBound(RenderArgs* args) override; bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const override; void computeMirrorView(ViewFrustum& viewFrustum) const override; + static void computeMirrorViewOperator(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation, + MirrorMode mirrorMode, const QUuid& portalExitID); protected: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index ae3f261d48..6b83d87732 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1085,10 +1085,6 @@ void ModelEntityRenderer::setKey(bool didVisualGeometryRequestSucceed, const Mod builder.withSubMetaCulled(); } - if (_mirrorMode == MirrorMode::MIRROR || (_mirrorMode == MirrorMode::PORTAL && !_portalExitID.isNull())) { - builder.withMirror(); - } - if (didVisualGeometryRequestSucceed) { _itemKey = builder.build(); } else { diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index a3ccf78593..6fd99a69fd 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -181,7 +181,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { } auto geometryCache = DependencyManager::get(); - if (pipelineType == Pipeline::SIMPLE) { + if (pipelineType == Pipeline::SIMPLE || pipelineType == Pipeline::MIRROR) { geometryCache->renderQuad(batch, glm::vec2(-0.5f), glm::vec2(0.5f), backgroundColor, _geometryID); } else { geometryCache->renderQuad(batch, glm::vec2(-0.5f), glm::vec2(0.5f), glm::vec2(0.0f), glm::vec2(1.0f), backgroundColor, _geometryID); @@ -261,6 +261,10 @@ ItemKey entities::TextPayload::getKey() const { builder.withInvisible(); } + if (textRenderable->_mirrorMode == MirrorMode::MIRROR || (textRenderable->_mirrorMode == MirrorMode::PORTAL && !textRenderable->_portalExitID.isNull())) { + builder.withMirror(); + } + return builder; } } @@ -312,6 +316,16 @@ bool entities::TextPayload::passesZoneOcclusionTest(const std::unordered_set(); + if (entityTreeRenderer) { + auto renderable = entityTreeRenderer->renderableForEntityId(_entityID); + if (renderable) { + return renderable->computeMirrorView(viewFrustum); + } + } +} + void entities::TextPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("TextPayload::render"); Q_ASSERT(args->_batch); @@ -336,12 +350,15 @@ void entities::TextPayload::render(RenderArgs* args) { glm::vec3 dimensions; glm::vec4 textColor; + bool mirror; textRenderable->withReadLock([&] { transform = textRenderable->_renderTransform; dimensions = textRenderable->_dimensions; float fadeRatio = textRenderable->_isFading ? Interpolate::calculateFadeRatio(textRenderable->_fadeStartTime) : 1.0f; 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; @@ -363,9 +380,8 @@ void entities::TextPayload::render(RenderArgs* args) { batch.setModelTransform(transform); 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, - textRenderable->_text, textRenderable->_font, textColor, effectColor, textRenderable->_effectThickness, textRenderable->_effect, - textRenderable->_alignment, textRenderable->_unlit, forward); + textRenderer->draw(batch, textRenderable->_font, { textRenderable->_text, textColor, effectColor, { textRenderable->_leftMargin / scale, -textRenderable->_topMargin / scale }, + bounds / scale, scale, textRenderable->_effectThickness, textRenderable->_effect, textRenderable->_alignment, textRenderable->_unlit, forward, mirror }); } namespace render { @@ -401,4 +417,10 @@ template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Poi return false; } +template <> void payloadComputeMirrorView(const entities::TextPayload::Pointer& payload, ViewFrustum& viewFrustum) { + if (payload) { + payload->computeMirrorView(viewFrustum); + } +} + } diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index 8a18554dea..00b9a1ec19 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -101,6 +101,7 @@ public: ShapeKey getShapeKey() const; void render(RenderArgs* args); bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const; + void computeMirrorView(ViewFrustum& viewFrustum) const; protected: QUuid _entityID; @@ -117,6 +118,7 @@ namespace render { template <> const ShapeKey shapeGetShapeKey(const entities::TextPayload::Pointer& payload); template <> void payloadRender(const entities::TextPayload::Pointer& payload, RenderArgs* args); template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Pointer& payload, const std::unordered_set& containingZones); + template <> void payloadComputeMirrorView(const entities::TextPayload::Pointer& payload, ViewFrustum& viewFrustum); } #endif // hifi_RenderableTextEntityItem_h diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index de88381468..43d0f379bf 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -221,6 +221,10 @@ void ModelMeshPartPayload::updateKey(const render::ItemKey& key) { builder.withSubMetaCulled(); } + if (_mirrorMode == MirrorMode::MIRROR || (_mirrorMode == MirrorMode::PORTAL && !_portalExitID.isNull())) { + builder.withMirror(); + } + _itemKey = builder.build(); } @@ -349,7 +353,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { procedural->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f, _shapeKey.isDeformed(), _shapeKey.isDualQuatSkinned())); batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); - } else { + } else if (!_itemKey.isMirror()) { // apply material properties if (RenderPipelines::bindMaterials(_drawMaterials, batch, args->_renderMode, args->_enableTexturing)) { args->_details._materialSwitches++; @@ -381,7 +385,9 @@ bool ModelMeshPartPayload::passesZoneOcclusionTest(const std::unordered_set& blendshapeBuffers, const QVector& blendedMeshSizes) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 066e8cb1d8..7d622ab489 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1078,9 +1078,11 @@ void Model::setMirrorMode(MirrorMode mirrorMode, const render::ScenePointer& sce } render::Transaction transaction; + auto renderItemsKey = _renderItemKeyGlobalFlags; for (auto item : _modelMeshRenderItemIDs) { - transaction.updateItem(item, [mirrorMode](ModelMeshPartPayload& data) { + transaction.updateItem(item, [mirrorMode, renderItemsKey](ModelMeshPartPayload& data) { data.setMirrorMode(mirrorMode); + data.updateKey(renderItemsKey); }); } scene->enqueueTransaction(transaction); @@ -1096,9 +1098,11 @@ void Model::setPortalExitID(const QUuid& portalExitID, const render::ScenePointe } render::Transaction transaction; + auto renderItemsKey = _renderItemKeyGlobalFlags; for (auto item : _modelMeshRenderItemIDs) { - transaction.updateItem(item, [portalExitID](ModelMeshPartPayload& data) { + transaction.updateItem(item, [portalExitID, renderItemsKey](ModelMeshPartPayload& data) { data.setPortalExitID(portalExitID); + data.updateKey(renderItemsKey); }); } scene->enqueueTransaction(transaction); diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index 76d8374fb7..8ab1b8e0e9 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -40,21 +40,18 @@ float TextRenderer3D::getFontSize() const { return 0.0f; } -void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, - const QString& str, const glm::vec4& color, bool unlit, bool forward) { +void TextRenderer3D::draw(gpu::Batch& batch, const Font::DrawProps& props) { 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, - const QString& str, const QString& font, const glm::vec4& color, const glm::vec3& effectColor, - float effectThickness, TextEffect effect, TextAlignment alignment, bool unlit, bool forward) { +void TextRenderer3D::draw(gpu::Batch& batch, const QString& font, const Font::DrawProps& props) { if (font != _family) { _family = font; _font = Font::load(_family); } if (_font) { - _font->drawString(batch, _drawInfo, str, color, effectColor, effectThickness, effect, alignment, { x, y }, bounds, scale, unlit, forward); + _font->drawString(batch, _drawInfo, props); } } \ No newline at end of file diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h index edccf1429c..9db93e9dcc 100644 --- a/libraries/render-utils/src/TextRenderer3D.h +++ b/libraries/render-utils/src/TextRenderer3D.h @@ -26,12 +26,9 @@ public: glm::vec2 computeExtent(const QString& str) const; float getFontSize() const; // Pixel size - - void draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, - const QString& str, const glm::vec4& color, bool unlit, bool forward); - 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); + + void draw(gpu::Batch& batch, const Font::DrawProps& props); + void draw(gpu::Batch& batch, const QString& font, const Font::DrawProps& props); private: TextRenderer3D(const char* family); diff --git a/libraries/render-utils/src/render-utils/sdf_text3D.slp b/libraries/render-utils/src/render-utils/sdf_text3D.slp index 118135d099..f3f9af59aa 100644 --- a/libraries/render-utils/src/render-utils/sdf_text3D.slp +++ b/libraries/render-utils/src/render-utils/sdf_text3D.slp @@ -1 +1 @@ -DEFINES (translucent unlit:f)/forward \ No newline at end of file +DEFINES (translucent unlit:f)/forward mirror:f \ No newline at end of file diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf index c5bed1ecab..ebcfe52f4d 100644 --- a/libraries/render-utils/src/sdf_text3D.slf +++ b/libraries/render-utils/src/sdf_text3D.slf @@ -41,6 +41,12 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord1 _texCoord01.zw 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() { vec4 color = evalSDFSuperSampled(_texCoord0, _glyphBounds); @@ -51,13 +57,17 @@ void main() { } <@endif@> +<@if HIFI_USE_MIRROR@> + color.rgb = texelFetch(mirrorMap, ivec2(gl_FragCoord.xy), 0).rgb; +<@endif@> + <@if HIFI_USE_UNLIT@> <@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@> - _fragColor0 = vec4(color.rgb * isUnlitEnabled(), color.a); + _fragColor0 = vec4(color.rgb * isUnlitEnabled(), 1.0); <@else@> packDeferredFragmentUnlit( normalize(_normalWS), - color.a, + 1.0, color.rgb); <@endif@> <@else@> diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 1a31e80d7d..95a3d895e8 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -29,7 +29,7 @@ static std::mutex fontMutex; -std::map, gpu::PipelinePointer> Font::_pipelines; +std::map, gpu::PipelinePointer> Font::_pipelines; gpu::Stream::FormatPointer Font::_format; struct TextureVertex { @@ -277,6 +277,7 @@ void Font::setupGPU() { if (_pipelines.empty()) { using namespace shader::render_utils::program; + // transparent, unlit, forward static const std::vector> keys = { 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), @@ -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*/) }; 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(); state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, !std::get<0>(key), gpu::LESS_EQUAL); - state->setBlendFunction(std::get<0>(key), + state->setDepthTest(true, !transparent, gpu::LESS_EQUAL); + state->setBlendFunction(transparent, 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); - if (std::get<0>(key)) { + if (transparent) { PrepareStencil::testMask(*state); } else { 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 @@ -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, - const glm::vec3& effectColor, float effectThickness, TextEffect effect, TextAlignment alignment, - const glm::vec2& origin, const glm::vec2& bounds, float scale, bool unlit, bool forward) { - if (!_loaded || str == "") { +void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const DrawProps& props) { + if (!_loaded || props.str == "") { return; } - int textEffect = (int)effect; + int textEffect = (int)props.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 (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)) || - (textEffect == SHADOW_EFFECT && scale != _scale)) { - _scale = scale; - _alignment = alignment; - buildVertices(drawInfo, str, origin, bounds, scale, textEffect == SHADOW_EFFECT, alignment); + (textEffect == SHADOW_EFFECT && props.scale != _scale)) { + _scale = props.scale; + _alignment = props.alignment; + buildVertices(drawInfo, props.str, props.origin, props.bounds, props.scale, textEffect == SHADOW_EFFECT, props.alignment); } setupGPU(); - if (!drawInfo.paramsBuffer || drawInfo.params.color != color || drawInfo.params.effectColor != effectColor || - drawInfo.params.effectThickness != effectThickness || drawInfo.params.effect != textEffect) { - drawInfo.params.color = color; - drawInfo.params.effectColor = effectColor; - drawInfo.params.effectThickness = effectThickness; + if (!drawInfo.paramsBuffer || drawInfo.params.color != props.color || drawInfo.params.effectColor != props.effectColor || + drawInfo.params.effectThickness != props.effectThickness || drawInfo.params.effect != textEffect) { + drawInfo.params.color = props.color; + drawInfo.params.effectColor = props.effectColor; + drawInfo.params.effectThickness = props.effectThickness; drawInfo.params.effect = textEffect; // 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); } - 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.setInputBuffer(0, drawInfo.verticesBuffer, 0, _format->getChannels().at(0)._stride); batch.setResourceTexture(render_utils::slot::texture::TextFont, _texture); diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h index 322e96439e..e8a353a686 100644 --- a/libraries/render-utils/src/text/Font.h +++ b/libraries/render-utils/src/text/Font.h @@ -57,10 +57,30 @@ public: glm::vec2 computeExtent(const QString& str) const; 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 - void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const QString& str, const glm::vec4& color, - const glm::vec3& effectColor, float effectThickness, TextEffect effect, TextAlignment alignment, - const glm::vec2& origin, const glm::vec2& bound, float scale, bool unlit, bool forward); + void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const DrawProps& props); static Pointer load(const QString& family); @@ -105,7 +125,7 @@ private: gpu::TexturePointer _texture; gpu::BufferStreamPointer _stream; - static std::map, gpu::PipelinePointer> _pipelines; + static std::map, gpu::PipelinePointer> _pipelines; static gpu::Stream::FormatPointer _format; }; diff --git a/libraries/shared/src/MirrorMode.cpp b/libraries/shared/src/MirrorMode.cpp index 53a51b1c1c..f84948c6e4 100644 --- a/libraries/shared/src/MirrorMode.cpp +++ b/libraries/shared/src/MirrorMode.cpp @@ -15,6 +15,8 @@ const char* MirrorModeNames[] = { }; static const size_t MIRROR_MODE_NAMES = (sizeof(MirrorModeNames) / sizeof(MirrorModeNames[0])); +std::function MirrorModeHelpers::_computeMirrorViewOperator = + [](ViewFrustum&, const glm::vec3&, const glm::quat&, MirrorMode, const QUuid&) { return; }; QString MirrorModeHelpers::getNameForMirrorMode(MirrorMode mode) { if (((int)mode <= 0) || ((int)mode >= (int)MIRROR_MODE_NAMES)) { @@ -23,3 +25,12 @@ QString MirrorModeHelpers::getNameForMirrorMode(MirrorMode mode) { return MirrorModeNames[(int)mode]; } + +void MirrorModeHelpers::setComputeMirrorViewOperator(std::function computeMirrorViewOperator) { + _computeMirrorViewOperator = computeMirrorViewOperator; +} + +void MirrorModeHelpers::computeMirrorView(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation, + MirrorMode mirrorMode, const QUuid& portalExitID) { + _computeMirrorViewOperator(viewFrustum, inPropertiesPosition, inPropertiesRotation, mirrorMode, portalExitID); +} \ No newline at end of file diff --git a/libraries/shared/src/MirrorMode.h b/libraries/shared/src/MirrorMode.h index 38af190070..2b2fa1d62e 100644 --- a/libraries/shared/src/MirrorMode.h +++ b/libraries/shared/src/MirrorMode.h @@ -9,8 +9,12 @@ #ifndef hifi_MirrorMode_h #define hifi_MirrorMode_h +#include + #include "QString" +#include "ViewFrustum.h" + /*@jsdoc *

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

* @@ -35,6 +39,13 @@ enum class MirrorMode { class MirrorModeHelpers { public: static QString getNameForMirrorMode(MirrorMode mode); + + static void setComputeMirrorViewOperator(std::function computeMirrorViewOperator); + static void computeMirrorView(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation, + MirrorMode mirrorMode, const QUuid& portalExitID); + +private: + static std::function _computeMirrorViewOperator; }; #endif // hifi_MirrorMode_h From 3336efb6ee3a231caff58adcf9417eb8ad4a3041 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 19 Nov 2023 14:54:31 -0800 Subject: [PATCH 008/109] portals use exit as ignoreItem --- .../entities-renderer/src/RenderableEntityItem.cpp | 10 ++++++---- .../entities-renderer/src/RenderableEntityItem.h | 6 +++--- .../src/RenderableImageEntityItem.cpp | 6 ++++-- .../src/RenderableTextEntityItem.cpp | 8 +++++--- .../entities-renderer/src/RenderableTextEntityItem.h | 4 ++-- libraries/render-utils/src/MeshPartPayload.cpp | 9 +++++---- libraries/render-utils/src/MeshPartPayload.h | 4 ++-- libraries/render-utils/src/RenderCommonTask.cpp | 10 +++++----- libraries/render/src/render/Item.cpp | 6 +++--- libraries/render/src/render/Item.h | 12 ++++++------ libraries/shared/src/MirrorMode.cpp | 12 ++++++------ libraries/shared/src/MirrorMode.h | 8 ++++---- 12 files changed, 51 insertions(+), 44 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index aec9f092b9..0c93a795ed 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -226,7 +226,7 @@ bool EntityRenderer::passesZoneOcclusionTest(const std::unordered_set& co return true; } -void EntityRenderer::computeMirrorView(ViewFrustum& viewFrustum) const { +ItemID EntityRenderer::computeMirrorView(ViewFrustum& viewFrustum) const { glm::vec3 inPropertiesPosition; glm::quat inPropertiesRotation; MirrorMode mirrorMode; @@ -237,11 +237,11 @@ void EntityRenderer::computeMirrorView(ViewFrustum& viewFrustum) const { mirrorMode = _mirrorMode; portalExitID = _portalExitID; }); - computeMirrorViewOperator(viewFrustum, inPropertiesPosition, inPropertiesRotation, mirrorMode, portalExitID); + return computeMirrorViewOperator(viewFrustum, inPropertiesPosition, inPropertiesRotation, mirrorMode, portalExitID); } -void EntityRenderer::computeMirrorViewOperator(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation, - MirrorMode mirrorMode, const QUuid& 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); @@ -313,6 +313,8 @@ void EntityRenderer::computeMirrorViewOperator(ViewFrustum& viewFrustum, const g projection[3][2] = c.w; viewFrustum.setProjection(projection, true); + + return (mirrorMode == MirrorMode::PORTAL && !portalExitID.isNull()) ? DependencyManager::get()->renderableIdForEntityId(portalExitID) : Item::INVALID_ITEM_ID; } void EntityRenderer::render(RenderArgs* args) { diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index fceba6f0e6..99dbffbc72 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -75,9 +75,9 @@ 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; - static void computeMirrorViewOperator(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation, - MirrorMode mirrorMode, const QUuid& portalExitID); + 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: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index 4c18653d4f..66a5d0d609 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -199,8 +199,10 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { procedural->prepare(*batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(transparent)); } else if (pipelineType == Pipeline::SIMPLE) { batch->setResourceTexture(0, _texture->getGPUTexture()); - } else if (RenderPipelines::bindMaterials(materials, *batch, args->_renderMode, args->_enableTexturing)) { - args->_details._materialSwitches++; + } else if (pipelineType == Pipeline::MATERIAL) { + if (RenderPipelines::bindMaterials(materials, *batch, args->_renderMode, args->_enableTexturing)) { + args->_details._materialSwitches++; + } } DependencyManager::get()->renderQuad( diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 6fd99a69fd..a15e2839a4 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -316,7 +316,7 @@ bool entities::TextPayload::passesZoneOcclusionTest(const std::unordered_set(); if (entityTreeRenderer) { auto renderable = entityTreeRenderer->renderableForEntityId(_entityID); @@ -324,6 +324,7 @@ void entities::TextPayload::computeMirrorView(ViewFrustum& viewFrustum) const { return renderable->computeMirrorView(viewFrustum); } } + return Item::INVALID_ITEM_ID; } void entities::TextPayload::render(RenderArgs* args) { @@ -417,10 +418,11 @@ template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Poi return false; } -template <> void payloadComputeMirrorView(const entities::TextPayload::Pointer& payload, ViewFrustum& viewFrustum) { +template <> ItemID payloadComputeMirrorView(const entities::TextPayload::Pointer& payload, ViewFrustum& viewFrustum) { if (payload) { - payload->computeMirrorView(viewFrustum); + return payload->computeMirrorView(viewFrustum); } + return Item::INVALID_ITEM_ID; } } diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index 00b9a1ec19..f48bb8085f 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -101,7 +101,7 @@ public: ShapeKey getShapeKey() const; void render(RenderArgs* args); bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const; - void computeMirrorView(ViewFrustum& viewFrustum) const; + ItemID computeMirrorView(ViewFrustum& viewFrustum) const; protected: QUuid _entityID; @@ -118,7 +118,7 @@ namespace render { template <> const ShapeKey shapeGetShapeKey(const entities::TextPayload::Pointer& payload); template <> void payloadRender(const entities::TextPayload::Pointer& payload, RenderArgs* args); template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Pointer& payload, const std::unordered_set& containingZones); - template <> void payloadComputeMirrorView(const entities::TextPayload::Pointer& payload, ViewFrustum& viewFrustum); + template <> ItemID payloadComputeMirrorView(const entities::TextPayload::Pointer& payload, ViewFrustum& viewFrustum); } #endif // hifi_RenderableTextEntityItem_h diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 43d0f379bf..9adeb39e7c 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -384,10 +384,10 @@ bool ModelMeshPartPayload::passesZoneOcclusionTest(const std::unordered_set& blendshapeBuffers, const QVector& blendedMeshSizes) { @@ -440,9 +440,10 @@ template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Poin return false; } -template <> void payloadComputeMirrorView(const ModelMeshPartPayload::Pointer& payload, ViewFrustum& viewFrustum) { +template <> ItemID payloadComputeMirrorView(const ModelMeshPartPayload::Pointer& payload, ViewFrustum& viewFrustum) { if (payload) { - payload->computeMirrorView(viewFrustum); + return payload->computeMirrorView(viewFrustum); } + return Item::INVALID_ITEM_ID; } } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 6e0bb91a30..7e331a9497 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -61,7 +61,7 @@ public: 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; + render::ItemID computeMirrorView(ViewFrustum& viewFrustum) const; void addMaterial(graphics::MaterialLayer material) { _drawMaterials.push(material); } void removeMaterial(graphics::MaterialPointer material) { _drawMaterials.remove(material); } @@ -112,7 +112,7 @@ 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); + template <> ItemID payloadComputeMirrorView(const ModelMeshPartPayload::Pointer& payload, ViewFrustum& viewFrustum); } diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index a3355ea805..7647a1b8d2 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -300,12 +300,12 @@ public: _cachedArgsPointer->_ignoreItem = args->_ignoreItem; _cachedArgsPointer->_mirrorDepth = args->_mirrorDepth; - args->_blitFramebuffer = _mirrorFramebuffer; - args->_ignoreItem = mirror.id; - args->_mirrorDepth = _depth; - ViewFrustum srcViewFrustum = args->getViewFrustum(); - args->_scene->getItem(mirror.id).computeMirrorView(srcViewFrustum); + 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; // Without calculating the bound planes, the mirror will use the same culling frustum as the main camera, // which is not what we want here. diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index 23e5570736..1633523267 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -161,10 +161,10 @@ namespace render { return payload->passesZoneOcclusionTest(containingZones); } - template <> void payloadComputeMirrorView(const PayloadProxyInterface::Pointer& payload, ViewFrustum& viewFrustum) { + template <> ItemID payloadComputeMirrorView(const PayloadProxyInterface::Pointer& payload, ViewFrustum& viewFrustum) { if (!payload) { - return; + return Item::INVALID_ITEM_ID; } - payload->computeMirrorView(viewFrustum); + return payload->computeMirrorView(viewFrustum); } } diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index c2f03b4e05..f91b887fcb 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -450,7 +450,7 @@ public: virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const = 0; - virtual void computeMirrorView(ViewFrustum& viewFrustum) const = 0; + virtual ItemID computeMirrorView(ViewFrustum& viewFrustum) const = 0; ~PayloadInterface() {} @@ -505,7 +505,7 @@ public: bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const { return _payload->passesZoneOcclusionTest(containingZones); } - void computeMirrorView(ViewFrustum& viewFrustum) const { _payload->computeMirrorView(viewFrustum); } + ItemID computeMirrorView(ViewFrustum& viewFrustum) const { return _payload->computeMirrorView(viewFrustum); } // Access the status const StatusPointer& getStatus() const { return _payload->getStatus(); } @@ -562,7 +562,7 @@ template uint32_t metaFetchMetaSubItems(const std::shared_ptr& payl 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; } +template ItemID payloadComputeMirrorView(const std::shared_ptr& payloadData, ViewFrustum& viewFrustum) { return Item::INVALID_ITEM_ID; } // 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 @@ -590,7 +590,7 @@ 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); } + virtual ItemID computeMirrorView(ViewFrustum& viewFrustum) const override { return payloadComputeMirrorView(_data, viewFrustum); } protected: DataPointer _data; @@ -647,7 +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; + 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 virtual void handleBlendedVertices(int blendshapeNumber, const QVector& blendshapeOffsets, @@ -660,7 +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); +template <> ItemID payloadComputeMirrorView(const PayloadProxyInterface::Pointer& payload, ViewFrustum& viewFrustum); typedef Item::PayloadPointer PayloadPointer; typedef std::vector Payloads; diff --git a/libraries/shared/src/MirrorMode.cpp b/libraries/shared/src/MirrorMode.cpp index f84948c6e4..272eb5d7c0 100644 --- a/libraries/shared/src/MirrorMode.cpp +++ b/libraries/shared/src/MirrorMode.cpp @@ -15,8 +15,8 @@ const char* MirrorModeNames[] = { }; static const size_t MIRROR_MODE_NAMES = (sizeof(MirrorModeNames) / sizeof(MirrorModeNames[0])); -std::function MirrorModeHelpers::_computeMirrorViewOperator = - [](ViewFrustum&, const glm::vec3&, const glm::quat&, MirrorMode, const QUuid&) { return; }; +std::function 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)) { @@ -26,11 +26,11 @@ QString MirrorModeHelpers::getNameForMirrorMode(MirrorMode mode) { return MirrorModeNames[(int)mode]; } -void MirrorModeHelpers::setComputeMirrorViewOperator(std::function computeMirrorViewOperator) { +void MirrorModeHelpers::setComputeMirrorViewOperator(std::function computeMirrorViewOperator) { _computeMirrorViewOperator = computeMirrorViewOperator; } -void MirrorModeHelpers::computeMirrorView(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation, - MirrorMode mirrorMode, const QUuid& portalExitID) { - _computeMirrorViewOperator(viewFrustum, inPropertiesPosition, inPropertiesRotation, mirrorMode, portalExitID); +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); } \ No newline at end of file diff --git a/libraries/shared/src/MirrorMode.h b/libraries/shared/src/MirrorMode.h index 2b2fa1d62e..e48e564df0 100644 --- a/libraries/shared/src/MirrorMode.h +++ b/libraries/shared/src/MirrorMode.h @@ -40,12 +40,12 @@ class MirrorModeHelpers { public: static QString getNameForMirrorMode(MirrorMode mode); - static void setComputeMirrorViewOperator(std::function computeMirrorViewOperator); - static void computeMirrorView(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation, - MirrorMode mirrorMode, const QUuid& portalExitID); + static void setComputeMirrorViewOperator(std::function computeMirrorViewOperator); + static uint32_t computeMirrorView(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation, + MirrorMode mirrorMode, const QUuid& portalExitID); private: - static std::function _computeMirrorViewOperator; + static std::function _computeMirrorViewOperator; }; #endif // hifi_MirrorMode_h From 6af7b9f77214790cf0d8425720f508d0d6322c33 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 19 Nov 2023 15:17:18 -0800 Subject: [PATCH 009/109] cleanup --- libraries/entities-renderer/src/RenderableEntityItem.cpp | 4 +++- libraries/render-utils/src/sdf_text3D.slf | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 0c93a795ed..ef91c1f034 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -248,6 +248,7 @@ ItemID EntityRenderer::computeMirrorViewOperator(ViewFrustum& viewFrustum, const 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(); if (renderer) { @@ -258,6 +259,7 @@ ItemID EntityRenderer::computeMirrorViewOperator(ViewFrustum& viewFrustum, const }); outToWorld = glm::translate(outPropertiesPosition) * glm::mat4_cast(outPropertiesRotation); + foundPortalExit = true; } } } @@ -314,7 +316,7 @@ ItemID EntityRenderer::computeMirrorViewOperator(ViewFrustum& viewFrustum, const viewFrustum.setProjection(projection, true); - return (mirrorMode == MirrorMode::PORTAL && !portalExitID.isNull()) ? DependencyManager::get()->renderableIdForEntityId(portalExitID) : Item::INVALID_ITEM_ID; + return foundPortalExit ? DependencyManager::get()->renderableIdForEntityId(portalExitID) : Item::INVALID_ITEM_ID; } void EntityRenderer::render(RenderArgs* args) { diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf index ebcfe52f4d..bf9bb0babd 100644 --- a/libraries/render-utils/src/sdf_text3D.slf +++ b/libraries/render-utils/src/sdf_text3D.slf @@ -59,15 +59,16 @@ void main() { <@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_TRANSLUCENT or HIFI_USE_FORWARD@> - _fragColor0 = vec4(color.rgb * isUnlitEnabled(), 1.0); + _fragColor0 = vec4(color.rgb * isUnlitEnabled(), color.a); <@else@> packDeferredFragmentUnlit( normalize(_normalWS), - 1.0, + color.a, color.rgb); <@endif@> <@else@> From 85f24f0951c6ef3e34549036b7dd09cc355f7073 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 2 Dec 2023 15:59:52 -0800 Subject: [PATCH 010/109] entity tags --- libraries/entities/src/EntityItem.cpp | 15 ++++ libraries/entities/src/EntityItem.h | 5 ++ .../entities/src/EntityItemProperties.cpp | 27 +++++++ libraries/entities/src/EntityItemProperties.h | 4 ++ .../entities/src/EntityItemPropertiesMacros.h | 7 ++ libraries/entities/src/EntityPropertyFlags.h | 1 + .../entities/src/EntityScriptingInterface.cpp | 11 +++ .../entities/src/EntityScriptingInterface.h | 19 +++++ libraries/entities/src/EntityTree.cpp | 36 ++++++++++ libraries/entities/src/EntityTree.h | 1 + libraries/entities/src/EntityTreeElement.cpp | 71 +++++++++++++++++++ libraries/entities/src/EntityTreeElement.h | 1 + libraries/networking/src/udt/PacketHeaders.h | 1 + libraries/octree/src/OctreePacketData.cpp | 35 +++++++++ libraries/octree/src/OctreePacketData.h | 4 ++ .../script-engine/src/ScriptValueUtils.cpp | 30 ++++++++ .../script-engine/src/ScriptValueUtils.h | 3 + 17 files changed, 271 insertions(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index b63f0d91ab..ad4345ba9f 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -104,6 +104,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_IGNORE_PICK_INTERSECTION; requestedProperties += PROP_RENDER_WITH_ZONES; requestedProperties += PROP_BILLBOARD_MODE; + requestedProperties += PROP_TAGS; requestedProperties += _grabProperties.getEntityProperties(params); // Physics @@ -301,6 +302,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, getIgnorePickIntersection()); APPEND_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, getRenderWithZones()); APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); + APPEND_ENTITY_PROPERTY(PROP_TAGS, getTags()); withReadLock([&] { _grabProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -874,6 +876,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, bool, setIgnorePickIntersection); READ_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, QVector, setRenderWithZones); READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); + READ_ENTITY_PROPERTY(PROP_TAGS, QSet, setTags); withWriteLock([&] { int bytesFromGrab = _grabProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, @@ -1358,6 +1361,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire COPY_ENTITY_PROPERTY_TO_PROPERTIES(ignorePickIntersection, getIgnorePickIntersection); COPY_ENTITY_PROPERTY_TO_PROPERTIES(renderWithZones, getRenderWithZones); COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(tags, getTags); withReadLock([&] { _grabProperties.getProperties(properties); }); @@ -1495,6 +1499,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignorePickIntersection, setIgnorePickIntersection); SET_ENTITY_PROPERTY_FROM_PROPERTIES(renderWithZones, setRenderWithZones); SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(tags, setTags); withWriteLock([&] { bool grabPropertiesChanged = _grabProperties.setProperties(properties); somethingChanged |= grabPropertiesChanged; @@ -3553,3 +3558,13 @@ void EntityItem::setBillboardMode(BillboardMode value) { _billboardMode = value; }); } + +void EntityItem::setTags(const QSet& tags) { + withWriteLock([&] { + _tags = tags; + }); +} + +QSet EntityItem::getTags() const { + return resultWithReadLock>([&] { return _tags; }); +} \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index ec84b0ccb2..01a2a7c5bf 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -559,6 +559,9 @@ public: BillboardMode getBillboardMode() const; virtual bool getRotateForPicking() const { return false; } + void setTags(const QSet& tags); + QSet getTags() const; + signals: void spaceUpdate(std::pair data); @@ -740,6 +743,8 @@ protected: bool _cullWithParent { false }; + QSet _tags; + mutable bool _needsRenderUpdate { false }; }; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index f543169401..205f0ab5f2 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -424,6 +424,21 @@ void EntityItemProperties::setEntityHostTypeFromString(const QString& entityHost } } +QVector EntityItemProperties::getTagsAsVector() const { + QVector tags; + for (const QString& tag : _tags) { + tags.push_back(tag); + } + return tags; +} + +void EntityItemProperties::setTagsFromVector(const QVector& tags) { + _tags.clear(); + for (const QString& tag : tags) { + _tags.insert(tag); + } +} + EntityPropertyFlags EntityItemProperties::getChangedProperties() const { EntityPropertyFlags changedProperties; @@ -454,6 +469,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_IGNORE_PICK_INTERSECTION, ignorePickIntersection); CHECK_PROPERTY_CHANGE(PROP_RENDER_WITH_ZONES, renderWithZones); CHECK_PROPERTY_CHANGE(PROP_BILLBOARD_MODE, billboardMode); + CHECK_PROPERTY_CHANGE(PROP_TAGS, tags); changedProperties += _grab.getChangedProperties(); // Physics @@ -822,6 +838,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * one of the zones in this list. * @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera. Use the rotation * property to control which axis is facing you. + * @property {string[]} tags=[] - A set of tags describing this entity. * * @property {Entities.Grab} grab - The entity's grab-related properties. * @@ -1615,6 +1632,7 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IGNORE_PICK_INTERSECTION, ignorePickIntersection); 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_TAGS, tags, getTagsAsVector()); _grab.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); // Physics @@ -2031,6 +2049,7 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h COPY_PROPERTY_FROM_QSCRIPTVALUE(ignorePickIntersection, bool, setIgnorePickIntersection); COPY_PROPERTY_FROM_QSCRIPTVALUE(renderWithZones, qVectorQUuid, setRenderWithZones); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(billboardMode, BillboardMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(tags, qVectorQString, setTagsFromVector, getTagsAsVector); _grab.copyFromScriptValue(object, namesSet, _defaultSettings); // Physics @@ -2317,6 +2336,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(ignorePickIntersection); COPY_PROPERTY_IF_CHANGED(renderWithZones); COPY_PROPERTY_IF_CHANGED(billboardMode); + COPY_PROPERTY_IF_CHANGED(tags); _grab.merge(other._grab); // Physics @@ -2605,6 +2625,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_IGNORE_PICK_INTERSECTION, IgnorePickIntersection, ignorePickIntersection, bool); ADD_PROPERTY_TO_MAP(PROP_RENDER_WITH_ZONES, RenderWithZones, renderWithZones, QVector); ADD_PROPERTY_TO_MAP(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode); + ADD_PROPERTY_TO_MAP(PROP_TAGS, Tags, tags, QSet); { // Grab ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_GRABBABLE, Grab, grab, Grabbable, grabbable); ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_KINEMATIC, Grab, grab, GrabKinematic, grabKinematic); @@ -3087,6 +3108,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, properties.getIgnorePickIntersection()); APPEND_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, properties.getRenderWithZones()); APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); + APPEND_ENTITY_PROPERTY(PROP_TAGS, properties.getTags()); _staticGrab.setProperties(properties); _staticGrab.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -3567,6 +3589,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IGNORE_PICK_INTERSECTION, bool, setIgnorePickIntersection); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RENDER_WITH_ZONES, QVector, setRenderWithZones); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TAGS, QSet, setTags); properties.getGrab().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); // Physics @@ -3959,6 +3982,7 @@ void EntityItemProperties::markAllChanged() { _ignorePickIntersectionChanged = true; _renderWithZonesChanged = true; _billboardModeChanged = true; + _tagsChanged = true; _grab.markAllChanged(); // Physics @@ -4358,6 +4382,9 @@ QList EntityItemProperties::listChangedProperties() { if (billboardModeChanged()) { out += "billboardMode"; } + if (tagsChanged()) { + out += "tags"; + } getGrab().listChangedProperties(out); // Physics diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 283d14c4cc..74cd9c4f15 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -203,6 +203,7 @@ public: DEFINE_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, IgnorePickIntersection, ignorePickIntersection, bool, false); 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_REF(PROP_TAGS, Tags, tags, QSet, QSet()); DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup); // Physics @@ -498,6 +499,9 @@ protected: QString getCollisionMaskAsString() const; void setCollisionMaskFromString(const QString& maskString); + QVector getTagsAsVector() const; + void setTagsFromVector(const QVector& tags); + private: QUuid _id; bool _idSet; diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index d6ee64ba9a..3b6e424663 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -125,6 +125,7 @@ inline ScriptValue convertScriptValue(ScriptEngine* e, const QVector& inline ScriptValue convertScriptValue(ScriptEngine* e, const QVector& v) {return qVectorBoolToScriptValue(e, v); } inline ScriptValue convertScriptValue(ScriptEngine* e, const QVector& v) { return qVectorFloatToScriptValue(e, v); } inline ScriptValue convertScriptValue(ScriptEngine* e, const QVector& v) { return qVectorQUuidToScriptValue(e, v); } +inline ScriptValue convertScriptValue(ScriptEngine* e, const QVector& v) { return qVectorQStringToScriptValue(e, v); } inline ScriptValue convertScriptValue(ScriptEngine* e, const QRect& v) { return qRectToScriptValue(e, v); } @@ -223,6 +224,7 @@ typedef QVector qVectorQuat; typedef QVector qVectorBool; typedef QVector qVectorFloat; typedef QVector qVectorQUuid; +typedef QVector qVectorQString; inline float float_convertFromScriptValue(const ScriptValue& v, bool& isValid) { return v.toVariant().toFloat(&isValid); } inline quint64 quint64_convertFromScriptValue(const ScriptValue& v, bool& isValid) { return v.toVariant().toULongLong(&isValid); } inline quint32 quint32_convertFromScriptValue(const ScriptValue& v, bool& isValid) { @@ -305,6 +307,11 @@ inline qVectorQUuid qVectorQUuid_convertFromScriptValue(const ScriptValue& v, bo return qVectorQUuidFromScriptValue(v); } +inline qVectorQString qVectorQString_convertFromScriptValue(const ScriptValue& v, bool& isValid) { + isValid = true; + return qVectorQStringFromScriptValue(v); +} + inline glm::quat quat_convertFromScriptValue(const ScriptValue& v, bool& isValid) { isValid = false; /// assume it can't be converted ScriptValue x = v.property("x"); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index e0b5a04094..555ac0a2de 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -46,6 +46,7 @@ enum EntityPropertyList { PROP_IGNORE_PICK_INTERSECTION, PROP_RENDER_WITH_ZONES, PROP_BILLBOARD_MODE, + PROP_TAGS, // Grab PROP_GRAB_GRABBABLE, PROP_GRAB_KINEMATIC, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 2c36b7ddbc..4acf9b8d1b 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1457,6 +1457,17 @@ QVector EntityScriptingInterface::findEntitiesByName(const QString entity return result; } +QVector EntityScriptingInterface::findEntitiesByTags(const QVector entityTags, const glm::vec3& center, float radius, bool caseSensitiveSearch) const { + QVector result; + if (_entityTree) { + _entityTree->withReadLock([&] { + unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); + _entityTree->evalEntitiesInSphereWithTags(center, radius, entityTags, caseSensitiveSearch, PickFilter(searchFilter), result); + }); + } + return result; +} + RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const ScriptValue& entityIdsToInclude, const ScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) const { PROFILE_RANGE(script_entities, __FUNCTION__); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 198cac005c..c677bdf0a1 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -789,6 +789,25 @@ public slots: Q_INVOKABLE QVector findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, bool caseSensitiveSearch = false) const; + /*@jsdoc + * Finds all domain and avatar entities with particular tags that intersect a sphere. + *

Note: Server entity scripts only find entities that have a server entity script + * running in them or a parent entity. You can apply a dummy script to entities that you want found in a search.

+ * @function Entities.findEntitiesByTags + * @param {string[]} entityTags - The tags of the entity to search for. + * @param {Vec3} center - The point about which to search. + * @param {number} radius - The radius within which to search. + * @param {boolean} [caseSensitive=false] - true if the search is case-sensitive, false if it is + * case-insensitive. + * @returns {Uuid[]} An array of entity IDs that have the specified name and intersect the search sphere. The array is + * empty if no entities could be found. + * @example
+ * var entityIDs = Entities.findEntitiesByTags(["Light-Target"], MyAvatar.position, 10, false); + * print("Number of entities with the tag Light-Target: " + entityIDs.length); + */ + Q_INVOKABLE QVector findEntitiesByTags(const QVector entityTags, const glm::vec3& center, float radius, + bool caseSensitiveSearch = false) const; + /*@jsdoc * Finds the first avatar or domain entity intersected by a {@link PickRay}. Light and Zone * entities are not intersected unless they've been configured as pickable using diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index fb7fbe6499..5f88d7191c 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1110,6 +1110,42 @@ void EntityTree::evalEntitiesInSphereWithName(const glm::vec3& center, float rad foundEntities.swap(args.entities); } +class FindEntitiesInSphereWithTagsArgs { +public: + // Inputs + glm::vec3 position; + float targetRadius; + QVector tags; + bool caseSensitive; + PickFilter searchFilter; + + // Outputs + QVector entities; +}; + +bool evalInSphereWithTagsOperation(const OctreeElementPointer& element, void* extraData) { + FindEntitiesInSphereWithTagsArgs* args = static_cast(extraData); + glm::vec3 penetration; + bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration); + + // If this element contains the point, then search it... + if (sphereIntersection) { + EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); + entityTreeElement->evalEntitiesInSphereWithTags(args->position, args->targetRadius, args->tags, args->caseSensitive, args->searchFilter, args->entities); + return true; // keep searching in case children have closer entities + } + + // if this element doesn't contain the point, then none of it's children can contain the point, so stop searching + return false; +} + +// NOTE: assumes caller has handled locking +void EntityTree::evalEntitiesInSphereWithTags(const glm::vec3& center, float radius, const QVector& tags, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities) { + FindEntitiesInSphereWithTagsArgs args = { center, radius, tags, caseSensitive, searchFilter, QVector() }; + recurseTreeWithOperation(evalInSphereWithTagsOperation, &args); + foundEntities.swap(args.entities); +} + class FindEntitiesInCubeArgs { public: // Inputs diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index d5c3a74eb7..1161bec6e9 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -138,6 +138,7 @@ public: void evalEntitiesInSphere(const glm::vec3& center, float radius, PickFilter searchFilter, QVector& foundEntities); void evalEntitiesInSphereWithType(const glm::vec3& center, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector& foundEntities); void evalEntitiesInSphereWithName(const glm::vec3& center, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities); + void evalEntitiesInSphereWithTags(const glm::vec3& center, float radius, const QVector& tags, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities); void evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector& foundEntities); void evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector& foundEntities); void evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector& foundEntities); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index f330058a78..030c3082c2 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -602,6 +602,77 @@ void EntityTreeElement::evalEntitiesInSphereWithName(const glm::vec3& position, }); } +void EntityTreeElement::evalEntitiesInSphereWithTags(const glm::vec3& position, float radius, const QVector& tags, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities) const { + forEachEntity([&](EntityItemPointer entity) { + if (!checkFilterSettings(entity, searchFilter)) { + return; + } + + QSet entityTags = entity->getTags(); + for (const QString& tag : tags) { + if (caseSensitive && !entityTags.contains(tag)) { + return; + } else { + const QString lowerTag = tag.toLower(); + bool found = false; + for (const QString& entityTag : entityTags) { + if (lowerTag == entityTag.toLower()) { + found = true; + } + } + if (!found) { + return; + } + } + } + + bool success; + AABox entityBox = entity->getAABox(success); + + // if the sphere doesn't intersect with our world frame AABox, we don't need to consider the more complex case + glm::vec3 penetration; + if (success && entityBox.findSpherePenetration(position, radius, penetration)) { + + glm::vec3 dimensions = entity->getScaledDimensions(); + + // FIXME - consider allowing the entity to determine penetration so that + // entities could presumably do actual hull testing if they wanted to + // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better in particular + // can we handle the ellipsoid case better? We only currently handle perfect spheres + // with centered registration points + if (entity->getShapeType() == SHAPE_TYPE_SPHERE && (dimensions.x == dimensions.y && dimensions.y == dimensions.z)) { + + // NOTE: entity->getRadius() doesn't return the true radius, it returns the radius of the + // maximum bounding sphere, which is actually larger than our actual radius + float entityTrueRadius = dimensions.x / 2.0f; + bool success; + glm::vec3 center = entity->getCenterPosition(success); + + if (success && findSphereSpherePenetration(position, radius, center, entityTrueRadius, penetration)) { + foundEntities.push_back(entity->getID()); + } + } else { + // determine the worldToEntityMatrix that doesn't include scale because + // we're going to use the registration aware aa box in the entity frame + glm::mat4 translation = glm::translate(entity->getWorldPosition()); + glm::mat4 rotation = glm::mat4_cast(entity->getWorldOrientation()); + glm::mat4 entityToWorldMatrix = translation * rotation; + glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); + + glm::vec3 registrationPoint = entity->getRegistrationPoint(); + glm::vec3 corner = -(dimensions * registrationPoint) + entity->getPivot(); + + AABox entityFrameBox(corner, dimensions); + + glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(position, 1.0f)); + if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, radius, penetration)) { + foundEntities.push_back(entity->getID()); + } + } + } + }); +} + void EntityTreeElement::evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector& foundEntities) const { forEachEntity([&](EntityItemPointer entity) { if (!checkFilterSettings(entity, searchFilter)) { diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index dab56132c9..951481fcb9 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -177,6 +177,7 @@ public: void evalEntitiesInSphere(const glm::vec3& position, float radius, PickFilter searchFilter, QVector& foundEntities) const; void evalEntitiesInSphereWithType(const glm::vec3& position, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector& foundEntities) const; void evalEntitiesInSphereWithName(const glm::vec3& position, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities) const; + void evalEntitiesInSphereWithTags(const glm::vec3& position, float radius, const QVector& tags, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities) const; void evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector& foundEntities) const; void evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector& foundEntities) const; void evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector& foundEntities) const; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 3badfdf418..55e183d00b 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, + EntityTags, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 3966fcf86f..c13d58226b 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -496,6 +496,24 @@ bool OctreePacketData::appendValue(const QVector& value) { return success; } +bool OctreePacketData::appendValue(const QSet& value) { + QVector valueVector; + for (const QString& valueString : value) { + valueVector.push_back(valueString); + } + + uint16_t qVecSize = value.size(); + bool success = appendValue(qVecSize); + if (success) { + success = append((const unsigned char*)valueVector.constData(), qVecSize * sizeof(QString)); + if (success) { + _bytesOfValues += qVecSize * sizeof(QString); + _totalBytesOfValues += qVecSize * sizeof(QString); + } + } + return success; +} + bool OctreePacketData::appendValue(const glm::quat& value) { const size_t VALUES_PER_QUAT = 4; const size_t PACKED_QUAT_SIZE = sizeof(uint16_t) * VALUES_PER_QUAT; @@ -802,6 +820,23 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QVecto return sizeof(uint16_t) + length * sizeof(QUuid); } +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QSet& result) { + QVector resultVector; + + uint16_t length; + memcpy(&length, dataBytes, sizeof(uint16_t)); + dataBytes += sizeof(length); + resultVector.resize(length); + memcpy(resultVector.data(), dataBytes, length * sizeof(QString)); + + result.clear(); + for (const QString& resultString : resultVector) { + result.insert(resultString); + } + + return sizeof(uint16_t) + length * sizeof(QString); +} + int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result) { uint16_t length; memcpy(&length, dataBytes, sizeof(length)); diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 583f090942..745551eb22 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -190,6 +190,9 @@ public: /// appends a QVector of QUuids to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const QVector& value); + /// appends a QSet of QStrings to the end of the stream, may fail if new data stream is too long to fit in packet + bool appendValue(const QSet& value); + /// appends a packed quat to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const glm::quat& value); @@ -290,6 +293,7 @@ public: static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, QSet& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result); static int unpackDataFromBytes(const unsigned char* dataBytes, AACube& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QRect& result); diff --git a/libraries/script-engine/src/ScriptValueUtils.cpp b/libraries/script-engine/src/ScriptValueUtils.cpp index 5bada98f15..f5808080ba 100644 --- a/libraries/script-engine/src/ScriptValueUtils.cpp +++ b/libraries/script-engine/src/ScriptValueUtils.cpp @@ -862,6 +862,14 @@ bool quuidFromScriptValue(const ScriptValue& object, QUuid& uuid) { return true; } +ScriptValue qStringToScriptValue(ScriptEngine* engine, const QString& string) { + if (string.isNull()) { + return engine->nullValue(); + } + ScriptValue obj(engine->newValue(string)); + return obj; +} + /*@jsdoc * A 2D size value. * @typedef {object} Size @@ -1029,3 +1037,25 @@ QVector qVectorEntityItemIDFromScriptValue(const ScriptValue& arra } return newVector; } + +ScriptValue qVectorQStringToScriptValue(ScriptEngine* engine, const QVector& vector) { + ScriptValue array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + array.setProperty(i, qStringToScriptValue(engine, vector.at(i))); + } + return array; +} + +QVector qVectorQStringFromScriptValue(const ScriptValue& array) { + if (!array.isArray()) { + return QVector(); + } + QVector newVector; + int length = array.property("length").toInteger(); + newVector.reserve(length); + for (int i = 0; i < length; i++) { + QString string = array.property(i).toString(); + newVector << string; + } + return newVector; +} \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptValueUtils.h b/libraries/script-engine/src/ScriptValueUtils.h index 45fee61cc4..0a206d1aa5 100644 --- a/libraries/script-engine/src/ScriptValueUtils.h +++ b/libraries/script-engine/src/ScriptValueUtils.h @@ -224,6 +224,9 @@ ScriptValue qVectorQUuidToScriptValue(ScriptEngine* engine, const QVector bool qVectorQUuidFromScriptValue(const ScriptValue& array, QVector& vector); QVector qVectorQUuidFromScriptValue(const ScriptValue& array); +ScriptValue qVectorQStringToScriptValue(ScriptEngine* engine, const QVector& vector); +QVector qVectorQStringFromScriptValue(const ScriptValue& array); + class AACube; ScriptValue aaCubeToScriptValue(ScriptEngine* engine, const AACube& aaCube); bool aaCubeFromScriptValue(const ScriptValue& object, AACube& aaCube); From 3e0c50e077c6c1c87a878cf4d5cb1afd0e3564cc Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Mon, 4 Dec 2023 14:44:28 -0800 Subject: [PATCH 011/109] wild guess to handle view correction, hide portalExitID in create when mirrorMode != portal --- libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp | 5 +++++ libraries/gpu-gl-common/src/gpu/gl/GLBackend.h | 2 ++ .../gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp | 6 +++++- libraries/gpu/src/gpu/Batch.cpp | 6 ++++++ libraries/gpu/src/gpu/Batch.h | 2 ++ libraries/gpu/src/gpu/FrameIOKeys.h | 1 + libraries/render-utils/src/RenderCommonTask.cpp | 10 ++++++++++ .../entityProperties/html/js/entityProperties.js | 1 + 8 files changed, 32 insertions(+), 1 deletion(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index af39458f17..421fef4334 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -81,6 +81,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_disableContextViewCorrection), (&::gpu::gl::GLBackend::do_restoreContextViewCorrection), + (&::gpu::gl::GLBackend::do_setContextMirrorViewCorrection), (&::gpu::gl::GLBackend::do_disableContextStereo), (&::gpu::gl::GLBackend::do_restoreContextStereo), @@ -619,6 +620,10 @@ void GLBackend::do_restoreContextViewCorrection(const Batch& batch, size_t param _transform._viewCorrectionEnabled = true; } +void GLBackend::do_setContextMirrorViewCorrection(const Batch& batch, size_t paramOffset) { + _transform._mirrorViewCorrection = batch._params[paramOffset + 1]._uint != 0; +} + void GLBackend::do_disableContextStereo(const Batch& batch, size_t paramOffset) { } diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index 2947649ce7..6e8af35037 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -211,6 +211,7 @@ public: 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_setContextMirrorViewCorrection(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; @@ -433,6 +434,7 @@ protected: Transform _view; CameraCorrection _correction; bool _viewCorrectionEnabled{ true }; + bool _mirrorViewCorrection{ false }; Mat4 _projection; Vec4i _viewport{ 0, 0, 1, 1 }; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp index 67ab502b6b..03c2b17a0e 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp @@ -111,7 +111,11 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo if (_viewIsCamera && (_viewCorrectionEnabled && _correction.correction != glm::mat4())) { // FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out? Transform result; - _view.mult(result, _view, _correction.correctionInverse); + glm::mat4 correction = _correction.correctionInverse; + if (_mirrorViewCorrection) { + correction = glm::scale(glm::mat4(), glm::vec3(-1.0f, 1.0f, 1.0f)) * correction; + } + _view.mult(result, _view, correction); if (_skybox) { result.setTranslation(vec3()); } diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index e6217cc600..a41f586d74 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -464,6 +464,12 @@ void Batch::restoreContextViewCorrection() { ADD_COMMAND(restoreContextViewCorrection); } +void Batch::setContextMirrorViewCorrection(bool shouldMirror) { + ADD_COMMAND(setContextMirrorViewCorrection); + uint mirrorFlag = shouldMirror ? 1 : 0; + _params.emplace_back(mirrorFlag); +} + void Batch::disableContextStereo() { ADD_COMMAND(disableContextStereo); } diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 0a438ea148..f89dd3ea90 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -239,6 +239,7 @@ public: void disableContextViewCorrection(); void restoreContextViewCorrection(); + void setContextMirrorViewCorrection(bool shouldMirror); void disableContextStereo(); void restoreContextStereo(); @@ -340,6 +341,7 @@ public: COMMAND_disableContextViewCorrection, COMMAND_restoreContextViewCorrection, + COMMAND_setContextMirrorViewCorrection, COMMAND_disableContextStereo, COMMAND_restoreContextStereo, diff --git a/libraries/gpu/src/gpu/FrameIOKeys.h b/libraries/gpu/src/gpu/FrameIOKeys.h index 1a98d0decd..2d88158afb 100644 --- a/libraries/gpu/src/gpu/FrameIOKeys.h +++ b/libraries/gpu/src/gpu/FrameIOKeys.h @@ -181,6 +181,7 @@ constexpr const char* COMMAND_NAMES[] = { "disableContextViewCorrection", "restoreContextViewCorrection", + "setContextMirrorViewCorrection", "disableContextStereo", "restoreContextStereo", diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index 7647a1b8d2..263af717f5 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -307,6 +307,11 @@ public: args->_ignoreItem = portalExitID != Item::INVALID_ITEM_ID ? portalExitID : mirror.id; args->_mirrorDepth = _depth; + gpu::doInBatch("SetupMirrorTask::run", args->_context, [&](gpu::Batch& batch) { + bool shouldMirror = _depth % 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(); @@ -360,6 +365,11 @@ public: gpu::doInBatch("DrawMirrorTask::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; + if (cachedArgs) { + bool shouldMirror = cachedArgs->_mirrorDepth % 2 == (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); + batch.setContextMirrorViewCorrection(shouldMirror); + } + batch.setFramebuffer(framebuffer); batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index ff28925b96..77e43fe829 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -146,6 +146,7 @@ const GROUPS = [ label: "Portal Exit", type: "string", propertyID: "portalExitID", + showPropertyRule: { "mirrorMode": "portal" }, } ] }, From 16341312faaf2367613b7b6753da996b04dc912e Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Wed, 6 Dec 2023 15:59:36 -0800 Subject: [PATCH 012/109] let's try this?? --- .../gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp | 2 +- .../entityProperties/html/js/entityProperties.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp index 03c2b17a0e..b27175a545 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp @@ -113,7 +113,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo Transform result; glm::mat4 correction = _correction.correctionInverse; if (_mirrorViewCorrection) { - correction = glm::scale(glm::mat4(), glm::vec3(-1.0f, 1.0f, 1.0f)) * correction; + correction = correction * glm::scale(glm::mat4(), glm::vec3(-1.0f, 1.0f, 1.0f)); } _view.mult(result, _view, correction); if (_skybox) { diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 77e43fe829..b509168262 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -127,11 +127,6 @@ const GROUPS = [ }, propertyID: "billboardMode", }, - { - label: "Render With Zones", - type: "multipleZonesSelection", - propertyID: "renderWithZones", - }, { label: "Mirror Mode", type: "dropdown", @@ -147,6 +142,11 @@ const GROUPS = [ type: "string", propertyID: "portalExitID", showPropertyRule: { "mirrorMode": "portal" }, + }, + { + label: "Render With Zones", + type: "multipleZonesSelection", + propertyID: "renderWithZones", } ] }, From 4e5ded0ba963d4cfcbec6c7a73f7038c0bb35b57 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Tue, 12 Dec 2023 15:18:04 -0800 Subject: [PATCH 013/109] plz --- libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp | 5 +++++ .../gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp | 9 +++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index 421fef4334..bb79c9073f 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -621,7 +621,12 @@ void GLBackend::do_restoreContextViewCorrection(const Batch& batch, size_t param } void GLBackend::do_setContextMirrorViewCorrection(const Batch& batch, size_t paramOffset) { + bool prevMirrorViewCorrection = _transform._mirrorViewCorrection; _transform._mirrorViewCorrection = batch._params[paramOffset + 1]._uint != 0; + if (prevMirrorViewCorrection != _transform._mirrorViewCorrection) { + static const mat4 flipXScale = glm::scale(glm::mat4(), glm::vec3(-1.0f, 1.0f, 1.0f)); + setCameraCorrection(_transform._correction.correction * flipXScale, _transform._correction.prevView); + } } void GLBackend::do_disableContextStereo(const Batch& batch, size_t paramOffset) { diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp index b27175a545..d6ccdb52c9 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp @@ -108,14 +108,11 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo if (_invalidView) { // Apply the correction - if (_viewIsCamera && (_viewCorrectionEnabled && _correction.correction != glm::mat4())) { + static const mat4 flipXScale = glm::scale(glm::mat4(), glm::vec3(-1.0f, 1.0f, 1.0f)); + if (_viewIsCamera && (_viewCorrectionEnabled && _correction.correction != (_mirrorViewCorrection ? flipXScale : glm::mat4()))) { // FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out? Transform result; - glm::mat4 correction = _correction.correctionInverse; - if (_mirrorViewCorrection) { - correction = correction * glm::scale(glm::mat4(), glm::vec3(-1.0f, 1.0f, 1.0f)); - } - _view.mult(result, _view, correction); + _view.mult(result, _view, _correction.correctionInverse); if (_skybox) { result.setTranslation(vec3()); } From e0f3657032c77386582fbb24580116d7736562a4 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Wed, 13 Dec 2023 21:58:48 -0800 Subject: [PATCH 014/109] promising --- .../gpu-gl-common/src/gpu/gl/GLBackend.cpp | 23 ++++++++++++++++--- .../gpu-gl-common/src/gpu/gl/GLBackend.h | 2 ++ .../src/gpu/gl/GLBackendTransform.cpp | 3 +-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index bb79c9073f..f4af949558 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -349,6 +349,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) { case Batch::COMMAND_setViewTransform: case Batch::COMMAND_setProjectionTransform: case Batch::COMMAND_setProjectionJitter: + case Batch::COMMAND_setContextMirrorViewCorrection: { CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); @@ -386,6 +387,7 @@ void GLBackend::renderPassDraw(const Batch& batch) { case Batch::COMMAND_setModelTransform: case Batch::COMMAND_setViewTransform: case Batch::COMMAND_setProjectionTransform: + case Batch::COMMAND_setContextMirrorViewCorrection: break; case Batch::COMMAND_draw: @@ -411,6 +413,7 @@ void GLBackend::renderPassDraw(const Batch& batch) { //case Batch::COMMAND_setModelTransform: //case Batch::COMMAND_setViewTransform: //case Batch::COMMAND_setProjectionTransform: + //case Batch::COMMAND_setContextMirrorViewCorrection: case Batch::COMMAND_setProjectionJitter: case Batch::COMMAND_setViewportTransform: case Batch::COMMAND_setDepthRangeTransform: @@ -623,9 +626,23 @@ void GLBackend::do_restoreContextViewCorrection(const Batch& batch, size_t param void GLBackend::do_setContextMirrorViewCorrection(const Batch& batch, size_t paramOffset) { bool prevMirrorViewCorrection = _transform._mirrorViewCorrection; _transform._mirrorViewCorrection = batch._params[paramOffset + 1]._uint != 0; - if (prevMirrorViewCorrection != _transform._mirrorViewCorrection) { - static const mat4 flipXScale = glm::scale(glm::mat4(), glm::vec3(-1.0f, 1.0f, 1.0f)); - setCameraCorrection(_transform._correction.correction * flipXScale, _transform._correction.prevView); + + if (_transform._correction.correction != glm::mat4()) { + // If we were previously not flipped, take this opportunity to save our flipped and unflipped matrices. + if (!prevMirrorViewCorrection) { + _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); + } + + if (prevMirrorViewCorrection != _transform._mirrorViewCorrection) { + setCameraCorrection(_transform._mirrorViewCorrection ? _transform._flippedCorrection : _transform._unflippedCorrection, _transform._correction.prevView); + _transform._invalidView = true; + } } } diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index 6e8af35037..fdc0bf983c 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -434,6 +434,8 @@ protected: Transform _view; CameraCorrection _correction; bool _viewCorrectionEnabled{ true }; + mat4 _unflippedCorrection; + mat4 _flippedCorrection; bool _mirrorViewCorrection{ false }; Mat4 _projection; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp index d6ccdb52c9..67ab502b6b 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp @@ -108,8 +108,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo if (_invalidView) { // Apply the correction - static const mat4 flipXScale = glm::scale(glm::mat4(), glm::vec3(-1.0f, 1.0f, 1.0f)); - if (_viewIsCamera && (_viewCorrectionEnabled && _correction.correction != (_mirrorViewCorrection ? flipXScale : glm::mat4()))) { + if (_viewIsCamera && (_viewCorrectionEnabled && _correction.correction != glm::mat4())) { // FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out? Transform result; _view.mult(result, _view, _correction.correctionInverse); From 5159367b4c29713d3b3cdad83ec2718c3cc110b0 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Mon, 18 Dec 2023 15:42:17 -0800 Subject: [PATCH 015/109] fix paramsOffset and view flipping --- .../display-plugins/OpenGLDisplayPlugin.cpp | 4 +- .../gpu-gl-common/src/gpu/gl/GLBackend.cpp | 42 +++++++++---------- .../gpu-gl-common/src/gpu/gl/GLBackend.h | 2 +- libraries/gpu/src/gpu/Context.h | 2 +- .../src/ToneMapAndResampleTask.cpp | 2 +- libraries/render/src/render/ResampleTask.cpp | 2 +- tools/gpu-frame-player/src/RenderThread.cpp | 2 +- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 03a463c82a..47f22dfaee 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -357,7 +357,7 @@ void OpenGLDisplayPlugin::customizeContext() { auto presentThread = DependencyManager::get(); Q_ASSERT(thread() == presentThread->thread()); - getGLBackend()->setCameraCorrection(mat4(), mat4(), true); + getGLBackend()->setCameraCorrection(mat4(), mat4(), true, true); for (auto& cursorValue : _cursorsData) { auto& cursorData = cursorValue.second; @@ -701,7 +701,7 @@ void OpenGLDisplayPlugin::present(const std::shared_ptr& if (_currentFrame) { auto correction = getViewCorrection(); - getGLBackend()->setCameraCorrection(correction, _prevRenderView); + getGLBackend()->setCameraCorrection(correction, _prevRenderView, true); _prevRenderView = correction * _currentFrame->view; { withPresentThreadLock([&] { diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index f4af949558..c0116274ee 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -387,7 +387,6 @@ void GLBackend::renderPassDraw(const Batch& batch) { case Batch::COMMAND_setModelTransform: case Batch::COMMAND_setViewTransform: case Batch::COMMAND_setProjectionTransform: - case Batch::COMMAND_setContextMirrorViewCorrection: break; case Batch::COMMAND_draw: @@ -413,10 +412,10 @@ void GLBackend::renderPassDraw(const Batch& batch) { //case Batch::COMMAND_setModelTransform: //case Batch::COMMAND_setViewTransform: //case Batch::COMMAND_setProjectionTransform: - //case Batch::COMMAND_setContextMirrorViewCorrection: case Batch::COMMAND_setProjectionJitter: case Batch::COMMAND_setViewportTransform: case Batch::COMMAND_setDepthRangeTransform: + case Batch::COMMAND_setContextMirrorViewCorrection: { PROFILE_RANGE(render_gpu_gl_detail, "transform"); CommandCall call = _commandCalls[(*command)]; @@ -625,24 +624,11 @@ void GLBackend::do_restoreContextViewCorrection(const Batch& batch, size_t param void GLBackend::do_setContextMirrorViewCorrection(const Batch& batch, size_t paramOffset) { bool prevMirrorViewCorrection = _transform._mirrorViewCorrection; - _transform._mirrorViewCorrection = batch._params[paramOffset + 1]._uint != 0; + _transform._mirrorViewCorrection = batch._params[paramOffset]._uint != 0; if (_transform._correction.correction != glm::mat4()) { - // If we were previously not flipped, take this opportunity to save our flipped and unflipped matrices. - if (!prevMirrorViewCorrection) { - _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); - } - - if (prevMirrorViewCorrection != _transform._mirrorViewCorrection) { - setCameraCorrection(_transform._mirrorViewCorrection ? _transform._flippedCorrection : _transform._unflippedCorrection, _transform._correction.prevView); - _transform._invalidView = true; - } + setCameraCorrection(_transform._mirrorViewCorrection ? _transform._flippedCorrection : _transform._unflippedCorrection, _transform._correction.prevView, false); + _transform._invalidView = true; } } @@ -1024,15 +1010,29 @@ void GLBackend::recycle() const { _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 invPrevView = glm::inverse(prevRenderView); _transform._correction.prevView = (reset ? Mat4() : prevRenderView); _transform._correction.prevViewInverse = (reset ? Mat4() : invPrevView); _transform._correction.correction = correction; _transform._correction.correctionInverse = invCorrection; - _pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction); - _pipeline._cameraCorrectionBuffer._buffer->flush(); + + if (!_inRenderTransferPass) { + _pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction); + _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) { diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index fdc0bf983c..5545858877 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -121,7 +121,7 @@ public: // Shutdown rendering and persist any required resources 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; // This call synchronize the Full Backend cache with the current GLState diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 1946f447f8..ebc81f14e9 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -66,7 +66,7 @@ public: virtual void syncProgram(const gpu::ShaderPointer& program) = 0; virtual void recycle() const = 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; diff --git a/libraries/render-utils/src/ToneMapAndResampleTask.cpp b/libraries/render-utils/src/ToneMapAndResampleTask.cpp index 6ac5142e45..36a08427cb 100644 --- a/libraries/render-utils/src/ToneMapAndResampleTask.cpp +++ b/libraries/render-utils/src/ToneMapAndResampleTask.cpp @@ -96,7 +96,7 @@ void ToneMapAndResample::run(const RenderContextPointer& renderContext, const In batch.setViewportTransform(destViewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); - bool shouldMirror = _depth % 2 == (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); + bool shouldMirror = _depth >= (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); batch.setPipeline(shouldMirror ? _mirrorPipeline : _pipeline); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(srcBufferSize, args->_viewport)); diff --git a/libraries/render/src/render/ResampleTask.cpp b/libraries/render/src/render/ResampleTask.cpp index 5206767bd2..af82d249cf 100644 --- a/libraries/render/src/render/ResampleTask.cpp +++ b/libraries/render/src/render/ResampleTask.cpp @@ -167,7 +167,7 @@ void UpsampleToBlitFramebuffer::run(const RenderContextPointer& renderContext, c batch.setViewportTransform(viewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); - bool shouldMirror = _depth % 2 == (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); + bool shouldMirror = _depth >= (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); batch.setPipeline(shouldMirror ? _mirrorPipeline : _pipeline); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport)); diff --git a/tools/gpu-frame-player/src/RenderThread.cpp b/tools/gpu-frame-player/src/RenderThread.cpp index 0089c1577b..de39dacdea 100644 --- a/tools/gpu-frame-player/src/RenderThread.cpp +++ b/tools/gpu-frame-player/src/RenderThread.cpp @@ -122,7 +122,7 @@ void RenderThread::renderFrame(gpu::FramePointer& frame) { if (_correction != glm::mat4()) { std::unique_lock lock(_frameLock); if (_correction != glm::mat4()) { - _backend->setCameraCorrection(_correction, _activeFrame->view); + _backend->setCameraCorrection(_correction, _activeFrame->view, true); //_prevRenderView = _correction * _activeFrame->view; } } From c608e82f22a5b147ef30f469fc874ced8d8ce859 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Thu, 21 Dec 2023 15:41:16 -0800 Subject: [PATCH 016/109] portals shouldn't flip --- libraries/render-utils/src/RenderCommonTask.cpp | 9 ++++++--- libraries/render-utils/src/ToneMapAndResampleTask.cpp | 2 +- libraries/render/src/render/Args.h | 1 + libraries/render/src/render/ResampleTask.cpp | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index 263af717f5..a509ede437 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -299,6 +299,7 @@ public: _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); @@ -306,9 +307,10 @@ public: 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 = _depth % 2 == (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); + bool shouldMirror = args->_numMirrorFlips % 2 == (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); batch.setContextMirrorViewCorrection(shouldMirror); }); @@ -366,7 +368,7 @@ public: args->_batch = &batch; if (cachedArgs) { - bool shouldMirror = cachedArgs->_mirrorDepth % 2 == (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); + bool shouldMirror = cachedArgs->_numMirrorFlips % 2 == (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); batch.setContextMirrorViewCorrection(shouldMirror); } @@ -390,11 +392,12 @@ public: args->_batch = nullptr; }); - // Restore the blit framebuffer after we've sampled from it 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; } } diff --git a/libraries/render-utils/src/ToneMapAndResampleTask.cpp b/libraries/render-utils/src/ToneMapAndResampleTask.cpp index 36a08427cb..1ea9deb1fa 100644 --- a/libraries/render-utils/src/ToneMapAndResampleTask.cpp +++ b/libraries/render-utils/src/ToneMapAndResampleTask.cpp @@ -96,7 +96,7 @@ void ToneMapAndResample::run(const RenderContextPointer& renderContext, const In batch.setViewportTransform(destViewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); - bool shouldMirror = _depth >= (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); + bool shouldMirror = args->_numMirrorFlips >= (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); batch.setPipeline(shouldMirror ? _mirrorPipeline : _pipeline); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(srcBufferSize, args->_viewport)); diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index 60daa35729..fb7d698672 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -162,6 +162,7 @@ namespace render { ItemID _ignoreItem { 0 }; size_t _mirrorDepth { 0 }; + size_t _numMirrorFlips { 0 }; }; } diff --git a/libraries/render/src/render/ResampleTask.cpp b/libraries/render/src/render/ResampleTask.cpp index af82d249cf..e77dda600c 100644 --- a/libraries/render/src/render/ResampleTask.cpp +++ b/libraries/render/src/render/ResampleTask.cpp @@ -167,7 +167,7 @@ void UpsampleToBlitFramebuffer::run(const RenderContextPointer& renderContext, c batch.setViewportTransform(viewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); - bool shouldMirror = _depth >= (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); + bool shouldMirror = args->_numMirrorFlips >= (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); batch.setPipeline(shouldMirror ? _mirrorPipeline : _pipeline); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport)); From f99a2fa4056ee58a840dc4cfbe18473b8b79b962 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 30 Dec 2023 13:41:56 -0800 Subject: [PATCH 017/109] break when tag found --- libraries/entities/src/EntityTreeElement.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 030c3082c2..32c791da33 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -618,6 +618,7 @@ void EntityTreeElement::evalEntitiesInSphereWithTags(const glm::vec3& position, for (const QString& entityTag : entityTags) { if (lowerTag == entityTag.toLower()) { found = true; + break; } } if (!found) { From 446207a4b4303858eae8287f0b2690c210667c13 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Wed, 3 Jan 2024 16:33:45 -0800 Subject: [PATCH 018/109] fix portal view calculation --- libraries/entities-renderer/src/RenderableEntityItem.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index ef91c1f034..09bbfb9284 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -268,6 +268,10 @@ ItemID EntityRenderer::computeMirrorViewOperator(ViewFrustum& viewFrustum, const 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 @@ -276,6 +280,10 @@ ItemID EntityRenderer::computeMirrorViewOperator(ViewFrustum& viewFrustum, const 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); From c6e4e5de37ce4190d3212bb133ec1bb5b9b1adef Mon Sep 17 00:00:00 2001 From: ksuprynowicz Date: Sat, 13 Jan 2024 11:18:08 +0100 Subject: [PATCH 019/109] Revert "Mirrors + Portals" --- interface/src/Application.cpp | 1 - interface/src/SecondaryCamera.cpp | 3 +- interface/src/avatar/MyAvatar.cpp | 3 +- .../scripting/RenderScriptingInterface.cpp | 24 +-- .../src/avatars-renderer/Avatar.cpp | 2 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 4 +- .../src/RenderableEntityItem.cpp | 114 +------------ .../src/RenderableEntityItem.h | 12 +- .../src/RenderableGizmoEntityItem.cpp | 3 +- .../src/RenderableGridEntityItem.cpp | 3 +- .../src/RenderableImageEntityItem.cpp | 9 +- .../src/RenderableLineEntityItem.cpp | 3 +- .../src/RenderableMaterialEntityItem.cpp | 3 +- .../src/RenderableModelEntityItem.cpp | 16 -- .../src/RenderableModelEntityItem.h | 2 - .../src/RenderablePolyLineEntityItem.cpp | 3 +- .../src/RenderablePolyVoxEntityItem.cpp | 3 +- .../src/RenderableShapeEntityItem.cpp | 5 +- .../src/RenderableTextEntityItem.cpp | 38 +---- .../src/RenderableTextEntityItem.h | 2 - .../src/RenderableWebEntityItem.cpp | 3 +- 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 - .../gpu-gl-common/src/gpu/gl/GLBackend.cpp | 33 +--- .../gpu-gl-common/src/gpu/gl/GLBackend.h | 6 +- libraries/gpu/src/gpu/Batch.cpp | 6 - libraries/gpu/src/gpu/Batch.h | 2 - libraries/gpu/src/gpu/Context.h | 2 +- libraries/gpu/src/gpu/FrameIOKeys.h | 1 - .../graphics/src/graphics/ShaderConstants.h | 2 - libraries/networking/src/udt/PacketHeaders.h | 1 - libraries/octree/src/OctreePacketData.h | 2 - .../src/CauterizedMeshPartPayload.cpp | 7 +- .../src/CauterizedMeshPartPayload.h | 2 +- .../src/DeferredLightingEffect.cpp | 1 - .../render-utils/src/DeferredLightingEffect.h | 4 +- .../render-utils/src/HighlightEffect.cpp | 7 +- .../render-utils/src/MeshPartPayload.cpp | 32 +--- libraries/render-utils/src/MeshPartPayload.h | 9 +- libraries/render-utils/src/Model.cpp | 46 +---- libraries/render-utils/src/Model.h | 9 - .../render-utils/src/RenderCommonTask.cpp | 159 +----------------- libraries/render-utils/src/RenderCommonTask.h | 17 +- .../render-utils/src/RenderDeferredTask.cpp | 33 ++-- .../render-utils/src/RenderDeferredTask.h | 2 +- .../render-utils/src/RenderForwardTask.cpp | 26 +-- .../render-utils/src/RenderForwardTask.h | 2 +- .../render-utils/src/RenderHUDLayerTask.cpp | 4 +- .../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/TextRenderer3D.cpp | 11 +- libraries/render-utils/src/TextRenderer3D.h | 9 +- .../src/ToneMapAndResampleTask.cpp | 6 +- .../render-utils/src/ToneMapAndResampleTask.h | 7 +- libraries/render-utils/src/model.slf | 22 +-- .../render-utils/src/render-utils/model.slp | 2 +- .../src/render-utils/sdf_text3D.slp | 2 +- libraries/render-utils/src/sdf_text3D.slf | 11 -- libraries/render-utils/src/text/Font.cpp | 46 +++-- libraries/render-utils/src/text/Font.h | 28 +-- libraries/render/src/render/Args.h | 4 - libraries/render/src/render/CullTask.cpp | 85 +++++++++- libraries/render/src/render/CullTask.h | 23 +++ 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/render/src/render/ResampleTask.cpp | 3 +- libraries/render/src/render/ResampleTask.h | 4 +- libraries/shared/src/MirrorMode.cpp | 36 ---- libraries/shared/src/MirrorMode.h | 51 ------ libraries/shared/src/ViewFrustum.cpp | 51 ++---- libraries/shared/src/ViewFrustum.h | 5 +- .../create/assets/data/createAppTooltips.json | 12 +- .../html/js/entityProperties.js | 16 -- tools/gpu-frame-player/src/RenderThread.cpp | 2 +- 81 files changed, 263 insertions(+), 1047 deletions(-) delete mode 100644 libraries/shared/src/MirrorMode.cpp delete mode 100644 libraries/shared/src/MirrorMode.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 931c41703b..a9e4bedbbd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2479,7 +2479,6 @@ Application::Application( copyViewFrustum(viewFrustum); return viewFrustum.getPosition(); }); - MirrorModeHelpers::setComputeMirrorViewOperator(EntityRenderer::computeMirrorViewOperator); DependencyManager::get()->setKickConfirmationOperator([this] (const QUuid& nodeID, unsigned int banFlags) { userKickConfirmation(nodeID, banFlags); }); diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index 130b8c77ea..704d7963e7 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(); + _cachedArgsPointer = std::make_shared(_cachedArgs); _attachedEntityPropertyFlags += PROP_POSITION; _attachedEntityPropertyFlags += PROP_ROTATION; } @@ -203,6 +203,7 @@ public: } protected: + RenderArgs _cachedArgs; RenderArgsPointer _cachedArgsPointer; private: diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index bb6fbcd899..827388aa1c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3530,9 +3530,8 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON_LOOK_AT || qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON; bool overrideAnim = _skeletonModel ? _skeletonModel->getRig().isPlayingOverrideAnimation() : false; - bool isInMirror = renderArgs->_mirrorDepth > 0; bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition()); - return !defaultMode || isInMirror || (!firstPerson && !insideHead) || (overrideAnim && !insideHead); + return !defaultMode || (!firstPerson && !insideHead) || (overrideAnim && !insideHead); } void MyAvatar::setRotationRecenterFilterLength(float length) { diff --git a/interface/src/scripting/RenderScriptingInterface.cpp b/interface/src/scripting/RenderScriptingInterface.cpp index 47f772b4bc..12814aa6b6 100644 --- a/interface/src/scripting/RenderScriptingInterface.cpp +++ b/interface/src/scripting/RenderScriptingInterface.cpp @@ -9,7 +9,6 @@ // #include "RenderScriptingInterface.h" -#include #include #include "LightingModel.h" @@ -80,35 +79,14 @@ 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); - QString configName = "RenderMainView.DeferredForwardSwitch"; - auto config = dynamic_cast(qApp->getRenderEngine()->getConfiguration()->getConfig(configName)); + auto config = dynamic_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("RenderMainView.DeferredForwardSwitch")); if (config) { config->setBranch((int)renderMethod); - - recursivelyUpdateMirrorRenderMethods(configName + (renderMethod == RenderMethod::FORWARD ? ".RenderForwardTask" : ".RenderShadowsAndDeferredTask.RenderDeferredTask"), - (int)renderMethod, 0); } }); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 790b45843c..b2d6a6260b 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1120,7 +1120,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const batch.setModelTransform(textTransform); { PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderText"); - displayNameRenderer->draw(batch, { nameUTF8.data(), textColor, { text_x, -text_y }, glm::vec2(-1.0f), forward }); + displayNameRenderer->draw(batch, text_x, -text_y, glm::vec2(-1.0f), nameUTF8.data(), textColor, true, forward); } } } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 47f22dfaee..03a463c82a 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -357,7 +357,7 @@ void OpenGLDisplayPlugin::customizeContext() { auto presentThread = DependencyManager::get(); Q_ASSERT(thread() == presentThread->thread()); - getGLBackend()->setCameraCorrection(mat4(), mat4(), true, true); + getGLBackend()->setCameraCorrection(mat4(), mat4(), true); for (auto& cursorValue : _cursorsData) { auto& cursorData = cursorValue.second; @@ -701,7 +701,7 @@ void OpenGLDisplayPlugin::present(const std::shared_ptr& if (_currentFrame) { auto correction = getViewCorrection(); - getGLBackend()->setCameraCorrection(correction, _prevRenderView, true); + getGLBackend()->setCameraCorrection(correction, _prevRenderView); _prevRenderView = correction * _currentFrame->view; { withPresentThreadLock([&] { diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 09bbfb9284..212baa6634 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -12,7 +12,6 @@ #include "RenderableEntityItem.h" -#include #include #include "RenderableShapeEntityItem.h" @@ -193,10 +192,6 @@ ItemKey EntityRenderer::getKey() { builder.withSubMetaCulled(); } - if (_mirrorMode == MirrorMode::MIRROR || (_mirrorMode == MirrorMode::PORTAL && !_portalExitID.isNull())) { - builder.withMirror(); - } - if (!_visible) { builder.withInvisible(); } @@ -226,113 +221,12 @@ bool EntityRenderer::passesZoneOcclusionTest(const std::unordered_set& co 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(); - 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()->renderableIdForEntityId(portalExitID) : Item::INVALID_ITEM_ID; -} - void EntityRenderer::render(RenderArgs* args) { if (!isValidRenderItem()) { return; } - if (_visible && (!_cauterized || args->_renderMode != RenderArgs::RenderMode::DEFAULT_RENDER_MODE || args->_mirrorDepth > 0)) { + if (_visible && (args->_renderMode != RenderArgs::RenderMode::DEFAULT_RENDER_MODE || !_cauterized)) { doRender(args); } } @@ -560,8 +454,6 @@ 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(); @@ -613,10 +505,6 @@ graphics::MaterialPointer EntityRenderer::getTopMaterial() { } 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()) { return Pipeline::PROCEDURAL; } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 99dbffbc72..3caeef0713 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -58,13 +58,12 @@ public: enum class Pipeline { SIMPLE, MATERIAL, - PROCEDURAL, - MIRROR + PROCEDURAL }; virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName); virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName); virtual graphics::MaterialPointer getTopMaterial(); - Pipeline getPipelineType(const graphics::MultiMaterial& materials); + static Pipeline getPipelineType(const graphics::MultiMaterial& materials); virtual gpu::TexturePointer getTexture() { return nullptr; } virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); } @@ -75,9 +74,6 @@ 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; - 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: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } @@ -120,8 +116,6 @@ 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); } @@ -157,8 +151,6 @@ 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/RenderableGizmoEntityItem.cpp b/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp index 10ae144334..42df1e2888 100644 --- a/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp @@ -263,9 +263,8 @@ void GizmoEntityRenderer::doRender(RenderArgs* args) { 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, - usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition(), true)); + args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition(), true)); batch.setModelTransform(transform); Pipeline pipelineType = getPipelineType(materials); diff --git a/libraries/entities-renderer/src/RenderableGridEntityItem.cpp b/libraries/entities-renderer/src/RenderableGridEntityItem.cpp index 3f40218d46..e374fe29c0 100644 --- a/libraries/entities-renderer/src/RenderableGridEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableGridEntityItem.cpp @@ -103,9 +103,8 @@ void GridEntityRenderer::doRender(RenderArgs* args) { } else { 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, - usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); batch->setModelTransform(transform); auto minCorner = glm::vec2(-0.5f, -0.5f); diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index 66a5d0d609..9592a3e57f 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -147,9 +147,8 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { 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, - usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); float imageWidth = _texture->getWidth(); float imageHeight = _texture->getHeight(); @@ -199,10 +198,8 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { procedural->prepare(*batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(transparent)); } else if (pipelineType == Pipeline::SIMPLE) { batch->setResourceTexture(0, _texture->getGPUTexture()); - } else if (pipelineType == Pipeline::MATERIAL) { - if (RenderPipelines::bindMaterials(materials, *batch, args->_renderMode, args->_enableTexturing)) { - args->_details._materialSwitches++; - } + } else if (RenderPipelines::bindMaterials(materials, *batch, args->_renderMode, args->_enableTexturing)) { + args->_details._materialSwitches++; } DependencyManager::get()->renderQuad( diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index 1117c97c75..a36cdde212 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -47,9 +47,8 @@ void LineEntityRenderer::doRender(RenderArgs* args) { const auto& modelTransform = getModelTransform(); Transform transform = Transform(); 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, - usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); batch.setModelTransform(transform); if (_linePoints.size() > 1) { DependencyManager::get()->bindSimpleProgram(batch, false, false, false, false, true, diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index fe44c41094..b8f829f4ba 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -303,9 +303,8 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) { proceduralRender = true; } - bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, - usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); batch.setModelTransform(transform); if (!proceduralRender) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 6b83d87732..8ed3f84609 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1274,8 +1274,6 @@ 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()-> @@ -1355,8 +1353,6 @@ 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()) { @@ -1470,18 +1466,6 @@ 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 425d082f01..f394d389f5 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -170,8 +170,6 @@ 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/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 81f4c5fcb4..aca501985a 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -325,9 +325,8 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) { buildPipelines(); } - bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, - usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); batch.setModelTransform(transform); batch.setPipeline(_pipelines[{args->_renderMethod, isTransparent()}]); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 26091a1ed4..8331e016fd 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1860,9 +1860,8 @@ void PolyVoxEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); 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, - usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); Transform transform(glm::translate(_position) * rotation * _lastVoxelToLocalMatrix); batch.setModelTransform(transform); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 82350f54bf..a6fee03311 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -118,9 +118,8 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { 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, - usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition(), + args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition(), _shape < entity::Shape::Cube || _shape > entity::Shape::Icosahedron)); batch.setModelTransform(transform); @@ -158,7 +157,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { } } } else { - if (pipelineType == Pipeline::MATERIAL && RenderPipelines::bindMaterials(materials, batch, args->_renderMode, args->_enableTexturing)) { + if (RenderPipelines::bindMaterials(materials, batch, args->_renderMode, args->_enableTexturing)) { args->_details._materialSwitches++; } diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index a15e2839a4..2858e12afd 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -164,9 +164,8 @@ void TextEntityRenderer::doRender(RenderArgs* args) { transform = _renderTransform; }); - bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, - usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); batch.setModelTransform(transform); Pipeline pipelineType = getPipelineType(materials); @@ -181,7 +180,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { } auto geometryCache = DependencyManager::get(); - if (pipelineType == Pipeline::SIMPLE || pipelineType == Pipeline::MIRROR) { + if (pipelineType == Pipeline::SIMPLE) { geometryCache->renderQuad(batch, glm::vec2(-0.5f), glm::vec2(0.5f), backgroundColor, _geometryID); } else { geometryCache->renderQuad(batch, glm::vec2(-0.5f), glm::vec2(0.5f), glm::vec2(0.0f), glm::vec2(1.0f), backgroundColor, _geometryID); @@ -261,10 +260,6 @@ ItemKey entities::TextPayload::getKey() const { builder.withInvisible(); } - if (textRenderable->_mirrorMode == MirrorMode::MIRROR || (textRenderable->_mirrorMode == MirrorMode::PORTAL && !textRenderable->_portalExitID.isNull())) { - builder.withMirror(); - } - return builder; } } @@ -316,17 +311,6 @@ bool entities::TextPayload::passesZoneOcclusionTest(const std::unordered_set(); - if (entityTreeRenderer) { - auto renderable = entityTreeRenderer->renderableForEntityId(_entityID); - if (renderable) { - return renderable->computeMirrorView(viewFrustum); - } - } - return Item::INVALID_ITEM_ID; -} - void entities::TextPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("TextPayload::render"); Q_ASSERT(args->_batch); @@ -351,15 +335,12 @@ void entities::TextPayload::render(RenderArgs* args) { glm::vec3 dimensions; glm::vec4 textColor; - bool mirror; textRenderable->withReadLock([&] { transform = textRenderable->_renderTransform; dimensions = textRenderable->_dimensions; float fadeRatio = textRenderable->_isFading ? Interpolate::calculateFadeRatio(textRenderable->_fadeStartTime) : 1.0f; 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; @@ -371,9 +352,8 @@ void entities::TextPayload::render(RenderArgs* args) { return; } - bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), textRenderable->_billboardMode, - usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); float scale = textRenderable->_lineHeight / textRenderer->getFontSize(); transform.postTranslate(glm::vec3(-0.5, 0.5, 1.0f + EPSILON / dimensions.z)); @@ -381,8 +361,9 @@ void entities::TextPayload::render(RenderArgs* args) { batch.setModelTransform(transform); glm::vec2 bounds = glm::vec2(dimensions.x - (textRenderable->_leftMargin + textRenderable->_rightMargin), dimensions.y - (textRenderable->_topMargin + textRenderable->_bottomMargin)); - textRenderer->draw(batch, textRenderable->_font, { textRenderable->_text, textColor, effectColor, { textRenderable->_leftMargin / scale, -textRenderable->_topMargin / scale }, - bounds / scale, scale, textRenderable->_effectThickness, textRenderable->_effect, textRenderable->_alignment, textRenderable->_unlit, forward, mirror }); + textRenderer->draw(batch, textRenderable->_leftMargin / scale, -textRenderable->_topMargin / scale, bounds / scale, scale, + textRenderable->_text, textRenderable->_font, textColor, effectColor, textRenderable->_effectThickness, textRenderable->_effect, + textRenderable->_alignment, textRenderable->_unlit, forward); } namespace render { @@ -418,11 +399,4 @@ template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Poi return false; } -template <> ItemID payloadComputeMirrorView(const entities::TextPayload::Pointer& payload, ViewFrustum& viewFrustum) { - if (payload) { - return payload->computeMirrorView(viewFrustum); - } - return Item::INVALID_ITEM_ID; -} - } diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index f48bb8085f..8a18554dea 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -101,7 +101,6 @@ public: ShapeKey getShapeKey() const; void render(RenderArgs* args); bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const; - ItemID computeMirrorView(ViewFrustum& viewFrustum) const; protected: QUuid _entityID; @@ -118,7 +117,6 @@ namespace render { template <> const ShapeKey shapeGetShapeKey(const entities::TextPayload::Pointer& payload); template <> void payloadRender(const entities::TextPayload::Pointer& payload, RenderArgs* args); template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Pointer& payload, const std::unordered_set& containingZones); - template <> ItemID payloadComputeMirrorView(const entities::TextPayload::Pointer& payload, ViewFrustum& viewFrustum); } #endif // hifi_RenderableTextEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 77f6fe99f6..c98bfe7f63 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -320,9 +320,8 @@ void WebEntityRenderer::doRender(RenderArgs* args) { 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, - usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); batch.setModelTransform(transform); // Turn off jitter for these entities diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 34217d5bdf..b63f0d91ab 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -105,8 +105,6 @@ 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; @@ -307,8 +305,6 @@ 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()); @@ -885,8 +881,6 @@ 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); { @@ -1367,8 +1361,6 @@ 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); @@ -1507,8 +1499,6 @@ 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); @@ -3563,29 +3553,3 @@ 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 32f6dccf37..ec84b0ccb2 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -559,12 +559,6 @@ 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); @@ -746,9 +740,6 @@ 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 4d21e040f5..f543169401 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -424,23 +424,6 @@ 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; @@ -472,8 +455,6 @@ 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); @@ -844,11 +825,6 @@ 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} @@ -1640,8 +1616,6 @@ 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); @@ -2058,8 +2032,6 @@ 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); @@ -2346,8 +2318,6 @@ 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); @@ -2657,8 +2627,6 @@ 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, @@ -3122,8 +3090,6 @@ 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()); @@ -3602,8 +3568,6 @@ 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); @@ -3996,8 +3960,6 @@ void EntityItemProperties::markAllChanged() { _renderWithZonesChanged = true; _billboardModeChanged = true; _grab.markAllChanged(); - _mirrorModeChanged = true; - _portalExitIDChanged = true; // Physics _densityChanged = true; @@ -4397,12 +4359,6 @@ 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 3e07a75616..283d14c4cc 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -70,7 +70,6 @@ #include "GizmoType.h" #include "TextEffect.h" #include "TextAlignment.h" -#include "MirrorMode.h" class ScriptEngine; @@ -205,8 +204,6 @@ 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 c0cbb3f9a1..e0b5a04094 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -60,8 +60,6 @@ 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/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index c0116274ee..af39458f17 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -81,7 +81,6 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_disableContextViewCorrection), (&::gpu::gl::GLBackend::do_restoreContextViewCorrection), - (&::gpu::gl::GLBackend::do_setContextMirrorViewCorrection), (&::gpu::gl::GLBackend::do_disableContextStereo), (&::gpu::gl::GLBackend::do_restoreContextStereo), @@ -349,7 +348,6 @@ void GLBackend::renderPassTransfer(const Batch& batch) { case Batch::COMMAND_setViewTransform: case Batch::COMMAND_setProjectionTransform: case Batch::COMMAND_setProjectionJitter: - case Batch::COMMAND_setContextMirrorViewCorrection: { CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); @@ -415,7 +413,6 @@ void GLBackend::renderPassDraw(const Batch& batch) { case Batch::COMMAND_setProjectionJitter: case Batch::COMMAND_setViewportTransform: case Batch::COMMAND_setDepthRangeTransform: - case Batch::COMMAND_setContextMirrorViewCorrection: { PROFILE_RANGE(render_gpu_gl_detail, "transform"); CommandCall call = _commandCalls[(*command)]; @@ -622,16 +619,6 @@ void GLBackend::do_restoreContextViewCorrection(const Batch& batch, size_t param _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) { } @@ -1010,29 +997,15 @@ void GLBackend::recycle() const { _textureManagement._transferEngine->manageMemory(); } -void GLBackend::setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool primary, bool reset) { +void GLBackend::setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset) { auto invCorrection = glm::inverse(correction); auto invPrevView = glm::inverse(prevRenderView); _transform._correction.prevView = (reset ? Mat4() : prevRenderView); _transform._correction.prevViewInverse = (reset ? Mat4() : invPrevView); _transform._correction.correction = correction; _transform._correction.correctionInverse = invCorrection; - - if (!_inRenderTransferPass) { - _pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction); - _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; - } + _pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction); + _pipeline._cameraCorrectionBuffer._buffer->flush(); } void GLBackend::syncProgram(const gpu::ShaderPointer& program) { diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index 5545858877..2947649ce7 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -121,7 +121,7 @@ public: // Shutdown rendering and persist any required resources void shutdown() override; - void setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool primary, bool reset = false) override; + void setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset = false) override; void render(const Batch& batch) final override; // This call synchronize the Full Backend cache with the current GLState @@ -211,7 +211,6 @@ public: 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_setContextMirrorViewCorrection(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; @@ -434,9 +433,6 @@ protected: Transform _view; CameraCorrection _correction; bool _viewCorrectionEnabled{ true }; - mat4 _unflippedCorrection; - mat4 _flippedCorrection; - bool _mirrorViewCorrection{ false }; Mat4 _projection; Vec4i _viewport{ 0, 0, 1, 1 }; diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index a41f586d74..e6217cc600 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -464,12 +464,6 @@ void Batch::restoreContextViewCorrection() { ADD_COMMAND(restoreContextViewCorrection); } -void Batch::setContextMirrorViewCorrection(bool shouldMirror) { - ADD_COMMAND(setContextMirrorViewCorrection); - uint mirrorFlag = shouldMirror ? 1 : 0; - _params.emplace_back(mirrorFlag); -} - void Batch::disableContextStereo() { ADD_COMMAND(disableContextStereo); } diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index f89dd3ea90..0a438ea148 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -239,7 +239,6 @@ public: void disableContextViewCorrection(); void restoreContextViewCorrection(); - void setContextMirrorViewCorrection(bool shouldMirror); void disableContextStereo(); void restoreContextStereo(); @@ -341,7 +340,6 @@ public: COMMAND_disableContextViewCorrection, COMMAND_restoreContextViewCorrection, - COMMAND_setContextMirrorViewCorrection, COMMAND_disableContextStereo, COMMAND_restoreContextStereo, diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index ebc81f14e9..1946f447f8 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -66,7 +66,7 @@ public: virtual void syncProgram(const gpu::ShaderPointer& program) = 0; virtual void recycle() const = 0; virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0; - virtual void setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool primary, bool reset = false) {} + virtual void setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset = false) {} virtual bool supportedTextureFormat(const gpu::Element& format) = 0; diff --git a/libraries/gpu/src/gpu/FrameIOKeys.h b/libraries/gpu/src/gpu/FrameIOKeys.h index 2d88158afb..1a98d0decd 100644 --- a/libraries/gpu/src/gpu/FrameIOKeys.h +++ b/libraries/gpu/src/gpu/FrameIOKeys.h @@ -181,7 +181,6 @@ constexpr const char* COMMAND_NAMES[] = { "disableContextViewCorrection", "restoreContextViewCorrection", - "setContextMirrorViewCorrection", "disableContextStereo", "restoreContextStereo", diff --git a/libraries/graphics/src/graphics/ShaderConstants.h b/libraries/graphics/src/graphics/ShaderConstants.h index 8fd0df31f0..3a614d26cd 100644 --- a/libraries/graphics/src/graphics/ShaderConstants.h +++ b/libraries/graphics/src/graphics/ShaderConstants.h @@ -27,7 +27,6 @@ #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 @@ -60,7 +59,6 @@ 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 51739e0f77..3badfdf418 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -290,7 +290,6 @@ 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 30b826aaec..583f090942 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -42,7 +42,6 @@ #include "GizmoType.h" #include "TextEffect.h" #include "TextAlignment.h" -#include "MirrorMode.h" #include "OctreeConstants.h" #include "OctreeElement.h" @@ -281,7 +280,6 @@ 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/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index 78652bfb09..dc1ffb7b67 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -71,15 +71,14 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(const Transform _cauterizedTransform = renderTransform; } -void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode, size_t mirrorDepth) const { - bool useCauterizedMesh = _enableCauterization && (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) && - mirrorDepth == 0; +void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode) const { + bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) && _enableCauterization; if (useCauterizedMesh) { if (_cauterizedClusterBuffer) { batch.setUniformBuffer(graphics::slot::buffer::Skinning, _cauterizedClusterBuffer); } batch.setModelTransform(_cauterizedTransform); } else { - ModelMeshPartPayload::bindTransform(batch, transform, renderMode, mirrorDepth); + ModelMeshPartPayload::bindTransform(batch, transform, renderMode); } } diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index cef7b6d9b5..430f41fc08 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -25,7 +25,7 @@ public: void updateTransformForCauterizedMesh(const Transform& modelTransform, const Model::MeshState& meshState, bool useDualQuaternionSkinning); - void bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode, size_t mirrorDepth) const override; + void bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode) const override; void setEnableCauterization(bool enableCauterization) { _enableCauterization = enableCauterization; } diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 3eb5924d19..8d7fc345ac 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -286,7 +286,6 @@ void PrepareDeferred::run(const RenderContextPointer& renderContext, const Input outputs.edit0() = _deferredFramebuffer; outputs.edit1() = _deferredFramebuffer->getLightingFramebuffer(); - outputs.edit2() = _deferredFramebuffer->getDeferredFramebuffer(); gpu::doInBatch("PrepareDeferred::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 058e0a4cbf..4779376410 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -78,8 +78,8 @@ class PrepareDeferred { public: // Inputs: primaryFramebuffer and lightingModel using Inputs = render::VaryingSet2 ; - // Output: DeferredFramebuffer, LightingFramebuffer, the framebuffer to be used for mirrors (same as DeferredFramebuffer) - using Outputs = render::VaryingSet3; + // Output: DeferredFramebuffer, LightingFramebuffer + using Outputs = render::VaryingSet2; using JobModel = render::Job::ModelIO; diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index b4aced626d..5a8b09b018 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -506,9 +506,8 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren const auto jitter = inputs.getN(4); // Prepare the ShapePipeline - static ShapePlumberPointer shapePlumber = std::make_shared(); - static std::once_flag once; - std::call_once(once, [] { + auto shapePlumber = std::make_shared(); + { auto state = std::make_shared(); state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setColorWriteMask(false, false, false, false); @@ -516,7 +515,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 9adeb39e7c..8ba5be54e6 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -12,7 +12,6 @@ #include "MeshPartPayload.h" #include -#include #include #include #include @@ -189,7 +188,7 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { batch.setInputStream(0, _drawMesh->getVertexStream()); } -void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode, size_t mirrorDepth) const { +void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode) const { if (_clusterBuffer) { batch.setUniformBuffer(graphics::slot::buffer::Skinning, _clusterBuffer); } @@ -221,10 +220,6 @@ void ModelMeshPartPayload::updateKey(const render::ItemKey& key) { builder.withSubMetaCulled(); } - if (_mirrorMode == MirrorMode::MIRROR || (_mirrorMode == MirrorMode::PORTAL && !_portalExitID.isNull())) { - builder.withMirror(); - } - _itemKey = builder.build(); } @@ -304,9 +299,8 @@ Item::Bound ModelMeshPartPayload::getBound(RenderArgs* args) const { auto worldBound = _adjustedLocalBound; auto parentTransform = _parentTransform; if (args) { - bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; parentTransform.setRotation(BillboardModeHelpers::getBillboardRotation(parentTransform.getTranslation(), parentTransform.getRotation(), _billboardMode, - usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); } worldBound.transform(parentTransform); return worldBound; @@ -319,19 +313,18 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { void ModelMeshPartPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("ModelMeshPartPayload::render"); - if (!args || (_cauterized && args->_renderMode == RenderArgs::RenderMode::DEFAULT_RENDER_MODE && args->_mirrorDepth == 0)) { + if (!args || (args->_renderMode == RenderArgs::RenderMode::DEFAULT_RENDER_MODE && _cauterized)) { return; } gpu::Batch& batch = *(args->_batch); Transform transform = _parentTransform; - bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, - usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); Transform modelTransform = transform.worldTransform(_localTransform); - bindTransform(batch, modelTransform, args->_renderMode, args->_mirrorDepth); + bindTransform(batch, modelTransform, args->_renderMode); //Bind the index buffer and vertex buffer and Blend shapes if needed bindMesh(batch); @@ -353,7 +346,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { procedural->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f, _shapeKey.isDeformed(), _shapeKey.isDualQuatSkinned())); batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); - } else if (!_itemKey.isMirror()) { + } else { // apply material properties if (RenderPipelines::bindMaterials(_drawMaterials, batch, args->_renderMode, args->_enableTexturing)) { args->_details._materialSwitches++; @@ -384,12 +377,6 @@ bool ModelMeshPartPayload::passesZoneOcclusionTest(const std::unordered_set& blendshapeBuffers, const QVector& blendedMeshSizes) { if (_meshIndex < blendedMeshSizes.length() && blendedMeshSizes.at(_meshIndex) == _meshNumVertices) { auto blendshapeBuffer = blendshapeBuffers.find(_meshIndex); @@ -439,11 +426,4 @@ template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Poin } return false; } - -template <> ItemID payloadComputeMirrorView(const ModelMeshPartPayload::Pointer& payload, ViewFrustum& viewFrustum) { - if (payload) { - return payload->computeMirrorView(viewFrustum); - } - return Item::INVALID_ITEM_ID; -} } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 7e331a9497..1a3a898582 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -37,7 +37,7 @@ public: // ModelMeshPartPayload functions to perform render void bindMesh(gpu::Batch& batch); - virtual void bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode, size_t mirrorDepth) const; + virtual void bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode) const; void drawCall(gpu::Batch& batch) const; void updateKey(const render::ItemKey& key); @@ -58,10 +58,7 @@ 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; - render::ItemID computeMirrorView(ViewFrustum& viewFrustum) const; void addMaterial(graphics::MaterialLayer material) { _drawMaterials.push(material); } void removeMaterial(graphics::MaterialPointer material) { _drawMaterials.remove(material); } @@ -96,8 +93,6 @@ private: bool _cullWithParent { false }; QVector _renderWithZones; BillboardMode _billboardMode { BillboardMode::NONE }; - MirrorMode _mirrorMode { MirrorMode::NONE }; - QUuid _portalExitID; uint64_t _created; Transform _localTransform; @@ -112,8 +107,6 @@ 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 <> ItemID 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 7d622ab489..eabcabc7e5 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -232,8 +232,6 @@ 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++) { @@ -248,7 +246,7 @@ void Model::updateRenderItems() { transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, invalidatePayloadShapeKey, primitiveMode, billboardMode, renderItemKeyGlobalFlags, - cauterized, renderWithZones, mirrorMode, portalExitID](ModelMeshPartPayload& data) { + cauterized, renderWithZones](ModelMeshPartPayload& data) { if (useDualQuaternionSkinning) { data.updateClusterBuffer(meshState.clusterDualQuaternions); data.computeAdjustedLocalBound(meshState.clusterDualQuaternions); @@ -262,8 +260,6 @@ 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); }); @@ -1069,46 +1065,6 @@ 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); - data.updateKey(renderItemsKey); - }); - } - 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 63a96f7253..17b7df9b23 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -41,7 +41,6 @@ #include "Rig.h" #include "PrimitiveMode.h" #include "BillboardMode.h" -#include "MirrorMode.h" // Use dual quaternion skinning! // Must match define in Skinning.slh @@ -132,12 +131,6 @@ 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; @@ -510,8 +503,6 @@ 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 a509ede437..7cf7f1129f 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -13,10 +13,7 @@ #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; @@ -28,11 +25,9 @@ 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; @@ -50,14 +45,10 @@ 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) { - static std::once_flag once; - std::call_once(once, [] { - initForwardPipelines(*_shapePlumber); - }); + initForwardPipelines(*_shapePlumber); } void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs& inputs) { @@ -271,149 +262,3 @@ 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, 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() }; - size_t _mirrorIndex; - size_t _depth; - -}; - -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; - - 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(); -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) { - size_t nextDepth = depth + 1; - const auto setupOutput = task.addJob("SetupMirror" + std::to_string(mirrorIndex) + "Depth" + std::to_string(depth), inputs, mirrorIndex, nextDepth); - - task.addJob("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" + 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 cada3cb6ea..15d6ff9895 100644 --- a/libraries/render-utils/src/RenderCommonTask.h +++ b/libraries/render-utils/src/RenderCommonTask.h @@ -10,8 +10,6 @@ #define hifi_RenderCommonTask_h #include -#include - #include "LightStage.h" #include "HazeStage.h" #include "LightingModel.h" @@ -73,7 +71,7 @@ public: void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); protected: - static render::ShapePlumberPointer _shapePlumber; + render::ShapePlumberPointer _shapePlumber; int _maxDrawn; // initialized by Config bool _opaquePass { true }; }; @@ -154,17 +152,4 @@ 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 fd10452dfa..c506f22bc7 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -100,14 +100,11 @@ void RenderDeferredTask::configure(const Config& config) { preparePrimaryBufferConfig->setResolutionScale(config.resolutionScale); } -void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, size_t depth) { - static auto fadeEffect = DependencyManager::get(); +void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { + auto fadeEffect = DependencyManager::get(); // Prepare the ShapePipelines - static ShapePlumberPointer shapePlumber = std::make_shared(); - static std::once_flag once; - std::call_once(once, [] { - initDeferredPipelines(*shapePlumber, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); - }); + ShapePlumberPointer shapePlumber = std::make_shared(); + initDeferredPipelines(*shapePlumber, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); const auto& inputs = input.get(); @@ -119,7 +116,6 @@ 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]; @@ -143,6 +139,7 @@ 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"); @@ -157,7 +154,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto prepareDeferredOutputs = task.addJob("PrepareDeferred", prepareDeferredInputs); const auto deferredFramebuffer = prepareDeferredOutputs.getN(0); const auto lightingFramebuffer = prepareDeferredOutputs.getN(1); - const auto mirrorTargetFramebuffer = prepareDeferredOutputs.getN(2); // draw a stencil mask in hidden regions of the framebuffer. task.addJob("PrepareStencil", scaledPrimaryFramebuffer); @@ -166,13 +162,6 @@ 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, mirrorTargetFramebuffer, 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 @@ -245,7 +234,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren // Lighting Buffer ready for tone mapping const auto toneMappingInputs = ToneMapAndResample::Input(lightingFramebuffer, destFramebuffer).asVarying(); - const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs, depth); + const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs); // 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 @@ -409,12 +398,8 @@ 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 - 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); - }); + auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; + auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath, image::TextureUsage::STRICT_TEXTURE); const auto drawStatusInputs = DrawStatus::Input(opaques, jitter).asVarying(); task.addJob("DrawStatus", drawStatusInputs, DrawStatus(statusIconMap)); } @@ -422,6 +407,8 @@ 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 5fc9580981..969094488e 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, render::CullFunctor cullFunctor, size_t depth); + void build(JobModel& task, const render::Varying& input, render::Varying& output); private: }; diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 722ba2248c..e34db755b7 100644 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -34,8 +34,6 @@ #include "RenderCommonTask.h" #include "RenderHUDLayerTask.h" -#include "RenderViewTask.h" - namespace ru { using render_utils::slot::texture::Texture; using render_utils::slot::buffer::Buffer; @@ -68,16 +66,13 @@ void RenderForwardTask::configure(const Config& config) { preparePrimaryBufferConfig->setResolutionScale(config.resolutionScale); } -void RenderForwardTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, size_t depth) { +void RenderForwardTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { task.addJob("SetRenderMethodTask", render::Args::FORWARD); // Prepare the ShapePipelines auto fadeEffect = DependencyManager::get(); - static ShapePlumberPointer shapePlumber = std::make_shared(); - static std::once_flag once; - std::call_once(once, [] { - initForwardPipelines(*shapePlumber); - }); + ShapePlumberPointer shapePlumber = std::make_shared(); + initForwardPipelines(*shapePlumber); // Unpack inputs const auto& inputs = input.get(); @@ -91,7 +86,6 @@ 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]; @@ -113,6 +107,7 @@ 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"); @@ -130,16 +125,6 @@ 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); @@ -149,6 +134,7 @@ 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); @@ -173,7 +159,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto destFramebuffer = static_cast(nullptr); const auto toneMappingInputs = ToneMapAndResample::Input(resolvedFramebuffer, destFramebuffer).asVarying(); - const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs, depth); + const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs); // HUD Layer const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(toneMappedBuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying(); task.addJob("RenderHUDLayer", renderHUDLayerInputs); diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index de3a6dd205..6833e42449 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, render::CullFunctor cullFunctor, size_t depth); + void build(JobModel& task, const render::Varying& input, render::Varying& output); }; diff --git a/libraries/render-utils/src/RenderHUDLayerTask.cpp b/libraries/render-utils/src/RenderHUDLayerTask.cpp index 8fee3d57bc..743e59eebc 100644 --- a/libraries/render-utils/src/RenderHUDLayerTask.cpp +++ b/libraries/render-utils/src/RenderHUDLayerTask.cpp @@ -16,8 +16,8 @@ void CompositeHUD::run(const RenderContextPointer& renderContext, const gpu::Fra assert(renderContext->args); assert(renderContext->args->_context); - // We do not want to render HUD elements in secondary camera or mirrors - if (nsightActive() || renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE || renderContext->args->_mirrorDepth > 0) { + // We do not want to render HUD elements in secondary camera + if (nsightActive() || renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) { return; } diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index b5a88b0ba9..ed9fb326c4 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -42,7 +42,6 @@ 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, @@ -369,45 +368,6 @@ 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 9fabad5fd0..dbfa23a143 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -46,16 +46,15 @@ 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 - static ShapePlumberPointer shapePlumber = std::make_shared(); - static std::once_flag once; - std::call_once(once, [] { + ShapePlumberPointer shapePlumber = std::make_shared(); + { 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 b9320b6ad3..93a3ff2d67 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, size_t depth) { +void RenderShadowsAndDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { 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, cullFunctor, depth); + task.addJob("RenderDeferredTask", renderDeferredInput); } -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); +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); - task.addBranch("RenderForwardTask", 1, input, cullFunctor, depth); + task.addBranch("RenderForwardTask", 1, input); } -void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask, size_t depth) { +void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) { 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, depth); + task.addJob("DeferredForwardSwitch", deferredForwardIn, cullFunctor, tagBits, tagMask); #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 139d00125e..cdb56a2189 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, size_t depth); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask); }; @@ -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, size_t depth); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask); }; @@ -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, size_t depth = 0); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00); }; diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index 8ab1b8e0e9..76d8374fb7 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -40,18 +40,21 @@ float TextRenderer3D::getFontSize() const { return 0.0f; } -void TextRenderer3D::draw(gpu::Batch& batch, const Font::DrawProps& props) { +void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, + const QString& str, const glm::vec4& color, bool unlit, bool forward) { if (_font) { - _font->drawString(batch, _drawInfo, props); + _font->drawString(batch, _drawInfo, str, color, glm::vec3(0.0f), 0, TextEffect::NO_EFFECT, TextAlignment::LEFT, { x, y }, bounds, 1.0f, unlit, forward); } } -void TextRenderer3D::draw(gpu::Batch& batch, const QString& font, const Font::DrawProps& props) { +void TextRenderer3D::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) { if (font != _family) { _family = font; _font = Font::load(_family); } if (_font) { - _font->drawString(batch, _drawInfo, props); + _font->drawString(batch, _drawInfo, str, color, effectColor, effectThickness, effect, alignment, { x, y }, bounds, scale, unlit, forward); } } \ No newline at end of file diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h index 9db93e9dcc..edccf1429c 100644 --- a/libraries/render-utils/src/TextRenderer3D.h +++ b/libraries/render-utils/src/TextRenderer3D.h @@ -26,9 +26,12 @@ public: glm::vec2 computeExtent(const QString& str) const; float getFontSize() const; // Pixel size - - void draw(gpu::Batch& batch, const Font::DrawProps& props); - 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, + const QString& str, const glm::vec4& color, bool unlit, bool forward); + 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: TextRenderer3D(const char* family); diff --git a/libraries/render-utils/src/ToneMapAndResampleTask.cpp b/libraries/render-utils/src/ToneMapAndResampleTask.cpp index 1ea9deb1fa..10312f7f2e 100644 --- a/libraries/render-utils/src/ToneMapAndResampleTask.cpp +++ b/libraries/render-utils/src/ToneMapAndResampleTask.cpp @@ -25,10 +25,9 @@ using namespace shader::render_utils::program; gpu::PipelinePointer ToneMapAndResample::_pipeline; gpu::PipelinePointer ToneMapAndResample::_mirrorPipeline; -ToneMapAndResample::ToneMapAndResample(size_t depth) { +ToneMapAndResample::ToneMapAndResample() { Parameters parameters; _parametersBuffer = gpu::BufferView(std::make_shared(sizeof(Parameters), (const gpu::Byte*) ¶meters)); - _depth = depth; } void ToneMapAndResample::init() { @@ -96,8 +95,7 @@ void ToneMapAndResample::run(const RenderContextPointer& renderContext, const In batch.setViewportTransform(destViewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); - bool shouldMirror = args->_numMirrorFlips >= (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); - batch.setPipeline(shouldMirror ? _mirrorPipeline : _pipeline); + batch.setPipeline(args->_renderMode == RenderArgs::MIRROR_RENDER_MODE ? _mirrorPipeline : _pipeline); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(srcBufferSize, args->_viewport)); batch.setUniformBuffer(render_utils::slot::buffer::ToneMappingParams, _parametersBuffer); diff --git a/libraries/render-utils/src/ToneMapAndResampleTask.h b/libraries/render-utils/src/ToneMapAndResampleTask.h index 3a812cf445..1c7ef2cf48 100644 --- a/libraries/render-utils/src/ToneMapAndResampleTask.h +++ b/libraries/render-utils/src/ToneMapAndResampleTask.h @@ -49,9 +49,11 @@ signals: class ToneMapAndResample { public: - ToneMapAndResample(size_t depth); + ToneMapAndResample(); virtual ~ToneMapAndResample() {} + void render(RenderArgs* args, const gpu::TexturePointer& lightingBuffer, gpu::FramebufferPointer& destinationBuffer); + void setExposure(float exposure); float getExposure() const { return _parametersBuffer.get()._exposure; } @@ -73,8 +75,7 @@ protected: gpu::FramebufferPointer _destinationFrameBuffer; - float _factor { 2.0f }; - size_t _depth { 0 }; + float _factor{ 2.0f }; gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer); diff --git a/libraries/render-utils/src/model.slf b/libraries/render-utils/src/model.slf index 2f80fbde99..98abc29d8c 100644 --- a/libraries/render-utils/src/model.slf +++ b/libraries/render-utils/src/model.slf @@ -36,15 +36,7 @@ <@include DeferredBufferWrite.slh@> <@endif@> <@else@> - <@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@> + layout(location=0) out vec4 _fragColor0; <@endif@> <@if HIFI_USE_UNLIT@> @@ -53,9 +45,6 @@ <@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@> @@ -135,14 +124,7 @@ void main(void) { <@endif@> <@endif@> - <@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@> + <@if 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 a3c28631e9..b63ec898eb 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 mirror:f) fade:f/forward:f deformed:v/deformeddq:v \ No newline at end of file +DEFINES (normalmap translucent:f unlit:f/lightmap:f)/shadow fade:f/forward:f deformed:v/deformeddq:v \ No newline at end of file diff --git a/libraries/render-utils/src/render-utils/sdf_text3D.slp b/libraries/render-utils/src/render-utils/sdf_text3D.slp index f3f9af59aa..118135d099 100644 --- a/libraries/render-utils/src/render-utils/sdf_text3D.slp +++ b/libraries/render-utils/src/render-utils/sdf_text3D.slp @@ -1 +1 @@ -DEFINES (translucent unlit:f)/forward mirror:f \ No newline at end of file +DEFINES (translucent unlit:f)/forward \ No newline at end of file diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf index bf9bb0babd..c5bed1ecab 100644 --- a/libraries/render-utils/src/sdf_text3D.slf +++ b/libraries/render-utils/src/sdf_text3D.slf @@ -41,12 +41,6 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord1 _texCoord01.zw 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() { vec4 color = evalSDFSuperSampled(_texCoord0, _glyphBounds); @@ -57,11 +51,6 @@ void main() { } <@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_TRANSLUCENT or HIFI_USE_FORWARD@> _fragColor0 = vec4(color.rgb * isUnlitEnabled(), color.a); diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index dd7074a071..81cdaa51c9 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -29,7 +29,7 @@ static std::mutex fontMutex; -std::map, gpu::PipelinePointer> Font::_pipelines; +std::map, gpu::PipelinePointer> Font::_pipelines; gpu::Stream::FormatPointer Font::_format; struct TextureVertex { @@ -277,7 +277,6 @@ void Font::setupGPU() { if (_pipelines.empty()) { using namespace shader::render_utils::program; - // transparent, unlit, forward static const std::vector> keys = { 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), @@ -285,23 +284,18 @@ 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*/) }; 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(); state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, !transparent, gpu::LESS_EQUAL); - state->setBlendFunction(transparent, + state->setDepthTest(true, !std::get<0>(key), gpu::LESS_EQUAL); + state->setBlendFunction(std::get<0>(key), 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); - if (transparent) { + if (std::get<0>(key)) { PrepareStencil::testMask(*state); } else { PrepareStencil::testMaskDrawShape(*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); + _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); } // Sanity checks @@ -450,30 +444,32 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm } } -void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const DrawProps& props) { - if (!_loaded || props.str == "") { +void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString& str, const glm::vec4& color, + const glm::vec3& effectColor, float effectThickness, TextEffect effect, TextAlignment alignment, + const glm::vec2& origin, const glm::vec2& bounds, float scale, bool unlit, bool forward) { + if (!_loaded || str == "") { return; } - int textEffect = (int)props.effect; + int textEffect = (int)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 (props.str != drawInfo.string || props.bounds != drawInfo.bounds || props.origin != drawInfo.origin || props.alignment != _alignment || + if (str != drawInfo.string || bounds != drawInfo.bounds || origin != drawInfo.origin || alignment != _alignment || (drawInfo.params.effect != textEffect && (textEffect == SHADOW_EFFECT || drawInfo.params.effect == SHADOW_EFFECT)) || - (textEffect == SHADOW_EFFECT && props.scale != _scale)) { - _scale = props.scale; - _alignment = props.alignment; - buildVertices(drawInfo, props.str, props.origin, props.bounds, props.scale, textEffect == SHADOW_EFFECT, props.alignment); + (textEffect == SHADOW_EFFECT && scale != _scale)) { + _scale = scale; + _alignment = alignment; + buildVertices(drawInfo, str, origin, bounds, scale, textEffect == SHADOW_EFFECT, alignment); } setupGPU(); - if (!drawInfo.paramsBuffer || drawInfo.params.color != props.color || drawInfo.params.effectColor != props.effectColor || - drawInfo.params.effectThickness != props.effectThickness || drawInfo.params.effect != textEffect) { - drawInfo.params.color = props.color; - drawInfo.params.effectColor = props.effectColor; - drawInfo.params.effectThickness = props.effectThickness; + if (!drawInfo.paramsBuffer || drawInfo.params.color != color || drawInfo.params.effectColor != effectColor || + drawInfo.params.effectThickness != effectThickness || drawInfo.params.effect != textEffect) { + drawInfo.params.color = color; + drawInfo.params.effectColor = effectColor; + drawInfo.params.effectThickness = effectThickness; drawInfo.params.effect = textEffect; // need the gamma corrected color here @@ -488,7 +484,7 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const DrawPro drawInfo.paramsBuffer->setSubData(0, sizeof(DrawParams), (const gpu::Byte*)&gpuDrawParams); } - batch.setPipeline(_pipelines[std::make_tuple(props.color.a < 1.0f, props.unlit, props.forward, props.mirror)]); + batch.setPipeline(_pipelines[std::make_tuple(color.a < 1.0f, unlit, forward)]); batch.setInputFormat(_format); batch.setInputBuffer(0, drawInfo.verticesBuffer, 0, _format->getChannels().at(0)._stride); batch.setResourceTexture(render_utils::slot::texture::TextFont, _texture); diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h index e8a353a686..322e96439e 100644 --- a/libraries/render-utils/src/text/Font.h +++ b/libraries/render-utils/src/text/Font.h @@ -57,30 +57,10 @@ public: glm::vec2 computeExtent(const QString& str) const; 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 - void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const DrawProps& props); + void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const QString& str, const glm::vec4& color, + 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); @@ -125,7 +105,7 @@ private: gpu::TexturePointer _texture; gpu::BufferStreamPointer _stream; - static std::map, gpu::PipelinePointer> _pipelines; + static std::map, gpu::PipelinePointer> _pipelines; static gpu::Stream::FormatPointer _format; }; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index fb7d698672..d09b0d2f2f 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -159,10 +159,6 @@ namespace render { bool _takingSnapshot { false }; StencilMaskMode _stencilMaskMode { StencilMaskMode::NONE }; std::function _stencilMaskOperator; - - ItemID _ignoreItem { 0 }; - size_t _mirrorDepth { 0 }; - size_t _numMirrorFlips { 0 }; }; } diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index cd69114bbf..039cf01c86 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -82,7 +82,7 @@ void FetchNonspatialItems::run(const RenderContextPointer& renderContext, const outItems.reserve(items.size()); for (auto& id : items) { auto& item = scene->getItem(id); - if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && item.passesZoneOcclusionTest(CullTest::_containingZones)) { + if (filter.test(item.getKey()) && item.passesZoneOcclusionTest(CullTest::_containingZones)) { outItems.emplace_back(ItemBound(id, item.getBound(renderContext->args))); } } @@ -190,7 +190,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideFitItems"); for (auto id : inSelection.insideItems) { auto& item = scene->getItem(id); - if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound(args)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -205,7 +205,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideSmallItems"); for (auto id : inSelection.insideSubcellItems) { auto& item = scene->getItem(id); - if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound(args)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -220,7 +220,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialFitItems"); for (auto id : inSelection.partialItems) { auto& item = scene->getItem(id); - if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound(args)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -235,7 +235,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialSmallItems"); for (auto id : inSelection.partialSubcellItems) { auto& item = scene->getItem(id); - if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound(args)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -252,7 +252,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideFitItems"); for (auto id : inSelection.insideItems) { auto& item = scene->getItem(id); - if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound(args)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -267,7 +267,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideSmallItems"); for (auto id : inSelection.insideSubcellItems) { auto& item = scene->getItem(id); - if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound(args)); if (test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); @@ -284,7 +284,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialFitItems"); for (auto id : inSelection.partialItems) { auto& item = scene->getItem(id); - if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound(args)); if (test.frustumTest(itemBound.bound)) { outItems.emplace_back(itemBound); @@ -301,7 +301,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialSmallItems"); for (auto id : inSelection.partialSubcellItems) { auto& item = scene->getItem(id); - if (id != renderContext->args->_ignoreItem && filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound(args)); if (test.frustumTest(itemBound.bound) && test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); @@ -325,6 +325,73 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, std::static_pointer_cast(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) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 9e214fd988..9a7466223d 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -121,6 +121,29 @@ namespace render { void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems); }; + class CullShapeBounds { + public: + using Inputs = render::VaryingSet4; + using Outputs = render::VaryingSet2; + using JobModel = Job::ModelIO; + + 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 { public: using Inputs = render::VaryingSet2; diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index 1633523267..369f227566 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -160,11 +160,4 @@ namespace render { } return payload->passesZoneOcclusionTest(containingZones); } - - template <> ItemID payloadComputeMirrorView(const PayloadProxyInterface::Pointer& payload, ViewFrustum& viewFrustum) { - if (!payload) { - return Item::INVALID_ITEM_ID; - } - return payload->computeMirrorView(viewFrustum); - } } diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index f91b887fcb..5952be8a84 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -110,8 +110,6 @@ 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 @@ -164,7 +162,6 @@ 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 @@ -208,9 +205,6 @@ 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); } @@ -280,10 +274,7 @@ 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& withoutMirror() { _value.reset(ItemKey::MIRROR); _mask.set(ItemKey::MIRROR); return (*this); } - Builder& withMirror() { _value.set(ItemKey::MIRROR); _mask.set(ItemKey::MIRROR); return (*this); } + Builder& withSubMetaCulled() { _value.set(ItemKey::SUB_META_CULLED); _mask.set(ItemKey::SUB_META_CULLED); 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); } @@ -301,7 +292,6 @@ 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(); } }; @@ -450,8 +440,6 @@ public: virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const = 0; - virtual ItemID computeMirrorView(ViewFrustum& viewFrustum) const = 0; - ~PayloadInterface() {} // Status interface is local to the base class @@ -505,8 +493,6 @@ public: bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const { return _payload->passesZoneOcclusionTest(containingZones); } - ItemID computeMirrorView(ViewFrustum& viewFrustum) const { return _payload->computeMirrorView(viewFrustum); } - // Access the status const StatusPointer& getStatus() const { return _payload->getStatus(); } @@ -561,9 +547,6 @@ 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 ItemID payloadComputeMirrorView(const std::shared_ptr& payloadData, ViewFrustum& viewFrustum) { return Item::INVALID_ITEM_ID; } - // 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" @@ -590,8 +573,6 @@ public: virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const override { return payloadPassesZoneOcclusionTest(_data, containingZones); } - virtual ItemID computeMirrorView(ViewFrustum& viewFrustum) const override { return payloadComputeMirrorView(_data, viewFrustum); } - protected: DataPointer _data; @@ -647,7 +628,6 @@ 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 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 virtual void handleBlendedVertices(int blendshapeNumber, const QVector& blendshapeOffsets, @@ -660,7 +640,6 @@ 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 <> ItemID 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 3bdaee25c6..b2656a597f 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -35,20 +35,18 @@ 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 = 5; + const int NUM_SPATIAL_FILTERS = 4; 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().withoutMirror(), + ItemFilter::Builder::opaqueShape(), ItemFilter::Builder::transparentShape(), ItemFilter::Builder::light(), - ItemFilter::Builder::meta().withoutMirror(), - ItemFilter::Builder::mirror() + ItemFilter::Builder::meta() } }; MultiFilterItems::ItemFilterArray nonspatialFilters = { { ItemFilter::Builder::opaqueShape(), @@ -67,7 +65,6 @@ 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]; @@ -79,7 +76,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin task.addJob("ClearContainingZones"); - output = Output(BucketList{ opaques, transparents, lights, metas, mirrors, + output = Output(BucketList{ opaques, transparents, lights, metas, 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 9823c2acdf..0b475614a1 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -23,7 +23,6 @@ public: TRANSPARENT_SHAPE, LIGHT, META, - MIRROR, LAYER_FRONT_OPAQUE_SHAPE, LAYER_FRONT_TRANSPARENT_SHAPE, LAYER_HUD_OPAQUE_SHAPE, diff --git a/libraries/render/src/render/ResampleTask.cpp b/libraries/render/src/render/ResampleTask.cpp index e77dda600c..b868c53542 100644 --- a/libraries/render/src/render/ResampleTask.cpp +++ b/libraries/render/src/render/ResampleTask.cpp @@ -167,8 +167,7 @@ void UpsampleToBlitFramebuffer::run(const RenderContextPointer& renderContext, c batch.setViewportTransform(viewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); - bool shouldMirror = args->_numMirrorFlips >= (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE); - batch.setPipeline(shouldMirror ? _mirrorPipeline : _pipeline); + batch.setPipeline(args->_renderMode == RenderArgs::MIRROR_RENDER_MODE ? _mirrorPipeline : _pipeline); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport)); batch.setResourceTexture(0, sourceFramebuffer->getRenderBuffer(0)); diff --git a/libraries/render/src/render/ResampleTask.h b/libraries/render/src/render/ResampleTask.h index bf1e535949..92f720c843 100644 --- a/libraries/render/src/render/ResampleTask.h +++ b/libraries/render/src/render/ResampleTask.h @@ -73,7 +73,7 @@ namespace render { using Input = gpu::FramebufferPointer; using JobModel = Job::ModelIO; - UpsampleToBlitFramebuffer(size_t depth) : _depth(depth) {} + UpsampleToBlitFramebuffer() {} void run(const RenderContextPointer& renderContext, const Input& input, gpu::FramebufferPointer& resampledFrameBuffer); @@ -81,8 +81,6 @@ namespace render { static gpu::PipelinePointer _pipeline; static gpu::PipelinePointer _mirrorPipeline; - - size_t _depth; }; } diff --git a/libraries/shared/src/MirrorMode.cpp b/libraries/shared/src/MirrorMode.cpp deleted file mode 100644 index 272eb5d7c0..0000000000 --- a/libraries/shared/src/MirrorMode.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// -// 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 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 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); -} \ No newline at end of file diff --git a/libraries/shared/src/MirrorMode.h b/libraries/shared/src/MirrorMode.h deleted file mode 100644 index e48e564df0..0000000000 --- a/libraries/shared/src/MirrorMode.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// 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 - -#include "QString" - -#include "ViewFrustum.h" - -/*@jsdoc - *

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

- *
Report the number of entities with the tag, "Light-Target".
- * - * - * - * - * - * - * - * - *
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); - - static void setComputeMirrorViewOperator(std::function computeMirrorViewOperator); - static uint32_t computeMirrorView(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation, - MirrorMode mirrorMode, const QUuid& portalExitID); - -private: - static std::function _computeMirrorViewOperator; -}; - -#endif // hifi_MirrorMode_h diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index daa08b5dbc..e925ef960d 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -53,7 +53,7 @@ static const glm::vec4 NDC_VALUES[NUM_FRUSTUM_CORNERS] = { glm::vec4(-1.0f, 1.0f, 1.0f, 1.0f), }; -void ViewFrustum::setProjection(const glm::mat4& projection, bool isOblique) { +void ViewFrustum::setProjection(const glm::mat4& projection) { _projection = projection; glm::mat4 inverseProjection = glm::inverse(projection); @@ -63,21 +63,16 @@ void ViewFrustum::setProjection(const glm::mat4& projection, bool isOblique) { _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 - _nearClip = -_corners[BOTTOM_LEFT_NEAR].z; - _farClip = -_corners[BOTTOM_LEFT_FAR].z; - _aspectRatio = (_corners[TOP_RIGHT_NEAR].x - _corners[BOTTOM_LEFT_NEAR].x) / - (_corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_LEFT_NEAR].y); - glm::vec4 top = inverseProjection * vec4(0.0f, 1.0f, -1.0f, 1.0f); - top /= top.w; - _fieldOfView = abs(glm::degrees(2.0f * abs(glm::angle(vec3(0.0f, 0.0f, -1.0f), glm::normalize(vec3(top)))))); - _height = _corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_RIGHT_NEAR].y; - _width = _corners[TOP_RIGHT_NEAR].x - _corners[TOP_LEFT_NEAR].x; - } - _isOblique = isOblique; + // compute frustum properties + _nearClip = -_corners[BOTTOM_LEFT_NEAR].z; + _farClip = -_corners[BOTTOM_LEFT_FAR].z; + _aspectRatio = (_corners[TOP_RIGHT_NEAR].x - _corners[BOTTOM_LEFT_NEAR].x) / + (_corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_LEFT_NEAR].y); + glm::vec4 top = inverseProjection * vec4(0.0f, 1.0f, -1.0f, 1.0f); + top /= top.w; + _fieldOfView = abs(glm::degrees(2.0f * abs(glm::angle(vec3(0.0f, 0.0f, -1.0f), glm::normalize(vec3(top)))))); + _height = _corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_RIGHT_NEAR].y; + _width = _corners[TOP_RIGHT_NEAR].x - _corners[TOP_LEFT_NEAR].x; } void ViewFrustum::setProjection(float cameraFov, float cameraAspectRatio, float cameraNearClip, float cameraFarClip) { @@ -114,24 +109,12 @@ void ViewFrustum::calculate() { // 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 // three consecutive points - if (!_isOblique) { - _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[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[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]); - } 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); - } + _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[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[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]); // Also calculate our projection matrix in case people want to project points... // Projection matrix : Field of View, ratio, display range : near to far diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index fa66a0a87e..9c80538e60 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -47,7 +47,7 @@ public: const glm::vec3& getRight() const { return _right; } // setters for lens attributes - void setProjection(const glm::mat4& projection, bool isOblique = false); + void setProjection(const glm::mat4 & projection); void setProjection(float cameraFov, float cameraAspectRatio, float cameraNearClip, float cameraFarClip); void setFocalLength(float focalLength) { _focalLength = focalLength; } bool isPerspective() const; @@ -103,6 +103,7 @@ public: bool pointIntersectsFrustum(const glm::vec3& point) const; bool sphereIntersectsFrustum(const glm::vec3& center, float radius) const; + bool cubeIntersectsFrustum(const AACube& box) const; bool boxIntersectsFrustum(const AABox& box) const; bool boxInsideFrustum(const AABox& box) const; @@ -174,8 +175,6 @@ private: float _nearClip { DEFAULT_NEAR_CLIP }; float _farClip { DEFAULT_FAR_CLIP }; - bool _isOblique { false }; - const char* debugPlaneName (int plane) const; // Used to project points diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index 57cb3d1bc8..9f6c3ef278 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,12 +503,6 @@ "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." }, @@ -612,7 +606,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 b509168262..e16b6ca653 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -127,22 +127,6 @@ const GROUPS = [ }, 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", type: "multipleZonesSelection", diff --git a/tools/gpu-frame-player/src/RenderThread.cpp b/tools/gpu-frame-player/src/RenderThread.cpp index de39dacdea..0089c1577b 100644 --- a/tools/gpu-frame-player/src/RenderThread.cpp +++ b/tools/gpu-frame-player/src/RenderThread.cpp @@ -122,7 +122,7 @@ void RenderThread::renderFrame(gpu::FramePointer& frame) { if (_correction != glm::mat4()) { std::unique_lock lock(_frameLock); if (_correction != glm::mat4()) { - _backend->setCameraCorrection(_correction, _activeFrame->view, true); + _backend->setCameraCorrection(_correction, _activeFrame->view); //_prevRenderView = _correction * _activeFrame->view; } } From e7591d6794436d6a16bcd47dfdbe291bad2c6c16 Mon Sep 17 00:00:00 2001 From: Dale Glass <51060919+daleglass@users.noreply.github.com> Date: Sat, 13 Jan 2024 11:23:36 +0100 Subject: [PATCH 020/109] Revert "Revert "Mirrors + Portals"" --- interface/src/Application.cpp | 1 + interface/src/SecondaryCamera.cpp | 3 +- interface/src/avatar/MyAvatar.cpp | 3 +- .../scripting/RenderScriptingInterface.cpp | 24 ++- .../src/avatars-renderer/Avatar.cpp | 2 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 4 +- .../src/RenderableEntityItem.cpp | 114 ++++++++++++- .../src/RenderableEntityItem.h | 12 +- .../src/RenderableGizmoEntityItem.cpp | 3 +- .../src/RenderableGridEntityItem.cpp | 3 +- .../src/RenderableImageEntityItem.cpp | 9 +- .../src/RenderableLineEntityItem.cpp | 3 +- .../src/RenderableMaterialEntityItem.cpp | 3 +- .../src/RenderableModelEntityItem.cpp | 16 ++ .../src/RenderableModelEntityItem.h | 2 + .../src/RenderablePolyLineEntityItem.cpp | 3 +- .../src/RenderablePolyVoxEntityItem.cpp | 3 +- .../src/RenderableShapeEntityItem.cpp | 5 +- .../src/RenderableTextEntityItem.cpp | 38 ++++- .../src/RenderableTextEntityItem.h | 2 + .../src/RenderableWebEntityItem.cpp | 3 +- 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 + .../gpu-gl-common/src/gpu/gl/GLBackend.cpp | 33 +++- .../gpu-gl-common/src/gpu/gl/GLBackend.h | 6 +- libraries/gpu/src/gpu/Batch.cpp | 6 + libraries/gpu/src/gpu/Batch.h | 2 + libraries/gpu/src/gpu/Context.h | 2 +- libraries/gpu/src/gpu/FrameIOKeys.h | 1 + .../graphics/src/graphics/ShaderConstants.h | 2 + libraries/networking/src/udt/PacketHeaders.h | 1 + libraries/octree/src/OctreePacketData.h | 2 + .../src/CauterizedMeshPartPayload.cpp | 7 +- .../src/CauterizedMeshPartPayload.h | 2 +- .../src/DeferredLightingEffect.cpp | 1 + .../render-utils/src/DeferredLightingEffect.h | 4 +- .../render-utils/src/HighlightEffect.cpp | 7 +- .../render-utils/src/MeshPartPayload.cpp | 32 +++- libraries/render-utils/src/MeshPartPayload.h | 9 +- libraries/render-utils/src/Model.cpp | 46 ++++- libraries/render-utils/src/Model.h | 9 + .../render-utils/src/RenderCommonTask.cpp | 159 +++++++++++++++++- libraries/render-utils/src/RenderCommonTask.h | 17 +- .../render-utils/src/RenderDeferredTask.cpp | 33 ++-- .../render-utils/src/RenderDeferredTask.h | 2 +- .../render-utils/src/RenderForwardTask.cpp | 26 ++- .../render-utils/src/RenderForwardTask.h | 2 +- .../render-utils/src/RenderHUDLayerTask.cpp | 4 +- .../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/TextRenderer3D.cpp | 11 +- libraries/render-utils/src/TextRenderer3D.h | 9 +- .../src/ToneMapAndResampleTask.cpp | 6 +- .../render-utils/src/ToneMapAndResampleTask.h | 7 +- libraries/render-utils/src/model.slf | 22 ++- .../render-utils/src/render-utils/model.slp | 2 +- .../src/render-utils/sdf_text3D.slp | 2 +- libraries/render-utils/src/sdf_text3D.slf | 11 ++ libraries/render-utils/src/text/Font.cpp | 46 ++--- libraries/render-utils/src/text/Font.h | 28 ++- libraries/render/src/render/Args.h | 4 + libraries/render/src/render/CullTask.cpp | 85 +--------- libraries/render/src/render/CullTask.h | 23 --- 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/render/src/render/ResampleTask.cpp | 3 +- libraries/render/src/render/ResampleTask.h | 4 +- libraries/shared/src/MirrorMode.cpp | 36 ++++ libraries/shared/src/MirrorMode.h | 51 ++++++ libraries/shared/src/ViewFrustum.cpp | 51 ++++-- libraries/shared/src/ViewFrustum.h | 5 +- .../create/assets/data/createAppTooltips.json | 12 +- .../html/js/entityProperties.js | 16 ++ tools/gpu-frame-player/src/RenderThread.cpp | 2 +- 81 files changed, 1047 insertions(+), 263 deletions(-) create mode 100644 libraries/shared/src/MirrorMode.cpp create mode 100644 libraries/shared/src/MirrorMode.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a9e4bedbbd..931c41703b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2479,6 +2479,7 @@ Application::Application( copyViewFrustum(viewFrustum); return viewFrustum.getPosition(); }); + MirrorModeHelpers::setComputeMirrorViewOperator(EntityRenderer::computeMirrorViewOperator); DependencyManager::get()->setKickConfirmationOperator([this] (const QUuid& nodeID, unsigned int banFlags) { userKickConfirmation(nodeID, banFlags); }); 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/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 827388aa1c..bb6fbcd899 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3530,8 +3530,9 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON_LOOK_AT || qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON; bool overrideAnim = _skeletonModel ? _skeletonModel->getRig().isPlayingOverrideAnimation() : false; + bool isInMirror = renderArgs->_mirrorDepth > 0; bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition()); - return !defaultMode || (!firstPerson && !insideHead) || (overrideAnim && !insideHead); + return !defaultMode || isInMirror || (!firstPerson && !insideHead) || (overrideAnim && !insideHead); } void MyAvatar::setRotationRecenterFilterLength(float length) { 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/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index b2d6a6260b..790b45843c 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1120,7 +1120,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const batch.setModelTransform(textTransform); { 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 }); } } } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 03a463c82a..47f22dfaee 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -357,7 +357,7 @@ void OpenGLDisplayPlugin::customizeContext() { auto presentThread = DependencyManager::get(); Q_ASSERT(thread() == presentThread->thread()); - getGLBackend()->setCameraCorrection(mat4(), mat4(), true); + getGLBackend()->setCameraCorrection(mat4(), mat4(), true, true); for (auto& cursorValue : _cursorsData) { auto& cursorData = cursorValue.second; @@ -701,7 +701,7 @@ void OpenGLDisplayPlugin::present(const std::shared_ptr& if (_currentFrame) { auto correction = getViewCorrection(); - getGLBackend()->setCameraCorrection(correction, _prevRenderView); + getGLBackend()->setCameraCorrection(correction, _prevRenderView, true); _prevRenderView = correction * _currentFrame->view; { withPresentThreadLock([&] { diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 212baa6634..09bbfb9284 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::MIRROR || (_mirrorMode == MirrorMode::PORTAL && !_portalExitID.isNull())) { + builder.withMirror(); + } + if (!_visible) { builder.withInvisible(); } @@ -221,12 +226,113 @@ bool EntityRenderer::passesZoneOcclusionTest(const std::unordered_set& co 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(); + 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()->renderableIdForEntityId(portalExitID) : Item::INVALID_ITEM_ID; +} + void EntityRenderer::render(RenderArgs* args) { if (!isValidRenderItem()) { 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); } } @@ -454,6 +560,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 +613,10 @@ graphics::MaterialPointer EntityRenderer::getTopMaterial() { } 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()) { return Pipeline::PROCEDURAL; } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 3caeef0713..99dbffbc72 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,9 @@ 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; + 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: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } @@ -116,6 +120,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 +157,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/RenderableGizmoEntityItem.cpp b/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp index 42df1e2888..10ae144334 100644 --- a/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp @@ -263,8 +263,9 @@ void GizmoEntityRenderer::doRender(RenderArgs* args) { 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, - args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition(), true)); + usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition(), true)); batch.setModelTransform(transform); Pipeline pipelineType = getPipelineType(materials); diff --git a/libraries/entities-renderer/src/RenderableGridEntityItem.cpp b/libraries/entities-renderer/src/RenderableGridEntityItem.cpp index e374fe29c0..3f40218d46 100644 --- a/libraries/entities-renderer/src/RenderableGridEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableGridEntityItem.cpp @@ -103,8 +103,9 @@ void GridEntityRenderer::doRender(RenderArgs* args) { } else { 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, - args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); batch->setModelTransform(transform); auto minCorner = glm::vec2(-0.5f, -0.5f); diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index 9592a3e57f..66a5d0d609 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -147,8 +147,9 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { 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, - args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); float imageWidth = _texture->getWidth(); float imageHeight = _texture->getHeight(); @@ -198,8 +199,10 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { procedural->prepare(*batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(transparent)); } else if (pipelineType == Pipeline::SIMPLE) { batch->setResourceTexture(0, _texture->getGPUTexture()); - } else if (RenderPipelines::bindMaterials(materials, *batch, args->_renderMode, args->_enableTexturing)) { - args->_details._materialSwitches++; + } else if (pipelineType == Pipeline::MATERIAL) { + if (RenderPipelines::bindMaterials(materials, *batch, args->_renderMode, args->_enableTexturing)) { + args->_details._materialSwitches++; + } } DependencyManager::get()->renderQuad( diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index a36cdde212..1117c97c75 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -47,8 +47,9 @@ void LineEntityRenderer::doRender(RenderArgs* args) { const auto& modelTransform = getModelTransform(); Transform transform = Transform(); 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, - args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); batch.setModelTransform(transform); if (_linePoints.size() > 1) { DependencyManager::get()->bindSimpleProgram(batch, false, false, false, false, true, diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index b8f829f4ba..fe44c41094 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -303,8 +303,9 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) { proceduralRender = true; } + bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; 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); if (!proceduralRender) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 8ed3f84609..6b83d87732 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1274,6 +1274,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 +1355,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 +1470,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/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index aca501985a..81f4c5fcb4 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -325,8 +325,9 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) { buildPipelines(); } + bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; 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.setPipeline(_pipelines[{args->_renderMethod, isTransparent()}]); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 8331e016fd..26091a1ed4 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1860,8 +1860,9 @@ void PolyVoxEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); 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, - 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); batch.setModelTransform(transform); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index a6fee03311..82350f54bf 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -118,8 +118,9 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { 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, - 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)); batch.setModelTransform(transform); @@ -157,7 +158,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-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 2858e12afd..a15e2839a4 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -164,8 +164,9 @@ void TextEntityRenderer::doRender(RenderArgs* args) { transform = _renderTransform; }); + bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; 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); Pipeline pipelineType = getPipelineType(materials); @@ -180,7 +181,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { } auto geometryCache = DependencyManager::get(); - if (pipelineType == Pipeline::SIMPLE) { + if (pipelineType == Pipeline::SIMPLE || pipelineType == Pipeline::MIRROR) { geometryCache->renderQuad(batch, glm::vec2(-0.5f), glm::vec2(0.5f), backgroundColor, _geometryID); } else { 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(); } + if (textRenderable->_mirrorMode == MirrorMode::MIRROR || (textRenderable->_mirrorMode == MirrorMode::PORTAL && !textRenderable->_portalExitID.isNull())) { + builder.withMirror(); + } + return builder; } } @@ -311,6 +316,17 @@ bool entities::TextPayload::passesZoneOcclusionTest(const std::unordered_set(); + if (entityTreeRenderer) { + auto renderable = entityTreeRenderer->renderableForEntityId(_entityID); + if (renderable) { + return renderable->computeMirrorView(viewFrustum); + } + } + return Item::INVALID_ITEM_ID; +} + void entities::TextPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("TextPayload::render"); Q_ASSERT(args->_batch); @@ -335,12 +351,15 @@ void entities::TextPayload::render(RenderArgs* args) { glm::vec3 dimensions; glm::vec4 textColor; + bool mirror; textRenderable->withReadLock([&] { transform = textRenderable->_renderTransform; dimensions = textRenderable->_dimensions; float fadeRatio = textRenderable->_isFading ? Interpolate::calculateFadeRatio(textRenderable->_fadeStartTime) : 1.0f; 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; @@ -352,8 +371,9 @@ void entities::TextPayload::render(RenderArgs* args) { return; } + bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; 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(); 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); 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, - textRenderable->_text, textRenderable->_font, textColor, effectColor, textRenderable->_effectThickness, textRenderable->_effect, - textRenderable->_alignment, textRenderable->_unlit, forward); + textRenderer->draw(batch, textRenderable->_font, { textRenderable->_text, textColor, effectColor, { textRenderable->_leftMargin / scale, -textRenderable->_topMargin / scale }, + bounds / scale, scale, textRenderable->_effectThickness, textRenderable->_effect, textRenderable->_alignment, textRenderable->_unlit, forward, mirror }); } namespace render { @@ -399,4 +418,11 @@ template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Poi return false; } +template <> ItemID payloadComputeMirrorView(const entities::TextPayload::Pointer& payload, ViewFrustum& viewFrustum) { + if (payload) { + return payload->computeMirrorView(viewFrustum); + } + return Item::INVALID_ITEM_ID; +} + } diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index 8a18554dea..f48bb8085f 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -101,6 +101,7 @@ public: ShapeKey getShapeKey() const; void render(RenderArgs* args); bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const; + ItemID computeMirrorView(ViewFrustum& viewFrustum) const; protected: QUuid _entityID; @@ -117,6 +118,7 @@ namespace render { template <> const ShapeKey shapeGetShapeKey(const entities::TextPayload::Pointer& payload); template <> void payloadRender(const entities::TextPayload::Pointer& payload, RenderArgs* args); template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Pointer& payload, const std::unordered_set& containingZones); + template <> ItemID payloadComputeMirrorView(const entities::TextPayload::Pointer& payload, ViewFrustum& viewFrustum); } #endif // hifi_RenderableTextEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index c98bfe7f63..77f6fe99f6 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -320,8 +320,9 @@ void WebEntityRenderer::doRender(RenderArgs* args) { 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, - args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); + usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); batch.setModelTransform(transform); // Turn off jitter for these entities 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/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index af39458f17..c0116274ee 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -81,6 +81,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_disableContextViewCorrection), (&::gpu::gl::GLBackend::do_restoreContextViewCorrection), + (&::gpu::gl::GLBackend::do_setContextMirrorViewCorrection), (&::gpu::gl::GLBackend::do_disableContextStereo), (&::gpu::gl::GLBackend::do_restoreContextStereo), @@ -348,6 +349,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) { case Batch::COMMAND_setViewTransform: case Batch::COMMAND_setProjectionTransform: case Batch::COMMAND_setProjectionJitter: + case Batch::COMMAND_setContextMirrorViewCorrection: { CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); @@ -413,6 +415,7 @@ void GLBackend::renderPassDraw(const Batch& batch) { case Batch::COMMAND_setProjectionJitter: case Batch::COMMAND_setViewportTransform: case Batch::COMMAND_setDepthRangeTransform: + case Batch::COMMAND_setContextMirrorViewCorrection: { PROFILE_RANGE(render_gpu_gl_detail, "transform"); CommandCall call = _commandCalls[(*command)]; @@ -619,6 +622,16 @@ void GLBackend::do_restoreContextViewCorrection(const Batch& batch, size_t param _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) { } @@ -997,15 +1010,29 @@ void GLBackend::recycle() const { _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 invPrevView = glm::inverse(prevRenderView); _transform._correction.prevView = (reset ? Mat4() : prevRenderView); _transform._correction.prevViewInverse = (reset ? Mat4() : invPrevView); _transform._correction.correction = correction; _transform._correction.correctionInverse = invCorrection; - _pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction); - _pipeline._cameraCorrectionBuffer._buffer->flush(); + + if (!_inRenderTransferPass) { + _pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction); + _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) { diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index 2947649ce7..5545858877 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -121,7 +121,7 @@ public: // Shutdown rendering and persist any required resources 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; // 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_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_restoreContextStereo(const Batch& batch, size_t paramOffset) final; @@ -433,6 +434,9 @@ protected: Transform _view; CameraCorrection _correction; bool _viewCorrectionEnabled{ true }; + mat4 _unflippedCorrection; + mat4 _flippedCorrection; + bool _mirrorViewCorrection{ false }; Mat4 _projection; Vec4i _viewport{ 0, 0, 1, 1 }; diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index e6217cc600..a41f586d74 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -464,6 +464,12 @@ void Batch::restoreContextViewCorrection() { ADD_COMMAND(restoreContextViewCorrection); } +void Batch::setContextMirrorViewCorrection(bool shouldMirror) { + ADD_COMMAND(setContextMirrorViewCorrection); + uint mirrorFlag = shouldMirror ? 1 : 0; + _params.emplace_back(mirrorFlag); +} + void Batch::disableContextStereo() { ADD_COMMAND(disableContextStereo); } diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 0a438ea148..f89dd3ea90 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -239,6 +239,7 @@ public: void disableContextViewCorrection(); void restoreContextViewCorrection(); + void setContextMirrorViewCorrection(bool shouldMirror); void disableContextStereo(); void restoreContextStereo(); @@ -340,6 +341,7 @@ public: COMMAND_disableContextViewCorrection, COMMAND_restoreContextViewCorrection, + COMMAND_setContextMirrorViewCorrection, COMMAND_disableContextStereo, COMMAND_restoreContextStereo, diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 1946f447f8..ebc81f14e9 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -66,7 +66,7 @@ public: virtual void syncProgram(const gpu::ShaderPointer& program) = 0; virtual void recycle() const = 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; diff --git a/libraries/gpu/src/gpu/FrameIOKeys.h b/libraries/gpu/src/gpu/FrameIOKeys.h index 1a98d0decd..2d88158afb 100644 --- a/libraries/gpu/src/gpu/FrameIOKeys.h +++ b/libraries/gpu/src/gpu/FrameIOKeys.h @@ -181,6 +181,7 @@ constexpr const char* COMMAND_NAMES[] = { "disableContextViewCorrection", "restoreContextViewCorrection", + "setContextMirrorViewCorrection", "disableContextStereo", "restoreContextStereo", 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/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index dc1ffb7b67..78652bfb09 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -71,14 +71,15 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(const Transform _cauterizedTransform = renderTransform; } -void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode) const { - bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) && _enableCauterization; +void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const Transform& transform, RenderArgs::RenderMode renderMode, size_t mirrorDepth) const { + bool useCauterizedMesh = _enableCauterization && (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) && + mirrorDepth == 0; if (useCauterizedMesh) { if (_cauterizedClusterBuffer) { batch.setUniformBuffer(graphics::slot::buffer::Skinning, _cauterizedClusterBuffer); } batch.setModelTransform(_cauterizedTransform); } else { - ModelMeshPartPayload::bindTransform(batch, transform, renderMode); + ModelMeshPartPayload::bindTransform(batch, transform, renderMode, mirrorDepth); } } diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index 430f41fc08..cef7b6d9b5 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -25,7 +25,7 @@ public: 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; } diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 8d7fc345ac..3eb5924d19 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -286,6 +286,7 @@ void PrepareDeferred::run(const RenderContextPointer& renderContext, const Input outputs.edit0() = _deferredFramebuffer; outputs.edit1() = _deferredFramebuffer->getLightingFramebuffer(); + outputs.edit2() = _deferredFramebuffer->getDeferredFramebuffer(); gpu::doInBatch("PrepareDeferred::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 4779376410..058e0a4cbf 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -78,8 +78,8 @@ class PrepareDeferred { public: // Inputs: primaryFramebuffer and lightingModel using Inputs = render::VaryingSet2 ; - // Output: DeferredFramebuffer, LightingFramebuffer - using Outputs = render::VaryingSet2; + // Output: DeferredFramebuffer, LightingFramebuffer, the framebuffer to be used for mirrors (same as DeferredFramebuffer) + using Outputs = render::VaryingSet3; using JobModel = render::Job::ModelIO; 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..9adeb39e7c 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 @@ -188,7 +189,7 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { 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) { batch.setUniformBuffer(graphics::slot::buffer::Skinning, _clusterBuffer); } @@ -220,6 +221,10 @@ void ModelMeshPartPayload::updateKey(const render::ItemKey& key) { builder.withSubMetaCulled(); } + if (_mirrorMode == MirrorMode::MIRROR || (_mirrorMode == MirrorMode::PORTAL && !_portalExitID.isNull())) { + builder.withMirror(); + } + _itemKey = builder.build(); } @@ -299,8 +304,9 @@ Item::Bound ModelMeshPartPayload::getBound(RenderArgs* args) const { auto worldBound = _adjustedLocalBound; auto parentTransform = _parentTransform; if (args) { + bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; 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); return worldBound; @@ -313,18 +319,19 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { void ModelMeshPartPayload::render(RenderArgs* args) { 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; } gpu::Batch& batch = *(args->_batch); Transform transform = _parentTransform; + bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; 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); - bindTransform(batch, modelTransform, args->_renderMode); + bindTransform(batch, modelTransform, args->_renderMode, args->_mirrorDepth); //Bind the index buffer and vertex buffer and Blend shapes if needed bindMesh(batch); @@ -346,7 +353,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { procedural->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f, _shapeKey.isDeformed(), _shapeKey.isDualQuatSkinned())); batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); - } else { + } else if (!_itemKey.isMirror()) { // apply material properties if (RenderPipelines::bindMaterials(_drawMaterials, batch, args->_renderMode, args->_enableTexturing)) { args->_details._materialSwitches++; @@ -377,6 +384,12 @@ 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 +439,11 @@ template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Poin } return false; } + +template <> ItemID payloadComputeMirrorView(const ModelMeshPartPayload::Pointer& payload, ViewFrustum& viewFrustum) { + if (payload) { + return payload->computeMirrorView(viewFrustum); + } + return Item::INVALID_ITEM_ID; +} } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 1a3a898582..7e331a9497 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -37,7 +37,7 @@ public: // ModelMeshPartPayload functions to perform render 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 updateKey(const render::ItemKey& key); @@ -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; + render::ItemID 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 <> ItemID 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..7d622ab489 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,46 @@ 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); + data.updateKey(renderItemsKey); + }); + } + 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..a509ede437 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,149 @@ 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, 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() }; + size_t _mirrorIndex; + size_t _depth; + +}; + +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; + + 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(); +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) { + size_t nextDepth = depth + 1; + const auto setupOutput = task.addJob("SetupMirror" + std::to_string(mirrorIndex) + "Depth" + std::to_string(depth), inputs, mirrorIndex, nextDepth); + + task.addJob("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" + 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..fd10452dfa 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"); @@ -154,6 +157,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto prepareDeferredOutputs = task.addJob("PrepareDeferred", prepareDeferredInputs); const auto deferredFramebuffer = prepareDeferredOutputs.getN(0); const auto lightingFramebuffer = prepareDeferredOutputs.getN(1); + const auto mirrorTargetFramebuffer = prepareDeferredOutputs.getN(2); // draw a stencil mask in hidden regions of the framebuffer. task.addJob("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(); task.addJob("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" + std::to_string(i) + "Depth" + std::to_string(depth), mirrorInputs, i, cullFunctor, depth); + } + } + // Opaque all rendered // Linear Depth Pass @@ -234,7 +245,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren // Lighting Buffer ready for tone mapping const auto toneMappingInputs = ToneMapAndResample::Input(lightingFramebuffer, destFramebuffer).asVarying(); - const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs); + const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs, depth); // 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 @@ -398,8 +409,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 +422,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..722ba2248c 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); @@ -159,7 +173,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto destFramebuffer = static_cast(nullptr); const auto toneMappingInputs = ToneMapAndResample::Input(resolvedFramebuffer, destFramebuffer).asVarying(); - const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs); + const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs, depth); // HUD Layer const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(toneMappedBuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying(); task.addJob("RenderHUDLayer", renderHUDLayerInputs); 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/RenderHUDLayerTask.cpp b/libraries/render-utils/src/RenderHUDLayerTask.cpp index 743e59eebc..8fee3d57bc 100644 --- a/libraries/render-utils/src/RenderHUDLayerTask.cpp +++ b/libraries/render-utils/src/RenderHUDLayerTask.cpp @@ -16,8 +16,8 @@ void CompositeHUD::run(const RenderContextPointer& renderContext, const gpu::Fra assert(renderContext->args); assert(renderContext->args->_context); - // We do not want to render HUD elements in secondary camera - if (nsightActive() || renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) { + // We do not want to render HUD elements in secondary camera or mirrors + if (nsightActive() || renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE || renderContext->args->_mirrorDepth > 0) { return; } 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/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index 76d8374fb7..8ab1b8e0e9 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -40,21 +40,18 @@ float TextRenderer3D::getFontSize() const { return 0.0f; } -void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, - const QString& str, const glm::vec4& color, bool unlit, bool forward) { +void TextRenderer3D::draw(gpu::Batch& batch, const Font::DrawProps& props) { 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, - const QString& str, const QString& font, const glm::vec4& color, const glm::vec3& effectColor, - float effectThickness, TextEffect effect, TextAlignment alignment, bool unlit, bool forward) { +void TextRenderer3D::draw(gpu::Batch& batch, const QString& font, const Font::DrawProps& props) { if (font != _family) { _family = font; _font = Font::load(_family); } if (_font) { - _font->drawString(batch, _drawInfo, str, color, effectColor, effectThickness, effect, alignment, { x, y }, bounds, scale, unlit, forward); + _font->drawString(batch, _drawInfo, props); } } \ No newline at end of file diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h index edccf1429c..9db93e9dcc 100644 --- a/libraries/render-utils/src/TextRenderer3D.h +++ b/libraries/render-utils/src/TextRenderer3D.h @@ -26,12 +26,9 @@ public: glm::vec2 computeExtent(const QString& str) const; float getFontSize() const; // Pixel size - - void draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, - const QString& str, const glm::vec4& color, bool unlit, bool forward); - 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); + + void draw(gpu::Batch& batch, const Font::DrawProps& props); + void draw(gpu::Batch& batch, const QString& font, const Font::DrawProps& props); private: TextRenderer3D(const char* family); diff --git a/libraries/render-utils/src/ToneMapAndResampleTask.cpp b/libraries/render-utils/src/ToneMapAndResampleTask.cpp index 10312f7f2e..1ea9deb1fa 100644 --- a/libraries/render-utils/src/ToneMapAndResampleTask.cpp +++ b/libraries/render-utils/src/ToneMapAndResampleTask.cpp @@ -25,9 +25,10 @@ using namespace shader::render_utils::program; gpu::PipelinePointer ToneMapAndResample::_pipeline; gpu::PipelinePointer ToneMapAndResample::_mirrorPipeline; -ToneMapAndResample::ToneMapAndResample() { +ToneMapAndResample::ToneMapAndResample(size_t depth) { Parameters parameters; _parametersBuffer = gpu::BufferView(std::make_shared(sizeof(Parameters), (const gpu::Byte*) ¶meters)); + _depth = depth; } void ToneMapAndResample::init() { @@ -95,7 +96,8 @@ void ToneMapAndResample::run(const RenderContextPointer& renderContext, const In batch.setViewportTransform(destViewport); batch.setProjectionTransform(glm::mat4()); 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.setUniformBuffer(render_utils::slot::buffer::ToneMappingParams, _parametersBuffer); diff --git a/libraries/render-utils/src/ToneMapAndResampleTask.h b/libraries/render-utils/src/ToneMapAndResampleTask.h index 1c7ef2cf48..3a812cf445 100644 --- a/libraries/render-utils/src/ToneMapAndResampleTask.h +++ b/libraries/render-utils/src/ToneMapAndResampleTask.h @@ -49,11 +49,9 @@ signals: class ToneMapAndResample { public: - ToneMapAndResample(); + ToneMapAndResample(size_t depth); virtual ~ToneMapAndResample() {} - void render(RenderArgs* args, const gpu::TexturePointer& lightingBuffer, gpu::FramebufferPointer& destinationBuffer); - void setExposure(float exposure); float getExposure() const { return _parametersBuffer.get()._exposure; } @@ -75,7 +73,8 @@ protected: gpu::FramebufferPointer _destinationFrameBuffer; - float _factor{ 2.0f }; + float _factor { 2.0f }; + size_t _depth { 0 }; gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer); 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-utils/src/render-utils/sdf_text3D.slp b/libraries/render-utils/src/render-utils/sdf_text3D.slp index 118135d099..f3f9af59aa 100644 --- a/libraries/render-utils/src/render-utils/sdf_text3D.slp +++ b/libraries/render-utils/src/render-utils/sdf_text3D.slp @@ -1 +1 @@ -DEFINES (translucent unlit:f)/forward \ No newline at end of file +DEFINES (translucent unlit:f)/forward mirror:f \ No newline at end of file diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf index c5bed1ecab..bf9bb0babd 100644 --- a/libraries/render-utils/src/sdf_text3D.slf +++ b/libraries/render-utils/src/sdf_text3D.slf @@ -41,6 +41,12 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord1 _texCoord01.zw 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() { vec4 color = evalSDFSuperSampled(_texCoord0, _glyphBounds); @@ -51,6 +57,11 @@ void main() { } <@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_TRANSLUCENT or HIFI_USE_FORWARD@> _fragColor0 = vec4(color.rgb * isUnlitEnabled(), color.a); diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 81cdaa51c9..dd7074a071 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -29,7 +29,7 @@ static std::mutex fontMutex; -std::map, gpu::PipelinePointer> Font::_pipelines; +std::map, gpu::PipelinePointer> Font::_pipelines; gpu::Stream::FormatPointer Font::_format; struct TextureVertex { @@ -277,6 +277,7 @@ void Font::setupGPU() { if (_pipelines.empty()) { using namespace shader::render_utils::program; + // transparent, unlit, forward static const std::vector> keys = { 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), @@ -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*/) }; 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(); state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, !std::get<0>(key), gpu::LESS_EQUAL); - state->setBlendFunction(std::get<0>(key), + state->setDepthTest(true, !transparent, gpu::LESS_EQUAL); + state->setBlendFunction(transparent, 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); - if (std::get<0>(key)) { + if (transparent) { PrepareStencil::testMask(*state); } else { 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 @@ -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, - const glm::vec3& effectColor, float effectThickness, TextEffect effect, TextAlignment alignment, - const glm::vec2& origin, const glm::vec2& bounds, float scale, bool unlit, bool forward) { - if (!_loaded || str == "") { +void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const DrawProps& props) { + if (!_loaded || props.str == "") { return; } - int textEffect = (int)effect; + int textEffect = (int)props.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 (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)) || - (textEffect == SHADOW_EFFECT && scale != _scale)) { - _scale = scale; - _alignment = alignment; - buildVertices(drawInfo, str, origin, bounds, scale, textEffect == SHADOW_EFFECT, alignment); + (textEffect == SHADOW_EFFECT && props.scale != _scale)) { + _scale = props.scale; + _alignment = props.alignment; + buildVertices(drawInfo, props.str, props.origin, props.bounds, props.scale, textEffect == SHADOW_EFFECT, props.alignment); } setupGPU(); - if (!drawInfo.paramsBuffer || drawInfo.params.color != color || drawInfo.params.effectColor != effectColor || - drawInfo.params.effectThickness != effectThickness || drawInfo.params.effect != textEffect) { - drawInfo.params.color = color; - drawInfo.params.effectColor = effectColor; - drawInfo.params.effectThickness = effectThickness; + if (!drawInfo.paramsBuffer || drawInfo.params.color != props.color || drawInfo.params.effectColor != props.effectColor || + drawInfo.params.effectThickness != props.effectThickness || drawInfo.params.effect != textEffect) { + drawInfo.params.color = props.color; + drawInfo.params.effectColor = props.effectColor; + drawInfo.params.effectThickness = props.effectThickness; drawInfo.params.effect = textEffect; // 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); } - 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.setInputBuffer(0, drawInfo.verticesBuffer, 0, _format->getChannels().at(0)._stride); batch.setResourceTexture(render_utils::slot::texture::TextFont, _texture); diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h index 322e96439e..e8a353a686 100644 --- a/libraries/render-utils/src/text/Font.h +++ b/libraries/render-utils/src/text/Font.h @@ -57,10 +57,30 @@ public: glm::vec2 computeExtent(const QString& str) const; 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 - void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const QString& str, const glm::vec4& color, - const glm::vec3& effectColor, float effectThickness, TextEffect effect, TextAlignment alignment, - const glm::vec2& origin, const glm::vec2& bound, float scale, bool unlit, bool forward); + void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const DrawProps& props); static Pointer load(const QString& family); @@ -105,7 +125,7 @@ private: gpu::TexturePointer _texture; gpu::BufferStreamPointer _stream; - static std::map, gpu::PipelinePointer> _pipelines; + static std::map, gpu::PipelinePointer> _pipelines; static gpu::Stream::FormatPointer _format; }; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index d09b0d2f2f..fb7d698672 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -159,6 +159,10 @@ namespace render { bool _takingSnapshot { false }; StencilMaskMode _stencilMaskMode { StencilMaskMode::NONE }; std::function _stencilMaskOperator; + + ItemID _ignoreItem { 0 }; + size_t _mirrorDepth { 0 }; + size_t _numMirrorFlips { 0 }; }; } diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 039cf01c86..cd69114bbf 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -82,7 +82,7 @@ void FetchNonspatialItems::run(const RenderContextPointer& renderContext, const outItems.reserve(items.size()); for (auto& id : items) { 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))); } } @@ -190,7 +190,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideFitItems"); for (auto id : inSelection.insideItems) { 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)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -205,7 +205,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideSmallItems"); for (auto id : inSelection.insideSubcellItems) { 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)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -220,7 +220,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialFitItems"); for (auto id : inSelection.partialItems) { 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)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -235,7 +235,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialSmallItems"); for (auto id : inSelection.partialSubcellItems) { 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)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -252,7 +252,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideFitItems"); for (auto id : inSelection.insideItems) { 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)); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -267,7 +267,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideSmallItems"); for (auto id : inSelection.insideSubcellItems) { 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)); if (test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); @@ -284,7 +284,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialFitItems"); for (auto id : inSelection.partialItems) { 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)); if (test.frustumTest(itemBound.bound)) { outItems.emplace_back(itemBound); @@ -301,7 +301,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialSmallItems"); for (auto id : inSelection.partialSubcellItems) { 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)); if (test.frustumTest(itemBound.bound) && test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); @@ -325,73 +325,6 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, std::static_pointer_cast(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) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 9a7466223d..9e214fd988 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -121,29 +121,6 @@ namespace render { void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems); }; - class CullShapeBounds { - public: - using Inputs = render::VaryingSet4; - using Outputs = render::VaryingSet2; - using JobModel = Job::ModelIO; - - 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 { public: using Inputs = render::VaryingSet2; diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index 369f227566..1633523267 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 <> ItemID payloadComputeMirrorView(const PayloadProxyInterface::Pointer& payload, ViewFrustum& viewFrustum) { + if (!payload) { + return Item::INVALID_ITEM_ID; + } + return payload->computeMirrorView(viewFrustum); + } } diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 5952be8a84..f91b887fcb 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 ItemID 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); } + ItemID computeMirrorView(ViewFrustum& viewFrustum) const { return _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 ItemID payloadComputeMirrorView(const std::shared_ptr& payloadData, ViewFrustum& viewFrustum) { return Item::INVALID_ITEM_ID; } + // 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 ItemID 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 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 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 <> ItemID 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/render/src/render/ResampleTask.cpp b/libraries/render/src/render/ResampleTask.cpp index b868c53542..e77dda600c 100644 --- a/libraries/render/src/render/ResampleTask.cpp +++ b/libraries/render/src/render/ResampleTask.cpp @@ -167,7 +167,8 @@ void UpsampleToBlitFramebuffer::run(const RenderContextPointer& renderContext, c batch.setViewportTransform(viewport); batch.setProjectionTransform(glm::mat4()); 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.setResourceTexture(0, sourceFramebuffer->getRenderBuffer(0)); diff --git a/libraries/render/src/render/ResampleTask.h b/libraries/render/src/render/ResampleTask.h index 92f720c843..bf1e535949 100644 --- a/libraries/render/src/render/ResampleTask.h +++ b/libraries/render/src/render/ResampleTask.h @@ -73,7 +73,7 @@ namespace render { using Input = gpu::FramebufferPointer; using JobModel = Job::ModelIO; - UpsampleToBlitFramebuffer() {} + UpsampleToBlitFramebuffer(size_t depth) : _depth(depth) {} void run(const RenderContextPointer& renderContext, const Input& input, gpu::FramebufferPointer& resampledFrameBuffer); @@ -81,6 +81,8 @@ namespace render { static gpu::PipelinePointer _pipeline; static gpu::PipelinePointer _mirrorPipeline; + + size_t _depth; }; } diff --git a/libraries/shared/src/MirrorMode.cpp b/libraries/shared/src/MirrorMode.cpp new file mode 100644 index 0000000000..272eb5d7c0 --- /dev/null +++ b/libraries/shared/src/MirrorMode.cpp @@ -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 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 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); +} \ No newline at end of file diff --git a/libraries/shared/src/MirrorMode.h b/libraries/shared/src/MirrorMode.h new file mode 100644 index 0000000000..e48e564df0 --- /dev/null +++ b/libraries/shared/src/MirrorMode.h @@ -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 + +#include "QString" + +#include "ViewFrustum.h" + +/*@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); + + static void setComputeMirrorViewOperator(std::function computeMirrorViewOperator); + static uint32_t computeMirrorView(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation, + MirrorMode mirrorMode, const QUuid& portalExitID); + +private: + static std::function _computeMirrorViewOperator; +}; + +#endif // hifi_MirrorMode_h diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index e925ef960d..daa08b5dbc 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -53,7 +53,7 @@ static const glm::vec4 NDC_VALUES[NUM_FRUSTUM_CORNERS] = { 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; glm::mat4 inverseProjection = glm::inverse(projection); @@ -63,16 +63,21 @@ void ViewFrustum::setProjection(const glm::mat4& projection) { _corners[i] /= _corners[i].w; } - // compute frustum properties - _nearClip = -_corners[BOTTOM_LEFT_NEAR].z; - _farClip = -_corners[BOTTOM_LEFT_FAR].z; - _aspectRatio = (_corners[TOP_RIGHT_NEAR].x - _corners[BOTTOM_LEFT_NEAR].x) / - (_corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_LEFT_NEAR].y); - glm::vec4 top = inverseProjection * vec4(0.0f, 1.0f, -1.0f, 1.0f); - top /= top.w; - _fieldOfView = abs(glm::degrees(2.0f * abs(glm::angle(vec3(0.0f, 0.0f, -1.0f), glm::normalize(vec3(top)))))); - _height = _corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_RIGHT_NEAR].y; - _width = _corners[TOP_RIGHT_NEAR].x - _corners[TOP_LEFT_NEAR].x; + // 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 + _nearClip = -_corners[BOTTOM_LEFT_NEAR].z; + _farClip = -_corners[BOTTOM_LEFT_FAR].z; + _aspectRatio = (_corners[TOP_RIGHT_NEAR].x - _corners[BOTTOM_LEFT_NEAR].x) / + (_corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_LEFT_NEAR].y); + glm::vec4 top = inverseProjection * vec4(0.0f, 1.0f, -1.0f, 1.0f); + top /= top.w; + _fieldOfView = abs(glm::degrees(2.0f * abs(glm::angle(vec3(0.0f, 0.0f, -1.0f), glm::normalize(vec3(top)))))); + _height = _corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_RIGHT_NEAR].y; + _width = _corners[TOP_RIGHT_NEAR].x - _corners[TOP_LEFT_NEAR].x; + } + _isOblique = isOblique; } void ViewFrustum::setProjection(float cameraFov, float cameraAspectRatio, float cameraNearClip, float cameraFarClip) { @@ -109,12 +114,24 @@ void ViewFrustum::calculate() { // 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 // three consecutive points - _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[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[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]); + if (!_isOblique) { + _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[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[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]); + } 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... // Projection matrix : Field of View, ratio, display range : near to far diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index 9c80538e60..fa66a0a87e 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -47,7 +47,7 @@ public: const glm::vec3& getRight() const { return _right; } // 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 setFocalLength(float focalLength) { _focalLength = focalLength; } bool isPerspective() const; @@ -103,7 +103,6 @@ public: bool pointIntersectsFrustum(const glm::vec3& point) const; bool sphereIntersectsFrustum(const glm::vec3& center, float radius) const; - bool cubeIntersectsFrustum(const AACube& box) const; bool boxIntersectsFrustum(const AABox& box) const; bool boxInsideFrustum(const AABox& box) const; @@ -175,6 +174,8 @@ private: float _nearClip { DEFAULT_NEAR_CLIP }; float _farClip { DEFAULT_FAR_CLIP }; + bool _isOblique { false }; + const char* debugPlaneName (int plane) const; // Used to project points 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..b509168262 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -127,6 +127,22 @@ const GROUPS = [ }, 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", type: "multipleZonesSelection", diff --git a/tools/gpu-frame-player/src/RenderThread.cpp b/tools/gpu-frame-player/src/RenderThread.cpp index 0089c1577b..de39dacdea 100644 --- a/tools/gpu-frame-player/src/RenderThread.cpp +++ b/tools/gpu-frame-player/src/RenderThread.cpp @@ -122,7 +122,7 @@ void RenderThread::renderFrame(gpu::FramePointer& frame) { if (_correction != glm::mat4()) { std::unique_lock lock(_frameLock); if (_correction != glm::mat4()) { - _backend->setCameraCorrection(_correction, _activeFrame->view); + _backend->setCameraCorrection(_correction, _activeFrame->view, true); //_prevRenderView = _correction * _activeFrame->view; } } From dc32e5c0a3b893daebde9d4c0013d6520b9a5732 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Thu, 15 Feb 2024 11:54:49 -0800 Subject: [PATCH 021/109] web entity wantsKeyboardFocus property --- .../src/RenderableWebEntityItem.cpp | 1 + .../src/RenderableWebEntityItem.h | 3 ++- libraries/entities/src/EntityItemProperties.cpp | 13 +++++++++++++ libraries/entities/src/EntityItemProperties.h | 1 + libraries/entities/src/EntityPropertyFlags.h | 7 ++++--- libraries/entities/src/WebEntityItem.cpp | 16 ++++++++++++++++ libraries/entities/src/WebEntityItem.h | 4 ++++ libraries/networking/src/udt/PacketHeaders.h | 1 + .../create/assets/data/createAppTooltips.json | 3 +++ .../entityProperties/html/js/entityProperties.js | 5 +++++ 10 files changed, 50 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 77f6fe99f6..c2eddef3aa 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -171,6 +171,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene _dpi = entity->getDPI(); _color = entity->getColor(); _alpha = entity->getAlpha(); + _wantsKeyboardFocus = entity->wantsKeyboardFocus(); _pulseProperties = entity->getPulseProperties(); if (_contentType == ContentType::NoContent) { diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 81165d140f..8182c02603 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -67,7 +67,7 @@ protected: virtual bool isTransparent() const override; virtual bool wantsHandControllerPointerEvents() const override { return true; } - virtual bool wantsKeyboardFocus() const override { return true; } + virtual bool wantsKeyboardFocus() const override { return _wantsKeyboardFocus; } void handlePointerEventAsTouch(const PointerEvent& event); void handlePointerEventAsMouse(const PointerEvent& event); @@ -103,6 +103,7 @@ private: bool _useBackground { false }; QString _userAgent; WebInputMode _inputMode { WebInputMode::TOUCH }; + bool _wantsKeyboardFocus { true }; glm::vec3 _contextPosition; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 590d93a451..27a371665d 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -644,6 +644,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_SCRIPT_URL, scriptURL); CHECK_PROPERTY_CHANGE(PROP_MAX_FPS, maxFPS); CHECK_PROPERTY_CHANGE(PROP_INPUT_MODE, inputMode); + CHECK_PROPERTY_CHANGE(PROP_WANTS_KEYBOARD_FOCUS, wantsKeyboardFocus); CHECK_PROPERTY_CHANGE(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, showKeyboardFocusHighlight); CHECK_PROPERTY_CHANGE(PROP_WEB_USE_BACKGROUND, useBackground); CHECK_PROPERTY_CHANGE(PROP_USER_AGENT, userAgent); @@ -1423,6 +1424,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {string} scriptURL="" - The URL of a JavaScript file to inject into the web page. * @property {number} maxFPS=10 - The maximum update rate for the web content, in frames/second. * @property {WebInputMode} inputMode="touch" - The user input mode to use. + * @property {boolean} wantsKeyboardFocus=true - true if the entity should capture keyboard focus, false if it + * shouldn't. * @property {boolean} showKeyboardFocusHighlight=true - true if the entity is highlighted when it has keyboard * focus, false if it isn't. * @property {boolean} useBackground=true - true if the web entity should have a background, @@ -1857,6 +1860,7 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SCRIPT_URL, scriptURL); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_FPS, maxFPS); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_INPUT_MODE, inputMode, getInputModeAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_WANTS_KEYBOARD_FOCUS, wantsKeyboardFocus); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, showKeyboardFocusHighlight); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_WEB_USE_BACKGROUND, useBackground); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_AGENT, userAgent); @@ -2238,6 +2242,7 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h COPY_PROPERTY_FROM_QSCRIPTVALUE(scriptURL, QString, setScriptURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(maxFPS, uint8_t, setMaxFPS); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(inputMode, InputMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE(wantsKeyboardFocus, bool, setWantsKeyboardFocus); COPY_PROPERTY_FROM_QSCRIPTVALUE(showKeyboardFocusHighlight, bool, setShowKeyboardFocusHighlight); COPY_PROPERTY_FROM_QSCRIPTVALUE(useBackground, bool, setUseBackground); COPY_PROPERTY_FROM_QSCRIPTVALUE(userAgent, QString, setUserAgent); @@ -2522,6 +2527,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(scriptURL); COPY_PROPERTY_IF_CHANGED(maxFPS); COPY_PROPERTY_IF_CHANGED(inputMode); + COPY_PROPERTY_IF_CHANGED(wantsKeyboardFocus); COPY_PROPERTY_IF_CHANGED(showKeyboardFocusHighlight); COPY_PROPERTY_IF_CHANGED(useBackground); COPY_PROPERTY_IF_CHANGED(userAgent); @@ -2917,6 +2923,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_SCRIPT_URL, ScriptURL, scriptURL, QString); ADD_PROPERTY_TO_MAP(PROP_MAX_FPS, MaxFPS, maxFPS, uint8_t); ADD_PROPERTY_TO_MAP(PROP_INPUT_MODE, InputMode, inputMode, WebInputMode); + ADD_PROPERTY_TO_MAP(PROP_WANTS_KEYBOARD_FOCUS, WantsKeyboardFocus, wantsKeyboardFocus, bool); ADD_PROPERTY_TO_MAP(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, ShowKeyboardFocusHighlight, showKeyboardFocusHighlight, bool); ADD_PROPERTY_TO_MAP(PROP_WEB_USE_BACKGROUND, useBackground, useBackground, bool); ADD_PROPERTY_TO_MAP(PROP_USER_AGENT, UserAgent, userAgent, QString); @@ -3340,6 +3347,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_SCRIPT_URL, properties.getScriptURL()); APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, properties.getMaxFPS()); APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)properties.getInputMode()); + APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, properties.getWantsKeyboardFocus()); APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, properties.getShowKeyboardFocusHighlight()); APPEND_ENTITY_PROPERTY(PROP_WEB_USE_BACKGROUND, properties.getUseBackground()); APPEND_ENTITY_PROPERTY(PROP_USER_AGENT, properties.getUserAgent()); @@ -3806,6 +3814,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT_URL, QString, setScriptURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_FPS, uint8_t, setMaxFPS); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_INPUT_MODE, WebInputMode, setInputMode); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_WANTS_KEYBOARD_FOCUS, bool, setWantsKeyboardFocus); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, bool, setShowKeyboardFocusHighlight); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_WEB_USE_BACKGROUND, bool, setUseBackground); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_AGENT, QString, setUserAgent); @@ -4169,6 +4178,7 @@ void EntityItemProperties::markAllChanged() { _scriptURLChanged = true; _maxFPSChanged = true; _inputModeChanged = true; + _wantsKeyboardFocusChanged = true; _showKeyboardFocusHighlightChanged = true; _useBackgroundChanged = true; _userAgentChanged = true; @@ -4836,6 +4846,9 @@ QList EntityItemProperties::listChangedProperties() { if (faceCameraChanged()) { out += "faceCamera"; } + if (wantsKeyboardFocusChanged()) { + out += "wantsKeyboardFocus"; + } if (showKeyboardFocusHighlightChanged()) { out += "showKeyboardFocusHighlight"; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 57d3234d90..ef54c2495d 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -362,6 +362,7 @@ public: DEFINE_PROPERTY_REF(PROP_SCRIPT_URL, ScriptURL, scriptURL, QString, ""); DEFINE_PROPERTY_REF(PROP_MAX_FPS, MaxFPS, maxFPS, uint8_t, WebEntityItem::DEFAULT_MAX_FPS); DEFINE_PROPERTY_REF_ENUM(PROP_INPUT_MODE, InputMode, inputMode, WebInputMode, WebInputMode::TOUCH); + DEFINE_PROPERTY_REF(PROP_WANTS_KEYBOARD_FOCUS, WantsKeyboardFocus, wantsKeyboardFocus, bool, true); DEFINE_PROPERTY_REF(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, ShowKeyboardFocusHighlight, showKeyboardFocusHighlight, bool, true); DEFINE_PROPERTY_REF(PROP_WEB_USE_BACKGROUND, UseBackground, useBackground, bool, true); DEFINE_PROPERTY_REF(PROP_USER_AGENT, UserAgent, userAgent, QString, WebEntityItem::DEFAULT_USER_AGENT); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 1117e0924f..881b4e2ba7 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -322,9 +322,10 @@ enum EntityPropertyList { PROP_SCRIPT_URL = PROP_DERIVED_2, PROP_MAX_FPS = PROP_DERIVED_3, PROP_INPUT_MODE = PROP_DERIVED_4, - PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT = PROP_DERIVED_5, - PROP_WEB_USE_BACKGROUND = PROP_DERIVED_6, - PROP_USER_AGENT = PROP_DERIVED_7, + PROP_WANTS_KEYBOARD_FOCUS = PROP_DERIVED_5, + PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT = PROP_DERIVED_6, + PROP_WEB_USE_BACKGROUND = PROP_DERIVED_7, + PROP_USER_AGENT = PROP_DERIVED_8, // Polyline PROP_LINE_POINTS = PROP_DERIVED_0, diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index e090ec25ae..579a656069 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -61,6 +61,7 @@ EntityItemProperties WebEntityItem::getProperties(const EntityPropertyFlags& des COPY_ENTITY_PROPERTY_TO_PROPERTIES(scriptURL, getScriptURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxFPS, getMaxFPS); COPY_ENTITY_PROPERTY_TO_PROPERTIES(inputMode, getInputMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(wantsKeyboardFocus, wantsKeyboardFocus); COPY_ENTITY_PROPERTY_TO_PROPERTIES(showKeyboardFocusHighlight, getShowKeyboardFocusHighlight); COPY_ENTITY_PROPERTY_TO_PROPERTIES(useBackground, getUseBackground); COPY_ENTITY_PROPERTY_TO_PROPERTIES(userAgent, getUserAgent); @@ -83,6 +84,7 @@ bool WebEntityItem::setSubClassProperties(const EntityItemProperties& properties SET_ENTITY_PROPERTY_FROM_PROPERTIES(scriptURL, setScriptURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxFPS, setMaxFPS); SET_ENTITY_PROPERTY_FROM_PROPERTIES(inputMode, setInputMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(wantsKeyboardFocus, setWantsKeyboardFocus); SET_ENTITY_PROPERTY_FROM_PROPERTIES(showKeyboardFocusHighlight, setShowKeyboardFocusHighlight); SET_ENTITY_PROPERTY_FROM_PROPERTIES(useBackground, setUseBackground); SET_ENTITY_PROPERTY_FROM_PROPERTIES(userAgent, setUserAgent); @@ -113,6 +115,7 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i READ_ENTITY_PROPERTY(PROP_SCRIPT_URL, QString, setScriptURL); READ_ENTITY_PROPERTY(PROP_MAX_FPS, uint8_t, setMaxFPS); READ_ENTITY_PROPERTY(PROP_INPUT_MODE, WebInputMode, setInputMode); + READ_ENTITY_PROPERTY(PROP_WANTS_KEYBOARD_FOCUS, bool, setWantsKeyboardFocus); READ_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, bool, setShowKeyboardFocusHighlight); READ_ENTITY_PROPERTY(PROP_WEB_USE_BACKGROUND, bool, setUseBackground); READ_ENTITY_PROPERTY(PROP_USER_AGENT, QString, setUserAgent); @@ -131,6 +134,7 @@ EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& pa requestedProperties += PROP_SCRIPT_URL; requestedProperties += PROP_MAX_FPS; requestedProperties += PROP_INPUT_MODE; + requestedProperties += PROP_WANTS_KEYBOARD_FOCUS; requestedProperties += PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT; requestedProperties += PROP_WEB_USE_BACKGROUND; requestedProperties += PROP_USER_AGENT; @@ -158,6 +162,7 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst APPEND_ENTITY_PROPERTY(PROP_SCRIPT_URL, getScriptURL()); APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, getMaxFPS()); APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)getInputMode()); + APPEND_ENTITY_PROPERTY(PROP_WANTS_KEYBOARD_FOCUS, wantsKeyboardFocus()); APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, getShowKeyboardFocusHighlight()); APPEND_ENTITY_PROPERTY(PROP_WEB_USE_BACKGROUND, getUseBackground()); APPEND_ENTITY_PROPERTY(PROP_USER_AGENT, getUserAgent()); @@ -269,6 +274,17 @@ WebInputMode WebEntityItem::getInputMode() const { }); } +void WebEntityItem::setWantsKeyboardFocus(bool value) { + withWriteLock([&] { + _needsRenderUpdate |= _wantsKeyboardFocus != value; + _wantsKeyboardFocus = value; + }); +} + +bool WebEntityItem::wantsKeyboardFocus() const { + return _wantsKeyboardFocus; +} + void WebEntityItem::setShowKeyboardFocusHighlight(bool value) { _showKeyboardFocusHighlight = value; } diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index d607bbc99d..86a9717862 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -72,6 +72,9 @@ public: void setInputMode(const WebInputMode& value); WebInputMode getInputMode() const; + bool wantsKeyboardFocus() const; + void setWantsKeyboardFocus(bool value); + bool getShowKeyboardFocusHighlight() const; void setShowKeyboardFocusHighlight(bool value); @@ -94,6 +97,7 @@ protected: QString _scriptURL; uint8_t _maxFPS; WebInputMode _inputMode; + bool _wantsKeyboardFocus { false }; bool _showKeyboardFocusHighlight { false }; bool _useBackground { false }; QString _userAgent; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 2ee8e0f609..33ed430e41 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -292,6 +292,7 @@ enum class EntityVersion : PacketVersion { TextAlignment, Mirror, EntityTags, + WantsKeyboardFocus, // Add new versions above here NUM_PACKET_TYPE, diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index 57cb3d1bc8..04c7d61c68 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -237,6 +237,9 @@ "showKeyboardFocusHighlight": { "tooltip": "If enabled, highlights when it has keyboard focus." }, + "wantsKeyboardFocus": { + "tooltip": "If enabled, this web entity will capture keyboard focus once clicked." + }, "isEmitting": { "tooltip": "If enabled, then particles are emitted." }, diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index b509168262..140c893399 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -808,6 +808,11 @@ const GROUPS = [ }, propertyID: "inputMode", }, + { + label: "Wants Keyboard Focus", + type: "bool", + propertyID: "wantsKeyboardFocus", + }, { label: "Focus Highlight", type: "bool", From 7941c9e2c9a020470bddbae949dcea90c3c4a399 Mon Sep 17 00:00:00 2001 From: HifiExperiments <53453710+HifiExperiments@users.noreply.github.com> Date: Fri, 1 Mar 2024 11:18:52 -0800 Subject: [PATCH 022/109] fix typo --- libraries/entities/src/EntityItemProperties.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 27a371665d..e39bc5fad3 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -3347,7 +3347,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_SCRIPT_URL, properties.getScriptURL()); APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, properties.getMaxFPS()); APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)properties.getInputMode()); - APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, properties.getWantsKeyboardFocus()); + APPEND_ENTITY_PROPERTY(PROP_WANTS_KEYBOARD_FOCUS, properties.getWantsKeyboardFocus()); APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, properties.getShowKeyboardFocusHighlight()); APPEND_ENTITY_PROPERTY(PROP_WEB_USE_BACKGROUND, properties.getUseBackground()); APPEND_ENTITY_PROPERTY(PROP_USER_AGENT, properties.getUserAgent()); From b8d2d71e07cad53bcbf2a8316ab5cc66d14c0af5 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 2 Mar 2024 11:27:04 -0800 Subject: [PATCH 023/109] move audio zones to zone entity properties --- assignment-client/src/audio/AudioMixer.cpp | 169 +++++++++++---- assignment-client/src/audio/AudioMixer.h | 44 ++-- .../src/audio/AudioMixerSlave.cpp | 61 ++++-- assignment-client/src/avatars/AvatarMixer.cpp | 8 +- assignment-client/src/avatars/AvatarMixer.h | 1 - assignment-client/src/octree/OctreeServer.cpp | 2 +- .../resources/describe-settings.json | 6 +- .../entities/src/EntityItemProperties.cpp | 23 ++- libraries/entities/src/EntityItemProperties.h | 2 + libraries/entities/src/EntityPropertyFlags.h | 11 + .../entities/src/ZoneAudioPropertyGroup.cpp | 194 ++++++++++++++++++ .../entities/src/ZoneAudioPropertyGroup.h | 97 +++++++++ libraries/entities/src/ZoneEntityItem.cpp | 17 +- libraries/entities/src/ZoneEntityItem.h | 12 +- libraries/networking/src/udt/PacketHeaders.h | 1 + .../create/assets/data/createAppTooltips.json | 15 ++ .../html/js/entityProperties.js | 38 +++- .../entityProperties/html/tabs/zone_audio.png | Bin 0 -> 598 bytes 18 files changed, 610 insertions(+), 91 deletions(-) create mode 100644 libraries/entities/src/ZoneAudioPropertyGroup.cpp create mode 100644 libraries/entities/src/ZoneAudioPropertyGroup.h create mode 100644 scripts/system/create/entityProperties/html/tabs/zone_audio.png diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 788dfeab93..7947582b5c 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "AudioLogging.h" #include "AudioHelpers.h" @@ -39,6 +40,7 @@ #include "AvatarAudioStream.h" #include "InjectedAudioStream.h" #include "crash-handler/CrashHandler.h" +#include "../entities/AssignmentParentFinder.h" using namespace std; @@ -56,9 +58,7 @@ float AudioMixer::_noiseMutingThreshold{ DEFAULT_NOISE_MUTING_THRESHOLD }; float AudioMixer::_attenuationPerDoublingInDistance{ DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE }; map> AudioMixer::_availableCodecs{ }; QStringList AudioMixer::_codecPreferenceOrder{}; -vector AudioMixer::_audioZones; -vector AudioMixer::_zoneSettings; -vector AudioMixer::_zoneReverbSettings; +unordered_map AudioMixer::_audioZones; AudioMixer::AudioMixer(ReceivedMessage& message) : ThreadedAssignment(message) @@ -112,6 +112,8 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : PacketReceiver::makeSourcedListenerReference(this, &AudioMixer::handleNodeMuteRequestPacket)); packetReceiver.registerListener(PacketType::KillAvatar, PacketReceiver::makeSourcedListenerReference(this, &AudioMixer::handleKillAvatarPacket)); + packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase }, + PacketReceiver::makeSourcedListenerReference(this, &AudioMixer::handleOctreePacket)); packetReceiver.registerListenerForTypes({ PacketType::ReplicatedMicrophoneAudioNoEcho, @@ -405,7 +407,7 @@ void AudioMixer::start() { // prepare the NodeList nodeList->addSetOfNodeTypesToNodeInterestSet({ - NodeType::Agent, NodeType::EntityScriptServer, + NodeType::Agent, NodeType::EntityScriptServer, NodeType::EntityServer, NodeType::UpstreamAudioMixer, NodeType::DownstreamAudioMixer }); nodeList->linkedDataCreateCallback = [&](Node* node) { getOrCreateClientData(node); }; @@ -417,12 +419,19 @@ void AudioMixer::start() { parseSettingsObject(settingsObject); } + setupEntityQuery(); + // mix state unsigned int frame = 1; while (!_isFinished) { auto ticTimer = _ticTiming.timer(); + // Set our query each frame + { + _entityViewer.queryOctree(); + } + if (_startFrameTimestamp.time_since_epoch().count() == 0) { _startFrameTimestamp = _idealFrameTimestamp = p_high_resolution_clock::now(); } else { @@ -555,8 +564,6 @@ void AudioMixer::clearDomainSettings() { _noiseMutingThreshold = DEFAULT_NOISE_MUTING_THRESHOLD; _codecPreferenceOrder.clear(); _audioZones.clear(); - _zoneSettings.clear(); - _zoneReverbSettings.clear(); } void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) { @@ -727,8 +734,13 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) { if (allOk) { glm::vec3 corner(xMin, yMin, zMin); glm::vec3 dimensions(xMax - xMin, yMax - yMin, zMax - zMin); - AABox zoneAABox(corner, dimensions); - _audioZones.push_back({ zoneName, zoneAABox }); + + Transform t; + t.setTranslation(corner + 0.5f * dimensions); + t.setScale(dimensions); + _audioZones[zoneName].inverseTransform = t.getInverseMatrix(); + _audioZones[zoneName].volume = dimensions.x * dimensions.y * dimensions.z; + qCDebug(audio) << "Added zone:" << zoneName << "(corner:" << corner << ", dimensions:" << dimensions << ")"; } } @@ -749,28 +761,17 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) { coefficientObject.contains(LISTENER) && coefficientObject.contains(COEFFICIENT)) { - auto itSource = find_if(begin(_audioZones), end(_audioZones), [&](const ZoneDescription& description) { - return description.name == coefficientObject.value(SOURCE).toString(); - }); - auto itListener = find_if(begin(_audioZones), end(_audioZones), [&](const ZoneDescription& description) { - return description.name == coefficientObject.value(LISTENER).toString(); - }); + auto itSource = _audioZones.find(coefficientObject.value(SOURCE).toString()); bool ok; float coefficient = coefficientObject.value(COEFFICIENT).toString().toFloat(&ok); + if (ok && coefficient <= 1.0f && itSource != _audioZones.end()) { + auto listener = coefficientObject.value(LISTENER).toString(); + itSource->second.listeners.emplace_back(listener); + itSource->second.coefficients.emplace_back(coefficient); - if (ok && coefficient <= 1.0f && - itSource != end(_audioZones) && - itListener != end(_audioZones)) { - - ZoneSettings settings; - settings.source = itSource - begin(_audioZones); - settings.listener = itListener - begin(_audioZones); - settings.coefficient = coefficient; - - _zoneSettings.push_back(settings); - qCDebug(audio) << "Added Coefficient:" << itSource->name << itListener->name << settings.coefficient; + qCDebug(audio) << "Added Coefficient:" << itSource->first << listener << coefficient; } } } @@ -791,21 +792,16 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) { reverbObject.contains(WET_LEVEL)) { bool okReverbTime, okWetLevel; - auto itZone = find_if(begin(_audioZones), end(_audioZones), [&](const ZoneDescription& description) { - return description.name == reverbObject.value(ZONE).toString(); - }); + auto itZone = _audioZones.find(reverbObject.value(ZONE).toString()); float reverbTime = reverbObject.value(REVERB_TIME).toString().toFloat(&okReverbTime); float wetLevel = reverbObject.value(WET_LEVEL).toString().toFloat(&okWetLevel); - if (okReverbTime && okWetLevel && itZone != end(_audioZones)) { - ReverbSettings settings; - settings.zone = itZone - begin(_audioZones); - settings.reverbTime = reverbTime; - settings.wetLevel = wetLevel; + if (okReverbTime && okWetLevel && itZone != _audioZones.end()) { + itZone->second.reverbEnabled = true; + itZone->second.reverbTime = reverbTime; + itZone->second.wetLevel = wetLevel; - _zoneReverbSettings.push_back(settings); - - qCDebug(audio) << "Added Reverb:" << itZone->name << reverbTime << wetLevel; + qCDebug(audio) << "Added Reverb:" << itZone->first << reverbTime << wetLevel; } } } @@ -813,6 +809,107 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) { } } +void AudioMixer::setupEntityQuery() { + _entityViewer.init(); + EntityTreePointer entityTree = _entityViewer.getTree(); + DependencyManager::registerInheritance(); + DependencyManager::set(entityTree); + + connect(entityTree.get(), &EntityTree::addingEntityPointer, this, &AudioMixer::entityAdded); + connect(entityTree.get(), &EntityTree::deletingEntityPointer, this, &AudioMixer::entityRemoved); + + // ES query: {"type": "Zone"} + QJsonObject zoneQuery; + zoneQuery["type"] = "Zone"; + + QJsonObject queryFlags; + queryFlags["includeAncestors"] = true; + queryFlags["includeDescendants"] = true; + zoneQuery["flags"] = queryFlags; + zoneQuery["name"] = true; // Handy for debugging. + + _entityViewer.getOctreeQuery().setJSONParameters(zoneQuery); +} + +void AudioMixer::handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode) { + PacketType packetType = message->getType(); + + switch (packetType) { + case PacketType::OctreeStats: + { // Ignore stats, but may have a different Entity packet appended. + OctreeHeadlessViewer::parseOctreeStats(message, senderNode); + const auto piggyBackedSizeWithHeader = message->getBytesLeftToRead(); + if (piggyBackedSizeWithHeader > 0) { + // pull out the piggybacked packet and create a new QSharedPointer for it + auto buffer = std::unique_ptr(new char[piggyBackedSizeWithHeader]); + memcpy(buffer.get(), message->getRawMessage() + message->getPosition(), piggyBackedSizeWithHeader); + + auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggyBackedSizeWithHeader, message->getSenderSockAddr()); + auto newMessage = QSharedPointer::create(*newPacket); + handleOctreePacket(newMessage, senderNode); + } + break; + } + + case PacketType::EntityData: + _entityViewer.processDatagram(*message, senderNode); + break; + + case PacketType::EntityErase: + _entityViewer.processEraseMessage(*message, senderNode); + break; + + default: + qCDebug(audio) << "Unexpected packet type:" << packetType; + break; + } +} + +void updateAudioZone(EntityItem* entity, std::unordered_map& audioZones) { + auto zoneEntity = (ZoneEntityItem*)entity; + auto& audioZone = audioZones[entity->getID().toString()]; + auto& audioSettings = zoneEntity->getAudioProperties(); + + vec3 dimensions = entity->getScaledDimensions(); + Transform t; + t.setTranslation(entity->getWorldPosition()); + t.setScale(dimensions); + t.setRotation(entity->getWorldOrientation()); + audioZone.inverseTransform = t.getInverseMatrix(); + audioZone.volume = dimensions.x * dimensions.y * dimensions.z; + + audioZone.reverbEnabled = audioSettings.getReverbEnabled(); + audioZone.reverbTime = audioSettings.getReverbTime(); + audioZone.wetLevel = audioSettings.getReverbWetLevel(); + + audioZone.listeners.clear(); + auto listenerZones = audioSettings.getListenerZones(); + audioZone.listeners.reserve(listenerZones.length()); + for (auto& listener : listenerZones) { + audioZone.listeners.push_back(listener.toString()); + } + + audioZone.coefficients = audioSettings.getListenerAttenuationCoefficients().toStdVector(); + + /*qCDebug(audio) << "Updated audio zone:" << entity->getID().toString() << "(position:" << t.getTranslation() + << ", dimensions:" << t.getScale() << ")";*/ +} + +void AudioMixer::entityAdded(EntityItem* entity) { + if (entity->getType() == EntityTypes::Zone) { + updateAudioZone(entity, _audioZones); + entity->registerChangeHandler([entity](const EntityItemID& entityItemID) { + updateAudioZone(entity, _audioZones); + }); + } +} + +void AudioMixer::entityRemoved(EntityItem* entity) { + if (entity->getType() == EntityTypes::Zone) { + _audioZones.erase(entity->getID().toString()); + } +} + AudioMixer::Timer::Timing::Timing(uint64_t& sum) : _sum(sum) { _timing = p_high_resolution_clock::now(); } diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 5b75ed54d2..658b86a4bd 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -14,7 +14,7 @@ #include -#include +#include #include #include #include @@ -25,6 +25,8 @@ #include "AudioMixerStats.h" #include "AudioMixerSlavePool.h" +#include "../entities/EntityTreeHeadlessViewer.h" + class PositionalAudioStream; class AvatarAudioStream; class AudioHRTF; @@ -36,28 +38,22 @@ class AudioMixer : public ThreadedAssignment { public: AudioMixer(ReceivedMessage& message); - - struct ZoneDescription { - QString name; - AABox area; - }; struct ZoneSettings { - int source; - int listener; - float coefficient; - }; - struct ReverbSettings { - int zone; - float reverbTime; - float wetLevel; + glm::mat4 inverseTransform; + float volume { FLT_MAX }; + + bool reverbEnabled { false }; + float reverbTime { 0.0f }; + float wetLevel { 0.0f }; + + std::vector listeners; + std::vector coefficients; }; static int getStaticJitterFrames() { return _numStaticJitterFrames; } static bool shouldMute(float quietestFrame) { return quietestFrame > _noiseMutingThreshold; } static float getAttenuationPerDoublingInDistance() { return _attenuationPerDoublingInDistance; } - static const std::vector& getAudioZones() { return _audioZones; } - static const std::vector& getZoneSettings() { return _zoneSettings; } - static const std::vector& getReverbSettings() { return _zoneReverbSettings; } + static const std::unordered_map& getAudioZones() { return _audioZones; } static const std::pair negotiateCodec(std::vector codecs); static bool shouldReplicateTo(const Node& from, const Node& to) { @@ -72,12 +68,17 @@ public slots: void run() override; void sendStatsPacket() override; + // Audio zone possibly changed + void entityAdded(EntityItem* entity); + void entityRemoved(EntityItem* entity); + private slots: // packet handlers void handleMuteEnvironmentPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleNodeMuteRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleNodeKilled(SharedNodePointer killedNode); void handleKillAvatarPacket(QSharedPointer packet, SharedNodePointer sendingNode); + void handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode); void queueAudioPacket(QSharedPointer packet, SharedNodePointer sendingNode); void queueReplicatedAudioPacket(QSharedPointer packet); @@ -146,14 +147,17 @@ private: static std::map _availableCodecs; static QStringList _codecPreferenceOrder; - static std::vector _audioZones; - static std::vector _zoneSettings; - static std::vector _zoneReverbSettings; + static std::unordered_map _audioZones; float _throttleStartTarget = 0.9f; float _throttleBackoffTarget = 0.44f; AudioMixerSlave::SharedData _workerSharedData; + + void setupEntityQuery(); + + // Attach to entity tree for audio zone info. + EntityTreeHeadlessViewer _entityViewer; }; #endif // hifi_AudioMixer_h diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index 204095ab94..9c0ef4a237 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -646,27 +646,36 @@ void sendMutePacket(const SharedNodePointer& node, AudioMixerClientData& data) { data.setShouldMuteClient(false); } +const AABox UNIT_BOX(vec3(-0.5f), vec3(1.0f)); void sendEnvironmentPacket(const SharedNodePointer& node, AudioMixerClientData& data) { bool hasReverb = false; float reverbTime, wetLevel; - auto& reverbSettings = AudioMixer::getReverbSettings(); auto& audioZones = AudioMixer::getAudioZones(); AvatarAudioStream* stream = data.getAvatarAudioStream(); - glm::vec3 streamPosition = stream->getPosition(); + vec4 streamPosition = vec4(stream->getPosition(), 1.0f); // find reverb properties - for (const auto& settings : reverbSettings) { - AABox box = audioZones[settings.zone].area; - if (box.contains(streamPosition)) { - hasReverb = true; - reverbTime = settings.reverbTime; - wetLevel = settings.wetLevel; - break; + QString bestZone; + float bestZoneVolume = FLT_MAX; + for (const auto& zone : audioZones) { + if (zone.second.reverbEnabled) { + vec4 localPosition = zone.second.inverseTransform * streamPosition; + if (UNIT_BOX.contains(localPosition) && zone.second.volume < bestZoneVolume) { + bestZone = zone.first; + bestZoneVolume = zone.second.volume; + } } } + if (bestZoneVolume < FLT_MAX) { + const auto& zone = audioZones.at(bestZone); + hasReverb = zone.reverbEnabled; + reverbTime = zone.reverbTime; + wetLevel = zone.wetLevel; + } + // check if data changed bool dataChanged = (stream->hasReverb() != hasReverb) || (stream->hasReverb() && (stream->getRevebTime() != reverbTime || stream->getWetLevel() != wetLevel)); @@ -759,18 +768,40 @@ float computeGain(float masterAvatarGain, } auto& audioZones = AudioMixer::getAudioZones(); - auto& zoneSettings = AudioMixer::getZoneSettings(); // find distance attenuation coefficient float attenuationPerDoublingInDistance = AudioMixer::getAttenuationPerDoublingInDistance(); - for (const auto& settings : zoneSettings) { - if (audioZones[settings.source].area.contains(streamToAdd.getPosition()) && - audioZones[settings.listener].area.contains(listeningNodeStream.getPosition())) { - attenuationPerDoublingInDistance = settings.coefficient; - break; + + float bestZonesVolume = FLT_MAX; + float bestZonesCoefficient; + for (const auto& sourceZone : audioZones) { + if (sourceZone.second.listeners.size() > 0 && sourceZone.second.listeners.size() == sourceZone.second.coefficients.size()) { + vec4 localSourcePosition = sourceZone.second.inverseTransform * vec4(streamToAdd.getPosition(), 1.0f); + if (UNIT_BOX.contains(localSourcePosition)) { + size_t listenerIndex = 0; + for (const auto& listener : sourceZone.second.listeners) { + const auto& listenerZone = audioZones.find(listener); + if (listenerZone != audioZones.end()) { + vec4 localListenerPosition = listenerZone->second.inverseTransform * vec4(listeningNodeStream.getPosition(), 1.0f); + if (UNIT_BOX.contains(localListenerPosition)) { + // This isn't an exact solution, but we target the smallest sum of volumes of the source and listener zones + const float zonesVolume = sourceZone.second.volume + listenerZone->second.volume; + if (zonesVolume < bestZonesVolume) { + bestZonesVolume = zonesVolume; + bestZonesCoefficient = sourceZone.second.coefficients[listenerIndex]; + } + } + } + listenerIndex++; + } + } } } + if (bestZonesVolume < FLT_MAX) { + attenuationPerDoublingInDistance = bestZonesCoefficient; + } + if (attenuationPerDoublingInDistance < 0.0f) { // translate a negative zone setting to distance limit const float MIN_DISTANCE_LIMIT = ATTN_DISTANCE_REF + 1.0f; // silent after 1m diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 7279627daf..8d322c36f2 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -1085,7 +1085,7 @@ void AvatarMixer::setupEntityQuery() { DependencyManager::set(entityTree); connect(entityTree.get(), &EntityTree::addingEntityPointer, this, &AvatarMixer::entityAdded); - connect(entityTree.get(), &EntityTree::deletingEntityPointer, this, &AvatarMixer::entityChange); + connect(entityTree.get(), &EntityTree::deletingEntityPointer, this, &AvatarMixer::entityRemoved); // ES query: {"avatarPriority": true, "type": "Zone"} QJsonObject priorityZoneQuery; @@ -1140,7 +1140,7 @@ void AvatarMixer::entityAdded(EntityItem* entity) { if (entity->getType() == EntityTypes::Zone) { _dirtyHeroStatus = true; entity->registerChangeHandler([this](const EntityItemID& entityItemID) { - entityChange(); + _dirtyHeroStatus = true; }); } } @@ -1151,10 +1151,6 @@ void AvatarMixer::entityRemoved(EntityItem * entity) { } } -void AvatarMixer::entityChange() { - _dirtyHeroStatus = true; -} - void AvatarMixer::aboutToFinish() { DependencyManager::destroy(); DependencyManager::destroy(); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 92821277de..dfea2b83f7 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -52,7 +52,6 @@ public slots: // Avatar zone possibly changed void entityAdded(EntityItem* entity); void entityRemoved(EntityItem* entity); - void entityChange(); private slots: void queueIncomingPacket(QSharedPointer message, SharedNodePointer node); diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index d9a2faa2de..983b88e176 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1239,7 +1239,7 @@ void OctreeServer::beginRunning() { // we need to ask the DS about agents so we can ping/reply with them nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer, - NodeType::AvatarMixer }); + NodeType::AvatarMixer, NodeType::AudioMixer }); beforeRun(); // after payload has been processed diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index ebfb519eac..58d5df5407 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1235,7 +1235,7 @@ { "name": "zones", "type": "table", - "label": "Zones", + "label": "Zones (deprecated, use Zone Entities)", "help": "In this table you can define a set of zones in which you can specify various audio properties.", "numbered": false, "content_setting": true, @@ -1287,7 +1287,7 @@ { "name": "attenuation_coefficients", "type": "table", - "label": "Attenuation Coefficients", + "label": "Attenuation Coefficients (deprecated, use Zone Entities)", "help": "In this table you can set custom attenuation coefficients between audio zones", "content_setting": true, "numbered": true, @@ -1317,7 +1317,7 @@ { "name": "reverb", "type": "table", - "label": "Reverb Settings", + "label": "Reverb Settings (deprecated, use Zone Entities)", "help": "In this table you can set reverb levels for audio zones. For a medium-sized (e.g., 100 square meter) meeting room, try a decay time of around 1.5 seconds and a wet/dry mix of 25%. For an airplane hangar or cathedral, try a decay time of 4 seconds and a wet/dry mix of 50%.", "numbered": true, "content_setting": true, diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index e39bc5fad3..5db64d12e4 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -44,6 +44,7 @@ AnimationPropertyGroup EntityItemProperties::_staticAnimation; SkyboxPropertyGroup EntityItemProperties::_staticSkybox; HazePropertyGroup EntityItemProperties::_staticHaze; BloomPropertyGroup EntityItemProperties::_staticBloom; +ZoneAudioPropertyGroup EntityItemProperties::_staticAudio; KeyLightPropertyGroup EntityItemProperties::_staticKeyLight; AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight; GrabPropertyGroup EntityItemProperties::_staticGrab; @@ -87,6 +88,7 @@ void EntityItemProperties::debugDump() const { getKeyLight().debugDump(); getAmbientLight().debugDump(); getBloom().debugDump(); + getAudio().debugDump(); getGrab().debugDump(); qCDebug(entities) << " changed properties..."; @@ -613,6 +615,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { changedProperties += _skybox.getChangedProperties(); changedProperties += _haze.getChangedProperties(); changedProperties += _bloom.getChangedProperties(); + changedProperties += _audio.getChangedProperties(); CHECK_PROPERTY_CHANGE(PROP_FLYING_ALLOWED, flyingAllowed); CHECK_PROPERTY_CHANGE(PROP_GHOSTING_ALLOWED, ghostingAllowed); CHECK_PROPERTY_CHANGE(PROP_FILTER_URL, filterURL); @@ -853,7 +856,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * avatar entities, false if they won't be. * @property {Uuid} cloneOriginID - The ID of the entity that this entity was cloned from. * - * @property {Uuid[]} renderWithZones=[]] - A list of entity IDs representing with which zones this entity should render. + * @property {Uuid[]} renderWithZones=[] - A list of entity IDs representing with which zones this entity should render. * If it is empty, this entity will render normally. Otherwise, this entity will only render if your avatar is within * one of the zones in this list. * @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera. Use the rotation @@ -1481,6 +1484,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Entities.ComponentMode} bloomMode="inherit" - Configures the bloom in the zone. * @property {Entities.Bloom} bloom - The bloom properties of the zone. * + * @property {Entities.ZoneAudio} audio - The audio properties of the zone. + * * @property {boolean} flyingAllowed=true - true if visitors can fly in the zone; false if they * cannot. Only works for domain entities. * @property {boolean} ghostingAllowed=true - true if visitors with avatar collisions turned off will not @@ -1834,6 +1839,7 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s _skybox.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); _haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); _bloom.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + _audio.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); } COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FLYING_ALLOWED, flyingAllowed); @@ -2211,6 +2217,7 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h _skybox.copyFromScriptValue(object, namesSet, _defaultSettings); _haze.copyFromScriptValue(object, namesSet, _defaultSettings); _bloom.copyFromScriptValue(object, namesSet, _defaultSettings); + _audio.copyFromScriptValue(object, namesSet, _defaultSettings); COPY_PROPERTY_FROM_QSCRIPTVALUE(flyingAllowed, bool, setFlyingAllowed); COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed); COPY_PROPERTY_FROM_QSCRIPTVALUE(filterURL, QString, setFilterURL); @@ -2496,6 +2503,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { _skybox.merge(other._skybox); _haze.merge(other._haze); _bloom.merge(other._bloom); + _audio.merge(other._audio); COPY_PROPERTY_IF_CHANGED(flyingAllowed); COPY_PROPERTY_IF_CHANGED(ghostingAllowed); COPY_PROPERTY_IF_CHANGED(filterURL); @@ -2892,6 +2900,13 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_THRESHOLD, Bloom, bloom, BloomThreshold, bloomThreshold); ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_SIZE, Bloom, bloom, BloomSize, bloomSize); } + { // Audio + ADD_GROUP_PROPERTY_TO_MAP(PROP_REVERB_ENABLED, Audio, audio, ReverbEnabled, reverbEnabled); + ADD_GROUP_PROPERTY_TO_MAP(PROP_REVERB_TIME, Audio, audio, ReverbTime, reverbTime); + ADD_GROUP_PROPERTY_TO_MAP(PROP_REVERB_WET_LEVEL, Audio, audio, ReverbWetLevel, reverbWetLevel); + ADD_GROUP_PROPERTY_TO_MAP(PROP_LISTENER_ZONES, Audio, audio, ListenerZones, listenerZones); + ADD_GROUP_PROPERTY_TO_MAP(PROP_LISTENER_ATTENUATION_COEFFICIENTS, Audio, audio, ListenerAttenuationCoefficients, listenerAttenuationCoefficients); + } ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool); ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool); ADD_PROPERTY_TO_MAP(PROP_FILTER_URL, FilterURL, filterURL, QString); @@ -3307,6 +3322,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticBloom.setProperties(properties); _staticBloom.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + _staticAudio.setProperties(properties); + _staticAudio.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, properties.getFlyingAllowed()); APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, properties.getGhostingAllowed()); APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, properties.getFilterURL()); @@ -3775,6 +3793,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int properties.getSkybox().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); properties.getBloom().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + properties.getAudio().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); @@ -4147,6 +4166,7 @@ void EntityItemProperties::markAllChanged() { _skybox.markAllChanged(); _haze.markAllChanged(); _bloom.markAllChanged(); + _audio.markAllChanged(); _flyingAllowedChanged = true; _ghostingAllowedChanged = true; _filterURLChanged = true; @@ -4738,6 +4758,7 @@ QList EntityItemProperties::listChangedProperties() { getSkybox().listChangedProperties(out); getHaze().listChangedProperties(out); getBloom().listChangedProperties(out); + getAudio().listChangedProperties(out); if (flyingAllowedChanged()) { out += "flyingAllowed"; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index ef54c2495d..6340313f2d 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -61,6 +61,7 @@ #include "BloomPropertyGroup.h" #include "PulsePropertyGroup.h" #include "RingGizmoPropertyGroup.h" +#include "ZoneAudioPropertyGroup.h" #include "MaterialMappingMode.h" #include "BillboardMode.h" @@ -331,6 +332,7 @@ public: DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup); DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup); DEFINE_PROPERTY_GROUP(Bloom, bloom, BloomPropertyGroup); + DEFINE_PROPERTY_GROUP(Audio, audio, ZoneAudioPropertyGroup); DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED); DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED); DEFINE_PROPERTY(PROP_FILTER_URL, FilterURL, filterURL, QString, ZoneEntityItem::DEFAULT_FILTER_URL); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 881b4e2ba7..ca96c04fd8 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -167,6 +167,11 @@ enum EntityPropertyList { PROP_DERIVED_32, PROP_DERIVED_33, PROP_DERIVED_34, + PROP_DERIVED_35, + PROP_DERIVED_36, + PROP_DERIVED_37, + PROP_DERIVED_38, + PROP_DERIVED_39, PROP_AFTER_LAST_ITEM, @@ -301,6 +306,12 @@ enum EntityPropertyList { PROP_AVATAR_PRIORITY = PROP_DERIVED_33, // Screen-sharing PROP_SCREENSHARE = PROP_DERIVED_34, + // Audio + PROP_REVERB_ENABLED = PROP_DERIVED_35, + PROP_REVERB_TIME = PROP_DERIVED_36, + PROP_REVERB_WET_LEVEL = PROP_DERIVED_37, + PROP_LISTENER_ZONES = PROP_DERIVED_38, + PROP_LISTENER_ATTENUATION_COEFFICIENTS = PROP_DERIVED_39, // Polyvox PROP_VOXEL_VOLUME_SIZE = PROP_DERIVED_0, diff --git a/libraries/entities/src/ZoneAudioPropertyGroup.cpp b/libraries/entities/src/ZoneAudioPropertyGroup.cpp new file mode 100644 index 0000000000..aeedeea977 --- /dev/null +++ b/libraries/entities/src/ZoneAudioPropertyGroup.cpp @@ -0,0 +1,194 @@ +// +// ZoneAudioPropertyGroup.cpp +// libraries/entities/src +// +// Created by HifiExperiments on 11/28/23 +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ZoneAudioPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" +#include "EntityItemPropertiesMacros.h" + +void ZoneAudioPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_REVERB_ENABLED, Audio, audio, ReverbEnabled, reverbEnabled); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_REVERB_TIME, Audio, audio, ReverbTime, reverbTime); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_REVERB_WET_LEVEL, Audio, audio, ReverbWetLevel, reverbWetLevel); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_LISTENER_ZONES, Audio, audio, ListenerZones, listenerZones); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_LISTENER_ATTENUATION_COEFFICIENTS, Audio, audio, ListenerAttenuationCoefficients, listenerAttenuationCoefficients); +} + +void ZoneAudioPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, reverbEnabled, bool, setReverbEnabled); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, reverbTime, float, setReverbTime); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, reverbWetLevel, float, setReverbWetLevel); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, listenerZones, qVectorQUuid, setListenerZones); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, listenerAttenuationCoefficients, qVectorFloat, setListenerAttenuationCoefficients); +} + +void ZoneAudioPropertyGroup::merge(const ZoneAudioPropertyGroup& other) { + COPY_PROPERTY_IF_CHANGED(reverbEnabled); + COPY_PROPERTY_IF_CHANGED(reverbTime); + COPY_PROPERTY_IF_CHANGED(reverbWetLevel); + COPY_PROPERTY_IF_CHANGED(listenerZones); + COPY_PROPERTY_IF_CHANGED(listenerAttenuationCoefficients); +} + +void ZoneAudioPropertyGroup::debugDump() const { + qCDebug(entities) << " ZoneAudioPropertyGroup: ---------------------------------------------"; + qCDebug(entities) << " _reverbEnabled:" << _reverbEnabled; + qCDebug(entities) << " _reverbTime:" << _reverbTime; + qCDebug(entities) << " _reverbWetLevel:" << _reverbWetLevel; + qCDebug(entities) << " _listenerZones:" << _listenerZones; + qCDebug(entities) << " _listenerAttenuationCoefficients:" << _listenerAttenuationCoefficients; +} + +void ZoneAudioPropertyGroup::listChangedProperties(QList& out) { + if (reverbEnabledChanged()) { + out << "reverbEnabled"; + } + if (reverbTimeChanged()) { + out << "reverbTime"; + } + if (reverbWetLevelChanged()) { + out << "reverbWetLevel"; + } + if (listenerZonesChanged()) { + out << "listenerZones"; + } + if (listenerAttenuationCoefficientsChanged()) { + out << "listenerAttenuationCoefficients"; + } +} + +bool ZoneAudioPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_REVERB_ENABLED, getReverbEnabled()); + APPEND_ENTITY_PROPERTY(PROP_REVERB_TIME, getReverbTime()); + APPEND_ENTITY_PROPERTY(PROP_REVERB_WET_LEVEL, getReverbWetLevel()); + APPEND_ENTITY_PROPERTY(PROP_LISTENER_ZONES, getListenerZones()); + APPEND_ENTITY_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, getListenerAttenuationCoefficients()); + + return true; +} + +bool ZoneAudioPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + + READ_ENTITY_PROPERTY(PROP_REVERB_ENABLED, bool, setReverbEnabled); + READ_ENTITY_PROPERTY(PROP_REVERB_TIME, float, setReverbTime); + READ_ENTITY_PROPERTY(PROP_REVERB_WET_LEVEL, float, setReverbWetLevel); + READ_ENTITY_PROPERTY(PROP_LISTENER_ZONES, QVector, setListenerZones); + READ_ENTITY_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, QVector, setListenerAttenuationCoefficients); + + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_REVERB_ENABLED, ReverbEnabled); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_REVERB_TIME, ReverbTime); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_REVERB_WET_LEVEL, ReverbWetLevel); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_LISTENER_ZONES, ListenerZones); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_LISTENER_ATTENUATION_COEFFICIENTS, ListenerAttenuationCoefficients); + + processedBytes += bytesRead; + + Q_UNUSED(somethingChanged); + + return true; +} + +void ZoneAudioPropertyGroup::markAllChanged() { + _reverbEnabledChanged = true; + _reverbTimeChanged = true; + _reverbWetLevelChanged = true; + _listenerZonesChanged = true; + _listenerAttenuationCoefficientsChanged = true; +} + +EntityPropertyFlags ZoneAudioPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + + CHECK_PROPERTY_CHANGE(PROP_REVERB_ENABLED, reverbEnabled); + CHECK_PROPERTY_CHANGE(PROP_REVERB_TIME, reverbTime); + CHECK_PROPERTY_CHANGE(PROP_REVERB_WET_LEVEL, reverbWetLevel); + CHECK_PROPERTY_CHANGE(PROP_LISTENER_ZONES, listenerZones); + CHECK_PROPERTY_CHANGE(PROP_LISTENER_ATTENUATION_COEFFICIENTS, listenerAttenuationCoefficients); + + return changedProperties; +} + +void ZoneAudioPropertyGroup::getProperties(EntityItemProperties& properties) const { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ReverbEnabled, getReverbEnabled); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ReverbTime, getReverbTime); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ReverbWetLevel, getReverbWetLevel); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ListenerZones, getListenerZones); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ListenerAttenuationCoefficients, getListenerAttenuationCoefficients); +} + +bool ZoneAudioPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ReverbEnabled, reverbEnabled, setReverbEnabled); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ReverbTime, reverbTime, setReverbTime); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ReverbWetLevel, reverbWetLevel, setReverbWetLevel); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ListenerZones, listenerZones, setListenerZones); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ListenerAttenuationCoefficients, listenerAttenuationCoefficients, setListenerAttenuationCoefficients); + + return somethingChanged; +} + +EntityPropertyFlags ZoneAudioPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + + requestedProperties += PROP_REVERB_ENABLED; + requestedProperties += PROP_REVERB_TIME; + requestedProperties += PROP_REVERB_WET_LEVEL; + requestedProperties += PROP_LISTENER_ZONES; + requestedProperties += PROP_LISTENER_ATTENUATION_COEFFICIENTS; + + return requestedProperties; +} + +void ZoneAudioPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_REVERB_ENABLED, getReverbEnabled()); + APPEND_ENTITY_PROPERTY(PROP_REVERB_TIME, getReverbTime()); + APPEND_ENTITY_PROPERTY(PROP_REVERB_WET_LEVEL, getReverbWetLevel()); + APPEND_ENTITY_PROPERTY(PROP_LISTENER_ZONES, getListenerZones()); + APPEND_ENTITY_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, getListenerAttenuationCoefficients()); +} + +int ZoneAudioPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_REVERB_ENABLED, bool, setReverbEnabled); + READ_ENTITY_PROPERTY(PROP_REVERB_TIME, float, setReverbTime); + READ_ENTITY_PROPERTY(PROP_REVERB_WET_LEVEL, float, setReverbWetLevel); + READ_ENTITY_PROPERTY(PROP_LISTENER_ZONES, QVector, setListenerZones); + READ_ENTITY_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, QVector, setListenerAttenuationCoefficients); + + return bytesRead; +} diff --git a/libraries/entities/src/ZoneAudioPropertyGroup.h b/libraries/entities/src/ZoneAudioPropertyGroup.h new file mode 100644 index 0000000000..a99a43e2be --- /dev/null +++ b/libraries/entities/src/ZoneAudioPropertyGroup.h @@ -0,0 +1,97 @@ +// +// ZoneAudioPropertyGroup.h +// libraries/entities/src +// +// Created by HifiExperiments on 11/28/23 +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_ZoneAudioPropertyGroup_h +#define hifi_ZoneAudioPropertyGroup_h + +#include +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +/*@jsdoc + * Zone audio is defined by the following properties: + * @typedef {object} Entities.ZoneAudio + * @property {boolean} reverbEnabled=false - If reverb should be enabled for listeners in this zone. + * @property {number} reverbTime=1.0 - The time (seconds) for the reverb tail to decay by 60dB, also known as RT60. + * @property {number} reverbWetLevel=50 - Adjusts the wet/dry percentage, from completely dry (0%) to completely wet (100%). + * @property {Uuid[]} listenerZones=[] - A list of entity IDs representing listener zones with this zone as a source. + * Sounds from this zone being heard by a listener in a listener zone will be attenuated by the corresponding + * listenerAttenuationCoefficient. + * @property {number[]} listenerAttenuationCoefficients=[] - A list of attenuation coefficients. Each coefficient will be + * applied to sounds coming from this zone and being heard by a listener in the corresponding listenerZone. + */ +class ZoneAudioPropertyGroup : public PropertyGroup { +public: + // EntityItemProperty related helpers + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, + ScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; + + void merge(const ZoneAudioPropertyGroup& other); + + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; + + virtual bool appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; + + // EntityItem related helpers + // methods for getting/setting all properties of an entity + virtual void getProperties(EntityItemProperties& propertiesOut) const override; + + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + DEFINE_PROPERTY(PROP_REVERB_ENABLED, ReverbEnabled, reverbEnabled, bool, false); + DEFINE_PROPERTY(PROP_REVERB_TIME, ReverbTime, reverbTime, float, 1.0f); + DEFINE_PROPERTY(PROP_REVERB_WET_LEVEL, ReverbWetLevel, reverbWetLevel, float, 50.0f); + DEFINE_PROPERTY(PROP_LISTENER_ZONES, ListenerZones, listenerZones, QVector, QVector()); + DEFINE_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, ListenerAttenuationCoefficients, listenerAttenuationCoefficients, QVector, QVector()); + +}; + +#endif // hifi_ZoneAudioPropertyGroup_h diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 8ac5a4329c..cefd2ccb26 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -60,6 +60,7 @@ EntityItemProperties ZoneEntityItem::getProperties(const EntityPropertyFlags& de }); _hazeProperties.getProperties(properties); _bloomProperties.getProperties(properties); + _audioProperties.getProperties(properties); COPY_ENTITY_PROPERTY_TO_PROPERTIES(flyingAllowed, getFlyingAllowed); COPY_ENTITY_PROPERTY_TO_PROPERTIES(ghostingAllowed, getGhostingAllowed); @@ -90,6 +91,7 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie }); _hazePropertiesChanged |= _hazeProperties.setProperties(properties); _bloomPropertiesChanged |= _bloomProperties.setProperties(properties); + bool audioPropertiesChanged = _audioProperties.setProperties(properties); SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed); @@ -103,8 +105,8 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie SET_ENTITY_PROPERTY_FROM_PROPERTIES(avatarPriority, setAvatarPriority); SET_ENTITY_PROPERTY_FROM_PROPERTIES(screenshare, setScreenshare); - somethingChanged |= _keyLightPropertiesChanged || _ambientLightPropertiesChanged || - _skyboxPropertiesChanged || _hazePropertiesChanged || _bloomPropertiesChanged; + somethingChanged |= _keyLightPropertiesChanged || _ambientLightPropertiesChanged || _skyboxPropertiesChanged || + _hazePropertiesChanged || _bloomPropertiesChanged || audioPropertiesChanged; return somethingChanged; } @@ -168,6 +170,13 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, dataAt += bytesFromBloom; } + { + int bytesFromAudio = _audioProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, somethingChanged); + bytesRead += bytesFromAudio; + dataAt += bytesFromAudio; + } + READ_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); READ_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); READ_ENTITY_PROPERTY(PROP_FILTER_URL, QString, setFilterURL); @@ -194,6 +203,7 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p requestedProperties += _skyboxProperties.getEntityProperties(params); requestedProperties += _hazeProperties.getEntityProperties(params); requestedProperties += _bloomProperties.getEntityProperties(params); + requestedProperties += _audioProperties.getEntityProperties(params); requestedProperties += PROP_FLYING_ALLOWED; requestedProperties += PROP_GHOSTING_ALLOWED; @@ -235,6 +245,8 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits propertyFlags, propertiesDidntFit, propertyCount, appendState); _bloomProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + _audioProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, getFlyingAllowed()); APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, getGhostingAllowed()); @@ -267,6 +279,7 @@ void ZoneEntityItem::debugDump() const { _skyboxProperties.debugDump(); _hazeProperties.debugDump(); _bloomProperties.debugDump(); + _audioProperties.debugDump(); } void ZoneEntityItem::setShapeType(ShapeType type) { diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 2b61bbd346..ccf895ca44 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -22,6 +22,7 @@ #include "SkyboxPropertyGroup.h" #include "HazePropertyGroup.h" #include "BloomPropertyGroup.h" +#include "ZoneAudioPropertyGroup.h" class ZoneEntityItem : public EntityItem { public: @@ -46,10 +47,9 @@ public: OctreeElement::AppendState& appendState) const override; virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; static bool getZonesArePickable() { return _zonesArePickable; } @@ -90,6 +90,7 @@ public: const HazePropertyGroup& getHazeProperties() const { return _hazeProperties; } const BloomPropertyGroup& getBloomProperties() const { return _bloomProperties; } + const ZoneAudioPropertyGroup& getAudioProperties() const { return _audioProperties; } bool getFlyingAllowed() const { return _flyingAllowed; } void setFlyingAllowed(bool value) { _flyingAllowed = value; } @@ -152,6 +153,7 @@ protected: SkyboxPropertyGroup _skyboxProperties; HazePropertyGroup _hazeProperties; BloomPropertyGroup _bloomProperties; + ZoneAudioPropertyGroup _audioProperties; bool _flyingAllowed { DEFAULT_FLYING_ALLOWED }; bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED }; @@ -167,7 +169,7 @@ protected: bool _keyLightPropertiesChanged { false }; bool _ambientLightPropertiesChanged { false }; bool _skyboxPropertiesChanged { false }; - bool _hazePropertiesChanged{ false }; + bool _hazePropertiesChanged { false }; bool _bloomPropertiesChanged { false }; static bool _drawZoneBoundaries; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 33ed430e41..6f84f25e86 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -293,6 +293,7 @@ enum class EntityVersion : PacketVersion { Mirror, EntityTags, WantsKeyboardFocus, + AudioZones, // Add new versions above here NUM_PACKET_TYPE, diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index 04c7d61c68..b4597e9141 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -158,6 +158,21 @@ "bloom.bloomSize": { "tooltip": "The radius of bloom. The higher the value, the larger the bloom." }, + "audio.reverbEnabled": { + "tooltip": "If reverb should be enabled for listeners in this zone." + }, + "audio.reverbTime": { + "tooltip": "The time (seconds) for the reverb tail to decay by 60dB." + }, + "audio.reverbWetLevel": { + "tooltip": "Adjusts the wet/dry percentage, from completely dry (0%) to completely wet (100%)." + }, + "audio.listenerZones": { + "tooltip": "A list of entity IDs representing listener zones with this zone as a source." + }, + "audio.listenerAttenuationCoefficients": { + "tooltip": "A list of attenuation coefficients." + }, "avatarPriority": { "tooltip": "Alter Avatars' update priorities." }, diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 140c893399..8687988c57 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -620,6 +620,42 @@ const GROUPS = [ } ] }, + { + id: "zone_audio", + label: "ZONE AUDIO", + properties: [ + { + label: "Enable Reverb", + type: "bool", + propertyID: "audio.enableReverb" + }, + { + label: "Reverb Time", + type: "number-draggable", + min: 0, + max: 10, + step: 0.1, + decimals: 2, + propertyID: "audio.reverbTime", + showPropertyRule: { "audio.enableReverb": "true" }, + }, + { + label: "Reverb Wet Level", + type: "number-draggable", + min: 0, + max: 100, + step: 1, + decimals: 1, + propertyID: "audio.reverbWetLevel", + showPropertyRule: { "audio.enableReverb": "true" }, + }, + { + label: "Listener Zones", + type: "multipleZonesSelection", + propertyID: "audio.listenerZones", + } + ] + }, { id: "model", label: "MODEL", @@ -1764,7 +1800,7 @@ const GROUPS_PER_TYPE = { Shape: [ 'base', 'shape', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Text: [ 'base', 'text', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Zone: [ 'base', 'zone', 'zone_key_light', 'zone_skybox', 'zone_ambient_light', 'zone_haze', - 'zone_bloom', 'zone_avatar_priority', 'spatial', 'behavior', 'scripts', 'physics' ], + 'zone_bloom', 'zone_avatar_priority', 'zone_audio', 'spatial', 'behavior', 'scripts', 'physics' ], Model: [ 'base', 'model', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Image: [ 'base', 'image', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Web: [ 'base', 'web', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], diff --git a/scripts/system/create/entityProperties/html/tabs/zone_audio.png b/scripts/system/create/entityProperties/html/tabs/zone_audio.png new file mode 100644 index 0000000000000000000000000000000000000000..6e7adf8fa50949a26577e637b2a2d19cbf88a138 GIT binary patch literal 598 zcmWm8O-Pe*0LSs)^X%EP7oX+BL^Bio!_XvjIM$o?vS+^J7HCsEC`GQhmIon4R+%fG zO-u_(n+P^WtwgW_YoOFY$l|Fs@?afIJcMONvx_SOCD~$!@9lH=-m=wJYt%Vv02-^M zvK~NoDkTeG<;w9b0IGINQzwv?k%~efm=erIbB!4Y-|_sb8v#ICEcG?OwR}MA1-6rb z7z5lTz`7kM8v}M<@;ARd2~hLaN^`^2Wye-BnXDiQwPf-xX$JO$HcuazmG4))F+Pv% zD_@)TZe{R2vU?g{DB?KolQ7E;q$BQTmH4>LO|BTU-EzKJ7%}adcp&RA@O!BaT7b80 zM1~FH+2+@jJKn;s({*G~p(Mj{ZkholF%rp(Xdfp`+JbiqJt#)~v{?vR zoi7vBFNB-{ zr86izX`7UP7ndj^ww^mHWB5YYmAPYMbK6Hbp8w#KT1xh zq-PID`$_{hKij$*3;Y!$uNY}4q<_&Dh$?u2TX20)kc3jwFL0~$nr_u?8I3xNes|bP ei^&anW;eszaqvOxZr+iNAOKjaYAdJ8?Y{rIU&;jl literal 0 HcmV?d00001 From af09c0a4866e2737d232ac168ea0a69319f97500 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Tue, 12 Mar 2024 16:12:44 -0700 Subject: [PATCH 024/109] fix audio zones in create --- .../html/js/entityProperties.js | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 8687988c57..8a1540d411 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -627,7 +627,7 @@ const GROUPS = [ { label: "Enable Reverb", type: "bool", - propertyID: "audio.enableReverb" + propertyID: "audio.reverbEnabled" }, { label: "Reverb Time", @@ -637,7 +637,7 @@ const GROUPS = [ step: 0.1, decimals: 2, propertyID: "audio.reverbTime", - showPropertyRule: { "audio.enableReverb": "true" }, + showPropertyRule: { "audio.reverbEnabled": "true" }, }, { label: "Reverb Wet Level", @@ -647,7 +647,7 @@ const GROUPS = [ step: 1, decimals: 1, propertyID: "audio.reverbWetLevel", - showPropertyRule: { "audio.enableReverb": "true" }, + showPropertyRule: { "audio.reverbEnabled": "true" }, }, { label: "Listener Zones", @@ -2883,10 +2883,18 @@ function createVec2Property(property, elProperty) { function updateVectorMinMax(property) { let min = property.data.min; let max = property.data.max; - property.elNumberX.updateMinMax(min, max); - property.elNumberY.updateMinMax(min, max); - if (property.elNumberZ) { - property.elNumberZ.updateMinMax(min, max); + if (property.elNumberX) { + property.elNumberX.updateMinMax(min, max); + property.elNumberY.updateMinMax(min, max); + if (property.elNumberZ) { + property.elNumberZ.updateMinMax(min, max); + } + } else if (property.elNumberR) { + property.elNumberR.updateMinMax(min, max); + property.elNumberG.updateMinMax(min, max); + if (property.elNumberB) { + property.elNumberB.updateMinMax(min, max); + } } } @@ -3904,6 +3912,7 @@ function addZoneToZonesSelection(propertyId, id) { hiddenField.value = JSON.stringify(selectedZones); displaySelectedZones(propertyId, true); let propertyName = propertyId.replace("property-", ""); + propertyName = propertyName.replace("-", "."); updateProperty(propertyName, selectedZones, false); document.getElementById("zones-select-selector-list-panel-" + propertyId).style.display = "none"; } @@ -3921,6 +3930,7 @@ function removeZoneFromZonesSelection(propertyId, zoneId) { hiddenField.value = JSON.stringify(selectedZones); displaySelectedZones(propertyId, true); let propertyName = propertyId.replace("property-", ""); + propertyName = propertyName.replace("-", "."); updateProperty(propertyName, selectedZones, false); } From 8ea3f1956e40d0bdc6cd0e45ac485c7a0f4f774c Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Thu, 14 Mar 2024 15:15:37 -0700 Subject: [PATCH 025/109] set dynamic factory --- assignment-client/src/audio/AudioMixer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 7947582b5c..92719abef6 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -40,6 +40,7 @@ #include "AvatarAudioStream.h" #include "InjectedAudioStream.h" #include "crash-handler/CrashHandler.h" +#include "../AssignmentDynamicFactory.h" #include "../entities/AssignmentParentFinder.h" using namespace std; @@ -64,6 +65,9 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : ThreadedAssignment(message) { + DependencyManager::registerInheritance(); + DependencyManager::set(); + // Always clear settings first // This prevents previous assignment settings from sticking around clearDomainSettings(); From f7c17d6035c1dc52e85c3b0587f967643343972f Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Mon, 27 Nov 2023 21:47:20 -0800 Subject: [PATCH 026/109] new procecural particle entity type --- .../src/RenderableEntityItem.cpp | 5 + .../src/RenderableEntityItem.h | 1 + .../RenderableParticleEffectEntityItem.cpp | 11 +- .../src/RenderableParticleEffectEntityItem.h | 2 + .../src/RenderablePolyLineEntityItem.cpp | 2 +- .../src/RenderablePolyLineEntityItem.h | 1 - ...ableProceduralParticleEffectEntityItem.cpp | 220 ++++++++ ...erableProceduralParticleEffectEntityItem.h | 63 +++ .../entities-renderer/proceduralParticle.slp | 1 + .../src/proceduralParticle.slf | 172 ++++++ .../src/proceduralParticle.slv | 38 ++ .../src/proceduralParticleUpdate.slf | 107 ++++ .../entities/src/EntityItemProperties.cpp | 124 +++++ libraries/entities/src/EntityItemProperties.h | 10 + libraries/entities/src/EntityPropertyFlags.h | 9 + libraries/entities/src/EntityTreeElement.cpp | 4 +- libraries/entities/src/EntityTypes.cpp | 3 + libraries/entities/src/EntityTypes.h | 5 + .../ProceduralParticleEffectEntityItem.cpp | 174 +++++++ .../src/ProceduralParticleEffectEntityItem.h | 88 ++++ libraries/gpu/src/gpu/Transform.slh | 10 + libraries/networking/src/udt/PacketHeaders.h | 1 + .../procedural/src/procedural/Procedural.cpp | 17 +- .../procedural/src/procedural/Procedural.h | 5 + .../procedural/ProceduralParticleCommon.slh | 82 +++ .../src/procedural/ShaderConstants.h | 13 + .../render-utils/src/RenderCommonTask.cpp | 21 + libraries/render-utils/src/RenderCommonTask.h | 10 + .../render-utils/src/RenderDeferredTask.cpp | 10 +- .../render-utils/src/RenderForwardTask.cpp | 6 + libraries/render/src/render/Item.cpp | 7 + libraries/render/src/render/Item.h | 16 + .../src/render/RenderFetchCullSortTask.cpp | 8 +- .../src/render/RenderFetchCullSortTask.h | 1 + libraries/render/src/render/Scene.h | 2 + .../create/assets/data/createAppTooltips.json | 20 +- scripts/system/create/edit.js | 33 +- .../create/entityList/html/js/entityList.js | 1 + .../html/js/entityProperties.js | 492 +++++++++++++++++- .../html/tabs/particles_procedural.png | Bin 0 -> 770 bytes .../entitySelectionTool.js | 2 +- .../system/create/qml/NewParticleDialog.qml | 109 ++++ .../system/create/qml/NewParticleWindow.qml | 20 + scripts/system/html/css/edit-style.css | 30 +- scripts/system/html/js/includes.js | 1 + 45 files changed, 1913 insertions(+), 44 deletions(-) create mode 100644 libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.cpp create mode 100644 libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.h create mode 100644 libraries/entities-renderer/src/entities-renderer/proceduralParticle.slp create mode 100644 libraries/entities-renderer/src/proceduralParticle.slf create mode 100644 libraries/entities-renderer/src/proceduralParticle.slv create mode 100644 libraries/entities-renderer/src/proceduralParticleUpdate.slf create mode 100644 libraries/entities/src/ProceduralParticleEffectEntityItem.cpp create mode 100644 libraries/entities/src/ProceduralParticleEffectEntityItem.h create mode 100644 libraries/procedural/src/procedural/ProceduralParticleCommon.slh create mode 100644 scripts/system/create/entityProperties/html/tabs/particles_procedural.png create mode 100644 scripts/system/create/qml/NewParticleDialog.qml create mode 100644 scripts/system/create/qml/NewParticleWindow.qml diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 09bbfb9284..2b57c8b78a 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -21,6 +21,7 @@ #include "RenderableImageEntityItem.h" #include "RenderableWebEntityItem.h" #include "RenderableParticleEffectEntityItem.h" +#include "RenderableProceduralParticleEffectEntityItem.h" #include "RenderableLineEntityItem.h" #include "RenderablePolyLineEntityItem.h" #include "RenderablePolyVoxEntityItem.h" @@ -379,6 +380,10 @@ EntityRenderer::Pointer EntityRenderer::addToScene(EntityTreeRenderer& renderer, result = make_renderer(entity); break; + case Type::ProceduralParticleEffect: + result = make_renderer(entity); + break; + case Type::Line: result = make_renderer(entity); break; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 99dbffbc72..495eeea220 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -78,6 +78,7 @@ public: ItemID computeMirrorView(ViewFrustum& viewFrustum) const override; static ItemID computeMirrorViewOperator(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation, MirrorMode mirrorMode, const QUuid& portalExitID); + virtual void renderSimulate(RenderArgs* args) override {} protected: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index e2a57840d9..8fa787d413 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -150,6 +150,8 @@ ItemKey ParticleEffectEntityRenderer::getKey() { builder.withSubMetaCulled(); } + builder.withSimulate(); + return builder.build(); } @@ -362,7 +364,11 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa return particle; } -void ParticleEffectEntityRenderer::stepSimulation() { +void ParticleEffectEntityRenderer::renderSimulate(RenderArgs* args) { + if (!_visible || !(_networkTexture && _networkTexture->isLoaded())) { + return; + } + if (_lastSimulated == 0) { _lastSimulated = usecTimestampNow(); return; @@ -436,9 +442,6 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) { return; } - // FIXME migrate simulation to a compute stage - stepSimulation(); - gpu::Batch& batch = *args->_batch; batch.setResourceTexture(0, _networkTexture->getGPUTexture()); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index 547d654486..b3414594c3 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -24,6 +24,8 @@ class ParticleEffectEntityRenderer : public TypedEntityRenderer +#include #include #include diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index 6e5068c24f..75309eca4a 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -13,7 +13,6 @@ #define hifi_RenderablePolyLineEntityItem_h #include "RenderableEntityItem.h" -#include #include namespace render { namespace entities { diff --git a/libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.cpp new file mode 100644 index 0000000000..c9f242b1cb --- /dev/null +++ b/libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.cpp @@ -0,0 +1,220 @@ +// +// RenderableProceduralParticleEffectEntityItem.cpp +// interface/src +// +// Created by HifiExperiements on 11/19/23 +// Copyright 2023 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 "RenderableProceduralParticleEffectEntityItem.h" + +#include +#include + +using namespace render; +using namespace render::entities; + +ProceduralParticleEffectEntityRenderer::ProceduralParticleEffectEntityRenderer(const EntityItemPointer& entity) : + Parent(entity) { + _updateProcedural._vertexSource = shader::Source::get(shader::gpu::vertex::DrawUnitQuadTexcoord); + _updateProcedural._opaqueFragmentSource = shader::Source::get(shader::entities_renderer::fragment::proceduralParticleUpdate); + _updateProcedural.setDoesFade(false); + + _renderProcedural._vertexSource = shader::Source::get(shader::entities_renderer::vertex::proceduralParticle); + _renderProcedural._opaqueFragmentSource = shader::Source::get(shader::entities_renderer::fragment::proceduralParticle); + _renderProcedural._transparentFragmentSource = shader::Source::get(shader::entities_renderer::fragment::proceduralParticle_translucent); + _renderProcedural._transparentState->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + _renderProcedural.setDoesFade(false); +} + +void ProceduralParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { + void* key = (void*)this; + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] { + withWriteLock([&] { + _renderTransform = getModelTransform(); + _renderTransform.postScale(entity->getScaledDimensions()); + }); + }); +} + +void ProceduralParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { + bool needsUpdateDefines = false; + bool needsRecreateParticles = false; + + uint32_t numParticles = entity->getNumParticles(); + if (_numParticles != numParticles) { + _numParticles = numParticles; + _particlePropTextureDim = pow(2, ceil(log2(sqrt(_numParticles)))); + needsUpdateDefines = true; + needsRecreateParticles = true; + } + + uint8_t numTrisPerParticle = entity->getNumTrianglesPerParticle(); + if (_numTrianglesPerParticle != numTrisPerParticle) { + _numTrianglesPerParticle = numTrisPerParticle; + needsUpdateDefines = true; + } + + uint8_t numUpdateProps = entity->getNumUpdateProps(); + if (_numUpdateProps != numUpdateProps) { + _numUpdateProps = numUpdateProps; + needsUpdateDefines = true; + needsRecreateParticles = true; + } + + if (needsRecreateParticles) { + recreateParticles(); + } + + bool particleTransparent = entity->getParticleTransparent(); + if (_transparent != particleTransparent) { + _transparent = particleTransparent; + } + + if (needsUpdateDefines) { + std::unordered_map replacements; + + static const std::string PROCEDURAL_PARTICLE_NUM_PARTICLES = "//PROCEDURAL_PARTICLE_NUM_PARTICLES"; + auto numParticlesDefine = "#undef NUM_PARTICLES\n#define NUM_PARTICLES " + std::to_string(_numParticles); + replacements[PROCEDURAL_PARTICLE_NUM_PARTICLES] = numParticlesDefine; + + static const std::string PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS = "//PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS"; + auto numUpdatePropsDefine = "#undef NUM_UPDATE_PROPS\n#define NUM_UPDATE_PROPS " + std::to_string(_numUpdateProps); + replacements[PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS] = numUpdatePropsDefine; + + static const std::string PROCEDURAL_PARTICLE_NUM_TRIS_PER_PARTICLE = "//PROCEDURAL_PARTICLE_NUM_TRIS_PER_PARTICLE"; + auto numTrisPerParticleDefine = "#undef NUM_TRIS_PER_PARTICLE\n#define NUM_TRIS_PER_PARTICLE " + std::to_string(_numTrianglesPerParticle); + replacements[PROCEDURAL_PARTICLE_NUM_TRIS_PER_PARTICLE] = numTrisPerParticleDefine; + + _updateProcedural.setFragmentReplacements(replacements); + _renderProcedural.setFragmentReplacements(replacements); + _renderProcedural.setVertexReplacements(replacements); + } + + QString particleUpdateData = entity->getParticleUpdateData(); + if (_particleUpdateData != particleUpdateData) { + _particleUpdateData = particleUpdateData; + _updateProcedural.setProceduralData(ProceduralData::parse(particleUpdateData)); + } + + QString particleRenderData = entity->getParticleRenderData(); + if (_particleRenderData != particleRenderData) { + _particleRenderData = particleRenderData; + _renderProcedural.setProceduralData(ProceduralData::parse(particleRenderData)); + } +} + +bool ProceduralParticleEffectEntityRenderer::isTransparent() const { + return _transparent || Parent::isTransparent(); +} + +ItemKey ProceduralParticleEffectEntityRenderer::getKey() { + ItemKey::Builder builder = + ItemKey::Builder().withTypeShape().withTypeMeta().withTagBits(getTagMask()).withLayer(getHifiRenderLayer()); + + if (isTransparent()) { + builder.withTransparent(); + } else if (_canCastShadow) { + builder.withShadowCaster(); + } + + if (_cullWithParent) { + builder.withSubMetaCulled(); + } + + if (!_visible) { + builder.withInvisible(); + } + + if (_numUpdateProps > 0) { + builder.withSimulate(); + } + + return builder.build(); +} + +ShapeKey ProceduralParticleEffectEntityRenderer::getShapeKey() { + auto builder = ShapeKey::Builder().withOwnPipeline(); + + if (isTransparent()) { + builder.withTranslucent(); + } + + if (_primitiveMode == PrimitiveMode::LINES) { + builder.withWireframe(); + } + + return builder.build(); +} + +void ProceduralParticleEffectEntityRenderer::recreateParticles() { + for (auto& buffer : _particleBuffers) { + if (!buffer) { + buffer = FramebufferPointer(gpu::Framebuffer::create(("RenderableProceduralParticleEffectEntity " + _entityID.toString()).toStdString())); + } + + buffer->removeRenderBuffers(); + for (size_t i = 0; i < _numUpdateProps; i++) { + TexturePointer texture = TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA), + (gpu::uint16)_particlePropTextureDim, (gpu::uint16)_particlePropTextureDim, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT))); + texture->setSource(("RenderableProceduralParticleEffectEntity " + _entityID.toString() + " " + (char)i).toStdString()); + buffer->setRenderBuffer((gpu::uint32)i, texture); + } + } +} + +void ProceduralParticleEffectEntityRenderer::renderSimulate(RenderArgs* args) { + PerformanceTimer perfTimer("RenderableProceduralParticleEffectEntityItem::simulate"); + Q_ASSERT(args->_batch); + gpu::Batch& batch = *args->_batch; + + if (!_visible || _numUpdateProps == 0 || !_updateProcedural.isReady()) { + return; + } + + _evenPass = !_evenPass; + + Transform transform; + withReadLock([&] { + transform = _renderTransform; + }); + + glm::ivec4 viewport = glm::ivec4(0, 0, _particleBuffers[!_evenPass]->getWidth(), _particleBuffers[!_evenPass]->getHeight()); + batch.setViewportTransform(viewport); + batch.setFramebuffer(_particleBuffers[_evenPass]); + + for (size_t i = 0; i < _numUpdateProps; i++) { + batch.setResourceTexture((gpu::uint32)(procedural::slot::texture::ParticleProp0 + i), _particleBuffers[!_evenPass]->getRenderBuffer((gpu::uint32)i)); + } + + _updateProcedural.prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey()); + batch.draw(gpu::TRIANGLE_STRIP, 4); +} + +void ProceduralParticleEffectEntityRenderer::doRender(RenderArgs* args) { + PerformanceTimer perfTimer("RenderableProceduralParticleEffectEntityItem::render"); + Q_ASSERT(args->_batch); + gpu::Batch& batch = *args->_batch; + + if (!_visible || (_numUpdateProps > 0 && !_updateProcedural.isReady()) || !_renderProcedural.isReady()) { + return; + } + + Transform transform; + withReadLock([&] { + transform = _renderTransform; + }); + + for (size_t i = 0; i < _numUpdateProps; i++) { + batch.setResourceTexture((gpu::uint32)(procedural::slot::texture::ParticleProp0 + i), _particleBuffers[_evenPass]->getRenderBuffer((gpu::uint32)i)); + } + + _renderProcedural.prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(_transparent)); + + static const size_t VERTEX_PER_TRIANGLE = 3; + batch.drawInstanced((gpu::uint32)_numParticles, gpu::TRIANGLES, (gpu::uint32)(VERTEX_PER_TRIANGLE * _numTrianglesPerParticle)); +} \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.h new file mode 100644 index 0000000000..1445e798b1 --- /dev/null +++ b/libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.h @@ -0,0 +1,63 @@ +// +// RenderableProceduralParticleEffectEntityItem.h +// interface/src/entities +// +// Created by HifiExperiements on 11/19/23 +// Copyright 2023 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_RenderableProceduralParticleEffectEntityItem_h +#define hifi_RenderableProceduralParticleEffectEntityItem_h + +#include "RenderableEntityItem.h" +#include + +#include + +namespace render { namespace entities { + +class ProceduralParticleEffectEntityRenderer : public TypedEntityRenderer { + using Parent = TypedEntityRenderer; + friend class EntityRenderer; + +public: + ProceduralParticleEffectEntityRenderer(const EntityItemPointer& entity); + + virtual void renderSimulate(RenderArgs* args) override; + +protected: + virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; + virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; + + bool isTransparent() const override; + virtual ItemKey getKey() override; + virtual ShapeKey getShapeKey() override; + virtual void doRender(RenderArgs* args) override; + +private: + using TexturePointer = gpu::TexturePointer; + using FramebufferPointer = gpu::FramebufferPointer; + + void recreateParticles(); + + QString _particleUpdateData; + Procedural _updateProcedural; + QString _particleRenderData; + Procedural _renderProcedural; + + size_t _numParticles { 0 }; + size_t _particlePropTextureDim { 128 }; // 2^ceil(log2(sqrt(10,000))) + size_t _numTrianglesPerParticle { particle::DEFAULT_NUM_TRIS_PER }; + size_t _numUpdateProps { particle::DEFAULT_NUM_UPDATE_PROPS }; + bool _transparent { false }; + + std::array _particleBuffers; + bool _evenPass{ true }; +}; + +} } // namespace + +#endif // hifi_RenderableProceduralParticleEffectEntityItem_h diff --git a/libraries/entities-renderer/src/entities-renderer/proceduralParticle.slp b/libraries/entities-renderer/src/entities-renderer/proceduralParticle.slp new file mode 100644 index 0000000000..e5119c55e4 --- /dev/null +++ b/libraries/entities-renderer/src/entities-renderer/proceduralParticle.slp @@ -0,0 +1 @@ +DEFINES translucent:f forward:f \ No newline at end of file diff --git a/libraries/entities-renderer/src/proceduralParticle.slf b/libraries/entities-renderer/src/proceduralParticle.slf new file mode 100644 index 0000000000..e2ad5bf7ff --- /dev/null +++ b/libraries/entities-renderer/src/proceduralParticle.slf @@ -0,0 +1,172 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by HifiExperiements on 11/21/23 +// Copyright 2023 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 +// + +<@if not HIFI_USE_TRANSLUCENT@> + <@include DeferredBufferWrite.slh@> +<@else@> + <@include DefaultMaterials.slh@> + + <@include GlobalLight.slh@> + <$declareEvalGlobalLightingAlphaBlended()$> + + layout(location=0) out vec4 _fragColor0; +<@endif@> + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + +<@include render-utils/ShaderConstants.h@> + +<@include procedural/ProceduralCommon.slh@> +<@include procedural/ProceduralParticleCommon.slh@> +<$declareProceduralParticleRender()$> + +layout(location=0) flat in int particleID; +layout(location=1) in vec4 _positionES; + +#line 1001 +//PROCEDURAL_BLOCK_BEGIN + +vec3 getProceduralColor() { + return vec3(1.0); +} + +float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float shininess) { + return 1.0; +} + +float getProceduralFragment(inout ProceduralFragment proceduralData) { + return 1.0; +} + +float getProceduralFragmentWithPosition(inout ProceduralFragmentWithPosition proceduralData) { + return 1.0; +} + +//PROCEDURAL_BLOCK_END + +#line 2030 +void main(void) { + vec3 normal = vec3(0.0, 1.0, 0.0); + vec3 diffuse = vec3(0.0); + vec3 fresnel = DEFAULT_FRESNEL; + float roughness = DEFAULT_ROUGHNESS; + float metallic = DEFAULT_METALLIC; + vec3 emissive = DEFAULT_EMISSIVE; + float occlusion = DEFAULT_OCCLUSION; + float scattering = DEFAULT_SCATTERING; + float alpha = 1.0; + + float emissiveAmount = 0.0; + +<@if HIFI_USE_TRANSLUCENT@> + TransformCamera cam = getTransformCamera(); + vec3 posEye = _positionES.xyz; +<@endif@> + +#if defined(PROCEDURAL_V1) + diffuse = getProceduralColor().rgb; + emissiveAmount = 1.0; + emissive = vec3(1.0); +#elif defined(PROCEDURAL_V2) + vec3 specular = DEFAULT_SPECULAR; + float shininess = DEFAULT_SHININESS; + emissiveAmount = getProceduralColors(diffuse, specular, shininess); + roughness = max(0.0, 1.0 - shininess / 128.0); + metallic = length(specular); + emissive = vec3(clamp(emissiveAmount, 0.0, 1.0)); +#elif defined(PROCEDURAL_V3) || defined(PROCEDURAL_V4) +#if defined(PROCEDURAL_V3) + ProceduralFragment proceduralData = ProceduralFragment( +#else + TransformCamera cam = getTransformCamera(); + vec4 position = cam._viewInverse * _positionES; + ProceduralFragmentWithPosition proceduralData = ProceduralFragmentWithPosition( + position.xyz, +#endif + normal, + diffuse, + fresnel, + emissive, + alpha, + roughness, + metallic, + occlusion, + scattering + ); + +#if defined(PROCEDURAL_V3) + emissiveAmount = getProceduralFragment(proceduralData); +#else + emissiveAmount = getProceduralFragmentWithPosition(proceduralData); +#endif + normal = proceduralData.normal; + diffuse = proceduralData.diffuse; + fresnel = proceduralData.specular; + roughness = proceduralData.roughness; + metallic = proceduralData.metallic; + emissive = proceduralData.emissive; + occlusion = proceduralData.occlusion; + scattering = proceduralData.scattering; + alpha = proceduralData.alpha; + +#if defined(PROCEDURAL_V4) + position = vec4(proceduralData.position, 1.0); + vec4 posEye4 = cam._view * position; +<@if HIFI_USE_TRANSLUCENT@> + posEye = posEye4.xyz; +<@endif@> + vec4 posClip = cam._projection * posEye4; + gl_FragDepth = 0.5 * (posClip.z / posClip.w + 1.0); +#endif + +#endif + +<@if not HIFI_USE_TRANSLUCENT@> + if (emissiveAmount > 0.0) { + packDeferredFragmentLightmap( + normal, + 1.0, + diffuse, + roughness, + metallic, + emissive); + } else { + packDeferredFragment( + normal, + 1.0, + diffuse, + roughness, + metallic, + emissive, + occlusion, + scattering); + } +<@else@> + if (emissiveAmount > 0.0) { + _fragColor0 = vec4(diffuse, alpha); + } else { + _fragColor0 = vec4(evalGlobalLightingAlphaBlended( + cam._viewInverse, + 1.0, + occlusion, + posEye, + normal, + diffuse, + fresnel, + metallic, + emissive, + roughness, alpha), + alpha); + } +<@endif@> +} diff --git a/libraries/entities-renderer/src/proceduralParticle.slv b/libraries/entities-renderer/src/proceduralParticle.slv new file mode 100644 index 0000000000..4d87399fa2 --- /dev/null +++ b/libraries/entities-renderer/src/proceduralParticle.slv @@ -0,0 +1,38 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by HifiExperiements on 11/21/23 +// Copyright 2023 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 gpu/Transform.slh@> +<$declareStandardTransform()$> + +<@include procedural/ProceduralCommon.slh@> +<@include procedural/ProceduralParticleCommon.slh@> +<$declareProceduralParticleRender()$> + +layout(location=0) flat out int particleID; +layout(location=1) out vec4 _positionES; + +#line 1001 +//PROCEDURAL_BLOCK_BEGIN +vec3 getProceduralVertex(const int particleID) { + return vec3(0.0); +} +//PROCEDURAL_BLOCK_END + +#line 2030 +void main(void) { + particleID = gpu_InstanceID(); + vec4 worldPos = vec4(getProceduralVertex(particleID), 1.0); + + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformWorldToEyeAndClipPos(cam, worldPos, _positionES, gl_Position)$> +} diff --git a/libraries/entities-renderer/src/proceduralParticleUpdate.slf b/libraries/entities-renderer/src/proceduralParticleUpdate.slf new file mode 100644 index 0000000000..2188bdc462 --- /dev/null +++ b/libraries/entities-renderer/src/proceduralParticleUpdate.slf @@ -0,0 +1,107 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by HifiExperiements on 11/21/23 +// Copyright 2023 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 procedural/ProceduralCommon.slh@> +<@include procedural/ProceduralParticleCommon.slh@> + +layout(location=0) in vec2 varTexCoord0; + +#if NUM_UPDATE_PROPS > 0 +layout(location=0) out vec4 _prop0; +#endif +#if NUM_UPDATE_PROPS > 1 +layout(location=1) out vec4 _prop1; +#endif +#if NUM_UPDATE_PROPS > 2 +layout(location=2) out vec4 _prop2; +#endif +#if NUM_UPDATE_PROPS > 3 +layout(location=3) out vec4 _prop3; +#endif +#if NUM_UPDATE_PROPS > 4 +layout(location=4) out vec4 _prop4; +#endif + +#if NUM_UPDATE_PROPS > 0 +struct ParticleUpdateProps { + vec4 prop0; +#if NUM_UPDATE_PROPS > 1 + vec4 prop1; +#endif +#if NUM_UPDATE_PROPS > 2 + vec4 prop2; +#endif +#if NUM_UPDATE_PROPS > 3 + vec4 prop3; +#endif +#if NUM_UPDATE_PROPS > 4 + vec4 prop4; +#endif +}; + +ParticleUpdateProps getParticleProps() { + ParticleUpdateProps particleProps; + particleProps.prop0 = texture(_prop0Texture, varTexCoord0); +#if NUM_UPDATE_PROPS > 1 + particleProps.prop1 = texture(_prop1Texture, varTexCoord0); +#endif +#if NUM_UPDATE_PROPS > 2 + particleProps.prop2 = texture(_prop2Texture, varTexCoord0); +#endif +#if NUM_UPDATE_PROPS > 3 + particleProps.prop3 = texture(_prop3Texture, varTexCoord0); +#endif +#if NUM_UPDATE_PROPS > 4 + particleProps.prop4 = texture(_prop4Texture, varTexCoord0); +#endif + return particleProps; +} +#endif + +#line 1001 +#if NUM_UPDATE_PROPS > 0 +//PROCEDURAL_BLOCK_BEGIN + +void updateParticleProps(const int particleID, inout ParticleUpdateProps particleProps) {} + +//PROCEDURAL_BLOCK_END +#endif + +#line 2030 +void main(void) { +#if NUM_UPDATE_PROPS > 0 + const ivec2 textureDims = textureSize(_prop0Texture, 0); + const ivec2 indexXY = ivec2(gl_FragCoord.xy); + const int particleID = indexXY.x + textureDims.x * indexXY.y; + + if (particleID >= NUM_PARTICLES) { + return; + } + + ParticleUpdateProps particleProps = getParticleProps(); + updateParticleProps(particleID, particleProps); + + _prop0 = particleProps.prop0; +#endif +#if NUM_UPDATE_PROPS > 1 + _prop1 = particleProps.prop1; +#endif +#if NUM_UPDATE_PROPS > 2 + _prop2 = particleProps.prop2; +#endif +#if NUM_UPDATE_PROPS > 3 + _prop3 = particleProps.prop3; +#endif +#if NUM_UPDATE_PROPS > 4 + _prop4 = particleProps.prop4; +#endif +} diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5db64d12e4..5842f09e5d 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -571,6 +571,14 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_SPIN_FINISH, spinFinish); CHECK_PROPERTY_CHANGE(PROP_PARTICLE_ROTATE_WITH_ENTITY, rotateWithEntity); + // Procedural Particles + CHECK_PROPERTY_CHANGE(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, numParticles); + CHECK_PROPERTY_CHANGE(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, numTrianglesPerParticle); + CHECK_PROPERTY_CHANGE(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, numUpdateProps); + CHECK_PROPERTY_CHANGE(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, particleTransparent); + CHECK_PROPERTY_CHANGE(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, particleUpdateData); + CHECK_PROPERTY_CHANGE(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, particleRenderData); + // Model CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); CHECK_PROPERTY_CHANGE(PROP_MODEL_SCALE, modelScale); @@ -882,6 +890,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @see {@link Entities.EntityProperties-ParticleEffect|EntityProperties-ParticleEffect} * @see {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine} * @see {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox} + * @see {@link Entities.EntityProperties-ProceduralParticleEffect|EntityProperties-ProceduralParticleEffect} * @see {@link Entities.EntityProperties-Shape|EntityProperties-Shape} * @see {@link Entities.EntityProperties-Sphere|EntityProperties-Sphere} * @see {@link Entities.EntityProperties-Text|EntityProperties-Text} @@ -1319,6 +1328,38 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * Entities.setVoxelSphere(polyVox, position, 0.8, 255); */ +/*@jsdoc + * The "ProceduralParticleEffect" {@link Entities.EntityType|EntityType} displays a particle system that can be + * used to simulate things such as fire, smoke, snow, magic spells, etc. The particles are fully controlled by the provided + * update and rendering shaders on the GPU. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-ProceduralParticleEffect + * @property {number} numParticles=10000 - The number of particles to render. + * @property {number} numTrianglesPerParticle=1 - The number of triangles to render per particle. By default, these triangles + * still need to be positioned in the particleRenderData vertex shader. + * @property {number} numUpdateProps=0 - The number of persistent Vec4 values stored per particle and updated once per frame. + * These can be modified in the particleUpdateData fragment shader and read in the + * particleRenderData vertex/fragment shaders. + * @property {boolean} particleTransparent=false - Whether the particles should render as transparent (with additive blending) + * or opaque. + * @property {ProceduralData} particleUpdateData="" - Used to store {@link ProceduralData} data as a JSON string to control + * per-particle updates if numUpdateProps > 0. You can use JSON.parse() to parse the string + * into a JavaScript object which you can manipulate the properties of, and use JSON.stringify() to convert + * the object into a string to put in the property. + * @property {ProceduralData} particleRenderData="" - Used to store {@link ProceduralData} data as a JSON string to control + * per-particle rendering. You can use JSON.parse() to parse the string into a JavaScript object which you + * can manipulate the properties of, and use JSON.stringify() to convert the object into a string to put in + * the property. + * + * @example TODO + * particles = Entities.addEntity({ + * type: "ProceduralParticleEffect", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -4 })), + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + /*@jsdoc * The "Shape" {@link Entities.EntityType|EntityType} displays an entity of a specified shape. * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. @@ -1759,6 +1800,16 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARTICLE_ROTATE_WITH_ENTITY, rotateWithEntity); } + // Procedural Particles + if (_type == EntityTypes::ProceduralParticleEffect) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, numParticles); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, numTrianglesPerParticle); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, numUpdateProps); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, particleTransparent); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, particleUpdateData); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, particleRenderData); + } + // Models only if (_type == EntityTypes::Model) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString()); @@ -2173,6 +2224,14 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h COPY_PROPERTY_FROM_QSCRIPTVALUE(spinFinish, float, setSpinFinish); COPY_PROPERTY_FROM_QSCRIPTVALUE(rotateWithEntity, bool, setRotateWithEntity); + // Procedural Particles + COPY_PROPERTY_FROM_QSCRIPTVALUE(numParticles, uint32_t, setNumParticles); + COPY_PROPERTY_FROM_QSCRIPTVALUE(numTrianglesPerParticle, uint8_t, setNumTrianglesPerParticle); + COPY_PROPERTY_FROM_QSCRIPTVALUE(numUpdateProps, uint8_t, setNumUpdateProps); + COPY_PROPERTY_FROM_QSCRIPTVALUE(particleTransparent, bool, setParticleTransparent); + COPY_PROPERTY_FROM_QSCRIPTVALUE(particleUpdateData, QString, setParticleUpdateData); + COPY_PROPERTY_FROM_QSCRIPTVALUE(particleRenderData, QString, setParticleRenderData); + // Model COPY_PROPERTY_FROM_QSCRIPTVALUE(modelURL, QString, setModelURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(modelScale, vec3, setModelScale); @@ -2459,6 +2518,14 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(spinFinish); COPY_PROPERTY_IF_CHANGED(rotateWithEntity); + // Procedural Particles + COPY_PROPERTY_IF_CHANGED(numParticles); + COPY_PROPERTY_IF_CHANGED(numTrianglesPerParticle); + COPY_PROPERTY_IF_CHANGED(numUpdateProps); + COPY_PROPERTY_IF_CHANGED(particleTransparent); + COPY_PROPERTY_IF_CHANGED(particleUpdateData); + COPY_PROPERTY_IF_CHANGED(particleRenderData); + // Model COPY_PROPERTY_IF_CHANGED(modelURL); COPY_PROPERTY_IF_CHANGED(modelScale); @@ -2812,6 +2879,17 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr particle::MINIMUM_PARTICLE_SPIN, particle::MAXIMUM_PARTICLE_SPIN); ADD_PROPERTY_TO_MAP(PROP_PARTICLE_ROTATE_WITH_ENTITY, RotateWithEntity, rotateWithEntity, float); + // Procedural Particles + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, NumParticles, numParticles, uint32_t, + particle::MINIMUM_MAX_PARTICLES, particle::MAXIMUM_NUM_PROCEDURAL_PARTICLES); + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, NumTrianglesPerParticle, numTrianglesPerParticle, uint8_t, + particle::MINIMUM_TRIS_PER, particle::MAXIMUM_TRIS_PER); + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, NumUpdateProps, numUpdateProps, uint8_t, + particle::MINIMUM_NUM_UPDATE_PROPS, particle::MAXIMUM_NUM_UPDATE_PROPS); + ADD_PROPERTY_TO_MAP(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, ParticleTransparent, particleTransparent, bool); + ADD_PROPERTY_TO_MAP(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, ParticleUpdateData, particleUpdateData, QString); + ADD_PROPERTY_TO_MAP(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, ParticleRenderData, particleRenderData, QString); + // Model ADD_PROPERTY_TO_MAP(PROP_MODEL_URL, ModelURL, modelURL, QString); ADD_PROPERTY_TO_MAP(PROP_MODEL_SCALE, ModelScale, modelScale, vec3); @@ -3250,6 +3328,15 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, properties.getRotateWithEntity()) } + if (properties.getType() == EntityTypes::ProceduralParticleEffect) { + APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, properties.getNumParticles()); + APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, properties.getNumTrianglesPerParticle()); + APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, properties.getNumUpdateProps()); + APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, properties.getParticleTransparent()); + APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, properties.getParticleUpdateData()); + APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, properties.getParticleRenderData()); + } + if (properties.getType() == EntityTypes::Model) { APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)(properties.getShapeType())); APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL()); @@ -3733,6 +3820,15 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_ROTATE_WITH_ENTITY, bool, setRotateWithEntity); } + if (properties.getType() == EntityTypes::ProceduralParticleEffect) { + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, uint32_t, setNumParticles); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, uint8_t, setNumTrianglesPerParticle); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, uint8_t, setNumUpdateProps); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, bool, setParticleTransparent); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, QString, setParticleUpdateData); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, QString, setParticleRenderData); + } + if (properties.getType() == EntityTypes::Model) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); @@ -4122,6 +4218,14 @@ void EntityItemProperties::markAllChanged() { _spinSpreadChanged = true; _rotateWithEntityChanged = true; + // Procedural Particles + _numParticlesChanged = true; + _numTrianglesPerParticleChanged = true; + _numUpdatePropsChanged = true; + _particleTransparentChanged = true; + _particleUpdateDataChanged = true; + _particleRenderDataChanged = true; + // Model _modelURLChanged = true; _modelScaleChanged = true; @@ -4652,6 +4756,26 @@ QList EntityItemProperties::listChangedProperties() { out += "rotateWithEntity"; } + // Procedural Particles + if (numParticlesChanged()) { + out += "numParticles"; + } + if (numTrianglesPerParticleChanged()) { + out += "numTrianglesPerParticle"; + } + if (numUpdatePropsChanged()) { + out += "numUpdateProps"; + } + if (particleTransparentChanged()) { + out += "particleTransparent"; + } + if (particleUpdateDataChanged()) { + out += "particleUpdateData"; + } + if (particleRenderDataChanged()) { + out += "particleRenderData"; + } + // Model if (modelURLChanged()) { out += "modelURL"; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 6340313f2d..78e69f98b7 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -48,6 +48,7 @@ #include "TextEntityItem.h" #include "WebEntityItem.h" #include "ParticleEffectEntityItem.h" +#include "ProceduralParticleEffectEntityItem.h" #include "LineEntityItem.h" #include "PolyVoxEntityItem.h" #include "GridEntityItem.h" @@ -116,6 +117,7 @@ class EntityItemProperties { friend class ImageEntityItem; friend class WebEntityItem; friend class ParticleEffectEntityItem; + friend class ProceduralParticleEffectEntityItem; friend class LineEntityItem; friend class PolyLineEntityItem; friend class PolyVoxEntityItem; @@ -288,6 +290,14 @@ public: DEFINE_PROPERTY(PROP_SPIN_FINISH, SpinFinish, spinFinish, float, particle::DEFAULT_SPIN_FINISH); DEFINE_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, RotateWithEntity, rotateWithEntity, bool, particle::DEFAULT_ROTATE_WITH_ENTITY); + // Procedural Particles + DEFINE_PROPERTY_REF(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, NumParticles, numParticles, uint32_t, particle::DEFAULT_NUM_PROCEDURAL_PARTICLES); + DEFINE_PROPERTY_REF(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, NumTrianglesPerParticle, numTrianglesPerParticle, uint8_t, particle::DEFAULT_NUM_TRIS_PER); + DEFINE_PROPERTY_REF(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, NumUpdateProps, numUpdateProps, uint8_t, particle::DEFAULT_NUM_UPDATE_PROPS); + DEFINE_PROPERTY_REF(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, ParticleTransparent, particleTransparent, bool, false); + DEFINE_PROPERTY_REF(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, ParticleUpdateData, particleUpdateData, QString, ""); + DEFINE_PROPERTY_REF(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, ParticleRenderData, particleRenderData, QString, ""); + // Model DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString, ""); DEFINE_PROPERTY_REF(PROP_MODEL_SCALE, ModelScale, modelScale, glm::vec3, glm::vec3(1.0f)); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index ca96c04fd8..4724c8654d 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -5,6 +5,7 @@ // Created by Brad Hefta-Gaub on 12/4/13. // Copyright 2013 High Fidelity, Inc. // Copyright 2020 Vircadia contributors. +// Copyright 2023 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 @@ -216,6 +217,14 @@ enum EntityPropertyList { PROP_SPIN_SPREAD = PROP_DERIVED_29, PROP_PARTICLE_ROTATE_WITH_ENTITY = PROP_DERIVED_30, + // Procedural Particles + PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES = PROP_DERIVED_0, + PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER = PROP_DERIVED_1, + PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS = PROP_DERIVED_2, + PROP_PROCEDURAL_PARTICLE_TRANSPARENT = PROP_DERIVED_3, + PROP_PROCEDURAL_PARTCILE_UPDATE_DATA = PROP_DERIVED_4, + PROP_PROCEDURAL_PARTCILE_RENDER_DATA = PROP_DERIVED_5, + // Model PROP_MODEL_URL = PROP_DERIVED_0, PROP_MODEL_SCALE = PROP_DERIVED_1, diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 32c791da33..01d998d796 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -258,7 +258,7 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori } else { // if the entity type doesn't support a detailed intersection, then just return the non-AABox results // Never intersect with particle entities - if (localDistance < distance && entity->getType() != EntityTypes::ParticleEffect) { + if (localDistance < distance && (entity->getType() != EntityTypes::ParticleEffect || entity->getType() != EntityTypes::ProceduralParticleEffect)) { distance = localDistance; face = localFace; surfaceNormal = glm::vec3(rotation * glm::vec4(localSurfaceNormal, 0.0f)); @@ -410,7 +410,7 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3 } else { // if the entity type doesn't support a detailed intersection, then just return the non-AABox results // Never intersect with particle entities - if (localDistance < parabolicDistance && entity->getType() != EntityTypes::ParticleEffect) { + if (localDistance < parabolicDistance && (entity->getType() != EntityTypes::ParticleEffect || entity->getType() != EntityTypes::ProceduralParticleEffect)) { parabolicDistance = localDistance; face = localFace; surfaceNormal = glm::vec3(rotation * glm::vec4(localSurfaceNormal, 0.0f)); diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 95057bedbc..fbbe5bb38d 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -4,6 +4,7 @@ // // Created by Brad Hefta-Gaub on 12/4/13. // Copyright 2013 High Fidelity, Inc. +// Copyright 2023 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 @@ -23,6 +24,7 @@ #include "ShapeEntityItem.h" #include "ModelEntityItem.h" #include "ParticleEffectEntityItem.h" +#include "ProceduralParticleEffectEntityItem.h" #include "TextEntityItem.h" #include "ImageEntityItem.h" #include "WebEntityItem.h" @@ -51,6 +53,7 @@ REGISTER_ENTITY_TYPE(Text) REGISTER_ENTITY_TYPE(Image) REGISTER_ENTITY_TYPE(Web) REGISTER_ENTITY_TYPE(ParticleEffect) +REGISTER_ENTITY_TYPE(ProceduralParticleEffect) REGISTER_ENTITY_TYPE(Line) REGISTER_ENTITY_TYPE(PolyLine) REGISTER_ENTITY_TYPE(PolyVox) diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 441e77fccd..9bfa566975 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -4,6 +4,7 @@ // // Created by Brad Hefta-Gaub on 12/4/13. // Copyright 2013 High Fidelity, Inc. +// Copyright 2023 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 @@ -70,6 +71,9 @@ public: * "ParticleEffect"A particle system that can be used to simulate things such as fire, * smoke, snow, magic spells, etc. * {@link Entities.EntityProperties-ParticleEffect|EntityProperties-ParticleEffect} + * "ProceduralParticleEffect"A GPU particle system with custom update and rendering that can + * be used to simulate things such as fire, smoke, snow, magic spells, etc. + * {@link Entities.EntityProperties-ProceduralParticleEffect|EntityProperties-ProceduralParticleEffect} * "Line"A sequence of one or more simple straight lines. * {@link Entities.EntityProperties-Line|EntityProperties-Line} * "PolyLine"A sequence of one or more textured straight lines. @@ -100,6 +104,7 @@ public: Image, Web, ParticleEffect, + ProceduralParticleEffect, Line, PolyLine, PolyVox, diff --git a/libraries/entities/src/ProceduralParticleEffectEntityItem.cpp b/libraries/entities/src/ProceduralParticleEffectEntityItem.cpp new file mode 100644 index 0000000000..a8a601d6f6 --- /dev/null +++ b/libraries/entities/src/ProceduralParticleEffectEntityItem.cpp @@ -0,0 +1,174 @@ +// +// ProceduralParticleEffectEntityItem.cpp +// libraries/entities/src +// +// Created by HifiExperiements on 11/19/23 +// Copyright 2023 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 "ProceduralParticleEffectEntityItem.h" + +#include "EntityTree.h" +#include "EntityTreeElement.h" +#include "EntitiesLogging.h" +#include "EntityScriptingInterface.h" + +EntityItemPointer ProceduralParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + std::shared_ptr entity(new ProceduralParticleEffectEntityItem(entityID), [](ProceduralParticleEffectEntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +// our non-pure virtual subclass for now... +ProceduralParticleEffectEntityItem::ProceduralParticleEffectEntityItem(const EntityItemID& entityItemID) : + EntityItem(entityItemID) +{ + _type = EntityTypes::ProceduralParticleEffect; +} + +EntityItemProperties ProceduralParticleEffectEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + + COPY_ENTITY_PROPERTY_TO_PROPERTIES(numParticles, getNumParticles); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(numTrianglesPerParticle, getNumTrianglesPerParticle); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(numUpdateProps, getNumUpdateProps); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleTransparent, getParticleTransparent); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleUpdateData, getParticleUpdateData); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleRenderData, getParticleRenderData); + + return properties; +} + +bool ProceduralParticleEffectEntityItem::setSubClassProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_PROPERTY_FROM_PROPERTIES(numParticles, setNumParticles); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(numTrianglesPerParticle, setNumTrianglesPerParticle); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(numUpdateProps, setNumUpdateProps); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleTransparent, setParticleTransparent); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleUpdateData, setParticleUpdateData); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleRenderData, setParticleRenderData); + + return somethingChanged; +} + +int ProceduralParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, uint32_t, setNumParticles); + READ_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, uint8_t, setNumTrianglesPerParticle); + READ_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, uint8_t, setNumUpdateProps); + READ_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, bool, setParticleTransparent); + READ_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, QString, setParticleUpdateData); + READ_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, QString, setParticleRenderData); + + return bytesRead; +} + +EntityPropertyFlags ProceduralParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + + requestedProperties += PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES; + requestedProperties += PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER; + requestedProperties += PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS; + requestedProperties += PROP_PROCEDURAL_PARTICLE_TRANSPARENT; + requestedProperties += PROP_PROCEDURAL_PARTCILE_UPDATE_DATA; + requestedProperties += PROP_PROCEDURAL_PARTCILE_RENDER_DATA; + + return requestedProperties; +} + +void ProceduralParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, getNumParticles()); + APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, getNumTrianglesPerParticle()); + APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, getNumUpdateProps()); + APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, getParticleTransparent()); + APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, getParticleUpdateData()); + APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, getParticleRenderData()); +} + +void ProceduralParticleEffectEntityItem::debugDump() const { + quint64 now = usecTimestampNow(); + qCDebug(entities) << "PROC PA EFFECT EntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); +} + +uint32_t ProceduralParticleEffectEntityItem::getNumParticles() const { + return resultWithReadLock([&] { return _numParticles; }); +} + +void ProceduralParticleEffectEntityItem::setNumParticles(uint32_t numParticles) { + withWriteLock([&] { + _needsRenderUpdate |= _numParticles != numParticles; + _numParticles = numParticles; + }); +} + +uint8_t ProceduralParticleEffectEntityItem::getNumTrianglesPerParticle() const { + return resultWithReadLock([&] { return _numTrianglesPerParticle; }); +} + +void ProceduralParticleEffectEntityItem::setNumTrianglesPerParticle(uint8_t numTrianglesPerParticle) { + withWriteLock([&] { + _needsRenderUpdate |= _numTrianglesPerParticle != numTrianglesPerParticle; + _numTrianglesPerParticle = numTrianglesPerParticle; + }); +} + +uint8_t ProceduralParticleEffectEntityItem::getNumUpdateProps() const { + return resultWithReadLock([&] { return _numUpdateProps; }); +} + +void ProceduralParticleEffectEntityItem::setNumUpdateProps(uint8_t numUpdateProps) { + withWriteLock([&] { + _needsRenderUpdate |= _numUpdateProps != numUpdateProps; + _numUpdateProps = numUpdateProps; + }); +} + +void ProceduralParticleEffectEntityItem::setParticleTransparent(bool particleTransparent) { + withWriteLock([&] { + _needsRenderUpdate |= _particleTransparent != particleTransparent; + _particleTransparent = particleTransparent; + }); +} + +QString ProceduralParticleEffectEntityItem::getParticleUpdateData() const { + return resultWithReadLock([&] { return _particleUpdateData; }); +} + +void ProceduralParticleEffectEntityItem::setParticleUpdateData(const QString& particleUpdateData) { + withWriteLock([&] { + _needsRenderUpdate |= _particleUpdateData != particleUpdateData; + _particleUpdateData = particleUpdateData; + }); +} + +QString ProceduralParticleEffectEntityItem::getParticleRenderData() const { + return resultWithReadLock([&] { return _particleRenderData; }); +} + +void ProceduralParticleEffectEntityItem::setParticleRenderData(const QString& particleRenderData) { + withWriteLock([&] { + _needsRenderUpdate |= _particleRenderData != particleRenderData; + _particleRenderData = particleRenderData; + }); +} \ No newline at end of file diff --git a/libraries/entities/src/ProceduralParticleEffectEntityItem.h b/libraries/entities/src/ProceduralParticleEffectEntityItem.h new file mode 100644 index 0000000000..e200f00fbc --- /dev/null +++ b/libraries/entities/src/ProceduralParticleEffectEntityItem.h @@ -0,0 +1,88 @@ +// +// ProceduralParticleEffectEntityItem.h +// libraries/entities/src +// +// Created by HifiExperiements on 11/19/23 +// Copyright 2023 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_ProceduralParticleEffectEntityItem_h +#define hifi_ProceduralParticleEffectEntityItem_h + +#include "EntityItem.h" + +namespace particle { + static const uint32_t DEFAULT_NUM_PROCEDURAL_PARTICLES = 10000; + static const uint32_t MAXIMUM_NUM_PROCEDURAL_PARTICLES = 1024 * 1024; + static const uint8_t DEFAULT_NUM_TRIS_PER = 1; + static const uint8_t MINIMUM_TRIS_PER = 1; + static const uint8_t MAXIMUM_TRIS_PER = 15; + static const uint8_t DEFAULT_NUM_UPDATE_PROPS = 1; + static const uint8_t MINIMUM_NUM_UPDATE_PROPS = 1; + static const uint8_t MAXIMUM_NUM_UPDATE_PROPS = 5; +} + +class ProceduralParticleEffectEntityItem : public EntityItem { +public: + ALLOW_INSTANTIATION // This class can be instantiated + + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + ProceduralParticleEffectEntityItem(const EntityItemID& entityItemID); + + // methods for getting/setting all properties of this entity + virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; + virtual bool setSubClassProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + bool shouldBePhysical() const override { return false; } + + virtual void debugDump() const override; + + virtual bool supportsDetailedIntersection() const override { return false; } + + uint32_t getNumParticles() const; + void setNumParticles(uint32_t numParticles); + + uint8_t getNumTrianglesPerParticle() const; + void setNumTrianglesPerParticle(uint8_t numTrianglesPerParticle); + + uint8_t getNumUpdateProps() const; + void setNumUpdateProps(uint8_t numUpdateProps); + + bool getParticleTransparent() const { return _particleTransparent; } + void setParticleTransparent(bool particleTransparent); + + QString getParticleUpdateData() const; + void setParticleUpdateData(const QString& particleUpdateData); + + QString getParticleRenderData() const; + void setParticleRenderData(const QString& particleRenderData); + +protected: + uint32_t _numParticles; + uint8_t _numTrianglesPerParticle; + uint8_t _numUpdateProps; + bool _particleTransparent; + QString _particleUpdateData; + QString _particleRenderData; +}; + +#endif // hifi_ProceduralParticleEffectEntityItem_h diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index 3015de7e0e..767db13595 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -246,6 +246,16 @@ TransformObject getTransformObject() { } <@endfunc@> +<@func transformWorldToEyeAndClipPos(cameraTransform, worldPos, eyePos, clipPos)@> + { // transformWorldToEyeAndClipPos + vec4 eyeWAPos = <$worldPos$> - vec4(<$cameraTransform$>._viewInverse[3].xyz, 0.0); + <$eyePos$> = vec4((<$cameraTransform$>._view * vec4(eyeWAPos.xyz, 0.0)).xyz, 1.0); + <$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * eyeWAPos; + + <$transformStereoClipsSpace($cameraTransform$, $clipPos$)$> + } +<@endfunc@> + <@func transformModelToWorldPos(objectTransform, modelPos, worldPos)@> { // transformModelToWorldPos <$worldPos$> = (<$objectTransform$>._model * <$modelPos$>); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 6f84f25e86..58a7ca0618 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -294,6 +294,7 @@ enum class EntityVersion : PacketVersion { EntityTags, WantsKeyboardFocus, AudioZones, + ProceduralParticles, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index 66dde1ca56..139da1bf20 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -327,12 +327,12 @@ void Procedural::prepare(gpu::Batch& batch, // Build the fragment and vertex shaders auto versionDefine = "#define PROCEDURAL_V" + std::to_string(_data.version); - fragmentSource.replacements.clear(); + fragmentSource.replacements = _fragmentReplacements; fragmentSource.replacements[PROCEDURAL_VERSION] = versionDefine; if (!_fragmentShaderSource.isEmpty()) { fragmentSource.replacements[PROCEDURAL_BLOCK] = _fragmentShaderSource.toStdString(); } - vertexSource.replacements.clear(); + vertexSource.replacements = _vertexReplacements; vertexSource.replacements[PROCEDURAL_VERSION] = versionDefine; if (!_vertexShaderSource.isEmpty()) { vertexSource.replacements[PROCEDURAL_BLOCK] = _vertexShaderSource.toStdString(); @@ -525,6 +525,19 @@ bool Procedural::hasVertexShader() const { return !_data.vertexShaderUrl.isEmpty(); } + +void Procedural::setVertexReplacements(const std::unordered_map& replacements) { + std::lock_guard lock(_mutex); + _vertexReplacements = replacements; + _shaderDirty = true; +} + +void Procedural::setFragmentReplacements(const std::unordered_map& replacements) { + std::lock_guard lock(_mutex); + _fragmentReplacements = replacements; + _shaderDirty = true; +} + void graphics::ProceduralMaterial::initializeProcedural() { _procedural._vertexSource = gpu::Shader::getVertexShaderSource(shader::render_utils::vertex::simple_procedural); _procedural._vertexSourceSkinned = gpu::Shader::getVertexShaderSource(shader::render_utils::vertex::simple_procedural_deformed); diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index 39c619c687..d343956b17 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -118,6 +118,9 @@ public: bool hasBoundOperator() const { return (bool)_boundOperator; } AABox getBound(RenderArgs* args) { return _boundOperator(args); } + void setVertexReplacements(const std::unordered_map& replacements); + void setFragmentReplacements(const std::unordered_map& replacements); + gpu::Shader::Source _vertexSource; gpu::Shader::Source _vertexSourceSkinned; gpu::Shader::Source _vertexSourceSkinnedDQ; @@ -179,6 +182,8 @@ protected: // Rendering objects UniformLambdas _uniforms; NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS]; + std::unordered_map _vertexReplacements; + std::unordered_map _fragmentReplacements; std::unordered_map _proceduralPipelines; diff --git a/libraries/procedural/src/procedural/ProceduralParticleCommon.slh b/libraries/procedural/src/procedural/ProceduralParticleCommon.slh new file mode 100644 index 0000000000..363cdeae01 --- /dev/null +++ b/libraries/procedural/src/procedural/ProceduralParticleCommon.slh @@ -0,0 +1,82 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by HifiExperiements on 11/21/23 +// Copyright 2023 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 procedural/ShaderConstants.h@> + +#define NUM_PARTICLES 1 +//PROCEDURAL_PARTICLE_NUM_PARTICLES + +#define NUM_TRIS_PER_PARTICLE 3 +//PROCEDURAL_PARTICLE_NUM_TRIS_PER_PARTICLE + +#define NUM_UPDATE_PROPS 0 +//PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS + + +#if NUM_UPDATE_PROPS > 0 +LAYOUT(binding=PROCEDURAL_PARTICLE_TEXTURE_PROP0) uniform sampler2D _prop0Texture; +#endif +#if NUM_UPDATE_PROPS > 1 +LAYOUT(binding=PROCEDURAL_PARTICLE_TEXTURE_PROP1) uniform sampler2D _prop1Texture; +#endif +#if NUM_UPDATE_PROPS > 2 +LAYOUT(binding=PROCEDURAL_PARTICLE_TEXTURE_PROP2) uniform sampler2D _prop2Texture; +#endif +#if NUM_UPDATE_PROPS > 3 +LAYOUT(binding=PROCEDURAL_PARTICLE_TEXTURE_PROP3) uniform sampler2D _prop3Texture; +#endif +#if NUM_UPDATE_PROPS > 4 +LAYOUT(binding=PROCEDURAL_PARTICLE_TEXTURE_PROP4) uniform sampler2D _prop4Texture; +#endif + +<@func declareProceduralParticleRender()@> + +vec4 getParticleProperty(const int propIndex, const int particleID) { + if (propIndex < 0 || propIndex >= NUM_UPDATE_PROPS || particleID < 0 || particleID >= NUM_PARTICLES) { + return vec4(0.0); + } + +#if NUM_UPDATE_PROPS > 0 + const ivec2 textureDims = textureSize(_prop0Texture, 0); + const ivec2 uv = ivec2(particleID % textureDims.x, particleID / textureDims.x); + if (propIndex == 0) { + return texelFetch(_prop0Texture, uv, 0); + } +#endif +#if NUM_UPDATE_PROPS > 1 + else if (propIndex == 1) { + return texelFetch(_prop1Texture, uv, 0); + } +#endif +#if NUM_UPDATE_PROPS > 2 + else if (propIndex == 2) { + return texelFetch(_prop2Texture, uv, 0); + } +#endif +#if NUM_UPDATE_PROPS > 3 + else if (propIndex == 3) { + return texelFetch(_prop3Texture, uv, 0); + } +#endif +#if NUM_UPDATE_PROPS > 4 + else if (propIndex == 4) { + return texelFetch(_prop4Texture, uv, 0); + } +#endif + + return vec4(0.0); +} + +<@endfunc@> + +// hack comment for extra whitespace + diff --git a/libraries/procedural/src/procedural/ShaderConstants.h b/libraries/procedural/src/procedural/ShaderConstants.h index f1336b6479..a70538359f 100644 --- a/libraries/procedural/src/procedural/ShaderConstants.h +++ b/libraries/procedural/src/procedural/ShaderConstants.h @@ -1,6 +1,7 @@ // ("DrawMirrorTask" + std::to_string(mirrorIndex) + "Depth" + std::to_string(depth), setupOutput); } + +void RenderSimulateTask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { + auto args = renderContext->args; + auto items = inputs.get0(); + auto framebuffer = inputs.get1(); + + gpu::doInBatch("RenderSimulateTask::run", args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + + for (ItemBound& item : items) { + args->_scene->simulate(item.id, args); + } + + // Reset render state after each simulate + batch.setFramebuffer(framebuffer); + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + + args->_batch = nullptr; + }); +} \ No newline at end of file diff --git a/libraries/render-utils/src/RenderCommonTask.h b/libraries/render-utils/src/RenderCommonTask.h index cada3cb6ea..255fcb6392 100644 --- a/libraries/render-utils/src/RenderCommonTask.h +++ b/libraries/render-utils/src/RenderCommonTask.h @@ -167,4 +167,14 @@ public: static const size_t MAX_MIRRORS_PER_LEVEL { 3 }; }; +class RenderSimulateTask { +public: + using Inputs = render::VaryingSet2; + using JobModel = render::Job::ModelI; + + RenderSimulateTask() {} + + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); +}; + #endif // hifi_RenderDeferredTask_h diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index fd10452dfa..aa80b5ec6e 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -120,6 +120,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE]; const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; const auto& mirrors = items[RenderFetchCullSortTask::MIRROR]; + const auto& simulateItems = items[RenderFetchCullSortTask::SIMULATE]; 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]; @@ -157,7 +158,12 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto prepareDeferredOutputs = task.addJob("PrepareDeferred", prepareDeferredInputs); const auto deferredFramebuffer = prepareDeferredOutputs.getN(0); const auto lightingFramebuffer = prepareDeferredOutputs.getN(1); - const auto mirrorTargetFramebuffer = prepareDeferredOutputs.getN(2); + const auto mainTargetFramebuffer = prepareDeferredOutputs.getN(2); + + if (depth == 0) { + const auto simulateInputs = RenderSimulateTask::Inputs(simulateItems, mainTargetFramebuffer).asVarying(); + task.addJob("RenderSimulation", simulateInputs); + } // draw a stencil mask in hidden regions of the framebuffer. task.addJob("PrepareStencil", scaledPrimaryFramebuffer); @@ -167,7 +173,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawOpaqueDeferred", opaqueInputs, shapePlumber); if (depth < RenderMirrorTask::MAX_MIRROR_DEPTH) { - const auto mirrorInputs = RenderMirrorTask::Inputs(mirrors, mirrorTargetFramebuffer, jitter).asVarying(); + const auto mirrorInputs = RenderMirrorTask::Inputs(mirrors, mainTargetFramebuffer, 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); } diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 722ba2248c..9b483c16c2 100644 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -92,6 +92,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; const auto& metas = items[RenderFetchCullSortTask::META]; const auto& mirrors = items[RenderFetchCullSortTask::MIRROR]; + const auto& simulateItems = items[RenderFetchCullSortTask::SIMULATE]; 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]; @@ -123,6 +124,11 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto prepareForwardInputs = PrepareForward::Inputs(scaledPrimaryFramebuffer, lightFrame).asVarying(); task.addJob("PrepareForward", prepareForwardInputs); + if (depth == 0) { + const auto simulateInputs = RenderSimulateTask::Inputs(simulateItems, scaledPrimaryFramebuffer).asVarying(); + task.addJob("RenderSimulation", simulateInputs); + } + // draw a stencil mask in hidden regions of the framebuffer. task.addJob("PrepareStencil", scaledPrimaryFramebuffer); diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index 1633523267..f169de5c98 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -147,6 +147,13 @@ namespace render { payload->render(args); } + template <> void payloadRenderSimulate(const PayloadProxyInterface::Pointer& payload, RenderArgs* args) { + if (!args || !payload) { + return; + } + payload->renderSimulate(args); + } + template <> uint32_t metaFetchMetaSubItems(const PayloadProxyInterface::Pointer& payload, ItemIDs& subItems) { if (!payload) { return 0; diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index f91b887fcb..a5cda7cedf 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -111,6 +111,7 @@ public: LAST_LAYER_BIT = FIRST_LAYER_BIT + NUM_LAYER_BITS, MIRROR, + SIMULATE, __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) @@ -165,6 +166,7 @@ public: 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& withSimulate() { _flags.set(SIMULATE); 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 @@ -211,6 +213,9 @@ public: bool isNotMirror() const { return !_flags[MIRROR]; } bool isMirror() const { return _flags[MIRROR]; } + bool isNotSimulate() const { return !_flags[SIMULATE]; } + bool isSimulate() const { return _flags[SIMULATE]; } + 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); } @@ -285,6 +290,9 @@ public: 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& withoutSimulate() { _value.reset(ItemKey::SIMULATE); _mask.set(ItemKey::SIMULATE); return (*this); } + Builder& withSimulate() { _value.set(ItemKey::SIMULATE); _mask.set(ItemKey::SIMULATE); 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); } // Set ALL the tags in one call using the Tag bits and the Tag bits touched @@ -443,6 +451,7 @@ public: virtual const ItemKey getKey() const = 0; virtual const Bound getBound(RenderArgs* args) const = 0; virtual void render(RenderArgs* args) = 0; + virtual void renderSimulate(RenderArgs* args) = 0; virtual const ShapeKey getShapeKey() const = 0; @@ -496,6 +505,9 @@ public: // Render call for the item void render(RenderArgs* args) const { _payload->render(args); } + // Render-side simulate call for the item + void renderSimulate(RenderArgs* args) { _payload->renderSimulate(args); } + // Shape Type Interface const ShapeKey getShapeKey() const; @@ -546,6 +558,7 @@ inline QDebug operator<<(QDebug debug, const Item& item) { template const ItemKey payloadGetKey(const std::shared_ptr& payloadData) { return ItemKey(); } template const Item::Bound payloadGetBound(const std::shared_ptr& payloadData, RenderArgs* args) { return Item::Bound(); } template void payloadRender(const std::shared_ptr& payloadData, RenderArgs* args) { } +template void payloadRenderSimulate(const std::shared_ptr& payloadData, RenderArgs* args) { } // Shape type interface // This allows shapes to characterize their pipeline via a ShapeKey, to be picked with a subclass of Shape. @@ -581,6 +594,7 @@ public: virtual const Item::Bound getBound(RenderArgs* args) const override { return payloadGetBound(_data, args); } virtual void render(RenderArgs* args) override { payloadRender(_data, args); } + virtual void renderSimulate(RenderArgs* args) override { payloadRenderSimulate(_data, args); } // Shape Type interface virtual const ShapeKey getShapeKey() const override { return shapeGetShapeKey(_data); } @@ -645,6 +659,7 @@ public: virtual ShapeKey getShapeKey() = 0; virtual Item::Bound getBound(RenderArgs* args) = 0; virtual void render(RenderArgs* args) = 0; + virtual void renderSimulate(RenderArgs* args) = 0; virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const = 0; virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const = 0; virtual ItemID computeMirrorView(ViewFrustum& viewFrustum) const = 0; @@ -657,6 +672,7 @@ public: template <> const ItemKey payloadGetKey(const PayloadProxyInterface::Pointer& payload); template <> const Item::Bound payloadGetBound(const PayloadProxyInterface::Pointer& payload, RenderArgs* args); template <> void payloadRender(const PayloadProxyInterface::Pointer& payload, RenderArgs* args); +template <> void payloadRenderSimulate(const PayloadProxyInterface::Pointer& payload, RenderArgs* args); 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); diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index 3bdaee25c6..5bb5e75774 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -35,20 +35,22 @@ 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 = 5; + const int NUM_SPATIAL_FILTERS = 6; 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 SIMULATE_BUCKET = 5; const int BACKGROUND_BUCKET = 2; MultiFilterItems::ItemFilterArray spatialFilters = { { ItemFilter::Builder::opaqueShape().withoutMirror(), ItemFilter::Builder::transparentShape(), ItemFilter::Builder::light(), ItemFilter::Builder::meta().withoutMirror(), - ItemFilter::Builder::mirror() + ItemFilter::Builder::mirror(), + ItemFilter::Builder().withSimulate() } }; MultiFilterItems::ItemFilterArray nonspatialFilters = { { ItemFilter::Builder::opaqueShape(), @@ -79,7 +81,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin task.addJob("ClearContainingZones"); - output = Output(BucketList{ opaques, transparents, lights, metas, mirrors, + output = Output(BucketList{ opaques, transparents, lights, metas, mirrors, filteredSpatialBuckets[SIMULATE_BUCKET], 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 9823c2acdf..ffbd815167 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -24,6 +24,7 @@ public: LIGHT, META, MIRROR, + SIMULATE, LAYER_FRONT_OPAQUE_SHAPE, LAYER_FRONT_TRANSPARENT_SHAPE, LAYER_HUD_OPAQUE_SHAPE, diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 1bc282646b..4dbcaa4e8f 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -194,6 +194,8 @@ public: void setItemTransition(ItemID id, Index transitionId); void removeItemTransition(ItemID id); + void simulate(ItemID id, RenderArgs* args) { _items[id].renderSimulate(args); } + protected: // Thread safe elements that can be accessed from anywhere diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index b4597e9141..f174258d09 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -706,5 +706,23 @@ }, "zTextureURL": { "tooltip": "The URL of the texture to map to surfaces perpendicular to the entity's local z-axis. JPG or PNG format." + }, + "numParticles": { + "tooltip": "The maximum number of particles to render at one time." + }, + "numTrianglesPerParticle": { + "tooltip": "The number of triangles to render per particle." + }, + "numUpdateProps": { + "tooltip": "The number of persistent Vec4 per-particle properties to use during simulation and rendering." + }, + "particleTransparent": { + "tooltip": "If the particles should render as transparent (with additive blending) or as opaque." + }, + "particleUpdateData": { + "tooltip": "A JSON description of the shaders, textures, and uniforms to use during particle updating." + }, + "particleRenderData": { + "tooltip": "A JSON description of the shaders, textures, and uniforms to use during particle rendering." } -} +} \ No newline at end of file diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index dabdfe34f9..81f398ab79 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -95,7 +95,7 @@ var ZONE_URL = Script.resolvePath("assets/images/icon-zone.svg"); var MATERIAL_URL = Script.resolvePath("assets/images/icon-material.svg"); - var entityIconOverlayManager = new EntityIconOverlayManager(["Light", "ParticleEffect", "Zone", "Material"], function(entityID) { + var entityIconOverlayManager = new EntityIconOverlayManager(["Light", "ParticleEffect", "ProceduralParticleEffect", "Zone", "Material"], function(entityID) { var properties = Entities.getEntityProperties(entityID, ["type", "isSpotlight", "parentID", "name"]); if (properties.type === "Light") { return { @@ -483,6 +483,9 @@ azimuthStart: -Math.PI, azimuthFinish: Math.PI }, + ProceduralParticleEffect: { + // TODO: what should the default procedural particle be? + }, Light: { color: { red: 255, green: 255, blue: 255 }, intensity: 5.0, @@ -571,7 +574,8 @@ properties.grab = {}; if (Menu.isOptionChecked(MENU_CREATE_ENTITIES_GRABBABLE) && !(properties.type === "Zone" || properties.type === "Light" - || properties.type === "ParticleEffect" || properties.type === "Web")) { + || properties.type === "ParticleEffect" || properties.type == "ProceduralParticleEffect" + || properties.type === "Web")) { properties.grab.grabbable = true; } else { properties.grab.grabbable = false; @@ -859,6 +863,14 @@ } } + function handleNewParticleDialogResult(result) { + if (result) { + createNewEntity({ + type: result.procedural ? "ProceduralParticleEffect" : "ParticleEffect" + }); + } + } + function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml. var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); tablet.popFromStack(); @@ -880,6 +892,13 @@ case "newMaterialDialogCancel": closeExistingDialogWindow(); break; + case "newParticleDialogAdd": + handleNewParticleDialogResult(message.params); + closeExistingDialogWindow(); + break; + case "newParticleDialogCancel": + closeExistingDialogWindow(); + break; case "newPolyVoxDialogAdd": handleNewPolyVoxDialogResult(message.params); closeExistingDialogWindow(); @@ -1046,11 +1065,7 @@ }); }); - addButton("newParticleButton", function () { - createNewEntity({ - type: "ParticleEffect", - }); - }); + addButton("newParticleButton", createNewEntityDialogButtonCallback("Particle")); addButton("newMaterialButton", createNewEntityDialogButtonCallback("Material")); @@ -2041,7 +2056,7 @@ var entityParentIDs = []; var propType = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]).type; - var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"]; + var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect", "ProceduralParticleEffect"]; if (NO_ADJUST_ENTITY_TYPES.indexOf(propType) === -1) { var targetDirection; if (Camera.mode === "entity" || Camera.mode === "independent") { @@ -2594,7 +2609,7 @@ if (data.snapToGrid !== undefined) { entityListTool.setListMenuSnapToGrid(data.snapToGrid); } - } else if (data.type === 'saveUserData' || data.type === 'saveMaterialData') { + } else if (data.type === 'saveUserData' || data.type === 'saveMaterialData' || data.type === 'saveParticleUpdateData' || data.type === 'saveParticleRenderData') { data.ids.forEach(function(entityID) { Entities.editEntity(entityID, data.properties); }); diff --git a/scripts/system/create/entityList/html/js/entityList.js b/scripts/system/create/entityList/html/js/entityList.js index a64d95a64c..f83c334073 100644 --- a/scripts/system/create/entityList/html/js/entityList.js +++ b/scripts/system/create/entityList/html/js/entityList.js @@ -174,6 +174,7 @@ const FILTER_TYPES = [ "Web", "Material", "ParticleEffect", + "ProceduralParticleEffect", "PolyLine", "PolyVox", "Text", diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 8a1540d411..86a2ecfb9c 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -1381,6 +1381,54 @@ const GROUPS = [ } ] }, + { + id: "particles_procedural", + label: "PROCEDURAL PARTICLES", + properties: [ + { + label: "Num Particles", + type: "number-draggable", + propertyID: "numParticles", + min: 1, + max: 1000000 + }, + { + label: "Num Triangles Per Particle", + type: "number-draggable", + propertyID: "numTrianglesPerParticle", + min: 1, + max: 15 + }, + { + label: "Num Particle Update Props", + type: "number-draggable", + propertyID: "numUpdateProps", + min: 1, + max: 5 + }, + { + label: "Transparent", + type: "bool", + propertyID: "particleTransparent", + }, + { + label: "Particle Update Data", + type: "textarea", + buttons: [{ id: "clear", label: "Clear Update Data", className: "secondary_red red", onClick: clearParticleUpdateData }, + { id: "edit", label: "Edit as JSON", className: "secondary", onClick: newJSONParticleUpdateEditor }, + { id: "save", label: "Save Update Data", className: "secondary", onClick: saveParticleUpdateData }], + propertyID: "particleUpdateData", + }, + { + label: "Particle Render Data", + type: "textarea", + buttons: [{ id: "clear", label: "Clear Render Data", className: "secondary_red red", onClick: clearParticleRenderData }, + { id: "edit", label: "Edit as JSON", className: "secondary", onClick: newJSONParticleRenderEditor }, + { id: "save", label: "Save Render Data", className: "secondary", onClick: saveParticleRenderData }], + propertyID: "particleRenderData", + } + ] + }, { id: "polyvox", label: "POLYVOX", @@ -1808,6 +1856,7 @@ const GROUPS_PER_TYPE = { Material: [ 'base', 'material', 'spatial', 'behavior', 'scripts', 'physics' ], ParticleEffect: [ 'base', 'particles', 'particles_emit', 'particles_size', 'particles_color', 'particles_behavior', 'particles_constraints', 'spatial', 'behavior', 'scripts', 'physics' ], + ProceduralParticleEffect: [ 'base', 'particles_procedural', 'spatial', 'behavior', 'scripts', 'physics' ], PolyLine: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], PolyVox: [ 'base', 'polyvox', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Grid: [ 'base', 'grid', 'spatial', 'behavior', 'scripts', 'physics' ], @@ -3786,6 +3835,327 @@ function saveJSONMaterialData(noUpdate, entityIDsToUpdate) { }, EDITOR_TIMEOUT_DURATION); } + +/** + * PROCEDURAL PARTICLE DATA FUNCTIONS + */ + +function clearParticleUpdateData() { + let elParticleUpdateData = getPropertyInputElement("particleUpdateData"); + deleteJSONParticleUpdateEditor(); + elParticleUpdateData.value = ""; + showParticleUpdateDataTextArea(); + showNewJSONParticleUpdateEditorButton(); + hideSaveParticleUpdateDataButton(); + updateProperty('particleUpdateData', elParticleUpdateData.value, false); +} + +function newJSONParticleUpdateEditor() { + getPropertyInputElement("particleUpdateData").classList.remove('multi-diff'); + deleteJSONParticleUpdateEditor(); + createJSONParticleUpdateEditor(); + let data = {}; + setParticleUpdateEditorJSON(data); + hideParticleUpdateDataTextArea(); + hideNewJSONParticleUpdateEditorButton(); + showSaveParticleUpdateDataButton(); +} + +/** + * @param {Set.} [entityIDsToUpdate] Entity IDs to update particleUpdateData for. + */ +function saveParticleUpdateData(entityIDsToUpdate) { + saveJSONParticleUpdateData(true, entityIDsToUpdate); +} + +function setJSONError(property, isError) { + $("#property-"+ property + "-editor").toggleClass('error', isError); + let $propertyParticleUpdateDataEditorStatus = $("#property-"+ property + "-editorStatus"); + $propertyParticleUpdateDataEditorStatus.css('display', isError ? 'block' : 'none'); + $propertyParticleUpdateDataEditorStatus.text(isError ? 'Invalid JSON code - look for red X in your code' : ''); +} + +/** + * @param {boolean} noUpdate - don't update the UI, but do send a property update. + * @param {Set.} [entityIDsToUpdate] - Entity IDs to update particleUpdateData for. + */ +function setParticleUpdateDataFromEditor(noUpdate, entityIDsToUpdate) { + let errorFound = false; + try { + particleUpdateEditor.get(); + } catch (e) { + errorFound = true; + } + + setJSONError('particleUpdateData', errorFound); + + if (errorFound) { + return; + } + + let text = particleUpdateEditor.getText(); + if (noUpdate) { + EventBridge.emitWebEvent( + JSON.stringify({ + ids: [...entityIDsToUpdate], + type: "saveParticleUpdateData", + properties: { + particleUpdateData: text + } + }) + ); + } else { + updateProperty('particleUpdateData', text, false); + } +} + +let particleUpdateEditor = null; + +function createJSONParticleUpdateEditor() { + let container = document.getElementById("property-particleUpdateData-editor"); + let options = { + search: false, + mode: 'tree', + modes: ['code', 'tree'], + name: 'particleUpdateData', + onError: function(e) { + alert('JSON editor:' + e); + }, + onChange: function() { + let currentJSONString = particleUpdateEditor.getText(); + + if (currentJSONString === '{"":""}') { + return; + } + $('#property-particleUpdateData-button-save').attr('disabled', false); + } + }; + particleUpdateEditor = new JSONEditor(container, options); +} + +function showSaveParticleUpdateDataButton() { + $('#property-particleUpdateData-button-save').show(); +} + +function hideSaveParticleUpdateDataButton() { + $('#property-particleUpdateData-button-save').hide(); +} + +function disableSaveParticleUpdateDataButton() { + $('#property-particleUpdateData-button-save').attr('disabled', true); +} + +function showNewJSONParticleUpdateEditorButton() { + $('#property-particleUpdateData-button-edit').show(); +} + +function hideNewJSONParticleUpdateEditorButton() { + $('#property-particleUpdateData-button-edit').hide(); +} + +function showParticleUpdateDataTextArea() { + $('#property-particleUpdateData').show(); +} + +function hideParticleUpdateDataTextArea() { + $('#property-particleUpdateData').hide(); +} + +function hideParticleUpdateDataSaved() { + $('#property-particleUpdateData-saved').hide(); +} + +function setParticleUpdateEditorJSON(json) { + particleUpdateEditor.set(json); + if (particleUpdateEditor.hasOwnProperty('expandAll')) { + particleUpdateEditor.expandAll(); + } +} + +function deleteJSONParticleUpdateEditor() { + if (particleUpdateEditor !== null) { + setJSONError('particleUpdateData', false); + particleUpdateEditor.destroy(); + particleUpdateEditor = null; + } +} + +let savedParticleUpdateJSONTimer = null; + +/** + * @param {boolean} noUpdate - don't update the UI, but do send a property update. + * @param {Set.} [entityIDsToUpdate] Entity IDs to update particleUpdateData for + */ +function saveJSONParticleUpdateData(noUpdate, entityIDsToUpdate) { + setParticleUpdateDataFromEditor(noUpdate, entityIDsToUpdate ? entityIDsToUpdate : selectedEntityIDs); + $('#property-particleUpdateData-saved').show(); + $('#property-particleUpdateData-button-save').attr('disabled', true); + if (savedJSONTimer !== null) { + clearTimeout(savedJSONTimer); + } + savedJSONTimer = setTimeout(function() { + hideParticleUpdateDataSaved(); + }, EDITOR_TIMEOUT_DURATION); +} + +function clearParticleRenderData() { + let elParticleRenderData = getPropertyInputElement("particleRenderData"); + deleteJSONParticleRenderEditor(); + elParticleRenderData.value = ""; + showParticleRenderDataTextArea(); + showNewJSONParticleRenderEditorButton(); + hideSaveParticleRenderDataButton(); + updateProperty('particleRenderData', elParticleRenderData.value, false); +} + +function newJSONParticleRenderEditor() { + getPropertyInputElement("particleRenderData").classList.remove('multi-diff'); + deleteJSONParticleRenderEditor(); + createJSONParticleRenderEditor(); + let data = {}; + setParticleRenderEditorJSON(data); + hideParticleRenderDataTextArea(); + hideNewJSONParticleRenderEditorButton(); + showSaveParticleRenderDataButton(); +} + +/** + * @param {Set.} [entityIDsToUpdate] Entity IDs to update particleRenderData for. + */ +function saveParticleRenderData(entityIDsToUpdate) { + saveJSONParticleRenderData(true, entityIDsToUpdate); +} + +function setJSONError(property, isError) { + $("#property-"+ property + "-editor").toggleClass('error', isError); + let $propertyParticleRenderDataEditorStatus = $("#property-"+ property + "-editorStatus"); + $propertyParticleRenderDataEditorStatus.css('display', isError ? 'block' : 'none'); + $propertyParticleRenderDataEditorStatus.text(isError ? 'Invalid JSON code - look for red X in your code' : ''); +} + +/** + * @param {boolean} noUpdate - don't update the UI, but do send a property update. + * @param {Set.} [entityIDsToUpdate] - Entity IDs to update particleRenderData for. + */ +function setParticleRenderDataFromEditor(noUpdate, entityIDsToUpdate) { + let errorFound = false; + try { + particleRenderEditor.get(); + } catch (e) { + errorFound = true; + } + + setJSONError('particleRenderData', errorFound); + + if (errorFound) { + return; + } + + let text = particleRenderEditor.getText(); + if (noUpdate) { + EventBridge.emitWebEvent( + JSON.stringify({ + ids: [...entityIDsToUpdate], + type: "saveParticleRenderData", + properties: { + particleRenderData: text + } + }) + ); + } else { + updateProperty('particleRenderData', text, false); + } +} + +let particleRenderEditor = null; + +function createJSONParticleRenderEditor() { + let container = document.getElementById("property-particleRenderData-editor"); + let options = { + search: false, + mode: 'tree', + modes: ['code', 'tree'], + name: 'particleRenderData', + onError: function(e) { + alert('JSON editor:' + e); + }, + onChange: function() { + let currentJSONString = particleRenderEditor.getText(); + + if (currentJSONString === '{"":""}') { + return; + } + $('#property-particleRenderData-button-save').attr('disabled', false); + } + }; + particleRenderEditor = new JSONEditor(container, options); +} + +function showSaveParticleRenderDataButton() { + $('#property-particleRenderData-button-save').show(); +} + +function hideSaveParticleRenderDataButton() { + $('#property-particleRenderData-button-save').hide(); +} + +function disableSaveParticleRenderDataButton() { + $('#property-particleRenderData-button-save').attr('disabled', true); +} + +function showNewJSONParticleRenderEditorButton() { + $('#property-particleRenderData-button-edit').show(); +} + +function hideNewJSONParticleRenderEditorButton() { + $('#property-particleRenderData-button-edit').hide(); +} + +function showParticleRenderDataTextArea() { + $('#property-particleRenderData').show(); +} + +function hideParticleRenderDataTextArea() { + $('#property-particleRenderData').hide(); +} + +function hideParticleRenderDataSaved() { + $('#property-particleRenderData-saved').hide(); +} + +function setParticleRenderEditorJSON(json) { + particleRenderEditor.set(json); + if (particleRenderEditor.hasOwnProperty('expandAll')) { + particleRenderEditor.expandAll(); + } +} + +function deleteJSONParticleRenderEditor() { + if (particleRenderEditor !== null) { + setJSONError('particleRenderData', false); + particleRenderEditor.destroy(); + particleRenderEditor = null; + } +} + +let savedParticleRenderJSONTimer = null; + +/** + * @param {boolean} noUpdate - don't update the UI, but do send a property update. + * @param {Set.} [entityIDsToUpdate] Entity IDs to update particleRenderData for + */ +function saveJSONParticleRenderData(noUpdate, entityIDsToUpdate) { + setParticleRenderDataFromEditor(noUpdate, entityIDsToUpdate ? entityIDsToUpdate : selectedEntityIDs); + $('#property-particleRenderData-saved').show(); + $('#property-particleRenderData-button-save').attr('disabled', true); + if (savedJSONTimer !== null) { + clearTimeout(savedJSONTimer); + } + savedJSONTimer = setTimeout(function() { + hideParticleRenderDataSaved(); + }, EDITOR_TIMEOUT_DURATION); +} + function bindAllNonJSONEditorElements() { let inputs = $('input'); let i; @@ -3796,7 +4166,9 @@ function bindAllNonJSONEditorElements() { // an outer scoped variable may lead to confusing semantics. field.on('focus', function(e) { if (e.target.id === "property-userData-button-edit" || e.target.id === "property-userData-button-clear" || - e.target.id === "property-materialData-button-edit" || e.target.id === "property-materialData-button-clear") { + e.target.id === "property-materialData-button-edit" || e.target.id === "property-materialData-button-clear" || + e.target.id === "property-particleUpdateData-button-edit" || e.target.id === "property-particleUpdateData-button-clear" || + e.target.id === "property-particleRenderData-button-edit" || e.target.id === "property-particleRenderData-button-clear") { return; } if ($('#property-userData-editor').css('height') !== "0px") { @@ -3805,6 +4177,12 @@ function bindAllNonJSONEditorElements() { if ($('#property-materialData-editor').css('height') !== "0px") { saveMaterialData(); } + if ($('#property-particleUpdateData-editor').css('height') !== "0px") { + saveParticleUpdateData(); + } + if ($('#property-particleRenderData-editor').css('height') !== "0px") { + saveParticleRenderData(); + } }); } } @@ -4213,6 +4591,8 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { if (selections.length === 0) { deleteJSONEditor(); deleteJSONMaterialEditor(); + deleteJSONParticleUpdateEditor(); + deleteJSONParticleRenderEditor(); resetProperties(); showGroupsForType("None"); @@ -4231,6 +4611,16 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { showSaveMaterialDataButton(); showNewJSONMaterialEditorButton(); + getPropertyInputElement("particleUpdateData").value = ""; + showParticleUpdateDataTextArea(); + showSaveParticleUpdateDataButton(); + showNewJSONParticleUpdateEditorButton(); + + getPropertyInputElement("particleRenderData").value = ""; + showParticleRenderDataTextArea(); + showSaveParticleRenderDataButton(); + showNewJSONParticleRenderEditorButton(); + setCopyPastePositionAndRotationAvailability (selections.length, true); disableProperties(); @@ -4268,6 +4658,8 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { enableProperties(); disableSaveUserDataButton(); disableSaveMaterialDataButton(); + disableSaveParticleUpdateDataButton(); + disableSaveParticleRenderDataButton(); setCopyPastePositionAndRotationAvailability (selections.length, false); } @@ -4536,6 +4928,70 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { requestMaterialTarget(); } + let particleUpdateDataMultiValue = getMultiplePropertyValue("particleUpdateData"); + let particleUpdateDataTextArea = getPropertyInputElement("particleUpdateData"); + let particleUpdateJSON = null; + if (!particleUpdateDataMultiValue.isMultiDiffValue) { + try { + particleUpdateJSON = JSON.parse(particleUpdateDataMultiValue.value); + } catch (e) { + + } + } + if (particleUpdateJSON !== null && !lockedMultiValue.isMultiDiffValue && !lockedMultiValue.value) { + if (particleUpdateEditor === null) { + createJSONParticleUpdateEditor(); + } + particleUpdateDataTextArea.classList.remove('multi-diff'); + setParticleUpdateEditorJSON(particleUpdateJSON); + showSaveParticleUpdateDataButton(); + hideParticleUpdateDataTextArea(); + hideNewJSONParticleUpdateEditorButton(); + hideParticleUpdateDataSaved(); + } else { + // normal text + deleteJSONParticleUpdateEditor(); + particleUpdateDataTextArea.classList.toggle('multi-diff', particleUpdateDataMultiValue.isMultiDiffValue); + particleUpdateDataTextArea.value = particleUpdateDataMultiValue.isMultiDiffValue ? "" : particleUpdateDataMultiValue.value; + + showParticleUpdateDataTextArea(); + showNewJSONParticleUpdateEditorButton(); + hideSaveParticleUpdateDataButton(); + hideParticleUpdateDataSaved(); + } + + let particleRenderDataMultiValue = getMultiplePropertyValue("particleRenderData"); + let particleRenderDataTextArea = getPropertyInputElement("particleRenderData"); + let particleRenderJSON = null; + if (!particleRenderDataMultiValue.isMultiDiffValue) { + try { + particleRenderJSON = JSON.parse(particleRenderDataMultiValue.value); + } catch (e) { + + } + } + if (particleRenderJSON !== null && !lockedMultiValue.isMultiDiffValue && !lockedMultiValue.value) { + if (particleRenderEditor === null) { + createJSONParticleRenderEditor(); + } + particleRenderDataTextArea.classList.remove('multi-diff'); + setParticleRenderEditorJSON(particleRenderJSON); + showSaveParticleRenderDataButton(); + hideParticleRenderDataTextArea(); + hideNewJSONParticleRenderEditorButton(); + hideParticleRenderDataSaved(); + } else { + // normal text + deleteJSONParticleRenderEditor(); + particleRenderDataTextArea.classList.toggle('multi-diff', particleRenderDataMultiValue.isMultiDiffValue); + particleRenderDataTextArea.value = particleRenderDataMultiValue.isMultiDiffValue ? "" : particleRenderDataMultiValue.value; + + showParticleRenderDataTextArea(); + showNewJSONParticleRenderEditorButton(); + hideSaveParticleRenderDataButton(); + hideParticleRenderDataSaved(); + } + let activeElement = document.activeElement; if (doSelectElement && typeof activeElement.select !== "undefined") { activeElement.select(); @@ -4833,6 +5289,37 @@ function loaded() { elDiv.insertBefore(elMaterialDataEditor, elMaterialData); elDiv.insertBefore(elMaterialDataEditorStatus, elMaterialData); + // Particle Update + Render Data + let particleUpdateDataProperty = properties["particleUpdateData"]; + let elParticleUpdateData = particleUpdateDataProperty.elInput; + let particleUpdateDataElementID = particleUpdateDataProperty.elementID; + elDiv = elParticleUpdateData.parentNode; + let elParticleUpdateDataEditor = document.createElement('div'); + elParticleUpdateDataEditor.setAttribute("id", particleUpdateDataElementID + "-editor"); + let elParticleUpdateDataEditorStatus = document.createElement('div'); + elParticleUpdateDataEditorStatus.setAttribute("id", particleUpdateDataElementID + "-editorStatus"); + let elParticleUpdateDataSaved = document.createElement('span'); + elParticleUpdateDataSaved.setAttribute("id", particleUpdateDataElementID + "-saved"); + elParticleUpdateDataSaved.innerText = "Saved!"; + elDiv.childNodes[JSON_EDITOR_ROW_DIV_INDEX].appendChild(elParticleUpdateDataSaved); + elDiv.insertBefore(elParticleUpdateDataEditor, elParticleUpdateData); + elDiv.insertBefore(elParticleUpdateDataEditorStatus, elParticleUpdateData); + + let particleRenderDataProperty = properties["particleRenderData"]; + let elParticleRenderData = particleRenderDataProperty.elInput; + let particleRenderDataElementID = particleRenderDataProperty.elementID; + elDiv = elParticleRenderData.parentNode; + let elParticleRenderDataEditor = document.createElement('div'); + elParticleRenderDataEditor.setAttribute("id", particleRenderDataElementID + "-editor"); + let elParticleRenderDataEditorStatus = document.createElement('div'); + elParticleRenderDataEditorStatus.setAttribute("id", particleRenderDataElementID + "-editorStatus"); + let elParticleRenderDataSaved = document.createElement('span'); + elParticleRenderDataSaved.setAttribute("id", particleRenderDataElementID + "-saved"); + elParticleRenderDataSaved.innerText = "Saved!"; + elDiv.childNodes[JSON_EDITOR_ROW_DIV_INDEX].appendChild(elParticleRenderDataSaved); + elDiv.insertBefore(elParticleRenderDataEditor, elParticleRenderData); + elDiv.insertBefore(elParticleRenderDataEditorStatus, elParticleRenderData); + // Textarea scrollbars let elTextareas = document.getElementsByTagName("TEXTAREA"); @@ -4946,7 +5433,8 @@ function loaded() { return; } - if (elUserDataEditor.contains(keyUpEvent.target) || elMaterialDataEditor.contains(keyUpEvent.target)) { + if (elUserDataEditor.contains(keyUpEvent.target) || elMaterialDataEditor.contains(keyUpEvent.target) || elParticleUpdateDataEditor.contains(keyUpEvent.target) + || elParticleRenderDataEditor.contains(keyUpEvent.target)) { return; } diff --git a/scripts/system/create/entityProperties/html/tabs/particles_procedural.png b/scripts/system/create/entityProperties/html/tabs/particles_procedural.png new file mode 100644 index 0000000000000000000000000000000000000000..6a0d47cacb7665e2553a63ceed9291712163d1fa GIT binary patch literal 770 zcmWlVe@u*F9KgTtbN9Yocir{0imt>X)gr?ozsvKUt1dZ|V~23gkJN0(S`$vaFNf!_ zX0gOj$3K3J`o|iUUoUHBbN+~!4cC@oMvUH)&hE2){`>s(39MXRY}BXf0l-*dx0M4x zN>rTy03Y_`s{jzY%f7Y-z>pY~gLCJjg|9Ua9 zq7DC}KA2K3*$uQSp7WGd=m9Y- zq*IVtCG{mS^UKUW*`*7-KuR?o7pvsT>ZbY4LKq({QW*Si=#s7E`i*w&aSfo|K4d#0?0Qwsp}z1q++J zmDZlMefp&mSo;~*F&BHSzlJS4+PlRbv&ORcv{B!f-uY;PpF_H{kA>R&>3A+RZqh54 zLfzXBUCRZ}evE9^jPa)Xhu%GiT3qd@O6AS zo=7aOcl^0^Cm(JjrX$%~iYkUVq?;;dop-dQIE&tBI Date: Fri, 22 Mar 2024 21:38:38 -0700 Subject: [PATCH 027/109] fix particle intersection --- libraries/entities/src/EntityTreeElement.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 01d998d796..64caf9e1ab 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -258,7 +258,7 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori } else { // if the entity type doesn't support a detailed intersection, then just return the non-AABox results // Never intersect with particle entities - if (localDistance < distance && (entity->getType() != EntityTypes::ParticleEffect || entity->getType() != EntityTypes::ProceduralParticleEffect)) { + if (localDistance < distance && (entity->getType() != EntityTypes::ParticleEffect && entity->getType() != EntityTypes::ProceduralParticleEffect)) { distance = localDistance; face = localFace; surfaceNormal = glm::vec3(rotation * glm::vec4(localSurfaceNormal, 0.0f)); @@ -410,7 +410,7 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3 } else { // if the entity type doesn't support a detailed intersection, then just return the non-AABox results // Never intersect with particle entities - if (localDistance < parabolicDistance && (entity->getType() != EntityTypes::ParticleEffect || entity->getType() != EntityTypes::ProceduralParticleEffect)) { + if (localDistance < parabolicDistance && (entity->getType() != EntityTypes::ParticleEffect && entity->getType() != EntityTypes::ProceduralParticleEffect)) { parabolicDistance = localDistance; face = localFace; surfaceNormal = glm::vec3(rotation * glm::vec4(localSurfaceNormal, 0.0f)); From b97378d4cd3464ec420f3f71c06cf9e9af4ae805 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 23 Mar 2024 13:01:48 -0700 Subject: [PATCH 028/109] shorten create labels --- .../create/entityProperties/html/js/entityProperties.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 86a2ecfb9c..0d8214795f 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -1386,21 +1386,21 @@ const GROUPS = [ label: "PROCEDURAL PARTICLES", properties: [ { - label: "Num Particles", + label: "Particles", type: "number-draggable", propertyID: "numParticles", min: 1, max: 1000000 }, { - label: "Num Triangles Per Particle", + label: "Triangles Per Particle", type: "number-draggable", propertyID: "numTrianglesPerParticle", min: 1, max: 15 }, { - label: "Num Particle Update Props", + label: "Update Props", type: "number-draggable", propertyID: "numUpdateProps", min: 1, From 053828449239ad634b631851708c0aacc1348821 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 23 Mar 2024 20:39:40 -0700 Subject: [PATCH 029/109] fix 0 update props case --- .../src/RenderableProceduralParticleEffectEntityItem.cpp | 2 +- .../src/RenderableProceduralParticleEffectEntityItem.h | 2 +- libraries/entities/src/ProceduralParticleEffectEntityItem.h | 4 ++-- .../create/entityProperties/html/js/entityProperties.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.cpp index c9f242b1cb..b458a6da3e 100644 --- a/libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.cpp @@ -200,7 +200,7 @@ void ProceduralParticleEffectEntityRenderer::doRender(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - if (!_visible || (_numUpdateProps > 0 && !_updateProcedural.isReady()) || !_renderProcedural.isReady()) { + if (!_visible || _numParticles == 0 || (_numUpdateProps > 0 && !_updateProcedural.isReady()) || !_renderProcedural.isReady()) { return; } diff --git a/libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.h index 1445e798b1..deb3f70f33 100644 --- a/libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableProceduralParticleEffectEntityItem.h @@ -55,7 +55,7 @@ private: bool _transparent { false }; std::array _particleBuffers; - bool _evenPass{ true }; + bool _evenPass { true }; }; } } // namespace diff --git a/libraries/entities/src/ProceduralParticleEffectEntityItem.h b/libraries/entities/src/ProceduralParticleEffectEntityItem.h index e200f00fbc..98ae8b53df 100644 --- a/libraries/entities/src/ProceduralParticleEffectEntityItem.h +++ b/libraries/entities/src/ProceduralParticleEffectEntityItem.h @@ -20,8 +20,8 @@ namespace particle { static const uint8_t DEFAULT_NUM_TRIS_PER = 1; static const uint8_t MINIMUM_TRIS_PER = 1; static const uint8_t MAXIMUM_TRIS_PER = 15; - static const uint8_t DEFAULT_NUM_UPDATE_PROPS = 1; - static const uint8_t MINIMUM_NUM_UPDATE_PROPS = 1; + static const uint8_t DEFAULT_NUM_UPDATE_PROPS = 0; + static const uint8_t MINIMUM_NUM_UPDATE_PROPS = 0; static const uint8_t MAXIMUM_NUM_UPDATE_PROPS = 5; } diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 0d8214795f..84c1dd468e 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -1403,7 +1403,7 @@ const GROUPS = [ label: "Update Props", type: "number-draggable", propertyID: "numUpdateProps", - min: 1, + min: 0, max: 5 }, { From b6199079f8dba56402d820c3b84f4c8488ae4007 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Mon, 19 Feb 2024 18:40:16 -0800 Subject: [PATCH 030/109] Ability to smooth model animations --- .../src/RenderableModelEntityItem.cpp | 89 +++++++++++-------- .../src/RenderableModelEntityItem.h | 3 +- .../entities/src/AnimationPropertyGroup.cpp | 61 +++++++------ .../entities/src/AnimationPropertyGroup.h | 5 +- .../entities/src/EntityItemProperties.cpp | 1 + libraries/entities/src/EntityPropertyFlags.h | 1 + libraries/entities/src/EntityTree.cpp | 5 ++ libraries/entities/src/ModelEntityItem.cpp | 22 ++++- libraries/entities/src/ModelEntityItem.h | 9 +- libraries/networking/src/udt/PacketHeaders.h | 1 + .../create/assets/data/createAppTooltips.json | 3 + .../html/js/entityProperties.js | 5 ++ 12 files changed, 130 insertions(+), 75 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 6b83d87732..9ea1d2f942 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1123,37 +1123,7 @@ void ModelEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entit entity->setModel({}); } -void ModelEntityRenderer::animate(const TypedEntityPointer& entity, const ModelPointer& model) { - if (!_animation || !_animation->isLoaded()) { - return; - } - - QVector jointsData; - - const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy - int frameCount = frames.size(); - if (frameCount <= 0) { - return; - } - - { - float currentFrame = fmod(entity->getAnimationCurrentFrame(), (float)(frameCount)); - if (currentFrame < 0.0f) { - currentFrame += (float)frameCount; - } - int currentIntegerFrame = (int)(glm::floor(currentFrame)); - if (currentIntegerFrame == _lastKnownCurrentFrame) { - return; - } - _lastKnownCurrentFrame = currentIntegerFrame; - } - - if (_jointMapping.size() != model->getJointStateCount()) { - qCWarning(entitiesrenderer) << "RenderableModelEntityItem::getAnimationFrame -- joint count mismatch" - << _jointMapping.size() << model->getJointStateCount(); - return; - } - +void ModelEntityRenderer::updateJointData(const QVector& translations, const QVector& rotations, const TypedEntityPointer& entity, const ModelPointer& model) { QStringList animationJointNames = _animation->getHFMModel().getJointNames(); auto& hfmJoints = _animation->getHFMModel().joints; @@ -1162,10 +1132,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity, const ModelP bool allowTranslation = entity->getAnimationAllowTranslation(); - const QVector& rotations = frames[_lastKnownCurrentFrame].rotations; - const QVector& translations = frames[_lastKnownCurrentFrame].translations; - - jointsData.resize(_jointMapping.size()); + QVector jointsData(_jointMapping.size()); for (int j = 0; j < _jointMapping.size(); j++) { int index = _jointMapping[j]; @@ -1206,6 +1173,58 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity, const ModelP entity->copyAnimationJointDataToModel(); } +void ModelEntityRenderer::animate(const TypedEntityPointer& entity, const ModelPointer& model) { + if (!_animation || !_animation->isLoaded()) { + return; + } + + const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy + int frameCount = frames.size(); + if (frameCount <= 0) { + return; + } + + float currentFrame = fmod(entity->getAnimationCurrentFrame(), (float)(frameCount)); + if (currentFrame < 0.0f) { + currentFrame += (float)frameCount; + } + + const bool smoothFrames = entity->getAnimationSmoothFrames(); + const int currentIntegerFrame = (int)(glm::floor(currentFrame)); + if (!smoothFrames && currentIntegerFrame == _lastKnownCurrentIntegerFrame) { + return; + } + _lastKnownCurrentIntegerFrame = currentIntegerFrame; + + if (_jointMapping.size() != model->getJointStateCount()) { + qCWarning(entitiesrenderer) << "RenderableModelEntityItem::getAnimationFrame -- joint count mismatch" + << _jointMapping.size() << model->getJointStateCount(); + return; + } + + if (smoothFrames) { + QVector rotations = frames[_lastKnownCurrentIntegerFrame].rotations; + QVector translations = frames[_lastKnownCurrentIntegerFrame].translations; + + const int nextIntegerFrame = entity->getAnimationNextFrame(_lastKnownCurrentIntegerFrame, frameCount); + + const QVector& nextRotations = frames[nextIntegerFrame].rotations; + const QVector& nextTranslations = frames[nextIntegerFrame].translations; + + const float frac = glm::fract(currentFrame); + for (int i = 0; i < translations.size(); i++) { + translations[i] = glm::mix(translations[i], nextTranslations[i], frac); + } + for (int i = 0; i < rotations.size(); i++) { + rotations[i] = glm::slerp(rotations[i], nextRotations[i], frac); + } + + updateJointData(translations, rotations, entity, model); + } else { + updateJointData(frames[_lastKnownCurrentIntegerFrame].translations, frames[_lastKnownCurrentIntegerFrame].rotations, entity, model); + } +} + bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { if (entity->blendshapesChanged()) { return true; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 425d082f01..4fd05f39b9 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -175,6 +175,7 @@ protected: private: void animate(const TypedEntityPointer& entity, const ModelPointer& model); + void updateJointData(const QVector& translations, const QVector& rotations, const TypedEntityPointer& entity, const ModelPointer& model); void mapJoints(const TypedEntityPointer& entity, const ModelPointer& model); // Transparency is handled in ModelMeshPartPayload @@ -184,7 +185,7 @@ private: ModelPointer _model; QString _textures; bool _texturesLoaded { false }; - int _lastKnownCurrentFrame { -1 }; + int _lastKnownCurrentIntegerFrame { -1 }; #ifdef MODEL_ENTITY_USE_FADE_EFFECT bool _hasTransitioned{ false }; #endif diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index d15ee3d4cf..e44bc135e1 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -32,20 +32,8 @@ bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b (a._lastFrame == b._lastFrame) && (a._fps == b._fps) && (a._allowTranslation == b._allowTranslation) && - (a._url == b._url); -} - -bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { - return - (a._currentFrame != b._currentFrame) || - (a._running != b._running) || - (a._loop != b._loop) || - (a._hold != b._hold) || - (a._firstFrame != b._firstFrame) || - (a._lastFrame != b._lastFrame) || - (a._fps != b._fps) || - (a._allowTranslation != b._allowTranslation) || - (a._url != b._url); + (a._url == b._url) && + (a._smoothFrames == b._smoothFrames); } @@ -66,6 +54,8 @@ bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b * it isn't. * @property {boolean} hold=false - true if the rotations and translations of the last frame played are * maintained when the animation stops playing, false if they aren't. + * @property {boolean} smoothFrames=true - true if the frames of the animation should be linearly interpolated to + * create smoother movement, false if the frames should not be interpolated. */ void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url); @@ -77,6 +67,7 @@ void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desire COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_SMOOTH_FRAMES, Animation, animation, SmoothFrames, smoothFrames); } @@ -96,6 +87,7 @@ void AnimationPropertyGroup::copyFromScriptValue(const ScriptValue& object, cons COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, firstFrame, float, setFirstFrame); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, lastFrame, float, setLastFrame); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, hold, bool, setHold); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, smoothFrames, bool, setSmoothFrames); // legacy property support COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFPS, float, setFPS, getFPS); @@ -113,6 +105,7 @@ void AnimationPropertyGroup::merge(const AnimationPropertyGroup& other) { COPY_PROPERTY_IF_CHANGED(firstFrame); COPY_PROPERTY_IF_CHANGED(lastFrame); COPY_PROPERTY_IF_CHANGED(hold); + COPY_PROPERTY_IF_CHANGED(smoothFrames); } void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { @@ -120,19 +113,23 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { // if it includes fps, currentFrame, or running, those values will be parsed out and // will over ride the regular animation settings + bool allowTranslation = getAllowTranslation(); float fps = getFPS(); float currentFrame = getCurrentFrame(); bool running = getRunning(); + bool loop = getLoop(); float firstFrame = getFirstFrame(); float lastFrame = getLastFrame(); - bool loop = getLoop(); bool hold = getHold(); - bool allowTranslation = getAllowTranslation(); QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8()); QJsonObject settingsAsJsonObject = settingsAsJson.object(); QVariantMap settingsMap = settingsAsJsonObject.toVariantMap(); + if (settingsMap.contains("allowTranslation")) { + allowTranslation = settingsMap["allowTranslation"].toBool(); + } + if (settingsMap.contains("fps")) { fps = settingsMap["fps"].toFloat(); } @@ -150,30 +147,25 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { firstFrame = settingsMap["firstFrame"].toFloat(); } + if (settingsMap.contains("loop")) { + loop = settingsMap["loop"].toBool(); + } + if (settingsMap.contains("lastFrame")) { lastFrame = settingsMap["lastFrame"].toFloat(); } - if (settingsMap.contains("loop")) { - running = settingsMap["loop"].toBool(); - } - if (settingsMap.contains("hold")) { - running = settingsMap["hold"].toBool(); + hold = settingsMap["hold"].toBool(); } - if (settingsMap.contains("allowTranslation")) { - allowTranslation = settingsMap["allowTranslation"].toBool(); - } - - setAllowTranslation(allowTranslation); setFPS(fps); setCurrentFrame(currentFrame); setRunning(running); + setLoop(loop); setFirstFrame(firstFrame); setLastFrame(lastFrame); - setLoop(loop); setHold(hold); } @@ -213,6 +205,9 @@ void AnimationPropertyGroup::listChangedProperties(QList& out) { if (holdChanged()) { out << "animation-hold"; } + if (smoothFramesChanged()) { + out << "animation-smoothFrames"; + } } @@ -234,6 +229,7 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, getSmoothFrames()); return true; } @@ -253,6 +249,7 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); + READ_ENTITY_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, bool, setSmoothFrames); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_URL, URL); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation); @@ -263,7 +260,8 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FIRST_FRAME, FirstFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LAST_FRAME, LastFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_HOLD, Hold); - + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_SMOOTH_FRAMES, SmoothFrames); + processedBytes += bytesRead; Q_UNUSED(somethingChanged); @@ -281,6 +279,7 @@ void AnimationPropertyGroup::markAllChanged() { _firstFrameChanged = true; _lastFrameChanged = true; _holdChanged = true; + _smoothFramesChanged = true; } EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const { @@ -295,6 +294,7 @@ EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FIRST_FRAME, firstFrame); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LAST_FRAME, lastFrame); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_HOLD, hold); + CHECK_PROPERTY_CHANGE(PROP_ANIMATION_SMOOTH_FRAMES, smoothFrames); return changedProperties; } @@ -309,6 +309,7 @@ void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) con COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FirstFrame, getFirstFrame); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, LastFrame, getLastFrame); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Hold, getHold); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, SmoothFrames, getSmoothFrames); } bool AnimationPropertyGroup::setProperties(const EntityItemProperties& properties) { @@ -323,6 +324,7 @@ bool AnimationPropertyGroup::setProperties(const EntityItemProperties& propertie SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FirstFrame, firstFrame, setFirstFrame); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, LastFrame, lastFrame, setLastFrame); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Hold, hold, setHold); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, SmoothFrames, smoothFrames, setSmoothFrames); return somethingChanged; } @@ -338,6 +340,7 @@ EntityPropertyFlags AnimationPropertyGroup::getEntityProperties(EncodeBitstreamP requestedProperties += PROP_ANIMATION_FIRST_FRAME; requestedProperties += PROP_ANIMATION_LAST_FRAME; requestedProperties += PROP_ANIMATION_HOLD; + requestedProperties += PROP_ANIMATION_SMOOTH_FRAMES; return requestedProperties; } @@ -361,6 +364,7 @@ void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, En APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, getSmoothFrames()); } int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, @@ -380,6 +384,7 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); + READ_ENTITY_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, bool, setSmoothFrames); return bytesRead; } diff --git a/libraries/entities/src/AnimationPropertyGroup.h b/libraries/entities/src/AnimationPropertyGroup.h index c980119f8e..b90417d78e 100644 --- a/libraries/entities/src/AnimationPropertyGroup.h +++ b/libraries/entities/src/AnimationPropertyGroup.h @@ -91,11 +91,12 @@ public: DEFINE_PROPERTY(PROP_ANIMATION_FIRST_FRAME, FirstFrame, firstFrame, float, 0.0f); // was animationSettings.firstFrame DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold - DEFINE_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation, allowTranslation, bool, true); + DEFINE_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation, allowTranslation, bool, true); + DEFINE_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, SmoothFrames, smoothFrames, bool, true); protected: friend bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b); - friend bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b); + friend bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { return !(a == b); } void setFromOldAnimationSettings(const QString& value); }; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5db64d12e4..3133ab83bd 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2833,6 +2833,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); + ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_SMOOTH_FRAMES, Animation, animation, SmoothFrames, smoothFrames); } // Light diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index ca96c04fd8..d8cc15211a 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -237,6 +237,7 @@ enum EntityPropertyList { PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_16, PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_17, PROP_ANIMATION_HOLD = PROP_DERIVED_18, + PROP_ANIMATION_SMOOTH_FRAMES = PROP_DERIVED_19, // Light PROP_IS_SPOTLIGHT = PROP_DERIVED_0, diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 5f88d7191c..f8ae726b90 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2892,6 +2892,11 @@ bool EntityTree::readFromMap(QVariantMap& map, const bool isImport) { } } + // Before, animations weren't smoothed + if (contentVersion < (int)EntityVersion::AnimationSmoothFrames && properties.getType() == EntityTypes::EntityType::Model) { + properties.getAnimation().setSmoothFrames(false); + } + EntityItemPointer entity = addEntity(entityItemID, properties, isImport); if (!entity) { qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType(); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 45d41c3a7e..8910961c50 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -34,12 +34,10 @@ EntityItemPointer ModelEntityItem::factory(const EntityItemID& entityID, const E } ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID), - _blendshapeCoefficientsVector((int)Blendshapes::BlendshapeCount, 0.0f) + _blendshapeCoefficientsVector((int)Blendshapes::BlendshapeCount, 0.0f), + _lastAnimated(usecTimestampNow()) { - _lastAnimated = usecTimestampNow(); - // set the last animated when interface (re)starts _type = EntityTypes::Model; - _lastKnownCurrentFrame = -1; _visuallyReady = false; } @@ -643,6 +641,22 @@ bool ModelEntityItem::isAnimatingSomething() const { }); } +bool ModelEntityItem::getAnimationSmoothFrames() const { + return resultWithReadLock([&] { + return _animationProperties.getSmoothFrames(); + }); +} + +int ModelEntityItem::getAnimationNextFrame(int currentFrame, int frameCount) const { + return resultWithReadLock([&] { + int result = currentFrame + 1; + if (result > _animationProperties.getLastFrame() || result > (frameCount - 1)) { + result = _animationProperties.getFirstFrame(); + } + return std::max(result, 0); + }); +} + bool ModelEntityItem::applyNewAnimationProperties(AnimationPropertyGroup newProperties) { // call applyNewAnimationProperties() whenever trying to update _animationProperties // because there is some reset logic we need to do whenever the animation "config" properties change diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index a00327251c..2e1995be88 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -56,8 +56,6 @@ public: void setShapeType(ShapeType type) override; virtual ShapeType getShapeType() const override; - // TODO: Move these to subclasses, or other appropriate abstraction - // getters/setters applicable to models and particles glm::u8vec3 getColor() const; void setColor(const glm::u8vec3& value); @@ -89,6 +87,8 @@ public: float getAnimationCurrentFrame() const; bool getAnimationAllowTranslation() const; bool isAnimatingSomething() const; + bool getAnimationSmoothFrames() const; + int getAnimationNextFrame(int currentFrame, int frameCount) const; void setRelayParentJoints(bool relayJoints); bool getRelayParentJoints() const; @@ -148,7 +148,6 @@ protected: }; QVector _localJointData; - int _lastKnownCurrentFrame{-1}; glm::u8vec3 _color; glm::vec3 _modelScale { 1.0f }; @@ -167,8 +166,8 @@ protected: ShapeType _shapeType { SHAPE_TYPE_NONE }; private: - uint64_t _lastAnimated{ 0 }; - float _currentFrame{ -1.0f }; + uint64_t _lastAnimated { 0 }; + float _currentFrame { -1.0f }; QVector _blendshapeCoefficientsVector; bool _blendshapesChanged { false }; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 6f84f25e86..9816c69cf3 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -294,6 +294,7 @@ enum class EntityVersion : PacketVersion { EntityTags, WantsKeyboardFocus, AudioZones, + AnimationSmoothFrames, // Add new versions above here NUM_PACKET_TYPE, diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index b4597e9141..fcb8caa511 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -215,6 +215,9 @@ "animation.fps": { "tooltip": "The speed of the animation." }, + "animation.smoothFrames": { + "tooltip": "If enabled, the frames of the animation will be linearly interpolated to create smoother movement." + }, "textures": { "tooltip": "A JSON string containing a texture. Use a name from the Original Texture property to override it." }, diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 8a1540d411..3ec47ad696 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -729,6 +729,11 @@ const GROUPS = [ type: "number-draggable", propertyID: "animation.fps", }, + { + label: "Smooth Animation", + type: "bool", + propertyID: "animation.smoothFrames", + }, { label: "Texture", type: "textarea", From 878774b5d3b5d9f4ea70ef184a4d6119d6bd3c32 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 24 Mar 2024 13:31:16 -0700 Subject: [PATCH 031/109] sound entities --- assignment-client/src/audio/AudioMixer.cpp | 1 + assignment-client/src/avatars/AvatarMixer.cpp | 1 + .../src/entities/EntityServer.cpp | 8 + .../src/scripts/EntityScriptServer.cpp | 1 + libraries/audio/src/AudioInjector.cpp | 3 + libraries/entities/CMakeLists.txt | 2 +- .../entities/src/EntityItemProperties.cpp | 139 +++++++ libraries/entities/src/EntityItemProperties.h | 13 + libraries/entities/src/EntityPropertyFlags.h | 10 + .../entities/src/EntityScriptingInterface.cpp | 26 ++ .../entities/src/EntityScriptingInterface.h | 21 + libraries/entities/src/EntityTree.h | 4 + libraries/entities/src/EntityTreeElement.cpp | 8 +- libraries/entities/src/EntityTypes.cpp | 2 + libraries/entities/src/EntityTypes.h | 3 + libraries/entities/src/SoundEntityItem.cpp | 371 ++++++++++++++++++ libraries/entities/src/SoundEntityItem.h | 100 +++++ libraries/networking/src/udt/PacketHeaders.h | 1 + libraries/physics/CMakeLists.txt | 1 + scripts/system/assets/images/tools/sound.svg | 258 ++++++++++++ .../create/assets/data/createAppTooltips.json | 24 ++ .../create/assets/images/icon-sound.svg | 84 ++++ scripts/system/create/edit.js | 19 +- .../system/create/entityList/entityList.js | 4 +- .../create/entityList/html/js/entityList.js | 1 + .../html/js/entityProperties.js | 57 +++ .../entityProperties/html/tabs/sound.png | Bin 0 -> 684 bytes .../entitySelectionTool.js | 2 +- .../system/create/modules/brokenURLReport.js | 11 + scripts/system/create/qml/EditTabView.qml | 12 + .../system/create/qml/EditToolsTabView.qml | 12 + scripts/system/create/qml/icons/sound.svg | 59 +++ scripts/system/html/js/includes.js | 1 + 33 files changed, 1249 insertions(+), 10 deletions(-) create mode 100644 libraries/entities/src/SoundEntityItem.cpp create mode 100644 libraries/entities/src/SoundEntityItem.h create mode 100644 scripts/system/assets/images/tools/sound.svg create mode 100644 scripts/system/create/assets/images/icon-sound.svg create mode 100644 scripts/system/create/entityProperties/html/tabs/sound.png create mode 100644 scripts/system/create/qml/icons/sound.svg diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 92719abef6..83fe2cc08f 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -816,6 +816,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) { void AudioMixer::setupEntityQuery() { _entityViewer.init(); EntityTreePointer entityTree = _entityViewer.getTree(); + entityTree->setIsServer(true); DependencyManager::registerInheritance(); DependencyManager::set(entityTree); diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 8d322c36f2..81a9b89b0a 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -1081,6 +1081,7 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { void AvatarMixer::setupEntityQuery() { _entityViewer.init(); EntityTreePointer entityTree = _entityViewer.getTree(); + entityTree->setIsServer(true); DependencyManager::registerInheritance(); DependencyManager::set(entityTree); diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index d27a69ff7c..9cbaaa0ea1 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -16,9 +16,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -49,6 +51,9 @@ EntityServer::EntityServer(ReceivedMessage& message) : DependencyManager::set(); // ModelFormatRegistry must be defined before ModelCache. See the ModelCache ctor DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityClone, @@ -71,6 +76,8 @@ EntityServer::~EntityServer() { void EntityServer::aboutToFinish() { DependencyManager::get()->cleanup(); + DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::destroy(); OctreeServer::aboutToFinish(); @@ -90,6 +97,7 @@ OctreePointer EntityServer::createTree() { EntityTreePointer tree = std::make_shared(true); tree->createRootElement(); tree->addNewlyCreatedHook(this); + tree->setIsEntityServer(true); if (!_entitySimulation) { SimpleEntitySimulationPointer simpleSimulation { new SimpleEntitySimulation() }; simpleSimulation->setEntityTree(tree); diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index b16e4561d6..3f49ecd889 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -313,6 +313,7 @@ void EntityScriptServer::run() { entityScriptingInterface->setEntityTree(_entityViewer.getTree()); auto treePtr = _entityViewer.getTree(); + treePtr->setIsServer(true); DependencyManager::set(treePtr); if (!_entitySimulation) { diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 2df766377f..8c3a6b118e 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -132,9 +132,11 @@ void AudioInjector::restart() { bool AudioInjector::inject(bool(AudioInjectorManager::*injection)(const AudioInjectorPointer&)) { AudioInjectorOptions options; + uint32_t numBytes; withWriteLock([&] { _state = AudioInjectorState::NotFinished; options = _options; + numBytes = _audioData->getNumBytes(); }); int byteOffset = 0; @@ -142,6 +144,7 @@ bool AudioInjector::inject(bool(AudioInjectorManager::*injection)(const AudioInj int numChannels = options.ambisonic ? 4 : (options.stereo ? 2 : 1); byteOffset = (int)(AudioConstants::SAMPLE_RATE * options.secondOffset * numChannels); byteOffset *= AudioConstants::SAMPLE_SIZE; + byteOffset = byteOffset % numBytes; } _currentSendOffset = byteOffset; diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index e04d9f9fa8..830cecd1ed 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -12,7 +12,7 @@ include_hifi_library_headers(image) include_hifi_library_headers(ktx) include_hifi_library_headers(material-networking) include_hifi_library_headers(procedural) -link_hifi_libraries(shared shaders networking octree avatars graphics model-networking script-engine) +link_hifi_libraries(audio shared shaders networking octree avatars graphics model-networking script-engine) if (WIN32) add_compile_definitions(_USE_MATH_DEFINES) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5db64d12e4..a32dcf78c0 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -690,6 +690,16 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_GIZMO_TYPE, gizmoType); changedProperties += _ring.getChangedProperties(); + // Sound + CHECK_PROPERTY_CHANGE(PROP_SOUND_URL, soundURL); + CHECK_PROPERTY_CHANGE(PROP_SOUND_VOLUME, volume); + CHECK_PROPERTY_CHANGE(PROP_SOUND_TIME_OFFSET, timeOffset); + CHECK_PROPERTY_CHANGE(PROP_SOUND_PITCH, pitch); + CHECK_PROPERTY_CHANGE(PROP_SOUND_PLAYING, playing); + CHECK_PROPERTY_CHANGE(PROP_SOUND_LOOP, loop); + CHECK_PROPERTY_CHANGE(PROP_SOUND_POSITIONAL, positional); + CHECK_PROPERTY_CHANGE(PROP_SOUND_LOCAL_ONLY, localOnly); + return changedProperties; } @@ -883,6 +893,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @see {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine} * @see {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox} * @see {@link Entities.EntityProperties-Shape|EntityProperties-Shape} + * @see {@link Entities.EntityProperties-Sound|EntityProperties-Sound} * @see {@link Entities.EntityProperties-Sphere|EntityProperties-Sphere} * @see {@link Entities.EntityProperties-Text|EntityProperties-Text} * @see {@link Entities.EntityProperties-Web|EntityProperties-Web} @@ -1340,6 +1351,34 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * }); */ +/*@jsdoc + * The "Sound" {@link Entities.EntityType|EntityType} plays a sound from a URL. It has properties in addition to + * the common {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-Sound + * @property {string} soundURL="" - The URL of the sound to play, as a wav, mp3, or raw file. Supports stereo and ambisonic. + * @property {boolean} playing=true - Whether or not the sound should play. + * @property {number} volume=1.0 - The volume of the sound, from 0 to 1. + * @property {number} pitch=1.0 - The relative sample rate at which to resample the sound, within +/- 2 octaves. + * @property {number} timeOffset=0.0 - The time (in seconds) at which to start playback within the sound file. If looping, + * this only affects the first loop. + * @property {boolean} loop=true - Whether or not to loop the sound. + * @property {boolean} positional=true - Whether or not the volume of the sound should decay with distance. + * @property {boolean} localOnly=false - Whether or not the sound should play locally for everyone (unsynced), or synchronously + * for everyone via the Entity Mixer. + * @example Create a Sound entity. + * var entity = Entities.addEntity({ + * type: "Sound", + * soundURL: "https://themushroomkingdom.net/sounds/wav/lm/lm_gold_mouse.wav", + * positional: true, + * volume: 0.75, + * localOnly: true, + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -4 })), + * rotation: MyAvatar.orientation, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + /*@jsdoc * The "Sphere" {@link Entities.EntityType|EntityType} is the same as the "Shape" * {@link Entities.EntityType|EntityType} except that its shape value is always set to "Sphere" @@ -1961,6 +2000,18 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s _ring.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); } + // Sound only + if (_type == EntityTypes::Sound) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_URL, soundURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_VOLUME, volume); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_TIME_OFFSET, timeOffset); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_PITCH, pitch); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_PLAYING, playing); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_LOOP, loop); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_POSITIONAL, positional); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_LOCAL_ONLY, localOnly); + } + /*@jsdoc * The axis-aligned bounding box of an entity. * @typedef {object} Entities.BoundingBox @@ -2292,6 +2343,16 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(gizmoType, GizmoType); _ring.copyFromScriptValue(object, namesSet, _defaultSettings); + // Sound + COPY_PROPERTY_FROM_QSCRIPTVALUE(soundURL, QString, setSoundURL); + COPY_PROPERTY_FROM_QSCRIPTVALUE(volume, float, setVolume); + COPY_PROPERTY_FROM_QSCRIPTVALUE(timeOffset, float, setTimeOffset); + COPY_PROPERTY_FROM_QSCRIPTVALUE(pitch, float, setPitch); + COPY_PROPERTY_FROM_QSCRIPTVALUE(playing, bool, setPlaying); + COPY_PROPERTY_FROM_QSCRIPTVALUE(loop, bool, setLoop); + COPY_PROPERTY_FROM_QSCRIPTVALUE(positional, bool, setPositional); + COPY_PROPERTY_FROM_QSCRIPTVALUE(localOnly, bool, setLocalOnly); + // Handle conversions from old 'textures' property to "imageURL" if (namesSet.contains("textures")) { ScriptValue V = object.property("textures"); @@ -2578,6 +2639,16 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(gizmoType); _ring.merge(other._ring); + // Sound + COPY_PROPERTY_IF_CHANGED(soundURL); + COPY_PROPERTY_IF_CHANGED(volume); + COPY_PROPERTY_IF_CHANGED(timeOffset); + COPY_PROPERTY_IF_CHANGED(pitch); + COPY_PROPERTY_IF_CHANGED(playing); + COPY_PROPERTY_IF_CHANGED(loop); + COPY_PROPERTY_IF_CHANGED(positional); + COPY_PROPERTY_IF_CHANGED(localOnly); + _lastEdited = usecTimestampNow(); } @@ -3002,6 +3073,16 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_GROUP_PROPERTY_TO_MAP(PROP_MAJOR_TICK_MARKS_COLOR, Ring, ring, MajorTickMarksColor, majorTickMarksColor); ADD_GROUP_PROPERTY_TO_MAP(PROP_MINOR_TICK_MARKS_COLOR, Ring, ring, MinorTickMarksColor, minorTickMarksColor); } + + // Sound + ADD_PROPERTY_TO_MAP(PROP_SOUND_URL, SoundURL, soundURL, QString); + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SOUND_VOLUME, Volume, volume, float, 0.0f, 1.0f); + ADD_PROPERTY_TO_MAP(PROP_SOUND_TIME_OFFSET, TimeOffset, timeOffset, float); + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SOUND_PITCH, Pitch, pitch, float, 1.0f / 16.0f, 16.0f); + ADD_PROPERTY_TO_MAP(PROP_SOUND_PLAYING, Playing, playing, bool); + ADD_PROPERTY_TO_MAP(PROP_SOUND_LOOP, Loop, loop, bool); + ADD_PROPERTY_TO_MAP(PROP_SOUND_POSITIONAL, Positional, positional, bool); + ADD_PROPERTY_TO_MAP(PROP_SOUND_LOCAL_ONLY, LocalOnly, localOnly, bool); }); auto iter = _propertyInfos.find(propertyName); @@ -3449,6 +3530,17 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticRing.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); } + + if (properties.getType() == EntityTypes::Sound) { + APPEND_ENTITY_PROPERTY(PROP_SOUND_URL, properties.getSoundURL()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_VOLUME, properties.getVolume()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_TIME_OFFSET, properties.getTimeOffset()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_PITCH, properties.getPitch()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_PLAYING, properties.getPlaying()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_LOOP, properties.getLoop()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_POSITIONAL, properties.getPositional()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_LOCAL_ONLY, properties.getLocalOnly()); + } } if (propertyCount > 0) { @@ -3911,6 +4003,17 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int properties.getRing().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); } + if (properties.getType() == EntityTypes::Sound) { + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_URL, QString, setSoundURL); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_VOLUME, float, setVolume); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_TIME_OFFSET, float, setTimeOffset); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_PITCH, float, setPitch); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_PLAYING, bool, setPlaying); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_LOOP, bool, setLoop); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_POSITIONAL, bool, setPositional); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_LOCAL_ONLY, bool, setLocalOnly); + } + return valid; } @@ -4240,6 +4343,16 @@ void EntityItemProperties::markAllChanged() { // Gizmo _gizmoTypeChanged = true; _ring.markAllChanged(); + + // Sound + _soundURLChanged = true; + _volumeChanged = true; + _timeOffsetChanged = true; + _pitchChanged = true; + _playingChanged = true; + _loopChanged = true; + _positionalChanged = true; + _localOnlyChanged = true; } // The minimum bounding box for the entity. @@ -4945,6 +5058,32 @@ QList EntityItemProperties::listChangedProperties() { } getRing().listChangedProperties(out); + // Sound + if (soundURLChanged()) { + out += "soundURL"; + } + if (volumeChanged()) { + out += "volume"; + } + if (timeOffsetChanged()) { + out += "timeOffset"; + } + if (pitchChanged()) { + out += "pitch"; + } + if (playingChanged()) { + out += "playing"; + } + if (loopChanged()) { + out += "loop"; + } + if (positionalChanged()) { + out += "positional"; + } + if (localOnlyChanged()) { + out += "localOnly"; + } + return out; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 6340313f2d..961049b1c7 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -52,6 +52,7 @@ #include "PolyVoxEntityItem.h" #include "GridEntityItem.h" #include "GizmoEntityItem.h" +#include "SoundEntityItem.h" #include "LightEntityItem.h" #include "ZoneEntityItem.h" @@ -124,6 +125,8 @@ class EntityItemProperties { friend class LightEntityItem; friend class ZoneEntityItem; friend class MaterialEntityItem; + friend class SoundEntityItem; + public: static bool blobToProperties(ScriptEngine& scriptEngine, const QByteArray& blob, EntityItemProperties& properties); static void propertiesToBlob(ScriptEngine& scriptEngine, const QUuid& myAvatarID, const EntityItemProperties& properties, @@ -407,6 +410,16 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_GIZMO_TYPE, GizmoType, gizmoType, GizmoType, GizmoType::RING); DEFINE_PROPERTY_GROUP(Ring, ring, RingGizmoPropertyGroup); + // Sound + DEFINE_PROPERTY_REF(PROP_SOUND_URL, SoundURL, soundURL, QString, ""); + DEFINE_PROPERTY(PROP_SOUND_VOLUME, Volume, volume, float, 1.0f); + DEFINE_PROPERTY(PROP_SOUND_TIME_OFFSET, TimeOffset, timeOffset, float, 0.0f); + DEFINE_PROPERTY(PROP_SOUND_PITCH, Pitch, pitch, float, 1.0f); + DEFINE_PROPERTY(PROP_SOUND_PLAYING, Playing, playing, bool, true); + DEFINE_PROPERTY(PROP_SOUND_LOOP, Loop, loop, bool, true); + DEFINE_PROPERTY(PROP_SOUND_POSITIONAL, Positional, positional, bool, true); + DEFINE_PROPERTY(PROP_SOUND_LOCAL_ONLY, LocalOnly, localOnly, bool, false); + static QString getComponentModeAsString(uint32_t mode); public: diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index ca96c04fd8..591725c2ea 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -394,6 +394,16 @@ enum EntityPropertyList { PROP_MAJOR_TICK_MARKS_COLOR = PROP_DERIVED_17, PROP_MINOR_TICK_MARKS_COLOR = PROP_DERIVED_18, + // Sound + PROP_SOUND_URL = PROP_DERIVED_0, + PROP_SOUND_VOLUME = PROP_DERIVED_1, + PROP_SOUND_TIME_OFFSET = PROP_DERIVED_2, + PROP_SOUND_PITCH = PROP_DERIVED_3, + PROP_SOUND_PLAYING = PROP_DERIVED_4, + PROP_SOUND_POSITIONAL = PROP_DERIVED_5, + PROP_SOUND_LOOP = PROP_DERIVED_6, + PROP_SOUND_LOCAL_ONLY = PROP_DERIVED_7, + // WARNING!!! DO NOT ADD PROPS_xxx here unless you really really meant to.... Add them UP above }; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 4acf9b8d1b..f0da5f3cf3 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1871,6 +1871,7 @@ bool EntityScriptingInterface::setAllPoints(const QUuid& entityID, const QVector EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); if (!entity) { qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID; + return false; } EntityTypes::EntityType entityType = entity->getType(); @@ -1907,6 +1908,31 @@ bool EntityScriptingInterface::appendPoint(const QUuid& entityID, const glm::vec return false; } +bool EntityScriptingInterface::restartSound(const QUuid& entityID) { + PROFILE_RANGE(script_entities, __FUNCTION__); + + EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); + if (!entity) { + qCDebug(entities) << "EntityScriptingInterface::restartSound no entity with ID" << entityID; + // There is no entity + return false; + } + + EntityTypes::EntityType entityType = entity->getType(); + + if (entityType == EntityTypes::Sound) { + auto soundEntity = std::dynamic_pointer_cast(entity); + bool isPlaying = soundEntity->getPlaying(); + if (isPlaying) { + soundEntity->withWriteLock([&] { + soundEntity->restartSound(); + }); + } + return isPlaying; + } + + return false; +} bool EntityScriptingInterface::actionWorker(const QUuid& entityID, std::function actor) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index c677bdf0a1..59204bdd96 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -1237,6 +1237,27 @@ public slots: */ Q_INVOKABLE bool appendPoint(const QUuid& entityID, const glm::vec3& point); + /*@jsdoc + * Restart a {@link Entities.EntityProperties-Sound|Sound} entity, locally only. It must also be localOnly. + * @function Entities.restartSound + * @param {Uuid} entityID - The ID of the {@link Entities.EntityProperties-Sound|Sound} entity. + * @example Play a sound once and repeat it every 3 seconds. + * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })); + * var sound = Entities.addEntity({ + * type: "Sound", + * position: position, + * soundURL: "https://themushroomkingdom.net/sounds/wav/lm/lm_gold_mouse.wav", + * positional: false, + * localOnly: true, + * loop: false, + * lifetime: 300 // Delete after 5 minutes. + * }); + * Script.setInterval(() => { + * Entities.restartSound(sound); + * }, 3000); + */ + Q_INVOKABLE bool restartSound(const QUuid& entityID); + /*@jsdoc * Dumps debug information about all entities in Interface's local in-memory tree of entities it knows about to the program * log. diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 1161bec6e9..a1f97ff621 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -257,6 +257,9 @@ public: void setIsServerlessMode(bool value) { _serverlessDomain = value; } bool isServerlessMode() const { return _serverlessDomain; } + void setIsEntityServer(bool value) { _entityServer = value; } + bool isEntityServer() const { return _entityServer; } + static void setGetEntityObjectOperator(std::function getEntityObjectOperator) { _getEntityObjectOperator = getEntityObjectOperator; } static QObject* getEntityObject(const QUuid& id); @@ -380,6 +383,7 @@ private: std::vector _staleProxies; bool _serverlessDomain { false }; + bool _entityServer { false }; std::map _namedPaths; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 32c791da33..b6f65cff65 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -257,8 +257,8 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori } } else { // if the entity type doesn't support a detailed intersection, then just return the non-AABox results - // Never intersect with particle entities - if (localDistance < distance && entity->getType() != EntityTypes::ParticleEffect) { + // Never intersect with particle or sound entities + if (localDistance < distance && (entity->getType() != EntityTypes::ParticleEffect && entity->getType() != EntityTypes::Sound)) { distance = localDistance; face = localFace; surfaceNormal = glm::vec3(rotation * glm::vec4(localSurfaceNormal, 0.0f)); @@ -409,8 +409,8 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3 } } else { // if the entity type doesn't support a detailed intersection, then just return the non-AABox results - // Never intersect with particle entities - if (localDistance < parabolicDistance && entity->getType() != EntityTypes::ParticleEffect) { + // Never intersect with particle or sound entities + if (localDistance < parabolicDistance && (entity->getType() != EntityTypes::ParticleEffect && entity->getType() != EntityTypes::Sound)) { parabolicDistance = localDistance; face = localFace; surfaceNormal = glm::vec3(rotation * glm::vec4(localSurfaceNormal, 0.0f)); diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 95057bedbc..49109fd5dd 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -34,6 +34,7 @@ #include "LightEntityItem.h" #include "ZoneEntityItem.h" #include "MaterialEntityItem.h" +#include "SoundEntityItem.h" QMap EntityTypes::_typeToNameMap; QMap EntityTypes::_nameToTypeMap; @@ -59,6 +60,7 @@ REGISTER_ENTITY_TYPE(Gizmo) REGISTER_ENTITY_TYPE(Light) REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Material) +REGISTER_ENTITY_TYPE(Sound) bool EntityTypes::typeIsValid(EntityType type) { return type > EntityType::Unknown && type <= EntityType::NUM_TYPES; diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 441e77fccd..30d59727d4 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -86,6 +86,8 @@ public: * {@link Entities.EntityProperties-Zone|EntityProperties-Zone} * "Material"Modifies the existing materials on entities and avatars. * {@link Entities.EntityProperties-Material|EntityProperties-Material} + * "Sound"Plays a sound. + * {@link Entities.EntityProperties-Material|EntityProperties-Sound} * * * @typedef {string} Entities.EntityType @@ -108,6 +110,7 @@ public: Light, Zone, Material, + Sound, NUM_TYPES } EntityType; diff --git a/libraries/entities/src/SoundEntityItem.cpp b/libraries/entities/src/SoundEntityItem.cpp new file mode 100644 index 0000000000..575efabb2e --- /dev/null +++ b/libraries/entities/src/SoundEntityItem.cpp @@ -0,0 +1,371 @@ +// +// Created by HifiExperiments on 12/30/2023 +// Copyright 2023 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 "SoundEntityItem.h" + +#include + +#include "EntitiesLogging.h" +#include "EntityItemProperties.h" +#include "EntityTree.h" +#include "EntityTreeElement.h" + +EntityItemPointer SoundEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + std::shared_ptr entity(new SoundEntityItem(entityID), [](SoundEntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +// our non-pure virtual subclass for now... +SoundEntityItem::SoundEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { + _type = EntityTypes::Sound; +} + +SoundEntityItem::~SoundEntityItem() { + auto manager = DependencyManager::get(); + if (manager && _injector) { + manager->stop(_injector); + } +} + +EntityItemProperties SoundEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + + COPY_ENTITY_PROPERTY_TO_PROPERTIES(soundURL, getURL); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(volume, getVolume); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(timeOffset, getTimeOffset); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(pitch, getPitch); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(playing, getPlaying); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(loop, getLoop); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(positional, getPositional); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(localOnly, getLocalOnly); + + return properties; +} + +bool SoundEntityItem::setSubClassProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_PROPERTY_FROM_PROPERTIES(soundURL, setURL); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(volume, setVolume); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(timeOffset, setTimeOffset); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(pitch, setPitch); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(playing, setPlaying); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(loop, setLoop); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(positional, setPositional); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(localOnly, setLocalOnly); + + return somethingChanged; +} + +int SoundEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_SOUND_URL, QString, setURL); + READ_ENTITY_PROPERTY(PROP_SOUND_VOLUME, float, setVolume); + READ_ENTITY_PROPERTY(PROP_SOUND_TIME_OFFSET, float, setTimeOffset); + READ_ENTITY_PROPERTY(PROP_SOUND_PITCH, float, setPitch); + READ_ENTITY_PROPERTY(PROP_SOUND_PLAYING, bool, setPlaying); + READ_ENTITY_PROPERTY(PROP_SOUND_LOOP, bool, setLoop); + READ_ENTITY_PROPERTY(PROP_SOUND_POSITIONAL, bool, setPositional); + READ_ENTITY_PROPERTY(PROP_SOUND_LOCAL_ONLY, bool, setLocalOnly); + + return bytesRead; +} + +EntityPropertyFlags SoundEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + + requestedProperties += PROP_SOUND_URL; + requestedProperties += PROP_SOUND_VOLUME; + requestedProperties += PROP_SOUND_TIME_OFFSET; + requestedProperties += PROP_SOUND_PITCH; + requestedProperties += PROP_SOUND_PLAYING; + requestedProperties += PROP_SOUND_LOOP; + requestedProperties += PROP_SOUND_POSITIONAL; + requestedProperties += PROP_SOUND_LOCAL_ONLY; + + return requestedProperties; +} + +void SoundEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_SOUND_URL, getURL()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_VOLUME, getVolume()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_TIME_OFFSET, getTimeOffset()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_PITCH, getPitch()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_PLAYING, getPlaying()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_LOOP, getLoop()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_POSITIONAL, getPositional()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_LOCAL_ONLY, getLocalOnly()); +} + +void SoundEntityItem::debugDump() const { + quint64 now = usecTimestampNow(); + qCDebug(entities) << "SOUND EntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " url:" << _url; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << "SOUND EntityItem Ptr:" << this; +} + +void SoundEntityItem::update(const quint64& now) { + withWriteLock([&] { + const auto tree = getTree(); + if (tree) { + _updateNeeded = false; + + if ((_localOnly && tree->getIsClient()) || (!_localOnly && (tree->isEntityServer() || tree->isServerlessMode()))) { + _sound = DependencyManager::get()->getSound(_url); + } + + if (_sound) { + if (_sound->isLoaded()) { + updateSound(true); + } else { + connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); + } + } + } + }); +} + +void SoundEntityItem::setLocalPosition(const glm::vec3& value, bool tellPhysics) { + EntityItem::setLocalPosition(value, tellPhysics); + withWriteLock([&] { + updateSound(); + }); +} + +void SoundEntityItem::setLocalOrientation(const glm::quat& value) { + EntityItem::setLocalOrientation(value); + withWriteLock([&] { + updateSound(); + }); +} + +void SoundEntityItem::setURL(const QString& value) { + withWriteLock([&] { + if (value != _url) { + _url = value; + + const auto tree = getTree(); + if (!tree) { + _updateNeeded = true; + return; + } + + if ((_localOnly && tree->getIsClient()) || (!_localOnly && (tree->isEntityServer() || tree->isServerlessMode()))) { + _sound = DependencyManager::get()->getSound(_url); + } + + if (_sound) { + if (_sound->isLoaded()) { + updateSound(true); + } else { + connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); + } + } + } + }); +} + +QString SoundEntityItem::getURL() const { + return resultWithReadLock([&] { + return _url; + }); +} + +void SoundEntityItem::setVolume(float value) { + withWriteLock([&] { + if (value != _volume) { + _volume = value; + updateSound(); + } + }); +} + +float SoundEntityItem::getVolume() const { + return resultWithReadLock([&] { + return _volume; + }); +} + +void SoundEntityItem::setTimeOffset(float value) { + withWriteLock([&] { + if (value != _timeOffset) { + _timeOffset = value; + updateSound(true); + } + }); +} + +float SoundEntityItem::getTimeOffset() const { + return resultWithReadLock([&] { + return _timeOffset; + }); +} + +void SoundEntityItem::setPitch(float value) { + withWriteLock([&] { + if (value != _pitch) { + _pitch = value; + updateSound(true); + } + }); +} + +float SoundEntityItem::getPitch() const { + return resultWithReadLock([&] { + return _pitch; + }); +} + +void SoundEntityItem::setPlaying(bool value) { + withWriteLock([&] { + if (value != _playing) { + _playing = value; + updateSound(); + } + }); +} + +bool SoundEntityItem::getPlaying() const { + return resultWithReadLock([&] { + return _playing; + }); +} + +void SoundEntityItem::setLoop(bool value) { + withWriteLock([&] { + if (value != _loop) { + _loop = value; + updateSound(true); + } + }); +} + +bool SoundEntityItem::getLoop() const { + return resultWithReadLock([&] { + return _loop; + }); +} + +void SoundEntityItem::setPositional(bool value) { + withWriteLock([&] { + if (value != _positional) { + _positional = value; + updateSound(); + } + }); +} + +bool SoundEntityItem::getPositional() const { + return resultWithReadLock([&] { + return _positional; + }); +} + +void SoundEntityItem::setLocalOnly(bool value) { + withWriteLock([&] { + if (value != _localOnly) { + _localOnly = value; + + const auto tree = getTree(); + if (!tree) { + _updateNeeded = true; + return; + } + + if ((_localOnly && tree->getIsClient()) || (!_localOnly && (tree->isEntityServer() || tree->isServerlessMode()))) { + _sound = DependencyManager::get()->getSound(_url); + } else { + _sound = nullptr; + + if (_injector) { + DependencyManager::get()->stop(_injector); + } + _injector = nullptr; + } + + if (_sound) { + if (_sound->isLoaded()) { + updateSound(true); + } else { + connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); + } + } + } + }); +} + +bool SoundEntityItem::getLocalOnly() const { + return resultWithReadLock([&] { + return _localOnly; + }); +} + +bool SoundEntityItem::restartSound() { + if (!_sound) { + return false; + } + + AudioInjectorOptions options; + options.position = getWorldPosition(); + options.positionSet = _positional; + options.volume = _volume; + options.loop = _loop; + options.orientation = getWorldOrientation(); + options.localOnly = _localOnly; + options.secondOffset = _timeOffset; + options.pitch = _pitch; + + if (_injector) { + DependencyManager::get()->setOptionsAndRestart(_injector, options); + } else { + _injector = DependencyManager::get()->playSound(_sound, options); + } + + return true; +} + +void SoundEntityItem::updateSound(bool restart) { + if (!_sound) { + return; + } + + if (restart) { + if (_injector) { + DependencyManager::get()->stop(_injector); + } + _injector = nullptr; + } + + if (_playing) { + restartSound(); + } else { + if (_injector) { + DependencyManager::get()->stop(_injector); + } + } +} \ No newline at end of file diff --git a/libraries/entities/src/SoundEntityItem.h b/libraries/entities/src/SoundEntityItem.h new file mode 100644 index 0000000000..781c270d45 --- /dev/null +++ b/libraries/entities/src/SoundEntityItem.h @@ -0,0 +1,100 @@ +// +// Created by HifiExperiments on 12/30/2023 +// Copyright 2023 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_SoundEntityItem_h +#define hifi_SoundEntityItem_h + +#include "EntityItem.h" + +#include +#include + +class SoundEntityItem : public EntityItem { +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + SoundEntityItem(const EntityItemID& entityItemID); + ~SoundEntityItem(); + + ALLOW_INSTANTIATION // This class can be instantiated + + // methods for getting/setting all properties of an entity + EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; + bool setSubClassProperties(const EntityItemProperties& properties) override; + + EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + bool shouldBePhysical() const override { return false; } + + virtual void debugDump() const override; + + virtual bool supportsDetailedIntersection() const override { return false; } + + virtual void update(const quint64& now) override; + bool needsToCallUpdate() const override { return _updateNeeded; } + + void setLocalPosition(const glm::vec3& value, bool tellPhysics = true) override; + void setLocalOrientation(const glm::quat& value) override; + + void setURL(const QString& value); + QString getURL() const; + + void setVolume(float value); + float getVolume() const; + + void setTimeOffset(float value); + float getTimeOffset() const; + + void setPitch(float value); + float getPitch() const; + + void setPlaying(bool value); + bool getPlaying() const; + + void setLoop(bool value); + bool getLoop() const; + + void setPositional(bool value); + bool getPositional() const; + + void setLocalOnly(bool value); + bool getLocalOnly() const; + + bool restartSound(); + +protected: + void updateSound(bool restart = false); + + QString _url { "" }; + float _volume { 1.0f }; + float _timeOffset { 0.0f }; + float _pitch { 1.0f }; + bool _playing { true }; + bool _loop { true }; + bool _positional { true }; + bool _localOnly { false }; + + SharedSoundPointer _sound; + AudioInjectorPointer _injector; + bool _updateNeeded { false }; +}; + +#endif // hifi_SoundEntityItem_h diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 6f84f25e86..46e2fe61c7 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -294,6 +294,7 @@ enum class EntityVersion : PacketVersion { EntityTags, WantsKeyboardFocus, AudioZones, + SoundEntities, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/physics/CMakeLists.txt b/libraries/physics/CMakeLists.txt index 3237e712f9..ad6d5292b9 100644 --- a/libraries/physics/CMakeLists.txt +++ b/libraries/physics/CMakeLists.txt @@ -5,6 +5,7 @@ set(TARGET_NAME physics) setup_hifi_library() link_hifi_libraries(shared workload entities shaders) +include_hifi_library_headers(audio) include_hifi_library_headers(networking) include_hifi_library_headers(gpu) include_hifi_library_headers(avatars) diff --git a/scripts/system/assets/images/tools/sound.svg b/scripts/system/assets/images/tools/sound.svg new file mode 100644 index 0000000000..a71b449045 --- /dev/null +++ b/scripts/system/assets/images/tools/sound.svg @@ -0,0 +1,258 @@ + + + +SOUNDSOUNDSOUND + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index b4597e9141..efb33fd422 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -706,5 +706,29 @@ }, "zTextureURL": { "tooltip": "The URL of the texture to map to surfaces perpendicular to the entity's local z-axis. JPG or PNG format." + }, + "soundURL": { + "tooltip": "The URL of the sound, as a wav, mp3, or raw file." + }, + "playing": { + "tooltip": "Whether or not the sound should play." + }, + "volume": { + "tooltip": "The volume of the sound." + }, + "pitch": { + "tooltip": "Alter the pitch of the sound, within +/- 2 octaves. The value is the relative sample rate at which to resample the sound." + }, + "timeOffset": { + "tooltip": "Starts playback from a specified time (seconds) within the sound file." + }, + "loop": { + "tooltip": "Whether or not the sound is played repeatedly." + }, + "positional": { + "tooltip": "Whether or not the sound volume drops off with distance." + }, + "localOnly": { + "tooltip": "Whether the sound should play locally for everyone separately, or globally via the audio mixer." } } diff --git a/scripts/system/create/assets/images/icon-sound.svg b/scripts/system/create/assets/images/icon-sound.svg new file mode 100644 index 0000000000..cf89956c41 --- /dev/null +++ b/scripts/system/create/assets/images/icon-sound.svg @@ -0,0 +1,84 @@ + +image/svg+xml diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index dabdfe34f9..ade2133803 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -51,6 +51,7 @@ var DEFAULT_IMAGE = Script.getExternalPath(Script.ExternalPaths.Assets, "Bazaar/Assets/Textures/Defaults/Interface/default_image.jpg"); var DEFAULT_PARTICLE = Script.getExternalPath(Script.ExternalPaths.Assets, "Bazaar/Assets/Textures/Defaults/Interface/default_particle.png"); + var DEFAULT_SOUND = "TODO"; var createToolsWindow = new CreateWindow( Script.resolvePath("qml/EditTools.qml"), @@ -94,8 +95,9 @@ var SPOT_LIGHT_URL = Script.resolvePath("assets/images/icon-spot-light.svg"); var ZONE_URL = Script.resolvePath("assets/images/icon-zone.svg"); var MATERIAL_URL = Script.resolvePath("assets/images/icon-material.svg"); + var SOUND_URL = Script.resolvePath("assets/images/icon-sound.svg"); - var entityIconOverlayManager = new EntityIconOverlayManager(["Light", "ParticleEffect", "Zone", "Material"], function(entityID) { + var entityIconOverlayManager = new EntityIconOverlayManager(["Light", "ParticleEffect", "Zone", "Material", "Sound"], function(entityID) { var properties = Entities.getEntityProperties(entityID, ["type", "isSpotlight", "parentID", "name"]); if (properties.type === "Light") { return { @@ -109,6 +111,8 @@ } else { return { imageURL: "" }; } + } else if (properties.type === "Sound") { + return { imageURL: SOUND_URL, rotation: Quat.fromPitchYawRollDegrees(0, 0, 0) }; } else { return { imageURL: PARTICLE_SYSTEM_URL }; } @@ -492,6 +496,9 @@ exponent: 1.0, cutoff: 75.0, }, + Sound: { + soundURL: DEFAULT_SOUND + }, }; var toolBar = (function () { @@ -570,7 +577,7 @@ if (!properties.grab) { properties.grab = {}; if (Menu.isOptionChecked(MENU_CREATE_ENTITIES_GRABBABLE) && - !(properties.type === "Zone" || properties.type === "Light" + !(properties.type === "Zone" || properties.type === "Light" || properties.type === "Sound" || properties.type === "ParticleEffect" || properties.type === "Web")) { properties.grab.grabbable = true; } else { @@ -1056,6 +1063,12 @@ addButton("newPolyVoxButton", createNewEntityDialogButtonCallback("PolyVox")); + addButton("newSoundButton", function () { + createNewEntity({ + type: "Sound", + }); + }); + var deactivateCreateIfDesktopWindowsHidden = function() { if (!shouldUseEditTabletApp() && !entityListTool.isVisible() && !createToolsWindow.isVisible()) { that.setActive(false); @@ -2041,7 +2054,7 @@ var entityParentIDs = []; var propType = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]).type; - var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"]; + var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "Sound", "ParticleEffect"]; if (NO_ADJUST_ENTITY_TYPES.indexOf(propType) === -1) { var targetDirection; if (Camera.mode === "entity" || Camera.mode === "independent") { diff --git a/scripts/system/create/entityList/entityList.js b/scripts/system/create/entityList/entityList.js index 257f967852..17dd942e13 100644 --- a/scripts/system/create/entityList/entityList.js +++ b/scripts/system/create/entityList/entityList.js @@ -211,7 +211,7 @@ var EntityListTool = function(shouldUseEditTabletApp, selectionManager) { PROFILE("getMultipleProperties", function () { var multipleProperties = Entities.getMultipleEntityProperties(ids, ['position', 'name', 'type', 'locked', 'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'serverScripts', - 'skybox.url', 'ambientLight.url', 'created', 'lastEdited']); + 'skybox.url', 'ambientLight.url', 'soundURL', 'created', 'lastEdited']); for (var i = 0; i < multipleProperties.length; i++) { var properties = multipleProperties[i]; @@ -223,6 +223,8 @@ var EntityListTool = function(shouldUseEditTabletApp, selectionManager) { url = properties.materialURL; } else if (properties.type === "Image") { url = properties.imageURL; + } else if (properties.type === "Sound") { + url = properties.soundURL; } //print("Global object before getParentState call: " + JSON.stringify(globalThis)); var parentStatus = that.createApp.getParentState(ids[i]); diff --git a/scripts/system/create/entityList/html/js/entityList.js b/scripts/system/create/entityList/html/js/entityList.js index a64d95a64c..213e4ad09c 100644 --- a/scripts/system/create/entityList/html/js/entityList.js +++ b/scripts/system/create/entityList/html/js/entityList.js @@ -178,6 +178,7 @@ const FILTER_TYPES = [ "PolyVox", "Text", "Grid", + "Sound", ]; const DOUBLE_CLICK_TIMEOUT = 300; // ms diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 8a1540d411..797156081e 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -1432,6 +1432,62 @@ const GROUPS = [ }, ] }, + { + id: "sound", + label: "SOUND", + properties: [ + { + label: "Sound", + type: "string", + propertyID: "soundURL", + placeholder: "URL", + }, + { + label: "Playing", + type: "bool", + propertyID: "playing", + }, + { + label: "Loop", + type: "bool", + propertyID: "loop", + }, + { + label: "Volume", + type: "number-draggable", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "volume", + }, + { + label: "Positional", + type: "bool", + propertyID: "positional", + }, + { + label: "Pitch", + type: "number-draggable", + min: 0.0625, + max: 16, + step: 0.1, + decimals: 2, + propertyID: "pitch", + }, + { + label: "Time Offset", + type: "number-draggable", + step: 0.1, + propertyID: "timeOffset", + }, + { + label: "Local Only", + type: "bool", + propertyID: "localOnly", + } + ] + }, { id: "spatial", label: "SPATIAL", @@ -1811,6 +1867,7 @@ const GROUPS_PER_TYPE = { PolyLine: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], PolyVox: [ 'base', 'polyvox', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Grid: [ 'base', 'grid', 'spatial', 'behavior', 'scripts', 'physics' ], + Sound: [ 'base', 'sound', 'spatial', 'behavior', 'scripts', 'physics' ], Multiple: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], }; diff --git a/scripts/system/create/entityProperties/html/tabs/sound.png b/scripts/system/create/entityProperties/html/tabs/sound.png new file mode 100644 index 0000000000000000000000000000000000000000..218bfe7c4ffa41221ac8182017661d4136236684 GIT binary patch literal 684 zcmWlWTS!v@7=ZtCnLC@?JTmi!>G776wwkp<4Yy;?r9qx?0Lf1Qf~tG3&*=crs7!5Ye*L=f@aRZ`DG(YG5|)AiCqF8HplroJ(pC9)(^OjR#`}u65*vjjkLr`n?MirCR1TM4efkg8DkK^FV z!X_ZC;aZfgB)AIt8*on;S1naa+&HqV9MB~gH^O*Kvg1PWRV5HqdC)HEQcxaCU`(Im z;2h(T%QSwA&-mN@m`g)>d^w%64nc`EehRaCh-{vw5B;riSdH3H9&L%HMT>(u^amjq z#JUBlje^+#1Sj9{hM0yB@+3MIO8$)G36dvpZoIib5?Z2h7^yNW4=3q`z{9o`>QOc8 zJ|?k*I8+NSR_%?2!(Y;5RgydrNoNIAOm#6fzP^dp%gcbIvkFRd`n`GnV3WOLx-9`v zVhug-T-aiDVQMgpVrEj_AxzzqD$@aJt(?8@yercC&o@sE zpUvu9YM87&OMYrQusC`iMvR1u0JSoODnDDX23+y)209-HrESMWbz(Rf>6^&b`xY%H z__r4^xyv}$>s_PGs!`w^-!WD1!wqS7n@ubjo#sJ5l2(nLK~U>XAmG7p;d|iMo$19S P0D#Q&9PR6iWi9^!#`NqK literal 0 HcmV?d00001 diff --git a/scripts/system/create/entitySelectionTool/entitySelectionTool.js b/scripts/system/create/entitySelectionTool/entitySelectionTool.js index 7a188f3dc0..5238b2d374 100644 --- a/scripts/system/create/entitySelectionTool/entitySelectionTool.js +++ b/scripts/system/create/entitySelectionTool/entitySelectionTool.js @@ -2241,7 +2241,7 @@ SelectionDisplay = (function() { Entities.editEntity(selectionBox, selectionBoxGeometry); // UPDATE ICON TRANSLATE HANDLE - if (SelectionManager.entityType === "ParticleEffect" || SelectionManager.entityType === "Light") { + if (SelectionManager.entityType === "ParticleEffect" || SelectionManager.entityType === "Light" || SelectionManager.entityType === "Sound") { var iconSelectionBoxGeometry = { position: position, rotation: rotation diff --git a/scripts/system/create/modules/brokenURLReport.js b/scripts/system/create/modules/brokenURLReport.js index c04612bb27..4224826023 100644 --- a/scripts/system/create/modules/brokenURLReport.js +++ b/scripts/system/create/modules/brokenURLReport.js @@ -352,6 +352,17 @@ function brokenURLReport(entityIDs) { }; brokenURLReportUrlList.push(brokenURLReportUrlEntry); } + if (properties.type === "Sound" && properties.soundURL.toLowerCase().startsWith("http")) { + brokenURLReportUrlEntry = { + id: entityIDs[i], + name: properties.name, + type: properties.type, + urlType: "soundURL", + url: soundURL, + validity: "NOT_TESTED" + }; + brokenURLReportUrlList.push(brokenURLReportUrlEntry); + } } if (brokenURLReportUrlList.length === 0) { audioFeedback.confirmation(); diff --git a/scripts/system/create/qml/EditTabView.qml b/scripts/system/create/qml/EditTabView.qml index 96e66c109e..26cc90a357 100644 --- a/scripts/system/create/qml/EditTabView.qml +++ b/scripts/system/create/qml/EditTabView.qml @@ -190,6 +190,18 @@ TabBar { editTabView.currentIndex = 2 } } + + NewEntityButton { + icon: "icons/sound.svg" + text: "SOUND" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", + params: { buttonName: "newSoundButton" } + }); + editTabView.currentIndex = 2 + } + } } HifiControls.Button { diff --git a/scripts/system/create/qml/EditToolsTabView.qml b/scripts/system/create/qml/EditToolsTabView.qml index 998c3a3aac..445f084392 100644 --- a/scripts/system/create/qml/EditToolsTabView.qml +++ b/scripts/system/create/qml/EditToolsTabView.qml @@ -196,6 +196,18 @@ TabBar { editTabView.currentIndex = tabIndex.properties } } + + NewEntityButton { + icon: "icons/sound.svg" + text: "SOUND" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", + params: { buttonName: "newSoundButton" } + }); + editTabView.currentIndex = tabIndex.properties + } + } } HifiControls.Button { diff --git a/scripts/system/create/qml/icons/sound.svg b/scripts/system/create/qml/icons/sound.svg new file mode 100644 index 0000000000..448156343d --- /dev/null +++ b/scripts/system/create/qml/icons/sound.svg @@ -0,0 +1,59 @@ + + + + + + + diff --git a/scripts/system/html/js/includes.js b/scripts/system/html/js/includes.js index c604115f91..69ae9bed2d 100644 --- a/scripts/system/html/js/includes.js +++ b/scripts/system/html/js/includes.js @@ -20,6 +20,7 @@ const ENTITY_TYPE_ICON = { PolyLine: "", Shape: "n", Sphere: "n", + Sound: "G", Text: "l", Web: "q", Zone: "o", From 968fa4af514667fdfcd3fe06a301038aba46bcc1 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Tue, 26 Mar 2024 16:56:54 -0700 Subject: [PATCH 032/109] fix layered simulate items --- libraries/render/src/render/FilterTask.cpp | 8 +++++++ libraries/render/src/render/FilterTask.h | 12 ++++++++++ .../src/render/RenderFetchCullSortTask.cpp | 23 +++++++++++-------- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/libraries/render/src/render/FilterTask.cpp b/libraries/render/src/render/FilterTask.cpp index b269f44b41..90720f5666 100644 --- a/libraries/render/src/render/FilterTask.cpp +++ b/libraries/render/src/render/FilterTask.cpp @@ -148,3 +148,11 @@ void IDsToBounds::run(const RenderContextPointer& renderContext, const ItemIDs& } } } + +void MergeItems::run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { + const auto& array1 = inputs.get0(); + const auto& array2 = inputs.get1(); + + outputs = array1; + outputs.insert(outputs.end(), array2.begin(), array2.end()); +} diff --git a/libraries/render/src/render/FilterTask.h b/libraries/render/src/render/FilterTask.h index c2244e5f57..1a8f8f2e15 100644 --- a/libraries/render/src/render/FilterTask.h +++ b/libraries/render/src/render/FilterTask.h @@ -158,6 +158,18 @@ namespace render { bool _disableAABBs{ false }; }; + // Concatenate two arrays of items + class MergeItems { + public: + using Inputs = VaryingSet2; + using Outputs = ItemBounds; + using JobModel = Job::ModelIO; + + MergeItems() {} + + void run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); + }; + } #endif // hifi_render_FilterTask_h; \ No newline at end of file diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index 5bb5e75774..4619c30de4 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -36,25 +36,26 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin // Multi filter visible items into different buckets const int NUM_SPATIAL_FILTERS = 6; - const int NUM_NON_SPATIAL_FILTERS = 3; + const int NUM_NON_SPATIAL_FILTERS = 4; 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 SIMULATE_BUCKET = 5; - const int BACKGROUND_BUCKET = 2; + const int SIMULATE_BUCKET = 2; + const int LIGHT_BUCKET = 3; + const int META_BUCKET = 4; + const int MIRROR_BUCKET = 5; + const int BACKGROUND_BUCKET = 3; MultiFilterItems::ItemFilterArray spatialFilters = { { ItemFilter::Builder::opaqueShape().withoutMirror(), ItemFilter::Builder::transparentShape(), + ItemFilter::Builder().withSimulate(), ItemFilter::Builder::light(), ItemFilter::Builder::meta().withoutMirror(), - ItemFilter::Builder::mirror(), - ItemFilter::Builder().withSimulate() + ItemFilter::Builder::mirror() } }; MultiFilterItems::ItemFilterArray nonspatialFilters = { { ItemFilter::Builder::opaqueShape(), ItemFilter::Builder::transparentShape(), + ItemFilter::Builder().withSimulate(), ItemFilter::Builder::background() } }; const auto filteredSpatialBuckets = @@ -79,9 +80,13 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin const auto filteredLayeredOpaque = task.addJob("FilterLayeredOpaque", layeredOpaques, ItemKey::Layer::LAYER_1); const auto filteredLayeredTransparent = task.addJob("FilterLayeredTransparent", layeredTransparents, ItemKey::Layer::LAYER_1); + // collect our simulate objects from both buckets + const auto mergeInputs = MergeItems::Inputs(filteredSpatialBuckets[SIMULATE_BUCKET], filteredNonspatialBuckets[SIMULATE_BUCKET]).asVarying(); + const auto simulate = task.addJob("MergeSimulateItems", mergeInputs); + task.addJob("ClearContainingZones"); - output = Output(BucketList{ opaques, transparents, lights, metas, mirrors, filteredSpatialBuckets[SIMULATE_BUCKET], + output = Output(BucketList{ opaques, transparents, lights, metas, mirrors, simulate, filteredLayeredOpaque.getN(0), filteredLayeredTransparent.getN(0), filteredLayeredOpaque.getN(1), filteredLayeredTransparent.getN(1), background }, spatialSelection); From 9914d4d133807c832baaab9130f83d0bcdb10df4 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 30 Mar 2024 00:05:11 -0700 Subject: [PATCH 033/109] fix stereo sound speed --- libraries/entities/src/EntityItemProperties.cpp | 1 + libraries/entities/src/SoundEntityItem.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index a32dcf78c0..7d5c01fd25 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -1357,6 +1357,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @typedef {object} Entities.EntityProperties-Sound * @property {string} soundURL="" - The URL of the sound to play, as a wav, mp3, or raw file. Supports stereo and ambisonic. + * Note: ambisonic sounds can only play as localOnly. * @property {boolean} playing=true - Whether or not the sound should play. * @property {number} volume=1.0 - The volume of the sound, from 0 to 1. * @property {number} pitch=1.0 - The relative sample rate at which to resample the sound, within +/- 2 octaves. diff --git a/libraries/entities/src/SoundEntityItem.cpp b/libraries/entities/src/SoundEntityItem.cpp index 575efabb2e..997225515c 100644 --- a/libraries/entities/src/SoundEntityItem.cpp +++ b/libraries/entities/src/SoundEntityItem.cpp @@ -336,10 +336,14 @@ bool SoundEntityItem::restartSound() { options.volume = _volume; options.loop = _loop; options.orientation = getWorldOrientation(); - options.localOnly = _localOnly; + options.localOnly = _localOnly || _sound->isAmbisonic(); // force localOnly when ambisonic options.secondOffset = _timeOffset; options.pitch = _pitch; + // stereo option isn't set from script, this comes from sound metadata or filename + options.stereo = _sound->isStereo(); + options.ambisonic = _sound->isAmbisonic(); + if (_injector) { DependencyManager::get()->setOptionsAndRestart(_injector, options); } else { From b7a3fb107295872fdd7b605446a705ecfbc4a3cd Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 7 Apr 2024 12:54:37 -0700 Subject: [PATCH 034/109] support non-localOnly sound avatar entities --- libraries/entities/src/SoundEntityItem.cpp | 12 +++++++++--- libraries/entities/src/SoundEntityItem.h | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/SoundEntityItem.cpp b/libraries/entities/src/SoundEntityItem.cpp index 997225515c..57ce158ee0 100644 --- a/libraries/entities/src/SoundEntityItem.cpp +++ b/libraries/entities/src/SoundEntityItem.cpp @@ -129,13 +129,19 @@ void SoundEntityItem::debugDump() const { qCDebug(entities) << "SOUND EntityItem Ptr:" << this; } +bool SoundEntityItem::shouldCreateSound(const EntityTreePointer& tree) const { + bool clientShouldMakeSound = _localOnly || isMyAvatarEntity() || tree->isServerlessMode(); + bool serverShouldMakeSound = !_localOnly; + return (clientShouldMakeSound && tree->getIsClient()) || (serverShouldMakeSound && tree->isEntityServer()); +} + void SoundEntityItem::update(const quint64& now) { withWriteLock([&] { const auto tree = getTree(); if (tree) { _updateNeeded = false; - if ((_localOnly && tree->getIsClient()) || (!_localOnly && (tree->isEntityServer() || tree->isServerlessMode()))) { + if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->getSound(_url); } @@ -175,7 +181,7 @@ void SoundEntityItem::setURL(const QString& value) { return; } - if ((_localOnly && tree->getIsClient()) || (!_localOnly && (tree->isEntityServer() || tree->isServerlessMode()))) { + if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->getSound(_url); } @@ -297,7 +303,7 @@ void SoundEntityItem::setLocalOnly(bool value) { return; } - if ((_localOnly && tree->getIsClient()) || (!_localOnly && (tree->isEntityServer() || tree->isServerlessMode()))) { + if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->getSound(_url); } else { _sound = nullptr; diff --git a/libraries/entities/src/SoundEntityItem.h b/libraries/entities/src/SoundEntityItem.h index 781c270d45..5cbd4bb814 100644 --- a/libraries/entities/src/SoundEntityItem.h +++ b/libraries/entities/src/SoundEntityItem.h @@ -81,6 +81,7 @@ public: bool restartSound(); protected: + bool shouldCreateSound(const EntityTreePointer& tree) const; void updateSound(bool restart = false); QString _url { "" }; From eb7d97064fc1937586cb9355d6c00b5864b12413 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 7 Apr 2024 13:40:42 -0700 Subject: [PATCH 035/109] add sound url prompt --- scripts/system/create/edit.js | 32 +++- scripts/system/create/qml/NewSoundDialog.qml | 159 +++++++++++++++++++ scripts/system/create/qml/NewSoundWindow.qml | 31 ++++ 3 files changed, 215 insertions(+), 7 deletions(-) create mode 100644 scripts/system/create/qml/NewSoundDialog.qml create mode 100644 scripts/system/create/qml/NewSoundWindow.qml diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index ade2133803..27502af711 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -51,7 +51,6 @@ var DEFAULT_IMAGE = Script.getExternalPath(Script.ExternalPaths.Assets, "Bazaar/Assets/Textures/Defaults/Interface/default_image.jpg"); var DEFAULT_PARTICLE = Script.getExternalPath(Script.ExternalPaths.Assets, "Bazaar/Assets/Textures/Defaults/Interface/default_particle.png"); - var DEFAULT_SOUND = "TODO"; var createToolsWindow = new CreateWindow( Script.resolvePath("qml/EditTools.qml"), @@ -497,7 +496,11 @@ cutoff: 75.0, }, Sound: { - soundURL: DEFAULT_SOUND + volume: 1.0, + playing: true, + loop: true, + positional: true, + localOnly: false }, }; @@ -866,6 +869,18 @@ } } + function handleNewSoundDialogResult(result) { + if (result) { + var soundURL = result.textInput; + if (soundURL) { + createNewEntity({ + type: "Sound", + soundURL: soundURL + }); + } + } + } + function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml. var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); tablet.popFromStack(); @@ -894,6 +909,13 @@ case "newPolyVoxDialogCancel": closeExistingDialogWindow(); break; + case "newSoundDialogAdd": + handleNewSoundDialogResult(message.params); + closeExistingDialogWindow(); + break; + case "newSoundDialogCancel": + closeExistingDialogWindow(); + break; } } @@ -1063,11 +1085,7 @@ addButton("newPolyVoxButton", createNewEntityDialogButtonCallback("PolyVox")); - addButton("newSoundButton", function () { - createNewEntity({ - type: "Sound", - }); - }); + addButton("newSoundButton", createNewEntityDialogButtonCallback("Sound")); var deactivateCreateIfDesktopWindowsHidden = function() { if (!shouldUseEditTabletApp() && !entityListTool.isVisible() && !createToolsWindow.isVisible()) { diff --git a/scripts/system/create/qml/NewSoundDialog.qml b/scripts/system/create/qml/NewSoundDialog.qml new file mode 100644 index 0000000000..d0b470109b --- /dev/null +++ b/scripts/system/create/qml/NewSoundDialog.qml @@ -0,0 +1,159 @@ +// +// NewSoundDialog.qml +// qml/hifi +// +// Created by HifiExperiments on 4/7/24 +// Copyright 2024 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 +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Dialogs 1.2 as OriginalDialogs + +import stylesUit 1.0 +import controlsUit 1.0 +import hifi.dialogs 1.0 + +Rectangle { + id: newSoundDialog + // width: parent.width + // height: parent.height + HifiConstants { id: hifi } + color: hifi.colors.baseGray; + signal sendToScript(var message); + property bool keyboardEnabled: false + property bool punctuationMode: false + property bool keyboardRasied: false + + function errorMessageBox(message) { + try { + return desktop.messageBox({ + icon: hifi.icons.warning, + defaultButton: OriginalDialogs.StandardButton.Ok, + title: "Error", + text: message + }); + } catch(e) { + Window.alert(message); + } + } + + Item { + id: column1 + anchors.rightMargin: 10 + anchors.leftMargin: 10 + anchors.bottomMargin: 10 + anchors.topMargin: 10 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: keyboard.top + + Text { + id: text1 + text: qsTr("Sound URL") + color: "#ffffff" + font.pixelSize: 12 + } + + TextInput { + id: soundURL + height: 20 + text: qsTr("") + color: "white" + anchors.top: text1.bottom + anchors.topMargin: 5 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + font.pixelSize: 12 + + onAccepted: { + newSoundDialog.keyboardEnabled = false; + } + + MouseArea { + anchors.fill: parent + onClicked: { + newSoundDialog.keyboardEnabled = HMD.active + parent.focus = true; + parent.forceActiveFocus(); + soundURL.cursorPosition = soundURL.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters); + } + } + } + + Rectangle { + id: textInputBox + color: "white" + anchors.fill: soundURL + opacity: 0.1 + } + + Row { + id: row1 + height: 400 + spacing: 30 + anchors.top: soundURL.bottom + anchors.topMargin: 5 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + + Column { + id: column3 + height: 400 + spacing: 10 + + Row { + id: row3 + width: 200 + height: 400 + spacing: 5 + + anchors.horizontalCenter: column3.horizontalCenter + anchors.horizontalCenterOffset: 0 + + Button { + id: button1 + text: qsTr("Create") + z: -1 + onClicked: { + newSoundDialog.sendToScript({ + method: "newSoundDialogAdd", + params: { + textInput: soundURL.text + } + }); + } + } + + Button { + id: button2 + z: -1 + text: qsTr("Cancel") + onClicked: { + newSoundDialog.sendToScript({method: "newSoundDialogCancel"}) + } + } + } + } + } + } + + Keyboard { + id: keyboard + raised: parent.keyboardEnabled + numeric: parent.punctuationMode + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + } +} diff --git a/scripts/system/create/qml/NewSoundWindow.qml b/scripts/system/create/qml/NewSoundWindow.qml new file mode 100644 index 0000000000..9f78ade0b1 --- /dev/null +++ b/scripts/system/create/qml/NewSoundWindow.qml @@ -0,0 +1,31 @@ +// +// NewSoundWindow.qml +// qml/hifi +// +// Created by HifiExperiments on 4/7/24 +// Copyright 2024 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 +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +StackView { + id: stackView + anchors.fill: parent + anchors.leftMargin: 10 + anchors.rightMargin: 10 + anchors.topMargin: 40 + + signal sendToScript(var message); + + NewSoundDialog { + id: dialog + anchors.fill: parent + Component.onCompleted:{ + dialog.sendToScript.connect(stackView.sendToScript); + } + } +} From 376be7b17eefdc29920ba7f4a85c3e069e68a691 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 12 Apr 2024 13:04:30 -0700 Subject: [PATCH 036/109] support registration point, improve locking --- libraries/entities/src/SoundEntityItem.cpp | 37 +++++++++++++--------- libraries/entities/src/SoundEntityItem.h | 4 +-- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/libraries/entities/src/SoundEntityItem.cpp b/libraries/entities/src/SoundEntityItem.cpp index 57ce158ee0..201592dc1f 100644 --- a/libraries/entities/src/SoundEntityItem.cpp +++ b/libraries/entities/src/SoundEntityItem.cpp @@ -136,36 +136,42 @@ bool SoundEntityItem::shouldCreateSound(const EntityTreePointer& tree) const { } void SoundEntityItem::update(const quint64& now) { - withWriteLock([&] { - const auto tree = getTree(); - if (tree) { - _updateNeeded = false; + const auto tree = getTree(); + if (tree) { + _updateNeeded = false; + withWriteLock([&] { if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->getSound(_url); } + }); + withReadLock([&] { if (_sound) { if (_sound->isLoaded()) { updateSound(true); } else { - connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); + connect(_sound.data(), &Resource::finished, this, [&] { + withReadLock([&] { + updateSound(true); + }); + }); } } - } - }); + }); + } } -void SoundEntityItem::setLocalPosition(const glm::vec3& value, bool tellPhysics) { - EntityItem::setLocalPosition(value, tellPhysics); - withWriteLock([&] { +void SoundEntityItem::locationChanged(bool tellPhysics, bool tellChildren) { + EntityItem::locationChanged(tellPhysics, tellChildren); + withReadLock([&] { updateSound(); }); } -void SoundEntityItem::setLocalOrientation(const glm::quat& value) { - EntityItem::setLocalOrientation(value); - withWriteLock([&] { +void SoundEntityItem::dimensionsChanged() { + EntityItem::dimensionsChanged(); + withReadLock([&] { updateSound(); }); } @@ -337,11 +343,12 @@ bool SoundEntityItem::restartSound() { } AudioInjectorOptions options; - options.position = getWorldPosition(); + const glm::quat orientation = getWorldOrientation(); + options.position = getWorldPosition() + orientation * (getScaledDimensions() * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); options.positionSet = _positional; options.volume = _volume; options.loop = _loop; - options.orientation = getWorldOrientation(); + options.orientation = orientation; options.localOnly = _localOnly || _sound->isAmbisonic(); // force localOnly when ambisonic options.secondOffset = _timeOffset; options.pitch = _pitch; diff --git a/libraries/entities/src/SoundEntityItem.h b/libraries/entities/src/SoundEntityItem.h index 5cbd4bb814..3d1815f557 100644 --- a/libraries/entities/src/SoundEntityItem.h +++ b/libraries/entities/src/SoundEntityItem.h @@ -51,8 +51,8 @@ public: virtual void update(const quint64& now) override; bool needsToCallUpdate() const override { return _updateNeeded; } - void setLocalPosition(const glm::vec3& value, bool tellPhysics = true) override; - void setLocalOrientation(const glm::quat& value) override; + void locationChanged(bool tellPhysics = true, bool tellChildren = true) override; + void dimensionsChanged() override; void setURL(const QString& value); QString getURL() const; From 6a180b14a1afd0acf31896d1a9de77c991e21f6a Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 12 Apr 2024 13:06:54 -0700 Subject: [PATCH 037/109] remove keyboardRasied --- scripts/system/create/qml/NewSoundDialog.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/create/qml/NewSoundDialog.qml b/scripts/system/create/qml/NewSoundDialog.qml index d0b470109b..870e8bb41d 100644 --- a/scripts/system/create/qml/NewSoundDialog.qml +++ b/scripts/system/create/qml/NewSoundDialog.qml @@ -26,7 +26,6 @@ Rectangle { signal sendToScript(var message); property bool keyboardEnabled: false property bool punctuationMode: false - property bool keyboardRasied: false function errorMessageBox(message) { try { From 98c11cef8283f6393b26ec620937fbc16c3724ae Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 14 Apr 2024 22:04:09 -0700 Subject: [PATCH 038/109] locking attempt #2 --- .../entities/src/EntityScriptingInterface.cpp | 4 +- libraries/entities/src/SoundEntityItem.cpp | 203 +++++++++++------- libraries/entities/src/SoundEntityItem.h | 3 +- 3 files changed, 133 insertions(+), 77 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index f0da5f3cf3..10c25f545b 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1924,9 +1924,7 @@ bool EntityScriptingInterface::restartSound(const QUuid& entityID) { auto soundEntity = std::dynamic_pointer_cast(entity); bool isPlaying = soundEntity->getPlaying(); if (isPlaying) { - soundEntity->withWriteLock([&] { - soundEntity->restartSound(); - }); + soundEntity->restartSound(true); } return isPlaying; } diff --git a/libraries/entities/src/SoundEntityItem.cpp b/libraries/entities/src/SoundEntityItem.cpp index 201592dc1f..71845920f9 100644 --- a/libraries/entities/src/SoundEntityItem.cpp +++ b/libraries/entities/src/SoundEntityItem.cpp @@ -138,68 +138,68 @@ bool SoundEntityItem::shouldCreateSound(const EntityTreePointer& tree) const { void SoundEntityItem::update(const quint64& now) { const auto tree = getTree(); if (tree) { + std::lock_guard lock(_soundLock); + _updateNeeded = false; - withWriteLock([&] { + withReadLock([&] { if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->getSound(_url); } }); - withReadLock([&] { - if (_sound) { - if (_sound->isLoaded()) { - updateSound(true); - } else { - connect(_sound.data(), &Resource::finished, this, [&] { - withReadLock([&] { - updateSound(true); - }); - }); - } + if (_sound) { + if (_sound->isLoaded()) { + updateSound(true); + } else { + connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); } - }); + } } } void SoundEntityItem::locationChanged(bool tellPhysics, bool tellChildren) { EntityItem::locationChanged(tellPhysics, tellChildren); - withReadLock([&] { - updateSound(); - }); + updateSound(); } void SoundEntityItem::dimensionsChanged() { EntityItem::dimensionsChanged(); - withReadLock([&] { - updateSound(); - }); + updateSound(); } void SoundEntityItem::setURL(const QString& value) { + bool changed = false; withWriteLock([&] { if (value != _url) { _url = value; + changed = true; + } + }); - const auto tree = getTree(); - if (!tree) { - _updateNeeded = true; - return; - } + if (changed) { + const auto tree = getTree(); + if (!tree) { + _updateNeeded = true; + return; + } + std::lock_guard lock(_soundLock); + + withReadLock([&] { if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->getSound(_url); } + }); - if (_sound) { - if (_sound->isLoaded()) { - updateSound(true); - } else { - connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); - } + if (_sound) { + if (_sound->isLoaded()) { + updateSound(true); + } else { + connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); } } - }); + } } QString SoundEntityItem::getURL() const { @@ -209,12 +209,17 @@ QString SoundEntityItem::getURL() const { } void SoundEntityItem::setVolume(float value) { + bool changed = false; withWriteLock([&] { if (value != _volume) { _volume = value; - updateSound(); + changed = true; } }); + + if (changed) { + updateSound(); + } } float SoundEntityItem::getVolume() const { @@ -224,12 +229,17 @@ float SoundEntityItem::getVolume() const { } void SoundEntityItem::setTimeOffset(float value) { + bool changed = false; withWriteLock([&] { if (value != _timeOffset) { _timeOffset = value; - updateSound(true); + changed = true; } }); + + if (changed) { + updateSound(true); + } } float SoundEntityItem::getTimeOffset() const { @@ -239,12 +249,17 @@ float SoundEntityItem::getTimeOffset() const { } void SoundEntityItem::setPitch(float value) { + bool changed = false; withWriteLock([&] { if (value != _pitch) { _pitch = value; - updateSound(true); + changed = true; } }); + + if (changed) { + updateSound(true); + } } float SoundEntityItem::getPitch() const { @@ -254,12 +269,17 @@ float SoundEntityItem::getPitch() const { } void SoundEntityItem::setPlaying(bool value) { + bool changed = false; withWriteLock([&] { if (value != _playing) { _playing = value; - updateSound(); + changed = true; } }); + + if (changed) { + updateSound(); + } } bool SoundEntityItem::getPlaying() const { @@ -269,12 +289,17 @@ bool SoundEntityItem::getPlaying() const { } void SoundEntityItem::setLoop(bool value) { + bool changed = false; withWriteLock([&] { if (value != _loop) { _loop = value; - updateSound(true); + changed = true; } }); + + if (changed) { + updateSound(true); + } } bool SoundEntityItem::getLoop() const { @@ -284,12 +309,17 @@ bool SoundEntityItem::getLoop() const { } void SoundEntityItem::setPositional(bool value) { + bool changed = false; withWriteLock([&] { if (value != _positional) { _positional = value; - updateSound(); + changed = true; } }); + + if (changed) { + updateSound(); + } } bool SoundEntityItem::getPositional() const { @@ -299,36 +329,48 @@ bool SoundEntityItem::getPositional() const { } void SoundEntityItem::setLocalOnly(bool value) { + bool changed = false; withWriteLock([&] { if (value != _localOnly) { _localOnly = value; - - const auto tree = getTree(); - if (!tree) { - _updateNeeded = true; - return; - } - - if (shouldCreateSound(tree)) { - _sound = DependencyManager::get()->getSound(_url); - } else { - _sound = nullptr; - - if (_injector) { - DependencyManager::get()->stop(_injector); - } - _injector = nullptr; - } - - if (_sound) { - if (_sound->isLoaded()) { - updateSound(true); - } else { - connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); - } - } + changed = true; } }); + + if (changed) { + const auto tree = getTree(); + if (!tree) { + _updateNeeded = true; + return; + } + + std::lock_guard lock(_soundLock); + + bool createdSound = false; + withReadLock([&] { + if (shouldCreateSound(tree)) { + _sound = DependencyManager::get()->getSound(_url); + createdSound = true; + } + }); + + if (!createdSound) { + _sound = nullptr; + + if (_injector) { + DependencyManager::get()->stop(_injector); + } + _injector = nullptr; + } + + if (_sound) { + if (_sound->isLoaded()) { + updateSound(true); + } else { + connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); + } + } + } } bool SoundEntityItem::getLocalOnly() const { @@ -337,21 +379,30 @@ bool SoundEntityItem::getLocalOnly() const { }); } -bool SoundEntityItem::restartSound() { +bool SoundEntityItem::restartSound(bool lock) { + if (lock) { + _soundLock.lock(); + } + if (!_sound) { + if (lock) { + _soundLock.unlock(); + } return false; } AudioInjectorOptions options; - const glm::quat orientation = getWorldOrientation(); - options.position = getWorldPosition() + orientation * (getScaledDimensions() * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); - options.positionSet = _positional; - options.volume = _volume; - options.loop = _loop; - options.orientation = orientation; - options.localOnly = _localOnly || _sound->isAmbisonic(); // force localOnly when ambisonic - options.secondOffset = _timeOffset; - options.pitch = _pitch; + withReadLock([&] { + const glm::quat orientation = getWorldOrientation(); + options.position = getWorldPosition() + orientation * (getScaledDimensions() * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); + options.positionSet = _positional; + options.volume = _volume; + options.loop = _loop; + options.orientation = orientation; + options.localOnly = _localOnly || _sound->isAmbisonic(); // force localOnly when ambisonic + options.secondOffset = _timeOffset; + options.pitch = _pitch; + }); // stereo option isn't set from script, this comes from sound metadata or filename options.stereo = _sound->isStereo(); @@ -363,10 +414,16 @@ bool SoundEntityItem::restartSound() { _injector = DependencyManager::get()->playSound(_sound, options); } + if (lock) { + _soundLock.unlock(); + } + return true; } void SoundEntityItem::updateSound(bool restart) { + std::lock_guard lock(_soundLock); + if (!_sound) { return; } @@ -385,4 +442,4 @@ void SoundEntityItem::updateSound(bool restart) { DependencyManager::get()->stop(_injector); } } -} \ No newline at end of file +} diff --git a/libraries/entities/src/SoundEntityItem.h b/libraries/entities/src/SoundEntityItem.h index 3d1815f557..bd590d29d2 100644 --- a/libraries/entities/src/SoundEntityItem.h +++ b/libraries/entities/src/SoundEntityItem.h @@ -78,7 +78,7 @@ public: void setLocalOnly(bool value); bool getLocalOnly() const; - bool restartSound(); + bool restartSound(bool lock = false); protected: bool shouldCreateSound(const EntityTreePointer& tree) const; @@ -93,6 +93,7 @@ protected: bool _positional { true }; bool _localOnly { false }; + std::recursive_mutex _soundLock; SharedSoundPointer _sound; AudioInjectorPointer _injector; bool _updateNeeded { false }; From ac29616b7cbb7e6d31e5807c12408299b888516a Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Wed, 17 Apr 2024 15:43:01 -0700 Subject: [PATCH 039/109] fix keyboardRasied typo --- scripts/system/create/qml/NewMaterialDialog.qml | 1 - scripts/system/create/qml/NewModelDialog.qml | 1 - scripts/system/create/qml/NewParticleDialog.qml | 1 - scripts/system/create/qml/NewPolyVoxDialog.qml | 1 - 4 files changed, 4 deletions(-) diff --git a/scripts/system/create/qml/NewMaterialDialog.qml b/scripts/system/create/qml/NewMaterialDialog.qml index e08ca868b6..ab61bc05a2 100644 --- a/scripts/system/create/qml/NewMaterialDialog.qml +++ b/scripts/system/create/qml/NewMaterialDialog.qml @@ -27,7 +27,6 @@ Rectangle { signal sendToScript(var message); property bool keyboardEnabled: false property bool punctuationMode: false - property bool keyboardRasied: false function errorMessageBox(message) { try { diff --git a/scripts/system/create/qml/NewModelDialog.qml b/scripts/system/create/qml/NewModelDialog.qml index c03e7c1c35..8df6bad33f 100644 --- a/scripts/system/create/qml/NewModelDialog.qml +++ b/scripts/system/create/qml/NewModelDialog.qml @@ -27,7 +27,6 @@ Rectangle { property bool keyboardEnabled: false property bool keyboardRaised: false property bool punctuationMode: false - property bool keyboardRasied: false function errorMessageBox(message) { try { diff --git a/scripts/system/create/qml/NewParticleDialog.qml b/scripts/system/create/qml/NewParticleDialog.qml index 6eadcf7f75..c07843f30a 100644 --- a/scripts/system/create/qml/NewParticleDialog.qml +++ b/scripts/system/create/qml/NewParticleDialog.qml @@ -26,7 +26,6 @@ Rectangle { property bool keyboardEnabled: false property bool keyboardRaised: false property bool punctuationMode: false - property bool keyboardRasied: false function errorMessageBox(message) { try { diff --git a/scripts/system/create/qml/NewPolyVoxDialog.qml b/scripts/system/create/qml/NewPolyVoxDialog.qml index 0f8ab5541d..c10b32840b 100644 --- a/scripts/system/create/qml/NewPolyVoxDialog.qml +++ b/scripts/system/create/qml/NewPolyVoxDialog.qml @@ -29,7 +29,6 @@ Rectangle { property bool keyboardEnabled: false property bool keyboardRaised: false property bool punctuationMode: false - property bool keyboardRasied: false function errorMessageBox(message) { try { From a6491d62252bfc4b07d7a5cd51c9fd82fb2ec7d2 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Wed, 17 Apr 2024 15:43:20 -0700 Subject: [PATCH 040/109] add default particle props --- .../proceduralParticleSwarmRender.frag | 7 ++ .../proceduralParticleSwarmRender.vert | 86 +++++++++++++++++++ .../proceduralParticleSwarmUpdate.frag | 73 ++++++++++++++++ .../entities/src/EntityItemProperties.cpp | 18 +++- scripts/system/create/edit.js | 24 +++++- 5 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 interface/resources/shaders/proceduralParticleSwarmRender.frag create mode 100644 interface/resources/shaders/proceduralParticleSwarmRender.vert create mode 100644 interface/resources/shaders/proceduralParticleSwarmUpdate.frag diff --git a/interface/resources/shaders/proceduralParticleSwarmRender.frag b/interface/resources/shaders/proceduralParticleSwarmRender.frag new file mode 100644 index 0000000000..746191814c --- /dev/null +++ b/interface/resources/shaders/proceduralParticleSwarmRender.frag @@ -0,0 +1,7 @@ +layout(location=2) in vec3 _normalWS; + +float getProceduralFragment(inout ProceduralFragment proceduralData) { + proceduralData.normal = normalize(_normalWS); + proceduralData.diffuse = 0.5 * (proceduralData.normal + 1.0); + return 0.0; +} diff --git a/interface/resources/shaders/proceduralParticleSwarmRender.vert b/interface/resources/shaders/proceduralParticleSwarmRender.vert new file mode 100644 index 0000000000..4f9806d1a3 --- /dev/null +++ b/interface/resources/shaders/proceduralParticleSwarmRender.vert @@ -0,0 +1,86 @@ +uniform float radius = 0.01; +uniform float lifespan = 1.0; // seconds + +layout(location=2) out vec3 _normalWS; + +float bezierInterpolate(float y1, float y2, float y3, float u) { + // https://en.wikipedia.org/wiki/Bezier_curve + return (1.0 - u) * (1.0 - u) * y1 + 2.0 * (1.0 - u) * u * y2 + u * u * y3; +} + +float interpolate3Points(float y1, float y2, float y3, float u) { + // Makes the interpolated values intersect the middle value. + + if ((u <= 0.5f && y1 == y2) || (u >= 0.5f && y2 == y3)) { + // Flat line. + return y2; + } + + float halfSlope; + if ((y2 >= y1 && y2 >= y3) || (y2 <= y1 && y2 <= y3)) { + // U or inverted-U shape. + // Make the slope at y2 = 0, which means that the control points half way between the value points have the value y2. + halfSlope = 0.0f; + + } else { + // L or inverted and/or mirrored L shape. + // Make the slope at y2 be the slope between y1 and y3, up to a maximum of double the minimum of the slopes between y1 + // and y2, and y2 and y3. Use this slope to calculate the control points half way between the value points. + // Note: The maximum ensures that the control points and therefore the interpolated values stay between y1 and y3. + halfSlope = (y3 - y1) / 2.0f; + float slope12 = y2 - y1; + float slope23 = y3 - y2; + + { + float check = float(abs(halfSlope) > abs(slope12)); + halfSlope = mix(halfSlope, slope12, check); + halfSlope = mix(halfSlope, slope23, (1.0 - check) * float(abs(halfSlope) > abs(slope23))); + } + } + + float stepU = step(0.5f, u); // 0.0 if u < 0.5, 1.0 otherwise. + float slopeSign = 2.0f * stepU - 1.0f; // -1.0 if u < 0.5, 1.0 otherwise + float start = (1.0f - stepU) * y1 + stepU * y2; // y1 if u < 0.5, y2 otherwise + float middle = y2 + slopeSign * halfSlope; + float finish = (1.0f - stepU) * y2 + stepU * y3; // y2 if u < 0.5, y3 otherwise + float v = 2.0f * u - step(0.5f, u); // 0.0-0.5 -> 0.0-1.0 and 0.5-1.0 -> 0.0-1.0 + return bezierInterpolate(start, middle, finish, v); +} + +vec3 getProceduralVertex(const int particleID) { + vec4 positionAndAge = getParticleProperty(0, particleID); + vec3 position = positionAndAge.xyz; + + const vec3 UP = vec3(0, 1, 0); + vec3 forward = normalize(getParticleProperty(1, particleID).xyz); + vec3 right = cross(forward, UP); + vec3 up = cross(right, forward); + + const int VERTEX = gl_VertexID % 3; + int TRIANGLE = int(gl_VertexID / 3); + + float age = positionAndAge.w; + float particleRadius = interpolate3Points(0.0, radius, 0.0, clamp(age / lifespan, 0.0, 1.0)); + + if (TRIANGLE < 3) { + const vec3 SIDE_POINTS[3] = vec3[3]( + up, + normalize(-up + right), + normalize(-up - right) + ); + position += particleRadius * (VERTEX == 2 ? forward : SIDE_POINTS[(TRIANGLE + VERTEX) % 3]); + _normalWS = normalize(cross(forward - SIDE_POINTS[TRIANGLE], forward - SIDE_POINTS[(TRIANGLE + 1) % 3])); + } else { + TRIANGLE -= 3; + vec3 backward = -2.0 * normalize(getParticleProperty(2, particleID).xyz); + const vec3 SIDE_POINTS[3] = vec3[3]( + up, + normalize(-up - right), + normalize(-up + right) + ); + position += particleRadius * (VERTEX == 2 ? backward : SIDE_POINTS[(TRIANGLE + VERTEX) % 3]); + _normalWS = normalize(cross(backward - SIDE_POINTS[TRIANGLE], backward - SIDE_POINTS[(TRIANGLE + 1) % 3])); + } + + return position; +} diff --git a/interface/resources/shaders/proceduralParticleSwarmUpdate.frag b/interface/resources/shaders/proceduralParticleSwarmUpdate.frag new file mode 100644 index 0000000000..cd052cb8db --- /dev/null +++ b/interface/resources/shaders/proceduralParticleSwarmUpdate.frag @@ -0,0 +1,73 @@ +uniform float lifespan = 1.0; // seconds +uniform float speed = 0.1; // m/s +uniform float speedSpread = 0.25; +uniform float mass = 1.0; + +const float G = 6.67e-11; + +// prop0: xyz: position, w: age +// prop1: xyz: velocity, w: prevUpdateTime +// prop2: xyz: prevVelocity + +vec3 initPosition(const int particleID) { + return 0.5 * (vec3(hifi_hash(particleID + iGlobalTime), + hifi_hash(particleID + iGlobalTime + 1.0), + hifi_hash(particleID + iGlobalTime + 2.0)) - 0.5); +} + +mat3 rotationMatrix(vec3 axis, float angle) { + float s = sin(angle); + float c = cos(angle); + float oc = 1.0 - c; + + return mat3(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, + oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, + oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c); +} + +vec3 initVelocity(const int particleID, const vec3 position) { + const float particleSpeed = speed * ((1.0 - speedSpread) + speedSpread * hifi_hash(particleID + iGlobalTime + 3.0)); + vec3 r = normalize(iWorldPosition - position); + float angle = 2.0 * 3.14159 * hifi_hash(particleID + iGlobalTime + 4.0); + return particleSpeed * rotationMatrix(r, angle) * cross(r, vec3(0, 1, 0)); +} + +void updateParticleProps(const int particleID, inout ParticleUpdateProps particleProps) { + // First draw + if (particleProps.prop1.w < 0.00001) { + particleProps.prop0.xyz = iWorldOrientation * (initPosition(particleID) * iWorldScale) + iWorldPosition; + particleProps.prop0.w = -lifespan * hifi_hash(particleID + iGlobalTime + 3.0); + particleProps.prop1.xyz = initVelocity(particleID, particleProps.prop0.xyz); + particleProps.prop1.w = iGlobalTime; + particleProps.prop2.xyz = particleProps.prop1.xyz; + return; + } + + // Particle expired + if (particleProps.prop0.w >= lifespan) { + particleProps.prop0.xyz = iWorldOrientation * (initPosition(particleID) * iWorldScale) + iWorldPosition; + particleProps.prop0.w = 0.0; + particleProps.prop1.xyz = initVelocity(particleID, particleProps.prop0.xyz); + particleProps.prop1.w = iGlobalTime; + particleProps.prop2.xyz = particleProps.prop1.xyz; + return; + } + + float dt = 0.01666666666;//max(0.0, iGlobalTime - particleProps.prop1.w); + particleProps.prop2.xyz = particleProps.prop1.xyz; + if (particleProps.prop0.w >= 0.0) { + // gravitational acceleration + vec3 r = iWorldPosition - particleProps.prop0.xyz; + vec3 g = (G * mass / max(0.01, dot(r, r))) * r; + + // position + particleProps.prop0.xyz += particleProps.prop1.xyz * dt + (0.5 * dt * dt) * g; + // velocity + particleProps.prop1.xyz += g * dt; + } + + // age + particleProps.prop0.w += dt; + // prevUpdateTime + particleProps.prop1.w = iGlobalTime; +} diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 81fd692e58..60559e54c7 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -1352,10 +1352,26 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * can manipulate the properties of, and use JSON.stringify() to convert the object into a string to put in * the property. * - * @example TODO + * @example A cube of oscillating, unlit, billboarded triangles, with the oscillation in the update (computed once per particle instead of once per vertex). * particles = Entities.addEntity({ * type: "ProceduralParticleEffect", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -4 })), + * dimensions: 3, + * numParticles: 10000, + * numTrianglesPerParticle: 1, + * numUpdateProps: 1, + * particleUpdateData: JSON.stringify({ + * version: 1.0, + * fragmentShaderURL: "https://gist.githubusercontent.com/HifiExperiments/9049fb4a8dcd2c1401ff4321103dce16/raw/4f9474ed82c66c1f94c1055d2724af808cd7aace/proceduralParticleUpdate.fs", + * }), + * particleRenderData: JSON.stringify({ + * version: 1.0, + * vertexShaderURL: "https://gist.github.com/HifiExperiments/5dda24e28e7de1719e3a594d81306343/raw/92e0c5b82a9fa87685064cdbab92ed0c16f49f94/proceduralParticle2.vs", + * fragmentShaderURL: "https://gist.github.com/HifiExperiments/7def54504362c7bc79b5c85cd515b98b/raw/93b3828c2ec66b12b789a625dd141f533c595ede/proceduralParticle.fs", + * uniforms: { + * radius: 0.03 + * } + * }), * lifetime: 300 // Delete after 5 minutes. * }); */ diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index 81f398ab79..1ddc2fa0d6 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -484,7 +484,29 @@ azimuthFinish: Math.PI }, ProceduralParticleEffect: { - // TODO: what should the default procedural particle be? + dimensions: 3, + numParticles: 10000, + numTrianglesPerParticle: 6, + numUpdateProps: 3, + particleUpdateData: JSON.stringify({ + version: 1.0, + fragmentShaderURL: "qrc:///shaders/proceduralParticleSwarmUpdate.frag", + uniforms: { + lifespan: 3.0, + speed: 2.0, + speedSpread: 0.25, + mass: 50000000000 + } + }), + particleRenderData: JSON.stringify({ + version: 3.0, + vertexShaderURL: "qrc:///shaders/proceduralParticleSwarmRender.vert", + fragmentShaderURL: "qrc:///shaders/proceduralParticleSwarmRender.frag", + uniforms: { + radius: 0.03, + lifespan: 3.0 + } + }) }, Light: { color: { red: 255, green: 255, blue: 255 }, From 179f81adcfd2c35796d01c0449c4e8a0fd7ecdd8 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 12 Apr 2024 22:45:40 -0700 Subject: [PATCH 041/109] add unlit property for shapes --- .../src/RenderableShapeEntityItem.cpp | 7 +++++++ .../src/RenderableShapeEntityItem.h | 1 + .../entities/src/EntityItemProperties.cpp | 21 ++++++++++++------- libraries/entities/src/EntityItemProperties.h | 2 +- libraries/entities/src/EntityPropertyFlags.h | 12 +++++------ libraries/entities/src/ShapeEntityItem.cpp | 20 +++++++++++++++++- libraries/entities/src/ShapeEntityItem.h | 4 ++++ libraries/networking/src/udt/PacketHeaders.h | 1 + .../html/js/entityProperties.js | 7 ++++++- 9 files changed, 58 insertions(+), 17 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 82350f54bf..aafca54b31 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -65,6 +65,13 @@ void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint materialChanged = true; } + bool unlit = entity->getUnlit(); + if (_unlit != unlit) { + _unlit = unlit; + _material->setUnlit(unlit); + materialChanged = true; + } + auto userData = entity->getUserData(); if (_proceduralData != userData) { _proceduralData = userData; diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 686014f4de..5e6f2dc243 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -42,6 +42,7 @@ private: std::shared_ptr _material { std::make_shared() }; glm::vec3 _color { NAN }; float _alpha { NAN }; + bool _unlit { false }; }; } } diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 60559e54c7..aed398ec21 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -535,6 +535,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); CHECK_PROPERTY_CHANGE(PROP_COLOR, color); CHECK_PROPERTY_CHANGE(PROP_ALPHA, alpha); + CHECK_PROPERTY_CHANGE(PROP_UNLIT, unlit); changedProperties += _pulse.getChangedProperties(); CHECK_PROPERTY_CHANGE(PROP_TEXTURES, textures); @@ -610,7 +611,6 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_RIGHT_MARGIN, rightMargin); CHECK_PROPERTY_CHANGE(PROP_TOP_MARGIN, topMargin); CHECK_PROPERTY_CHANGE(PROP_BOTTOM_MARGIN, bottomMargin); - CHECK_PROPERTY_CHANGE(PROP_UNLIT, unlit); CHECK_PROPERTY_CHANGE(PROP_FONT, font); CHECK_PROPERTY_CHANGE(PROP_TEXT_EFFECT, textEffect); CHECK_PROPERTY_CHANGE(PROP_TEXT_EFFECT_COLOR, textEffectColor); @@ -1385,6 +1385,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. * @property {Color} color=255,255,255 - The color of the entity. * @property {number} alpha=1 - The opacity of the entity, range 0.01.0. + * @property {boolean} unlit=false - true if the entity is unaffected by lighting, false if it is lit + * by the key light and local lights. * @property {Entities.Pulse} pulse - Color and alpha pulse. *

Deprecated: This property is deprecated and will be removed.

* @example Create a cylinder. @@ -1859,6 +1861,7 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s if (_type == EntityTypes::Box || _type == EntityTypes::Sphere || _type == EntityTypes::Shape) { COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_UNLIT, unlit); _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHAPE, shape); } @@ -2204,6 +2207,7 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h COPY_PROPERTY_FROM_QSCRIPTVALUE(compoundShapeURL, QString, setCompoundShapeURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(color, u8vec3Color, setColor); COPY_PROPERTY_FROM_QSCRIPTVALUE(alpha, float, setAlpha); + COPY_PROPERTY_FROM_QSCRIPTVALUE(unlit, bool, setUnlit); _pulse.copyFromScriptValue(object, namesSet, _defaultSettings); COPY_PROPERTY_FROM_QSCRIPTVALUE(textures, QString, setTextures); @@ -2279,7 +2283,6 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h COPY_PROPERTY_FROM_QSCRIPTVALUE(rightMargin, float, setRightMargin); COPY_PROPERTY_FROM_QSCRIPTVALUE(topMargin, float, setTopMargin); COPY_PROPERTY_FROM_QSCRIPTVALUE(bottomMargin, float, setBottomMargin); - COPY_PROPERTY_FROM_QSCRIPTVALUE(unlit, bool, setUnlit); COPY_PROPERTY_FROM_QSCRIPTVALUE(font, QString, setFont); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(textEffect, TextEffect); COPY_PROPERTY_FROM_QSCRIPTVALUE(textEffectColor, u8vec3Color, setTextEffectColor); @@ -2498,6 +2501,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(compoundShapeURL); COPY_PROPERTY_IF_CHANGED(color); COPY_PROPERTY_IF_CHANGED(alpha); + COPY_PROPERTY_IF_CHANGED(unlit); _pulse.merge(other._pulse); COPY_PROPERTY_IF_CHANGED(textures); @@ -2573,7 +2577,6 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(rightMargin); COPY_PROPERTY_IF_CHANGED(topMargin); COPY_PROPERTY_IF_CHANGED(bottomMargin); - COPY_PROPERTY_IF_CHANGED(unlit); COPY_PROPERTY_IF_CHANGED(font); COPY_PROPERTY_IF_CHANGED(textEffect); COPY_PROPERTY_IF_CHANGED(textEffectColor); @@ -2829,6 +2832,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString); ADD_PROPERTY_TO_MAP(PROP_COLOR, Color, color, u8vec3Color); ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA, Alpha, alpha, float, particle::MINIMUM_ALPHA, particle::MAXIMUM_ALPHA); + ADD_PROPERTY_TO_MAP(PROP_UNLIT, Unlit, unlit, bool); { // Pulse ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_MIN, Pulse, pulse, Min, min); ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_MAX, Pulse, pulse, Max, max); @@ -2949,7 +2953,6 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_RIGHT_MARGIN, RightMargin, rightMargin, float); ADD_PROPERTY_TO_MAP(PROP_TOP_MARGIN, TopMargin, topMargin, float); ADD_PROPERTY_TO_MAP(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float); - ADD_PROPERTY_TO_MAP(PROP_UNLIT, Unlit, unlit, bool); ADD_PROPERTY_TO_MAP(PROP_FONT, Font, font, QString); ADD_PROPERTY_TO_MAP(PROP_TEXT_EFFECT, TextEffect, textEffect, TextEffect); ADD_PROPERTY_TO_MAP(PROP_TEXT_EFFECT_COLOR, TextEffectColor, textEffectColor, u8vec3Color); @@ -3501,6 +3504,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy properties.getType() == EntityTypes::Sphere) { APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha()); + APPEND_ENTITY_PROPERTY(PROP_UNLIT, properties.getUnlit()); _staticPulse.setProperties(properties); _staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -3978,6 +3982,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int properties.getType() == EntityTypes::Sphere) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_UNLIT, bool, setUnlit); properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE, QString, setShape); @@ -4199,6 +4204,7 @@ void EntityItemProperties::markAllChanged() { _compoundShapeURLChanged = true; _colorChanged = true; _alphaChanged = true; + _unlitChanged = true; _pulse.markAllChanged(); _texturesChanged = true; @@ -4274,7 +4280,6 @@ void EntityItemProperties::markAllChanged() { _rightMarginChanged = true; _topMarginChanged = true; _bottomMarginChanged = true; - _unlitChanged = true; _fontChanged = true; _textEffectChanged = true; _textEffectColorChanged = true; @@ -4673,6 +4678,9 @@ QList EntityItemProperties::listChangedProperties() { if (alphaChanged()) { out += "alpha"; } + if (unlitChanged()) { + out += "unlit"; + } getPulse().listChangedProperties(out); if (texturesChanged()) { out += "textures"; @@ -4874,9 +4882,6 @@ QList EntityItemProperties::listChangedProperties() { if (bottomMarginChanged()) { out += "bottomMargin"; } - if (unlitChanged()) { - out += "unlit"; - } if (fontChanged()) { out += "font"; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 78e69f98b7..196e2ef46f 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -254,6 +254,7 @@ public: DEFINE_PROPERTY_REF(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString, ""); DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, u8vec3Color, ENTITY_ITEM_DEFAULT_COLOR); DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float, ENTITY_ITEM_DEFAULT_ALPHA); + DEFINE_PROPERTY_REF(PROP_UNLIT, Unlit, unlit, bool, false); DEFINE_PROPERTY_GROUP(Pulse, pulse, PulsePropertyGroup); DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString, ""); @@ -329,7 +330,6 @@ public: DEFINE_PROPERTY_REF(PROP_RIGHT_MARGIN, RightMargin, rightMargin, float, TextEntityItem::DEFAULT_MARGIN); DEFINE_PROPERTY_REF(PROP_TOP_MARGIN, TopMargin, topMargin, float, TextEntityItem::DEFAULT_MARGIN); DEFINE_PROPERTY_REF(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float, TextEntityItem::DEFAULT_MARGIN); - DEFINE_PROPERTY_REF(PROP_UNLIT, Unlit, unlit, bool, false); DEFINE_PROPERTY_REF(PROP_FONT, Font, font, QString, ROBOTO_FONT_FAMILY); DEFINE_PROPERTY_REF_ENUM(PROP_TEXT_EFFECT, TextEffect, textEffect, TextEffect, TextEffect::NO_EFFECT); DEFINE_PROPERTY_REF(PROP_TEXT_EFFECT_COLOR, TextEffectColor, textEffectColor, u8vec3Color, TextEntityItem::DEFAULT_TEXT_COLOR); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index ae8928b68e..8d4fd515f4 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -121,6 +121,7 @@ enum EntityPropertyList { PROP_COMPOUND_SHAPE_URL, PROP_COLOR, PROP_ALPHA, + PROP_UNLIT, PROP_PULSE_MIN, PROP_PULSE_MAX, PROP_PULSE_PERIOD, @@ -266,12 +267,11 @@ enum EntityPropertyList { PROP_RIGHT_MARGIN = PROP_DERIVED_7, PROP_TOP_MARGIN = PROP_DERIVED_8, PROP_BOTTOM_MARGIN = PROP_DERIVED_9, - PROP_UNLIT = PROP_DERIVED_10, - PROP_FONT = PROP_DERIVED_11, - PROP_TEXT_EFFECT = PROP_DERIVED_12, - PROP_TEXT_EFFECT_COLOR = PROP_DERIVED_13, - PROP_TEXT_EFFECT_THICKNESS = PROP_DERIVED_14, - PROP_TEXT_ALIGNMENT = PROP_DERIVED_15, + PROP_FONT = PROP_DERIVED_10, + PROP_TEXT_EFFECT = PROP_DERIVED_11, + PROP_TEXT_EFFECT_COLOR = PROP_DERIVED_12, + PROP_TEXT_EFFECT_THICKNESS = PROP_DERIVED_13, + PROP_TEXT_ALIGNMENT = PROP_DERIVED_14, // Zone // Keylight diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 825660bd72..054d7f96be 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -119,6 +119,7 @@ EntityItemProperties ShapeEntityItem::getProperties(const EntityPropertyFlags& d COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(unlit, getUnlit); withReadLock([&] { _pulseProperties.getProperties(properties); }); @@ -170,6 +171,7 @@ bool ShapeEntityItem::setSubClassProperties(const EntityItemProperties& properti SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(unlit, setUnlit); withWriteLock([&] { bool pulsePropertiesChanged = _pulseProperties.setProperties(properties); somethingChanged |= pulsePropertiesChanged; @@ -190,6 +192,7 @@ int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); + READ_ENTITY_PROPERTY(PROP_UNLIT, bool, setUnlit); withWriteLock([&] { int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, @@ -206,6 +209,7 @@ EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams& EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); requestedProperties += PROP_COLOR; requestedProperties += PROP_ALPHA; + requestedProperties += PROP_UNLIT; requestedProperties += _pulseProperties.getEntityProperties(params); requestedProperties += PROP_SHAPE; return requestedProperties; @@ -222,6 +226,7 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); + APPEND_ENTITY_PROPERTY(PROP_UNLIT, getUnlit()); withReadLock([&] { _pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -255,6 +260,19 @@ float ShapeEntityItem::getAlpha() const { }); } +void ShapeEntityItem::setUnlit(bool unlit) { + withWriteLock([&] { + _needsRenderUpdate |= _unlit != unlit; + _unlit = unlit; + }); +} + +bool ShapeEntityItem::getUnlit() const { + return resultWithReadLock([&] { + return _unlit; + }); +} + void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) { const float MAX_FLAT_DIMENSION = 0.0001f; const auto shape = getShape(); @@ -468,4 +486,4 @@ void ShapeEntityItem::setUserData(const QString& value) { _needsRenderUpdate |= _userData != value; _userData = value; }); -} \ No newline at end of file +} diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index d5b934153a..1559949c8c 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -82,6 +82,9 @@ public: glm::u8vec3 getColor() const; void setColor(const glm::u8vec3& value); + bool getUnlit() const; + void setUnlit(bool unlit); + void setUnscaledDimensions(const glm::vec3& value) override; bool supportsDetailedIntersection() const override; @@ -107,6 +110,7 @@ public: protected: glm::u8vec3 _color; float _alpha { 1.0f }; + bool _unlit { false }; PulsePropertyGroup _pulseProperties; entity::Shape _shape { entity::Shape::Sphere }; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index b4fb5a172d..f35bce1388 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -296,6 +296,7 @@ enum class EntityVersion : PacketVersion { AudioZones, AnimationSmoothFrames, ProceduralParticles, + ShapeUnlit, // Add new versions above here NUM_PACKET_TYPE, diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index ff55abbde9..2c72b8ea6d 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -177,7 +177,12 @@ const GROUPS = [ decimals: 2, propertyID: "shapeAlpha", propertyName: "alpha", - }, + }, + { + label: "Unlit", + type: "bool", + propertyID: "unlit", + } ] }, { From ee705d285e3b5e0d65accffdf8bfbfab9c6448f1 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Tue, 18 Jun 2024 21:08:21 -0700 Subject: [PATCH 042/109] Merge branch master into protocol_changes --- .github/workflows/linux_server_build.yml | 111 +- .github/workflows/master_build.yml | 6 +- .github/workflows/master_deploy_apidocs.yml | 4 +- .github/workflows/master_deploy_doxygen.yml | 4 +- .github/workflows/pr_build.yml | 17 +- BUILD_ANDROID.md | 2 + CMakeLists.txt | 16 +- .../hifiinterface/PermissionChecker.java | 2 +- .../src/AgentScriptingInterface.h | 8 +- .../src/audio/AudioMixerClientData.cpp | 18 +- .../src/avatars/ScriptableAvatar.cpp | 168 +- .../src/avatars/ScriptableAvatar.h | 15 +- .../src/scripts/EntityScriptServer.cpp | 50 +- .../src/scripts/EntityScriptServer.h | 27 + cmake/compiler.cmake | 4 - cmake/externals/LibOVR/CMakeLists.txt | 10 +- cmake/externals/LibOVR/LibOVRCMakeLists.txt | 4 +- cmake/externals/LibOVRPlatform/CMakeLists.txt | 2 + cmake/externals/crashpad/CMakeLists.txt | 7 + cmake/externals/steamworks/CMakeLists.txt | 70 +- cmake/macros/ConfigureCCache.cmake | 45 - cmake/macros/TargetOpenEXR.cmake | 31 +- .../artery-font-format/disable-checksum.patch | 38 + cmake/ports/artery-font-format/portfile.cmake | 15 + cmake/ports/artery-font-format/vcpkg.json | 7 + cmake/ports/cgltf/portfile.cmake | 15 + cmake/ports/cgltf/vcpkg.json | 7 + cmake/ports/hifi-deps/CONTROL | 2 +- cmake/ports/imath/portfile.cmake | 25 + cmake/ports/imath/vcpkg.json | 18 + cmake/ports/node/portfile.cmake | 8 +- cmake/ports/openexr/CONTROL | 4 - cmake/ports/openexr/FindOpenEXR.cmake | 87 - .../openexr/fix-arm64-windows-build.patch | 13 + cmake/ports/openexr/fix_install_ilmimf.patch | 19 - cmake/ports/openexr/portfile.cmake | 101 +- cmake/ports/openexr/usage | 4 + cmake/ports/openexr/vcpkg.json | 25 + cmake/templates/launch-c.in | 12 - cmake/templates/launch-cxx.in | 12 - .../resources/describe-settings.json | 68 +- .../resources/web/settings/js/settings.js | 10 +- domain-server/src/DomainGatekeeper.cpp | 2 + domain-server/src/DomainServer.cpp | 25 +- .../src/DomainServerSettingsManager.cpp | 23 + hifi_qt.py | 23 +- hifi_vcpkg.py | 35 +- .../dialogs/graphics/GraphicsSettings.qml | 186 +- .../hifi/dialogs/security/ScriptSecurity.qml | 152 ++ .../hifi/simplifiedUI/helpApp/faq/HelpFAQ.qml | 3 +- .../helpApp/support/HelpSupport.qml | 3 +- .../hifi/simplifiedUI/settingsApp/dev/Dev.qml | 5 +- .../settingsApp/general/General.qml | 3 +- .../simplifiedControls/Button.qml | 4 +- .../qml/hifi/tablet/ControllerSettings.qml | 2 +- .../Scripts/activator-doppleganger.js | 2 +- interface/src/Application.cpp | 281 +-- interface/src/Application.h | 58 +- interface/src/AvatarBookmarks.cpp | 42 + interface/src/AvatarBookmarks.h | 7 +- interface/src/CrashRecoveryHandler.cpp | 7 +- interface/src/LODManager.cpp | 24 +- interface/src/Menu.cpp | 41 +- interface/src/Menu.h | 5 + interface/src/RefreshRateManager.cpp | 38 +- interface/src/RefreshRateManager.h | 16 +- interface/src/avatar/AvatarDoctor.cpp | 8 + interface/src/avatar/AvatarMotionState.cpp | 4 + interface/src/avatar/AvatarProject.cpp | 8 + interface/src/avatar/DetailedMotionState.cpp | 4 + interface/src/avatar/MyAvatar.cpp | 67 +- interface/src/avatar/MyAvatar.h | 8 +- interface/src/graphics/GraphicsEngine.cpp | 2 +- interface/src/main.cpp | 138 +- interface/src/raypick/LaserPointer.cpp | 4 +- interface/src/raypick/ParabolaPick.cpp | 2 +- interface/src/raypick/PathPointer.cpp | 4 +- interface/src/raypick/RayPick.cpp | 4 +- .../scripting/ControllerScriptingInterface.h | 8 +- .../PerformanceScriptingInterface.cpp | 12 +- .../scripting/PerformanceScriptingInterface.h | 16 + .../scripting/SettingsScriptingInterface.cpp | 8 +- .../scripting/WindowScriptingInterface.cpp | 14 +- interface/src/ui/ApplicationOverlay.h | 11 +- interface/src/ui/Keyboard.cpp | 10 +- interface/src/ui/PreferencesDialog.cpp | 3 +- interface/src/ui/overlays/Overlays.cpp | 10 +- interface/src/ui/overlays/Overlays.h | 3 - launchers/qt/CMakeLists.txt | 2 +- libraries/audio-client/src/AudioClient.cpp | 2 + libraries/avatars/src/AvatarData.cpp | 20 +- libraries/avatars/src/AvatarData.h | 4 +- libraries/avatars/src/ScriptAvatarData.cpp | 8 +- libraries/baking/src/MaterialBaker.cpp | 48 +- libraries/baking/src/MaterialBaker.h | 3 +- .../src/EntityTreeRenderer.cpp | 39 +- .../src/RenderableEntityItem.cpp | 49 +- .../src/RenderableEntityItem.h | 5 + .../src/RenderableImageEntityItem.cpp | 3 +- .../src/RenderableMaterialEntityItem.cpp | 47 +- .../src/RenderableMaterialEntityItem.h | 3 + .../RenderableParticleEffectEntityItem.cpp | 78 +- .../src/RenderableParticleEffectEntityItem.h | 1 + .../src/RenderablePolyVoxEntityItem.cpp | 49 +- .../src/RenderablePolyVoxEntityItem.h | 1 - .../src/RenderableShapeEntityItem.cpp | 21 +- .../src/RenderableShapeEntityItem.h | 2 + .../src/RenderableTextEntityItem.cpp | 3 +- .../entities-renderer/textured_particle.slp | 1 + .../src/textured_particle.slf | 30 +- .../src/textured_particle.slv | 6 +- .../src/AmbientLightPropertyGroup.cpp | 8 +- .../entities/src/AmbientLightPropertyGroup.h | 3 +- .../entities/src/AnimationPropertyGroup.cpp | 7 +- .../entities/src/AnimationPropertyGroup.h | 3 +- libraries/entities/src/BloomPropertyGroup.cpp | 4 +- libraries/entities/src/BloomPropertyGroup.h | 3 +- .../entities/src/EntityItemProperties.cpp | 83 +- libraries/entities/src/EntityItemProperties.h | 6 +- .../src/EntityItemPropertiesDefaults.h | 6 +- .../entities/src/EntityItemPropertiesMacros.h | 51 +- .../src/EntityScriptServerLogClient.cpp | 67 +- .../src/EntityScriptServerLogClient.h | 9 + .../entities/src/EntityScriptingInterface.cpp | 18 +- .../entities/src/EntityScriptingInterface.h | 38 +- libraries/entities/src/EntityTree.cpp | 27 +- libraries/entities/src/EntityTree.h | 4 +- libraries/entities/src/GrabPropertyGroup.cpp | 6 +- libraries/entities/src/GrabPropertyGroup.h | 3 +- libraries/entities/src/HazePropertyGroup.cpp | 4 +- libraries/entities/src/HazePropertyGroup.h | 3 +- .../entities/src/KeyLightPropertyGroup.cpp | 3 +- .../entities/src/KeyLightPropertyGroup.h | 3 +- libraries/entities/src/PropertyGroup.h | 3 +- libraries/entities/src/PulsePropertyGroup.cpp | 5 +- libraries/entities/src/PulsePropertyGroup.h | 3 +- .../entities/src/RingGizmoPropertyGroup.cpp | 5 +- .../entities/src/RingGizmoPropertyGroup.h | 3 +- .../entities/src/SkyboxPropertyGroup.cpp | 6 +- libraries/entities/src/SkyboxPropertyGroup.h | 3 +- .../entities/src/ZoneAudioPropertyGroup.cpp | 3 +- .../entities/src/ZoneAudioPropertyGroup.h | 3 +- .../gpu-gl-common/src/gpu/gl/GLBackend.cpp | 18 - .../gpu-gl-common/src/gpu/gl/GLBackend.h | 6 - .../src/gpu/gl/GLBackendInput.cpp | 16 - .../gpu-gl/src/gpu/gl41/GL41BackendInput.cpp | 13 - libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp | 24 +- .../gpu-gl/src/gpu/gl45/GL45BackendInput.cpp | 13 - libraries/gpu/src/gpu/Batch.cpp | 9 - libraries/gpu/src/gpu/Batch.h | 36 +- libraries/gpu/src/gpu/Buffer.h | 18 +- libraries/gpu/src/gpu/FrameIOKeys.h | 2 - libraries/gpu/src/gpu/State.h | 2 +- libraries/graphics-scripting/CMakeLists.txt | 4 + .../src/graphics-scripting/Forward.h | 74 +- .../GraphicsScriptingInterface.cpp | 370 ++-- .../graphics-scripting/ScriptableModel.cpp | 167 +- libraries/graphics/src/graphics/Geometry.cpp | 18 +- libraries/graphics/src/graphics/Geometry.h | 6 +- libraries/graphics/src/graphics/Material.cpp | 44 +- libraries/graphics/src/graphics/Material.h | 128 +- libraries/graphics/src/graphics/Material.slh | 126 +- .../src/graphics/MaterialTextures.slh | 366 +++- .../graphics/src/graphics/ShaderConstants.h | 16 +- libraries/hfm/src/hfm/HFM.cpp | 31 + libraries/hfm/src/hfm/HFM.h | 8 + libraries/image/src/image/OpenEXRReader.cpp | 7 +- .../src/model-networking/ModelCache.cpp | 32 +- libraries/model-serializers/CMakeLists.txt | 2 +- .../model-serializers/src/GLTFSerializer.cpp | 1710 ++++++----------- .../model-serializers/src/GLTFSerializer.h | 825 +------- libraries/networking/src/FingerprintUtils.cpp | 2 +- libraries/networking/src/LimitedNodeList.cpp | 24 +- libraries/networking/src/LimitedNodeList.h | 5 +- .../networking/src/NetworkingConstants.h | 2 + libraries/networking/src/Node.h | 1 + libraries/networking/src/NodePermissions.cpp | 5 + libraries/networking/src/NodePermissions.h | 3 +- libraries/networking/src/SockAddr.cpp | 9 +- libraries/networking/src/SocketType.h | 4 + .../networking/src/udt/NetworkSocket.cpp | 22 +- libraries/physics/src/CharacterController.cpp | 2 +- libraries/physics/src/CharacterController.h | 2 +- libraries/physics/src/EntityMotionState.cpp | 7 +- libraries/physics/src/ObjectMotionState.cpp | 4 - .../plugins/src/plugins/PluginManager.cpp | 32 +- libraries/plugins/src/plugins/PluginManager.h | 220 ++- .../procedural/ProceduralMaterialCache.cpp | 784 ++++++-- .../src/procedural/ProceduralMaterialCache.h | 132 +- .../src/procedural/ReferenceMaterial.cpp | 119 +- .../src/procedural/ReferenceMaterial.h | 19 + .../recording/RecordingScriptingInterface.cpp | 69 +- .../recording/RecordingScriptingInterface.h | 2 + .../res/fonts/CourierPrime-OFL.txt | 93 + .../res/fonts/CourierPrime.arfont | Bin 0 -> 54316 bytes .../res/fonts/CourierPrime.license | 51 - .../render-utils/res/fonts/CourierPrime.sdff | Bin 380376 -> 0 bytes .../res/fonts/Inconsolata-OFL.txt | 93 + .../res/fonts/InconsolataMedium.arfont | Bin 0 -> 55452 bytes .../res/fonts/InconsolataMedium.sdff | Bin 129710 -> 0 bytes .../render-utils/res/fonts/Roboto-LICENSE.txt | 202 ++ .../render-utils/res/fonts/Roboto.arfont | Bin 0 -> 59804 bytes libraries/render-utils/res/fonts/Roboto.sdff | Bin 435353 -> 0 bytes .../render-utils/res/fonts/Timeless.arfont | Bin 0 -> 110692 bytes .../render-utils/res/fonts/Timeless.sdff | Bin 120350 -> 0 bytes libraries/render-utils/res/fonts/fonts.qrc | 8 +- libraries/render-utils/src/GeometryCache.cpp | 948 +++------ libraries/render-utils/src/GeometryCache.h | 90 +- libraries/render-utils/src/GlobalLight.slh | 74 + .../render-utils/src/HighlightEffect.cpp | 138 +- libraries/render-utils/src/HighlightEffect.h | 35 +- .../render-utils/src/MeshPartPayload.cpp | 38 +- libraries/render-utils/src/MeshPartPayload.h | 5 +- libraries/render-utils/src/Model.cpp | 23 +- .../render-utils/src/RenderCommonTask.cpp | 11 + .../render-utils/src/RenderPipelines.cpp | 1349 ++++++++----- .../render-utils/src/RenderShadowTask.cpp | 62 +- libraries/render-utils/src/model.slf | 165 +- libraries/render-utils/src/model.slv | 7 +- .../render-utils/src/render-utils/model.slp | 2 +- libraries/render-utils/src/sdf_text3D.slf | 3 +- libraries/render-utils/src/sdf_text3D.slh | 63 +- libraries/render-utils/src/sdf_text3D.slv | 2 + libraries/render-utils/src/text/Font.cpp | 337 ++-- libraries/render-utils/src/text/Font.h | 24 +- libraries/render-utils/src/text/Glyph.cpp | 12 - libraries/render-utils/src/text/Glyph.h | 3 - libraries/render/src/render/FilterTask.cpp | 4 +- libraries/render/src/render/HighlightStyle.h | 54 +- libraries/render/src/render/Item.cpp | 8 + libraries/render/src/render/Item.h | 39 +- .../src/render/RenderFetchCullSortTask.cpp | 9 +- .../src/render/RenderFetchCullSortTask.h | 2 + libraries/render/src/render/Scene.cpp | 17 + libraries/render/src/render/Scene.h | 11 + libraries/render/src/render/Selection.h | 4 +- libraries/render/src/render/ShapePipeline.cpp | 4 +- libraries/render/src/render/ShapePipeline.h | 13 +- .../src/AssetScriptingInterface.cpp | 13 +- .../src/ConsoleScriptingInterface.cpp | 34 +- .../script-engine/src/HelperScriptEngine.cpp | 31 + .../script-engine/src/HelperScriptEngine.h | 65 + libraries/script-engine/src/Mat4.cpp | 2 +- libraries/script-engine/src/Quat.cpp | 2 +- libraries/script-engine/src/ScriptContext.h | 7 + libraries/script-engine/src/ScriptEngines.cpp | 61 + libraries/script-engine/src/ScriptEngines.h | 72 +- libraries/script-engine/src/ScriptManager.cpp | 157 +- libraries/script-engine/src/ScriptManager.h | 71 +- .../src/ScriptManagerScriptingInterface.cpp | 45 + .../src/ScriptManagerScriptingInterface.h | 44 +- libraries/script-engine/src/ScriptMessage.cpp | 48 + libraries/script-engine/src/ScriptMessage.h | 64 + .../script-engine/src/ScriptPermissions.cpp | 124 ++ .../script-engine/src/ScriptPermissions.h | 31 + libraries/script-engine/src/ScriptUUID.cpp | 2 +- .../script-engine/src/ScriptValueUtils.cpp | 1 + libraries/script-engine/src/Vec3.cpp | 2 +- .../src/v8/FastScriptValueUtils.cpp | 49 +- .../src/v8/FastScriptValueUtils.h | 4 + .../src/v8/ScriptContextV8Wrapper.cpp | 31 + .../src/v8/ScriptContextV8Wrapper.h | 7 + .../script-engine/src/v8/ScriptEngineV8.cpp | 133 +- .../script-engine/src/v8/ScriptEngineV8.h | 2 + .../src/v8/ScriptEngineV8_cast.cpp | 4 +- .../src/v8/ScriptObjectV8Proxy.cpp | 181 +- .../src/v8/ScriptObjectV8Proxy.h | 9 +- .../src/v8/ScriptValueV8Wrapper.cpp | 88 +- libraries/shared/src/BlendshapeConstants.h | 6 +- .../shared/src/PortableHighResolutionClock.h | 2 + libraries/shared/src/SettingHandle.cpp | 2 + libraries/shared/src/SettingHandle.h | 9 + libraries/shared/src/SimpleMovingAverage.h | 4 +- libraries/shared/src/shared/WebRTC.h | 2 + libraries/ui/CMakeLists.txt | 4 + libraries/ui/src/QmlWindowClass.cpp | 1 + libraries/ui/src/ui/OffscreenQmlSurface.cpp | 2 + pkg-scripts/make-rpm-server | 6 +- pkg-scripts/overte-server.spec | 6 +- plugins/CMakeLists.txt | 23 +- plugins/JSAPIExample/src/JSAPIExample.cpp | 2 +- plugins/oculus/CMakeLists.txt | 2 +- plugins/steamClient/src/SteamAPIPlugin.cpp | 5 +- prebuild.py | 49 +- scripts/communityScripts/chat/FloofChat.html | 2 +- scripts/communityScripts/chat/FloofChat.js | 12 +- scripts/defaultScripts.js | 2 +- .../resources/modules/pickRayController.js | 21 +- scripts/simplifiedUI/ui/simplifiedUI.js | 3 +- scripts/system/away.js | 30 +- .../controllers/controllerDispatcher.js | 46 +- .../controllerModules/equipEntity.js | 3 +- .../controllerModules/farActionGrabEntity.js | 12 +- .../controllerModules/farGrabEntity.js | 12 +- .../controllerModules/hudOverlayPointer.js | 5 +- .../controllerModules/inEditMode.js | 5 +- .../controllerModules/nearGrabEntity.js | 2 +- .../nearParentGrabOverlay.js | 2 +- .../controllers/controllerModules/teleport.js | 14 +- .../controllerModules/trackedHandTablet.js | 10 +- .../controllerModules/trackedHandWalk.js | 14 +- .../controllerModules/webSurfaceLaserInput.js | 6 +- scripts/system/controllers/grab.js | 4 +- scripts/system/controllers/handTouch.js | 17 +- scripts/system/controllers/squeezeHands.js | 18 +- ...oggleAdvancedMovementForHandControllers.js | 6 +- .../touchControllerConfiguration.js | 4 +- .../viveControllerConfiguration.js | 6 +- .../create/assets/data/createAppTooltips.json | 2 +- scripts/system/create/edit.js | 121 +- scripts/system/create/editModes/editVoxels.js | 33 +- .../system/create/entityList/entityList.js | 6 +- .../create/entityList/html/entityList.html | 13 + .../create/entityList/html/js/entityList.js | 21 +- .../html/js/entityListContextMenu.js | 6 +- .../html/js/entityProperties.js | 71 +- .../entitySelectionTool.js | 50 +- .../html/css/importEntities.css | 160 ++ .../importEntities/html/importEntities.html | 77 + .../html/js/importEntitiesUi.js | 217 +++ .../modules/renderWithZonesManager.html | 410 ++++ .../create/modules/renderWithZonesManager.js | 453 +++++ scripts/system/create/qml/EditTabView.qml | 19 + .../system/create/qml/EditToolsTabView.qml | 21 +- scripts/system/emote.js | 33 +- scripts/system/fingerPaint.js | 12 +- scripts/system/html/css/edit-style.css | 65 +- scripts/system/html/gridControls.html | 55 +- scripts/system/libraries/Trigger.js | 5 +- scripts/system/libraries/WebTablet.js | 3 +- .../libraries/controllerDispatcherUtils.js | 6 +- scripts/system/libraries/controllers.js | 6 +- scripts/system/makeUserConnection.js | 20 +- scripts/system/miniTablet.js | 12 +- scripts/system/mod.js | 6 +- scripts/system/notifications.js | 21 +- scripts/system/pal.js | 9 +- scripts/system/tablet-ui/tabletUI.js | 18 +- tests/model-serializers/CMakeLists.txt | 2 +- .../src/ModelSerializersTests.cpp | 6 +- tests/networking/src/PacketTests.cpp | 4 + tests/networking/src/QtNetworkTests.cpp | 167 ++ tests/networking/src/QtNetworkTests.h | 31 + tests/networking/src/ResourceTests.cpp | 3 + .../src/ScriptEngineBenchmarkTests.cpp | 200 ++ .../src/ScriptEngineBenchmarkTests.h | 36 + .../src/ScriptEngineNetworkedTests.cpp | 34 +- .../src/ScriptEngineNetworkedTests.h | 1 + tests/script-engine/src/tests/b_require.js | 6 + tests/script-engine/src/tests/c_require.js | 6 + tests/shared/src/BitVectorHelperTests.cpp | 4 +- tests/shared/src/FileCacheTests.cpp | 5 + tests/shared/src/MovingPercentileTests.cpp | 54 +- tools/ci-scripts/build_server_package.bash | 87 + ...tu-18.04 => Dockerfile_build_ubuntu-24.04} | 20 +- ...d_fedora-38 => Dockerfile_build_fedora-39} | 8 +- ...d_fedora-37 => Dockerfile_build_fedora-40} | 8 +- tools/jsdoc/plugins/hifi.js | 1 + .../AppDataHighFidelity/Interface.json | 4 +- tools/nitpick/src/TestRunnerDesktop.cpp | 17 +- tools/nitpick/src/TestRunnerDesktop.h | 5 +- .../Editor/AvatarExporter/AvatarExporter.cs | 291 +-- .../Assets/Editor/AvatarExporter/FST.cs | 402 ++++ .../Assets/Editor/AvatarExporter/FST.cs.meta | 11 + tools/unity-avatar-exporter/Assets/README.txt | 2 +- .../avatarExporter.unitypackage | Bin 37559 -> 39808 bytes .../assets/cards/Leather0100-bump.jpg | Bin 1832250 -> 0 bytes .../assets/cards/Leather0100_2_L.jpg | Bin 1456875 -> 0 bytes .../assets/cards/texture_accreditation.txt | 1 - winprepareVS22 | 5 + 370 files changed, 11873 insertions(+), 6091 deletions(-) delete mode 100644 cmake/macros/ConfigureCCache.cmake create mode 100644 cmake/ports/artery-font-format/disable-checksum.patch create mode 100644 cmake/ports/artery-font-format/portfile.cmake create mode 100644 cmake/ports/artery-font-format/vcpkg.json create mode 100644 cmake/ports/cgltf/portfile.cmake create mode 100644 cmake/ports/cgltf/vcpkg.json create mode 100644 cmake/ports/imath/portfile.cmake create mode 100644 cmake/ports/imath/vcpkg.json mode change 100755 => 100644 cmake/ports/node/portfile.cmake delete mode 100644 cmake/ports/openexr/CONTROL delete mode 100644 cmake/ports/openexr/FindOpenEXR.cmake create mode 100644 cmake/ports/openexr/fix-arm64-windows-build.patch delete mode 100644 cmake/ports/openexr/fix_install_ilmimf.patch create mode 100644 cmake/ports/openexr/usage create mode 100644 cmake/ports/openexr/vcpkg.json delete mode 100644 cmake/templates/launch-c.in delete mode 100644 cmake/templates/launch-cxx.in create mode 100644 interface/resources/qml/hifi/dialogs/security/ScriptSecurity.qml create mode 100644 libraries/render-utils/res/fonts/CourierPrime-OFL.txt create mode 100644 libraries/render-utils/res/fonts/CourierPrime.arfont delete mode 100644 libraries/render-utils/res/fonts/CourierPrime.license delete mode 100644 libraries/render-utils/res/fonts/CourierPrime.sdff create mode 100644 libraries/render-utils/res/fonts/Inconsolata-OFL.txt create mode 100644 libraries/render-utils/res/fonts/InconsolataMedium.arfont delete mode 100644 libraries/render-utils/res/fonts/InconsolataMedium.sdff create mode 100644 libraries/render-utils/res/fonts/Roboto-LICENSE.txt create mode 100644 libraries/render-utils/res/fonts/Roboto.arfont delete mode 100644 libraries/render-utils/res/fonts/Roboto.sdff create mode 100644 libraries/render-utils/res/fonts/Timeless.arfont delete mode 100644 libraries/render-utils/res/fonts/Timeless.sdff create mode 100644 libraries/script-engine/src/HelperScriptEngine.cpp create mode 100644 libraries/script-engine/src/HelperScriptEngine.h create mode 100644 libraries/script-engine/src/ScriptMessage.cpp create mode 100644 libraries/script-engine/src/ScriptMessage.h create mode 100644 libraries/script-engine/src/ScriptPermissions.cpp create mode 100644 libraries/script-engine/src/ScriptPermissions.h create mode 100644 scripts/system/create/importEntities/html/css/importEntities.css create mode 100644 scripts/system/create/importEntities/html/importEntities.html create mode 100644 scripts/system/create/importEntities/html/js/importEntitiesUi.js create mode 100644 scripts/system/create/modules/renderWithZonesManager.html create mode 100644 scripts/system/create/modules/renderWithZonesManager.js create mode 100644 tests/networking/src/QtNetworkTests.cpp create mode 100644 tests/networking/src/QtNetworkTests.h create mode 100644 tests/script-engine/src/ScriptEngineBenchmarkTests.cpp create mode 100644 tests/script-engine/src/ScriptEngineBenchmarkTests.h create mode 100644 tests/script-engine/src/tests/b_require.js create mode 100644 tests/script-engine/src/tests/c_require.js create mode 100755 tools/ci-scripts/build_server_package.bash rename tools/ci-scripts/deb_package/{Dockerfile_build_ubuntu-18.04 => Dockerfile_build_ubuntu-24.04} (63%) rename tools/ci-scripts/rpm_package/{Dockerfile_build_fedora-38 => Dockerfile_build_fedora-39} (65%) rename tools/ci-scripts/rpm_package/{Dockerfile_build_fedora-37 => Dockerfile_build_fedora-40} (65%) create mode 100644 tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/FST.cs create mode 100644 tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/FST.cs.meta delete mode 100644 unpublishedScripts/marketplace/gameTable/assets/cards/Leather0100-bump.jpg delete mode 100644 unpublishedScripts/marketplace/gameTable/assets/cards/Leather0100_2_L.jpg delete mode 100644 unpublishedScripts/marketplace/gameTable/assets/cards/texture_accreditation.txt create mode 100644 winprepareVS22 diff --git a/.github/workflows/linux_server_build.yml b/.github/workflows/linux_server_build.yml index 51bf4ae3bc..8cf2d46169 100644 --- a/.github/workflows/linux_server_build.yml +++ b/.github/workflows/linux_server_build.yml @@ -1,6 +1,6 @@ # Copyright 2013-2019 High Fidelity, Inc. # Copyright 2020-2022 Vircadia contributors. -# Copyright 2021-2023 Overte e.V. +# Copyright 2021-2024 Overte e.V. # SPDX-License-Identifier: Apache-2.0 name: Linux Server CI Build @@ -12,6 +12,10 @@ on: push: branches: - master + tags: + # Release tags. E.g. 2024.06.1 + # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet + - "[0-9][0-9][0-9][0-9].[0-9][0-9].**" env: BUILD_TYPE: Release @@ -19,8 +23,8 @@ env: UPLOAD_BUCKET: overte-public UPLOAD_REGION: fra1 UPLOAD_ENDPOINT: "https://fra1.digitaloceanspaces.com" - CMAKE_BACKTRACE_URL: ${{ secrets.SENTRY_MINIDUMP_ENDPOINT }} - CMAKE_BACKTRACE_TOKEN: server_${{ github.event.number }}_${{ github.sha }} + # Disable VCPKG caching to save time. + VCPKG_FEATURE_FLAGS: -binarycaching jobs: build: @@ -33,62 +37,77 @@ jobs: - os: debian-11 image: docker.io/overte/overte-server-build:0.1.3-debian-11-amd64 arch: amd64 - runner: linux_amd64 + # https://github.com/testflows/TestFlows-GitHub-Hetzner-Runners/wiki/Meta-Labels + # self_hosted makes the Hetzner auto-scaler put up the job. + # type-cx52 is a Hetzner VPS server type. In this case cs52 is a server with 16-cores and 32GB of RAM. + # image-x86-app-docker-ce is a Hetzner image. + # https://github.com/testflows/TestFlows-GitHub-Hetzner-Runners/wiki/Specifying-The-Runner-Image + runner: [self_hosted, type-cx52, image-x86-app-docker-ce] - os: debian-11 image: docker.io/overte/overte-server-build:0.1.3-debian-11-aarch64 arch: aarch64 - runner: linux_aarch64 + runner: [self_hosted, type-cax41, image-arm-app-docker-ce] - os: debian-12 image: docker.io/overte/overte-server-build:0.1.3-debian-12-amd64 arch: amd64 - runner: linux_amd64 + runner: [self_hosted, type-cx52, image-x86-app-docker-ce] - os: debian-12 image: docker.io/overte/overte-server-build:0.1.3-debian-12-aarch64 arch: aarch64 - runner: linux_aarch64 + runner: [self_hosted, type-cax41, image-arm-app-docker-ce] - os: ubuntu-20.04 image: docker.io/overte/overte-server-build:0.1.3-ubuntu-20.04-amd64 arch: amd64 - runner: linux_amd64 + runner: [self_hosted, type-cx52, image-x86-app-docker-ce] - os: ubuntu-22.04 image: docker.io/overte/overte-server-build:0.1.3-ubuntu-22.04-amd64 arch: amd64 - runner: linux_amd64 + runner: [self_hosted, type-cx52, image-x86-app-docker-ce] - os: ubuntu-22.04 image: docker.io/overte/overte-server-build:0.1.3-ubuntu-22.04-aarch64 arch: aarch64 - runner: linux_aarch64 + runner: [self_hosted, type-cax41, image-arm-app-docker-ce] - - os: fedora-37 - image: docker.io/overte/overte-server-build:0.1.3-fedora-37-amd64 + - os: ubuntu-24.04 + image: docker.io/overte/overte-server-build:0.1.3-ubuntu-24.04-amd64 arch: amd64 - runner: linux_amd64 + runner: [self_hosted, type-cx52, image-x86-app-docker-ce] - - os: fedora-37 - image: docker.io/overte/overte-server-build:0.1.3-fedora-37-aarch64 + - os: ubuntu-24.04 + image: docker.io/overte/overte-server-build:0.1.3-ubuntu-24.04-aarch64 arch: aarch64 - runner: linux_aarch64 + runner: [self_hosted, type-cax41, image-arm-app-docker-ce] - - os: fedora-38 - image: docker.io/overte/overte-server-build:0.1.3-fedora-38-amd64 + - os: fedora-39 + image: docker.io/overte/overte-server-build:0.1.4-fedora-39-amd64 arch: amd64 - runner: linux_amd64 + runner: [self_hosted, type-cx52, image-x86-app-docker-ce] - - os: fedora-38 - image: docker.io/overte/overte-server-build:0.1.3-fedora-38-aarch64 + - os: fedora-39 + image: docker.io/overte/overte-server-build:0.1.4-fedora-39-aarch64 arch: aarch64 - runner: linux_aarch64 + runner: [self_hosted, type-cax41, image-arm-app-docker-ce] + + - os: fedora-40 + image: docker.io/overte/overte-server-build:0.1.4-fedora-39-amd64 + arch: amd64 + runner: [self_hosted, type-cx52, image-x86-app-docker-ce] + + - os: fedora-40 + image: docker.io/overte/overte-server-build:0.1.4-fedora-39-aarch64 + arch: aarch64 + runner: [self_hosted, type-cax41, image-arm-app-docker-ce] - os: rockylinux-9 image: docker.io/overte/overte-server-build:0.1.3-rockylinux-9-amd64 arch: amd64 - runner: linux_amd64 + runner: [self_hosted, type-cx52, image-x86-app-docker-ce] fail-fast: false @@ -132,7 +151,7 @@ jobs: fi # Tagged builds. E.g. release or release candidate builds. - if [ "${{github.event_name}}" != "pull_request" ]; then + if [ "${{github.ref_type}}" == "tag" ]; then echo "PRODUCTION_BUILD=true" >> $GITHUB_ENV fi @@ -166,16 +185,30 @@ jobs: echo "UPLOAD_PREFIX=build/overte/master" >> $GITHUB_ENV echo "RELEASE_NUMBER=${{ github.run_number }}" >> $GITHUB_ENV else # tagged - echo "DEBVERSION=${{ github.run_number }}-${{ github.ref_name }}-$GIT_COMMIT_SHORT-${{ matrix.os }}" >> $GITHUB_ENV - echo "RPMVERSION=${${{ github.ref_name }}//-/.}.${{ github.run_number }}.$GIT_COMMIT_SHORT" >> $GITHUB_ENV + echo "DEBVERSION=${{ github.ref_name }}-$GIT_COMMIT_SHORT-${{ matrix.os }}" >> $GITHUB_ENV + echo "RPMVERSION=${{ github.ref_name }}.$GIT_COMMIT_SHORT" >> $GITHUB_ENV fi - if [[ "${{ github.ref_name }}" != "master" && "${{ github.ref_name }}" != "pull_request" ]]; then # tagged - echo "RELEASE_NUMBER=/${{ github.ref_name }}" >> $GITHUB_ENV - if [ "${{ github.ref_name }}" == *"rc"* ]; then # release candidate - echo "UPLOAD_PREFIX=build/overte/release-candidate" >> $GITHUB_ENV + if [ "${{ github.ref_type }}" == "tag" ]; then # tagged + echo "RELEASE_NUMBER=${{ github.ref_name }}" >> $GITHUB_ENV + if [[ "${{ github.ref_name }}" == *"rc"* ]]; then # release candidate + # The uploader already creates a subfolder for each RELEASE_NUMBER. + echo "UPLOAD_PREFIX=build/overte/release-candidate/" >> $GITHUB_ENV else # release - echo "UPLOAD_PREFIX=build/overte/release" >> $GITHUB_ENV + echo "UPLOAD_PREFIX=build/overte/release/" >> $GITHUB_ENV + fi + fi + + echo "BUILD_NUMBER=$GIT_COMMIT_SHORT" >> $GITHUB_ENV + + if [ -z "$CMAKE_BACKTRACE_URL" ]; then + if [ "${{ github.ref_type }}" == "tag" ]; then + export CMAKE_BACKTRACE_URL="${{ secrets.SENTRY_MINIDUMP_ENDPOINT }}" + export CMAKE_BACKTRACE_TOKEN="${{ github.ref_name }}_${{ matrix.os }}_${{ github.sha }}" + else + # We're building a PR, default to the PR endpoint + export CMAKE_BACKTRACE_URL="https://o4504831972343808.ingest.sentry.io/api/4504832427950080/minidump/?sentry_key=f511de295975461b8f92a36f4a4a4f32" + export CMAKE_BACKTRACE_TOKEN="server_pr_${{ github.event.number }}_${{ github.sha }}" fi fi @@ -192,10 +225,12 @@ jobs: else # RPM if [ "${{ matrix.os }}" == "rockylinux-9" ]; then echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.el9.$INSTALLER_EXT" >> $GITHUB_ENV - elif [ "${{ matrix.os }}" == "fedora-37" ]; then - echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.fc37.$INSTALLER_EXT" >> $GITHUB_ENV elif [ "${{ matrix.os }}" == "fedora-38" ]; then echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.fc38.$INSTALLER_EXT" >> $GITHUB_ENV + elif [ "${{ matrix.os }}" == "fedora-39" ]; then + echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.fc39.$INSTALLER_EXT" >> $GITHUB_ENV + elif [ "${{ matrix.os }}" == "fedora-40" ]; then + echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.fc40.$INSTALLER_EXT" >> $GITHUB_ENV else echo "Error! ARTIFACT_PATTERN not set!" exit 1 # Fail @@ -203,7 +238,7 @@ jobs: fi - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: false fetch-depth: 1 @@ -216,11 +251,6 @@ jobs: working-directory: build shell: bash run: | - if [ -z "$CMAKE_BACKTRACE_URL" ] ; then - # We're building a PR, default to the PR endpoint - export CMAKE_BACKTRACE_URL="https://o4504831972343808.ingest.sentry.io/api/4504832427950080/minidump/?sentry_key=f511de295975461b8f92a36f4a4a4f32" - export CMAKE_BACKTRACE_TOKEN="server_pr_${{ github.event.number }}_${{ github.sha }}" - fi cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_BUILD_TYPE=release $CMAKE_EXTRA @@ -275,8 +305,7 @@ jobs: df -h - name: Upload artifact to GitHub - if: github.event_name == 'pull_request' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.ARTIFACT_PATTERN }} path: pkg-scripts/${{ env.ARTIFACT_PATTERN }} diff --git a/.github/workflows/master_build.yml b/.github/workflows/master_build.yml index 0f8e719094..265adde87c 100644 --- a/.github/workflows/master_build.yml +++ b/.github/workflows/master_build.yml @@ -1,6 +1,6 @@ # Copyright 2013-2019 High Fidelity, Inc. # Copyright 2020-2022 Vircadia contributors -# Copyright 2021-2022 Overte e.V. +# Copyright 2021-2024 Overte e.V. # SPDX-License-Identifier: Apache-2.0 name: Master CI Build @@ -26,6 +26,8 @@ env: UPLOAD_ENDPOINT: "https://fra1.digitaloceanspaces.com" CMAKE_BACKTRACE_URL: ${{ secrets.SENTRY_MINIDUMP_ENDPOINT }} CMAKE_BACKTRACE_TOKEN: master_${{ github.event.number }}_${{ github.sha }} + # Disable VCPKG caching to save time. + VCPKG_FEATURE_FLAGS: -binarycaching # OSX-specific variables DEVELOPER_DIR: /Applications/Xcode_11.2.app/Contents/Developer @@ -120,7 +122,7 @@ jobs: rm -rf ~/overte-files rm -rf ~/.cache - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 with: submodules: false fetch-depth: 1 diff --git a/.github/workflows/master_deploy_apidocs.yml b/.github/workflows/master_deploy_apidocs.yml index e8d21f9d97..79d4533b40 100644 --- a/.github/workflows/master_deploy_apidocs.yml +++ b/.github/workflows/master_deploy_apidocs.yml @@ -1,4 +1,4 @@ -# Copyright 2022-2023 Overte e.V. +# Copyright 2022-2024 Overte e.V. # SPDX-License-Identifier: Apache-2.0 name: Master API-docs CI Build and Deploy @@ -14,7 +14,7 @@ jobs: name: Build and deploy API-docs steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies working-directory: tools/jsdoc diff --git a/.github/workflows/master_deploy_doxygen.yml b/.github/workflows/master_deploy_doxygen.yml index ea99e265bb..9a12a165a8 100644 --- a/.github/workflows/master_deploy_doxygen.yml +++ b/.github/workflows/master_deploy_doxygen.yml @@ -1,4 +1,4 @@ -# Copyright 2022-2023 Overte e.V. +# Copyright 2022-2024 Overte e.V. # SPDX-License-Identifier: Apache-2.0 name: Master Doxygen CI Build and Deploy @@ -14,7 +14,7 @@ jobs: name: Build and deploy Doxygen documentation steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies run: | diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index d9548fd862..2bd3b629c4 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -1,6 +1,6 @@ # Copyright 2013-2019 High Fidelity, Inc. # Copyright 2020-2022 Vircadia contributors. -# Copyright 2021-2022 Overte e.V. +# Copyright 2021-2024 Overte e.V. # SPDX-License-Identifier: Apache-2.0 name: Pull Request CI Build @@ -24,6 +24,8 @@ env: # We can't use secrets or actions here, so the actual value has to be hardcoded. CMAKE_BACKTRACE_URL: "https://o4504831972343808.ingest.sentry.io/api/4504832427950080/minidump/?sentry_key=f511de295975461b8f92a36f4a4a4f32" CMAKE_BACKTRACE_TOKEN: PR_${{ github.event.number }}_${{ github.sha }} + # Disable VCPKG caching to save time. + VCPKG_FEATURE_FLAGS: -binarycaching UPLOAD_BUCKET: overte-public UPLOAD_REGION: fra1 @@ -54,7 +56,12 @@ jobs: #- os: macOS-10.15 # build_type: full - os: Ubuntu 20.04 - runner: linux_amd64 + # https://github.com/testflows/TestFlows-GitHub-Hetzner-Runners/wiki/Meta-Labels + # self_hosted makes the Hetzner auto-scaler put up the job. + # type-cx52 is a Hetzner VPS server type. In this case cs52 is a server with 16-cores and 32GB of RAM. + # image-x86-app-docker-ce is a Hetzner image. + # https://github.com/testflows/TestFlows-GitHub-Hetzner-Runners/wiki/Specifying-The-Runner-Image + runner: [self_hosted, type-cx52, image-x86-app-docker-ce] arch: amd64 build_type: full apt-dependencies: pkg-config libxext-dev libdouble-conversion-dev libpcre2-16-0 libpulse0 libharfbuzz-dev libnss3 libnspr4 libxdamage1 libasound2 # add missing dependencies to docker image when convenient @@ -65,7 +72,7 @@ jobs: # apt-dependencies: mesa-common-dev libegl1 libglvnd-dev libdouble-conversion1 libpulse0 python3-github python3-distro # Do not change the names of self-hosted runners without knowing what you are doing, as they correspond to labels that have to be set on the runner. - os: Ubuntu 22.04 - runner: linux_aarch64 + runner: [self_hosted, type-cax41, image-arm-app-docker-ce] arch: aarch64 build_type: full image: docker.io/overte/overte-full-build:0.1.1-ubuntu-22.04-aarch64 @@ -165,7 +172,7 @@ jobs: rm -rf ~/overte-files rm -rf ~/.cache - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: false fetch-depth: 1 @@ -335,7 +342,7 @@ jobs: - name: Upload Artifact if: startsWith(matrix.os, 'Windows') || startsWith(matrix.os, 'macOS') - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.ARTIFACT_PATTERN }} path: ./build/${{ env.ARTIFACT_PATTERN }} diff --git a/BUILD_ANDROID.md b/BUILD_ANDROID.md index 8a0b9da260..7bceb54cad 100644 --- a/BUILD_ANDROID.md +++ b/BUILD_ANDROID.md @@ -8,6 +8,8 @@ SPDX-License-Identifier: Apache-2.0 # Build Android *Last Updated on December 15, 2020* +> [!WARNING] +> Android building is currently broken, due to breaking changes in Qt and Gradle. Help with updating (or rewriting) the Gradle scripts would be great. Please read the [general build guide](BUILD.md) for information on building other platforms. Only Android specific instructions are found in this file. **Note that these instructions apply to building for the Oculus Quest 1.** diff --git a/CMakeLists.txt b/CMakeLists.txt index 33e30fa6e4..a46f1963ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,9 @@ endif() # 3.14 is the minimum version that supports symlinks on Windows cmake_minimum_required(VERSION 3.14) +# This should allow using long paths on Windows +SET(CMAKE_NINJA_FORCE_RESPONSE_FILE 1 CACHE INTERNAL "") + # Passing of variables to vcpkg # # vcpkg runs cmake scripts in an isolated environment, see this for details: @@ -181,8 +184,13 @@ if(OVERTE_WARNINGS_WHITELIST) endif() if(OVERTE_WARNINGS_AS_ERRORS) - set(ENV{CXXFLAGS} "$ENV{CXXFLAGS} -Werror") - set(ENV{CFLAGS} "$ENV{CXXFLAGS} -Werror") + if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC" OR (CMAKE_CXX_COMPILER_ID MATCHES "" AND WIN32)) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /WX") + set(CMAKE_CFLAGS "${CMAKE_CFLAGS} /WX") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") + set(CMAKE_CFLAGS "${CMAKE_CFLAGS} -Werror") + endif() endif() @@ -249,9 +257,6 @@ else() endif() set(SCREENSHARE 0) -if (WIN32) - set(SCREENSHARE 1) -endif() if (APPLE AND NOT CLIENT_ONLY) # Don't include Screenshare in OSX client-only builds. set(SCREENSHARE 1) @@ -483,6 +488,7 @@ if (BUILD_CLIENT) endif() option(USE_SIXENSE "Build Interface with sixense library/plugin" OFF) + option(USE_NEURON "Build Interface with Neuron library/plugin" OFF) endif() if (BUILD_CLIENT OR BUILD_SERVER) diff --git a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java index ef9876c71a..0703fabf02 100644 --- a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java +++ b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java @@ -109,7 +109,7 @@ public class PermissionChecker extends Activity { JSONObject obj = new JSONObject(); try { obj.put("firstRun",false); - obj.put("Avatar/fullAvatarURL", avatarPaths[which]); + obj.put(SETTINGS_FULL_PRIVATE_GROUP_NAME + "/Avatar/fullAvatarURL", avatarPaths[which]); File directory = new File(pathForJson); if(!directory.exists()) directory.mkdirs(); diff --git a/assignment-client/src/AgentScriptingInterface.h b/assignment-client/src/AgentScriptingInterface.h index cd5aa5ad65..1146c4006b 100644 --- a/assignment-client/src/AgentScriptingInterface.h +++ b/assignment-client/src/AgentScriptingInterface.h @@ -40,7 +40,7 @@ */ class AgentScriptingInterface : public QObject { Q_OBJECT - Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar) + Q_PROPERTY(bool isAvatar READ getIsAvatar WRITE setIsAvatar) Q_PROPERTY(bool isPlayingAvatarSound READ isPlayingAvatarSound) Q_PROPERTY(bool isListeningToAudioStream READ isListeningToAudioStream WRITE setIsListeningToAudioStream) Q_PROPERTY(bool isNoiseGateEnabled READ isNoiseGateEnabled WRITE setIsNoiseGateEnabled) @@ -77,15 +77,15 @@ public slots: /*@jsdoc * Checks whether the script is emulating an avatar. - * @function Agent.isAvatar + * @function Agent.getIsAvatar * @returns {boolean} true if the script is emulating an avatar, otherwise false. * @example Check whether the agent is emulating an avatar. * (function () { - * print("Agent is avatar: " + Agent.isAvatar()); + * print("Agent is avatar: " + Agent.getIsAvatar()); * print("Agent is avatar: " + Agent.isAvatar); // Same result. * }()); */ - bool isAvatar() const { return _agent->isAvatar(); } + bool getIsAvatar() const { return _agent->isAvatar(); } /*@jsdoc * Plays a sound from the position and with the orientation of the emulated avatar's head. No sound is played unless diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 470ab3f233..a47420a873 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -222,13 +222,23 @@ void AudioMixerClientData::parseInjectorGainSet(ReceivedMessage& message, const qCDebug(audio) << "Setting MASTER injector gain for" << uuid << "to" << gain; } -void AudioMixerClientData::setGainForAvatar(QUuid nodeID, float gain) { - auto it = std::find_if(_streams.active.cbegin(), _streams.active.cend(), [nodeID](const MixableStream& mixableStream){ +bool setGainInStreams(const QUuid &nodeID, float gain, std::vector &streamVector) { + auto itActive = std::find_if(streamVector.cbegin(), streamVector.cend(), + [nodeID](const AudioMixerClientData::MixableStream& mixableStream){ return mixableStream.nodeStreamID.nodeID == nodeID && mixableStream.nodeStreamID.streamID.isNull(); }); - if (it != _streams.active.cend()) { - it->hrtf->setGainAdjustment(gain); + if (itActive != streamVector.cend()) { + itActive->hrtf->setGainAdjustment(gain); + return true; + } else { + return false; + } +} + +void AudioMixerClientData::setGainForAvatar(QUuid nodeID, float gain) { + if (!setGainInStreams(nodeID, gain, _streams.active)) { + setGainInStreams(nodeID, gain, _streams.inactive); } } diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index c919d2a2d7..a1db33fbc8 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -30,8 +30,12 @@ #include -ScriptableAvatar::ScriptableAvatar(): _scriptEngine(newScriptEngine()) { +ScriptableAvatar::ScriptableAvatar() { _clientTraitsHandler.reset(new ClientTraitsHandler(this)); + static std::once_flag once; + std::call_once(once, [] { + qRegisterMetaType("HFMModel::Pointer"); + }); } QByteArray ScriptableAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) { @@ -52,6 +56,7 @@ void ScriptableAvatar::startAnimation(const QString& url, float fps, float prior _animation = DependencyManager::get()->getAnimation(url); _animationDetails = AnimationDetails("", QUrl(url), fps, 0, loop, hold, false, firstFrame, lastFrame, true, firstFrame, false); _maskedJoints = maskedJoints; + _isAnimationRigValid = false; } void ScriptableAvatar::stopAnimation() { @@ -89,11 +94,12 @@ QStringList ScriptableAvatar::getJointNames() const { } void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { - _bind.reset(); - _animSkeleton.reset(); + _avatarAnimSkeleton.reset(); + _geometryResource.reset(); AvatarData::setSkeletonModelURL(skeletonModelURL); updateJointMappings(); + _isRigValid = false; } int ScriptableAvatar::sendAvatarDataPacket(bool sendAll) { @@ -137,65 +143,87 @@ static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation, } void ScriptableAvatar::update(float deltatime) { + if (!_geometryResource && !_skeletonModelFilenameURL.isEmpty()) { // AvatarData will parse the .fst, but not get the .fbx skeleton. + _geometryResource = DependencyManager::get()->getGeometryResource(_skeletonModelFilenameURL); + } + // Run animation - if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && !_bind.isNull() && _bind->isLoaded()) { - if (!_animSkeleton) { - _animSkeleton = std::make_shared(_bind->getHFMModel()); - } - float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps; - if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) { - while (currentFrame >= _animationDetails.lastFrame) { - currentFrame -= (_animationDetails.lastFrame - _animationDetails.firstFrame); + Q_ASSERT(QThread::currentThread() == thread()); + if (_animation && _animation->isLoaded()) { + Q_ASSERT(thread() == _animation->thread()); + auto frames = _animation->getFramesReference(); + if (frames.size() > 0 && _geometryResource && _geometryResource->isHFMModelLoaded()) { + if (!_isRigValid) { + _rig.reset(_geometryResource->getHFMModel()); + _isRigValid = true; } - _animationDetails.currentFrame = currentFrame; - - const QVector& modelJoints = _bind->getHFMModel().joints; - QStringList animationJointNames = _animation->getJointNames(); - - const int nJoints = modelJoints.size(); - if (_jointData.size() != nJoints) { - _jointData.resize(nJoints); + if (!_isAnimationRigValid) { + _animationRig.reset(_animation->getHFMModel()); + _isAnimationRigValid = true; } - - const int frameCount = _animation->getFrames().size(); - const HFMAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount); - const HFMAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount); - const float frameFraction = glm::fract(currentFrame); - std::vector poses = _animSkeleton->getRelativeDefaultPoses(); - - const float UNIT_SCALE = 0.01f; - - for (int i = 0; i < animationJointNames.size(); i++) { - const QString& name = animationJointNames[i]; - // As long as we need the model preRotations anyway, let's get the jointIndex from the bind skeleton rather than - // trusting the .fst (which is sometimes not updated to match changes to .fbx). - int mapping = _bind->getHFMModel().getJointIndex(name); - if (mapping != -1 && !_maskedJoints.contains(name)) { - - AnimPose floorPose = composeAnimPose(modelJoints[mapping], floorFrame.rotations[i], floorFrame.translations[i] * UNIT_SCALE); - AnimPose ceilPose = composeAnimPose(modelJoints[mapping], ceilFrame.rotations[i], floorFrame.translations[i] * UNIT_SCALE); - blend(1, &floorPose, &ceilPose, frameFraction, &poses[mapping]); - } + if (!_avatarAnimSkeleton) { + _avatarAnimSkeleton = std::make_shared(_geometryResource->getHFMModel()); } - - std::vector absPoses = poses; - _animSkeleton->convertRelativePosesToAbsolute(absPoses); - for (int i = 0; i < nJoints; i++) { - JointData& data = _jointData[i]; - AnimPose& absPose = absPoses[i]; - if (data.rotation != absPose.rot()) { - data.rotation = absPose.rot(); - data.rotationIsDefaultPose = false; + float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps; + if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) { + while (currentFrame >= _animationDetails.lastFrame) { + currentFrame -= (_animationDetails.lastFrame - _animationDetails.firstFrame); } - AnimPose& relPose = poses[i]; - if (data.translation != relPose.trans()) { - data.translation = relPose.trans(); - data.translationIsDefaultPose = false; - } - } + _animationDetails.currentFrame = currentFrame; - } else { - _animation.clear(); + const QVector& modelJoints = _geometryResource->getHFMModel().joints; + QStringList animationJointNames = _animation->getJointNames(); + + const int nJoints = modelJoints.size(); + if (_jointData.size() != nJoints) { + _jointData.resize(nJoints); + } + + const int frameCount = frames.size(); + const HFMAnimationFrame& floorFrame = frames.at((int)glm::floor(currentFrame) % frameCount); + const HFMAnimationFrame& ceilFrame = frames.at((int)glm::ceil(currentFrame) % frameCount); + const float frameFraction = glm::fract(currentFrame); + std::vector poses = _avatarAnimSkeleton->getRelativeDefaultPoses(); + + // TODO: this needs more testing, it's possible that we need not only scale but also rotation and translation + // According to tests with unmatching avatar and animation armatures, sometimes bones are not rotated correctly. + // Matching armatures already work very well now. + const float UNIT_SCALE = _animationRig.GetScaleFactorGeometryToUnscaledRig() / _rig.GetScaleFactorGeometryToUnscaledRig(); + + for (int i = 0; i < animationJointNames.size(); i++) { + const QString& name = animationJointNames[i]; + // As long as we need the model preRotations anyway, let's get the jointIndex from the bind skeleton rather than + // trusting the .fst (which is sometimes not updated to match changes to .fbx). + int mapping = _geometryResource->getHFMModel().getJointIndex(name); + if (mapping != -1 && !_maskedJoints.contains(name)) { + AnimPose floorPose = composeAnimPose(modelJoints[mapping], floorFrame.rotations[i], + floorFrame.translations[i] * UNIT_SCALE); + AnimPose ceilPose = composeAnimPose(modelJoints[mapping], ceilFrame.rotations[i], + ceilFrame.translations[i] * UNIT_SCALE); + blend(1, &floorPose, &ceilPose, frameFraction, &poses[mapping]); + } + } + + std::vector absPoses = poses; + Q_ASSERT(_avatarAnimSkeleton != nullptr); + _avatarAnimSkeleton->convertRelativePosesToAbsolute(absPoses); + for (int i = 0; i < nJoints; i++) { + JointData& data = _jointData[i]; + AnimPose& absPose = absPoses[i]; + if (data.rotation != absPose.rot()) { + data.rotation = absPose.rot(); + data.rotationIsDefaultPose = false; + } + AnimPose& relPose = poses[i]; + if (data.translation != relPose.trans()) { + data.translation = relPose.trans(); + data.translationIsDefaultPose = false; + } + } + + } else { + _animation.clear(); + } } } @@ -245,6 +273,7 @@ void ScriptableAvatar::setJointMappingsFromNetworkReply() { networkReply->deleteLater(); return; } + // TODO: this works only with .fst files currently, not directly with FBX and GLB models { QWriteLocker writeLock(&_jointDataLock); QByteArray line; @@ -253,7 +282,7 @@ void ScriptableAvatar::setJointMappingsFromNetworkReply() { if (line.startsWith("filename")) { int filenameIndex = line.indexOf('=') + 1; if (filenameIndex > 0) { - _skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed())); + _skeletonModelFilenameURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed())); } } if (!line.startsWith("jointIndex")) { @@ -315,7 +344,9 @@ AvatarEntityMap ScriptableAvatar::getAvatarEntityDataInternal(bool allProperties EntityItemProperties properties = entity->getProperties(desiredProperties); QByteArray blob; - EntityItemProperties::propertiesToBlob(*_scriptEngine, sessionID, properties, blob, allProperties); + _helperScriptEngine.run( [&] { + EntityItemProperties::propertiesToBlob(*_helperScriptEngine.get(), sessionID, properties, blob, allProperties); + }); data[id] = blob; } }); @@ -339,8 +370,12 @@ void ScriptableAvatar::setAvatarEntityData(const AvatarEntityMap& avatarEntityDa while (dataItr != avatarEntityData.end()) { EntityItemProperties properties; const QByteArray& blob = dataItr.value(); - if (!blob.isNull() && EntityItemProperties::blobToProperties(*_scriptEngine, blob, properties)) { - newProperties[dataItr.key()] = properties; + if (!blob.isNull()) { + _helperScriptEngine.run([&] { + if (EntityItemProperties::blobToProperties(*_helperScriptEngine.get(), blob, properties)) { + newProperties[dataItr.key()] = properties; + } + }); } ++dataItr; } @@ -419,9 +454,16 @@ void ScriptableAvatar::updateAvatarEntity(const QUuid& entityID, const QByteArra EntityItemPointer entity; EntityItemProperties properties; - if (!EntityItemProperties::blobToProperties(*_scriptEngine, entityData, properties)) { - // entityData is corrupt - return; + { + // TODO: checking how often this happens and what is the performance impact of having the script engine on separate thread + // If it's happening often, a method to move HelperScriptEngine into the current thread would be a good idea + bool result = _helperScriptEngine.runWithResult ( [&]() { + return EntityItemProperties::blobToProperties(*_helperScriptEngine.get(), entityData, properties); + }); + if (!result) { + // entityData is corrupt + return; + } } std::map::iterator itr = _entities.find(entityID); diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 703a0a9f64..7e79d25cd0 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -19,6 +19,9 @@ #include #include #include +#include "model-networking/ModelCache.h" +#include "Rig.h" +#include /*@jsdoc * The Avatar API is used to manipulate scriptable avatars on the domain. This API is a subset of the @@ -217,12 +220,16 @@ private: AnimationPointer _animation; AnimationDetails _animationDetails; QStringList _maskedJoints; - AnimationPointer _bind; // a sleazy way to get the skeleton, given the various library/cmake dependencies - std::shared_ptr _animSkeleton; + GeometryResource::Pointer _geometryResource; + Rig _rig; + bool _isRigValid{false}; + Rig _animationRig; + bool _isAnimationRigValid{false}; + std::shared_ptr _avatarAnimSkeleton; QHash _fstJointIndices; ///< 1-based, since zero is returned for missing keys QStringList _fstJointNames; ///< in order of depth-first traversal - QUrl _skeletonFBXURL; - mutable ScriptEnginePointer _scriptEngine; + QUrl _skeletonModelFilenameURL; // This contains URL from filename field in fst file + mutable HelperScriptEngine _helperScriptEngine; std::map _entities; /// Loads the joint indices, names from the FST file (if any) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index b16e4561d6..2b23434195 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -44,16 +44,8 @@ using Mutex = std::mutex; using Lock = std::lock_guard; -static std::mutex logBufferMutex; -static std::string logBuffer; - void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { auto logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message); - - if (!logMessage.isEmpty()) { - Lock lock(logBufferMutex); - logBuffer.append(logMessage.toStdString() + '\n'); - } } int EntityScriptServer::_entitiesScriptEngineCount = 0; @@ -217,10 +209,10 @@ void EntityScriptServer::handleEntityServerScriptLogPacket(QSharedPointer(); + QJsonDocument document; + document.setArray(buffer); + QString data(document.toJson()); + std::string string = data.toStdString(); + auto cstring = string.c_str(); for (auto uuid : _logListeners) { auto node = nodeList->nodeWithUUID(uuid); if (node && node->getActiveSocket()) { auto packet = NLPacketList::create(PacketType::EntityServerScriptLog, QByteArray(), true, true); - packet->write(buffer.data(), buffer.size()); + packet->write(cstring, strlen(cstring)); nodeList->sendPacketList(std::move(packet), *node); } } } +void EntityScriptServer::addLogEntry(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, ScriptMessage::Severity severity) { + ScriptMessage entry(message, fileName, lineNumber, entityID, ScriptMessage::ScriptType::TYPE_ENTITY_SCRIPT, severity); + Lock lock(_logBufferMutex); + _logBuffer.append(entry.toJson()); +} + void EntityScriptServer::handleEntityScriptCallMethodPacket(QSharedPointer receivedMessage, SharedNodePointer senderNode) { if (_entitiesScriptManager && _entityViewer.getTree() && !_shuttingDown) { @@ -469,6 +472,29 @@ void EntityScriptServer::resetEntitiesScriptEngine() { connect(newManager.get(), &ScriptManager::warningMessage, scriptEngines, &ScriptEngines::onWarningMessage); connect(newManager.get(), &ScriptManager::infoMessage, scriptEngines, &ScriptEngines::onInfoMessage); + // Make script engine messages available through ScriptDiscoveryService + connect(newManager.get(), &ScriptManager::infoEntityMessage, scriptEngines, &ScriptEngines::infoEntityMessage); + connect(newManager.get(), &ScriptManager::printedEntityMessage, scriptEngines, &ScriptEngines::printedEntityMessage); + connect(newManager.get(), &ScriptManager::errorEntityMessage, scriptEngines, &ScriptEngines::errorEntityMessage); + connect(newManager.get(), &ScriptManager::warningEntityMessage, scriptEngines, &ScriptEngines::warningEntityMessage); + + connect(newManager.get(), &ScriptManager::infoEntityMessage, + [this](const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID) { + addLogEntry(message, fileName, lineNumber, entityID, ScriptMessage::Severity::SEVERITY_INFO); + }); + connect(newManager.get(), &ScriptManager::printedEntityMessage, + [this](const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID) { + addLogEntry(message, fileName, lineNumber, entityID, ScriptMessage::Severity::SEVERITY_PRINT); + }); + connect(newManager.get(), &ScriptManager::errorEntityMessage, + [this](const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID) { + addLogEntry(message, fileName, lineNumber, entityID, ScriptMessage::Severity::SEVERITY_ERROR); + }); + connect(newManager.get(), &ScriptManager::warningEntityMessage, + [this](const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID) { + addLogEntry(message, fileName, lineNumber, entityID, ScriptMessage::Severity::SEVERITY_WARNING); + }); + connect(newManager.get(), &ScriptManager::update, this, [this] { _entityViewer.queryOctree(); _entityViewer.getTree()->preUpdate(); diff --git a/assignment-client/src/scripts/EntityScriptServer.h b/assignment-client/src/scripts/EntityScriptServer.h index 3f15f5733c..2fba985ef4 100644 --- a/assignment-client/src/scripts/EntityScriptServer.h +++ b/assignment-client/src/scripts/EntityScriptServer.h @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include "../entities/EntityTreeHeadlessViewer.h" @@ -55,10 +57,32 @@ private slots: void handleSettings(); void updateEntityPPS(); + /** + * @brief Handles log subscribe/unsubscribe requests + * + * Clients can subscribe to logs by sending a script log packet. Entity Script Server keeps list of subscribers + * and sends them logs in JSON format. + */ + void handleEntityServerScriptLogPacket(QSharedPointer message, SharedNodePointer senderNode); + /** + * @brief Transmit logs + * + * This is called periodically through a timer to transmit logs from scripts. + */ + void pushLogs(); + /** + * @brief Adds log entry to the transmit buffer + * + * This is connected to entity script log events in the script manager and adds script log message to the buffer + * containing messages that will be sent to subscribed clients. + */ + + void addLogEntry(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, ScriptMessage::Severity severity); + void handleEntityScriptCallMethodPacket(QSharedPointer message, SharedNodePointer senderNode); @@ -85,6 +109,9 @@ private: EntityEditPacketSender _entityEditSender; EntityTreeHeadlessViewer _entityViewer; + QJsonArray _logBuffer; + std::mutex _logBufferMutex; + int _maxEntityPPS { DEFAULT_MAX_ENTITY_PPS }; int _entityPPSPerScript { DEFAULT_ENTITY_PPS_PER_SCRIPT }; diff --git a/cmake/compiler.cmake b/cmake/compiler.cmake index 5108253403..316b0632c6 100644 --- a/cmake/compiler.cmake +++ b/cmake/compiler.cmake @@ -6,10 +6,6 @@ if (NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8") message( FATAL_ERROR "Only 64 bit builds supported." ) endif() -if (USE_CCACHE OR "$ENV{USE_CCACHE}") - configure_ccache() -endif() - if (WIN32) add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) diff --git a/cmake/externals/LibOVR/CMakeLists.txt b/cmake/externals/LibOVR/CMakeLists.txt index 51a85f0117..2ef232ae1f 100644 --- a/cmake/externals/LibOVR/CMakeLists.txt +++ b/cmake/externals/LibOVR/CMakeLists.txt @@ -15,14 +15,22 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) if (WIN32) + # Note the -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + # It's important that we pass our build type down to other builds we make, especially on Windows. + # On Windows, debug libraries get a 'd' suffix, eg, LibOVRd.lib. This means that a mismatch of build + # types means we'll generate a LibOVRd.lib and the rest of the system will look for LibOVR.lib, or + # viceversa. ExternalProject_Add( ${EXTERNAL_NAME} URL "${EXTERNAL_BUILD_ASSETS}/dependencies/ovr_sdk_win_1.35.0.zip" URL_MD5 1e3e8b2101387af07ff9c841d0ea285e - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/LibOVRCMakeLists.txt" /CMakeLists.txt LOG_DOWNLOAD 1 DOWNLOAD_EXTRACT_TIMESTAMP 1 + BUILD_BYPRODUCTS + "project/Lib/LibOVR.lib" + "project/Lib/LibOVRd.lib" ) ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) diff --git a/cmake/externals/LibOVR/LibOVRCMakeLists.txt b/cmake/externals/LibOVR/LibOVRCMakeLists.txt index a52cff5463..7740c618f6 100644 --- a/cmake/externals/LibOVR/LibOVRCMakeLists.txt +++ b/cmake/externals/LibOVR/LibOVRCMakeLists.txt @@ -1,6 +1,8 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.20) project(LibOVR) +message(STATUS "Building LibOVR for ${CMAKE_BUILD_TYPE} configuration") + include_directories(LibOVR/Include LibOVR/Src) file(GLOB HEADER_FILES LibOVR/Include/*.h) file(GLOB EXTRA_HEADER_FILES LibOVR/Include/Extras/*.h) diff --git a/cmake/externals/LibOVRPlatform/CMakeLists.txt b/cmake/externals/LibOVRPlatform/CMakeLists.txt index 492827210a..d6b3478418 100644 --- a/cmake/externals/LibOVRPlatform/CMakeLists.txt +++ b/cmake/externals/LibOVRPlatform/CMakeLists.txt @@ -16,6 +16,8 @@ if (WIN32) INSTALL_COMMAND "" LOG_DOWNLOAD 1 DOWNLOAD_EXTRACT_TIMESTAMP 1 + BUILD_BYPRODUCTS + "project/src/LibOVRPlatform/Windows/LibOVRPlatform64_1.lib" ) ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) diff --git a/cmake/externals/crashpad/CMakeLists.txt b/cmake/externals/crashpad/CMakeLists.txt index c174022aa3..725c0172a5 100644 --- a/cmake/externals/crashpad/CMakeLists.txt +++ b/cmake/externals/crashpad/CMakeLists.txt @@ -13,6 +13,13 @@ if (WIN32) INSTALL_COMMAND "" LOG_DOWNLOAD 1 DOWNLOAD_EXTRACT_TIMESTAMP 1 + BUILD_BYPRODUCTS + "project/src/crashpad/out/Release_x64/lib_MD/crashpad_client.lib" + "project/src/crashpad/out/Release_x64/lib_MD/crashpad_util.lib" + "project/src/crashpad/out/Release_x64/lib_MD/base.lib" + "project/src/crashpad/out/Debug_x64/lib_MD/crashpad_client.lib" + "project/src/crashpad/out/Debug_x64/lib_MD/crashpad_util.lib" + "project/src/crashpad/out/Debug_x64/lib_MD/base.lib" ) ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) diff --git a/cmake/externals/steamworks/CMakeLists.txt b/cmake/externals/steamworks/CMakeLists.txt index 47e5029206..62961f46a4 100644 --- a/cmake/externals/steamworks/CMakeLists.txt +++ b/cmake/externals/steamworks/CMakeLists.txt @@ -4,18 +4,82 @@ set(EXTERNAL_NAME steamworks) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(STEAMWORKS_URL "${EXTERNAL_BUILD_ASSETS}/dependencies/steamworks_sdk_137.zip") -set(STEAMWORKS_URL_MD5 "95ba9d0e3ddc04f8a8be17d2da806cbb") +set(STEAMWORKS_URL "${EXTERNAL_BUILD_ASSETS}/dependencies/steamworks_sdk_158a.zip") +set(STEAMWORKS_URL_SHA512 "fe906a7510a2125ab1441ad349e8bc31fafc9ab8130ec3843287e615a850305a8ed303e8d9e5bae4fee06024987834fb9f64c6c10d3da3784267a4906e59c831") +# Ninja needs to know all the files that result from this upfront, so we need to tell it what files this is going +# to generate with BUILD_BYPRODUCTS. We need to include all the files that are going to be referenced from elsewhere +# in the build. +# +# This should include both libraries and headers, since from the point of view of the build, those are the outputs +# of the project, even though we're not actually building anything here, and just unzipping an existing binary. +# +# I believe this list can't be obtained automatically from the compressed file, and needs to be generated by hand. +# Steam SDK .zip has a sdk/ subdirectory, but for ExternalProject, this gets turned into project/src/steamworks. +# So inside the SDK, sdk/redistributable_bin/steam_api.dll becomes project/src/steamworks/redistributable_bin/steam_api.dll +# This can be seen under $BUILD_DIR/ext. ExternalProject_Add( ${EXTERNAL_NAME} URL ${STEAMWORKS_URL} - URL_MD5 ${STEAMWORKS_URL_MD5} + URL_HASH SHA512=${STEAMWORKS_URL_SHA512} CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" LOG_DOWNLOAD 1 DOWNLOAD_EXTRACT_TIMESTAMP 1 + BUILD_BYPRODUCTS + "project/src/steamworks/redistributable_bin/win64/steam_api64.lib" + "project/src/steamworks/redistributable_bin/win64/steam_api64.dll" + "project/src/steamworks/redistributable_bin/osx/steam_api.dylib" + "project/src/steamworks/redistributable_bin/linux64/libsteam_api.so" + "project/src/steamworks/redistributable_bin/linux32/libsteam_api.so" + "project/src/steamworks/redistributable_bin/steam_api.lib" + "project/src/steamworks/redistributable_bin/steam_api.dll" + "project/src/steamworks/public/steam/isteamapplist.h" + "project/src/steamworks/public/steam/isteamapps.h" + "project/src/steamworks/public/steam/isteamappticket.h" + "project/src/steamworks/public/steam/isteamclient.h" + "project/src/steamworks/public/steam/isteamcontroller.h" + "project/src/steamworks/public/steam/isteamdualsense.h" + "project/src/steamworks/public/steam/isteamfriends.h" + "project/src/steamworks/public/steam/isteamgamecoordinator.h" + "project/src/steamworks/public/steam/isteamgameserver.h" + "project/src/steamworks/public/steam/isteamgameserverstats.h" + "project/src/steamworks/public/steam/isteamhtmlsurface.h" + "project/src/steamworks/public/steam/isteamhttp.h" + "project/src/steamworks/public/steam/isteaminput.h" + "project/src/steamworks/public/steam/isteaminventory.h" + "project/src/steamworks/public/steam/isteammatchmaking.h" + "project/src/steamworks/public/steam/isteammusic.h" + "project/src/steamworks/public/steam/isteammusicremote.h" + "project/src/steamworks/public/steam/isteamnetworking.h" + "project/src/steamworks/public/steam/isteamnetworkingmessages.h" + "project/src/steamworks/public/steam/isteamnetworkingsockets.h" + "project/src/steamworks/public/steam/isteamnetworkingutils.h" + "project/src/steamworks/public/steam/isteamparentalsettings.h" + "project/src/steamworks/public/steam/isteamps3overlayrenderer.h" + "project/src/steamworks/public/steam/isteamremoteplay.h" + "project/src/steamworks/public/steam/isteamremotestorage.h" + "project/src/steamworks/public/steam/isteamscreenshots.h" + "project/src/steamworks/public/steam/isteamugc.h" + "project/src/steamworks/public/steam/isteamuser.h" + "project/src/steamworks/public/steam/isteamuserstats.h" + "project/src/steamworks/public/steam/isteamutils.h" + "project/src/steamworks/public/steam/isteamvideo.h" + "project/src/steamworks/public/steam/matchmakingtypes.h" + "project/src/steamworks/public/steam/steam_api_common.h" + "project/src/steamworks/public/steam/steam_api_flat.h" + "project/src/steamworks/public/steam/steam_api.h" + "project/src/steamworks/public/steam/steam_api_internal.h" + "project/src/steamworks/public/steam/steamclientpublic.h" + "project/src/steamworks/public/steam/steamencryptedappticket.h" + "project/src/steamworks/public/steam/steam_gameserver.h" + "project/src/steamworks/public/steam/steamhttpenums.h" + "project/src/steamworks/public/steam/steamnetworkingfakeip.h" + "project/src/steamworks/public/steam/steamnetworkingtypes.h" + "project/src/steamworks/public/steam/steamps3params.h" + "project/src/steamworks/public/steam/steamtypes.h" + "project/src/steamworks/public/steam/steamuniverse.h" ) set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") diff --git a/cmake/macros/ConfigureCCache.cmake b/cmake/macros/ConfigureCCache.cmake deleted file mode 100644 index bec159ef09..0000000000 --- a/cmake/macros/ConfigureCCache.cmake +++ /dev/null @@ -1,45 +0,0 @@ -# -# ConfigureCCache.cmake -# cmake/macros -# -# Created by Clement Brisset on 10/10/18. -# Copyright 2018 High Fidelity, Inc. -# -# Distributed under the Apache License, Version 2.0. -# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html -# - -macro(configure_ccache) - find_program(CCACHE_PROGRAM ccache) - if(CCACHE_PROGRAM) - message(STATUS "Configuring ccache") - - # Set up wrapper scripts - set(C_LAUNCHER "${CCACHE_PROGRAM}") - set(CXX_LAUNCHER "${CCACHE_PROGRAM}") - - set(LAUNCH_C_IN "${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/launch-c.in") - set(LAUNCH_CXX_IN "${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/launch-cxx.in") - set(LAUNCH_C "${CMAKE_BINARY_DIR}/CMakeFiles/launch-c") - set(LAUNCH_CXX "${CMAKE_BINARY_DIR}/CMakeFiles/launch-cxx") - - configure_file(${LAUNCH_C_IN} ${LAUNCH_C}) - configure_file(${LAUNCH_CXX_IN} ${LAUNCH_CXX}) - execute_process(COMMAND chmod a+rx ${LAUNCH_C} ${LAUNCH_CXX}) - - if(CMAKE_GENERATOR STREQUAL "Xcode") - # Set Xcode project attributes to route compilation and linking - # through our scripts - set(CMAKE_XCODE_ATTRIBUTE_CC ${LAUNCH_C}) - set(CMAKE_XCODE_ATTRIBUTE_CXX ${LAUNCH_CXX}) - set(CMAKE_XCODE_ATTRIBUTE_LD ${LAUNCH_C}) - set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS ${LAUNCH_CXX}) - else() - # Support Unix Makefiles and Ninja - set(CMAKE_C_COMPILER_LAUNCHER ${LAUNCH_C}) - set(CMAKE_CXX_COMPILER_LAUNCHER ${LAUNCH_CXX}) - endif() - else() - message(WARNING "Could not find ccache") - endif() -endmacro() diff --git a/cmake/macros/TargetOpenEXR.cmake b/cmake/macros/TargetOpenEXR.cmake index 9d63ba3ef4..29a1cd77a0 100644 --- a/cmake/macros/TargetOpenEXR.cmake +++ b/cmake/macros/TargetOpenEXR.cmake @@ -14,36 +14,38 @@ macro(TARGET_OPENEXR) TMP REGEX "#define OPENEXR_VERSION_STRING.*$") string(REGEX MATCHALL "[0-9.]+" OPENEXR_VERSION ${TMP}) - + file(STRINGS ${openexr_config_file} TMP REGEX "#define OPENEXR_VERSION_MAJOR.*$") string(REGEX MATCHALL "[0-9]" OPENEXR_MAJOR_VERSION ${TMP}) - + file(STRINGS ${openexr_config_file} TMP REGEX "#define OPENEXR_VERSION_MINOR.*$") string(REGEX MATCHALL "[0-9]" OPENEXR_MINOR_VERSION ${TMP}) + else() + message(WARNING "Failed to find ${openexr_config_file}") endif() set(OPENEXR_LIBRARY_RELEASE "") set(OPENEXR_LIBRARY_DEBUG "") foreach(OPENEXR_LIB - IlmImf - IlmImfUtil - Half + OpenEXRCore + OpenEXR + OpenEXRUtil Iex - IexMath + IlmThread Imath - IlmThread) + ) # OpenEXR libraries may be suffixed with the version number, so we search # using both versioned and unversioned names. find_library(OPENEXR_${OPENEXR_LIB}_LIBRARY_RELEASE NAMES - ${OPENEXR_LIB}-${OPENEXR_MAJOR_VERSION}_${OPENEXR_MINOR_VERSION}_s + ${OPENEXR_LIB}-${OPENEXR_MAJOR_VERSION}_${OPENEXR_MINOR_VERSION} ${OPENEXR_LIB}_s PATHS ${VCPKG_INSTALL_ROOT}/lib NO_DEFAULT_PATH @@ -52,13 +54,15 @@ macro(TARGET_OPENEXR) if(OPENEXR_${OPENEXR_LIB}_LIBRARY_RELEASE) list(APPEND OPENEXR_LIBRARY_RELEASE ${OPENEXR_${OPENEXR_LIB}_LIBRARY_RELEASE}) + else() + message(WARNING "Failed to find ${OPENEXR_LIB} (release); ${OPENEXR_LIB}-${OPENEXR_MAJOR_VERSION}_${OPENEXR_MINOR_VERSION}") endif() # OpenEXR libraries may be suffixed with the version number, so we search # using both versioned and unversioned names. find_library(OPENEXR_${OPENEXR_LIB}_LIBRARY_DEBUG NAMES - ${OPENEXR_LIB}-${OPENEXR_MAJOR_VERSION}_${OPENEXR_MINOR_VERSION}_s_d + ${OPENEXR_LIB}-${OPENEXR_MAJOR_VERSION}_${OPENEXR_MINOR_VERSION}_d ${OPENEXR_LIB}_s_d PATHS ${VCPKG_INSTALL_ROOT}/debug/lib NO_DEFAULT_PATH @@ -67,10 +71,19 @@ macro(TARGET_OPENEXR) if(OPENEXR_${OPENEXR_LIB}_LIBRARY_DEBUG) list(APPEND OPENEXR_LIBRARY_DEBUG ${OPENEXR_${OPENEXR_LIB}_LIBRARY_DEBUG}) + else() + message(WARNING "Failed to find ${OPENEXR_LIB} (debug); ${OPENEXR_LIB}-${OPENEXR_MAJOR_VERSION}_${OPENEXR_MINOR_VERSION}_d") endif() endforeach(OPENEXR_LIB) select_library_configurations(OPENEXR) target_link_libraries(${TARGET_NAME} ${OPENEXR_LIBRARY}) + target_include_directories(${TARGET_NAME} PUBLIC "${VCPKG_INSTALL_ROOT}/include/Imath") + + # This prevents: + # LNK2001 unresolved external symbol imath_half_to_float_table + # + # Apparently something changed in newer versions. + target_compile_definitions(${TARGET_NAME} PUBLIC IMATH_HALF_NO_LOOKUP_TABLE) endif() endmacro() diff --git a/cmake/ports/artery-font-format/disable-checksum.patch b/cmake/ports/artery-font-format/disable-checksum.patch new file mode 100644 index 0000000000..cd5133ea9e --- /dev/null +++ b/cmake/ports/artery-font-format/disable-checksum.patch @@ -0,0 +1,38 @@ +diff --git a/artery-font/serialization.hpp b/artery-font/serialization.hpp +index 69263a8..6075eda 100644 +--- a/artery-font/serialization.hpp ++++ b/artery-font/serialization.hpp +@@ -109,15 +109,16 @@ template class LIST, clas + bool decode(ArteryFont &font, void *userData) { + uint32 totalLength = 0; + uint32 prevLength = 0; +- uint32 checksum = crc32Init(); ++ //uint32 checksum = crc32Init(); + byte dump[4]; + #define ARTERY_FONT_DECODE_READ(target, len) { \ + if (READ((target), (len), userData) != int(len)) \ + return false; \ + totalLength += (len); \ +- for (int _i = 0; _i < int(len); ++_i) \ +- checksum = crc32Update(checksum, reinterpret_cast(target)[_i]); \ + } ++ // for (int _i = 0; _i < int(len); ++_i) \ ++ // checksum = crc32update(checksum, reinterpret_cast(target)[_i]); \ ++ //} + #define ARTERY_FONT_DECODE_REALIGN() { \ + if (totalLength&0x03u) { \ + uint32 len = 0x04u-(totalLength&0x03u); \ +@@ -228,10 +229,10 @@ bool decode(ArteryFont &font, void *userData) { + ARTERY_FONT_DECODE_READ(&footer, sizeof(footer)-sizeof(footer.checksum)); + if (footer.magicNo != ARTERY_FONT_FOOTER_MAGIC_NO) + return false; +- uint32 finalChecksum = checksum; ++ //uint32 finalChecksum = checksum; + ARTERY_FONT_DECODE_READ(&footer.checksum, sizeof(footer.checksum)); +- if (footer.checksum != finalChecksum) +- return false; ++ //if (footer.checksum != finalChecksum) ++ // return false; + if (totalLength != footer.totalLength) + return false; + } diff --git a/cmake/ports/artery-font-format/portfile.cmake b/cmake/ports/artery-font-format/portfile.cmake new file mode 100644 index 0000000000..4b2491ab67 --- /dev/null +++ b/cmake/ports/artery-font-format/portfile.cmake @@ -0,0 +1,15 @@ +# header-only library + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO Chlumsky/artery-font-format + REF 34134bde3cea35a93c2ae5703fa8d3d463793400 + SHA512 6b2fc0de9ca7b367c9b98f829ce6cee858f1252b12a49b6f1e89a5a2fdb109e20ef812f0b30495195ca0b177adae32d5e238fdc305724857ced098be2d29a6af + HEAD_REF master + PATCHES "disable-checksum.patch" +) + +file(COPY "${SOURCE_PATH}/artery-font" DESTINATION "${CURRENT_PACKAGES_DIR}/include") + +# Handle copyright +configure_file("${SOURCE_PATH}/LICENSE.txt" "${CURRENT_PACKAGES_DIR}/share/${PORT}/copyright" COPYONLY) \ No newline at end of file diff --git a/cmake/ports/artery-font-format/vcpkg.json b/cmake/ports/artery-font-format/vcpkg.json new file mode 100644 index 0000000000..9bfcddcb24 --- /dev/null +++ b/cmake/ports/artery-font-format/vcpkg.json @@ -0,0 +1,7 @@ +{ + "name": "artery-font-format", + "version": "1.0.1", + "description": "Header-only C++ library that facilitates encoding and decoding of the Artery Atlas Font format", + "homepage": "https://github.com/Chlumsky/artery-font-format", + "license": "MIT" +} \ No newline at end of file diff --git a/cmake/ports/cgltf/portfile.cmake b/cmake/ports/cgltf/portfile.cmake new file mode 100644 index 0000000000..95cc008a20 --- /dev/null +++ b/cmake/ports/cgltf/portfile.cmake @@ -0,0 +1,15 @@ +# header-only library + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO jkuhlmann/cgltf + REF de399881c65c438a635627c749440eeea7e05599 + SHA512 753923116b92642848ff2bda70695ddd0e7be6db43ed3cfc37aff4cba90a29a92e3dbda139a5f2c80cad1d2cdaf81e1383e4ea7a12195f61fe8cfeb105e53ea2 + HEAD_REF master +) + +file(COPY "${SOURCE_PATH}/cgltf.h" DESTINATION "${CURRENT_PACKAGES_DIR}/include") +file(COPY "${SOURCE_PATH}/cgltf_write.h" DESTINATION "${CURRENT_PACKAGES_DIR}/include") + +# Handle copyright +configure_file("${SOURCE_PATH}/LICENSE" "${CURRENT_PACKAGES_DIR}/share/${PORT}/copyright" COPYONLY) \ No newline at end of file diff --git a/cmake/ports/cgltf/vcpkg.json b/cmake/ports/cgltf/vcpkg.json new file mode 100644 index 0000000000..a57db71cb4 --- /dev/null +++ b/cmake/ports/cgltf/vcpkg.json @@ -0,0 +1,7 @@ +{ + "name": "cgltf", + "version": "1.13", + "description": "Single-file glTF 2.0 loader and writer written in C99", + "homepage": "https://github.com/jkuhlmann/cgltf", + "license": "MIT" +} \ No newline at end of file diff --git a/cmake/ports/hifi-deps/CONTROL b/cmake/ports/hifi-deps/CONTROL index ee9f4cf1b3..a904618ee6 100644 --- a/cmake/ports/hifi-deps/CONTROL +++ b/cmake/ports/hifi-deps/CONTROL @@ -5,4 +5,4 @@ Source: hifi-deps Version: 0.1.5-github-actions Description: Collected dependencies for High Fidelity applications -Build-Depends: bullet3, draco, etc2comp, glad, glm, node, nvtt, openexr (!android), openssl (windows), opus, polyvox, tbb (!android), vhacd, webrtc (!android|!(linux&arm)), zlib +Build-Depends: artery-font-format, bullet3, cgltf, draco, etc2comp, glad, glm, node, nvtt, openexr (!android), openssl (windows), opus, polyvox, tbb (!android), vhacd, webrtc (!android|!(linux&arm)), zlib diff --git a/cmake/ports/imath/portfile.cmake b/cmake/ports/imath/portfile.cmake new file mode 100644 index 0000000000..3f4f34ad58 --- /dev/null +++ b/cmake/ports/imath/portfile.cmake @@ -0,0 +1,25 @@ +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO AcademySoftwareFoundation/Imath + REF v3.1.9 + SHA512 ad96b2ac306fc13c01e8ea3256f885499c3f545be327feaba0f5e093b70b544bcca6f8b353fa7e35107aae515c19caced44331a95d0414f367ead4691ec73564 + HEAD_REF master +) + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + -DIMATH_INSTALL_SYM_LINK=OFF + -DBUILD_TESTING=OFF + -DIMATH_INSTALL_PKG_CONFIG=ON +) + +vcpkg_cmake_install() + +vcpkg_copy_pdbs() +vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/Imath) +vcpkg_fixup_pkgconfig() + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") + +file(INSTALL "${SOURCE_PATH}/LICENSE.md" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright) diff --git a/cmake/ports/imath/vcpkg.json b/cmake/ports/imath/vcpkg.json new file mode 100644 index 0000000000..85b1cb6915 --- /dev/null +++ b/cmake/ports/imath/vcpkg.json @@ -0,0 +1,18 @@ +{ + "name": "imath", + "version": "3.1.9", + "port-version": 1, + "description": "Imath is a C++ and Python library of 2D and 3D vector, matrix, and math operations for computer graphics.", + "homepage": "https://github.com/AcademySoftwareFoundation/Imath", + "license": "BSD-3-Clause", + "dependencies": [ + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ] +} diff --git a/cmake/ports/node/portfile.cmake b/cmake/ports/node/portfile.cmake old mode 100755 new mode 100644 index 5407a0d276..8a33927a78 --- a/cmake/ports/node/portfile.cmake +++ b/cmake/ports/node/portfile.cmake @@ -1,4 +1,4 @@ -# Copyright 2023 Overte e.V. +# Copyright 2023-2024 Overte e.V. # SPDX-License-Identifier: Apache-2.0 set(NODE_VERSION 18.14.2) @@ -28,9 +28,9 @@ else () vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO nodejs/node - REF v18.16.1 - SHA512 cd2d7871a1a2aca8d800e0a501bd2836cbce076de750dcfc0b2bbe602c8a23705154bfb12faa3ff78e25ec753f419220742228569c281fa458987fb24f6d4d09 - HEAD_REF v18.16.1 + REF v18.20.2 + SHA512 10d3637c26274677d137f76bbb648d0e7851c994634a16c89858c3a13094a0692ea2cb9a787c6463c3001abd71dab0d83123127bc305171d097c48d21d691678 + HEAD_REF v18.20.2 ) # node cannot configure out of source, which VCPKG expects. So we copy the source to the configure directory. file(COPY ${SOURCE_PATH}/ DESTINATION "${CURRENT_BUILDTREES_DIR}") diff --git a/cmake/ports/openexr/CONTROL b/cmake/ports/openexr/CONTROL deleted file mode 100644 index d59ab286e1..0000000000 --- a/cmake/ports/openexr/CONTROL +++ /dev/null @@ -1,4 +0,0 @@ -Source: openexr -Version: 2.3.0-2 -Description: OpenEXR is a high dynamic-range (HDR) image file format developed by Industrial Light & Magic for use in computer imaging applications -Build-Depends: zlib \ No newline at end of file diff --git a/cmake/ports/openexr/FindOpenEXR.cmake b/cmake/ports/openexr/FindOpenEXR.cmake deleted file mode 100644 index a381c6db9a..0000000000 --- a/cmake/ports/openexr/FindOpenEXR.cmake +++ /dev/null @@ -1,87 +0,0 @@ -include(FindPackageHandleStandardArgs) - -find_path(OpenEXR_INCLUDE_DIRS OpenEXR/OpenEXRConfig.h) -find_path(OPENEXR_INCLUDE_PATHS NAMES ImfRgbaFile.h PATH_SUFFIXES OpenEXR) - -file(STRINGS "${OpenEXR_INCLUDE_DIRS}/OpenEXR/OpenEXRConfig.h" OPENEXR_CONFIG_H) - -string(REGEX REPLACE "^.*define OPENEXR_VERSION_MAJOR ([0-9]+).*$" "\\1" OpenEXR_VERSION_MAJOR "${OPENEXR_CONFIG_H}") -string(REGEX REPLACE "^.*define OPENEXR_VERSION_MINOR ([0-9]+).*$" "\\1" OpenEXR_VERSION_MINOR "${OPENEXR_CONFIG_H}") -set(OpenEXR_LIB_SUFFIX "${OpenEXR_VERSION_MAJOR}_${OpenEXR_VERSION_MINOR}") - -include(SelectLibraryConfigurations) - -if(NOT OpenEXR_BASE_LIBRARY) - find_library(OpenEXR_BASE_LIBRARY_RELEASE NAMES IlmImf-${OpenEXR_LIB_SUFFIX}) - find_library(OpenEXR_BASE_LIBRARY_DEBUG NAMES IlmImf-${OpenEXR_LIB_SUFFIX}_d) - select_library_configurations(OpenEXR_BASE) -endif() - -if(NOT OpenEXR_UTIL_LIBRARY) - find_library(OpenEXR_UTIL_LIBRARY_RELEASE NAMES IlmImfUtil-${OpenEXR_LIB_SUFFIX}) - find_library(OpenEXR_UTIL_LIBRARY_DEBUG NAMES IlmImfUtil-${OpenEXR_LIB_SUFFIX}_d) - select_library_configurations(OpenEXR_UTIL) -endif() - -if(NOT OpenEXR_HALF_LIBRARY) - find_library(OpenEXR_HALF_LIBRARY_RELEASE NAMES Half-${OpenEXR_LIB_SUFFIX}) - find_library(OpenEXR_HALF_LIBRARY_DEBUG NAMES Half-${OpenEXR_LIB_SUFFIX}_d) - select_library_configurations(OpenEXR_HALF) -endif() - -if(NOT OpenEXR_IEX_LIBRARY) - find_library(OpenEXR_IEX_LIBRARY_RELEASE NAMES Iex-${OpenEXR_LIB_SUFFIX}) - find_library(OpenEXR_IEX_LIBRARY_DEBUG NAMES Iex-${OpenEXR_LIB_SUFFIX}_d) - select_library_configurations(OpenEXR_IEX) -endif() - -if(NOT OpenEXR_MATH_LIBRARY) - find_library(OpenEXR_MATH_LIBRARY_RELEASE NAMES Imath-${OpenEXR_LIB_SUFFIX}) - find_library(OpenEXR_MATH_LIBRARY_DEBUG NAMES Imath-${OpenEXR_LIB_SUFFIX}_d) - select_library_configurations(OpenEXR_MATH) -endif() - -if(NOT OpenEXR_THREAD_LIBRARY) - find_library(OpenEXR_THREAD_LIBRARY_RELEASE NAMES IlmThread-${OpenEXR_LIB_SUFFIX}) - find_library(OpenEXR_THREAD_LIBRARY_DEBUG NAMES IlmThread-${OpenEXR_LIB_SUFFIX}_d) - select_library_configurations(OpenEXR_THREAD) -endif() - -if(NOT OpenEXR_IEXMATH_LIBRARY) - find_library(OpenEXR_IEXMATH_LIBRARY_RELEASE NAMES IexMath-${OpenEXR_LIB_SUFFIX}) - find_library(OpenEXR_IEXMATH_LIBRARY_DEBUG NAMES IexMath-${OpenEXR_LIB_SUFFIX}d) - select_library_configurations(OpenEXR_IEXMATH) -endif() - -set(OPENEXR_HALF_LIBRARY "${OpenEXR_HALF_LIBRARY}") -set(OPENEXR_IEX_LIBRARY "${OpenEXR_IEX_LIBRARY}") -set(OPENEXR_IMATH_LIBRARY "${OpenEXR_MATH_LIBRARY}") -set(OPENEXR_ILMIMF_LIBRARY "${OpenEXR_BASE_LIBRARY}") -set(OPENEXR_ILMIMFUTIL_LIBRARY "${OpenEXR_UTIL_LIBRARY}") -set(OPENEXR_ILMTHREAD_LIBRARY "${OpenEXR_THREAD_LIBRARY}") - -set(OpenEXR_LIBRARY "${OpenEXR_BASE_LIBRARY}") - -set(OpenEXR_LIBRARIES - ${OpenEXR_LIBRARY} - ${OpenEXR_MATH_LIBRARY} - ${OpenEXR_IEXMATH_LIBRARY} - ${OpenEXR_UTIL_LIBRARY} - ${OpenEXR_HALF_LIBRARY} - ${OpenEXR_IEX_LIBRARY} - ${OpenEXR_THREAD_LIBRARY} -) - -set(OPENEXR_LIBRARIES - ${OPENEXR_HALF_LIBRARY} - ${OPENEXR_IEX_LIBRARY} - ${OPENEXR_IMATH_LIBRARY} - ${OPENEXR_ILMIMF_LIBRARY} - ${OPENEXR_ILMTHREAD_LIBRARY} -) - -FIND_PACKAGE_HANDLE_STANDARD_ARGS(OpenEXR REQUIRED_VARS OpenEXR_LIBRARIES OpenEXR_INCLUDE_DIRS) - -if(OpenEXR_FOUND) - set(OPENEXR_FOUND 1) -endif() diff --git a/cmake/ports/openexr/fix-arm64-windows-build.patch b/cmake/ports/openexr/fix-arm64-windows-build.patch new file mode 100644 index 0000000000..1d3310a8b9 --- /dev/null +++ b/cmake/ports/openexr/fix-arm64-windows-build.patch @@ -0,0 +1,13 @@ +diff --git a/src/lib/OpenEXRCore/internal_dwa_simd.h b/src/lib/OpenEXRCore/internal_dwa_simd.h +index 7b53501ac..ca69c9848 100644 +--- a/src/lib/OpenEXRCore/internal_dwa_simd.h ++++ b/src/lib/OpenEXRCore/internal_dwa_simd.h +@@ -18,7 +18,7 @@ + // aligned. Unaligned pointers may risk seg-faulting. + // + +-#if defined __SSE2__ || (_MSC_VER >= 1300 && !_M_CEE_PURE) ++#if defined __SSE2__ || (_MSC_VER >= 1300 && (_M_IX86 || _M_X64) && !_M_CEE_PURE) + # define IMF_HAVE_SSE2 1 + # include + # include diff --git a/cmake/ports/openexr/fix_install_ilmimf.patch b/cmake/ports/openexr/fix_install_ilmimf.patch deleted file mode 100644 index db65be7368..0000000000 --- a/cmake/ports/openexr/fix_install_ilmimf.patch +++ /dev/null @@ -1,19 +0,0 @@ -diff --git a/OpenEXR/IlmImf/CMakeLists.txt b/OpenEXR/IlmImf/CMakeLists.txt -index e1a8740..d31cf68 100644 ---- a/OpenEXR/IlmImf/CMakeLists.txt -+++ b/OpenEXR/IlmImf/CMakeLists.txt -@@ -2,14 +2,6 @@ - - SET(CMAKE_INCLUDE_CURRENT_DIR 1) - --IF (WIN32) -- SET(RUNTIME_DIR ${OPENEXR_PACKAGE_PREFIX}/bin) -- SET(WORKING_DIR ${RUNTIME_DIR}) --ELSE () -- SET(RUNTIME_DIR ${OPENEXR_PACKAGE_PREFIX}/lib) -- SET(WORKING_DIR .) --ENDIF () -- - SET(BUILD_B44EXPLOGTABLE OFF) - IF (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/b44ExpLogTable.h") - SET(BUILD_B44EXPLOGTABLE ON) diff --git a/cmake/ports/openexr/portfile.cmake b/cmake/ports/openexr/portfile.cmake index 6e773434e8..8ffa6c76bb 100644 --- a/cmake/ports/openexr/portfile.cmake +++ b/cmake/ports/openexr/portfile.cmake @@ -1,71 +1,46 @@ -set(OPENEXR_VERSION 2.3.0) -set(OPENEXR_HASH 268ae64b40d21d662f405fba97c307dad1456b7d996a447aadafd41b640ca736d4851d9544b4741a94e7b7c335fe6e9d3b16180e710671abfc0c8b2740b147b2) - vcpkg_from_github( - OUT_SOURCE_PATH SOURCE_PATH - REPO openexr/openexr - REF v${OPENEXR_VERSION} - SHA512 ${OPENEXR_HASH} - HEAD_REF master - PATCHES "fix_install_ilmimf.patch" + OUT_SOURCE_PATH SOURCE_PATH + REPO AcademySoftwareFoundation/openexr + REF "v${VERSION}" + SHA512 ec60e79341695452e05f50bbcc0d55e0ce00fbb64cdec01a83911189c8643eb28a8046b14ee4230e5f438f018f2f1d0714f691983474d7979befd199f3f34758 + HEAD_REF master + PATCHES + fix-arm64-windows-build.patch # https://github.com/AcademySoftwareFoundation/openexr/pull/1447 ) -set(OPENEXR_STATIC ON) -set(OPENEXR_SHARED OFF) - -vcpkg_configure_cmake(SOURCE_PATH ${SOURCE_PATH} - PREFER_NINJA - OPTIONS - -DOPENEXR_BUILD_PYTHON_LIBS=OFF - -DOPENEXR_BUILD_VIEWERS=OFF - -DOPENEXR_RUN_FUZZ_TESTS=OFF - -DOPENEXR_BUILD_SHARED=${OPENEXR_SHARED} - -DOPENEXR_BUILD_STATIC=${OPENEXR_STATIC} - OPTIONS_DEBUG - -DILMBASE_PACKAGE_PREFIX=${CURRENT_INSTALLED_DIR}/debug - OPTIONS_RELEASE - -DILMBASE_PACKAGE_PREFIX=${CURRENT_INSTALLED_DIR}) - -vcpkg_install_cmake() - -file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) -file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/share) - -# NOTE: Only use ".exe" extension on Windows executables. -# Is there a cleaner way to do this? -if(WIN32) - set(EXECUTABLE_SUFFIX ".exe") -else() - set(EXECUTABLE_SUFFIX "") -endif() -file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrenvmap${EXECUTABLE_SUFFIX}) -file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrheader${EXECUTABLE_SUFFIX}) -file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrmakepreview${EXECUTABLE_SUFFIX}) -file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrmaketiled${EXECUTABLE_SUFFIX}) -file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrmultipart${EXECUTABLE_SUFFIX}) -file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrmultiview${EXECUTABLE_SUFFIX}) -file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrstdattr${EXECUTABLE_SUFFIX}) -file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrenvmap${EXECUTABLE_SUFFIX}) -file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrheader${EXECUTABLE_SUFFIX}) -file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrmakepreview${EXECUTABLE_SUFFIX}) -file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrmaketiled${EXECUTABLE_SUFFIX}) -file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrmultipart${EXECUTABLE_SUFFIX}) -file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrmultiview${EXECUTABLE_SUFFIX}) -file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrstdattr${EXECUTABLE_SUFFIX}) - +vcpkg_check_features(OUT_FEATURE_OPTIONS OPTIONS + FEATURES + tools OPENEXR_BUILD_TOOLS + tools OPENEXR_INSTALL_TOOLS +) +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + ${OPTIONS} + -DBUILD_TESTING=OFF + -DOPENEXR_INSTALL_EXAMPLES=OFF + -DBUILD_DOCS=OFF + OPTIONS_DEBUG + -DOPENEXR_BUILD_TOOLS=OFF + -DOPENEXR_INSTALL_TOOLS=OFF +) +vcpkg_cmake_install() vcpkg_copy_pdbs() -if (OPENEXR_STATIC) - file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/bin ${CURRENT_PACKAGES_DIR}/debug/bin) +vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/OpenEXR) +vcpkg_fixup_pkgconfig() + +if(OPENEXR_INSTALL_TOOLS) + vcpkg_copy_tools( + TOOL_NAMES exrenvmap exrheader exrinfo exrmakepreview exrmaketiled exrmultipart exrmultiview exrstdattr exr2aces + AUTO_CLEAN + ) endif() -if (VCPKG_CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(OPENEXR_PORT_DIR "openexr") -else() - set(OPENEXR_PORT_DIR "OpenEXR") -endif() +file(REMOVE_RECURSE + "${CURRENT_PACKAGES_DIR}/debug/include" + "${CURRENT_PACKAGES_DIR}/debug/share" +) -file(COPY ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/${OPENEXR_PORT_DIR}) -file(RENAME ${CURRENT_PACKAGES_DIR}/share/${OPENEXR_PORT_DIR}/LICENSE ${CURRENT_PACKAGES_DIR}/share/${OPENEXR_PORT_DIR}/copyright) - -file(COPY ${CMAKE_CURRENT_LIST_DIR}/FindOpenEXR.cmake DESTINATION ${CURRENT_PACKAGES_DIR}/share/${OPENEXR_PORT_DIR}) +file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") +file(INSTALL "${SOURCE_PATH}/LICENSE.md" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright) diff --git a/cmake/ports/openexr/usage b/cmake/ports/openexr/usage new file mode 100644 index 0000000000..6b09d9db5f --- /dev/null +++ b/cmake/ports/openexr/usage @@ -0,0 +1,4 @@ +openexr provides CMake targets: + + find_package(OpenEXR CONFIG REQUIRED) + target_link_libraries(main PRIVATE OpenEXR::OpenEXR) diff --git a/cmake/ports/openexr/vcpkg.json b/cmake/ports/openexr/vcpkg.json new file mode 100644 index 0000000000..7f35bfac69 --- /dev/null +++ b/cmake/ports/openexr/vcpkg.json @@ -0,0 +1,25 @@ +{ + "name": "openexr", + "version": "3.1.8", + "description": "OpenEXR is a high dynamic-range (HDR) image file format developed by Industrial Light & Magic for use in computer imaging applications", + "homepage": "https://www.openexr.com/", + "license": "BSD-3-Clause", + "supports": "!uwp", + "dependencies": [ + "imath", + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + }, + "zlib" + ], + "features": { + "tools": { + "description": "Build tools" + } + } +} diff --git a/cmake/templates/launch-c.in b/cmake/templates/launch-c.in deleted file mode 100644 index 6c91d96dd9..0000000000 --- a/cmake/templates/launch-c.in +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -# Xcode generator doesn't include the compiler as the -# first argument, Ninja and Makefiles do. Handle both cases. -if [[ "$1" = "${CMAKE_C_COMPILER}" ]] ; then - shift -fi - -export CCACHE_CPP2=true -export CCACHE_HARDLINK=true -export CCACHE_SLOPPINESS=file_macro,time_macros,include_file_mtime,include_file_ctime,file_stat_matches -exec "${C_LAUNCHER}" "${CMAKE_C_COMPILER}" "$@" diff --git a/cmake/templates/launch-cxx.in b/cmake/templates/launch-cxx.in deleted file mode 100644 index 4215d89c80..0000000000 --- a/cmake/templates/launch-cxx.in +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -# Xcode generator doesn't include the compiler as the -# first argument, Ninja and Makefiles do. Handle both cases. -if [[ "$1" = "${CMAKE_CXX_COMPILER}" ]] ; then - shift -fi - -export CCACHE_CPP2=true -export CCACHE_HARDLINK=true -export CCACHE_SLOPPINESS=file_macro,time_macros,include_file_mtime,include_file_ctime,file_stat_matches -exec "${CXX_LAUNCHER}" "${CMAKE_CXX_COMPILER}" "$@" diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 58d5df5407..5b1c3482c1 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1,5 +1,5 @@ { - "version": 2.6, + "version": 2.7, "settings": [ { "name": "metaverse", @@ -402,7 +402,7 @@ }, { "label": "Permissions ?", - "span": 12 + "span": 11 } ], "columns": [ @@ -479,6 +479,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_view_asset_urls", + "label": "View Asset URLs", + "type": "checkbox", + "editable": true, + "default": false } ], "non-deletable-row-key": "permissions_id", @@ -505,6 +512,7 @@ "id_can_rez_tmp": true, "id_can_write_to_asset_server": true, "id_can_get_and_set_private_user_data": true, + "id_can_view_asset_urls": true, "permissions_id": "localhost" }, { @@ -633,6 +641,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_view_asset_urls", + "label": "View Asset URLs", + "type": "checkbox", + "editable": true, + "default": false } ] }, @@ -752,6 +767,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_view_asset_urls", + "label": "View Asset URLs", + "type": "checkbox", + "editable": true, + "default": false } ] }, @@ -844,6 +866,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_view_asset_urls", + "label": "View Asset URLs", + "type": "checkbox", + "editable": true, + "default": false } ] }, @@ -936,6 +965,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_view_asset_urls", + "label": "View Asset URLs", + "type": "checkbox", + "editable": true, + "default": false } ] }, @@ -1022,13 +1058,20 @@ "editable": true, "default": false }, - { - "name": "id_can_get_and_set_private_user_data", - "label": "Get and Set Private User Data", - "type": "checkbox", - "editable": true, - "default": false - } + { + "name": "id_can_get_and_set_private_user_data", + "label": "Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_view_asset_urls", + "label": "View Asset URLs", + "type": "checkbox", + "editable": true, + "default": false + } ] }, { @@ -1120,6 +1163,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_view_asset_urls", + "label": "View Asset URLs", + "type": "checkbox", + "editable": true, + "default": false } ] }, diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index cc7451e3f2..d3792cf36e 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -601,7 +601,7 @@ $(document).ready(function(){ form += ''; form += ' Edit'; form += ''; - form += '
This defines how nodes will connect to your domain. You can read more about automatic networking here.
'; + form += '
This defines how nodes will connect to your domain. Since the displayed setting is read back from directory server, it takes some time to update after being edited. You can read more about automatic networking here.
'; form += ''; form = $(form); @@ -664,7 +664,13 @@ $(document).ready(function(){ success: function(xhr) { console.log(xhr, parseJSONResponse(xhr)); dialog.modal('hide'); - reloadDomainInfo(); + reloadDomainInfo(); // TODO: this one doesn't work since directory server still has old data + setTimeout(function() { + reloadDomainInfo(); + }, 16000); + setTimeout(function() { + reloadDomainInfo(); + }, 64000); }, error:function(xhr) { var data = parseJSONResponse(xhr); diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index a30ddd1623..ff5ae875bc 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -353,6 +353,7 @@ void DomainGatekeeper::updateNodePermissions() { userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent; userPerms.permissions |= NodePermissions::Permission::canGetAndSetPrivateUserData; userPerms.permissions |= NodePermissions::Permission::canRezAvatarEntities; + userPerms.permissions |= NodePermissions::Permission::canViewAssetURLs; } else { // at this point we don't have a sending socket for packets from this node - assume it is the active socket // or the public socket if we haven't activated a socket for the node yet @@ -446,6 +447,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent; userPerms.permissions |= NodePermissions::Permission::canGetAndSetPrivateUserData; userPerms.permissions |= NodePermissions::Permission::canRezAvatarEntities; + userPerms.permissions |= NodePermissions::Permission::canViewAssetURLs; newNode->setPermissions(userPerms); return newNode; } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index fb92ff526d..7d9456f059 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -73,9 +73,9 @@ Q_LOGGING_CATEGORY(domain_server_auth, "overte.domain_server.auth") const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token"; const QString DomainServer::REPLACEMENT_FILE_EXTENSION = ".replace"; +const QString& DOMAIN_SERVER_SETTINGS_KEY = "domain_server"; const QString PUBLIC_SOCKET_ADDRESS_KEY = "network_address"; const QString PUBLIC_SOCKET_PORT_KEY = "network_port"; -const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking"; const int MIN_PORT = 1; const int MAX_PORT = 65535; @@ -1567,18 +1567,25 @@ QJsonObject jsonForDomainSocketUpdate(const SockAddr& socket) { } void DomainServer::performIPAddressPortUpdate(const SockAddr& newPublicSockAddr) { - const QString& DOMAIN_SERVER_SETTINGS_KEY = "domain_server"; const QString& publicSocketAddress = newPublicSockAddr.getAddress().toString(); const int publicSocketPort = newPublicSockAddr.getPort(); - sendHeartbeatToMetaverse(publicSocketAddress, publicSocketPort); + if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) { + sendHeartbeatToMetaverse(publicSocketAddress, 0); + } else { + // Full automatic networking, update both port and IP address + sendHeartbeatToMetaverse(publicSocketAddress, publicSocketPort); + } QJsonObject rootObject; QJsonObject domainServerObject; domainServerObject.insert(PUBLIC_SOCKET_ADDRESS_KEY, publicSocketAddress); - domainServerObject.insert(PUBLIC_SOCKET_PORT_KEY, publicSocketPort); + if (_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) { + domainServerObject.insert(PUBLIC_SOCKET_PORT_KEY, publicSocketPort); + } rootObject.insert(DOMAIN_SERVER_SETTINGS_KEY, domainServerObject); QJsonDocument doc(rootObject); + qDebug() << "DomainServer::performIPAddressPortUpdate: " << doc; _settingsManager.recurseJSONObjectAndOverwriteSettings(rootObject, DomainSettings); } @@ -2487,6 +2494,16 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url return true; } auto domainID = domainSetting.toString(); + qDebug() << connection->parseUrlEncodedForm(); + auto parsed = connection->parseUrlEncodedForm(); + if (parsed.contains(PUBLIC_SOCKET_PORT_KEY) || parsed.contains(PUBLIC_SOCKET_ADDRESS_KEY)) { + QJsonObject domainServerObject; + domainServerObject.insert(PUBLIC_SOCKET_PORT_KEY, parsed[PUBLIC_SOCKET_PORT_KEY]); + domainServerObject.insert(PUBLIC_SOCKET_ADDRESS_KEY, parsed[PUBLIC_SOCKET_ADDRESS_KEY]); + QJsonObject rootObject; + rootObject.insert(DOMAIN_SERVER_SETTINGS_KEY, domainServerObject); + _settingsManager.recurseJSONObjectAndOverwriteSettings(rootObject, DomainSettings); + } return forwardMetaverseAPIRequest(connection, url, "/api/v1/domains/" + domainID, "domain", { }, { "network_address", "network_port", "label" }); } else if (url.path() == URI_API_PLACES) { diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index c59fc43d34..bcea7f0e01 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -547,6 +547,29 @@ void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilena // No migration needed to version 2.6. + if (oldVersion < 2.7) { + // Default values for new canViewAssetURLs permission. + unpackPermissions(); + std::list> permissionsSets{ + _standardAgentPermissions.get(), + _agentPermissions.get(), + _ipPermissions.get(), + _macPermissions.get(), + _machineFingerprintPermissions.get(), + _groupPermissions.get(), + _groupForbiddens.get() + }; + foreach (auto permissionsSet, permissionsSets) { + for (auto entry : permissionsSet) { + const auto& userKey = entry.first; + if (permissionsSet[userKey]->can(NodePermissions::Permission::canConnectToDomain)) { + permissionsSet[userKey]->set(NodePermissions::Permission::canViewAssetURLs); + } + } + } + packPermissions(); + } + // write the current description version to our settings *versionVariant = _descriptionVersion; diff --git a/hifi_qt.py b/hifi_qt.py index 254f0be1e3..ad5401aaf3 100644 --- a/hifi_qt.py +++ b/hifi_qt.py @@ -81,7 +81,9 @@ endif() qt_found = True system_qt = True - print("Using system Qt") + + if not self.args.quiet: + print("Using system Qt") elif os.getenv('OVERTE_QT_PATH', "") != "": # 2. Using an user-provided directory. @@ -92,7 +94,9 @@ endif() self.cmakePath = os.path.join(self.fullPath, 'lib', 'cmake') qt_found = True - print("Using Qt from " + self.fullPath) + + if not self.args.quiet: + print("Using Qt from " + self.fullPath) else: # 3. Using a pre-built Qt. @@ -135,7 +139,8 @@ endif() self.lockFile = os.path.join(lockDir, lockName) if qt_found: - print("Found pre-built Qt5") + if not self.args.quiet: + print("Found pre-built Qt5") return if 'Windows' == system: @@ -147,8 +152,8 @@ endif() cpu_architecture = platform.machine() if 'x86_64' == cpu_architecture: - # `major_version()` can return blank string on rolling release distros like arch - # The `or 0` conditional assignment prevents the int parsing error from hiding the useful Qt package error + # `major_version()` can return blank string on rolling release distros like arch + # The `or 0` conditional assignment prevents the int parsing error from hiding the useful Qt package error u_major = int( distro.major_version() or '0' ) if distro.id() == 'ubuntu' or distro.id() == 'linuxmint': if (distro.id() == 'ubuntu' and u_major == 20) or distro.id() == 'linuxmint' and u_major == 20: @@ -165,9 +170,7 @@ endif() if distro.id() == 'ubuntu': u_major = int( distro.major_version() ) - if u_major == 18: - self.qtUrl = 'http://motofckr9k.ddns.net/vircadia_packages/qt5-install-5.15.2-ubuntu-18.04-aarch64_test.tar.xz' - elif u_major == 20: + if u_major == 20: self.qtUrl = self.assets_url + '/dependencies/qt5/qt5-install-5.15.9-2023.05.21-kde_fb3ec282151b1ee281a24f0545a40ac6438537c2-ubuntu-20.04-aarch64.tar.xz' elif u_major > 20: self.__no_qt_package_error() @@ -177,9 +180,7 @@ endif() elif distro.id() == 'debian': u_major = int( distro.major_version() ) - if u_major == 10: - self.qtUrl = 'https://data.moto9000.moe/vircadia_packages/qt5-install-5.15.2-debian-10-aarch64.tar.xz' - elif u_major > 10: + if u_major > 10: self.__no_qt_package_error() else: self.__unsupported_error() diff --git a/hifi_vcpkg.py b/hifi_vcpkg.py index a45975e7a5..4dc9268980 100644 --- a/hifi_vcpkg.py +++ b/hifi_vcpkg.py @@ -13,7 +13,7 @@ from os import path print = functools.partial(print, flush=True) -# Encapsulates the vcpkg system +# Encapsulates the vcpkg system class VcpkgRepo: CMAKE_TEMPLATE = """ # this file auto-generated by hifi_vcpkg.py @@ -41,7 +41,13 @@ endif() else: self.id = hifi_utils.hashFolder(self.sourcePortsPath)[:8] self.configFilePath = os.path.join(args.build_root, 'vcpkg.cmake') - self.assets_url = self.readVar('EXTERNAL_BUILD_ASSETS') + + if args.get_vcpkg_id or args.get_vcpkg_path: + # With these arguments no assets will be downloaded, and they may be used in conditions + # where the _env hack doesn't work. + self.assets_url = "http://no_assets.invalid" + else: + self.assets_url = self.readVar('EXTERNAL_BUILD_ASSETS') # The noClean flag indicates we're doing weird dependency maintenance stuff # i.e. we've got an explicit checkout of vcpkg and we don't want the script to @@ -71,7 +77,8 @@ endif() os.makedirs(self.basePath) self.path = os.path.join(self.basePath, self.id) - print("Using vcpkg path {}".format(self.path)) + if not self.args.quiet: + print("Using vcpkg path {}".format(self.path)) lockDir, lockName = os.path.split(self.path) lockName += '.lock' if not os.path.isdir(lockDir): @@ -80,7 +87,7 @@ endif() self.lockFile = os.path.join(lockDir, lockName) self.tagFile = os.path.join(self.path, '.id') self.prebuildTagFile = os.path.join(self.path, '.prebuild') - # A format version attached to the tag file... increment when you want to force the build systems to rebuild + # A format version attached to the tag file... increment when you want to force the build systems to rebuild # without the contents of the ports changing self.version = 1 self.tagContents = "{}_{}".format(self.id, self.version) @@ -108,14 +115,14 @@ endif() elif 'Linux' == system and 'aarch64' == machine: self.exe = os.path.join(self.path, 'vcpkg') self.bootstrapCmds = [ os.path.join(self.path, 'bootstrap-vcpkg.sh'), '-disableMetrics' ] - self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/vcpkg-linux_aarch64_2022.07.25.tar.xz' - self.vcpkgHash = '7abb7aa96200e3cb5a6d0ec1c6ee63aa7886df2d1fecf8f9ee41ebe4d2cea0d4143274222c4941cb7aca61e4048229fdfe9eb2cd36dd559dd26db871a3b3ed61' + self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/vcpkg-linux_aarch64_2023.11.20.tar.xz' + self.vcpkgHash = 'f38efba40bd4b0b6df47986e373d5535d3e787e257cf19d66ee8ee00e670a6fb95b3e824020024f3edbdcf86a0548e5bbddcc0ac7bd2ff6352a245efac8402fe' self.hostTriplet = 'arm64-linux' else: self.exe = os.path.join(self.path, 'vcpkg') self.bootstrapCmds = [ os.path.join(self.path, 'bootstrap-vcpkg.sh'), '-disableMetrics' ] - self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/vcpkg-linux_amd64_2022.07.25.tar.xz' - self.vcpkgHash = '6a1ce47ef6621e699a4627e8821ad32528c82fce62a6939d35b205da2d299aaa405b5f392df4a9e5343dd6a296516e341105fbb2dd8b48864781d129d7fba10d' + self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/vcpkg-linux_amd64_2023.10.19.tar.xz' + self.vcpkgHash = '6c26ff73d6348e121cca47e90d5358587bf83ba22852acb195b76fbf0473070b24512c8fdd3216d26f03515a79c085f239272ef87c7020cc578cc79abbbd338d' self.hostTriplet = 'x64-linux' if self.args.android: @@ -188,7 +195,7 @@ endif() if not downloadVcpkg and not os.path.isfile(self.exe): print("Missing executable, boot-strapping") downloadVcpkg = True - + # Make sure we have a vcpkg executable testFile = os.path.join(self.path, '.vcpkg-root') if not downloadVcpkg and not os.path.isfile(testFile): @@ -241,7 +248,7 @@ endif() hifi_utils.downloadAndExtract(self.prebuiltArchive, self.path) self.writePrebuildTag() return - + if qt is not None: self.buildEnv['QT_CMAKE_PREFIX_PATH'] = qt @@ -327,12 +334,12 @@ endif() write_obj.write(line) else: isFileChanged = True - + if isFileChanged: shutil.move(newCmakeScript, cmakeScript) else: os.remove(newCmakeScript) - + def writeConfig(self): print("Writing cmake config to {}".format(self.configFilePath)) @@ -352,7 +359,7 @@ endif() f.write(cmakeConfig) def cleanOldBuilds(self): - # FIXME because we have the base directory, and because a build will - # update the tag file on every run, we can scan the base dir for sub directories containing + # FIXME because we have the base directory, and because a build will + # update the tag file on every run, we can scan the base dir for sub directories containing # a tag file that is older than N days, and if found, delete the directory, recovering space print("Not implemented") diff --git a/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml b/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml index 06894d9576..5bac374fb5 100644 --- a/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml +++ b/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml @@ -347,6 +347,10 @@ Flickable { text: "Real-Time" refreshRatePreset: 2 // RefreshRateProfile::REALTIME } + ListElement { + text: "Custom" + refreshRatePreset: 3 // RefreshRateProfile::CUSTOM + } } HifiControlsUit.ComboBox { @@ -362,13 +366,7 @@ Flickable { currentIndex: -1 function refreshRefreshRateDropdownDisplay() { - if (Performance.getRefreshRateProfile() === 0) { - refreshRateDropdown.currentIndex = 0; - } else if (Performance.getRefreshRateProfile() === 1) { - refreshRateDropdown.currentIndex = 1; - } else { - refreshRateDropdown.currentIndex = 2; - } + refreshRateDropdown.currentIndex = Performance.getRefreshRateProfile(); } Component.onCompleted: { @@ -382,6 +380,180 @@ Flickable { } } + ColumnLayout { + width: parent.width + Layout.topMargin: 32 + visible: refreshRateDropdown.currentIndex == 3 + + RowLayout { + Layout.margins: 8 + + HifiControlsUit.SpinBox { + id: refreshRateCustomFocusActive + decimals: 0 + width: 160 + height: 32 + suffix: " FPS" + label: "Focus Active" + realFrom: 1 + realTo: 1000 + realStepSize: 15 + realValue: 60 + colorScheme: hifi.colorSchemes.dark + property var loaded: false + + Component.onCompleted: { + realValue = Performance.getCustomRefreshRate(0) + loaded = true + } + + onRealValueChanged: { + if (loaded) { + Performance.setCustomRefreshRate(0, realValue) + } + } + } + + HifiControlsUit.SpinBox { + id: refreshRateCustomFocusInactive + decimals: 0 + width: 160 + height: 32 + suffix: " FPS" + label: "Focus Inactive" + realFrom: 1 + realTo: 1000 + realStepSize: 15 + realValue: 60 + colorScheme: hifi.colorSchemes.dark + property var loaded: false + + Component.onCompleted: { + realValue = Performance.getCustomRefreshRate(1) + loaded = true + } + + onRealValueChanged: { + if (loaded) { + Performance.setCustomRefreshRate(1, realValue) + } + } + } + } + + RowLayout { + Layout.margins: 8 + + HifiControlsUit.SpinBox { + id: refreshRateCustomUnfocus + decimals: 0 + width: 160 + height: 32 + suffix: " FPS" + label: "Unfocus" + realFrom: 1 + realTo: 1000 + realStepSize: 15 + realValue: 60 + colorScheme: hifi.colorSchemes.dark + property var loaded: false + + Component.onCompleted: { + realValue = Performance.getCustomRefreshRate(2) + loaded = true + } + + onRealValueChanged: { + if (loaded) { + Performance.setCustomRefreshRate(2, realValue); + } + } + } + + HifiControlsUit.SpinBox { + id: refreshRateCustomMinimized + decimals: 0 + width: 160 + height: 32 + suffix: " FPS" + label: "Minimized" + realFrom: 1 + realTo: 1000 + realStepSize: 1 + realValue: 60 + colorScheme: hifi.colorSchemes.dark + property var loaded: false + + Component.onCompleted: { + realValue = Performance.getCustomRefreshRate(3) + loaded = true + } + + onRealValueChanged: { + if (loaded) { + Performance.setCustomRefreshRate(3, realValue) + } + } + } + } + + RowLayout { + Layout.margins: 8 + + HifiControlsUit.SpinBox { + id: refreshRateCustomStartup + decimals: 0 + width: 160 + height: 32 + suffix: " FPS" + label: "Startup" + realFrom: 1 + realTo: 1000 + realStepSize: 15 + realValue: 60 + colorScheme: hifi.colorSchemes.dark + property var loaded: false + + Component.onCompleted: { + realValue = Performance.getCustomRefreshRate(4) + loaded = true + } + + onRealValueChanged: { + if (loaded) { + Performance.setCustomRefreshRate(4, realValue) + } + } + } + + HifiControlsUit.SpinBox { + id: refreshRateCustomShutdown + decimals: 0 + width: 160 + height: 32 + suffix: " FPS" + label: "Shutdown" + realFrom: 1 + realTo: 1000 + realStepSize: 15 + realValue: 60 + colorScheme: hifi.colorSchemes.dark + property var loaded: false + + Component.onCompleted: { + realValue = Performance.getCustomRefreshRate(5) + loaded = true + } + + onRealValueChanged: { + if (loaded) { + Performance.setCustomRefreshRate(5, realValue) + } + } + } + } + } + Item { Layout.preferredWidth: parent.width Layout.preferredHeight: 35 diff --git a/interface/resources/qml/hifi/dialogs/security/ScriptSecurity.qml b/interface/resources/qml/hifi/dialogs/security/ScriptSecurity.qml new file mode 100644 index 0000000000..de7304b6fb --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/security/ScriptSecurity.qml @@ -0,0 +1,152 @@ +// +// ScriptPermissions.cpp +// libraries/script-engine/src/ScriptPermissions.cpp +// +// Created by dr Karol Suprynowicz on 2024/03/24. +// Copyright 2024 Overte e.V. +// +// Based on EntityScriptQMLWhitelist.qml +// Created by Kalila L. on 2019.12.05 | realities.dev | somnilibertas@gmail.com +// Copyright 2019 Kalila L. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +// Security settings for the script engines + +import Hifi 1.0 as Hifi +import QtQuick 2.8 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.12 +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as HiFiControls +import PerformanceEnums 1.0 +import "../../../windows" + + +Rectangle { + id: parentBody; + + function getWhitelistAsText() { + var whitelist = Settings.getValue("private/scriptPermissionGetAvatarURLSafeURLs"); + var arrayWhitelist = whitelist.replace(",", "\n"); + return arrayWhitelist; + } + + function setWhitelistAsText(whitelistText) { + Settings.setValue("private/scriptPermissionGetAvatarURLSafeURLs", whitelistText.text); + notificationText.text = "Whitelist saved."; + } + + function setAvatarProtection(enabled) { + Settings.setValue("private/scriptPermissionGetAvatarURLEnable", enabled); + console.info("Setting Protect Avatar URLs to:", enabled); + } + + anchors.fill: parent + width: parent.width; + height: 120; + color: "#80010203"; + + HifiStylesUit.RalewayRegular { + id: titleText; + text: "Protect Avatar URLs" + // Text size + size: 24; + // Style + color: "white"; + elide: Text.ElideRight; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 60; + + CheckBox { + id: whitelistEnabled; + + checked: Settings.getValue("private/scriptPermissionGetAvatarURLEnable", true); + + anchors.right: parent.right; + anchors.top: parent.top; + anchors.topMargin: 10; + onToggled: { + setAvatarProtection(whitelistEnabled.checked) + } + + Label { + text: "Enabled" + color: "white" + font.pixelSize: 18; + anchors.right: parent.left; + anchors.top: parent.top; + anchors.topMargin: 10; + } + } + } + + Rectangle { + id: textAreaRectangle; + color: "black"; + width: parent.width; + height: 250; + anchors.top: titleText.bottom; + + ScrollView { + id: textAreaScrollView + anchors.fill: parent; + width: parent.width + height: parent.height + contentWidth: parent.width + contentHeight: parent.height + clip: false; + + TextArea { + id: whitelistTextArea + text: getWhitelistAsText(); + onTextChanged: notificationText.text = ""; + width: parent.width; + height: parent.height; + font.family: "Ubuntu"; + font.pointSize: 12; + color: "white"; + } + } + + Button { + id: saveChanges + anchors.topMargin: 5; + anchors.leftMargin: 20; + anchors.rightMargin: 20; + x: textAreaRectangle.x + textAreaRectangle.width - width - 15; + y: textAreaRectangle.y + textAreaRectangle.height - height; + contentItem: Text { + text: saveChanges.text + font.family: "Ubuntu"; + font.pointSize: 12; + opacity: enabled ? 1.0 : 0.3 + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + text: "Save Changes" + onClicked: setWhitelistAsText(whitelistTextArea) + + HifiStylesUit.RalewayRegular { + id: notificationText; + text: "" + // Text size + size: 16; + // Style + color: "white"; + elide: Text.ElideLeft; + // Anchors + anchors.right: parent.left; + anchors.rightMargin: 10; + } + } + } +} diff --git a/interface/resources/qml/hifi/simplifiedUI/helpApp/faq/HelpFAQ.qml b/interface/resources/qml/hifi/simplifiedUI/helpApp/faq/HelpFAQ.qml index 7bfb711e29..6272598e29 100644 --- a/interface/resources/qml/hifi/simplifiedUI/helpApp/faq/HelpFAQ.qml +++ b/interface/resources/qml/hifi/simplifiedUI/helpApp/faq/HelpFAQ.qml @@ -3,6 +3,7 @@ // // Created by Zach Fox on 2019-08-08 // Copyright 2019 High Fidelity, Inc. +// Copyright 2024 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 @@ -78,7 +79,7 @@ Item { temporaryText: "Viewing!" onClicked: { - Qt.openUrlExternally("https://www.highfidelity.com/knowledge"); + Qt.openUrlExternally("https://overte.org/"); } } diff --git a/interface/resources/qml/hifi/simplifiedUI/helpApp/support/HelpSupport.qml b/interface/resources/qml/hifi/simplifiedUI/helpApp/support/HelpSupport.qml index 156e5cf5fd..8d294be95d 100644 --- a/interface/resources/qml/hifi/simplifiedUI/helpApp/support/HelpSupport.qml +++ b/interface/resources/qml/hifi/simplifiedUI/helpApp/support/HelpSupport.qml @@ -3,6 +3,7 @@ // // Created by Zach Fox on 2019-08-20 // Copyright 2019 High Fidelity, Inc. +// Copyright 2024 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 @@ -76,7 +77,7 @@ Item { temporaryText: "Opening browser..." onClicked: { - Qt.openUrlExternally("https://www.highfidelity.com/knowledge/kb-tickets/new"); + Qt.openUrlExternally("https://overte.org/contact.html"); } } } diff --git a/interface/resources/qml/hifi/simplifiedUI/settingsApp/dev/Dev.qml b/interface/resources/qml/hifi/simplifiedUI/settingsApp/dev/Dev.qml index 359b1bb670..68a66c11c6 100644 --- a/interface/resources/qml/hifi/simplifiedUI/settingsApp/dev/Dev.qml +++ b/interface/resources/qml/hifi/simplifiedUI/settingsApp/dev/Dev.qml @@ -3,6 +3,7 @@ // // Created by Zach Fox on 2019-06-11 // Copyright 2019 High Fidelity, Inc. +// Copyright 2024 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 @@ -93,9 +94,9 @@ Flickable { width: parent.width height: 18 labelTextOn: "Keep Old Menus (File, Edit, etc)" - checked: Settings.getValue("simplifiedUI/keepMenus", false); + checked: Settings.getValue("simplifiedUI/keepMenus", true); onClicked: { - Settings.setValue("simplifiedUI/keepMenus", !Settings.getValue("simplifiedUI/keepMenus", false)); + Settings.setValue("simplifiedUI/keepMenus", !Settings.getValue("simplifiedUI/keepMenus", true)); } } diff --git a/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml b/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml index a61fd68239..db65d8868a 100644 --- a/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml +++ b/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml @@ -3,6 +3,7 @@ // // Created by Zach Fox on 2019-05-06 // Copyright 2019 High Fidelity, Inc. +// Copyright 2024 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 @@ -175,7 +176,7 @@ Flickable { spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons SimplifiedControls.RadioButton { - id: performanceLow + id: performanceLowPower text: "Low Power Quality" + (PlatformInfo.getTierProfiled() === PerformanceEnums.LOW_POWER ? " (Recommended)" : "") checked: Performance.getPerformancePreset() === PerformanceEnums.LOW_POWER onClicked: { diff --git a/interface/resources/qml/hifi/simplifiedUI/simplifiedControls/Button.qml b/interface/resources/qml/hifi/simplifiedUI/simplifiedControls/Button.qml index e4a1c2af08..fe2a6b83eb 100644 --- a/interface/resources/qml/hifi/simplifiedUI/simplifiedControls/Button.qml +++ b/interface/resources/qml/hifi/simplifiedUI/simplifiedControls/Button.qml @@ -3,6 +3,7 @@ // // Created by Zach Fox on 2019-05-08 // Copyright 2019 High Fidelity, Inc. +// Copyright 2024 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 @@ -96,12 +97,11 @@ Original.Button { } } - contentItem: HifiStylesUit.FiraSansMedium { + contentItem: Text { id: buttonText //topPadding: -2 // Necessary for proper alignment using Graphik Medium wrapMode: Text.Wrap color: enabled ? simplifiedUI.colors.controls.button.text.enabled : simplifiedUI.colors.controls.button.text.disabled - size: simplifiedUI.sizes.controls.button.textSize verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter text: root.text diff --git a/interface/resources/qml/hifi/tablet/ControllerSettings.qml b/interface/resources/qml/hifi/tablet/ControllerSettings.qml index 492ec265d4..0115961d02 100644 --- a/interface/resources/qml/hifi/tablet/ControllerSettings.qml +++ b/interface/resources/qml/hifi/tablet/ControllerSettings.qml @@ -27,7 +27,7 @@ Item { width: parent.width property string title: "Controls" - property var openVRDevices: ["HTC Vive", "Valve Index", "Valve HMD", "Valve", "WindowsMR"] + property var openVRDevices: ["HTC Vive", "Valve Index", "Valve HMD", "Valve", "WindowsMR", "Oculus"] HifiConstants { id: hifi } diff --git a/interface/resources/serverless/Scripts/activator-doppleganger.js b/interface/resources/serverless/Scripts/activator-doppleganger.js index 89661683d3..3e39ea3f62 100644 --- a/interface/resources/serverless/Scripts/activator-doppleganger.js +++ b/interface/resources/serverless/Scripts/activator-doppleganger.js @@ -64,7 +64,7 @@ function startDopplegangerShow(entityID) { var properties = Entities.getEntityProperties(entityID, ["position", "rotation"]); - var avatarPosition = MyAvatar.position; + var avatarPosition = MyAvatar.feetPosition; var drawPosition = { "x": properties.position.x, "y": avatarPosition.y, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 931c41703b..e756ca276a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -724,8 +724,8 @@ extern DisplayPluginList getDisplayPlugins(); extern InputPluginList getInputPlugins(); extern void saveInputPluginSettings(const InputPluginList& plugins); -bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, bool runningMarkerExisted) { - qInstallMessageHandler(messageHandler); +bool setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted) { + const int listenPort = parser.isSet("listenPort") ? parser.value("listenPort").toInt() : INVALID_PORT; @@ -743,6 +743,7 @@ bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, b bool previousSessionCrashed { false }; if (!inTestMode) { + // TODO: FIX previousSessionCrashed = CrashRecoveryHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt); } @@ -763,13 +764,12 @@ bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, b } } - // Tell the plugin manager about our statically linked plugins + + DependencyManager::set(); - DependencyManager::set(); + + // Tell the plugin manager about our statically linked plugins auto pluginManager = PluginManager::getInstance(); - pluginManager->setInputPluginProvider([] { return getInputPlugins(); }); - pluginManager->setDisplayPluginProvider([] { return getDisplayPlugins(); }); - pluginManager->setInputPluginSettingsPersister([](const InputPluginList& plugins) { saveInputPluginSettings(plugins); }); if (auto steamClient = pluginManager->getSteamClientPlugin()) { steamClient->init(); } @@ -777,6 +777,7 @@ bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, b oculusPlatform->init(); } + PROFILE_SET_THREAD_NAME("Main Thread"); #if defined(Q_OS_WIN) @@ -901,7 +902,12 @@ bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, b DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); + auto scriptEngines = DependencyManager::get(); + auto entityScriptServerLog = DependencyManager::get(); + QObject::connect(scriptEngines.data(), &ScriptEngines::requestingEntityScriptServerLog, entityScriptServerLog.data(), &EntityScriptServerLogClient::requestMessagesForScriptEngines); + DependencyManager::set(); DependencyManager::set(nullptr, qApp->getOcteeSceneStats()); DependencyManager::set(); @@ -993,8 +999,7 @@ bool Application::initMenu() { Application::Application( int& argc, char** argv, const QCommandLineParser& parser, - QElapsedTimer& startupTimer, - bool runningMarkerExisted + QElapsedTimer& startupTimer ) : QApplication(argc, argv), _window(new MainWindow(desktop())), @@ -1004,10 +1009,7 @@ Application::Application( #ifndef Q_OS_ANDROID _logger(new FileLogger(this)), #endif - _previousSessionCrashed(setupEssentials(argc, argv, parser, runningMarkerExisted)), - _entitySimulation(std::make_shared()), - _physicsEngine(std::make_shared(Vectors::ZERO)), - _entityClipboard(std::make_shared()), + _previousSessionCrashed(false), //setupEssentials(parser, false)), _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT), @@ -1032,12 +1034,72 @@ Application::Application( _snapshotSound(nullptr), _sampleSound(nullptr) { - auto steamClient = PluginManager::getInstance()->getSteamClientPlugin(); - setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning())); setProperty(hifi::properties::CRASHED, _previousSessionCrashed); LogHandler::getInstance().moveToThread(thread()); LogHandler::getInstance().setupRepeatedMessageFlusher(); + qInstallMessageHandler(messageHandler); + + DependencyManager::set(); +} + +void Application::initializePluginManager(const QCommandLineParser& parser) { + DependencyManager::set(); + auto pluginManager = PluginManager::getInstance(); + + // To avoid any confusion: the getInputPlugins and getDisplayPlugins are not the ones + // from PluginManager, but functions exported by input-plugins/InputPlugin.cpp and + // display-plugins/DisplayPlugin.cpp. + // + // These functions provide the plugin manager with static default plugins. + pluginManager->setInputPluginProvider([] { return getInputPlugins(); }); + pluginManager->setDisplayPluginProvider([] { return getDisplayPlugins(); }); + pluginManager->setInputPluginSettingsPersister([](const InputPluginList& plugins) { saveInputPluginSettings(plugins); }); + + + // This must be a member function -- PluginManager must exist, and for that + // QApplication must exist, or it can't find the plugin path, as QCoreApplication:applicationDirPath + // won't work yet. + + if (parser.isSet("display")) { + auto preferredDisplays = parser.value("display").split(',', Qt::SkipEmptyParts); + qInfo() << "Setting prefered display plugins:" << preferredDisplays; + PluginManager::getInstance()->setPreferredDisplayPlugins(preferredDisplays); + } + + if (parser.isSet("disableDisplayPlugins")) { + auto disabledDisplays = parser.value("disableDisplayPlugins").split(',', Qt::SkipEmptyParts); + qInfo() << "Disabling following display plugins:" << disabledDisplays; + PluginManager::getInstance()->disableDisplays(disabledDisplays); + } + + if (parser.isSet("disableInputPlugins")) { + auto disabledInputs = parser.value("disableInputPlugins").split(',', Qt::SkipEmptyParts); + qInfo() << "Disabling following input plugins:" << disabledInputs; + PluginManager::getInstance()->disableInputs(disabledInputs); + } + +} + +void Application::initialize(const QCommandLineParser &parser) { + + //qCDebug(interfaceapp) << "Setting up essentials"; + setupEssentials(parser, _previousSessionCrashed); + qCDebug(interfaceapp) << "Initializing application"; + + _entitySimulation = std::make_shared(); + _physicsEngine = std::make_shared(Vectors::ZERO); + _entityClipboard = std::make_shared(); + _octreeProcessor = std::make_shared(); + _entityEditSender = std::make_shared(); + _graphicsEngine = std::make_shared(); + _applicationOverlay = std::make_shared(); + + + + auto steamClient = PluginManager::getInstance()->getSteamClientPlugin(); + setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning())); + { if (parser.isSet("testScript")) { @@ -1405,7 +1467,7 @@ Application::Application( connect(myAvatar.get(), &MyAvatar::positionGoneTo, this, [this] { if (!_physicsEnabled) { // when we arrive somewhere without physics enabled --> startSafeLanding - _octreeProcessor.startSafeLanding(); + _octreeProcessor->startSafeLanding(); } }, Qt::QueuedConnection); @@ -1578,9 +1640,9 @@ Application::Application( qCDebug(interfaceapp, "init() complete."); // create thread for parsing of octree data independent of the main network and rendering threads - _octreeProcessor.initialize(_enableProcessOctreeThread); - connect(&_octreeProcessor, &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); - _entityEditSender.initialize(_enableProcessOctreeThread); + _octreeProcessor->initialize(_enableProcessOctreeThread); + connect(_octreeProcessor.get(), &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); + _entityEditSender->initialize(_enableProcessOctreeThread); _idleLoopStdev.reset(); @@ -1698,7 +1760,7 @@ Application::Application( userActivityLogger.logAction("launch", properties); } - _entityEditSender.setMyAvatar(myAvatar.get()); + _entityEditSender->setMyAvatar(myAvatar.get()); // The entity octree will have to know about MyAvatar for the parentJointName import getEntities()->getTree()->setMyAvatar(myAvatar); @@ -1707,7 +1769,7 @@ Application::Application( // For now we're going to set the PPS for outbound packets to be super high, this is // probably not the right long term solution. But for now, we're going to do this to // allow you to move an entity around in your hand - _entityEditSender.setPacketsPerSecond(3000); // super high!! + _entityEditSender->setPacketsPerSecond(3000); // super high!! // Make sure we don't time out during slow operations at startup updateHeartbeat(); @@ -2375,7 +2437,7 @@ Application::Application( connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); connect(_window, SIGNAL(windowMinimizedChanged(bool)), this, SLOT(windowMinimizedChanged(bool))); - qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); + qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)_sessionRunTimer.elapsed() / 1000.0); EntityTreeRenderer::setEntitiesShouldFadeFunction([this]() { SharedNodePointer entityServerNode = DependencyManager::get()->soloNodeOfType(NodeType::EntityServer); @@ -2573,7 +2635,7 @@ Application::Application( } _pendingIdleEvent = false; - _graphicsEngine.startup(); + _graphicsEngine->startup(); qCDebug(interfaceapp) << "Directory Service session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID()); @@ -2880,43 +2942,59 @@ void Application::cleanupBeforeQuit() { Application::~Application() { // remove avatars from physics engine - auto avatarManager = DependencyManager::get(); - avatarManager->clearOtherAvatars(); - auto myCharacterController = getMyAvatar()->getCharacterController(); - myCharacterController->clearDetailedMotionStates(); + if (auto avatarManager = DependencyManager::get()) { + // AvatarManager may not yet exist in case of an early exit - PhysicsEngine::Transaction transaction; - avatarManager->buildPhysicsTransaction(transaction); - _physicsEngine->processTransaction(transaction); - avatarManager->handleProcessedPhysicsTransaction(transaction); - avatarManager->deleteAllAvatars(); + avatarManager->clearOtherAvatars(); + auto myCharacterController = getMyAvatar()->getCharacterController(); + myCharacterController->clearDetailedMotionStates(); - _physicsEngine->setCharacterController(nullptr); + PhysicsEngine::Transaction transaction; + avatarManager->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + avatarManager->handleProcessedPhysicsTransaction(transaction); + avatarManager->deleteAllAvatars(); + } + + if (_physicsEngine) { + _physicsEngine->setCharacterController(nullptr); + } // the _shapeManager should have zero references _shapeManager.collectGarbage(); assert(_shapeManager.getNumShapes() == 0); - // shutdown graphics engine - _graphicsEngine.shutdown(); + if (_graphicsEngine) { + // shutdown graphics engine + _graphicsEngine->shutdown(); + } _gameWorkload.shutdown(); DependencyManager::destroy(); PlatformHelper::shutdown(); - _entityClipboard->eraseAllOctreeElements(); - _entityClipboard.reset(); - - _octreeProcessor.terminate(); - _entityEditSender.terminate(); - - if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { - steamClient->shutdown(); + if (_entityClipboard) { + _entityClipboard->eraseAllOctreeElements(); + _entityClipboard.reset(); } - if (auto oculusPlatform = PluginManager::getInstance()->getOculusPlatformPlugin()) { - oculusPlatform->shutdown(); + if (_octreeProcessor) { + _octreeProcessor->terminate(); + } + + if (_entityEditSender) { + _entityEditSender->terminate(); + } + + if (auto pluginManager = PluginManager::getInstance()) { + if (auto steamClient = pluginManager->getSteamClientPlugin()) { + steamClient->shutdown(); + } + + if (auto oculusPlatform = pluginManager->getOculusPlatformPlugin()) { + oculusPlatform->shutdown(); + } } DependencyManager::destroy(); @@ -2944,7 +3022,9 @@ Application::~Application() { DependencyManager::destroy(); DependencyManager::destroy(); - DependencyManager::get()->cleanup(); + if (auto resourceManager = DependencyManager::get()) { + resourceManager->cleanup(); + } // remove the NodeList from the DependencyManager DependencyManager::destroy(); @@ -2958,13 +3038,14 @@ Application::~Application() { _window->deleteLater(); // make sure that the quit event has finished sending before we take the application down - auto closeEventSender = DependencyManager::get(); - while (!closeEventSender->hasFinishedQuitEvent() && !closeEventSender->hasTimedOutQuitEvent()) { - // sleep a little so we're not spinning at 100% - std::this_thread::sleep_for(std::chrono::milliseconds(10)); + if (auto closeEventSender = DependencyManager::get()) { + while (!closeEventSender->hasFinishedQuitEvent() && !closeEventSender->hasTimedOutQuitEvent()) { + // sleep a little so we're not spinning at 100% + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + // quit the thread used by the closure event sender + closeEventSender->thread()->quit(); } - // quit the thread used by the closure event sender - closeEventSender->thread()->quit(); // Can't log to file past this point, FileLogger about to be deleted qInstallMessageHandler(LogHandler::verboseMessageHandler); @@ -3104,7 +3185,7 @@ void Application::initializeGL() { glClear(GL_COLOR_BUFFER_BIT); _glWidget->swapBuffers(); - _graphicsEngine.initializeGPU(_glWidget); + _graphicsEngine->initializeGPU(_glWidget); } void Application::initializeDisplayPlugins() { @@ -3116,7 +3197,7 @@ void Application::initializeDisplayPlugins() { // Once time initialization code DisplayPluginPointer targetDisplayPlugin; for(const auto& displayPlugin : displayPlugins) { - displayPlugin->setContext(_graphicsEngine.getGPUContext()); + displayPlugin->setContext(_graphicsEngine->getGPUContext()); if (displayPlugin->getName() == lastActiveDisplayPluginName) { targetDisplayPlugin = displayPlugin; } @@ -3168,7 +3249,7 @@ void Application::initializeDisplayPlugins() { void Application::initializeRenderEngine() { // FIXME: on low end systems os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread. DeadlockWatchdogThread::withPause([&] { - _graphicsEngine.initializeRender(); + _graphicsEngine->initializeRender(); DependencyManager::get()->registerKeyboardHighlighting(); }); } @@ -3225,7 +3306,7 @@ void Application::initializeUi() { // END PULL SAFEURLS FROM INTERFACE.JSON Settings - if (AUTHORIZED_EXTERNAL_QML_SOURCE.isParentOf(url)) { + if (QUrl(NetworkingConstants::OVERTE_COMMUNITY_APPLICATIONS).isParentOf(url)) { return true; } else { for (const auto& str : safeURLS) { @@ -3425,7 +3506,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Recording", DependencyManager::get().data()); surfaceContext->setContextProperty("Preferences", DependencyManager::get().data()); surfaceContext->setContextProperty("AddressManager", DependencyManager::get().data()); - surfaceContext->setContextProperty("FrameTimings", &_graphicsEngine._frameTimingsScriptingInterface); + surfaceContext->setContextProperty("FrameTimings", &_graphicsEngine->_frameTimingsScriptingInterface); surfaceContext->setContextProperty("Rates", new RatesScriptingInterface(this)); surfaceContext->setContextProperty("TREE_SCALE", TREE_SCALE); @@ -4061,7 +4142,7 @@ std::map Application::prepareServerlessDomainContents(QUrl dom bool success = tmpTree->readFromByteArray(domainURL.toString(), data); if (success) { tmpTree->reaverageOctreeElements(); - tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), "domain", 0, 0, 0); + tmpTree->sendEntities(_entityEditSender.get(), getEntities()->getTree(), "domain", 0, 0, 0); } std::map namedPaths = tmpTree->getNamedPaths(); @@ -4131,8 +4212,8 @@ void Application::onPresent(quint32 frameCount) { postEvent(this, new QEvent((QEvent::Type)ApplicationEvent::Idle), Qt::HighEventPriority); } expected = false; - if (_graphicsEngine.checkPendingRenderEvent() && !isAboutToQuit()) { - postEvent(_graphicsEngine._renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render)); + if (_graphicsEngine->checkPendingRenderEvent() && !isAboutToQuit()) { + postEvent(_graphicsEngine->_renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render)); } } @@ -4202,7 +4283,9 @@ bool Application::handleFileOpenEvent(QFileOpenEvent* fileEvent) { } bool Application::notify(QObject * object, QEvent * event) { - if (thread() == QThread::currentThread()) { + if (thread() == QThread::currentThread() && _profilingInitialized ) { + // _profilingInitialized gets set once we're reading for profiling. + // this prevents a deadlock due to profiling not working yet PROFILE_RANGE_IF_LONGER(app, "notify", 2) return QApplication::notify(object, event); } @@ -5247,8 +5330,8 @@ void Application::idle() { PROFILE_COUNTER_IF_CHANGED(app, "pendingDownloads", uint32_t, ResourceCache::getPendingRequestCount()); PROFILE_COUNTER_IF_CHANGED(app, "currentProcessing", int, DependencyManager::get()->getStat("Processing").toInt()); PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get()->getStat("PendingProcessing").toInt()); - auto renderConfig = _graphicsEngine.getRenderEngine()->getConfiguration(); - PROFILE_COUNTER_IF_CHANGED(render, "gpuTime", float, (float)_graphicsEngine.getGPUContext()->getFrameTimerGPUAverage()); + auto renderConfig = _graphicsEngine->getRenderEngine()->getConfiguration(); + PROFILE_COUNTER_IF_CHANGED(render, "gpuTime", float, (float)_graphicsEngine->getGPUContext()->getFrameTimerGPUAverage()); PROFILE_RANGE(app, __FUNCTION__); @@ -5615,7 +5698,7 @@ bool Application::importEntities(const QString& urlOrFilename, const bool isObse } QVector Application::pasteEntities(const QString& entityHostType, float x, float y, float z) { - return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), entityHostType, x, y, z); + return _entityClipboard->sendEntities(_entityEditSender.get(), getEntities()->getTree(), entityHostType, x, y, z); } void Application::init() { @@ -5665,7 +5748,7 @@ void Application::init() { _physicsEngine->init(); EntityTreePointer tree = getEntities()->getTree(); - _entitySimulation->init(tree, _physicsEngine, &_entityEditSender); + _entitySimulation->init(tree, _physicsEngine, _entityEditSender.get()); tree->setSimulation(_entitySimulation); auto entityScriptingInterface = DependencyManager::get(); @@ -5689,7 +5772,7 @@ void Application::init() { } }, Qt::QueuedConnection); - _gameWorkload.startup(getEntities()->getWorkloadSpace(), _graphicsEngine.getRenderScene(), _entitySimulation); + _gameWorkload.startup(getEntities()->getWorkloadSpace(), _graphicsEngine->getRenderScene(), _entitySimulation); _entitySimulation->setWorkloadSpace(getEntities()->getWorkloadSpace()); } @@ -5861,7 +5944,7 @@ void Application::updateLOD(float deltaTime) const { // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode if (!isThrottleRendering()) { float presentTime = getActiveDisplayPlugin()->getAveragePresentTime(); - float engineRunTime = (float)(_graphicsEngine.getRenderEngine()->getConfiguration().get()->getCPURunTime()); + float engineRunTime = (float)(_graphicsEngine->getRenderEngine()->getConfiguration().get()->getCPURunTime()); float gpuTime = getGPUContext()->getFrameTimerGPUAverage(); float batchTime = getGPUContext()->getFrameTimerBatchAverage(); auto lodManager = DependencyManager::get(); @@ -5897,8 +5980,8 @@ void Application::updateThreads(float deltaTime) { // parse voxel packets if (!_enableProcessOctreeThread) { - _octreeProcessor.threadRoutine(); - _entityEditSender.threadRoutine(); + _octreeProcessor->threadRoutine(); + _entityEditSender->threadRoutine(); } } @@ -6021,7 +6104,7 @@ void Application::resetPhysicsReadyInformation() { _gpuTextureMemSizeStabilityCount = 0; _gpuTextureMemSizeAtLastCheck = 0; _physicsEnabled = false; - _octreeProcessor.stopSafeLanding(); + _octreeProcessor->stopSafeLanding(); } void Application::reloadResourceCaches() { @@ -6166,7 +6249,7 @@ void Application::updateSecondaryCameraViewFrustum() { // camera should be. // Code based on SecondaryCameraJob - auto renderConfig = _graphicsEngine.getRenderEngine()->getConfiguration(); + auto renderConfig = _graphicsEngine->getRenderEngine()->getConfiguration(); assert(renderConfig); auto camera = dynamic_cast(renderConfig->getConfig("SecondaryCamera")); @@ -6284,7 +6367,7 @@ void Application::tryToEnablePhysics() { auto myAvatar = getMyAvatar(); if (myAvatar->isReadyForPhysics()) { myAvatar->getCharacterController()->setPhysicsEngine(_physicsEngine); - _octreeProcessor.resetSafeLanding(); + _octreeProcessor->resetSafeLanding(); _physicsEnabled = true; setIsInterstitialMode(false); myAvatar->updateMotionBehaviorFromMenu(); @@ -6293,7 +6376,7 @@ void Application::tryToEnablePhysics() { } void Application::update(float deltaTime) { - PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_graphicsEngine._renderFrameCount + 1); + PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_graphicsEngine->_renderFrameCount + 1); if (_aboutToQuit) { return; @@ -6310,12 +6393,12 @@ void Application::update(float deltaTime) { if (isServerlessMode()) { tryToEnablePhysics(); } else if (_failedToConnectToEntityServer) { - if (_octreeProcessor.safeLandingIsActive()) { - _octreeProcessor.stopSafeLanding(); + if (_octreeProcessor->safeLandingIsActive()) { + _octreeProcessor->stopSafeLanding(); } } else { - _octreeProcessor.updateSafeLanding(); - if (_octreeProcessor.safeLandingIsComplete()) { + _octreeProcessor->updateSafeLanding(); + if (_octreeProcessor->safeLandingIsComplete()) { tryToEnablePhysics(); } } @@ -6802,7 +6885,7 @@ void Application::update(float deltaTime) { } void Application::updateRenderArgs(float deltaTime) { - _graphicsEngine.editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) { + _graphicsEngine->editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) { PerformanceTimer perfTimer("editRenderArgs"); appRenderArgs._headPose = getHMDSensorPose(); @@ -6831,7 +6914,7 @@ void Application::updateRenderArgs(float deltaTime) { _viewFrustum.setProjection(adjustedProjection); _viewFrustum.calculate(); } - appRenderArgs._renderArgs = RenderArgs(_graphicsEngine.getGPUContext(), lodManager->getVisibilityDistance(), + appRenderArgs._renderArgs = RenderArgs(_graphicsEngine->getGPUContext(), lodManager->getVisibilityDistance(), lodManager->getBoundaryLevelAdjust(), lodManager->getLODFarHalfAngleTan(), lodManager->getLODNearHalfAngleTan(), lodManager->getLODFarDistance(), lodManager->getLODNearDistance(), RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::DEFERRED, RenderArgs::RENDER_DEBUG_NONE); @@ -6970,7 +7053,7 @@ int Application::sendNackPackets() { // if there are octree packets from this node that are waiting to be processed, // don't send a NACK since the missing packets may be among those waiting packets. - if (_octreeProcessor.hasPacketsToProcessFrom(nodeUUID)) { + if (_octreeProcessor->hasPacketsToProcessFrom(nodeUUID)) { return; } @@ -7012,7 +7095,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) { const bool isModifiedQuery = !_physicsEnabled; if (isModifiedQuery) { - if (!_octreeProcessor.safeLandingIsActive()) { + if (!_octreeProcessor->safeLandingIsActive()) { // don't send the octreeQuery until SafeLanding knows it has started return; } @@ -7281,12 +7364,12 @@ void Application::resettingDomain() { void Application::nodeAdded(SharedNodePointer node) { if (node->getType() == NodeType::EntityServer) { if (_failedToConnectToEntityServer && !_entityServerConnectionTimer.isActive()) { - _octreeProcessor.stopSafeLanding(); + _octreeProcessor->stopSafeLanding(); _failedToConnectToEntityServer = false; } else if (_entityServerConnectionTimer.isActive()) { _entityServerConnectionTimer.stop(); } - _octreeProcessor.startSafeLanding(); + _octreeProcessor->startSafeLanding(); _entityServerConnectionTimer.setInterval(ENTITY_SERVER_CONNECTION_TIMEOUT); _entityServerConnectionTimer.start(); } @@ -7358,9 +7441,9 @@ void Application::nodeKilled(SharedNodePointer node) { // OctreePacketProcessor::nodeKilled is not being called when NodeList::nodeKilled is emitted. // This may have to do with GenericThread::threadRoutine() blocking the QThread event loop - _octreeProcessor.nodeKilled(node); + _octreeProcessor->nodeKilled(node); - _entityEditSender.nodeKilled(node); + _entityEditSender->nodeKilled(node); if (node->getType() == NodeType::AudioMixer) { QMetaObject::invokeMethod(DependencyManager::get().data(), "audioMixerKilled"); @@ -7449,7 +7532,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptManagerPoint // setup the packet sender of the script engine's scripting interfaces so // we can use the same ones from the application. auto entityScriptingInterface = DependencyManager::get(); - entityScriptingInterface->setPacketSender(&_entityEditSender); + entityScriptingInterface->setPacketSender(_entityEditSender.get()); entityScriptingInterface->setEntityTree(getEntities()->getTree()); if (property(hifi::properties::TEST).isValid()) { @@ -7575,7 +7658,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptManagerPoint { auto connection = std::make_shared(); - *connection = scriptManager->connect(scriptManager.get(), &ScriptManager::scriptEnding, [scriptManager, connection]() { + *connection = scriptManager->connect(scriptManager.get(), &ScriptManager::scriptEnding, [this, scriptManager, connection]() { // Request removal of controller routes with callbacks to a given script engine auto userInputMapper = DependencyManager::get(); // scheduleScriptEndpointCleanup will have the last instance of shared pointer to script manager @@ -8215,7 +8298,7 @@ void Application::addAssetToWorldCheckModelSize() { propertyFlags += PROP_NAME; propertyFlags += PROP_DIMENSIONS; auto entityScriptingInterface = DependencyManager::get(); - auto properties = entityScriptingInterface->getEntityPropertiesInternal(entityID, propertyFlags); + auto properties = entityScriptingInterface->getEntityPropertiesInternal(entityID, propertyFlags, false); auto name = properties.getName(); auto dimensions = properties.getDimensions(); @@ -8763,26 +8846,6 @@ void Application::sendLambdaEvent(const std::function& f) { } } -void Application::initPlugins(const QCommandLineParser& parser) { - if (parser.isSet("display")) { - auto preferredDisplays = parser.value("display").split(',', Qt::SkipEmptyParts); - qInfo() << "Setting prefered display plugins:" << preferredDisplays; - PluginManager::getInstance()->setPreferredDisplayPlugins(preferredDisplays); - } - - if (parser.isSet("disable-displays")) { - auto disabledDisplays = parser.value("disable-displays").split(',', Qt::SkipEmptyParts); - qInfo() << "Disabling following display plugins:" << disabledDisplays; - PluginManager::getInstance()->disableDisplays(disabledDisplays); - } - - if (parser.isSet("disable-inputs")) { - auto disabledInputs = parser.value("disable-inputs").split(',', Qt::SkipEmptyParts); - qInfo() << "Disabling following input plugins:" << disabledInputs; - PluginManager::getInstance()->disableInputs(disabledInputs); - } -} - void Application::shutdownPlugins() { } @@ -9205,7 +9268,7 @@ void Application::updateLoginDialogPosition() { auto entityScriptingInterface = DependencyManager::get(); EntityPropertyFlags desiredProperties; desiredProperties += PROP_POSITION; - auto properties = entityScriptingInterface->getEntityPropertiesInternal(_loginDialogID, desiredProperties); + auto properties = entityScriptingInterface->getEntityPropertiesInternal(_loginDialogID, desiredProperties, false); auto positionVec = properties.getPosition(); auto cameraPositionVec = _myCamera.getPosition(); auto cameraOrientation = cancelOutRollAndPitch(_myCamera.getOrientation()); diff --git a/interface/src/Application.h b/interface/src/Application.h index 82b39e868b..63a035dd45 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -123,6 +123,31 @@ class Application : public QApplication, friend class OctreePacketProcessor; public: + + /** + * @brief Initialize the plugin manager + * + * This both does the initial startup and parses arguments. This + * is necessary because the plugin manager's options must be set + * before any usage of it is made, or they won't apply. + * + * @param parser + */ + void initializePluginManager(const QCommandLineParser& parser); + + /** + * @brief Initialize everything + * + * This is a QApplication, and for Qt reasons it's desirable to create this object + * as early as possible. Without that some Qt functions don't work, like QCoreApplication::applicationDirPath() + * + * So we keep the constructor as minimal as possible, and do the rest of the work in + * this function. + */ + void initialize(const QCommandLineParser &parser); + + void setPreviousSessionCrashed(bool value) { _previousSessionCrashed = value; } + // virtual functions required for PluginContainer virtual ui::Menu* getPrimaryMenu() override; virtual void requestReset() override { resetSensors(false); } @@ -135,15 +160,12 @@ public: virtual DisplayPluginPointer getActiveDisplayPlugin() const override; - // FIXME? Empty methods, do we still need them? - static void initPlugins(const QCommandLineParser& parser); static void shutdownPlugins(); Application( int& argc, char** argv, const QCommandLineParser& parser, - QElapsedTimer& startup_time, - bool runningMarkerExisted + QElapsedTimer& startup_time ); ~Application(); @@ -197,16 +219,16 @@ public: const ConicalViewFrustums& getConicalViews() const override { return _conicalViews; } - const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; } + const OctreePacketProcessor& getOctreePacketProcessor() const { return *_octreeProcessor; } QSharedPointer getEntities() const { return DependencyManager::get(); } MainWindow* getWindow() const { return _window; } EntityTreePointer getEntityClipboard() const { return _entityClipboard; } - EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; } + std::shared_ptr getEntityEditPacketSender() { return _entityEditSender; } ivec2 getMouse() const; - ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; } - const ApplicationOverlay& getApplicationOverlay() const { return _applicationOverlay; } + ApplicationOverlay& getApplicationOverlay() { return *_applicationOverlay; } + const ApplicationOverlay& getApplicationOverlay() const { return *_applicationOverlay; } CompositorHelper& getApplicationCompositor() const; Overlays& getOverlays() { return _overlays; } @@ -214,8 +236,8 @@ public: PerformanceManager& getPerformanceManager() { return _performanceManager; } RefreshRateManager& getRefreshRateManager() { return _refreshRateManager; } - size_t getRenderFrameCount() const { return _graphicsEngine.getRenderFrameCount(); } - float getRenderLoopRate() const { return _graphicsEngine.getRenderLoopRate(); } + size_t getRenderFrameCount() const { return _graphicsEngine->getRenderFrameCount(); } + float getRenderLoopRate() const { return _graphicsEngine->getRenderLoopRate(); } float getNumCollisionObjects() const; float getTargetRenderFrameRate() const; // frames/second @@ -293,9 +315,9 @@ public: void setMaxOctreePacketsPerSecond(int maxOctreePPS); int getMaxOctreePacketsPerSecond() const; - render::ScenePointer getMain3DScene() override { return _graphicsEngine.getRenderScene(); } - render::EnginePointer getRenderEngine() override { return _graphicsEngine.getRenderEngine(); } - gpu::ContextPointer getGPUContext() const { return _graphicsEngine.getGPUContext(); } + render::ScenePointer getMain3DScene() override { return _graphicsEngine->getRenderScene(); } + render::EnginePointer getRenderEngine() override { return _graphicsEngine->getRenderEngine(); } + gpu::ContextPointer getGPUContext() const { return _graphicsEngine->getGPUContext(); } const GameWorkload& getGameWorkload() const { return _gameWorkload; } @@ -709,8 +731,8 @@ private: bool _enableProcessOctreeThread; bool _interstitialMode { false }; - OctreePacketProcessor _octreeProcessor; - EntityEditPacketSender _entityEditSender; + std::shared_ptr _octreeProcessor; + std::shared_ptr _entityEditSender; StDev _idleLoopStdev; float _idleLoopMeasuredJitter; @@ -757,13 +779,13 @@ private: GameWorkload _gameWorkload; - GraphicsEngine _graphicsEngine; + std::shared_ptr _graphicsEngine; void updateRenderArgs(float deltaTime); bool _disableLoginScreen { true }; Overlays _overlays; - ApplicationOverlay _applicationOverlay; + std::shared_ptr _applicationOverlay; OverlayConductor _overlayConductor; DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); @@ -860,5 +882,7 @@ private: bool _crashOnShutdown { false }; DiscordPresence* _discordPresence{ nullptr }; + + bool _profilingInitialized { false }; }; #endif // hifi_Application_h diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 256ce2f6fc..461c55e64e 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -41,6 +41,15 @@ #include #include #include "WarningsSuppression.h" +#include "ScriptPermissions.h" + +QVariantMap AvatarBookmarks::getBookmarks() { + if (ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL)) { + return _bookmarks; + } else { + return {}; + } +} void addAvatarEntities(const QVariantList& avatarEntities) { auto nodeList = DependencyManager::get(); @@ -123,6 +132,12 @@ AvatarBookmarks::AvatarBookmarks() { } void AvatarBookmarks::addBookmark(const QString& bookmarkName) { + if (ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL)) { + addBookmarkInternal(bookmarkName); + } +} + +void AvatarBookmarks::addBookmarkInternal(const QString& bookmarkName) { if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(this, "addBookmark", Q_ARG(QString, bookmarkName)); return; @@ -134,6 +149,12 @@ void AvatarBookmarks::addBookmark(const QString& bookmarkName) { } void AvatarBookmarks::saveBookmark(const QString& bookmarkName) { + if (ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL)) { + saveBookmarkInternal(bookmarkName); + } +} + +void AvatarBookmarks::saveBookmarkInternal(const QString& bookmarkName) { if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(this, "saveBookmark", Q_ARG(QString, bookmarkName)); return; @@ -145,6 +166,12 @@ void AvatarBookmarks::saveBookmark(const QString& bookmarkName) { } void AvatarBookmarks::removeBookmark(const QString& bookmarkName) { + if (ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL)) { + removeBookmarkInternal(bookmarkName); + } +} + +void AvatarBookmarks::removeBookmarkInternal(const QString& bookmarkName) { if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(this, "removeBookmark", Q_ARG(QString, bookmarkName)); return; @@ -200,6 +227,12 @@ void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { */ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { + if (ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL)) { + loadBookmarkInternal(bookmarkName); + } +} + +void AvatarBookmarks::loadBookmarkInternal(const QString& bookmarkName) { if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(this, "loadBookmark", Q_ARG(QString, bookmarkName)); return; @@ -268,6 +301,15 @@ void AvatarBookmarks::readFromFile() { } QVariantMap AvatarBookmarks::getBookmark(const QString &bookmarkName) +{ + if (ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL)) { + return getBookmarkInternal(bookmarkName); + } else { + return {}; + } +} + +QVariantMap AvatarBookmarks::getBookmarkInternal(const QString &bookmarkName) { if (QThread::currentThread() != thread()) { QVariantMap result; diff --git a/interface/src/AvatarBookmarks.h b/interface/src/AvatarBookmarks.h index c2c7eb5a0a..bf06743b3f 100644 --- a/interface/src/AvatarBookmarks.h +++ b/interface/src/AvatarBookmarks.h @@ -100,7 +100,7 @@ public slots: * print("- " + key + " " + bookmarks[key].avatarUrl); * }; */ - QVariantMap getBookmarks() { return _bookmarks; } + QVariantMap getBookmarks(); signals: /*@jsdoc @@ -147,6 +147,11 @@ protected slots: void deleteBookmark() override; private: + QVariantMap getBookmarkInternal(const QString &bookmarkName); + void addBookmarkInternal(const QString& bookmarkName); + void saveBookmarkInternal(const QString& bookmarkName); + void loadBookmarkInternal(const QString& bookmarkName); + void removeBookmarkInternal(const QString& bookmarkName); const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json"; const QString ENTRY_AVATAR_URL = "avatarUrl"; const QString ENTRY_AVATAR_ICON = "avatarIcon"; diff --git a/interface/src/CrashRecoveryHandler.cpp b/interface/src/CrashRecoveryHandler.cpp index 1f6cbef9ba..c03e8bc70f 100644 --- a/interface/src/CrashRecoveryHandler.cpp +++ b/interface/src/CrashRecoveryHandler.cpp @@ -258,10 +258,11 @@ void CrashRecoveryHandler::handleCrash(CrashRecoveryHandler::Action action) { // Display name and avatar settings.beginGroup(AVATAR_GROUP); displayName = settings.value(DISPLAY_NAME_KEY).toString(); - fullAvatarURL = settings.value(FULL_AVATAR_URL_KEY).toUrl(); fullAvatarModelName = settings.value(FULL_AVATAR_MODEL_NAME_KEY).toString(); settings.endGroup(); + fullAvatarURL = settings.value(SETTINGS_FULL_PRIVATE_GROUP_NAME + "/" + AVATAR_GROUP + "/" + FULL_AVATAR_URL_KEY).toUrl(); + // Tutorial complete tutorialComplete = settings.value(TUTORIAL_COMPLETE_FLAG_KEY).toBool(); } @@ -280,12 +281,12 @@ void CrashRecoveryHandler::handleCrash(CrashRecoveryHandler::Action action) { // Display name and avatar settings.beginGroup(AVATAR_GROUP); settings.setValue(DISPLAY_NAME_KEY, displayName); - settings.setValue(FULL_AVATAR_URL_KEY, fullAvatarURL); settings.setValue(FULL_AVATAR_MODEL_NAME_KEY, fullAvatarModelName); settings.endGroup(); + settings.setValue(SETTINGS_FULL_PRIVATE_GROUP_NAME + "/" + AVATAR_GROUP + "/" + FULL_AVATAR_URL_KEY, fullAvatarURL); + // Tutorial complete settings.setValue(TUTORIAL_COMPLETE_FLAG_KEY, tutorialComplete); } } - diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index fdd1b51fb3..9512b2f758 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -49,10 +49,10 @@ LODManager::LODManager() { const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.08f; // sec // batchTIme is always contained in presentTime. -// We favor using batchTime instead of presentTime as a representative value for rendering duration (on present thread) +// We favor using batchTime instead of presentTime as a representative value for rendering duration (on present thread) // if batchTime + cushionTime < presentTime. // since we are shooting for fps around 60, 90Hz, the ideal frames are around 10ms -// so we are picking a cushion time of 3ms +// so we are picking a cushion time of 3ms const float LOD_BATCH_TO_PRESENT_CUSHION_TIME = 3.0f; // msec void LODManager::setRenderTimes(float presentTime, float engineRunTime, float batchTime, float gpuTime) { @@ -64,8 +64,8 @@ void LODManager::setRenderTimes(float presentTime, float engineRunTime, float ba } void LODManager::autoAdjustLOD(float realTimeDelta) { - std::lock_guard { _automaticLODLock }; - + std::lock_guard lock(_automaticLODLock); + // The "render time" is the worse of: // - engineRunTime: Time spent in the render thread in the engine producing the gpu::Frame N // - batchTime: Time spent in the present thread processing the batches of gpu::Frame N+1 @@ -92,7 +92,7 @@ void LODManager::autoAdjustLOD(float realTimeDelta) { float smoothBlend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE * _smoothScale) ? realTimeDelta / (LOD_ADJUST_RUNNING_AVG_TIMESCALE * _smoothScale) : 1.0f; //Evaluate the running averages for the render time - // We must sanity check for the output average evaluated to be in a valid range to avoid issues + // We must sanity check for the output average evaluated to be in a valid range to avoid issues _nowRenderTime = (1.0f - nowBlend) * _nowRenderTime + nowBlend * maxRenderTime; // msec _nowRenderTime = std::max(0.0f, std::min(_nowRenderTime, (float)MSECS_PER_SECOND)); _smoothRenderTime = (1.0f - smoothBlend) * _smoothRenderTime + smoothBlend * maxRenderTime; // msec @@ -112,7 +112,7 @@ void LODManager::autoAdjustLOD(float realTimeDelta) { // Current fps based on latest measurments float currentNowFPS = (float)MSECS_PER_SECOND / _nowRenderTime; float currentSmoothFPS = (float)MSECS_PER_SECOND / _smoothRenderTime; - + // Compute the Variance of the FPS signal (FPS - smouthFPS)^2 // Also scale it by a percentage for fine tuning (default is 100%) float currentVarianceFPS = (currentSmoothFPS - currentNowFPS); @@ -165,7 +165,7 @@ void LODManager::autoAdjustLOD(float realTimeDelta) { // Compute the output of the PID and record intermediate results for tuning _pidOutputs.x = _pidCoefs.x * error; // Kp * error - _pidOutputs.y = _pidCoefs.y * integral; // Ki * integral + _pidOutputs.y = _pidCoefs.y * integral; // Ki * integral _pidOutputs.z = _pidCoefs.z * derivative; // Kd * derivative auto output = _pidOutputs.x + _pidOutputs.y + _pidOutputs.z; @@ -300,7 +300,7 @@ void LODManager::resetLODAdjust() { } void LODManager::setAutomaticLODAdjust(bool value) { - std::lock_guard { _automaticLODLock }; + std::lock_guard lock(_automaticLODLock); _automaticLODAdjust = value; saveSettings(); emit autoLODChanged(); @@ -399,7 +399,7 @@ void LODManager::loadSettings() { if (qApp->property(hifi::properties::OCULUS_STORE).toBool() && firstRun.get()) { hmdQuality = WORLD_DETAIL_HIGH; } - + _automaticLODAdjust = automaticLODAdjust.get(); _lodHalfAngle = lodHalfAngle.get(); @@ -457,7 +457,7 @@ float LODManager::getLODTargetFPS() const { if (qApp->isHMDMode()) { lodTargetFPS = getHMDLODTargetFPS(); } - + // if RefreshRate is slower than LOD target then it becomes the true LOD target if (lodTargetFPS > refreshRateFPS) { return refreshRateFPS; @@ -476,7 +476,7 @@ void LODManager::setWorldDetailQuality(WorldDetailQuality quality, bool isHMDMod setDesktopLODTargetFPS(desiredFPS); } } - + void LODManager::setWorldDetailQuality(WorldDetailQuality quality) { setWorldDetailQuality(quality, qApp->isHMDMode()); saveSettings(); @@ -492,7 +492,7 @@ ScriptValue worldDetailQualityToScriptValue(ScriptEngine* engine, const WorldDet } bool worldDetailQualityFromScriptValue(const ScriptValue& object, WorldDetailQuality& worldDetailQuality) { - worldDetailQuality = + worldDetailQuality = static_cast(std::min(std::max(object.toInt32(), (int)WORLD_DETAIL_LOW), (int)WORLD_DETAIL_HIGH)); return true; } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index d30fe66a6f..7017f2a083 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -323,6 +323,19 @@ Menu::Menu() { } }); + // Settings > Script Security + action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::ScriptSecurity); + connect(action, &QAction::triggered, [] { + auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + auto hmd = DependencyManager::get(); + + tablet->pushOntoStack("hifi/dialogs/security/ScriptSecurity.qml"); + + if (!hmd->getShouldShowTablet()) { + hmd->toggleShouldShowTablet(); + } + }); + // Settings > Developer Menu addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menu", 0, false, this, SLOT(toggleDeveloperMenus())); @@ -388,13 +401,18 @@ Menu::Menu() { // Developer > UI >>> MenuWrapper* uiOptionsMenu = developerMenu->addMenu("UI"); + + // Developer > UI > Show Overlays + action = addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::Overlays, 0, true); + + connect(action, &QAction::triggered, [action] { + qApp->getApplicationOverlay().setEnabled(action->isChecked()); + }); + + // Developer > UI > Desktop Tablet Becomes Toolbar action = addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::DesktopTabletToToolbar, 0, qApp->getDesktopTabletBecomesToolbarSetting()); - // Developer > UI > Show Overlays - addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::Overlays, 0, true); - - // Developer > UI > Desktop Tablet Becomes Toolbar connect(action, &QAction::triggered, [action] { qApp->setDesktopTabletBecomesToolbarSetting(action->isChecked()); }); @@ -445,6 +463,10 @@ Menu::Menu() { textureGroup->addAction(addCheckableActionToQMenuAndActionHash(textureMenu, MenuOption::RenderMaxTexture4096MB, 0, false)); textureGroup->addAction(addCheckableActionToQMenuAndActionHash(textureMenu, MenuOption::RenderMaxTexture6144MB, 0, false)); textureGroup->addAction(addCheckableActionToQMenuAndActionHash(textureMenu, MenuOption::RenderMaxTexture8192MB, 0, false)); + textureGroup->addAction(addCheckableActionToQMenuAndActionHash(textureMenu, MenuOption::RenderMaxTexture10240MB, 0, false)); + textureGroup->addAction(addCheckableActionToQMenuAndActionHash(textureMenu, MenuOption::RenderMaxTexture12288MB, 0, false)); + textureGroup->addAction(addCheckableActionToQMenuAndActionHash(textureMenu, MenuOption::RenderMaxTexture16384MB, 0, false)); + textureGroup->addAction(addCheckableActionToQMenuAndActionHash(textureMenu, MenuOption::RenderMaxTexture20480MB, 0, false)); connect(textureGroup, &QActionGroup::triggered, [textureGroup] { auto checked = textureGroup->checkedAction(); auto text = checked->text(); @@ -465,8 +487,16 @@ Menu::Menu() { newMaxTextureMemory = MB_TO_BYTES(4096); } else if (MenuOption::RenderMaxTexture6144MB == text) { newMaxTextureMemory = MB_TO_BYTES(6144); - } else if (MenuOption::RenderMaxTexture8192MB == text) { + } else if (MenuOption::RenderMaxTexture1024MB == text) { newMaxTextureMemory = MB_TO_BYTES(8192); + } else if (MenuOption::RenderMaxTexture10240MB == text) { + newMaxTextureMemory = MB_TO_BYTES(10240); + } else if (MenuOption::RenderMaxTexture12288MB == text) { + newMaxTextureMemory = MB_TO_BYTES(12288); + } else if (MenuOption::RenderMaxTexture16384MB == text) { + newMaxTextureMemory = MB_TO_BYTES(16384); + } else if (MenuOption::RenderMaxTexture20480MB == text) { + newMaxTextureMemory = MB_TO_BYTES(20480); } gpu::Texture::setAllowedGPUMemoryUsage(newMaxTextureMemory); }); @@ -775,7 +805,6 @@ Menu::Menu() { addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashOnShutdown, 0, qApp, SLOT(crashOnShutdown())); } - // Developer > Show Statistics addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats, 0, true); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 2bb9f10e68..e0cdfdf4fd 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -175,6 +175,10 @@ namespace MenuOption { const QString RenderMaxTexture4096MB = "4096 MB"; const QString RenderMaxTexture6144MB = "6144 MB"; const QString RenderMaxTexture8192MB = "8192 MB"; + const QString RenderMaxTexture10240MB = "10240 MB"; + const QString RenderMaxTexture12288MB = "12288 MB"; + const QString RenderMaxTexture16384MB = "16384 MB"; + const QString RenderMaxTexture20480MB = "20480 MB"; const QString RenderSensorToWorldMatrix = "Show SensorToWorld Matrix"; const QString RenderIKTargets = "Show IK Targets"; const QString RenderIKConstraints = "Show IK Constraints"; @@ -186,6 +190,7 @@ namespace MenuOption { const QString RunTimingTests = "Run Timing Tests"; const QString ScriptedMotorControl = "Enable Scripted Motor Control"; const QString EntityScriptQMLWhitelist = "Entity Script / QML Whitelist"; + const QString ScriptSecurity = "Script Security"; const QString ShowTrackedObjects = "Show Tracked Objects"; const QString SelfieCamera = "Selfie"; const QString SendWrongDSConnectVersion = "Send wrong DS connect version"; diff --git a/interface/src/RefreshRateManager.cpp b/interface/src/RefreshRateManager.cpp index 09428da5ea..9ba446750b 100644 --- a/interface/src/RefreshRateManager.cpp +++ b/interface/src/RefreshRateManager.cpp @@ -48,12 +48,13 @@ static const int VR_TARGET_RATE = 90; * "Interactive"Medium refresh rate, which is reduced when Interface doesn't have focus or is * minimized. * "Realtime"High refresh rate, even when Interface doesn't have focus or is minimized. + * "Custom"Custom refresh rate for full control over the refresh rate in all states. * * * @typedef {string} RefreshRateProfileName */ static const std::array REFRESH_RATE_PROFILE_TO_STRING = - { { "Eco", "Interactive", "Realtime" } }; + { { "Eco", "Interactive", "Realtime", "Custom" } }; /*@jsdoc *

Interface states that affect the refresh rate.

@@ -94,7 +95,8 @@ static const std::array UX_MODE static const std::map REFRESH_RATE_PROFILE_FROM_STRING = { { "Eco", RefreshRateManager::RefreshRateProfile::ECO }, { "Interactive", RefreshRateManager::RefreshRateProfile::INTERACTIVE }, - { "Realtime", RefreshRateManager::RefreshRateProfile::REALTIME } }; + { "Realtime", RefreshRateManager::RefreshRateProfile::REALTIME }, + { "Custom", RefreshRateManager::RefreshRateProfile::CUSTOM } }; // Porfile regimes are: @@ -107,10 +109,12 @@ static const std::array { { 30, 20, 10, 2, 30, 30 } }; static const std::array REALTIME_PROFILE = - { { 60, 60, 60, 2, 30, 30} }; + { { 60, 60, 60, 2, 30, 30 } }; -static const std::array, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> REFRESH_RATE_PROFILES = - { { ECO_PROFILE, INTERACTIVE_PROFILE, REALTIME_PROFILE } }; +static const std::array CUSTOM_PROFILE = REALTIME_PROFILE; // derived from settings and modified by scripts below + +static std::array, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> REFRESH_RATE_PROFILES = + { { ECO_PROFILE, INTERACTIVE_PROFILE, REALTIME_PROFILE, CUSTOM_PROFILE } }; static const int INACTIVE_TIMER_LIMIT = 3000; @@ -134,6 +138,10 @@ std::string RefreshRateManager::uxModeToString(RefreshRateManager::RefreshRateMa RefreshRateManager::RefreshRateManager() { _refreshRateProfile = (RefreshRateManager::RefreshRateProfile) _refreshRateProfileSetting.get(); + for (size_t i = 0; i < _customRefreshRateSettings.size(); i++) { + REFRESH_RATE_PROFILES[CUSTOM][i] = _customRefreshRateSettings[i].get(); + } + _inactiveTimer->setInterval(INACTIVE_TIMER_LIMIT); _inactiveTimer->setSingleShot(true); QObject::connect(_inactiveTimer.get(), &QTimer::timeout, [&] { @@ -168,6 +176,25 @@ void RefreshRateManager::setRefreshRateProfile(RefreshRateManager::RefreshRatePr } } +int RefreshRateManager::getCustomRefreshRate(RefreshRateRegime regime) { + if (isValidRefreshRateRegime(regime)) { + return REFRESH_RATE_PROFILES[RefreshRateProfile::CUSTOM][regime]; + } + + return 0; +} + +void RefreshRateManager::setCustomRefreshRate(RefreshRateRegime regime, int value) { + value = std::max(value, 1); + if (isValidRefreshRateRegime(regime)) { + _refreshRateProfileSettingLock.withWriteLock([&] { + REFRESH_RATE_PROFILES[RefreshRateProfile::CUSTOM][regime] = value; + _customRefreshRateSettings[regime].set(value); + }); + updateRefreshRateController(); + } +} + RefreshRateManager::RefreshRateProfile RefreshRateManager::getRefreshRateProfile() const { RefreshRateManager::RefreshRateProfile profile = RefreshRateManager::RefreshRateProfile::REALTIME; @@ -191,7 +218,6 @@ void RefreshRateManager::setRefreshRateRegime(RefreshRateManager::RefreshRateReg _refreshRateRegime = refreshRateRegime; updateRefreshRateController(); } - } void RefreshRateManager::setUXMode(RefreshRateManager::UXMode uxMode) { diff --git a/interface/src/RefreshRateManager.h b/interface/src/RefreshRateManager.h index 4b91f0c45e..6b94a94a4a 100644 --- a/interface/src/RefreshRateManager.h +++ b/interface/src/RefreshRateManager.h @@ -32,10 +32,11 @@ public: ECO = 0, INTERACTIVE, REALTIME, + CUSTOM, PROFILE_NUM }; Q_ENUM(RefreshRateProfile) - static bool isValidRefreshRateProfile(RefreshRateProfile value) { return (value >= RefreshRateProfile::ECO && value <= RefreshRateProfile::REALTIME); } + static bool isValidRefreshRateProfile(RefreshRateProfile value) { return (value >= 0 && value < RefreshRateProfile::PROFILE_NUM); } /*@jsdoc *

Interface states that affect the refresh rate.

@@ -106,6 +107,9 @@ public: // query the refresh rate target at the specified combination int queryRefreshRateTarget(RefreshRateProfile profile, RefreshRateRegime regime, UXMode uxMode) const; + int getCustomRefreshRate(RefreshRateRegime regime); + void setCustomRefreshRate(RefreshRateRegime regime, int value); + void resetInactiveTimer(); void toggleInactive(); @@ -121,7 +125,15 @@ private: UXMode _uxMode { UXMode::DESKTOP }; mutable ReadWriteLockable _refreshRateProfileSettingLock; - Setting::Handle _refreshRateProfileSetting { "refreshRateProfile", RefreshRateProfile::INTERACTIVE }; + Setting::Handle _refreshRateProfileSetting{ "refreshRateProfile", RefreshRateProfile::INTERACTIVE }; + std::array, REGIME_NUM> _customRefreshRateSettings { { + { "customRefreshRateFocusActive", 60 }, + { "customRefreshRateFocusInactive", 60 }, + { "customRefreshRateUnfocus", 60 }, + { "customRefreshRateMinimized", 2 }, + { "customRefreshRateStartup", 30 }, + { "customRefreshRateShutdown", 30 } + } }; std::function _refreshRateOperator { nullptr }; diff --git a/interface/src/avatar/AvatarDoctor.cpp b/interface/src/avatar/AvatarDoctor.cpp index 5c2fc01911..a46652c7d9 100644 --- a/interface/src/avatar/AvatarDoctor.cpp +++ b/interface/src/avatar/AvatarDoctor.cpp @@ -333,6 +333,14 @@ void AvatarDoctor::diagnoseTextures() { addTextureToList(material.occlusionTexture); addTextureToList(material.scatteringTexture); addTextureToList(material.lightmapTexture); + + if (material.isMToonMaterial) { + addTextureToList(material.shadeTexture); + addTextureToList(material.shadingShiftTexture); + addTextureToList(material.matcapTexture); + addTextureToList(material.rimTexture); + addTextureToList(material.uvAnimationTexture); + } } for (const auto& materialMapping : model->getMaterialMapping()) { diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index 97085c8443..362ac579a9 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -27,6 +27,10 @@ void AvatarMotionState::handleEasyChanges(uint32_t& flags) { if (flags & Simulation::DIRTY_PHYSICS_ACTIVATION && !_body->isActive()) { _body->activate(); } + + if (flags & Simulation::DIRTY_MASS) { + updateBodyMassProperties(); + } } AvatarMotionState::~AvatarMotionState() { diff --git a/interface/src/avatar/AvatarProject.cpp b/interface/src/avatar/AvatarProject.cpp index 466db613f6..cf41c0a040 100644 --- a/interface/src/avatar/AvatarProject.cpp +++ b/interface/src/avatar/AvatarProject.cpp @@ -136,6 +136,14 @@ AvatarProject* AvatarProject::createAvatarProject(const QString& projectsFolder, addTextureToList(material.occlusionTexture); addTextureToList(material.scatteringTexture); addTextureToList(material.lightmapTexture); + + if (material.isMToonMaterial) { + addTextureToList(material.shadeTexture); + addTextureToList(material.shadingShiftTexture); + addTextureToList(material.matcapTexture); + addTextureToList(material.rimTexture); + addTextureToList(material.uvAnimationTexture); + } } QDir textureDir(textureFolder.isEmpty() ? fbxInfo.absoluteDir() : textureFolder); diff --git a/interface/src/avatar/DetailedMotionState.cpp b/interface/src/avatar/DetailedMotionState.cpp index 02a2b9d425..a22d533bc9 100644 --- a/interface/src/avatar/DetailedMotionState.cpp +++ b/interface/src/avatar/DetailedMotionState.cpp @@ -31,6 +31,10 @@ void DetailedMotionState::handleEasyChanges(uint32_t& flags) { if (flags & Simulation::DIRTY_PHYSICS_ACTIVATION && !_body->isActive()) { _body->activate(); } + + if (flags & Simulation::DIRTY_MASS) { + updateBodyMassProperties(); + } } DetailedMotionState::~DetailedMotionState() { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index bb6fbcd899..0c61b0d01a 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -73,6 +73,7 @@ #include "MovingEntitiesOperator.h" #include "SceneScriptingInterface.h" #include "WarningsSuppression.h" +#include "ScriptPermissions.h" using namespace std; @@ -226,7 +227,7 @@ MyAvatar::MyAvatar(QThread* thread) : _yawSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "yawSpeed", _yawSpeed), _hmdYawSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "hmdYawSpeed", _hmdYawSpeed), _pitchSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "pitchSpeed", _pitchSpeed), - _fullAvatarURLSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "fullAvatarURL", + _fullAvatarURLSetting(QStringList() << SETTINGS_FULL_PRIVATE_GROUP_NAME << AVATAR_SETTINGS_GROUP_NAME << "fullAvatarURL", AvatarData::defaultFullAvatarModelUrl()), _fullAvatarModelNameSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "fullAvatarModelName", _fullAvatarModelName), _animGraphURLSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "animGraphURL", QUrl("")), @@ -1035,8 +1036,8 @@ void MyAvatar::simulate(float deltaTime, bool inView) { std::pair zoneInteractionProperties; entityTree->withWriteLock([&] { zoneInteractionProperties = entityTreeRenderer->getZoneInteractionProperties(); - EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); - entityTree->updateEntityQueryAACube(shared_from_this(), packetSender, false, true); + std::shared_ptr packetSender = qApp->getEntityEditPacketSender(); + entityTree->updateEntityQueryAACube(shared_from_this(), packetSender.get(), false, true); }); bool isPhysicsEnabled = qApp->isPhysicsEnabled(); bool zoneAllowsFlying = zoneInteractionProperties.first; @@ -1729,7 +1730,7 @@ void MyAvatar::handleChangedAvatarEntityData() { entityTree->deleteEntitiesByID(entitiesToDelete); // ADD real entities - EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); + auto packetSender = qApp->getEntityEditPacketSender(); for (const auto& id : entitiesToAdd) { bool blobFailed = false; EntityItemProperties properties; @@ -1739,10 +1740,11 @@ void MyAvatar::handleChangedAvatarEntityData() { blobFailed = true; // blob doesn't exist return; } - std::lock_guard guard(_scriptEngineLock); - if (!EntityItemProperties::blobToProperties(*_scriptEngine, itr.value(), properties)) { - blobFailed = true; // blob is corrupt - } + _helperScriptEngine.run( [&] { + if (!EntityItemProperties::blobToProperties(*_helperScriptEngine.get(), itr.value(), properties)) { + blobFailed = true; // blob is corrupt + } + }); }); if (blobFailed) { // remove from _cachedAvatarEntityBlobUpdatesToSkip just in case: @@ -1775,10 +1777,11 @@ void MyAvatar::handleChangedAvatarEntityData() { skip = true; return; } - std::lock_guard guard(_scriptEngineLock); - if (!EntityItemProperties::blobToProperties(*_scriptEngine, itr.value(), properties)) { - skip = true; - } + _helperScriptEngine.run( [&] { + if (!EntityItemProperties::blobToProperties(*_helperScriptEngine.get(), itr.value(), properties)) { + skip = true; + } + }); }); if (!skip && canRezAvatarEntites) { sanitizeAvatarEntityProperties(properties); @@ -1883,10 +1886,9 @@ bool MyAvatar::updateStaleAvatarEntityBlobs() const { if (found) { ++numFound; QByteArray blob; - { - std::lock_guard guard(_scriptEngineLock); - EntityItemProperties::propertiesToBlob(*_scriptEngine, getID(), properties, blob); - } + _helperScriptEngine.run( [&] { + EntityItemProperties::propertiesToBlob(*_helperScriptEngine.get(), getID(), properties, blob); + }); _avatarEntitiesLock.withWriteLock([&] { _cachedAvatarEntityBlobs[id] = blob; }); @@ -1947,10 +1949,9 @@ AvatarEntityMap MyAvatar::getAvatarEntityData() const { EntityItemProperties properties = entity->getProperties(desiredProperties); QByteArray blob; - { - std::lock_guard guard(_scriptEngineLock); - EntityItemProperties::propertiesToBlob(*_scriptEngine, getID(), properties, blob, true); - } + _helperScriptEngine.run( [&] { + EntityItemProperties::propertiesToBlob(*_helperScriptEngine.get(), getID(), properties, blob, true); + }); data[entityID] = blob; } @@ -2092,9 +2093,6 @@ void MyAvatar::avatarEntityDataToJson(QJsonObject& root) const { } void MyAvatar::loadData() { - if (!_scriptEngine) { - _scriptEngine = newScriptEngine(); - } getHead()->setBasePitch(_headPitchSetting.get()); _yawSpeed = _yawSpeedSetting.get(_yawSpeed); @@ -2236,6 +2234,9 @@ AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL, const QString& return attachment; } +bool MyAvatar::isMyAvatarURLProtected() const { + return !ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL); +} int MyAvatar::parseDataFromBuffer(const QByteArray& buffer) { qCDebug(interfaceapp) << "Error: ignoring update packet for MyAvatar" @@ -2700,11 +2701,10 @@ QVariantList MyAvatar::getAvatarEntitiesVariant() { QVariantMap avatarEntityData; avatarEntityData["id"] = entityID; EntityItemProperties entityProperties = entity->getProperties(desiredProperties); - { - std::lock_guard guard(_scriptEngineLock); - ScriptValue scriptProperties = EntityItemPropertiesToScriptValue(_scriptEngine.get(), entityProperties); + _helperScriptEngine.run( [&] { + ScriptValue scriptProperties = EntityItemPropertiesToScriptValue(_helperScriptEngine.get(), entityProperties); avatarEntityData["properties"] = scriptProperties.toVariant(); - } + }); avatarEntitiesData.append(QVariant(avatarEntityData)); } } @@ -4232,7 +4232,7 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) { _avatarEntitiesLock.withReadLock([&] { avatarEntityIDs = _packedAvatarEntityData.keys(); }); - EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); + auto packetSender = qApp->getEntityEditPacketSender(); entityTree->withWriteLock([&] { for (const auto& entityID : avatarEntityIDs) { auto entity = entityTree->findEntityByID(entityID); @@ -5769,8 +5769,7 @@ void MyAvatar::FollowHelper::deactivate() { } void MyAvatar::FollowHelper::deactivate(CharacterController::FollowType type) { - int int_type = static_cast(type); - assert(int_type >= 0 && int_type < static_cast(CharacterController::FollowType::Count)); + assert(type < CharacterController::FollowType::Count); _timeRemaining[(int)type] = 0.0f; } @@ -5778,16 +5777,14 @@ void MyAvatar::FollowHelper::deactivate(CharacterController::FollowType type) { // eg. activate(FollowType::Rotation, true) snaps the FollowHelper's rotation immediately // to the rotation of its _followDesiredBodyTransform. void MyAvatar::FollowHelper::activate(CharacterController::FollowType type, const bool snapFollow) { - int int_type = static_cast(type); - assert(int_type >= 0 && int_type < static_cast(CharacterController::FollowType::Count)); + assert(type < CharacterController::FollowType::Count); // TODO: Perhaps, the follow time should be proportional to the displacement. _timeRemaining[(int)type] = snapFollow ? CharacterController::FOLLOW_TIME_IMMEDIATE_SNAP : FOLLOW_TIME; } bool MyAvatar::FollowHelper::isActive(CharacterController::FollowType type) const { - int int_type = static_cast(type); - assert(int_type >= 0 && int_type < static_cast(CharacterController::FollowType::Count)); + assert(type < CharacterController::FollowType::Count); return _timeRemaining[(int)type] > 0.0f; } @@ -6889,7 +6886,7 @@ void MyAvatar::sendPacket(const QUuid& entityID) const { if (entityTree) { entityTree->withWriteLock([&] { // force an update packet - EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); + auto packetSender = qApp->getEntityEditPacketSender(); packetSender->queueEditAvatarEntityMessage(entityTree, entityID); }); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 5e0627360c..60c07ad42c 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -2683,6 +2684,7 @@ private: void setEnableDrawAverageFacing(bool drawAverage) { _drawAverageFacingEnabled = drawAverage; } bool getEnableDrawAverageFacing() const { return _drawAverageFacingEnabled; } virtual bool isMyAvatar() const override { return true; } + virtual bool isMyAvatarURLProtected() const override; virtual int parseDataFromBuffer(const QByteArray& buffer) override; virtual glm::vec3 getSkeletonPosition() const override; int _skeletonModelChangeCount { 0 }; @@ -3101,8 +3103,10 @@ private: mutable std::set _staleCachedAvatarEntityBlobs; // // keep a ScriptEngine around so we don't have to instantiate on the fly (these are very slow to create/delete) - mutable std::mutex _scriptEngineLock; - ScriptEnginePointer _scriptEngine { nullptr }; + // TODO: profile if it performs better when script engine is on avatar thread or on its own thread + // Own thread is safer from deadlocks + mutable HelperScriptEngine _helperScriptEngine; + bool _needToSaveAvatarEntitySettings { false }; bool _reactionTriggers[NUM_AVATAR_TRIGGER_REACTIONS] { false, false }; diff --git a/interface/src/graphics/GraphicsEngine.cpp b/interface/src/graphics/GraphicsEngine.cpp index bf69efd23e..5075d9b57f 100644 --- a/interface/src/graphics/GraphicsEngine.cpp +++ b/interface/src/graphics/GraphicsEngine.cpp @@ -281,7 +281,7 @@ void GraphicsEngine::render_performFrame() { { PROFILE_RANGE(render, "/runRenderFrame"); - renderArgs._hudOperator = displayPlugin->getHUDOperator(); + renderArgs._hudOperator = qApp->getApplicationOverlay().enabled() ? displayPlugin->getHUDOperator() : nullptr; renderArgs._hudTexture = qApp->getApplicationOverlay().getOverlayTexture(); renderArgs._takingSnapshot = qApp->takeSnapshotOperators(snapshotOperators); renderArgs._blitFramebuffer = finalFramebuffer; diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 835e4060a7..7e8c1afff3 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "AddressManager.h" #include "Application.h" @@ -33,6 +34,9 @@ #include "MainWindow.h" #include "Profile.h" #include "LogHandler.h" +#include +#include +#include #ifdef Q_OS_WIN #include @@ -63,11 +67,24 @@ int main(int argc, const char* argv[]) { } #endif + // Setup QCoreApplication settings, install log message handler setupHifiApplication(BuildInfo::INTERFACE_NAME); // Journald by default in user applications is probably a bit too modern still. LogHandler::getInstance().setShouldUseJournald(false); + + // Extend argv to enable WebGL rendering + std::vector argvExtended(&argv[0], &argv[argc]); + argvExtended.push_back("--ignore-gpu-blocklist"); +#ifdef Q_OS_ANDROID + argvExtended.push_back("--suppress-settings-reset"); +#endif + int argcExtended = (int)argvExtended.size(); + + QElapsedTimer startupTime; + startupTime.start(); + QCommandLineParser parser; parser.setApplicationDescription("Overte -- A free/libre and open-source virtual worlds client"); QCommandLineOption helpOption = parser.addHelpOption(); @@ -125,12 +142,12 @@ int main(int argc, const char* argv[]) { "displays" ); QCommandLineOption disableDisplaysOption( - "disable-displays", + "disableDisplayPlugins", "Displays to disable. Valid options include \"OpenVR (Vive)\" and \"Oculus Rift\"", "string" ); QCommandLineOption disableInputsOption( - "disable-inputs", + "disableInputPlugins", "Inputs to disable. Valid options include \"OpenVR (Vive)\" and \"Oculus Rift\"", "string" ); @@ -246,6 +263,19 @@ int main(int argc, const char* argv[]) { "Logging options, comma separated: color,nocolor,process_id,thread_id,milliseconds,keep_repeats,journald,nojournald", "options" ); + QCommandLineOption getPluginsOption( + "getPlugins", + "Print out a list of plugins in JSON" + ); + QCommandLineOption abortAfterStartupOption( + "abortAfterStartup", + "Debug option. Aborts right after startup." + ); + QCommandLineOption abortAfterInitOption( + "abortAfterInit", + "Debug option. Aborts after initialization, right before the program starts running the event loop." + ); + // "--qmljsdebugger", which appears in output from "--help-all". // Those below don't seem to be optional. // --ignore-gpu-blacklist @@ -288,6 +318,10 @@ int main(int argc, const char* argv[]) { parser.addOption(quitWhenFinishedOption); parser.addOption(fastHeartbeatOption); parser.addOption(logOption); + parser.addOption(abortAfterStartupOption); + parser.addOption(abortAfterInitOption); + parser.addOption(getPluginsOption); + QString applicationPath; // A temporary application instance is needed to get the location of the running executable @@ -310,6 +344,16 @@ int main(int argc, const char* argv[]) { #endif } + // TODO: We need settings for Application, but Settings needs an Application + // to handle events. Needs splitting into two parts: enough initialization + // for Application to work, and then thread start afterwards. + Setting::init(); + Application app(argcExtended, const_cast(argvExtended.data()), parser, startupTime); + + if (parser.isSet("abortAfterStartup")) { + return 99; + } + // We want to configure the logging system as early as possible auto& logHandler = LogHandler::getInstance(); @@ -321,6 +365,75 @@ int main(int argc, const char* argv[]) { } } + app.initializePluginManager(parser); + + if (parser.isSet(getPluginsOption)) { + auto pluginManager = PluginManager::getInstance(); + + QJsonObject pluginsJson; + for (const auto &plugin : pluginManager->getPluginInfo()) { + QJsonObject data; + data["data"] = plugin.metaData; + data["loaded"] = plugin.loaded; + data["disabled"] = plugin.disabled; + data["filteredOut"] = plugin.filteredOut; + data["wrongVersion"] = plugin.wrongVersion; + pluginsJson[plugin.name] = data; + } + + QJsonObject inputJson; + for (const auto &plugin : pluginManager->getInputPlugins()) { + QJsonObject data; + data["subdeviceNames"] = QJsonArray::fromStringList(plugin->getSubdeviceNames()); + data["deviceName"] = plugin->getDeviceName(); + data["configurable"] = plugin->configurable(); + data["isHandController"] = plugin->isHandController(); + data["isHeadController"] = plugin->isHeadController(); + data["isActive"] = plugin->isActive(); + data["isSupported"] = plugin->isSupported(); + + inputJson[plugin->getName()] = data; + } + + QJsonObject displayJson; + for (const auto &plugin : pluginManager->getDisplayPlugins()) { + QJsonObject data; + data["isHmd"] = plugin->isHmd(); + data["isStereo"] = plugin->isStereo(); + data["targetFramerate"] = plugin->getTargetFrameRate(); + data["hasAsyncReprojection"] = plugin->hasAsyncReprojection(); + data["isActive"] = plugin->isActive(); + data["isSupported"] = plugin->isSupported(); + + displayJson[plugin->getName()] = data; + } + + QJsonObject codecsJson; + for (const auto &plugin : pluginManager->getCodecPlugins()) { + QJsonObject data; + data["isActive"] = plugin->isActive(); + data["isSupported"] = plugin->isSupported(); + + codecsJson[plugin->getName()] = data; + } + + QJsonObject platformsJson; + platformsJson["steamAvailable"] = (pluginManager->getSteamClientPlugin() != nullptr); + platformsJson["oculusAvailable"] = (pluginManager->getOculusPlatformPlugin() != nullptr); + + QJsonObject root; + root["plugins"] = pluginsJson; + root["inputs"] = inputJson; + root["displays"] = displayJson; + root["codecs"] = codecsJson; + root["platforms"] = platformsJson; + + std::cout << QJsonDocument(root).toJson().toStdString() << "\n"; + + return 0; + } + + // Act on arguments for early termination. if (parser.isSet(versionOption)) { parser.showVersion(); @@ -407,10 +520,9 @@ int main(int argc, const char* argv[]) { QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); #endif - QElapsedTimer startupTime; - startupTime.start(); - Setting::init(); + + // Instance UserActivityLogger now that the settings are loaded auto& ual = UserActivityLogger::getInstance(); @@ -549,7 +661,7 @@ int main(int argc, const char* argv[]) { // Oculus initialization MUST PRECEDE OpenGL context creation. // The nature of the Application constructor means this has to be either here, // or in the main window ctor, before GL startup. - Application::initPlugins(parser); + //app.configurePlugins(parser); #ifdef Q_OS_WIN // If we're running in steam mode, we need to do an explicit check to ensure we're up to the required min spec @@ -587,17 +699,10 @@ int main(int argc, const char* argv[]) { SandboxUtils::runLocalSandbox(serverContentPath, true, noUpdater); } - // Extend argv to enable WebGL rendering - std::vector argvExtended(&argv[0], &argv[argc]); - argvExtended.push_back("--ignore-gpu-blocklist"); -#ifdef Q_OS_ANDROID - argvExtended.push_back("--suppress-settings-reset"); -#endif - int argcExtended = (int)argvExtended.size(); - PROFILE_SYNC_END(startup, "main startup", ""); PROFILE_SYNC_BEGIN(startup, "app full ctor", ""); - Application app(argcExtended, const_cast(argvExtended.data()), parser, startupTime, runningMarkerExisted); + app.setPreviousSessionCrashed(runningMarkerExisted); + app.initialize(parser); PROFILE_SYNC_END(startup, "app full ctor", ""); #if defined(Q_OS_LINUX) @@ -665,6 +770,9 @@ int main(int argc, const char* argv[]) { translator.load("i18n/interface_en"); app.installTranslator(&translator); qCDebug(interfaceapp, "Created QT Application."); + if (parser.isSet("abortAfterInit")) { + return 99; + } exitCode = app.exec(); server.close(); diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index ce44d3011c..91af721ea6 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -137,12 +137,12 @@ LaserPointer::RenderState::RenderState(const QUuid& startID, const QUuid& pathID { EntityPropertyFlags desiredProperties; desiredProperties += PROP_IGNORE_PICK_INTERSECTION; - _pathIgnorePicks = entityScriptingInterface->getEntityPropertiesInternal(getPathID(), desiredProperties).getIgnorePickIntersection(); + _pathIgnorePicks = entityScriptingInterface->getEntityPropertiesInternal(getPathID(), desiredProperties, false).getIgnorePickIntersection(); } { EntityPropertyFlags desiredProperties; desiredProperties += PROP_STROKE_WIDTHS; - auto widths = entityScriptingInterface->getEntityPropertiesInternal(getPathID(), desiredProperties).getStrokeWidths(); + auto widths = entityScriptingInterface->getEntityPropertiesInternal(getPathID(), desiredProperties, false).getStrokeWidths(); _lineWidth = widths.length() == 0 ? PolyLineEntityItem::DEFAULT_LINE_WIDTH : widths[0]; } } diff --git a/interface/src/raypick/ParabolaPick.cpp b/interface/src/raypick/ParabolaPick.cpp index 378a46b96b..e8dd759449 100644 --- a/interface/src/raypick/ParabolaPick.cpp +++ b/interface/src/raypick/ParabolaPick.cpp @@ -71,7 +71,7 @@ PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) if (getFilter().doesPickLocalEntities()) { EntityPropertyFlags desiredProperties; desiredProperties += PROP_ENTITY_HOST_TYPE; - if (DependencyManager::get()->getEntityPropertiesInternal(entityRes.entityID, desiredProperties).getEntityHostType() == entity::HostType::LOCAL) { + if (DependencyManager::get()->getEntityPropertiesInternal(entityRes.entityID, desiredProperties, false).getEntityHostType() == entity::HostType::LOCAL) { type = IntersectionType::LOCAL_ENTITY; } } diff --git a/interface/src/raypick/PathPointer.cpp b/interface/src/raypick/PathPointer.cpp index b24c5630c4..9333e0f03b 100644 --- a/interface/src/raypick/PathPointer.cpp +++ b/interface/src/raypick/PathPointer.cpp @@ -257,7 +257,7 @@ StartEndRenderState::StartEndRenderState(const QUuid& startID, const QUuid& endI EntityPropertyFlags desiredProperties; desiredProperties += PROP_DIMENSIONS; desiredProperties += PROP_IGNORE_PICK_INTERSECTION; - auto properties = entityScriptingInterface->getEntityPropertiesInternal(_startID, desiredProperties); + auto properties = entityScriptingInterface->getEntityPropertiesInternal(_startID, desiredProperties, false); _startDim = properties.getDimensions(); _startIgnorePicks = properties.getIgnorePickIntersection(); } @@ -266,7 +266,7 @@ StartEndRenderState::StartEndRenderState(const QUuid& startID, const QUuid& endI desiredProperties += PROP_DIMENSIONS; desiredProperties += PROP_ROTATION; desiredProperties += PROP_IGNORE_PICK_INTERSECTION; - auto properties = entityScriptingInterface->getEntityPropertiesInternal(_endID, desiredProperties); + auto properties = entityScriptingInterface->getEntityPropertiesInternal(_endID, desiredProperties, false); _endDim = properties.getDimensions(); _endRot = properties.getRotation(); _endIgnorePicks = properties.getIgnorePickIntersection(); diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index 17326baa1a..4cb7232095 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -40,7 +40,7 @@ PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { if (getFilter().doesPickLocalEntities()) { EntityPropertyFlags desiredProperties; desiredProperties += PROP_ENTITY_HOST_TYPE; - if (DependencyManager::get()->getEntityPropertiesInternal(entityRes.entityID, desiredProperties).getEntityHostType() == entity::HostType::LOCAL) { + if (DependencyManager::get()->getEntityPropertiesInternal(entityRes.entityID, desiredProperties, false).getEntityHostType() == entity::HostType::LOCAL) { type = IntersectionType::LOCAL_ENTITY; } } @@ -123,6 +123,6 @@ glm::vec2 RayPick::projectOntoEntityXYPlane(const QUuid& entityID, const glm::ve desiredProperties += PROP_ROTATION; desiredProperties += PROP_DIMENSIONS; desiredProperties += PROP_REGISTRATION_POINT; - auto props = DependencyManager::get()->getEntityPropertiesInternal(entityID, desiredProperties); + auto props = DependencyManager::get()->getEntityPropertiesInternal(entityID, desiredProperties, false); return projectOntoXYPlane(worldPos, props.getPosition(), props.getRotation(), props.getDimensions(), props.getRegistrationPoint(), unNormalized); } diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 175021e559..4128976fdc 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -216,6 +216,7 @@ class ScriptEngine; * * @property {Controller.Actions} Actions - Predefined actions on Interface and the user's avatar. These can be used as end * points in a {@link RouteObject} mapping. A synonym for Controller.Hardware.Actions. + * Getting this property is computationally expensive, so it's best to cache it once on script start. * Read-only. *

Default mappings are provided from the Controller.Hardware.Keyboard and Controller.Standard * to actions in @@ -225,13 +226,16 @@ class ScriptEngine; * standard.json, respectively.

* * @property {Controller.Hardware} Hardware - Standard and hardware-specific controller and computer outputs, plus predefined - * actions on Interface and the user's avatar. The outputs can be mapped to Actions or functions in a + * actions on Interface and the user's avatar. Getting this property is computationally expensive, so it's best to cache it + * instead of calling on every update. + * The outputs can be mapped to Actions or functions in a * {@link RouteObject} mapping. Additionally, hardware-specific controller outputs can be mapped to * Controller.Standard controller outputs. Read-only. * * @property {Controller.Standard} Standard - Standard controller outputs that can be mapped to Actions or * functions in a {@link RouteObject} mapping. Read-only. - *

Each hardware device has a mapping from its outputs to Controller.Standard items, specified in a JSON file. + *

Each hardware device has a mapping from its outputs to Controller.Standard items, specified in a JSON file. + * Getting this property is computationally expensive, so it's best to cache it once on script start. * For example, * leapmotion.json and * vive.json.

diff --git a/interface/src/scripting/PerformanceScriptingInterface.cpp b/interface/src/scripting/PerformanceScriptingInterface.cpp index 9f3534b3e8..f619729eff 100644 --- a/interface/src/scripting/PerformanceScriptingInterface.cpp +++ b/interface/src/scripting/PerformanceScriptingInterface.cpp @@ -56,12 +56,22 @@ void PerformanceScriptingInterface::setRefreshRateProfile(RefreshRateProfile ref emit settingsChanged(); } +void PerformanceScriptingInterface::setCustomRefreshRate(RefreshRateManager::RefreshRateRegime refreshRateRegime, int value) +{ + qApp->getRefreshRateManager().setCustomRefreshRate(refreshRateRegime, value); + emit settingsChanged(); +} + +int PerformanceScriptingInterface::getCustomRefreshRate(RefreshRateManager::RefreshRateRegime refreshRateRegime) const { + return qApp->getRefreshRateManager().getCustomRefreshRate(refreshRateRegime); +} + PerformanceScriptingInterface::RefreshRateProfile PerformanceScriptingInterface::getRefreshRateProfile() const { return (PerformanceScriptingInterface::RefreshRateProfile)qApp->getRefreshRateManager().getRefreshRateProfile(); } QStringList PerformanceScriptingInterface::getRefreshRateProfileNames() const { - static const QStringList refreshRateProfileNames = { "ECO", "INTERACTIVE", "REALTIME" }; + static const QStringList refreshRateProfileNames = { "ECO", "INTERACTIVE", "REALTIME", "CUSTOM" }; return refreshRateProfileNames; } diff --git a/interface/src/scripting/PerformanceScriptingInterface.h b/interface/src/scripting/PerformanceScriptingInterface.h index 86350c8a1f..05e2c1d7bc 100644 --- a/interface/src/scripting/PerformanceScriptingInterface.h +++ b/interface/src/scripting/PerformanceScriptingInterface.h @@ -127,6 +127,22 @@ public slots: */ void setRefreshRateProfile(RefreshRateProfile refreshRateProfile); + /*@jsdoc + * Sets a custom refresh rate. + * @function Performance.setCustomRefreshRate + * @param {RefreshRateRegime} refreshRateRegime - The refresh rate regime + * @param {int} value - The value for the regime + */ + void setCustomRefreshRate(RefreshRateManager::RefreshRateRegime refreshRateRegime, int value); + + /*@jsdoc + * Gets the value for a specific RefreshRateRegime. + * @function Performance.getCustomRefreshRate + * @param {RefreshRateRegime} - The regime to get the value from + * @returns {int} - The value from the specified regime + */ + int getCustomRefreshRate(RefreshRateManager::RefreshRateRegime regime) const; + /*@jsdoc * Gets the current refresh rate profile in use. * @function Performance.getRefreshRateProfile diff --git a/interface/src/scripting/SettingsScriptingInterface.cpp b/interface/src/scripting/SettingsScriptingInterface.cpp index b7ef172f19..00cdf009eb 100644 --- a/interface/src/scripting/SettingsScriptingInterface.cpp +++ b/interface/src/scripting/SettingsScriptingInterface.cpp @@ -21,6 +21,9 @@ SettingsScriptingInterface* SettingsScriptingInterface::getInstance() { } QVariant SettingsScriptingInterface::getValue(const QString& setting) { + if (_restrictPrivateValues && setting.startsWith(SETTINGS_FULL_PRIVATE_GROUP_NAME + "/")) { + return {""}; + } QVariant value = Setting::Handle(setting).get(); if (!value.isValid()) { value = ""; @@ -29,6 +32,9 @@ QVariant SettingsScriptingInterface::getValue(const QString& setting) { } QVariant SettingsScriptingInterface::getValue(const QString& setting, const QVariant& defaultValue) { + if (_restrictPrivateValues && setting.startsWith(SETTINGS_FULL_PRIVATE_GROUP_NAME + "/")) { + return {""}; + } QVariant value = Setting::Handle(setting, defaultValue).get(); if (!value.isValid()) { value = ""; @@ -40,7 +46,7 @@ void SettingsScriptingInterface::setValue(const QString& setting, const QVariant if (getValue(setting) == value) { return; } - if (setting.startsWith("private/")) { + if (setting.startsWith("private/") || setting.startsWith(SETTINGS_FULL_PRIVATE_GROUP_NAME + "/")) { if (_restrictPrivateValues) { qWarning() << "SettingsScriptingInterface::setValue -- restricted write: " << setting << value; return; diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 27564b39eb..b70798d02a 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -78,7 +78,7 @@ WindowScriptingInterface::~WindowScriptingInterface() { } ScriptValue WindowScriptingInterface::hasFocus() { - Q_ASSERT(engine); + Q_ASSERT(engine()); return engine()->newValue(qApp->hasFocus()); } @@ -107,7 +107,7 @@ void WindowScriptingInterface::alert(const QString& message) { /// \param const QString& message message to display /// \return ScriptValue `true` if 'Yes' was clicked, `false` otherwise ScriptValue WindowScriptingInterface::confirm(const QString& message) { - Q_ASSERT(engine); + Q_ASSERT(engine()); return engine()->newValue((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))); } @@ -117,7 +117,7 @@ ScriptValue WindowScriptingInterface::confirm(const QString& message) { /// \return ScriptValue string text value in text box if the dialog was accepted, `null` otherwise. ScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) { QString result = OffscreenUi::getText(nullptr, "", message, QLineEdit::Normal, defaultText); - Q_ASSERT(engine); + Q_ASSERT(engine()); auto sResult = engine()->newValue(result); if (sResult.equals(engine()->newValue(""))) { return engine()->nullValue(); @@ -234,7 +234,7 @@ ScriptValue WindowScriptingInterface::browseDir(const QString& title, const QStr if (!result.isEmpty()) { setPreviousBrowseLocation(QFileInfo(result).absolutePath()); } - Q_ASSERT(engine); + Q_ASSERT(engine()); return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result); } @@ -279,7 +279,7 @@ ScriptValue WindowScriptingInterface::browse(const QString& title, const QString if (!result.isEmpty()) { setPreviousBrowseLocation(QFileInfo(result).absolutePath()); } - Q_ASSERT(engine); + Q_ASSERT(engine()); return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result); } @@ -327,7 +327,7 @@ ScriptValue WindowScriptingInterface::save(const QString& title, const QString& if (!result.isEmpty()) { setPreviousBrowseLocation(QFileInfo(result).absolutePath()); } - Q_ASSERT(engine); + Q_ASSERT(engine()); return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result); } @@ -378,7 +378,7 @@ ScriptValue WindowScriptingInterface::browseAssets(const QString& title, const Q if (!result.isEmpty()) { setPreviousBrowseAssetLocation(QFileInfo(result).absolutePath()); } - Q_ASSERT(engine); + Q_ASSERT(engine()); return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result); } diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 23055cf246..9db09b0b9b 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -27,18 +27,17 @@ public: void renderOverlay(RenderArgs* renderArgs); - gpu::TexturePointer getOverlayTexture(); + gpu::TexturePointer getOverlayTexture(); + + bool enabled() const { return _enabled; } + void setEnabled(bool enabled) { _enabled = enabled; } private: - void renderStatsAndLogs(RenderArgs* renderArgs); void renderDomainConnectionStatusBorder(RenderArgs* renderArgs); void renderQmlUi(RenderArgs* renderArgs); void renderOverlays(RenderArgs* renderArgs); void buildFramebufferObject(); - float _alpha{ 1.0f }; - float _trailingAudioLoudness{ 0.0f }; - int _domainStatusBorder; int _magnifierBorder; @@ -47,6 +46,8 @@ private: gpu::TexturePointer _overlayColorTexture; gpu::FramebufferPointer _overlayFramebuffer; int _qmlGeometryId { 0 }; + + bool _enabled { true }; }; #endif // hifi_ApplicationOverlay_h diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp index 80ba33d0c7..d8038e8727 100644 --- a/interface/src/ui/Keyboard.cpp +++ b/interface/src/ui/Keyboard.cpp @@ -118,7 +118,7 @@ std::pair calculateKeyboardPositionAndOrientation() { EntityPropertyFlags desiredProperties; desiredProperties += PROP_POSITION; desiredProperties += PROP_ROTATION; - auto properties = DependencyManager::get()->getEntityPropertiesInternal(tabletID, desiredProperties); + auto properties = DependencyManager::get()->getEntityPropertiesInternal(tabletID, desiredProperties, false); auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); bool landscapeMode = tablet->getLandscape(); @@ -146,7 +146,7 @@ void Key::saveDimensionsAndLocalPosition() { EntityPropertyFlags desiredProperties; desiredProperties += PROP_LOCAL_POSITION; desiredProperties += PROP_DIMENSIONS; - auto properties = DependencyManager::get()->getEntityPropertiesInternal(_keyID, desiredProperties); + auto properties = DependencyManager::get()->getEntityPropertiesInternal(_keyID, desiredProperties, false); _originalLocalPosition = properties.getLocalPosition(); _originalDimensions = properties.getDimensions(); @@ -469,7 +469,7 @@ void Keyboard::switchToLayer(int layerIndex) { EntityPropertyFlags desiredProperties; desiredProperties += PROP_POSITION; desiredProperties += PROP_ROTATION; - auto oldProperties = entityScriptingInterface->getEntityPropertiesInternal(_anchor.entityID, desiredProperties); + auto oldProperties = entityScriptingInterface->getEntityPropertiesInternal(_anchor.entityID, desiredProperties, false); glm::vec3 currentPosition = oldProperties.getPosition(); glm::quat currentOrientation = oldProperties.getRotation(); @@ -530,7 +530,7 @@ void Keyboard::handleTriggerBegin(const QUuid& id, const PointerEvent& event) { EntityPropertyFlags desiredProperties; desiredProperties += PROP_POSITION; - glm::vec3 keyWorldPosition = DependencyManager::get()->getEntityPropertiesInternal(id, desiredProperties).getPosition(); + glm::vec3 keyWorldPosition = DependencyManager::get()->getEntityPropertiesInternal(id, desiredProperties, false).getPosition(); AudioInjectorOptions audioOptions; audioOptions.localOnly = true; @@ -662,7 +662,7 @@ void Keyboard::handleTriggerContinue(const QUuid& id, const PointerEvent& event) auto entityScriptingInterface = DependencyManager::get(); EntityPropertyFlags desiredProperties; desiredProperties += PROP_ROTATION; - glm::quat orientation = entityScriptingInterface->getEntityPropertiesInternal(id, desiredProperties).getRotation(); + glm::quat orientation = entityScriptingInterface->getEntityPropertiesInternal(id, desiredProperties, false).getRotation(); glm::vec3 yAxis = orientation * Z_AXIS; glm::vec3 yOffset = yAxis * Z_OFFSET; glm::vec3 localPosition = key.getCurrentLocalPosition() - yOffset; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 8597cb5717..ce5a588776 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -100,7 +100,8 @@ void setupPreferences() { QStringList refreshRateProfiles { QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::ECO)), QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::INTERACTIVE)), - QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::REALTIME)) }; + QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::REALTIME)), + QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::CUSTOM)) }; preference->setItems(refreshRateProfiles); preferences->addPreference(preference); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index e245acfd40..5349564043 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -153,14 +153,6 @@ void Overlays::render(RenderArgs* renderArgs) { } } -void Overlays::disable() { - _enabled = false; -} - -void Overlays::enable() { - _enabled = true; -} - Overlay::Pointer Overlays::take2DOverlay(const QUuid& id) { if (_shuttingDown) { return nullptr; @@ -378,7 +370,7 @@ QObject* Overlays::getOverlayObject(const QUuid& id) { } QUuid Overlays::getOverlayAtPoint(const glm::vec2& point) { - if (_shuttingDown || !_enabled) { + if (_shuttingDown) { return UNKNOWN_ENTITY_ID; } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index a0f2e866e2..fcf0c71bc9 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -99,8 +99,6 @@ public: void init(); void update(float deltatime); void render(RenderArgs* renderArgs); - void disable(); - void enable(); Overlay::Pointer take2DOverlay(const QUuid& id); Overlay::Pointer get2DOverlay(const QUuid& id) const; @@ -683,7 +681,6 @@ private: unsigned int _stackOrder { 1 }; - bool _enabled { true }; std::atomic _shuttingDown { false }; PointerEvent calculateOverlayPointerEvent(const QUuid& id, const PickRay& ray, const RayToOverlayIntersectionResult& rayPickResult, diff --git a/launchers/qt/CMakeLists.txt b/launchers/qt/CMakeLists.txt index 12cf7f08d4..0e5ddd6990 100644 --- a/launchers/qt/CMakeLists.txt +++ b/launchers/qt/CMakeLists.txt @@ -281,7 +281,7 @@ if (APPLE) set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) - set(DMG_SUBFOLDER_NAME "Vircadia") + set(DMG_SUBFOLDER_NAME "Overte") set(ESCAPED_DMG_SUBFOLDER_NAME "") set(DMG_SUBFOLDER_ICON "${CMAKE_SOURCE_DIR}/cmake/installer/install-folder.rsrc") diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 6eb77da4d7..16b217bc53 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -27,7 +27,9 @@ #endif #ifdef WIN32 +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 +#endif #include #include #include diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c71da50b1a..4068f7c547 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2106,6 +2106,14 @@ const QUrl& AvatarData::getSkeletonModelURL() const { } } +QString AvatarData::getSkeletonModelURLFromScript() const { + if (isMyAvatar() && !isMyAvatarURLProtected()) { + return _skeletonModelURL.toString(); + } + + return QString(); +}; + QByteArray AvatarData::packSkeletonData() const { // Send an avatar trait packet with the skeleton data before the mesh is loaded int avatarDataSize = 0; @@ -2558,7 +2566,7 @@ QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) { void AttachmentDataObject::setModelURL(const QString& modelURL) { AttachmentData data = scriptvalue_cast(thisObject()); data.modelURL = modelURL; - Q_ASSERT(engine); + Q_ASSERT(engine()); thisObject() = engine()->toScriptValue(data); } @@ -2569,7 +2577,7 @@ QString AttachmentDataObject::getModelURL() const { void AttachmentDataObject::setJointName(const QString& jointName) { AttachmentData data = scriptvalue_cast(thisObject()); data.jointName = jointName; - Q_ASSERT(engine); + Q_ASSERT(engine()); thisObject() = engine()->toScriptValue(data); } @@ -2580,7 +2588,7 @@ QString AttachmentDataObject::getJointName() const { void AttachmentDataObject::setTranslation(const glm::vec3& translation) { AttachmentData data = scriptvalue_cast(thisObject()); data.translation = translation; - Q_ASSERT(engine); + Q_ASSERT(engine()); thisObject() = engine()->toScriptValue(data); } @@ -2591,7 +2599,7 @@ glm::vec3 AttachmentDataObject::getTranslation() const { void AttachmentDataObject::setRotation(const glm::quat& rotation) { AttachmentData data = scriptvalue_cast(thisObject()); data.rotation = rotation; - Q_ASSERT(engine); + Q_ASSERT(engine()); thisObject() = engine()->toScriptValue(data); } @@ -2602,7 +2610,7 @@ glm::quat AttachmentDataObject::getRotation() const { void AttachmentDataObject::setScale(float scale) { AttachmentData data = scriptvalue_cast(thisObject()); data.scale = scale; - Q_ASSERT(engine); + Q_ASSERT(engine()); thisObject() = engine()->toScriptValue(data); } @@ -2613,7 +2621,7 @@ float AttachmentDataObject::getScale() const { void AttachmentDataObject::setIsSoft(bool isSoft) { AttachmentData data = scriptvalue_cast(thisObject()); data.isSoft = isSoft; - Q_ASSERT(engine); + Q_ASSERT(engine()); thisObject() = engine()->toScriptValue(data); } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 0b2a925de0..d3bf8a3282 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -610,6 +610,8 @@ public: AvatarData(); virtual ~AvatarData(); + virtual bool isMyAvatarURLProtected() const { return false; } // This needs to be here because both MyAvatar and AvatarData inherit from MyAvatar + static const QUrl& defaultFullAvatarModelUrl(); const QUuid getSessionUUID() const { return getID(); } @@ -1355,7 +1357,7 @@ public: */ Q_INVOKABLE virtual void detachAll(const QString& modelURL, const QString& jointName = QString()); - QString getSkeletonModelURLFromScript() const { return _skeletonModelURL.toString(); } + QString getSkeletonModelURLFromScript() const; void setSkeletonModelURLFromScript(const QString& skeletonModelString) { setSkeletonModelURL(QUrl(skeletonModelString)); } void setOwningAvatarMixer(const QWeakPointer& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; } diff --git a/libraries/avatars/src/ScriptAvatarData.cpp b/libraries/avatars/src/ScriptAvatarData.cpp index a07c402555..1d93a6e954 100644 --- a/libraries/avatars/src/ScriptAvatarData.cpp +++ b/libraries/avatars/src/ScriptAvatarData.cpp @@ -13,6 +13,7 @@ #include "ScriptAvatarData.h" +#include #include #include @@ -204,7 +205,12 @@ bool ScriptAvatarData::getLookAtSnappingEnabled() const { // QString ScriptAvatarData::getSkeletonModelURLFromScript() const { if (AvatarSharedPointer sharedAvatarData = _avatarData.lock()) { - return sharedAvatarData->getSkeletonModelURLFromScript(); + auto nodeList = DependencyManager::get(); + if (sharedAvatarData->isMyAvatar() && !sharedAvatarData->isMyAvatarURLProtected() && nodeList->getThisNodeCanViewAssetURLs()) { + return sharedAvatarData->getSkeletonModelURLFromScript(); + } + + return QString(); } else { return QString(); } diff --git a/libraries/baking/src/MaterialBaker.cpp b/libraries/baking/src/MaterialBaker.cpp index 48718cd6c3..631a10fd11 100644 --- a/libraries/baking/src/MaterialBaker.cpp +++ b/libraries/baking/src/MaterialBaker.cpp @@ -36,8 +36,7 @@ MaterialBaker::MaterialBaker(const QString& materialData, bool isURL, const QStr _isURL(isURL), _destinationPath(destinationPath), _bakedOutputDir(bakedOutputDir), - _textureOutputDir(bakedOutputDir + "/materialTextures/" + QString::number(materialNum++)), - _scriptEngine(newScriptEngine()) + _textureOutputDir(bakedOutputDir + "/materialTextures/" + QString::number(materialNum++)) { } @@ -214,16 +213,20 @@ void MaterialBaker::outputMaterial() { if (_materialResource->parsedMaterials.networkMaterials.size() == 1) { auto networkMaterial = _materialResource->parsedMaterials.networkMaterials.begin(); auto scriptableMaterial = scriptable::ScriptableMaterial(networkMaterial->second); - QVariant materialVariant = - scriptable::scriptableMaterialToScriptValue(_scriptEngine.get(), scriptableMaterial).toVariant(); - json.insert("materials", QJsonDocument::fromVariant(materialVariant).object()); + _helperScriptEngine.run( [&] { + QVariant materialVariant = + scriptable::scriptableMaterialToScriptValue(_helperScriptEngine.get(), scriptableMaterial).toVariant(); + json.insert("materials", QJsonDocument::fromVariant(materialVariant).object()); + }); } else { QJsonArray materialArray; for (auto networkMaterial : _materialResource->parsedMaterials.networkMaterials) { auto scriptableMaterial = scriptable::ScriptableMaterial(networkMaterial.second); - QVariant materialVariant = - scriptable::scriptableMaterialToScriptValue(_scriptEngine.get(), scriptableMaterial).toVariant(); - materialArray.append(QJsonDocument::fromVariant(materialVariant).object()); + _helperScriptEngine.run( [&] { + QVariant materialVariant = + scriptable::scriptableMaterialToScriptValue(_helperScriptEngine.get(), scriptableMaterial).toVariant(); + materialArray.append(QJsonDocument::fromVariant(materialVariant).object()); + }); } json.insert("materials", materialArray); } @@ -269,19 +272,32 @@ void MaterialBaker::setMaterials(const QHash& materials, _materialResource = NetworkMaterialResourcePointer(new NetworkMaterialResource(), [](NetworkMaterialResource* ptr) { ptr->deleteLater(); }); for (auto& material : materials) { _materialResource->parsedMaterials.names.push_back(material.name.toStdString()); - _materialResource->parsedMaterials.networkMaterials[material.name.toStdString()] = std::make_shared(material, baseURL); + if (!material.isMToonMaterial) { + _materialResource->parsedMaterials.networkMaterials[material.name.toStdString()] = std::make_shared(material, baseURL); + } else { + _materialResource->parsedMaterials.networkMaterials[material.name.toStdString()] = std::make_shared(material, baseURL); + } // Store any embedded texture content addTexture(material.name, image::TextureUsage::NORMAL_TEXTURE, material.normalTexture); addTexture(material.name, image::TextureUsage::ALBEDO_TEXTURE, material.albedoTexture); - addTexture(material.name, image::TextureUsage::GLOSS_TEXTURE, material.glossTexture); - addTexture(material.name, image::TextureUsage::ROUGHNESS_TEXTURE, material.roughnessTexture); - addTexture(material.name, image::TextureUsage::SPECULAR_TEXTURE, material.specularTexture); - addTexture(material.name, image::TextureUsage::METALLIC_TEXTURE, material.metallicTexture); addTexture(material.name, image::TextureUsage::EMISSIVE_TEXTURE, material.emissiveTexture); - addTexture(material.name, image::TextureUsage::OCCLUSION_TEXTURE, material.occlusionTexture); - addTexture(material.name, image::TextureUsage::SCATTERING_TEXTURE, material.scatteringTexture); - addTexture(material.name, image::TextureUsage::LIGHTMAP_TEXTURE, material.lightmapTexture); + + if (!material.isMToonMaterial) { + addTexture(material.name, image::TextureUsage::GLOSS_TEXTURE, material.glossTexture); + addTexture(material.name, image::TextureUsage::ROUGHNESS_TEXTURE, material.roughnessTexture); + addTexture(material.name, image::TextureUsage::SPECULAR_TEXTURE, material.specularTexture); + addTexture(material.name, image::TextureUsage::METALLIC_TEXTURE, material.metallicTexture); + addTexture(material.name, image::TextureUsage::OCCLUSION_TEXTURE, material.occlusionTexture); + addTexture(material.name, image::TextureUsage::SCATTERING_TEXTURE, material.scatteringTexture); + addTexture(material.name, image::TextureUsage::LIGHTMAP_TEXTURE, material.lightmapTexture); + } else { + addTexture(material.name, image::TextureUsage::ALBEDO_TEXTURE, material.shadeTexture); + addTexture(material.name, image::TextureUsage::ROUGHNESS_TEXTURE, material.shadingShiftTexture); + addTexture(material.name, image::TextureUsage::EMISSIVE_TEXTURE, material.matcapTexture); + addTexture(material.name, image::TextureUsage::ALBEDO_TEXTURE, material.rimTexture); + addTexture(material.name, image::TextureUsage::ROUGHNESS_TEXTURE, material.uvAnimationTexture); + } } } diff --git a/libraries/baking/src/MaterialBaker.h b/libraries/baking/src/MaterialBaker.h index 129b36aa8f..4fd8948b03 100644 --- a/libraries/baking/src/MaterialBaker.h +++ b/libraries/baking/src/MaterialBaker.h @@ -21,6 +21,7 @@ #include "TextureBaker.h" #include "baking/TextureFileNamer.h" +#include #include #include @@ -72,7 +73,7 @@ private: QString _textureOutputDir; QString _bakedMaterialData; - ScriptEnginePointer _scriptEngine; + HelperScriptEngine _helperScriptEngine; static std::function _getNextOvenWorkerThreadOperator; TextureFileNamer _textureFileNamer; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 4861bc6ecb..8fba576616 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -232,6 +232,14 @@ void EntityTreeRenderer::resetPersistentEntitiesScriptEngine() { _persistentEntitiesScriptManager = scriptManagerFactory(ScriptManager::ENTITY_CLIENT_SCRIPT, NO_SCRIPT, QString("about:Entities %1").arg(++_entitiesScriptEngineCount)); DependencyManager::get()->runScriptInitializers(_persistentEntitiesScriptManager); + + // Make script engine messages available through ScriptDiscoveryService + auto scriptEngines = DependencyManager::get().data(); + connect(_persistentEntitiesScriptManager.get(), &ScriptManager::infoEntityMessage, scriptEngines, &ScriptEngines::infoEntityMessage); + connect(_persistentEntitiesScriptManager.get(), &ScriptManager::printedEntityMessage, scriptEngines, &ScriptEngines::printedEntityMessage); + connect(_persistentEntitiesScriptManager.get(), &ScriptManager::errorEntityMessage, scriptEngines, &ScriptEngines::errorEntityMessage); + connect(_persistentEntitiesScriptManager.get(), &ScriptManager::warningEntityMessage, scriptEngines, &ScriptEngines::warningEntityMessage); + _persistentEntitiesScriptManager->runInThread(); std::shared_ptr entitiesScriptEngineProvider = _persistentEntitiesScriptManager; auto entityScriptingInterface = DependencyManager::get(); @@ -255,6 +263,14 @@ void EntityTreeRenderer::resetNonPersistentEntitiesScriptEngine() { _nonPersistentEntitiesScriptManager = scriptManagerFactory(ScriptManager::ENTITY_CLIENT_SCRIPT, NO_SCRIPT, QString("about:Entities %1").arg(++_entitiesScriptEngineCount)); DependencyManager::get()->runScriptInitializers(_nonPersistentEntitiesScriptManager); + + // Make script engine messages available through ScriptDiscoveryService + auto scriptEngines = DependencyManager::get().data(); + connect(_nonPersistentEntitiesScriptManager.get(), &ScriptManager::infoEntityMessage, scriptEngines, &ScriptEngines::infoEntityMessage); + connect(_nonPersistentEntitiesScriptManager.get(), &ScriptManager::printedEntityMessage, scriptEngines, &ScriptEngines::printedEntityMessage); + connect(_nonPersistentEntitiesScriptManager.get(), &ScriptManager::errorEntityMessage, scriptEngines, &ScriptEngines::errorEntityMessage); + connect(_nonPersistentEntitiesScriptManager.get(), &ScriptManager::warningEntityMessage, scriptEngines, &ScriptEngines::warningEntityMessage); + _nonPersistentEntitiesScriptManager->runInThread(); std::shared_ptr entitiesScriptEngineProvider = _nonPersistentEntitiesScriptManager; DependencyManager::get()->setNonPersistentEntitiesScriptEngine(entitiesScriptEngineProvider); @@ -912,7 +928,7 @@ QUuid EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, ray.direction, toPointerButton(*event), toPointerButtons(*event), - Qt::NoModifier); // TODO -- check for modifier keys? + event->modifiers()); emit entityScriptingInterface->mousePressOnEntity(rayPickResult.entityID, pointerEvent); @@ -943,9 +959,10 @@ void EntityTreeRenderer::mouseDoublePressEvent(QMouseEvent* event) { if (rayPickResult.intersects && (entity = getTree()->findEntityByID(rayPickResult.entityID))) { glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult); PointerEvent pointerEvent(PointerEvent::Press, PointerManager::MOUSE_POINTER_ID, - pos2D, rayPickResult.intersection, - rayPickResult.surfaceNormal, ray.direction, - toPointerButton(*event), toPointerButtons(*event), Qt::NoModifier); + pos2D, rayPickResult.intersection, + rayPickResult.surfaceNormal, ray.direction, + toPointerButton(*event), toPointerButtons(*event), + event->modifiers()); emit entityScriptingInterface->mouseDoublePressOnEntity(rayPickResult.entityID, pointerEvent); @@ -979,7 +996,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) { pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, ray.direction, toPointerButton(*event), toPointerButtons(*event), - Qt::NoModifier); // TODO -- check for modifier keys? + event->modifiers()); emit entityScriptingInterface->mouseReleaseOnEntity(rayPickResult.entityID, pointerEvent); @@ -995,7 +1012,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) { pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, ray.direction, toPointerButton(*event), toPointerButtons(*event), - Qt::NoModifier); // TODO -- check for modifier keys? + event->modifiers()); emit entityScriptingInterface->clickReleaseOnEntity(_currentClickingOnEntityID, pointerEvent); } @@ -1022,7 +1039,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, ray.direction, toPointerButton(*event), toPointerButtons(*event), - Qt::NoModifier); // TODO -- check for modifier keys? + event->modifiers()); emit entityScriptingInterface->mouseMoveOnEntity(rayPickResult.entityID, pointerEvent); @@ -1036,7 +1053,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, ray.direction, toPointerButton(*event), toPointerButtons(*event), - Qt::NoModifier); // TODO -- check for modifier keys? + event->modifiers()); emit entityScriptingInterface->hoverLeaveEntity(_currentHoverOverEntityID, pointerEvent); } @@ -1064,10 +1081,10 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { if (!_currentHoverOverEntityID.isInvalidID()) { glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult); PointerEvent pointerEvent(PointerEvent::Move, PointerManager::MOUSE_POINTER_ID, - pos2D, rayPickResult.intersection, - rayPickResult.surfaceNormal, ray.direction, + pos2D, rayPickResult.intersection, + rayPickResult.surfaceNormal, ray.direction, toPointerButton(*event), toPointerButtons(*event), - Qt::NoModifier); // TODO -- check for modifier keys? + event->modifiers()); emit entityScriptingInterface->hoverLeaveEntity(_currentHoverOverEntityID, pointerEvent); _currentHoverOverEntityID = UNKNOWN_ENTITY_ID; // makes it the unknown ID diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 2b57c8b78a..c68651d42c 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -4,6 +4,7 @@ // // Created by Brad Hefta-Gaub on 12/6/13. // Copyright 2013 High Fidelity, Inc. +// Copyright 2024 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 @@ -202,6 +203,8 @@ ItemKey EntityRenderer::getKey() { builder.withInvisible(); } + updateItemKeyBuilderFromMaterials(builder); + return builder; } @@ -328,6 +331,20 @@ ItemID EntityRenderer::computeMirrorViewOperator(ViewFrustum& viewFrustum, const return foundPortalExit ? DependencyManager::get()->renderableIdForEntityId(portalExitID) : Item::INVALID_ITEM_ID; } +HighlightStyle EntityRenderer::getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const { + std::lock_guard lock(_materialsLock); + auto materials = _materials.find("0"); + if (materials != _materials.end()) { + glm::vec3 position; + withReadLock([&] { + position = _renderTransform.getTranslation(); + }); + return HighlightStyle::calculateOutlineStyle(materials->second.getOutlineWidthMode(), materials->second.getOutlineWidth(), + materials->second.getOutline(), position, viewFrustum, height); + } + return HighlightStyle(); +} + void EntityRenderer::render(RenderArgs* args) { if (!isValidRenderItem()) { return; @@ -627,7 +644,7 @@ EntityRenderer::Pipeline EntityRenderer::getPipelineType(const graphics::MultiMa } graphics::MaterialKey drawMaterialKey = materials.getMaterialKey(); - if (drawMaterialKey.isEmissive() || drawMaterialKey.isMetallic() || drawMaterialKey.isScattering()) { + if (materials.isMToon() || drawMaterialKey.isEmissive() || drawMaterialKey.isMetallic() || drawMaterialKey.isScattering()) { return Pipeline::MATERIAL; } @@ -747,6 +764,26 @@ Item::Bound EntityRenderer::getMaterialBound(RenderArgs* args) { return EntityRenderer::getBound(args); } +void EntityRenderer::updateItemKeyBuilderFromMaterials(ItemKey::Builder& builder) { + MaterialMap::iterator materials; + { + std::lock_guard lock(_materialsLock); + materials = _materials.find("0"); + + if (materials != _materials.end()) { + if (materials->second.shouldUpdate()) { + RenderPipelines::updateMultiMaterial(materials->second); + } + } else { + return; + } + } + + if (materials->second.hasOutline()) { + builder.withOutline(); + } +} + void EntityRenderer::updateShapeKeyBuilderFromMaterials(ShapeKey::Builder& builder) { MaterialMap::iterator materials; { @@ -773,7 +810,7 @@ void EntityRenderer::updateShapeKeyBuilderFromMaterials(ShapeKey::Builder& build builder.withCullFaceMode(materials->second.getCullFaceMode()); graphics::MaterialKey drawMaterialKey = materials->second.getMaterialKey(); - if (drawMaterialKey.isUnlit()) { + if (!materials->second.isMToon() && drawMaterialKey.isUnlit()) { builder.withUnlit(); } @@ -783,8 +820,12 @@ void EntityRenderer::updateShapeKeyBuilderFromMaterials(ShapeKey::Builder& build if (drawMaterialKey.isNormalMap()) { builder.withTangents(); } - if (drawMaterialKey.isLightMap()) { - builder.withLightMap(); + if (!materials->second.isMToon()) { + if (drawMaterialKey.isLightMap()) { + builder.withLightMap(); + } + } else { + builder.withMToon(); } } else if (pipelineType == Pipeline::PROCEDURAL) { builder.withOwnPipeline(); diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 495eeea220..949590c472 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -4,6 +4,7 @@ // // Created by Brad Hefta-Gaub on 12/6/13. // Copyright 2013 High Fidelity, Inc. +// Copyright 2024 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 @@ -13,6 +14,8 @@ #define hifi_RenderableEntityItem_h #include +#include + #include #include #include "AbstractViewStateInterface.h" @@ -79,6 +82,7 @@ public: static ItemID computeMirrorViewOperator(ViewFrustum& viewFrustum, const glm::vec3& inPropertiesPosition, const glm::quat& inPropertiesRotation, MirrorMode mirrorMode, const QUuid& portalExitID); virtual void renderSimulate(RenderArgs* args) override {} + virtual HighlightStyle getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const override; protected: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } @@ -138,6 +142,7 @@ protected: void updateMaterials(bool baseMaterialChanged = false); bool materialsTransparent() const; Item::Bound getMaterialBound(RenderArgs* args); + void updateItemKeyBuilderFromMaterials(ItemKey::Builder& builder); void updateShapeKeyBuilderFromMaterials(ShapeKey::Builder& builder); Item::Bound _bound; diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index 66a5d0d609..0b76038cd4 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -130,8 +130,7 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { materials = _materials["0"]; } - auto& schema = materials.getSchemaBuffer().get(); - glm::vec4 color = glm::vec4(ColorUtils::tosRGBVec3(schema._albedo), schema._opacity); + glm::vec4 color = materials.getColor(); color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created); if (!_texture || !_texture->isLoaded() || color.a == 0.0f) { diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index fe44c41094..576e842f84 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -1,6 +1,7 @@ // // Created by Sam Gondelman on 1/18/2018 // Copyright 2018 High Fidelity, Inc. +// Copyright 2024 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 @@ -219,7 +220,7 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo ItemKey MaterialEntityRenderer::getKey() { auto builder = ItemKey::Builder().withTypeShape().withTagBits(getTagMask()).withLayer(getHifiRenderLayer()); - if (!_visible) { + if (!_visible || !_parentID.isNull()) { builder.withInvisible(); } @@ -229,6 +230,10 @@ ItemKey MaterialEntityRenderer::getKey() { if (matKey.isTranslucent()) { builder.withTransparent(); } + + if (drawMaterial->getOutlineWidthMode() != NetworkMToonMaterial::OutlineWidthMode::OUTLINE_NONE && drawMaterial->getOutlineWidth() > 0.0f) { + builder.withOutline(); + } } return builder.build(); @@ -258,11 +263,16 @@ ShapeKey MaterialEntityRenderer::getShapeKey() { if (drawMaterialKey.isNormalMap()) { builder.withTangents(); } - if (drawMaterialKey.isLightMap()) { - builder.withLightMap(); - } - if (drawMaterialKey.isUnlit()) { - builder.withUnlit(); + + if (drawMaterial && drawMaterial->isMToon()) { + builder.withMToon(); + } else { + if (drawMaterialKey.isLightMap()) { + builder.withLightMap(); + } + if (drawMaterialKey.isUnlit()) { + builder.withUnlit(); + } } } @@ -273,6 +283,18 @@ ShapeKey MaterialEntityRenderer::getShapeKey() { return builder.build(); } +HighlightStyle MaterialEntityRenderer::getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const { + if (const auto drawMaterial = getMaterial()) { + glm::vec3 position; + withReadLock([&] { + position = _renderTransform.getTranslation(); + }); + return HighlightStyle::calculateOutlineStyle(drawMaterial->getOutlineWidthMode(), drawMaterial->getOutlineWidth(), + drawMaterial->getOutline(), position, viewFrustum, height); + } + return HighlightStyle(); +} + void MaterialEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("RenderableMaterialEntityItem::render"); Q_ASSERT(args->_batch); @@ -316,21 +338,26 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) { } // Draw! - DependencyManager::get()->renderSphere(batch); + const uint32_t compactColor = 0xFFFFFFFF; + _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); + DependencyManager::get()->renderShape(batch, GeometryCache::Shape::Sphere, _colorBuffer); } else { auto proceduralDrawMaterial = std::static_pointer_cast(drawMaterial); glm::vec4 outColor = glm::vec4(drawMaterial->getAlbedo(), drawMaterial->getOpacity()); outColor = proceduralDrawMaterial->getColor(outColor); proceduralDrawMaterial->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f)); + + const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); if (render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES) { - DependencyManager::get()->renderWireSphere(batch, outColor); + DependencyManager::get()->renderWireShape(batch, GeometryCache::Shape::Sphere, _colorBuffer); } else { - DependencyManager::get()->renderSphere(batch, outColor); + DependencyManager::get()->renderShape(batch, GeometryCache::Shape::Sphere, _colorBuffer); } } - args->_details._trianglesRendered += (int)DependencyManager::get()->getSphereTriangleCount(); + args->_details._trianglesRendered += (int)DependencyManager::get()->getShapeTriangleCount(GeometryCache::Shape::Sphere); } void MaterialEntityRenderer::setCurrentMaterialName(const std::string& currentMaterialName) { diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h index 25403e8a2b..efded3aab3 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h @@ -1,6 +1,7 @@ // // Created by Sam Gondelman on 1/18/2018 // Copyright 2018 High Fidelity, Inc. +// Copyright 2024 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 @@ -32,6 +33,7 @@ private: virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; virtual void doRender(RenderArgs* args) override; + virtual HighlightStyle getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const override; ItemKey getKey() override; ShapeKey getShapeKey() override; @@ -65,6 +67,7 @@ private: std::shared_ptr _appliedMaterial; std::string _currentMaterialName; + gpu::BufferPointer _colorBuffer { std::make_shared() }; }; } } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 8fa787d413..6393f76603 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -21,23 +21,48 @@ using namespace render::entities; static uint8_t CUSTOM_PIPELINE_NUMBER = 0; static gpu::Stream::FormatPointer _vertexFormat; -static std::weak_ptr _texturedPipeline; +// forward, transparent, shadow, wireframe +static std::map, gpu::PipelinePointer> _pipelines; static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key, RenderArgs* args) { - auto texturedPipeline = _texturedPipeline.lock(); - if (!texturedPipeline) { - auto state = std::make_shared(); - state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, false, gpu::LESS_EQUAL); - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - PrepareStencil::testMask(*state); + if (_pipelines.empty()) { + using namespace shader::entities_renderer::program; - auto program = gpu::Shader::createProgram(shader::entities_renderer::program::textured_particle); - _texturedPipeline = texturedPipeline = gpu::Pipeline::create(program, state); + // forward, translucent, shadow + static const std::vector> keys = { + std::make_tuple(false, false, false, textured_particle), + std::make_tuple(true, false, false, textured_particle_forward), + std::make_tuple(false, true, false, textured_particle_translucent), + std::make_tuple(true, true, false, textured_particle_translucent_forward), + std::make_tuple(false, false, true, textured_particle_shadow), + // no such thing as shadow and forward/translucent + }; + + for (auto& key : keys) { + for (int i = 0; i < 2; ++i) { + bool transparent = std::get<1>(key); + bool wireframe = i == 0; + + auto state = std::make_shared(); + state->setCullMode(gpu::State::CULL_BACK); + + if (wireframe) { + state->setFillMode(gpu::State::FILL_LINE); + } + + state->setDepthTest(true, !transparent, gpu::LESS_EQUAL); + state->setBlendFunction(transparent, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + transparent ? PrepareStencil::testMask(*state) : PrepareStencil::testMaskDrawShape(*state); + + auto program = gpu::Shader::createProgram(std::get<3>(key)); + _pipelines[std::make_tuple(std::get<0>(key), transparent, std::get<2>(key), wireframe)] = gpu::Pipeline::create(program, state); + } + } } - return std::make_shared(texturedPipeline, nullptr, nullptr, nullptr); + return std::make_shared(_pipelines[std::make_tuple(args->_renderMethod == Args::RenderMethod::FORWARD, key.isTranslucent(), + args->_renderMode == Args::RenderMode::SHADOW_RENDER_MODE, key.isWireframe())], nullptr, nullptr, nullptr); } struct GpuParticle { @@ -138,28 +163,31 @@ void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEn _uniformBuffer.edit() = particleUniforms; } +bool ParticleEffectEntityRenderer::isTransparent() const { + bool particleTransparent = _particleProperties.getColorStart().a < 1.0f || _particleProperties.getColorMiddle().a < 1.0f || + _particleProperties.getColorFinish().a < 1.0f || _particleProperties.getColorSpread().a > 0.0f || + _pulseProperties.getAlphaMode() != PulseMode::NONE || (_textureLoaded && _networkTexture && _networkTexture->getGPUTexture() && + _networkTexture->getGPUTexture()->getUsage().isAlpha() && !_networkTexture->getGPUTexture()->getUsage().isAlphaMask()); + return particleTransparent || Parent::isTransparent(); +} + ItemKey ParticleEffectEntityRenderer::getKey() { - // FIXME: implement isTransparent() for particles and an opaque pipeline - auto builder = ItemKey::Builder::transparentShape().withTagBits(getTagMask()).withLayer(getHifiRenderLayer()); - - if (!_visible) { - builder.withInvisible(); - } - - if (_cullWithParent) { - builder.withSubMetaCulled(); - } - + auto builder = ItemKey::Builder(Parent::getKey()); builder.withSimulate(); - return builder.build(); } ShapeKey ParticleEffectEntityRenderer::getShapeKey() { - auto builder = ShapeKey::Builder().withCustom(CUSTOM_PIPELINE_NUMBER).withTranslucent(); + auto builder = ShapeKey::Builder().withCustom(CUSTOM_PIPELINE_NUMBER); + + if (isTransparent()) { + builder.withTranslucent(); + } + if (_primitiveMode == PrimitiveMode::LINES) { builder.withWireframe(); } + return builder.build(); } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index b3414594c3..d9a1745b06 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -30,6 +30,7 @@ protected: virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; + bool isTransparent() const override; virtual ItemKey getKey() override; virtual ShapeKey getShapeKey() override; virtual Item::Bound getBound(RenderArgs* args) override; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 26091a1ed4..bda800abe2 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1719,21 +1719,28 @@ using namespace render; using namespace render::entities; static uint8_t CUSTOM_PIPELINE_NUMBER; -static std::map, ShapePipelinePointer> _pipelines; +// forward, shadow, fade, wireframe +static std::map, ShapePipelinePointer> _pipelines; static gpu::Stream::FormatPointer _vertexFormat; ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key, RenderArgs* args) { - // FIXME: custom pipelines like this don't handle shadows or renderLayers correctly - if (_pipelines.empty()) { using namespace shader::entities_renderer::program; - static const std::vector> keys = { - std::make_tuple(false, false, polyvox), std::make_tuple(true, false, polyvox_forward) + // forward, shadow, fade + static const std::vector> keys = { + std::make_tuple(false, false, false, polyvox), + std::make_tuple(true, false, false, polyvox_forward), + std::make_tuple(false, true, false, polyvox_shadow), + // no such thing as forward + shadow #ifdef POLYVOX_ENTITY_USE_FADE_EFFECT - , std::make_tuple(false, true, polyvox_fade), std::make_tuple(true, true, polyvox_forward_fade) + std::make_tuple(false, false, true, polyvox_fade), + std::make_tuple(false, true, true, polyvox_shadow_fade), + // no such thing as forward + fade/shadow #else - , std::make_tuple(false, true, polyvox), std::make_tuple(true, true, polyvox_forward) + std::make_tuple(false, false, true, polyvox), + std::make_tuple(false, true, true, polyvox_shadow), + // no such thing as forward + fade/shadow #endif }; for (auto& key : keys) { @@ -1749,19 +1756,19 @@ ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const Sha state->setFillMode(gpu::State::FILL_LINE); } - auto pipeline = gpu::Pipeline::create(gpu::Shader::createProgram(std::get<2>(key)), state); - if (std::get<1>(key)) { - _pipelines[std::make_tuple(std::get<0>(key), std::get<1>(key), wireframe)] = std::make_shared(pipeline, nullptr, nullptr, nullptr); + auto pipeline = gpu::Pipeline::create(gpu::Shader::createProgram(std::get<3>(key)), state); + if (!std::get<2>(key)) { + _pipelines[std::make_tuple(std::get<0>(key), std::get<1>(key), std::get<2>(key), wireframe)] = std::make_shared(pipeline, nullptr, nullptr, nullptr); } else { const auto& fadeEffect = DependencyManager::get(); - _pipelines[std::make_tuple(std::get<0>(key), std::get<1>(key), wireframe)] = std::make_shared(pipeline, nullptr, + _pipelines[std::make_tuple(std::get<0>(key), std::get<1>(key), std::get<2>(key), wireframe)] = std::make_shared(pipeline, nullptr, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); } } } } - return _pipelines[std::make_tuple(args->_renderMethod == Args::RenderMethod::FORWARD, key.isFaded(), key.isWireframe())]; + return _pipelines[std::make_tuple(args->_renderMethod == Args::RenderMethod::FORWARD, args->_renderMode == Args::RenderMode::SHADOW_RENDER_MODE, key.isFaded(), key.isWireframe())]; } PolyVoxEntityRenderer::PolyVoxEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { @@ -1775,16 +1782,6 @@ PolyVoxEntityRenderer::PolyVoxEntityRenderer(const EntityItemPointer& entity) : _params = std::make_shared(sizeof(glm::vec4), nullptr); } -ItemKey PolyVoxEntityRenderer::getKey() { - auto builder = ItemKey::Builder::opaqueShape().withTagBits(getTagMask()).withLayer(getHifiRenderLayer()); - - if (_cullWithParent) { - builder.withSubMetaCulled(); - } - - return builder.build(); -} - ShapeKey PolyVoxEntityRenderer::getShapeKey() { auto builder = ShapeKey::Builder().withCustom(CUSTOM_PIPELINE_NUMBER); if (_primitiveMode == PrimitiveMode::LINES) { @@ -1867,13 +1864,7 @@ void PolyVoxEntityRenderer::doRender(RenderArgs* args) { batch.setModelTransform(transform); batch.setInputFormat(_vertexFormat); - batch.setInputBuffer(gpu::Stream::POSITION, _mesh->getVertexBuffer()._buffer, 0, - sizeof(PolyVox::PositionMaterialNormal)); - - // TODO -- should we be setting this? - // batch.setInputBuffer(gpu::Stream::NORMAL, mesh->getVertexBuffer()._buffer, - // 12, - // sizeof(PolyVox::PositionMaterialNormal)); + batch.setInputBuffer(gpu::Stream::POSITION, _mesh->getVertexBuffer()._buffer, 0, sizeof(PolyVox::PositionMaterialNormal)); batch.setIndexBuffer(gpu::UINT32, _mesh->getIndexBuffer()._buffer, 0); for (size_t i = 0; i < _xyzTextures.size(); ++i) { diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index c1c35a21c8..1debeb957c 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -203,7 +203,6 @@ public: } protected: - virtual ItemKey getKey() override; virtual ShapeKey getShapeKey() override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 82350f54bf..2286045d5e 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -99,8 +99,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { materials = _materials["0"]; } - auto& schema = materials.getSchemaBuffer().get(); - glm::vec4 outColor = glm::vec4(ColorUtils::tosRGBVec3(schema._albedo), schema._opacity); + glm::vec4 outColor = materials.getColor(); outColor = EntityRenderer::calculatePulseColor(outColor, _pulseProperties, _created); if (outColor.a == 0.0f) { @@ -133,10 +132,12 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { procedural->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f)); }); + const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); if (wireframe) { - geometryCache->renderWireShape(batch, geometryShape, outColor); + geometryCache->renderWireShape(batch, geometryShape, _colorBuffer); } else { - geometryCache->renderShape(batch, geometryShape, outColor); + geometryCache->renderShape(batch, geometryShape, _colorBuffer); } } else if (pipelineType == Pipeline::SIMPLE) { // FIXME, support instanced multi-shape rendering using multidraw indirect @@ -151,10 +152,12 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, pipeline); } } else { + const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); if (wireframe) { - geometryCache->renderWireShape(batch, geometryShape, outColor); + geometryCache->renderWireShape(batch, geometryShape, _colorBuffer); } else { - geometryCache->renderShape(batch, geometryShape, outColor); + geometryCache->renderShape(batch, geometryShape, _colorBuffer); } } } else { @@ -162,7 +165,9 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { args->_details._materialSwitches++; } - geometryCache->renderShape(batch, geometryShape); + const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); + geometryCache->renderShape(batch, geometryShape, _colorBuffer); } const auto triCount = geometryCache->getShapeTriangleCount(geometryShape); @@ -179,7 +184,7 @@ scriptable::ScriptableModelBase ShapeEntityRenderer::getScriptableModel() { result.appendMaterials(_materials); auto materials = _materials.find("0"); if (materials != _materials.end()) { - vertexColor = ColorUtils::tosRGBVec3(materials->second.getSchemaBuffer().get()._albedo); + vertexColor = materials->second.getColor(); } } if (auto mesh = geometryCache->meshFromShape(geometryShape, vertexColor)) { diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 686014f4de..fd7bd4795b 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -42,6 +42,8 @@ private: std::shared_ptr _material { std::make_shared() }; glm::vec3 _color { NAN }; float _alpha { NAN }; + + gpu::BufferPointer _colorBuffer { std::make_shared() }; }; } } diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index a15e2839a4..1cfab07986 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -147,8 +147,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { materials = _materials["0"]; } - auto& schema = materials.getSchemaBuffer().get(); - glm::vec4 backgroundColor = glm::vec4(ColorUtils::tosRGBVec3(schema._albedo), schema._opacity); + glm::vec4 backgroundColor = materials.getColor(); backgroundColor = EntityRenderer::calculatePulseColor(backgroundColor, _pulseProperties, _created); if (backgroundColor.a <= 0.0f) { diff --git a/libraries/entities-renderer/src/entities-renderer/textured_particle.slp b/libraries/entities-renderer/src/entities-renderer/textured_particle.slp index e69de29bb2..80b57dee25 100644 --- a/libraries/entities-renderer/src/entities-renderer/textured_particle.slp +++ b/libraries/entities-renderer/src/entities-renderer/textured_particle.slp @@ -0,0 +1 @@ +DEFINES (translucent:f forward:f)/shadow:f \ No newline at end of file diff --git a/libraries/entities-renderer/src/textured_particle.slf b/libraries/entities-renderer/src/textured_particle.slf index 7dadb6fc49..04b74771a0 100644 --- a/libraries/entities-renderer/src/textured_particle.slf +++ b/libraries/entities-renderer/src/textured_particle.slf @@ -15,8 +15,34 @@ LAYOUT(binding=0) uniform sampler2D colorMap; layout(location=0) in vec4 varColor; layout(location=1) in vec2 varTexcoord; -layout(location=0) out vec4 outFragColor; +<@if HIFI_USE_FORWARD or HIFI_USE_SHADOW@> + layout(location=0) out vec4 _fragColor0; +<@else@> + <@include DeferredBufferWrite.slh@> +<@endif@> void main(void) { - outFragColor = texture(colorMap, varTexcoord.xy) * varColor; + vec4 albedo = texture(colorMap, varTexcoord.xy) * varColor; + +<@if HIFI_USE_FORWARD or HIFI_USE_SHADOW@> + <@if not HIFI_USE_TRANSLUCENT@> + // to reduce texel flickering for floating point error we discard when alpha is "almost one" + if (albedo.a < 0.999999) { + discard; + } + <@endif@> + +<@if HIFI_USE_FORWARD@> + _fragColor0 = albedo; +<@else@> + _fragColor0 = vec4(1.0); +<@endif@> +<@else@> + vec3 NORMAL = vec3(1.0, 0.0, 0.0); + <@if not HIFI_USE_TRANSLUCENT@> + packDeferredFragmentUnlit(NORMAL, albedo.a, albedo.rgb); + <@else@> + packDeferredFragmentTranslucent(NORMAL, albedo.a, albedo.rgb, DEFAULT_ROUGHNESS); + <@endif@> +<@endif@> } diff --git a/libraries/entities-renderer/src/textured_particle.slv b/libraries/entities-renderer/src/textured_particle.slv index 98d25eae2e..9fd5e87d32 100644 --- a/libraries/entities-renderer/src/textured_particle.slv +++ b/libraries/entities-renderer/src/textured_particle.slv @@ -152,9 +152,9 @@ void main(void) { <$transformModelToWorldDir(cam, obj, UP, modelUpWorld)$> vec3 upWorld = mix(UP, normalize(modelUpWorld), float(particle.rotateWithEntity)); vec3 upEye = normalize(view3 * upWorld); - vec3 FORWARD = vec3(0, 0, -1); - vec3 particleRight = normalize(cross(FORWARD, upEye)); - vec3 particleUp = cross(particleRight, FORWARD); // don't need to normalize + vec3 eyeToParticle = normalize(anchorPoint.xyz - vec3(0.0)); + vec3 particleRight = cross(eyeToParticle, upEye); + vec3 particleUp = cross(particleRight, eyeToParticle); // don't need to normalize // This ordering ensures that un-rotated particles render upright in the viewer. vec3 UNIT_QUAD[NUM_VERTICES_PER_PARTICLE] = vec3[NUM_VERTICES_PER_PARTICLE]( normalize(-particleRight + particleUp), diff --git a/libraries/entities/src/AmbientLightPropertyGroup.cpp b/libraries/entities/src/AmbientLightPropertyGroup.cpp index 829d8ecdf6..c430688113 100644 --- a/libraries/entities/src/AmbientLightPropertyGroup.cpp +++ b/libraries/entities/src/AmbientLightPropertyGroup.cpp @@ -22,10 +22,12 @@ const float AmbientLightPropertyGroup::DEFAULT_AMBIENT_LIGHT_INTENSITY = 0.5f; void AmbientLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { - + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const { + + auto nodeList = DependencyManager::get(); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_LIGHT_INTENSITY, AmbientLight, ambientLight, AmbientIntensity, ambientIntensity); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_LIGHT_URL, AmbientLight, ambientLight, AmbientURL, ambientURL); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_AMBIENT_LIGHT_URL, AmbientLight, ambientLight, AmbientURL, ambientURL); } void AmbientLightPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { diff --git a/libraries/entities/src/AmbientLightPropertyGroup.h b/libraries/entities/src/AmbientLightPropertyGroup.h index 52451a6f8b..67597d1713 100644 --- a/libraries/entities/src/AmbientLightPropertyGroup.h +++ b/libraries/entities/src/AmbientLightPropertyGroup.h @@ -43,7 +43,8 @@ public: // EntityItemProperty related helpers virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties) const override; + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const override; virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; void merge(const AmbientLightPropertyGroup& other); diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index e44bc135e1..b1b5c08295 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -57,8 +57,11 @@ bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b * @property {boolean} smoothFrames=true - true if the frames of the animation should be linearly interpolated to * create smoother movement, false if the frames should not be interpolated. */ -void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url); +void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const { + auto nodeList = DependencyManager::get(); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_ANIMATION_URL, Animation, animation, URL, url); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FPS, Animation, animation, FPS, fps); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame); diff --git a/libraries/entities/src/AnimationPropertyGroup.h b/libraries/entities/src/AnimationPropertyGroup.h index b90417d78e..875a1f7126 100644 --- a/libraries/entities/src/AnimationPropertyGroup.h +++ b/libraries/entities/src/AnimationPropertyGroup.h @@ -37,7 +37,8 @@ public: // EntityItemProperty related helpers virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties) const override; + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const override; virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; void merge(const AnimationPropertyGroup& other); diff --git a/libraries/entities/src/BloomPropertyGroup.cpp b/libraries/entities/src/BloomPropertyGroup.cpp index f785dc7465..18ad85d797 100644 --- a/libraries/entities/src/BloomPropertyGroup.cpp +++ b/libraries/entities/src/BloomPropertyGroup.cpp @@ -18,7 +18,9 @@ #include "EntityItemProperties.h" #include "EntityItemPropertiesMacros.h" -void BloomPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { +void BloomPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_INTENSITY, Bloom, bloom, BloomIntensity, bloomIntensity); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_THRESHOLD, Bloom, bloom, BloomThreshold, bloomThreshold); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_SIZE, Bloom, bloom, BloomSize, bloomSize); diff --git a/libraries/entities/src/BloomPropertyGroup.h b/libraries/entities/src/BloomPropertyGroup.h index 44b2d18a39..d459bb2f3c 100644 --- a/libraries/entities/src/BloomPropertyGroup.h +++ b/libraries/entities/src/BloomPropertyGroup.h @@ -44,7 +44,8 @@ public: // EntityItemProperty related helpers virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties) const override; + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const override; virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; void merge(const BloomPropertyGroup& other); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 60559e54c7..54d20301ae 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include "EntitiesLogging.h" #include "EntityItem.h" @@ -1430,7 +1431,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {boolean} unlit=false - true if the entity is unaffected by lighting, false if it is lit * by the key light and local lights. * @property {string} font="" - The font to render the text with. It can be one of the following: "Courier", - * "Inconsolata", "Roboto", "Timeless", or a path to a .sdff file. + * "Inconsolata", "Roboto", "Timeless", or a path to a PNG MTSDF .arfont file generated + * by the msdf-atlas-gen tool (https://github.com/Chlumsky/msdf-atlas-gen). * @property {Entities.TextEffect} textEffect="none" - The effect that is applied to the text. * @property {Color} textEffectColor=255,255,255 - The color of the effect. * @property {number} textEffectThickness=0.2 - The magnitude of the text effect, range 0.00.5. @@ -1665,13 +1667,15 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s const bool pseudoPropertyFlagsActive = pseudoPropertyFlags.test(EntityPseudoPropertyFlag::FlagsActive); // Fix to skip the default return all mechanism, when pseudoPropertyFlagsActive - const bool pseudoPropertyFlagsButDesiredEmpty = pseudoPropertyFlagsActive && _desiredProperties.isEmpty(); + const bool returnNothingOnEmptyPropertyFlags = pseudoPropertyFlagsActive; if (_created == UNKNOWN_CREATED_TIME && !allowUnknownCreateTime) { // No entity properties can have been set so return without setting any default, zero property values. return properties; } + auto nodeList = DependencyManager::get(); + bool isMyOwnAvatarEntity = _entityHostType == entity::HostType::AVATAR && (_owningAvatarID == AVATAR_SELF_ID || _owningAvatarID == Physics::getSessionUUID()); if (_idSet && (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::ID))) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(id, _id.toString()); } @@ -1722,7 +1726,7 @@ 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()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TAGS, tags, getTagsAsVector()); - _grab.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + _grab.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_MIRROR_MODE, mirrorMode, getMirrorModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PORTAL_EXIT_ID, portalExitID); @@ -1743,7 +1747,7 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_COLLISION_MASK, collisionMask, collidesWith, getCollisionMaskAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DYNAMIC, dynamic); COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_DYNAMIC, dynamic, collisionsWillMove, getDynamic()); // legacy support - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_SOUND_URL, collisionSoundURL); + COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_COLLISION_SOUND_URL, collisionSoundURL); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ACTION_DATA, actionData); // Cloning @@ -1769,10 +1773,10 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s // Particles only if (_type == EntityTypes::ParticleEffect) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); + COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); - _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_PARTICLES, maxParticles); @@ -1829,11 +1833,11 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s // Models only if (_type == EntityTypes::Model) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); + COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MODEL_URL, modelURL); + COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_MODEL_URL, modelURL); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MODEL_SCALE, modelScale); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_ROTATIONS_SET, jointRotationsSet); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_ROTATIONS, jointRotations); @@ -1843,9 +1847,7 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GROUP_CULLED, groupCulled); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USE_ORIGINAL_PIVOT, useOriginalPivot); - if (!pseudoPropertyFlagsButDesiredEmpty) { - _animation.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); - } + _animation.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); } // FIXME: Shouldn't provide a shapeType property for Box and Sphere entities. @@ -1859,7 +1861,7 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s if (_type == EntityTypes::Box || _type == EntityTypes::Sphere || _type == EntityTypes::Shape) { COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); - _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHAPE, shape); } @@ -1875,7 +1877,7 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s // Text only if (_type == EntityTypes::Text) { - _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT, text); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_HEIGHT, lineHeight); @@ -1898,20 +1900,18 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s // Zones only if (_type == EntityTypes::Zone) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); + COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); - if (!pseudoPropertyFlagsButDesiredEmpty) { - _keyLight.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); - _ambientLight.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); - _skybox.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); - _haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); - _bloom.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); - _audio.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); - } + _keyLight.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); + _ambientLight.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); + _skybox.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); + _haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); + _bloom.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); + _audio.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FLYING_ALLOWED, flyingAllowed); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GHOSTING_ALLOWED, ghostingAllowed); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FILTER_URL, filterURL); + COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_FILTER_URL, filterURL); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_KEY_LIGHT_MODE, keyLightMode, getKeyLightModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_LIGHT_MODE, ambientLightMode, getAmbientLightModeAsString()); @@ -1926,11 +1926,11 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s if (_type == EntityTypes::Web) { COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); - _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOURCE_URL, sourceUrl); + COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_SOURCE_URL, sourceUrl); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DPI, dpi); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SCRIPT_URL, scriptURL); + COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_SCRIPT_URL, scriptURL); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_FPS, maxFPS); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_INPUT_MODE, inputMode, getInputModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_WANTS_KEYBOARD_FOCUS, wantsKeyboardFocus); @@ -1944,9 +1944,9 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VOXEL_DATA, voxelData); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VOXEL_SURFACE_STYLE, voxelSurfaceStyle); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_X_TEXTURE_URL, xTextureURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_Y_TEXTURE_URL, yTextureURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_Z_TEXTURE_URL, zTextureURL); + COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_X_TEXTURE_URL, xTextureURL); + COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_Y_TEXTURE_URL, yTextureURL); + COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_Z_TEXTURE_URL, zTextureURL); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_X_N_NEIGHBOR_ID, xNNeighborID); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_Y_N_NEIGHBOR_ID, yNNeighborID); @@ -1980,14 +1980,14 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s // Materials if (_type == EntityTypes::Material) { - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_URL, materialURL); + COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_MATERIAL_URL, materialURL); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_MATERIAL_MAPPING_MODE, materialMappingMode, getMaterialMappingModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_PRIORITY, priority); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARENT_MATERIAL_NAME, parentMaterialName); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_MAPPING_POS, materialMappingPos); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_MAPPING_SCALE, materialMappingScale); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_MAPPING_ROT, materialMappingRot); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_DATA, materialData); + COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_MATERIAL_DATA, materialData); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_REPEAT, materialRepeat); } @@ -1995,15 +1995,16 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s if (_type == EntityTypes::Image) { COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); - _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IMAGE_URL, imageURL); + COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_IMAGE_URL, imageURL); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMISSIVE, emissive); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_KEEP_ASPECT_RATIO, keepAspectRatio); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SUB_IMAGE, subImage); // Handle conversions to old 'textures' property from "imageURL" - if (((!pseudoPropertyFlagsButDesiredEmpty && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(PROP_IMAGE_URL)) && + if ((isMyOwnAvatarEntity || nodeList->getThisNodeCanViewAssetURLs()) && + ((!returnNothingOnEmptyPropertyFlags && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(PROP_IMAGE_URL)) && (!skipDefaults || defaultEntityProperties._imageURL != _imageURL)) { ScriptValue textures = engine->newObject(); textures.setProperty("tex.picture", _imageURL); @@ -2015,7 +2016,7 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s if (_type == EntityTypes::Grid) { COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); - _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GRID_FOLLOW_CAMERA, followCamera); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAJOR_GRID_EVERY, majorGridEvery); @@ -2025,7 +2026,7 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s // Gizmo only if (_type == EntityTypes::Gizmo) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_GIZMO_TYPE, gizmoType, getGizmoTypeAsString()); - _ring.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + _ring.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); } /*@jsdoc @@ -2700,7 +2701,7 @@ bool EntityItemProperties::entityPropertyFlagsFromScriptValue(const ScriptValue& if (object.isString()) { EntityPropertyInfo propertyInfo; if (getPropertyInfo(object.toString(), propertyInfo)) { - flags << propertyInfo.propertyEnum; + flags << propertyInfo.propertyEnums; } } else if (object.isArray()) { @@ -2709,7 +2710,7 @@ bool EntityItemProperties::entityPropertyFlagsFromScriptValue(const ScriptValue& QString propertyName = object.property(i).toString(); EntityPropertyInfo propertyInfo; if (getPropertyInfo(propertyName, propertyInfo)) { - flags << propertyInfo.propertyEnum; + flags << propertyInfo.propertyEnums; } } } @@ -2960,7 +2961,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr { // Keylight ADD_GROUP_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLight, keyLight, Color, color); ADD_GROUP_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLight, keyLight, Intensity, intensity); - ADD_GROUP_PROPERTY_TO_MAP(PROP_KEYLIGHT_DIRECTION, KeyLight, keylight, Direction, direction); + ADD_GROUP_PROPERTY_TO_MAP(PROP_KEYLIGHT_DIRECTION, KeyLight, keyLight, Direction, direction); ADD_GROUP_PROPERTY_TO_MAP(PROP_KEYLIGHT_CAST_SHADOW, KeyLight, keyLight, CastShadows, castShadows); ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_KEYLIGHT_SHADOW_BIAS, KeyLight, keyLight, ShadowBias, shadowBias, 0.0f, 1.0f); ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_KEYLIGHT_SHADOW_MAX_DISTANCE, KeyLight, keyLight, ShadowMaxDistance, shadowMaxDistance, 1.0f, 250.0f); @@ -3117,14 +3118,14 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr */ ScriptValue EntityPropertyInfoToScriptValue(ScriptEngine* engine, const EntityPropertyInfo& propertyInfo) { ScriptValue obj = engine->newObject(); - obj.setProperty("propertyEnum", propertyInfo.propertyEnum); + obj.setProperty("propertyEnum", propertyInfo.propertyEnums.firstFlag()); obj.setProperty("minimum", propertyInfo.minimum.toString()); obj.setProperty("maximum", propertyInfo.maximum.toString()); return obj; } bool EntityPropertyInfoFromScriptValue(const ScriptValue& object, EntityPropertyInfo& propertyInfo) { - propertyInfo.propertyEnum = (EntityPropertyList)object.property("propertyEnum").toVariant().toUInt(); + propertyInfo.propertyEnums = (EntityPropertyList)object.property("propertyEnum").toVariant().toUInt(); propertyInfo.minimum = object.property("minimum").toVariant(); propertyInfo.maximum = object.property("maximum").toVariant(); return true; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 78e69f98b7..fde4ea5312 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -83,11 +83,11 @@ using u8vec3Color = glm::u8vec3; struct EntityPropertyInfo { EntityPropertyInfo(EntityPropertyList propEnum) : - propertyEnum(propEnum) {} + propertyEnums(propEnum) {} EntityPropertyInfo(EntityPropertyList propEnum, QVariant min, QVariant max) : - propertyEnum(propEnum), minimum(min), maximum(max) {} + propertyEnums(propEnum), minimum(min), maximum(max) {} EntityPropertyInfo() = default; - EntityPropertyList propertyEnum; + EntityPropertyFlags propertyEnums; QVariant minimum; QVariant maximum; }; diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index c9ecff41f2..ad9de6ac18 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -57,9 +57,9 @@ const glm::vec3 ENTITY_ITEM_DEFAULT_DIMENSIONS = glm::vec3(ENTITY_ITEM_DEFAULT_W const float ENTITY_ITEM_DEFAULT_VOLUME = ENTITY_ITEM_DEFAULT_WIDTH * ENTITY_ITEM_DEFAULT_WIDTH * ENTITY_ITEM_DEFAULT_WIDTH; const float ENTITY_ITEM_MIN_VOLUME = ENTITY_ITEM_MIN_DIMENSION * ENTITY_ITEM_MIN_DIMENSION * ENTITY_ITEM_MIN_DIMENSION; -const float ENTITY_ITEM_MAX_DENSITY = 10000.0f; // kg/m^3 density of silver -const float ENTITY_ITEM_MIN_DENSITY = 100.0f; // kg/m^3 density of balsa wood -const float ENTITY_ITEM_DEFAULT_DENSITY = 1000.0f; // density of water +const float ENTITY_ITEM_MAX_DENSITY = 100000.0f; // kg/m^3 more than 5 times density of tungsten. +const float ENTITY_ITEM_MIN_DENSITY = 0.1f; // kg/m^3 ten times less than air density. +const float ENTITY_ITEM_DEFAULT_DENSITY = 1000.0f; // density of water. const float ENTITY_ITEM_DEFAULT_MASS = ENTITY_ITEM_DEFAULT_DENSITY * ENTITY_ITEM_DEFAULT_VOLUME; const glm::vec3 ENTITY_ITEM_DEFAULT_VELOCITY = ENTITY_ITEM_ZERO_VEC3; diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 3b6e424663..0fe438459d 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -139,7 +139,7 @@ inline ScriptValue convertScriptValue(ScriptEngine* e, const EntityItemID& v) { inline ScriptValue convertScriptValue(ScriptEngine* e, const AACube& v) { return aaCubeToScriptValue(e, v); } #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(X,G,g,P,p) \ - if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \ + if (((!returnNothingOnEmptyPropertyFlags && desiredProperties.isEmpty()) || desiredProperties.getHasProperty(X)) && \ (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P())) { \ ScriptValue groupProperties = properties.property(#g); \ if (!groupProperties.isValid()) { \ @@ -151,7 +151,7 @@ inline ScriptValue convertScriptValue(ScriptEngine* e, const AACube& v) { return } #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(X,G,g,P,p,T) \ - if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \ + if (((!returnNothingOnEmptyPropertyFlags && desiredProperties.isEmpty()) || desiredProperties.getHasProperty(X)) && \ (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P())) { \ ScriptValue groupProperties = properties.property(#g); \ if (!groupProperties.isValid()) { \ @@ -163,7 +163,7 @@ inline ScriptValue convertScriptValue(ScriptEngine* e, const AACube& v) { return } #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(X,G,g,P,p,M) \ - if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \ + if (((!returnNothingOnEmptyPropertyFlags && desiredProperties.isEmpty()) || desiredProperties.getHasProperty(X)) && \ (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P())) { \ ScriptValue groupProperties = properties.property(#g); \ if (!groupProperties.isValid()) { \ @@ -175,14 +175,14 @@ inline ScriptValue convertScriptValue(ScriptEngine* e, const AACube& v) { return } #define COPY_PROPERTY_TO_QSCRIPTVALUE(p,P) \ - if (((!pseudoPropertyFlagsButDesiredEmpty && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(p)) && \ + if (((!returnNothingOnEmptyPropertyFlags && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(p)) && \ (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ ScriptValue V = convertScriptValue(engine, _##P); \ properties.setProperty(#P, V); \ } #define COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(p,P,T) \ - if ((_desiredProperties.isEmpty() || _desiredProperties.getHasProperty(p)) && \ + if (((!returnNothingOnEmptyPropertyFlags && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(p)) && \ (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ ScriptValue V = T##_convertScriptValue(engine, _##P); \ properties.setProperty(#P, V); \ @@ -192,7 +192,7 @@ inline ScriptValue convertScriptValue(ScriptEngine* e, const AACube& v) { return properties.setProperty(#P, G); #define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(p, P, G) \ - if (((!pseudoPropertyFlagsButDesiredEmpty && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(p)) && \ + if (((!returnNothingOnEmptyPropertyFlags && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(p)) && \ (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ ScriptValue V = convertScriptValue(engine, G); \ properties.setProperty(#P, V); \ @@ -207,7 +207,7 @@ inline ScriptValue convertScriptValue(ScriptEngine* e, const AACube& v) { return // same as COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER but uses #X instead of #P in the setProperty() step #define COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(p, P, X, G) \ - if (((!pseudoPropertyFlagsButDesiredEmpty && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(p)) && \ + if (((!returnNothingOnEmptyPropertyFlags && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(p)) && \ (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ ScriptValue V = convertScriptValue(engine, G); \ properties.setProperty(#X, V); \ @@ -219,6 +219,37 @@ inline ScriptValue convertScriptValue(ScriptEngine* e, const AACube& v) { return properties.setProperty(#P, V); \ } +#define COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(p, P) \ + if (((!returnNothingOnEmptyPropertyFlags && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(p)) && \ + (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ + if (isMyOwnAvatarEntity || nodeList->getThisNodeCanViewAssetURLs()) { \ + ScriptValue V = convertScriptValue(engine, _##P); \ + properties.setProperty(#P, V); \ + } else { \ + const QString emptyURL = ""; \ + ScriptValue V = convertScriptValue(engine, emptyURL); \ + properties.setProperty(#P, V); \ + } \ + } + +#define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(X, G, g, P, p) \ + if (((!returnNothingOnEmptyPropertyFlags && desiredProperties.isEmpty()) || desiredProperties.getHasProperty(X)) && \ + (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P())) { \ + if (isMyOwnAvatarEntity || nodeList->getThisNodeCanViewAssetURLs()) { \ + ScriptValue groupProperties = properties.property(#g); \ + if (!groupProperties.isValid()) { \ + groupProperties = engine->newObject(); \ + } \ + ScriptValue V = convertScriptValue(engine, get##P()); \ + groupProperties.setProperty(#p, V); \ + properties.setProperty(#g, groupProperties); \ + } else { \ + const QString emptyURL = ""; \ + ScriptValue V = convertScriptValue(engine, emptyURL); \ + properties.setProperty(#P, V); \ + } \ + } + typedef QVector qVectorVec3; typedef QVector qVectorQuat; typedef QVector qVectorBool; @@ -466,14 +497,16 @@ inline QRect QRect_convertFromScriptValue(const ScriptValue& v, bool& isValid) { { \ EntityPropertyInfo propertyInfo = EntityPropertyInfo(P); \ _propertyInfos[#g "." #n] = propertyInfo; \ - _enumsToPropertyStrings[P] = #g "." #n; \ + _propertyInfos[#g].propertyEnums << P; \ + _enumsToPropertyStrings[P] = #g "." #n; \ } #define ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(P, G, g, N, n, M, X) \ { \ EntityPropertyInfo propertyInfo = EntityPropertyInfo(P, M, X); \ _propertyInfos[#g "." #n] = propertyInfo; \ - _enumsToPropertyStrings[P] = #g "." #n; \ + _propertyInfos[#g].propertyEnums << P; \ + _enumsToPropertyStrings[P] = #g "." #n; \ } #define DEFINE_CORE(N, n, T, V) \ diff --git a/libraries/entities/src/EntityScriptServerLogClient.cpp b/libraries/entities/src/EntityScriptServerLogClient.cpp index 5d7d4017cd..7329cf1fdd 100644 --- a/libraries/entities/src/EntityScriptServerLogClient.cpp +++ b/libraries/entities/src/EntityScriptServerLogClient.cpp @@ -9,7 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include #include "EntityScriptServerLogClient.h" +#include "ScriptMessage.h" +#include "ScriptEngines.h" EntityScriptServerLogClient::EntityScriptServerLogClient() { auto nodeList = DependencyManager::get(); @@ -35,9 +39,9 @@ void EntityScriptServerLogClient::disconnectNotify(const QMetaMethod& signal) { void EntityScriptServerLogClient::connectionsChanged() { auto numReceivers = receivers(SIGNAL(receivedNewLogLines(QString))); - if (!_subscribed && numReceivers > 0) { + if (!_subscribed && (numReceivers > 0 || _areMessagesRequestedByScripts)) { enableToEntityServerScriptLog(DependencyManager::get()->getThisNodeCanRez()); - } else if (_subscribed && numReceivers == 0) { + } else if (_subscribed && (numReceivers == 0 && !_areMessagesRequestedByScripts)) { enableToEntityServerScriptLog(false); } } @@ -62,7 +66,59 @@ void EntityScriptServerLogClient::enableToEntityServerScriptLog(bool enable) { } void EntityScriptServerLogClient::handleEntityServerScriptLogPacket(QSharedPointer message, SharedNodePointer senderNode) { - emit receivedNewLogLines(QString::fromUtf8(message->readAll())); + QString messageText = QString::fromUtf8(message->readAll()); + QJsonParseError error; + QJsonDocument document = QJsonDocument::fromJson(messageText.toUtf8(), &error); + emit receivedNewLogLines(messageText); + if(document.isNull()) { + qWarning() << "EntityScriptServerLogClient::handleEntityServerScriptLogPacket: Cannot parse JSON: " << error.errorString() + << " Contents: " << messageText; + return; + } + // Iterate through contents and emit messages + if(!document.isArray()) { + qWarning() << "EntityScriptServerLogClient::handleEntityServerScriptLogPacket: JSON is not an array: " << messageText; + return; + } + + auto scriptEngines = DependencyManager::get().data(); + + auto array = document.array(); + for (int n = 0; n < array.size(); n++) { + if (!array[n].isObject()) { + qWarning() << "EntityScriptServerLogClient::handleEntityServerScriptLogPacket: message is not an object: " << messageText; + continue; + } + ScriptMessage scriptMessage; + if (!scriptMessage.fromJson(array[n].toObject())) { + qWarning() << "EntityScriptServerLogClient::handleEntityServerScriptLogPacket: message parsing failed: " << messageText; + continue; + } + switch (scriptMessage.getSeverity()) { + case ScriptMessage::Severity::SEVERITY_INFO: + emit scriptEngines->infoEntityMessage(scriptMessage.getMessage(), scriptMessage.getFileName(), + scriptMessage.getLineNumber(), scriptMessage.getEntityID(), true); + break; + + case ScriptMessage::Severity::SEVERITY_PRINT: + emit scriptEngines->printedEntityMessage(scriptMessage.getMessage(), scriptMessage.getFileName(), + scriptMessage.getLineNumber(), scriptMessage.getEntityID(), true); + break; + + case ScriptMessage::Severity::SEVERITY_WARNING: + emit scriptEngines->warningEntityMessage(scriptMessage.getMessage(), scriptMessage.getFileName(), + scriptMessage.getLineNumber(), scriptMessage.getEntityID(), true); + break; + + case ScriptMessage::Severity::SEVERITY_ERROR: + emit scriptEngines->errorEntityMessage(scriptMessage.getMessage(), scriptMessage.getFileName(), + scriptMessage.getLineNumber(), scriptMessage.getEntityID(), true); + break; + + default: + break; + } + } } void EntityScriptServerLogClient::nodeActivated(SharedNodePointer activatedNode) { @@ -84,3 +140,8 @@ void EntityScriptServerLogClient::canRezChanged(bool canRez) { enableToEntityServerScriptLog(canRez); } } + +void EntityScriptServerLogClient::requestMessagesForScriptEngines(bool areMessagesRequested) { + _areMessagesRequestedByScripts = areMessagesRequested; + connectionsChanged(); +} diff --git a/libraries/entities/src/EntityScriptServerLogClient.h b/libraries/entities/src/EntityScriptServerLogClient.h index 9eee5daed8..e388de917a 100644 --- a/libraries/entities/src/EntityScriptServerLogClient.h +++ b/libraries/entities/src/EntityScriptServerLogClient.h @@ -33,6 +33,12 @@ class EntityScriptServerLogClient : public QObject, public Dependency { public: EntityScriptServerLogClient(); + /** + * @brief This is called by ScriptEngines when scripts need access to entity server script messages and when access + * is not needed anymore. + */ + void requestMessagesForScriptEngines(bool areMessagesRequested); + signals: /*@jsdoc @@ -66,7 +72,10 @@ private slots: void connectionsChanged(); private: + std::atomic _areMessagesRequestedByScripts {false}; bool _subscribed { false }; + + friend class ScriptEngines; }; #endif // hifi_EntityScriptServerLogClient_h diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 4acf9b8d1b..523c95641e 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -97,6 +97,7 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership connect(nodeList.data(), &NodeList::canWriteAssetsChanged, this, &EntityScriptingInterface::canWriteAssetsChanged); connect(nodeList.data(), &NodeList::canGetAndSetPrivateUserDataChanged, this, &EntityScriptingInterface::canGetAndSetPrivateUserDataChanged); connect(nodeList.data(), &NodeList::canRezAvatarEntitiesChanged, this, &EntityScriptingInterface::canRezAvatarEntitiesChanged); + connect(nodeList.data(), &NodeList::canViewAssetURLsChanged, this, &EntityScriptingInterface::canViewAssetURLsChanged); auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::EntityScriptCallMethod, @@ -291,6 +292,11 @@ bool EntityScriptingInterface::canRezAvatarEntities() { return nodeList->getThisNodeCanRezAvatarEntities(); } +bool EntityScriptingInterface::canViewAssetURLs() { + auto nodeList = DependencyManager::get(); + return nodeList->getThisNodeCanViewAssetURLs(); +} + void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) { if (_entityTree) { disconnect(_entityTree.get(), &EntityTree::addingEntityPointer, this, &EntityScriptingInterface::onAddingEntity); @@ -785,7 +791,7 @@ QUuid EntityScriptingInterface::cloneEntity(const QUuid& entityIDToClone) { EntityItemProperties EntityScriptingInterface::getEntityProperties(const QUuid& entityID) { const EntityPropertyFlags noSpecificProperties; - return getEntityPropertiesInternal(entityID, noSpecificProperties); + return getEntityPropertiesInternal(entityID, noSpecificProperties, false); } ScriptValue EntityScriptingInterface::getEntityProperties(const QUuid& entityID, const ScriptValue& extendedDesiredProperties) { @@ -810,12 +816,14 @@ ScriptValue EntityScriptingInterface::getEntityProperties(const QUuid& entityID, } } - EntityItemProperties properties = getEntityPropertiesInternal(entityID, desiredProperties); + EntityItemProperties properties = getEntityPropertiesInternal(entityID, desiredProperties, !desiredPseudoPropertyFlags.none()); return properties.copyToScriptValue(extendedDesiredProperties.engine().get(), false, false, false, desiredPseudoPropertyFlags); } -EntityItemProperties EntityScriptingInterface::getEntityPropertiesInternal(const QUuid& entityID, EntityPropertyFlags desiredProperties) { +EntityItemProperties EntityScriptingInterface::getEntityPropertiesInternal(const QUuid& entityID, + EntityPropertyFlags desiredProperties, + bool returnNothingOnEmptyPropertyFlags) { PROFILE_RANGE(script_entities, __FUNCTION__); @@ -838,7 +846,7 @@ EntityItemProperties EntityScriptingInterface::getEntityPropertiesInternal(const desiredProperties.setHasProperty(PROP_PARENT_JOINT_INDEX); } - if (desiredProperties.isEmpty()) { + if (desiredProperties.isEmpty() && !returnNothingOnEmptyPropertyFlags) { // these are left out of EntityItem::getEntityProperties so that localPosition and localRotation // don't end up in json saves, etc. We still want them here, though. EncodeBitstreamParams params; // unknown @@ -850,7 +858,7 @@ EntityItemProperties EntityScriptingInterface::getEntityPropertiesInternal(const desiredProperties.setHasProperty(PROP_LOCAL_DIMENSIONS); } - results = entity->getProperties(desiredProperties); + results = entity->getProperties(desiredProperties, true); } }); } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index c677bdf0a1..6fcb863fe7 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -206,7 +206,8 @@ public: * @param {Uuid[]} entityIDs - The IDs of the entities to get the properties of. * @param {string[]|string} [desiredProperties=[]] - The name or names of the properties to get. For properties that are * objects (e.g., the "keyLight" property), use the property and subproperty names in dot notation (e.g., - * "keyLight.color"). + * "keyLight.color"). Getting all subproperties with the name of an object is currently not supported (e.g., + * passing the "keyLight" property only). * @returns {Entities.EntityProperties[]} The specified properties of each entity for each entity that can be found. If * none of the entities can be found, then an empty array is returned. If no properties are specified, then all * properties are returned. @@ -287,6 +288,14 @@ public slots: */ Q_INVOKABLE bool canRezAvatarEntities(); + /*@jsdoc + * Checks whether or not the script can view asset URLs + * @function Entities.canViewAssetURLs + * @returns {boolean} true if the domain server will allow the script to view asset URLs, + * otherwise false. + */ + Q_INVOKABLE bool canViewAssetURLs(); + /*@jsdoc *

How an entity is hosted and sent to others for display.

* @@ -395,7 +404,24 @@ public slots: */ Q_INVOKABLE EntityItemProperties getEntityProperties(const QUuid& entityID); Q_INVOKABLE ScriptValue getEntityProperties(const QUuid& entityID, const ScriptValue &desiredProperties); - Q_INVOKABLE EntityItemProperties getEntityPropertiesInternal(const QUuid& entityID, EntityPropertyFlags desiwredProperties); + /** + * @brief Internal function to get entity properties. + * + * It's being called by EntityScriptingInterface::getEntityProperties + * and also from C++ side in several places in the source code. + * + * @param entityID The ID of the entity to get the properties of. + * @param desiredProperties Flags representing requested entity properties + * @param returnNothingOnEmptyPropertyFlags If this parameter is false and property flags are empty, then all possible + * properties will get returned. This is needed because we divide properties requested through getEntityProperties into + * real properties and pseudo properties. Only real properties are passed here as desiredProperties, so if user requests + * only pseudo properties, then desiredProperties will be empty. In such case we need to pass true + * as returnNothingOnEmptyPropertyFlags to avoid mistakenly returning all the properties. + * @return EntityItemProperties Requested properties of the entity if it can be found. + */ + + Q_INVOKABLE EntityItemProperties getEntityPropertiesInternal(const QUuid& entityID, EntityPropertyFlags desiredProperties, + bool returnNothingOnEmptyPropertyFlags); //Q_INVOKABLE EntityItemProperties getEntityProperties(const QUuid& entityID, EntityPropertyFlags desiredProperties); /*@jsdoc @@ -2250,6 +2276,14 @@ signals: */ void canRezAvatarEntitiesChanged(bool canRezAvatarEntities); + /*@jsdoc + * Triggered when your ability to view asset URLs is changed. + * @function Entities.canViewAssetURLsChanged + * @param {boolean} canViewAssetURLs - true if the script can view asset URLs, + * false if it can't. + * @returns {Signal} + */ + void canViewAssetURLsChanged(bool canViewAssetURLs); /*@jsdoc * Triggered when a mouse button is clicked while the mouse cursor is on an entity, or a controller trigger is fully diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index f8ae726b90..8f2321dbb3 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2589,11 +2589,10 @@ bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElementPointer } entityDescription["DataVersion"] = _persistDataVersion; entityDescription["Id"] = _persistID; - const std::lock_guard scriptLock(scriptEngineMutex); - RecurseOctreeToMapOperator theOperator(entityDescription, element, scriptEngine.get(), skipDefaultValues, - skipThoseWithBadParents, _myAvatar); - withReadLock([&] { - recurseTreeWithOperator(&theOperator); + _helperScriptEngine.run( [&] { + RecurseOctreeToMapOperator theOperator(entityDescription, element, _helperScriptEngine.get(), skipDefaultValues, + skipThoseWithBadParents, _myAvatar); + withReadLock([&] { recurseTreeWithOperator(&theOperator); }); }); return true; } @@ -2764,11 +2763,10 @@ bool EntityTree::readFromMap(QVariantMap& map, const bool isImport) { } EntityItemProperties properties; - { - const std::lock_guard scriptLock(scriptEngineMutex); - ScriptValue entityScriptValue = variantMapToScriptValue(entityMap, *scriptEngine); + _helperScriptEngine.run( [&] { + ScriptValue entityScriptValue = variantMapToScriptValue(entityMap, *_helperScriptEngine.get()); EntityItemPropertiesFromScriptValueIgnoreReadOnly(entityScriptValue, properties); - } + }); EntityItemID entityItemID; if (entityMap.contains("id")) { @@ -2922,13 +2920,12 @@ bool EntityTree::readFromMap(QVariantMap& map, const bool isImport) { } bool EntityTree::writeToJSON(QString& jsonString, const OctreeElementPointer& element) { - const std::lock_guard scriptLock(scriptEngineMutex); - RecurseOctreeToJSONOperator theOperator(element, scriptEngine.get(), jsonString); - withReadLock([&] { - recurseTreeWithOperator(&theOperator); - }); + _helperScriptEngine.run( [&] { + RecurseOctreeToJSONOperator theOperator(element, _helperScriptEngine.get(), jsonString); + withReadLock([&] { recurseTreeWithOperator(&theOperator); }); - jsonString = theOperator.getJson(); + jsonString = theOperator.getJson(); + }); return true; } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 1161bec6e9..1d43a91ec8 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -388,8 +389,7 @@ private: MovingEntitiesOperator& moveOperator, bool force, bool tellServer); // Script engine for writing entity tree data to and from JSON - std::mutex scriptEngineMutex; - ScriptEnginePointer scriptEngine{ newScriptEngine() }; + HelperScriptEngine _helperScriptEngine; }; void convertGrabUserDataToProperties(EntityItemProperties& properties); diff --git a/libraries/entities/src/GrabPropertyGroup.cpp b/libraries/entities/src/GrabPropertyGroup.cpp index f0026d8904..a4037ff98f 100644 --- a/libraries/entities/src/GrabPropertyGroup.cpp +++ b/libraries/entities/src/GrabPropertyGroup.cpp @@ -20,7 +20,9 @@ void GrabPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties) const { + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const { + auto nodeList = DependencyManager::get(); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_GRABBABLE, Grab, grab, Grabbable, grabbable); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_KINEMATIC, Grab, grab, GrabKinematic, grabKinematic); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_FOLLOWS_CONTROLLER, Grab, grab, GrabFollowsController, grabFollowsController); @@ -36,7 +38,7 @@ void GrabPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProp EquippableRightPosition, equippableRightPosition); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, EquippableRightRotation, equippableRightRotation); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, Grab, grab, + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, Grab, grab, EquippableIndicatorURL, equippableIndicatorURL); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, Grab, grab, EquippableIndicatorScale, equippableIndicatorScale); diff --git a/libraries/entities/src/GrabPropertyGroup.h b/libraries/entities/src/GrabPropertyGroup.h index 368867a6d6..23211bde21 100644 --- a/libraries/entities/src/GrabPropertyGroup.h +++ b/libraries/entities/src/GrabPropertyGroup.h @@ -75,7 +75,8 @@ public: // EntityItemProperty related helpers virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties) const override; + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const override; virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; void merge(const GrabPropertyGroup& other); diff --git a/libraries/entities/src/HazePropertyGroup.cpp b/libraries/entities/src/HazePropertyGroup.cpp index 7cd41e8d59..fd091de8ac 100644 --- a/libraries/entities/src/HazePropertyGroup.cpp +++ b/libraries/entities/src/HazePropertyGroup.cpp @@ -18,7 +18,9 @@ #include "EntityItemProperties.h" #include "EntityItemPropertiesMacros.h" -void HazePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { +void HazePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_RANGE, Haze, haze, HazeRange, hazeRange); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_HAZE_COLOR, Haze, haze, HazeColor, hazeColor, u8vec3Color); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_HAZE_GLARE_COLOR, Haze, haze, HazeGlareColor, hazeGlareColor, u8vec3Color); diff --git a/libraries/entities/src/HazePropertyGroup.h b/libraries/entities/src/HazePropertyGroup.h index 2b899871fa..a84ec20713 100644 --- a/libraries/entities/src/HazePropertyGroup.h +++ b/libraries/entities/src/HazePropertyGroup.h @@ -80,7 +80,8 @@ public: // EntityItemProperty related helpers virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties) const override; + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const override; virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; void merge(const HazePropertyGroup& other); diff --git a/libraries/entities/src/KeyLightPropertyGroup.cpp b/libraries/entities/src/KeyLightPropertyGroup.cpp index 74ea8dc520..f431aa55cc 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.cpp +++ b/libraries/entities/src/KeyLightPropertyGroup.cpp @@ -28,7 +28,8 @@ const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_SHADOW_BIAS { 0.5f }; const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_SHADOW_MAX_DISTANCE { 40.0f }; void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_KEYLIGHT_COLOR, KeyLight, keyLight, Color, color, u8vec3Color); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_INTENSITY, KeyLight, keyLight, Intensity, intensity); diff --git a/libraries/entities/src/KeyLightPropertyGroup.h b/libraries/entities/src/KeyLightPropertyGroup.h index 7d92813a54..4a412f9802 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.h +++ b/libraries/entities/src/KeyLightPropertyGroup.h @@ -50,7 +50,8 @@ public: // EntityItemProperty related helpers virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties) const override; + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const override; virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; void merge(const KeyLightPropertyGroup& other); diff --git a/libraries/entities/src/PropertyGroup.h b/libraries/entities/src/PropertyGroup.h index fab78c5ef6..b73c2dad2a 100644 --- a/libraries/entities/src/PropertyGroup.h +++ b/libraries/entities/src/PropertyGroup.h @@ -34,7 +34,8 @@ public: virtual ~PropertyGroup() = default; // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const = 0; + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const = 0; virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) = 0; virtual void debugDump() const { } virtual void listChangedProperties(QList& out) { } diff --git a/libraries/entities/src/PulsePropertyGroup.cpp b/libraries/entities/src/PulsePropertyGroup.cpp index b760ac3c29..ab61a1f8ad 100644 --- a/libraries/entities/src/PulsePropertyGroup.cpp +++ b/libraries/entities/src/PulsePropertyGroup.cpp @@ -60,8 +60,9 @@ void PulsePropertyGroup::setAlphaModeFromString(const QString& pulseMode) { } void PulsePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties) const { + ScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_PULSE_MIN, Pulse, pulse, Min, min); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_PULSE_MAX, Pulse, pulse, Max, max); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_PULSE_PERIOD, Pulse, pulse, Period, period); diff --git a/libraries/entities/src/PulsePropertyGroup.h b/libraries/entities/src/PulsePropertyGroup.h index e874f114e4..649005b970 100644 --- a/libraries/entities/src/PulsePropertyGroup.h +++ b/libraries/entities/src/PulsePropertyGroup.h @@ -44,7 +44,8 @@ public: // EntityItemProperty related helpers virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties) const override; + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const override; virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; void merge(const PulsePropertyGroup& other); diff --git a/libraries/entities/src/RingGizmoPropertyGroup.cpp b/libraries/entities/src/RingGizmoPropertyGroup.cpp index f8e106c722..68021f44a2 100644 --- a/libraries/entities/src/RingGizmoPropertyGroup.cpp +++ b/libraries/entities/src/RingGizmoPropertyGroup.cpp @@ -23,8 +23,9 @@ const float RingGizmoPropertyGroup::MIN_RADIUS = 0.0f; const float RingGizmoPropertyGroup::MAX_RADIUS = 0.5f; void RingGizmoPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties) const { + ScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_START_ANGLE, Ring, ring, StartAngle, startAngle); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_END_ANGLE, Ring, ring, EndAngle, endAngle); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_INNER_RADIUS, Ring, ring, InnerRadius, innerRadius); diff --git a/libraries/entities/src/RingGizmoPropertyGroup.h b/libraries/entities/src/RingGizmoPropertyGroup.h index 7779fea3c1..51ef709f5b 100644 --- a/libraries/entities/src/RingGizmoPropertyGroup.h +++ b/libraries/entities/src/RingGizmoPropertyGroup.h @@ -60,7 +60,8 @@ public: // EntityItemProperty related helpers virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties) const override; + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const override; virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; void merge(const RingGizmoPropertyGroup& other); diff --git a/libraries/entities/src/SkyboxPropertyGroup.cpp b/libraries/entities/src/SkyboxPropertyGroup.cpp index 12be5fe4df..9c3ad46fce 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.cpp +++ b/libraries/entities/src/SkyboxPropertyGroup.cpp @@ -20,9 +20,11 @@ const glm::u8vec3 SkyboxPropertyGroup::DEFAULT_COLOR = { 0, 0, 0 }; -void SkyboxPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { +void SkyboxPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + auto nodeList = DependencyManager::get(); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color, u8vec3Color); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_SKYBOX_URL, Skybox, skybox, URL, url); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_SKYBOX_URL, Skybox, skybox, URL, url); } void SkyboxPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { diff --git a/libraries/entities/src/SkyboxPropertyGroup.h b/libraries/entities/src/SkyboxPropertyGroup.h index 0cb7d8568b..30c9ef1d3a 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.h +++ b/libraries/entities/src/SkyboxPropertyGroup.h @@ -43,7 +43,8 @@ public: // EntityItemProperty related helpers virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties) const override; + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const override; virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; void merge(const SkyboxPropertyGroup& other); diff --git a/libraries/entities/src/ZoneAudioPropertyGroup.cpp b/libraries/entities/src/ZoneAudioPropertyGroup.cpp index aeedeea977..34bde41e1d 100644 --- a/libraries/entities/src/ZoneAudioPropertyGroup.cpp +++ b/libraries/entities/src/ZoneAudioPropertyGroup.cpp @@ -17,7 +17,8 @@ #include "EntityItemProperties.h" #include "EntityItemPropertiesMacros.h" -void ZoneAudioPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { +void ZoneAudioPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_REVERB_ENABLED, Audio, audio, ReverbEnabled, reverbEnabled); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_REVERB_TIME, Audio, audio, ReverbTime, reverbTime); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_REVERB_WET_LEVEL, Audio, audio, ReverbWetLevel, reverbWetLevel); diff --git a/libraries/entities/src/ZoneAudioPropertyGroup.h b/libraries/entities/src/ZoneAudioPropertyGroup.h index a99a43e2be..bbe32b1549 100644 --- a/libraries/entities/src/ZoneAudioPropertyGroup.h +++ b/libraries/entities/src/ZoneAudioPropertyGroup.h @@ -44,7 +44,8 @@ public: // EntityItemProperty related helpers virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties) const override; + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const override; virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; void merge(const ZoneAudioPropertyGroup& other); diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index c0116274ee..86b0df982a 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -101,8 +101,6 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_glUniformMatrix3fv), (&::gpu::gl::GLBackend::do_glUniformMatrix4fv), - (&::gpu::gl::GLBackend::do_glColor4f), - (&::gpu::gl::GLBackend::do_pushProfileRange), (&::gpu::gl::GLBackend::do_popProfileRange), }; @@ -851,22 +849,6 @@ void GLBackend::do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } -void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) { - - glm::vec4 newColor( - batch._params[paramOffset + 3]._float, - batch._params[paramOffset + 2]._float, - batch._params[paramOffset + 1]._float, - batch._params[paramOffset + 0]._float); - - if (_input._colorAttribute != newColor) { - _input._colorAttribute = newColor; - glVertexAttrib4fv(gpu::Stream::COLOR, &_input._colorAttribute.r); - _input._hasColorAttribute = true; - } - (void)CHECK_GL_ERROR(); -} - void GLBackend::releaseBuffer(GLuint id, Size size) const { Lock lock(_trashMutex); _currentFrameTrash.buffersTrash.push_back({ id, size }); diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index 5545858877..8a1648a01b 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -242,8 +242,6 @@ public: virtual void do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) final; virtual void do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) final; - virtual void do_glColor4f(const Batch& batch, size_t paramOffset) final; - // The State setters called by the GLState::Commands when a new state is assigned virtual void do_setStateFillMode(int32 mode) final; virtual void do_setStateCullMode(int32 mode) final; @@ -351,8 +349,6 @@ protected: struct InputStageState { bool _invalidFormat { true }; bool _lastUpdateStereoState { false }; - bool _hasColorAttribute { false }; - bool _hadColorAttribute { false }; FormatReference _format { GPU_REFERENCE_INIT_VALUE }; std::string _formatKey; @@ -369,8 +365,6 @@ protected: std::array _bufferStrides; std::array _bufferVBOs; - glm::vec4 _colorAttribute { 1.0f }; - BufferReference _indexBuffer; Offset _indexBufferOffset { 0 }; Type _indexBufferType { UINT32 }; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendInput.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendInput.cpp index 4efd5f9941..e5b0ead027 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendInput.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendInput.cpp @@ -103,9 +103,6 @@ void GLBackend::resetInputStage() { reset(_input._format); _input._formatKey.clear(); _input._invalidFormat = false; - _input._hasColorAttribute = false; - _input._hadColorAttribute = false; - _input._colorAttribute = vec4(1.0f); _input._attributeActivation.reset(); for (uint32_t i = 0; i < _input._buffers.size(); i++) { @@ -163,8 +160,6 @@ void GLBackend::updateInput() { #endif _input._lastUpdateStereoState = isStereoNow; - bool hasColorAttribute = _input._hasColorAttribute; - if (_input._invalidFormat) { InputStageState::ActivationCache newActivation; @@ -194,8 +189,6 @@ void GLBackend::updateInput() { GLenum perLocationSize = attrib._element.getLocationSize(); - hasColorAttribute |= slot == Stream::COLOR; - for (GLuint locNum = 0; locNum < locationCount; ++locNum) { GLuint attriNum = (GLuint)(slot + locNum); newActivation.set(attriNum); @@ -226,12 +219,6 @@ void GLBackend::updateInput() { glVertexBindingDivisor(bufferChannelNum, frequency); #endif } - - if (!hasColorAttribute && _input._hadColorAttribute) { - // The previous input stage had a color attribute but this one doesn't, so reset the color to pure white. - _input._colorAttribute = glm::vec4(1.0f); - glVertexAttrib4fv(Stream::COLOR, &_input._colorAttribute.r); - } } // Manage Activation what was and what is expected now @@ -253,9 +240,6 @@ void GLBackend::updateInput() { _stats._ISNumFormatChanges++; } - _input._hadColorAttribute = hasColorAttribute; - _input._hasColorAttribute = false; - if (_input._invalidBuffers.any()) { auto vbo = _input._bufferVBOs.data(); auto offset = _input._bufferOffsets.data(); diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp index 188b4a1084..bdb37f6319 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp @@ -33,8 +33,6 @@ void GL41Backend::updateInput() { #endif _input._lastUpdateStereoState = isStereoNow; - bool hasColorAttribute = _input._hasColorAttribute; - if (_input._invalidFormat || _input._invalidBuffers.any()) { auto format = acquire(_input._format); @@ -110,8 +108,6 @@ void GL41Backend::updateInput() { uintptr_t pointer = (uintptr_t)(attrib._offset + offsets[bufferNum]); GLboolean isNormalized = attrib._element.isNormalized(); - hasColorAttribute |= slot == Stream::COLOR; - for (size_t locNum = 0; locNum < locationCount; ++locNum) { if (attrib._element.isInteger()) { glVertexAttribIPointer(slot + (GLuint)locNum, count, type, stride, @@ -131,17 +127,8 @@ void GL41Backend::updateInput() { } } } - - if (!hasColorAttribute && _input._hadColorAttribute) { - // The previous input stage had a color attribute but this one doesn't, so reset the color to pure white. - _input._colorAttribute = glm::vec4(1.0f); - glVertexAttrib4fv(Stream::COLOR, &_input._colorAttribute.r); - } } // everything format related should be in sync now _input._invalidFormat = false; } - - _input._hadColorAttribute = hasColorAttribute; - _input._hasColorAttribute = false; } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index 385ddca065..d65e8b0af9 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -12,7 +12,9 @@ #include #include #include +#include +#include "glad/glad.h" Q_LOGGING_CATEGORY(gpugl45logging, "hifi.gpu.gl45") using namespace gpu; @@ -21,9 +23,27 @@ using namespace gpu::gl45; GLint GL45Backend::MAX_COMBINED_SHADER_STORAGE_BLOCKS{ 0 }; GLint GL45Backend::MAX_UNIFORM_LOCATIONS{ 0 }; +#ifdef GLAD_DEBUG +static void post_call_callback_gl(const char *name, void *funcptr, int len_args, ...) { + (void)funcptr; + (void)len_args; + + GLenum error_code = glad_glGetError(); + if (error_code != GL_NO_ERROR) { + qCWarning(gpugl45logging) << "OpenGL error" << error_code << "in" << name; + } +} +#endif + + static void staticInit() { static std::once_flag once; std::call_once(once, [&] { +#ifdef GLAD_DEBUG + // This sets the post call callback to a logging function. By default it prints on + // stderr and skips our log. It only exists in debug builds. + glad_set_post_callback(&post_call_callback_gl); +#endif glGetIntegerv(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, &GL45Backend::MAX_COMBINED_SHADER_STORAGE_BLOCKS); glGetIntegerv(GL_MAX_UNIFORM_LOCATIONS, &GL45Backend::MAX_UNIFORM_LOCATIONS); }); @@ -82,7 +102,7 @@ void GL45Backend::do_drawIndexed(const Batch& batch, size_t paramOffset) { uint32 startIndex = batch._params[paramOffset + 0]._uint; GLenum glType = gl::ELEMENT_TYPE_TO_GL[_input._indexBufferType]; - + auto typeByteSize = TYPE_SIZE[_input._indexBufferType]; GLvoid* indexBufferByteOffset = reinterpret_cast(startIndex * typeByteSize + _input._indexBufferOffset); @@ -148,7 +168,7 @@ void GL45Backend::do_drawIndexedInstanced(const Batch& batch, size_t paramOffset GLenum glType = gl::ELEMENT_TYPE_TO_GL[_input._indexBufferType]; auto typeByteSize = TYPE_SIZE[_input._indexBufferType]; GLvoid* indexBufferByteOffset = reinterpret_cast(startIndex * typeByteSize + _input._indexBufferOffset); - + if (isStereo()) { GLint trueNumInstances = 2 * numInstances; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp index 8add4a9296..7513ff7caf 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp @@ -35,8 +35,6 @@ void GL45Backend::updateInput() { #endif _input._lastUpdateStereoState = isStereoNow; - bool hasColorAttribute = _input._hasColorAttribute; - if (_input._invalidFormat) { InputStageState::ActivationCache newActivation; @@ -66,8 +64,6 @@ void GL45Backend::updateInput() { GLenum perLocationSize = attrib._element.getLocationSize(); - hasColorAttribute |= slot == Stream::COLOR; - for (GLuint locNum = 0; locNum < locationCount; ++locNum) { GLuint attriNum = (GLuint)(slot + locNum); newActivation.set(attriNum); @@ -98,12 +94,6 @@ void GL45Backend::updateInput() { glVertexBindingDivisor(bufferChannelNum, frequency); #endif } - - if (!hasColorAttribute && _input._hadColorAttribute) { - // The previous input stage had a color attribute but this one doesn't, so reset the color to pure white. - _input._colorAttribute = glm::vec4(1.0f); - glVertexAttrib4fv(Stream::COLOR, &_input._colorAttribute.r); - } } // Manage Activation what was and what is expected now @@ -125,9 +115,6 @@ void GL45Backend::updateInput() { _stats._ISNumFormatChanges++; } - _input._hadColorAttribute = hasColorAttribute; - _input._hasColorAttribute = false; - if (_input._invalidBuffers.any()) { auto vbo = _input._bufferVBOs.data(); auto offset = _input._bufferOffsets.data(); diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index a41f586d74..bd4aef9768 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -693,15 +693,6 @@ void Batch::_glUniformMatrix4fv(int32 location, int count, uint8 transpose, cons _params.emplace_back(location); } -void Batch::_glColor4f(float red, float green, float blue, float alpha) { - ADD_COMMAND(glColor4f); - - _params.emplace_back(alpha); - _params.emplace_back(blue); - _params.emplace_back(green); - _params.emplace_back(red); -} - void Batch::finishFrame(BufferUpdates& updates) { PROFILE_RANGE(render_gpu, __FUNCTION__); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index f89dd3ea90..3cf8184913 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -30,11 +30,11 @@ class QDebug; #define BATCH_PREALLOCATE_MIN 128 namespace gpu { -// The named batch data provides a mechanism for accumulating data into buffers over the course -// of many independent calls. For instance, two objects in the scene might both want to render +// The named batch data provides a mechanism for accumulating data into buffers over the course +// of many independent calls. For instance, two objects in the scene might both want to render // a simple box, but are otherwise unaware of each other. The common code that they call to render -// the box can create buffers to store the rendering parameters for each box and register a function -// that will be called with the accumulated buffer data when the batch commands are finally +// the box can create buffers to store the rendering parameters for each box and register a function +// that will be called with the accumulated buffer data when the batch commands are finally // executed against the backend @@ -100,15 +100,15 @@ public: void clear(); // Batches may need to override the context level stereo settings - // if they're performing framebuffer copy operations, like the + // if they're performing framebuffer copy operations, like the // deferred lighting resolution mechanism void enableStereo(bool enable = true); bool isStereoEnabled() const; - // Stereo batches will pre-translate the view matrix, but this isn't - // appropriate for skyboxes or other things intended to be drawn at - // infinite distance, so provide a mechanism to render in stereo - // without the pre-translation of the view. + // Stereo batches will pre-translate the view matrix, but this isn't + // appropriate for skyboxes or other things intended to be drawn at + // infinite distance, so provide a mechanism to render in stereo + // without the pre-translation of the view. void enableSkybox(bool enable = true); bool isSkyboxEnabled() const; @@ -147,7 +147,7 @@ public: // Indirect buffer is used by the multiDrawXXXIndirect calls // The indirect buffer contains the command descriptions to execute multiple drawcalls in a single call void setIndirectBuffer(const BufferPointer& buffer, Offset offset = 0, Offset stride = 0); - + // multi command desctription for multiDrawIndexedIndirect class DrawIndirectCommand { public: @@ -249,7 +249,7 @@ public: void popProfileRange(); // TODO: As long as we have gl calls explicitely issued from interface - // code, we need to be able to record and batch these calls. THe long + // code, we need to be able to record and batch these calls. THe long // term strategy is to get rid of any GL calls in favor of the HIFI GPU API // For now, instead of calling the raw gl Call, use the equivalent call on the batch so the call is beeing recorded // THe implementation of these functions is in GLBackend.cpp @@ -288,8 +288,6 @@ public: _glUniformMatrix3fv(location, 1, false, glm::value_ptr(v)); } - void _glColor4f(float red, float green, float blue, float alpha); - // Maybe useful but shoudln't be public. Please convince me otherwise // Well porting to gles i need it... void runLambda(std::function f); @@ -352,7 +350,7 @@ public: COMMAND_stopNamedCall, // TODO: As long as we have gl calls explicitely issued from interface - // code, we need to be able to record and batch these calls. THe long + // code, we need to be able to record and batch these calls. THe long // term strategy is to get rid of any GL calls in favor of the HIFI GPU API COMMAND_glUniform1i, COMMAND_glUniform1f, @@ -365,8 +363,6 @@ public: COMMAND_glUniformMatrix3fv, COMMAND_glUniformMatrix4fv, - COMMAND_glColor4f, - COMMAND_pushProfileRange, COMMAND_popProfileRange, @@ -383,7 +379,7 @@ public: union { #if (QT_POINTER_SIZE == 8) size_t _size; -#endif +#endif int32 _int; uint32 _uint; float _float; @@ -391,7 +387,7 @@ public: }; #if (QT_POINTER_SIZE == 8) Param(size_t val) : _size(val) {} -#endif +#endif Param(int32 val) : _int(val) {} Param(uint32 val) : _uint(val) {} Param(float val) : _float(val) {} @@ -408,7 +404,7 @@ public: public: typedef T Data; Data _data; - Cache(const Data& data) : _data(data) {} + Cache(const Data& data) : _data(data) {} static size_t _max; class Vector { @@ -575,7 +571,7 @@ private: #else -#define PROFILE_RANGE_BATCH(batch, name) +#define PROFILE_RANGE_BATCH(batch, name) #endif diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h index 49eed80b54..a30c6474c1 100644 --- a/libraries/gpu/src/gpu/Buffer.h +++ b/libraries/gpu/src/gpu/Buffer.h @@ -60,7 +60,7 @@ public: Size getNumTypedElements() const { return getSize() / sizeof(T); }; const Byte* getData() const { return getSysmem().readData(); } - + // Resize the buffer // Keep previous data [0 to min(pSize, mSize)] Size resize(Size pSize); @@ -95,7 +95,7 @@ public: // \return the number of bytes copied Size append(Size size, const Byte* data); - template + template Size append(const T& t) { return append(sizeof(t), reinterpret_cast(&t)); } @@ -110,10 +110,10 @@ public: const GPUObjectPointer gpuObject {}; - + // Access the sysmem object, limited to ourselves and GPUObject derived classes const Sysmem& getSysmem() const { return _sysmem; } - + bool isDirty() const { return _pages(PageManager::DIRTY); } @@ -127,7 +127,7 @@ protected: // For use by the render thread to avoid the intermediate step of getUpdate/applyUpdate void flush() const; - // FIXME don't maintain a second buffer continuously. We should be able to apply updates + // FIXME don't maintain a second buffer continuously. We should be able to apply updates // directly to the GL object and discard _renderSysmem and _renderPages mutable PageManager _renderPages; mutable Sysmem _renderSysmem; @@ -292,7 +292,7 @@ public: // Direct memory access to the buffer contents is incompatible with the paging memory scheme template Iterator begin() { return Iterator(&edit(0), _stride); } template Iterator end() { return Iterator(&edit(getNum()), _stride); } -#else +#else template Iterator begin() const { return Iterator(&get(), _stride); } template Iterator end() const { // reimplement get without bounds checking @@ -378,7 +378,7 @@ public: return *(reinterpret_cast (_buffer->editData() + elementOffset)); } }; - + template class StructBuffer : public gpu::BufferView { public: @@ -387,8 +387,8 @@ public: U t; return std::make_shared(sizeof(U), (const gpu::Byte*) &t, sizeof(U)); } - ~StructBuffer() {}; - StructBuffer() : gpu::BufferView(makeBuffer()) {} + ~StructBuffer() {}; + StructBuffer() : gpu::BufferView(makeBuffer()) {} T& edit() { diff --git a/libraries/gpu/src/gpu/FrameIOKeys.h b/libraries/gpu/src/gpu/FrameIOKeys.h index 2d88158afb..5a5cfdf2b1 100644 --- a/libraries/gpu/src/gpu/FrameIOKeys.h +++ b/libraries/gpu/src/gpu/FrameIOKeys.h @@ -202,8 +202,6 @@ constexpr const char* COMMAND_NAMES[] = { "glUniformMatrix3fv", "glUniformMatrix4fv", - "glColor4f", - "pushProfileRange", "popProfileRange", }; diff --git a/libraries/gpu/src/gpu/State.h b/libraries/gpu/src/gpu/State.h index 86bc5d4c5a..757169a138 100644 --- a/libraries/gpu/src/gpu/State.h +++ b/libraries/gpu/src/gpu/State.h @@ -41,6 +41,7 @@ class GPUObject; class State { public: State(); + State(const State& state) : _values(state._values), _signature(state._signature), _stamp(state._stamp) {} virtual ~State(); Stamp getStamp() const { return _stamp; } @@ -464,7 +465,6 @@ public: const GPUObjectPointer gpuObject{}; protected: - State(const State& state); State& operator=(const State& state); Data _values; diff --git a/libraries/graphics-scripting/CMakeLists.txt b/libraries/graphics-scripting/CMakeLists.txt index 3542704fc8..46dc2130ea 100644 --- a/libraries/graphics-scripting/CMakeLists.txt +++ b/libraries/graphics-scripting/CMakeLists.txt @@ -2,3 +2,7 @@ set(TARGET_NAME graphics-scripting) setup_hifi_library() link_hifi_libraries(shared networking graphics model-serializers image material-networking model-networking script-engine) include_hifi_library_headers(gpu) + +if (WIN32) + add_compile_definitions(_USE_MATH_DEFINES) +endif() \ No newline at end of file diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index 847937ba4f..23a7426610 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -52,35 +52,55 @@ namespace scriptable { ScriptableMaterial(const ScriptableMaterial& material) { *this = material; } ScriptableMaterial& operator=(const ScriptableMaterial& material); - QString name; - QString model; - float opacity; - float roughness; - float metallic; - float scattering; - bool unlit; - glm::vec3 emissive; - glm::vec3 albedo; - QString emissiveMap; - QString albedoMap; - QString opacityMap; - QString opacityMapMode; - float opacityCutoff; - QString metallicMap; - QString specularMap; - QString roughnessMap; - QString glossMap; - QString normalMap; - QString bumpMap; - QString occlusionMap; - QString lightMap; - QString scatteringMap; + QString name { "" }; + QString model { "" }; + float opacity { 0.0f }; + float roughness { 0.0f }; + float metallic { 0.0f }; + float scattering { 0.0f }; + bool unlit { false }; + glm::vec3 emissive { 0.0f }; + glm::vec3 albedo { 0.0f }; + QString emissiveMap { "" }; + QString albedoMap { "" }; + QString opacityMap { "" }; + QString opacityMapMode { "" }; + float opacityCutoff { 0.0f }; + QString metallicMap { "" }; + QString specularMap { "" }; + QString roughnessMap { "" }; + QString glossMap { "" }; + QString normalMap { "" }; + QString bumpMap { "" }; + QString occlusionMap { "" }; + QString lightMap { "" }; + QString scatteringMap { "" }; std::array texCoordTransforms; - QString cullFaceMode; - bool defaultFallthrough; + QString cullFaceMode { "" }; + bool defaultFallthrough { false }; std::unordered_map propertyFallthroughs; // not actually exposed to script - QString procedural; + QString procedural { "" }; + + glm::vec3 shade { 0.0f }; + QString shadeMap { "" }; + float shadingShift { 0.0f }; + QString shadingShiftMap { "" }; + float shadingToony { 0.0f }; + glm::vec3 matcap { 0.0f }; + QString matcapMap { "" }; + glm::vec3 parametricRim { 0.0f }; + float parametricRimFresnelPower { 0.0f }; + float parametricRimLift { 0.0f }; + QString rimMap { "" }; + float rimLightingMix { 0.0f }; + QString outlineWidthMode { "" }; + float outlineWidth { 0.0f }; + glm::vec3 outline { 0.0f }; + QString uvAnimationMaskMap { "" }; + float uvAnimationScrollXSpeed { 0.0f }; + float uvAnimationScrollYSpeed { 0.0f }; + float uvAnimationRotationSpeed { 0.0f }; graphics::MaterialKey key { 0 }; }; @@ -88,7 +108,7 @@ namespace scriptable { /*@jsdoc * A material layer. * @typedef {object} Graphics.MaterialLayer - * @property {Graphics.Material} material - The layer's material. + * @property {Entities.Material} material - The layer's material. * @property {number} priority - The priority of the layer. If multiple materials are applied to a mesh part, only the * layer with the highest priority is applied, with materials of the same priority randomly assigned. */ diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 0dd5b95532..d907b5e9d6 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -369,120 +369,6 @@ namespace scriptable { return true; } - /*@jsdoc - * A material in a {@link GraphicsModel}. - * @typedef {object} Graphics.Material - * @property {string} name - The name of the material. - * @property {string} model - Different material models support different properties and rendering modes. Supported models - * are: "hifi_pbr" and "hifi_shader_simple". - * @property {Vec3|string} [albedo] - The albedo color. Component values are in the range 0.0 – - * 1.0. - * If "fallthrough" then it falls through to the material below. - * @property {number|string} [opacity] - The opacity, range 0.01.0. - * If "fallthrough" then it falls through to the material below. - * - * @property {number|string} [opacityCutoff] - The opacity cutoff threshold used to determine the opaque texels of the - * opacityMap when opacityMapMode is "OPACITY_MAP_MASK". Range 0.0 - * – 1.0. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {number|string} [roughness] - The roughness, range 0.01.0. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {number|string} [metallic] - The metallicness, range 0.01.0. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {number|string} [scattering] - The scattering, range 0.01.0. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {boolean|string} [unlit] - true if the material is unaffected by lighting, false if it - * it is lit by the key light and local lights. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {Vec3|string} [emissive] - The emissive color, i.e., the color that the material emits. Component values are - * in the range 0.01.0. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {string} [albedoMap] - The URL of the albedo texture image. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {string} [opacityMap] - The URL of the opacity texture image. - * "hifi_pbr" model only. - * @property {string} [opacityMapMode] - The mode defining the interpretation of the opacity map. Values can be: - *
    - *
  • "OPACITY_MAP_OPAQUE" for ignoring the opacity map information.
  • - *
  • "OPACITY_MAP_MASK" for using the opacityMap as a mask, where only the texel greater - * than opacityCutoff are visible and rendered opaque.
  • - *
  • "OPACITY_MAP_BLEND" for using the opacityMap for alpha blending the material surface - * with the background.
  • - *
- * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {string} [occlusionMap] - The URL of the occlusion texture image. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {string} [lightMap] - The URL of the light map texture image. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {string} [lightmapParams] - Parameters for controlling how lightMap is used. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - *

Currently not used.

- * @property {string} [scatteringMap] - The URL of the scattering texture image. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {string} [emissiveMap] - The URL of the emissive texture image. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {string} [metallicMap] - The URL of the metallic texture image. - * If "fallthrough" then it and specularMap fall through to the material below. - * Only use one of metallicMap and specularMap. - * "hifi_pbr" model only. - * @property {string} [specularMap] - The URL of the specular texture image. - * Only use one of metallicMap and specularMap. - * "hifi_pbr" model only. - * @property {string} [roughnessMap] - The URL of the roughness texture image. - * If "fallthrough" then it and glossMap fall through to the material below. - * Only use one of roughnessMap and glossMap. - * "hifi_pbr" model only. - * @property {string} [glossMap] - The URL of the gloss texture image. - * Only use one of roughnessMap and glossMap. - * "hifi_pbr" model only. - * @property {string} [normalMap] - The URL of the normal texture image. - * If "fallthrough" then it and bumpMap fall through to the material below. - * Only use one of normalMap and bumpMap. - * "hifi_pbr" model only. - * @property {string} [bumpMap] - The URL of the bump texture image. - * Only use one of normalMap and bumpMap. - * "hifi_pbr" model only. - * @property {string} [materialParams] - Parameters for controlling the material projection and repetition. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - *

Currently not used.

- * @property {string} [cullFaceMode="CULL_BACK"] - Specifies Which faces of the geometry to render. Values can be: - *
    - *
  • "CULL_NONE" to render both sides of the geometry.
  • - *
  • "CULL_FRONT" to cull the front faces of the geometry.
  • - *
  • "CULL_BACK" (the default) to cull the back faces of the geometry.
  • - *
- * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {Mat4|string} [texCoordTransform0] - The transform to use for all of the maps apart from - * occlusionMap and lightMap. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {Mat4|string} [texCoordTransform1] - The transform to use for occlusionMap and - * lightMap. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * - * @property {string} procedural - The definition of a procedural shader material. - * "hifi_shader_simple" model only. - *

Currently not used.

- * - * @property {boolean} defaultFallthrough - true if all properties fall through to the material below unless - * they are set, false if properties respect their individual fall-through settings. - */ ScriptValue scriptableMaterialToScriptValue(ScriptEngine* engine, const scriptable::ScriptableMaterial &material) { ScriptValue obj = engine->newObject(); obj.setProperty("name", material.name); @@ -503,7 +389,7 @@ namespace scriptable { obj.setProperty("albedo", vec3ColorToScriptValue(engine, material.albedo)); } - if (material.model.toStdString() == graphics::Material::HIFI_PBR) { + if (material.model.toStdString() == graphics::Material::HIFI_PBR || material.model.toStdString() == graphics::Material::VRM_MTOON) { if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_CUTOFF_VAL_BIT)) { obj.setProperty("opacityCutoff", FALLTHROUGH); } else if (material.key.isOpacityCutoff()) { @@ -516,30 +402,6 @@ namespace scriptable { obj.setProperty("opacityMapMode", material.opacityMapMode); } - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT)) { - obj.setProperty("roughness", FALLTHROUGH); - } else if (material.key.isGlossy()) { - obj.setProperty("roughness", material.roughness); - } - - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_VAL_BIT)) { - obj.setProperty("metallic", FALLTHROUGH); - } else if (material.key.isMetallic()) { - obj.setProperty("metallic", material.metallic); - } - - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_VAL_BIT)) { - obj.setProperty("scattering", FALLTHROUGH); - } else if (material.key.isScattering()) { - obj.setProperty("scattering", material.scattering); - } - - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::UNLIT_VAL_BIT)) { - obj.setProperty("unlit", FALLTHROUGH); - } else if (material.key.isUnlit()) { - obj.setProperty("unlit", material.unlit); - } - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_VAL_BIT)) { obj.setProperty("emissive", FALLTHROUGH); } else if (material.key.isEmissive()) { @@ -562,41 +424,6 @@ namespace scriptable { obj.setProperty("opacityMap", material.opacityMap); } - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT)) { - obj.setProperty("occlusionMap", FALLTHROUGH); - } else if (!material.occlusionMap.isEmpty()) { - obj.setProperty("occlusionMap", material.occlusionMap); - } - - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::LIGHT_MAP_BIT)) { - obj.setProperty("lightMap", FALLTHROUGH); - } else if (!material.lightMap.isEmpty()) { - obj.setProperty("lightMap", material.lightMap); - } - - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_MAP_BIT)) { - obj.setProperty("scatteringMap", FALLTHROUGH); - } else if (!material.scatteringMap.isEmpty()) { - obj.setProperty("scatteringMap", material.scatteringMap); - } - - // Only set one of each of these - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT)) { - obj.setProperty("metallicMap", FALLTHROUGH); - } else if (!material.metallicMap.isEmpty()) { - obj.setProperty("metallicMap", material.metallicMap); - } else if (!material.specularMap.isEmpty()) { - obj.setProperty("specularMap", material.specularMap); - } - - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT)) { - obj.setProperty("roughnessMap", FALLTHROUGH); - } else if (!material.roughnessMap.isEmpty()) { - obj.setProperty("roughnessMap", material.roughnessMap); - } else if (!material.glossMap.isEmpty()) { - obj.setProperty("glossMap", material.glossMap); - } - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::NORMAL_MAP_BIT)) { obj.setProperty("normalMap", FALLTHROUGH); } else if (!material.normalMap.isEmpty()) { @@ -616,10 +443,7 @@ namespace scriptable { obj.setProperty("texCoordTransform1", mat4toScriptValue(engine, material.texCoordTransforms[1])); } - // These need to be implemented, but set the fallthrough for now - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::LIGHTMAP_PARAMS)) { - obj.setProperty("lightmapParams", FALLTHROUGH); - } + // This needs to be implemented, but set the fallthrough for now if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::MATERIAL_PARAMS)) { obj.setProperty("materialParams", FALLTHROUGH); } @@ -629,6 +453,196 @@ namespace scriptable { } else if (!material.cullFaceMode.isEmpty()) { obj.setProperty("cullFaceMode", material.cullFaceMode); } + + if (material.model.toStdString() == graphics::Material::HIFI_PBR) { + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT)) { + obj.setProperty("roughness", FALLTHROUGH); + } else if (material.key.isGlossy()) { + obj.setProperty("roughness", material.roughness); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_VAL_BIT)) { + obj.setProperty("metallic", FALLTHROUGH); + } else if (material.key.isMetallic()) { + obj.setProperty("metallic", material.metallic); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_VAL_BIT)) { + obj.setProperty("scattering", FALLTHROUGH); + } else if (material.key.isScattering()) { + obj.setProperty("scattering", material.scattering); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::UNLIT_VAL_BIT)) { + obj.setProperty("unlit", FALLTHROUGH); + } else if (material.key.isUnlit()) { + obj.setProperty("unlit", material.unlit); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT)) { + obj.setProperty("occlusionMap", FALLTHROUGH); + } else if (!material.occlusionMap.isEmpty()) { + obj.setProperty("occlusionMap", material.occlusionMap); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::LIGHT_MAP_BIT)) { + obj.setProperty("lightMap", FALLTHROUGH); + } else if (!material.lightMap.isEmpty()) { + obj.setProperty("lightMap", material.lightMap); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_MAP_BIT)) { + obj.setProperty("scatteringMap", FALLTHROUGH); + } else if (!material.scatteringMap.isEmpty()) { + obj.setProperty("scatteringMap", material.scatteringMap); + } + + // Only set one of each of these + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT)) { + obj.setProperty("metallicMap", FALLTHROUGH); + } else if (!material.metallicMap.isEmpty()) { + obj.setProperty("metallicMap", material.metallicMap); + } else if (!material.specularMap.isEmpty()) { + obj.setProperty("specularMap", material.specularMap); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT)) { + obj.setProperty("roughnessMap", FALLTHROUGH); + } else if (!material.roughnessMap.isEmpty()) { + obj.setProperty("roughnessMap", material.roughnessMap); + } else if (!material.glossMap.isEmpty()) { + obj.setProperty("glossMap", material.glossMap); + } + + // This needs to be implemented, but set the fallthrough for now + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::LIGHTMAP_PARAMS)) { + obj.setProperty("lightmapParams", FALLTHROUGH); + } + } else { + // See the mappings in ProceduralMatericalCache.h + // SHADE_VAL_BIT = graphics::MaterialKey::FlagBit::UNLIT_VAL_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::UNLIT_VAL_BIT)) { + obj.setProperty("shade", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::UNLIT_VAL_BIT]) { + obj.setProperty("shade", vec3ColorToScriptValue(engine, material.shade)); + } + + // SHADE_MAP_BIT = graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT)) { + obj.setProperty("shadeMap", FALLTHROUGH); + } else if (!material.shadeMap.isEmpty()) { + obj.setProperty("shadeMap", material.shadeMap); + } + + // SHADING_SHIFT_VAL_BIT = graphics::MaterialKey::FlagBit::METALLIC_VAL_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_VAL_BIT)) { + obj.setProperty("shadingShift", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::METALLIC_VAL_BIT]) { + obj.setProperty("shadingShift", material.shadingShift); + } + + // SHADING_SHIFT_MAP_BIT = graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT)) { + obj.setProperty("shadingShiftMap", FALLTHROUGH); + } else if (!material.shadingShiftMap.isEmpty()) { + obj.setProperty("shadingShiftMap", material.shadingShiftMap); + } + + // SHADING_TOONY_VAL_BIT = graphics::MaterialKey::FlagBit::GLOSSY_VAL_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT)) { + obj.setProperty("shadingToony", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::GLOSSY_VAL_BIT]) { + obj.setProperty("shadingToony", material.shadingToony); + } + + // MATCAP_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_1_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::EXTRA_1_BIT)) { + obj.setProperty("matcap", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::EXTRA_1_BIT]) { + obj.setProperty("matcap", vec3ColorToScriptValue(engine, material.matcap)); + } + + // MATCAP_MAP_BIT = graphics::MaterialKey::FlagBit::OCCLUSION_MAP_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT)) { + obj.setProperty("matcapMap", FALLTHROUGH); + } else if (!material.matcapMap.isEmpty()) { + obj.setProperty("matcapMap", material.matcapMap); + } + + // PARAMETRIC_RIM_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_2_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::EXTRA_2_BIT)) { + obj.setProperty("parametricRim", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::EXTRA_2_BIT]) { + obj.setProperty("parametricRim", vec3ColorToScriptValue(engine, material.parametricRim)); + } + + // PARAMETRIC_RIM_POWER_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_3_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::EXTRA_3_BIT)) { + obj.setProperty("parametricRimFresnelPower", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::EXTRA_3_BIT]) { + obj.setProperty("parametricRimFresnelPower", material.parametricRimFresnelPower); + } + + // PARAMETRIC_RIM_LIFT_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_4_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::EXTRA_4_BIT)) { + obj.setProperty("parametricRimLift", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::EXTRA_4_BIT]) { + obj.setProperty("parametricRimLift", material.parametricRimLift); + } + + // RIM_MAP_BIT = graphics::MaterialKey::FlagBit::SCATTERING_MAP_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_MAP_BIT)) { + obj.setProperty("rimMap", FALLTHROUGH); + } else if (!material.rimMap.isEmpty()) { + obj.setProperty("rimMap", material.rimMap); + } + + // RIM_LIGHTING_MIX_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_5_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::EXTRA_5_BIT)) { + obj.setProperty("rimLightingMix", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::EXTRA_5_BIT]) { + obj.setProperty("rimLightingMix", material.rimLightingMix); + } + + // UV_ANIMATION_MASK_MAP_BIT = graphics::MaterialKey::FlagBit::LIGHT_MAP_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::LIGHT_MAP_BIT)) { + obj.setProperty("uvAnimationMaskMap", FALLTHROUGH); + } else if (!material.uvAnimationMaskMap.isEmpty()) { + obj.setProperty("uvAnimationMaskMap", material.uvAnimationMaskMap); + } + + // UV_ANIMATION_SCROLL_VAL_BIT = graphics::MaterialKey::FlagBit::SCATTERING_VAL_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_VAL_BIT)) { + obj.setProperty("uvAnimationScrollXSpeed", FALLTHROUGH); + obj.setProperty("uvAnimationScrollYSpeed", FALLTHROUGH); + obj.setProperty("uvAnimationRotationSpeed", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::SCATTERING_VAL_BIT]) { + obj.setProperty("uvAnimationScrollXSpeed", material.uvAnimationScrollXSpeed); + obj.setProperty("uvAnimationScrollYSpeed", material.uvAnimationScrollYSpeed); + obj.setProperty("uvAnimationRotationSpeed", material.uvAnimationRotationSpeed); + } + + // OUTLINE_WIDTH_MODE_VAL_BIT = graphics::Material::ExtraFlagBit::EXTRA_1_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::EXTRA_1_BIT)) { + obj.setProperty("outlineWidthMode", FALLTHROUGH); + } else { + obj.setProperty("outlineWidthMode", material.outlineWidthMode); + } + + // OUTLINE_WIDTH_VAL_BIT = graphics::Material::ExtraFlagBit::EXTRA_2_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::EXTRA_2_BIT)) { + obj.setProperty("outlineWidth", FALLTHROUGH); + } else { + obj.setProperty("outlineWidth", material.outlineWidth); + } + + // OUTLINE_VAL_BIT = graphics::Material::ExtraFlagBit::EXTRA_3_BIT + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::EXTRA_3_BIT)) { + obj.setProperty("outline", FALLTHROUGH); + } else { + obj.setProperty("outline", vec3ColorToScriptValue(engine, material.outline)); + } + } } else if (material.model.toStdString() == graphics::Material::HIFI_SHADER_SIMPLE) { obj.setProperty("procedural", material.procedural); } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index 21454dfda0..3cf70915c3 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -27,27 +27,50 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const opacity = material.opacity; albedo = material.albedo; - if (model.toStdString() == graphics::Material::HIFI_PBR) { + if (model.toStdString() == graphics::Material::HIFI_PBR || model.toStdString() == graphics::Material::VRM_MTOON) { opacityCutoff = material.opacityCutoff; opacityMapMode = material.opacityMapMode; - roughness = material.roughness; - metallic = material.metallic; - scattering = material.scattering; - unlit = material.unlit; emissive = material.emissive; emissiveMap = material.emissiveMap; albedoMap = material.albedoMap; opacityMap = material.opacityMap; - metallicMap = material.metallicMap; - specularMap = material.specularMap; - roughnessMap = material.roughnessMap; - glossMap = material.glossMap; normalMap = material.normalMap; bumpMap = material.bumpMap; - occlusionMap = material.occlusionMap; - lightMap = material.lightMap; - scatteringMap = material.scatteringMap; cullFaceMode = material.cullFaceMode; + + if (model.toStdString() == graphics::Material::HIFI_PBR) { + roughness = material.roughness; + metallic = material.metallic; + scattering = material.scattering; + unlit = material.unlit; + metallicMap = material.metallicMap; + specularMap = material.specularMap; + roughnessMap = material.roughnessMap; + glossMap = material.glossMap; + occlusionMap = material.occlusionMap; + lightMap = material.lightMap; + scatteringMap = material.scatteringMap; + } else { + shade = material.shade; + shadeMap = material.shadeMap; + shadingShift = material.shadingShift; + shadingShiftMap = material.shadingShiftMap; + shadingToony = material.shadingToony; + matcap = material.matcap; + matcapMap = material.matcapMap; + parametricRim = material.parametricRim; + parametricRimFresnelPower = material.parametricRimFresnelPower; + parametricRimLift = material.parametricRimLift; + rimMap = material.rimMap; + rimLightingMix = material.rimLightingMix; + outlineWidthMode = material.outlineWidthMode; + outlineWidth = material.outlineWidth; + outline = material.outline; + uvAnimationMaskMap = material.uvAnimationMaskMap; + uvAnimationScrollXSpeed = material.uvAnimationScrollXSpeed; + uvAnimationScrollYSpeed = material.uvAnimationScrollYSpeed; + uvAnimationRotationSpeed = material.uvAnimationRotationSpeed; + } } else if (model.toStdString() == graphics::Material::HIFI_SHADER_SIMPLE) { procedural = material.procedural; } @@ -67,13 +90,9 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint opacity = material->getOpacity(); albedo = material->getAlbedo(); - if (model.toStdString() == graphics::Material::HIFI_PBR) { + if (model.toStdString() == graphics::Material::HIFI_PBR || model.toStdString() == graphics::Material::VRM_MTOON) { opacityCutoff = material->getOpacityCutoff(); opacityMapMode = QString(graphics::MaterialKey::getOpacityMapModeName(material->getOpacityMapMode()).c_str()); - roughness = material->getRoughness(); - metallic = material->getMetallic(); - scattering = material->getScattering(); - unlit = material->isUnlit(); emissive = material->getEmissive(); auto map = material->getTextureMap(graphics::Material::MapChannel::EMISSIVE_MAP); @@ -89,24 +108,6 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint } } - map = material->getTextureMap(graphics::Material::MapChannel::METALLIC_MAP); - if (map && map->getTextureSource()) { - if (map->getTextureSource()->getType() == image::TextureUsage::Type::METALLIC_TEXTURE) { - metallicMap = map->getTextureSource()->getUrl().toString(); - } else if (map->getTextureSource()->getType() == image::TextureUsage::Type::SPECULAR_TEXTURE) { - specularMap = map->getTextureSource()->getUrl().toString(); - } - } - - map = material->getTextureMap(graphics::Material::MapChannel::ROUGHNESS_MAP); - if (map && map->getTextureSource()) { - if (map->getTextureSource()->getType() == image::TextureUsage::Type::ROUGHNESS_TEXTURE) { - roughnessMap = map->getTextureSource()->getUrl().toString(); - } else if (map->getTextureSource()->getType() == image::TextureUsage::Type::GLOSS_TEXTURE) { - glossMap = map->getTextureSource()->getUrl().toString(); - } - } - map = material->getTextureMap(graphics::Material::MapChannel::NORMAL_MAP); if (map && map->getTextureSource()) { if (map->getTextureSource()->getType() == image::TextureUsage::Type::NORMAL_TEXTURE) { @@ -116,26 +117,92 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint } } - map = material->getTextureMap(graphics::Material::MapChannel::OCCLUSION_MAP); - if (map && map->getTextureSource()) { - occlusionMap = map->getTextureSource()->getUrl().toString(); - } - - map = material->getTextureMap(graphics::Material::MapChannel::LIGHT_MAP); - if (map && map->getTextureSource()) { - lightMap = map->getTextureSource()->getUrl().toString(); - } - - map = material->getTextureMap(graphics::Material::MapChannel::SCATTERING_MAP); - if (map && map->getTextureSource()) { - scatteringMap = map->getTextureSource()->getUrl().toString(); - } - for (int i = 0; i < graphics::Material::NUM_TEXCOORD_TRANSFORMS; i++) { texCoordTransforms[i] = material->getTexCoordTransform(i); } cullFaceMode = QString(graphics::MaterialKey::getCullFaceModeName(material->getCullFaceMode()).c_str()); + + if (model.toStdString() == graphics::Material::HIFI_PBR) { + roughness = material->getRoughness(); + metallic = material->getMetallic(); + scattering = material->getScattering(); + unlit = material->isUnlit(); + + map = material->getTextureMap(graphics::Material::MapChannel::METALLIC_MAP); + if (map && map->getTextureSource()) { + if (map->getTextureSource()->getType() == image::TextureUsage::Type::METALLIC_TEXTURE) { + metallicMap = map->getTextureSource()->getUrl().toString(); + } else if (map->getTextureSource()->getType() == image::TextureUsage::Type::SPECULAR_TEXTURE) { + specularMap = map->getTextureSource()->getUrl().toString(); + } + } + + map = material->getTextureMap(graphics::Material::MapChannel::ROUGHNESS_MAP); + if (map && map->getTextureSource()) { + if (map->getTextureSource()->getType() == image::TextureUsage::Type::ROUGHNESS_TEXTURE) { + roughnessMap = map->getTextureSource()->getUrl().toString(); + } else if (map->getTextureSource()->getType() == image::TextureUsage::Type::GLOSS_TEXTURE) { + glossMap = map->getTextureSource()->getUrl().toString(); + } + } + + map = material->getTextureMap(graphics::Material::MapChannel::OCCLUSION_MAP); + if (map && map->getTextureSource()) { + occlusionMap = map->getTextureSource()->getUrl().toString(); + } + + map = material->getTextureMap(graphics::Material::MapChannel::LIGHT_MAP); + if (map && map->getTextureSource()) { + lightMap = map->getTextureSource()->getUrl().toString(); + } + + map = material->getTextureMap(graphics::Material::MapChannel::SCATTERING_MAP); + if (map && map->getTextureSource()) { + scatteringMap = map->getTextureSource()->getUrl().toString(); + } + } else { + shade = material->getShade(); + shadingShift = material->getShadingShift(); + shadingToony = material->getShadingToony(); + matcap = material->getMatcap(); + parametricRim = material->getParametricRim(); + parametricRimFresnelPower = material->getParametricRimFresnelPower(); + parametricRimLift = material->getParametricRimLift(); + rimLightingMix = material->getRimLightingMix(); + outlineWidthMode = material->getOutlineWidthMode(); + outlineWidth = material->getOutlineWidth(); + outline = material->getOutline(); + uvAnimationScrollXSpeed = material->getUVAnimationScrollXSpeed(); + uvAnimationScrollYSpeed = material->getUVAnimationScrollYSpeed(); + uvAnimationRotationSpeed = material->getUVAnimationRotationSpeed(); + + // See the mappings in ProceduralMatericalCache.h + map = material->getTextureMap(graphics::Material::MapChannel::ROUGHNESS_MAP); + if (map && map->getTextureSource()) { + shadeMap = map->getTextureSource()->getUrl().toString(); + } + + map = material->getTextureMap(graphics::Material::MapChannel::METALLIC_MAP); + if (map && map->getTextureSource()) { + shadingShiftMap = map->getTextureSource()->getUrl().toString(); + } + + map = material->getTextureMap(graphics::Material::MapChannel::OCCLUSION_MAP); + if (map && map->getTextureSource()) { + matcapMap = map->getTextureSource()->getUrl().toString(); + } + + map = material->getTextureMap(graphics::Material::MapChannel::SCATTERING_MAP); + if (map && map->getTextureSource()) { + rimMap = map->getTextureSource()->getUrl().toString(); + } + + map = material->getTextureMap(graphics::Material::MapChannel::LIGHT_MAP); + if (map && map->getTextureSource()) { + uvAnimationMaskMap = map->getTextureSource()->getUrl().toString(); + } + } } else if (model.toStdString() == graphics::Material::HIFI_SHADER_SIMPLE) { procedural = material->getProceduralString(); } diff --git a/libraries/graphics/src/graphics/Geometry.cpp b/libraries/graphics/src/graphics/Geometry.cpp index 0fb2a0eb51..e46097207b 100644 --- a/libraries/graphics/src/graphics/Geometry.cpp +++ b/libraries/graphics/src/graphics/Geometry.cpp @@ -19,6 +19,8 @@ Mesh::Mesh() : _vertexBuffer(gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)), _indexBuffer(gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::INDEX)), _partBuffer(gpu::Element(gpu::VEC4, gpu::UINT32, gpu::PART)) { + const uint32_t compactColor = 0xFFFFFFFF; + _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); } Mesh::Mesh(const Mesh& mesh) : @@ -26,7 +28,8 @@ Mesh::Mesh(const Mesh& mesh) : _vertexBuffer(mesh._vertexBuffer), _attributeBuffers(mesh._attributeBuffers), _indexBuffer(mesh._indexBuffer), - _partBuffer(mesh._partBuffer) { + _partBuffer(mesh._partBuffer), + _colorBuffer(mesh._colorBuffer) { } Mesh::~Mesh() { @@ -39,6 +42,13 @@ void Mesh::setVertexFormatAndStream(const gpu::Stream::FormatPointer& vf, const auto attrib = _vertexFormat->getAttribute(gpu::Stream::POSITION); _vertexBuffer = BufferView(vbs->getBuffers()[attrib._channel], vbs->getOffsets()[attrib._channel], vbs->getBuffers()[attrib._channel]->getSize(), (gpu::uint16) vbs->getStrides()[attrib._channel], attrib._element); + + // We require meshes to have a color attribute. If they don't, we default to white. + if (!_vertexFormat->hasAttribute(gpu::Stream::COLOR)) { + gpu::Stream::Slot channelNum = (gpu::Stream::Slot)_vertexStream.getNumBuffers(); + _vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); + _vertexStream.addBuffer(_colorBuffer, 0, _vertexFormat->getChannels().at(channelNum)._stride); + } } void Mesh::setVertexBuffer(const BufferView& buffer) { @@ -98,6 +108,12 @@ void Mesh::evalVertexStream() { _vertexStream.addBuffer(view._buffer, view._offset, stride); channelNum++; } + + // We require meshes to have a color attribute. If they don't, we default to white. + if (!_vertexFormat->hasAttribute(gpu::Stream::COLOR)) { + _vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); + _vertexStream.addBuffer(_colorBuffer, 0, _vertexFormat->getChannels().at(channelNum)._stride); + } } void Mesh::setIndexBuffer(const BufferView& buffer) { diff --git a/libraries/graphics/src/graphics/Geometry.h b/libraries/graphics/src/graphics/Geometry.h index fe1981c0e9..dcaeaad271 100644 --- a/libraries/graphics/src/graphics/Geometry.h +++ b/libraries/graphics/src/graphics/Geometry.h @@ -15,6 +15,7 @@ #include +#include #include #include @@ -28,7 +29,6 @@ typedef glm::vec3 Vec3; class Mesh; using MeshPointer = std::shared_ptr< Mesh >; - class Mesh { public: const static Index PRIMITIVE_RESTART_INDEX = -1; @@ -142,6 +142,8 @@ public: std::string modelName; std::string displayName; + gpu::BufferPointer getColorBuffer() const { return _colorBuffer; } + protected: gpu::Stream::FormatPointer _vertexFormat; @@ -154,6 +156,8 @@ protected: BufferView _partBuffer; + gpu::BufferPointer _colorBuffer { std::make_shared() }; + void evalVertexFormat(); void evalVertexStream(); diff --git a/libraries/graphics/src/graphics/Material.cpp b/libraries/graphics/src/graphics/Material.cpp index 836487de14..1061347e27 100644 --- a/libraries/graphics/src/graphics/Material.cpp +++ b/libraries/graphics/src/graphics/Material.cpp @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 12/10/2014. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 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 @@ -60,7 +61,8 @@ bool MaterialKey::getCullFaceModeFromName(const std::string& modeName, CullFaceM } const std::string Material::HIFI_PBR { "hifi_pbr" }; -const std::string Material::HIFI_SHADER_SIMPLE { "hifi_shader_simple" }; +const std::string Material::HIFI_SHADER_SIMPLE{ "hifi_shader_simple" }; +const std::string Material::VRM_MTOON { "vrm_mtoon" }; Material::Material() { for (int i = 0; i < NUM_TOTAL_FLAGS; i++) { @@ -70,8 +72,8 @@ Material::Material() { Material::Material(const Material& material) : _name(material._name), - _model(material._model), _key(material._key), + _model(material._model), _emissive(material._emissive), _opacity(material._opacity), _albedo(material._albedo), @@ -258,6 +260,17 @@ void Material::setTextureTransforms(const Transform& transform, MaterialMappingM _materialParams = glm::vec2(mode, repeat); } +const glm::vec3 Material::DEFAULT_SHADE = glm::vec3(0.0f); +const float Material::DEFAULT_SHADING_SHIFT = 0.0f; +const float Material::DEFAULT_SHADING_TOONY = 0.9f; +const glm::vec3 Material::DEFAULT_MATCAP = glm::vec3(1.0f); +const glm::vec3 Material::DEFAULT_PARAMETRIC_RIM = glm::vec3(0.0f); +const float Material::DEFAULT_PARAMETRIC_RIM_FRESNEL_POWER = 5.0f; +const float Material::DEFAULT_PARAMETRIC_RIM_LIFT = 0.0f; +const float Material::DEFAULT_RIM_LIGHTING_MIX = 1.0f; +const float Material::DEFAULT_UV_ANIMATION_SCROLL_SPEED = 0.0f; +const glm::vec3 Material::DEFAULT_OUTLINE = glm::vec3(0.0f); + MultiMaterial::MultiMaterial() { Schema schema; _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema))); @@ -311,3 +324,30 @@ bool MultiMaterial::anyReferenceMaterialsOrTexturesChanged() const { return false; } + +void MultiMaterial::setisMToon(bool isMToon) { + if (isMToon != _isMToon) { + if (isMToon) { + MToonSchema toonSchema; + _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(MToonSchema), (const gpu::Byte*) &toonSchema, sizeof(MToonSchema))); + } else { + Schema schema; + _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema))); + } + } + _isMToon = isMToon; +} + +void MultiMaterial::setMToonTime() { + assert(_isMToon); + + // Some objects, like material entities, don't have persistent MultiMaterials to store this in, so we just store it once statically + static uint64_t mtoonStartTime; + static std::once_flag once; + std::call_once(once, [] { + mtoonStartTime = usecTimestampNow(); + }); + + // Minimize floating point error by doing an integer division to milliseconds, before the floating point division to seconds + _schemaBuffer.edit()._time = (float)((usecTimestampNow() - mtoonStartTime) / USECS_PER_MSEC) / MSECS_PER_SECOND; +} diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index 2eb4e0cbe1..fd9c76dd97 100644 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 12/10/2014. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 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 @@ -34,6 +35,7 @@ typedef std::shared_ptr< TextureMap > TextureMapPointer; // Material Key is a coarse trait description of a material used to classify the materials class MaterialKey { public: + // Be careful changing these, they need to match up with the bits in graphics/Material.slh enum FlagBit { EMISSIVE_VAL_BIT = 0, UNLIT_VAL_BIT, @@ -57,6 +59,12 @@ public: LIGHT_MAP_BIT, SCATTERING_MAP_BIT, + EXTRA_1_BIT, + EXTRA_2_BIT, + EXTRA_3_BIT, + EXTRA_4_BIT, + EXTRA_5_BIT, + NUM_FLAGS, }; typedef std::bitset Flags; @@ -419,6 +427,10 @@ public: MATERIAL_PARAMS, CULL_FACE_MODE, + EXTRA_1_BIT, + EXTRA_2_BIT, + EXTRA_3_BIT, + NUM_TOTAL_FLAGS }; std::unordered_map getPropertyFallthroughs() { return _propertyFallthroughs; } @@ -432,16 +444,43 @@ public: virtual bool isReference() const { return false; } + virtual bool isMToon() const { return false; } + static const glm::vec3 DEFAULT_SHADE; + virtual glm::vec3 getShade(bool SRGB = true) const { return glm::vec3(0.0f); } + static const float DEFAULT_SHADING_SHIFT; + virtual float getShadingShift() const { return 0.0f; } + static const float DEFAULT_SHADING_TOONY; + virtual float getShadingToony() const { return 0.0f; } + static const glm::vec3 DEFAULT_MATCAP; + virtual glm::vec3 getMatcap(bool SRGB = true) const { return glm::vec3(0.0f); } + static const glm::vec3 DEFAULT_PARAMETRIC_RIM; + virtual glm::vec3 getParametricRim(bool SRGB = true) const { return glm::vec3(0.0f); } + static const float DEFAULT_PARAMETRIC_RIM_FRESNEL_POWER; + virtual float getParametricRimFresnelPower() const { return 0.0f; } + static const float DEFAULT_PARAMETRIC_RIM_LIFT; + virtual float getParametricRimLift() const { return 0.0f; } + static const float DEFAULT_RIM_LIGHTING_MIX; + virtual float getRimLightingMix() const { return 0.0f; } + static const float DEFAULT_UV_ANIMATION_SCROLL_SPEED; + virtual float getUVAnimationScrollXSpeed() const { return 0.0f; } + virtual float getUVAnimationScrollYSpeed() const { return 0.0f; } + virtual float getUVAnimationRotationSpeed() const { return 0.0f; } + + static const glm::vec3 DEFAULT_OUTLINE; + virtual uint8_t getOutlineWidthMode() { return 0; } + virtual float getOutlineWidth() { return 0.0f; } + virtual glm::vec3 getOutline(bool SRGB = true) const { return glm::vec3(0.0f); } + static const std::string HIFI_PBR; static const std::string HIFI_SHADER_SIMPLE; + static const std::string VRM_MTOON; protected: std::string _name { "" }; + mutable MaterialKey _key { 0 }; + std::string _model { HIFI_PBR }; private: - std::string _model { HIFI_PBR }; - mutable MaterialKey _key { 0 }; - // Material properties glm::vec3 _emissive { DEFAULT_EMISSIVE }; float _opacity { DEFAULT_OPACITY }; @@ -525,12 +564,12 @@ public: // Texture Coord Transform Array glm::mat4 _texcoordTransforms[Material::NUM_TEXCOORD_TRANSFORMS]; - glm::vec2 _lightmapParams { 0.0, 1.0 }; - // x: material mode (0 for UV, 1 for PROJECTED) // y: 1 for texture repeat, 0 for discard outside of 0 - 1 glm::vec2 _materialParams { 0.0, 1.0 }; + glm::vec2 _lightmapParams { 0.0, 1.0 }; + Schema() { for (auto& transform : _texcoordTransforms) { transform = glm::mat4(); @@ -538,8 +577,68 @@ public: } }; + class MToonSchema { + public: + glm::vec3 _emissive { Material::DEFAULT_EMISSIVE }; // No Emissive + float _opacity { Material::DEFAULT_OPACITY }; // Opacity = 1 => Not Transparent + + glm::vec3 _albedo { Material::DEFAULT_ALBEDO }; // Grey albedo => isAlbedo + float _opacityCutoff { Material::DEFAULT_OPACITY_CUTOFF }; // Opacity cutoff applyed when using opacityMap as Mask + + glm::vec3 _shade { Material::DEFAULT_SHADE }; + float _shadingShift { Material::DEFAULT_SHADING_SHIFT }; + + glm::vec3 _matcap { Material::DEFAULT_MATCAP }; + float _shadingToony { Material::DEFAULT_SHADING_TOONY }; + + glm::vec3 _parametricRim { Material::DEFAULT_PARAMETRIC_RIM }; + float _parametricRimFresnelPower { Material::DEFAULT_PARAMETRIC_RIM_FRESNEL_POWER }; + + float _parametricRimLift { Material::DEFAULT_PARAMETRIC_RIM_LIFT }; + float _rimLightingMix { Material::DEFAULT_RIM_LIGHTING_MIX }; + glm::vec2 _uvAnimationScrollSpeed { Material::DEFAULT_UV_ANIMATION_SCROLL_SPEED }; + + float _uvAnimationScrollRotationSpeed { Material::DEFAULT_UV_ANIMATION_SCROLL_SPEED }; + float _time { 0.0f }; + uint32_t _key { 0 }; // a copy of the materialKey + float _spare { 0.0f }; + + // Texture Coord Transform Array + glm::mat4 _texcoordTransforms[Material::NUM_TEXCOORD_TRANSFORMS]; + + // x: material mode (0 for UV, 1 for PROJECTED) + // y: 1 for texture repeat, 0 for discard outside of 0 - 1 + glm::vec2 _materialParams { 0.0, 1.0 }; + + MToonSchema() { + for (auto& transform : _texcoordTransforms) { + transform = glm::mat4(); + } + } + }; + gpu::BufferView& getSchemaBuffer() { return _schemaBuffer; } - graphics::MaterialKey getMaterialKey() const { return graphics::MaterialKey(_schemaBuffer.get()._key); } + graphics::MaterialKey getMaterialKey() const { + if (_isMToon) { + return graphics::MaterialKey(_schemaBuffer.get()._key); + } else { + return graphics::MaterialKey(_schemaBuffer.get()._key); + } + } + glm::vec4 getColor() const { + glm::vec3 albedo; + float opacity; + if (_isMToon) { + const auto& schema = _schemaBuffer.get(); + albedo = schema._albedo; + opacity = schema._opacity; + } else { + const auto& schema = _schemaBuffer.get(); + albedo = schema._albedo; + opacity = schema._opacity; + } + return glm::vec4(ColorUtils::tosRGBVec3(albedo), opacity); + } const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; } void setCullFaceMode(graphics::MaterialKey::CullFaceMode cullFaceMode) { _cullFaceMode = cullFaceMode; } @@ -559,6 +658,18 @@ public: void addReferenceTexture(const std::function& textureOperator); void addReferenceMaterial(const std::function& materialOperator); + void setisMToon(bool isMToon); + bool isMToon() const { return _isMToon; } + void setMToonTime(); + bool hasOutline() const { return _outlineWidthMode != 0 && _outlineWidth > 0.0f; } + uint8_t getOutlineWidthMode() const { return _outlineWidthMode; } + float getOutlineWidth() const { return _outlineWidth; } + glm::vec3 getOutline() const { return _outline; } + void resetOutline() { _outlineWidthMode = 0; _outlineWidth = 0.0f; _outline = glm::vec3(0.0f); } + void setOutlineWidthMode(uint8_t mode) { _outlineWidthMode = mode; } + void setOutlineWidth(float width) { _outlineWidth = width; } + void setOutline(const glm::vec3& outline) { _outline = outline; } + private: gpu::BufferView _schemaBuffer; graphics::MaterialKey::CullFaceMode _cullFaceMode { graphics::Material::DEFAULT_CULL_FACE_MODE }; @@ -576,6 +687,11 @@ private: std::vector, gpu::TexturePointer>> _referenceTextures; std::vector, graphics::MaterialPointer>> _referenceMaterials; + + bool _isMToon { false }; + uint8_t _outlineWidthMode { 0 }; + float _outlineWidth { 0.0f }; + glm::vec3 _outline { graphics::Material::DEFAULT_OUTLINE }; }; }; diff --git a/libraries/graphics/src/graphics/Material.slh b/libraries/graphics/src/graphics/Material.slh index 274dbc1cdd..4d4dcde34c 100644 --- a/libraries/graphics/src/graphics/Material.slh +++ b/libraries/graphics/src/graphics/Material.slh @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 12/16/14. // Copyright 2013 High Fidelity, Inc. +// Copyright 2024 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 @@ -18,8 +19,10 @@ const int MAX_TEXCOORDS = 2; struct TexMapArray { mat4 _texcoordTransforms0; mat4 _texcoordTransforms1; - vec2 _lightmapParams; vec2 _materialParams; +<@if not HIFI_USE_MTOON@> + vec2 _lightmapParams; +<@endif@> }; <@func declareMaterialTexMapArrayBuffer()@> @@ -45,11 +48,24 @@ struct TexMapArray { // The material values (at least the material key) must be precisely bitwise accurate // to what is provided by the uniform buffer, or the material key has the wrong bits +<@if not HIFI_USE_MTOON@> struct Material { vec4 _emissiveOpacity; vec4 _albedoRoughness; vec4 _metallicScatteringOpacityCutoffKey; }; +<@else@> +struct Material { + vec4 _emissiveOpacity; + vec4 _albedoOpacityCutoff; + + vec4 _shadeShadingShift; + vec4 _matcapShadingToony; + vec4 _parametricRimAndFresnelPower; + vec4 _parametricRimLiftMixUVAnimationScrollSpeedXY; + vec4 _uvAnimationScrollRotationSpeedTimeKeySpare; +}; +<@endif@> LAYOUT_STD140(binding=GRAPHICS_BUFFER_MATERIAL) uniform materialBuffer { Material _mat; @@ -63,39 +79,91 @@ TexMapArray getTexMapArray() { return _texMapArray; } -vec3 getMaterialEmissive(Material m) { return m._emissiveOpacity.rgb; } -float getMaterialOpacity(Material m) { return m._emissiveOpacity.a; } +<@if not HIFI_USE_MTOON@> + vec3 getMaterialEmissive(Material m) { return m._emissiveOpacity.rgb; } + float getMaterialOpacity(Material m) { return m._emissiveOpacity.a; } -vec3 getMaterialAlbedo(Material m) { return m._albedoRoughness.rgb; } -float getMaterialRoughness(Material m) { return m._albedoRoughness.a; } -float getMaterialShininess(Material m) { return 1.0 - getMaterialRoughness(m); } + vec3 getMaterialAlbedo(Material m) { return m._albedoRoughness.rgb; } + float getMaterialRoughness(Material m) { return m._albedoRoughness.a; } + float getMaterialShininess(Material m) { return 1.0 - getMaterialRoughness(m); } -float getMaterialMetallic(Material m) { return m._metallicScatteringOpacityCutoffKey.x; } -float getMaterialScattering(Material m) { return m._metallicScatteringOpacityCutoffKey.y; } -float getMaterialOpacityCutoff(Material m) { return m._metallicScatteringOpacityCutoffKey.z; } + float getMaterialMetallic(Material m) { return m._metallicScatteringOpacityCutoffKey.x; } + float getMaterialScattering(Material m) { return m._metallicScatteringOpacityCutoffKey.y; } + float getMaterialOpacityCutoff(Material m) { return m._metallicScatteringOpacityCutoffKey.z; } -BITFIELD getMaterialKey(Material m) { return floatBitsToInt(m._metallicScatteringOpacityCutoffKey.w); } + BITFIELD getMaterialKey(Material m) { return floatBitsToInt(m._metallicScatteringOpacityCutoffKey.w); } -const BITFIELD EMISSIVE_VAL_BIT = 0x00000001; -const BITFIELD UNLIT_VAL_BIT = 0x00000002; -const BITFIELD ALBEDO_VAL_BIT = 0x00000004; -const BITFIELD METALLIC_VAL_BIT = 0x00000008; -const BITFIELD GLOSSY_VAL_BIT = 0x00000010; -const BITFIELD OPACITY_VAL_BIT = 0x00000020; -const BITFIELD OPACITY_MASK_MAP_BIT = 0x00000040; -const BITFIELD OPACITY_TRANSLUCENT_MAP_BIT = 0x00000080; -const BITFIELD OPACITY_MAP_MODE_BIT = 0x00000100; -const BITFIELD OPACITY_CUTOFF_VAL_BIT = 0x00000200; -const BITFIELD SCATTERING_VAL_BIT = 0x00000400; + const BITFIELD EMISSIVE_VAL_BIT = 0x00000001; + const BITFIELD UNLIT_VAL_BIT = 0x00000002; + const BITFIELD ALBEDO_VAL_BIT = 0x00000004; + const BITFIELD METALLIC_VAL_BIT = 0x00000008; + const BITFIELD GLOSSY_VAL_BIT = 0x00000010; + const BITFIELD OPACITY_VAL_BIT = 0x00000020; + const BITFIELD OPACITY_MASK_MAP_BIT = 0x00000040; + const BITFIELD OPACITY_TRANSLUCENT_MAP_BIT = 0x00000080; + const BITFIELD OPACITY_MAP_MODE_BIT = 0x00000100; + const BITFIELD OPACITY_CUTOFF_VAL_BIT = 0x00000200; + const BITFIELD SCATTERING_VAL_BIT = 0x00000400; -const BITFIELD EMISSIVE_MAP_BIT = 0x00000800; -const BITFIELD ALBEDO_MAP_BIT = 0x00001000; -const BITFIELD METALLIC_MAP_BIT = 0x00002000; -const BITFIELD ROUGHNESS_MAP_BIT = 0x00004000; -const BITFIELD NORMAL_MAP_BIT = 0x00008000; -const BITFIELD OCCLUSION_MAP_BIT = 0x00010000; -const BITFIELD LIGHTMAP_MAP_BIT = 0x00020000; -const BITFIELD SCATTERING_MAP_BIT = 0x00040000; + const BITFIELD EMISSIVE_MAP_BIT = 0x00000800; + const BITFIELD ALBEDO_MAP_BIT = 0x00001000; + const BITFIELD METALLIC_MAP_BIT = 0x00002000; + const BITFIELD ROUGHNESS_MAP_BIT = 0x00004000; + const BITFIELD NORMAL_MAP_BIT = 0x00008000; + const BITFIELD OCCLUSION_MAP_BIT = 0x00010000; + const BITFIELD LIGHTMAP_MAP_BIT = 0x00020000; + const BITFIELD SCATTERING_MAP_BIT = 0x00040000; +<@else@> + vec3 getMaterialEmissive(Material m) { return m._emissiveOpacity.rgb; } + float getMaterialOpacity(Material m) { return m._emissiveOpacity.a; } + + vec3 getMaterialAlbedo(Material m) { return m._albedoOpacityCutoff.rgb; } + float getMaterialOpacityCutoff(Material m) { return m._albedoOpacityCutoff.z; } + + vec3 getMaterialShade(Material m) { return m._shadeShadingShift.rgb; } + float getMaterialShadingShift(Material m) { return m._shadeShadingShift.a; } + + vec3 getMaterialMatcap(Material m) { return m._matcapShadingToony.rgb; } + float getMaterialShadingToony(Material m) { return m._matcapShadingToony.a; } + + vec3 getMaterialParametricRim(Material m) { return m._parametricRimAndFresnelPower.rgb; } + float getMaterialParametricRimFresnelPower(Material m) { return m._parametricRimAndFresnelPower.a; } + + float getMaterialParametricRimLift(Material m) { return m._parametricRimLiftMixUVAnimationScrollSpeedXY.r; } + float getMaterialRimLightingMix(Material m) { return m._parametricRimLiftMixUVAnimationScrollSpeedXY.g; } + + vec3 getMaterialUVScrollSpeed(Material m) { return vec3(m._parametricRimLiftMixUVAnimationScrollSpeedXY.ba, m._uvAnimationScrollRotationSpeedTimeKeySpare.r); } + float getMaterialTime(Material m) { return m._uvAnimationScrollRotationSpeedTimeKeySpare.g; } + + BITFIELD getMaterialKey(Material m) { return floatBitsToInt(m._uvAnimationScrollRotationSpeedTimeKeySpare.b); } + + const BITFIELD EMISSIVE_VAL_BIT = 0x00000001; + const BITFIELD SHADE_VAL_BIT = 0x00000002; + const BITFIELD ALBEDO_VAL_BIT = 0x00000004; + const BITFIELD SHADING_SHIFT_VAL_BIT = 0x00000008; + const BITFIELD SHADING_TOONY_VAL_BIT = 0x00000010; + const BITFIELD OPACITY_VAL_BIT = 0x00000020; + const BITFIELD OPACITY_MASK_MAP_BIT = 0x00000040; + const BITFIELD OPACITY_TRANSLUCENT_MAP_BIT = 0x00000080; + const BITFIELD OPACITY_MAP_MODE_BIT = 0x00000100; + const BITFIELD OPACITY_CUTOFF_VAL_BIT = 0x00000200; + const BITFIELD UV_ANIMATION_SCROLL_VAL_BIT = 0x00000400; + + const BITFIELD EMISSIVE_MAP_BIT = 0x00000800; + const BITFIELD ALBEDO_MAP_BIT = 0x00001000; + const BITFIELD SHADING_SHIFT_MAP_BIT = 0x00002000; + const BITFIELD SHADE_MAP_BIT = 0x00004000; + const BITFIELD NORMAL_MAP_BIT = 0x00008000; + const BITFIELD MATCAP_MAP_BIT = 0x00010000; + const BITFIELD UV_ANIMATION_MASK_MAP_BIT = 0x00020000; + const BITFIELD RIM_MAP_BIT = 0x00040000; + + const BITFIELD MATCAP_VAL_BIT = 0x00080000; + const BITFIELD PARAMETRIC_RIM_VAL_BIT = 0x00100000; + const BITFIELD PARAMETRIC_RIM_POWER_VAL_BIT = 0x00200000; + const BITFIELD PARAMETRIC_RIM_LIFT_VAL_BIT = 0x00400000; + const BITFIELD RIM_LIGHTING_MIX_VAL_BIT = 0x00800000; +<@endif@> <@endif@> diff --git a/libraries/graphics/src/graphics/MaterialTextures.slh b/libraries/graphics/src/graphics/MaterialTextures.slh index cb83f7d9cf..083a1146be 100644 --- a/libraries/graphics/src/graphics/MaterialTextures.slh +++ b/libraries/graphics/src/graphics/MaterialTextures.slh @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 2/22/16 // Copyright 2016 High Fidelity, Inc. +// Copyright 2024 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 @@ -14,10 +15,70 @@ <@include graphics/ShaderConstants.h@> <@include graphics/Material.slh@> -<@func declareMaterialTextures(withAlbedo, withRoughness, withNormal, withMetallic, withEmissive, withOcclusion, withScattering)@> - #define TAA_TEXTURE_LOD_BIAS -1.0 +<@func evalMaterialNormalLOD(fragPosES, fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@> +{ + vec3 normalizedNormal = normalize(<$interpolatedNormal$>.xyz); + vec3 normalizedTangent = normalize(<$interpolatedTangent$>.xyz); + vec3 normalizedBitangent = cross(normalizedNormal, normalizedTangent); + // attenuate the normal map divergence from the mesh normal based on distance + // The attenuation range [30,100] meters from the eye is arbitrary for now + vec3 localNormal = mix(<$fetchedNormal$>, vec3(0.0, 1.0, 0.0), smoothstep(30.0, 100.0, (-<$fragPosES$>).z)); + <$normal$> = vec3(normalizedBitangent * localNormal.x + normalizedNormal * localNormal.y + normalizedTangent * localNormal.z); +} +<@endfunc@> + +<@func evalMaterialAlbedo(fetchedAlbedo, materialAlbedo, matKey, albedo)@> +{ + <$albedo$>.xyz = mix(vec3(1.0), <$materialAlbedo$>, float((<$matKey$> & ALBEDO_VAL_BIT) != 0)); + <$albedo$>.xyz *= mix(vec3(1.0), <$fetchedAlbedo$>.xyz, float((<$matKey$> & ALBEDO_MAP_BIT) != 0)); +} +<@endfunc@> + +<@func evalMaterialOpacityMask(fetchedOpacity, materialOpacityCutoff, materialOpacity, matKey, opacity)@> +{ + // This path only valid for opaque or texel opaque material + <$opacity$> = mix(<$materialOpacity$>, + step(<$materialOpacityCutoff$>, <$fetchedOpacity$>), + float((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0)); +} +<@endfunc@> + +<@func evalMaterialOpacity(fetchedOpacity, materialOpacityCutoff, materialOpacity, matKey, opacity)@> +{ + // This path only valid for transparent material + <$opacity$> = mix(<$fetchedOpacity$>, + step(<$materialOpacityCutoff$>, <$fetchedOpacity$>), + float((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0)) + * <$materialOpacity$>; +} +<@endfunc@> + +<@func evalMaterialEmissive(fetchedEmissive, materialEmissive, matKey, emissive)@> +{ + <$emissive$> = mix(<$materialEmissive$>, <$fetchedEmissive$>, float((<$matKey$> & EMISSIVE_MAP_BIT) != 0)); +} +<@endfunc@> + +<@func discardTransparent(opacity)@> +{ + if (<$opacity$> < 1.0) { + discard; + } +} +<@endfunc@> +<@func discardInvisible(opacity)@> +{ + if (<$opacity$> <= 0.0) { + discard; + } +} +<@endfunc@> + +<@if not HIFI_USE_MTOON@> +<@func declareMaterialTextures(withAlbedo, withRoughness, withNormal, withMetallic, withEmissive, withOcclusion, withScattering)@> + <@include gpu/TextureTable.slh@> #ifdef GPU_TEXTURE_TABLE_BINDLESS @@ -41,14 +102,6 @@ vec4 fetchAlbedoMap(vec2 uv) { } <@endif@> -<@if withRoughness@> -#define roughnessMap 4 -float fetchRoughnessMap(vec2 uv) { - // Should take into account TAA_TEXTURE_LOD_BIAS? - return tableTexValue(matTex, roughnessMap, uv).r; -} -<@endif@> - <@if withNormal@> #define normalMap 1 vec3 fetchNormalMap(vec2 uv) { @@ -73,6 +126,14 @@ vec3 fetchEmissiveMap(vec2 uv) { } <@endif@> +<@if withRoughness@> +#define roughnessMap 4 +float fetchRoughnessMap(vec2 uv) { + // Should take into account TAA_TEXTURE_LOD_BIAS? + return tableTexValue(matTex, roughnessMap, uv).r; +} +<@endif@> + <@if withOcclusion@> #define occlusionMap 5 float fetchOcclusionMap(vec2 uv) { @@ -98,13 +159,6 @@ vec4 fetchAlbedoMap(vec2 uv) { } <@endif@> -<@if withRoughness@> -LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_ROUGHNESS) uniform sampler2D roughnessMap; -float fetchRoughnessMap(vec2 uv) { - return (texture(roughnessMap, uv, TAA_TEXTURE_LOD_BIAS).r); -} -<@endif@> - <@if withNormal@> LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_NORMAL) uniform sampler2D normalMap; vec3 fetchNormalMap(vec2 uv) { @@ -129,6 +183,13 @@ vec3 fetchEmissiveMap(vec2 uv) { } <@endif@> +<@if withRoughness@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_ROUGHNESS) uniform sampler2D roughnessMap; +float fetchRoughnessMap(vec2 uv) { + return (texture(roughnessMap, uv, TAA_TEXTURE_LOD_BIAS).r); +} +<@endif@> + <@if withOcclusion@> LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_OCCLUSION) uniform sampler2D occlusionMap; float fetchOcclusionMap(vec2 uv) { @@ -183,7 +244,6 @@ float fetchScatteringMap(vec2 uv) { <@endfunc@> - <@func declareMaterialLightmap()@> <$declareMaterialTexMapArrayBuffer()$> @@ -195,59 +255,6 @@ vec3 fetchLightMap(vec2 uv) { } <@endfunc@> -<@func evalMaterialNormalLOD(fragPosES, fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@> -{ - vec3 normalizedNormal = normalize(<$interpolatedNormal$>.xyz); - vec3 normalizedTangent = normalize(<$interpolatedTangent$>.xyz); - vec3 normalizedBitangent = cross(normalizedNormal, normalizedTangent); - // attenuate the normal map divergence from the mesh normal based on distance - // The attenuation range [30,100] meters from the eye is arbitrary for now - vec3 localNormal = mix(<$fetchedNormal$>, vec3(0.0, 1.0, 0.0), smoothstep(30.0, 100.0, (-<$fragPosES$>).z)); - <$normal$> = vec3(normalizedBitangent * localNormal.x + normalizedNormal * localNormal.y + normalizedTangent * localNormal.z); -} -<@endfunc@> - -<@func evalMaterialAlbedo(fetchedAlbedo, materialAlbedo, matKey, albedo)@> -{ - <$albedo$>.xyz = mix(vec3(1.0), <$materialAlbedo$>, float((<$matKey$> & ALBEDO_VAL_BIT) != 0)); - <$albedo$>.xyz *= mix(vec3(1.0), <$fetchedAlbedo$>.xyz, float((<$matKey$> & ALBEDO_MAP_BIT) != 0)); -} -<@endfunc@> - -<@func evalMaterialOpacityMask(fetchedOpacity, materialOpacityCutoff, materialOpacity, matKey, opacity)@> -{ - // This path only valid for opaque or texel opaque material - <$opacity$> = mix(<$materialOpacity$>, - step(<$materialOpacityCutoff$>, <$fetchedOpacity$>), - float((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0)); -} -<@endfunc@> - -<@func evalMaterialOpacity(fetchedOpacity, materialOpacityCutoff, materialOpacity, matKey, opacity)@> -{ - // This path only valid for transparent material - <$opacity$> = mix(<$fetchedOpacity$>, - step(<$materialOpacityCutoff$>, <$fetchedOpacity$>), - float((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0)) - * <$materialOpacity$>; -} -<@endfunc@> - -<@func discardTransparent(opacity)@> -{ - if (<$opacity$> < 1.0) { - discard; - } -} -<@endfunc@> -<@func discardInvisible(opacity)@> -{ - if (<$opacity$> <= 0.0) { - discard; - } -} -<@endfunc@> - <@func evalMaterialRoughness(fetchedRoughness, materialRoughness, matKey, roughness)@> { <$roughness$> = mix(<$materialRoughness$>, <$fetchedRoughness$>, float((<$matKey$> & ROUGHNESS_MAP_BIT) != 0)); @@ -260,12 +267,6 @@ vec3 fetchLightMap(vec2 uv) { } <@endfunc@> -<@func evalMaterialEmissive(fetchedEmissive, materialEmissive, matKey, emissive)@> -{ - <$emissive$> = mix(<$materialEmissive$>, <$fetchedEmissive$>, float((<$matKey$> & EMISSIVE_MAP_BIT) != 0)); -} -<@endfunc@> - <@func evalMaterialOcclusion(fetchedOcclusion, matKey, occlusion)@> { <$occlusion$> = <$fetchedOcclusion$>; @@ -277,5 +278,214 @@ vec3 fetchLightMap(vec2 uv) { <$scattering$> = mix(<$materialScattering$>, <$fetchedScattering$>, float((<$matKey$> & SCATTERING_MAP_BIT) != 0)); } <@endfunc@> +<@else@> +<@func declareMToonMaterialTextures(withAlbedo, withNormal, withShade, withEmissive, withShadingShift, withMatcap, withRim, withUVAnimationMask)@> -<@endif@> \ No newline at end of file +<@include gpu/TextureTable.slh@> + +#ifdef GPU_TEXTURE_TABLE_BINDLESS + +TextureTable(0, matTex); + + +<@if withAlbedo@> +#define albedoMap 0 +vec4 fetchAlbedoMap(vec2 uv) { + return tableTexValue(matTex, albedoMap, uv); +} +<@endif@> + +<@if withNormal@> +#define normalMap 1 +vec3 fetchNormalMap(vec2 uv) { + return tableTexValue(matTex, normalMap, uv).xyz; +} +<@endif@> + +<@if withShade@> +#define shadeMap 2 +vec3 fetchShadeMap(vec2 uv) { + return tableTexValue(matTex, shadeMap, uv).rgb; +} +<@endif@> + +<@if withEmissive@> +#define emissiveMap 3 +vec3 fetchEmissiveMap(vec2 uv) { + return tableTexValue(matTex, emissiveMap, uv).rgb; +} +<@endif@> + +<@if withShadingShift@> +#define shadingShiftMap 4 +float fetchShadingShiftMap(vec2 uv) { + return tableTexValue(matTex, shadingShiftMap, uv).r; +} +<@endif@> + +<@if withMatcap@> +#define matcapMap 5 +vec3 fetchMatcapMap(vec2 uv) { + return tableTexValue(matTex, matcapMap, uv).rgb; +} +<@endif@> + +<@if withRim@> +#define rimMap 6 +vec3 fetchRimMap(vec2 uv) { + return tableTexValue(matTex, rimMap, uv).rgb; +} +<@endif@> + +<@if withUVAnimationMask@> +#define uvAnimationMaskMap 7 +float fetchUVAnimationMaskMap(vec2 uv) { + return tableTexValue(matTex, uvAnimationMaskMap, uv).r; +} +<@endif@> + +#else + +<@if withAlbedo@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_ALBEDO) uniform sampler2D albedoMap; +vec4 fetchAlbedoMap(vec2 uv) { + return texture(albedoMap, uv, TAA_TEXTURE_LOD_BIAS); +} +<@endif@> + +<@if withNormal@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_NORMAL) uniform sampler2D normalMap; +vec3 fetchNormalMap(vec2 uv) { + // unpack normal, swizzle to get into hifi tangent space with Y axis pointing out + vec2 t = 2.0 * (texture(normalMap, uv, TAA_TEXTURE_LOD_BIAS).rg - vec2(0.5, 0.5)); + vec2 t2 = t*t; + return vec3(t.x, sqrt(max(0.0, 1.0 - t2.x - t2.y)), t.y); +} +<@endif@> + +<@if withShade@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_SHADE) uniform sampler2D shadeMap; +vec3 fetchShadeMap(vec2 uv) { + return texture(shadeMap, uv, TAA_TEXTURE_LOD_BIAS).rgb; +} +<@endif@> + +<@if withEmissive@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_EMISSIVE_LIGHTMAP) uniform sampler2D emissiveMap; +vec3 fetchEmissiveMap(vec2 uv) { + return texture(emissiveMap, uv, TAA_TEXTURE_LOD_BIAS).rgb; +} +<@endif@> + +<@if withShadingShift@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_SHADING_SHIFT) uniform sampler2D shadingShiftMap; +float fetchShadingShiftMap(vec2 uv) { + return texture(shadingShiftMap, uv, TAA_TEXTURE_LOD_BIAS).r; +} +<@endif@> + +<@if withMatcap@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_MATCAP) uniform sampler2D matcapMap; +vec3 fetchMatcapMap(vec2 uv) { + return texture(matcapMap, uv, TAA_TEXTURE_LOD_BIAS).rgb; +} +<@endif@> + +<@if withRim@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_RIM) uniform sampler2D rimMap; +vec3 fetchRimMap(vec2 uv) { + return texture(rimMap, uv, TAA_TEXTURE_LOD_BIAS).rgb; +} +<@endif@> + +<@if withUVAnimationMask@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_UV_ANIMATION_MASK) uniform sampler2D uvAnimationMaskMap; +float fetchUVAnimationMaskMap(vec2 uv) { + return texture(uvAnimationMaskMap, uv, TAA_TEXTURE_LOD_BIAS).r; +} +<@endif@> + +#endif + +<@endfunc@> + +<@func fetchMToonMaterialTexturesCoord0(matKey, texcoord0, albedo, normal, shade, emissive, shadingShift, rim, uvScrollSpeed, time)@> + if (getTexMapArray()._materialParams.y != 1.0 && clamp(<$texcoord0$>, vec2(0.0), vec2(1.0)) != <$texcoord0$>) { + discard; + } + + vec2 texCoord = <$texcoord0$>; + +<@if uvScrollSpeed and time@> + if ((<$matKey$> & UV_ANIMATION_SCROLL_VAL_BIT) != 0) { + <$uvScrollSpeed$> *= mix(1.0, fetchUVAnimationMaskMap(texCoord), float((<$matKey$> & UV_ANIMATION_MASK_MAP_BIT) != 0)); + <$uvScrollSpeed$> *= time; + float cosTime = cos(<$uvScrollSpeed$>.z); + float sinTime = sin(<$uvScrollSpeed$>.z); + texCoord = (mat3(cosTime, sinTime, 0, -sinTime, cosTime, 0, 0, 0, 1) * vec3(texCoord - vec2(0.5), 1.0)).xy + vec2(0.5) + <$uvScrollSpeed$>.xy; + } +<@endif@> + +<@if albedo@> + vec4 <$albedo$> = mix(vec4(1.0), fetchAlbedoMap(texCoord), float((<$matKey$> & (ALBEDO_MAP_BIT | OPACITY_MASK_MAP_BIT | OPACITY_TRANSLUCENT_MAP_BIT)) != 0)); +<@endif@> +<@if normal@> + vec3 <$normal$> = mix(vec3(0.0, 1.0, 0.0), fetchNormalMap(texCoord), float((<$matKey$> & NORMAL_MAP_BIT) != 0)); +<@endif@> +<@if shade@> + vec3 <$shade$> = float((<$matKey$> & SHADE_MAP_BIT) != 0) * fetchShadeMap(texCoord); +<@endif@> +<@if emissive@> + vec3 <$emissive$> = float((<$matKey$> & EMISSIVE_MAP_BIT) != 0) * fetchEmissiveMap(texCoord); +<@endif@> +<@if shadingShift@> + float <$shadingShift$> = float((<$matKey$> & SHADING_SHIFT_MAP_BIT) != 0) * fetchShadingShiftMap(texCoord); +<@endif@> +<@if rim@> + vec3 <$rim$> = mix(vec3(1.0), fetchRimMap(texCoord), float((<$matKey$> & RIM_MAP_BIT) != 0)); +<@endif@> +<@endfunc@> + +<@func evalMaterialShade(fetchedShade, materialShade, matKey, shade)@> +{ + <$shade$> = mix(vec3(1.0), <$materialShade$>, float((<$matKey$> & SHADE_VAL_BIT) != 0)); + <$shade$> *= mix(vec3(1.0), <$fetchedShade$>.rgb, float((<$matKey$> & SHADE_MAP_BIT) != 0)); +} +<@endfunc@> + +<@func evalMaterialShadingShift(fetchedShadingShift, materialShadingShift, matKey, shadingShift)@> +{ + <$shadingShift$> = mix(0.0, <$materialShadingShift$>, float((<$matKey$> & SHADING_SHIFT_VAL_BIT) != 0)); + <$shadingShift$> += mix(0.0, <$fetchedShadingShift$>.r, float((<$matKey$> & SHADING_SHIFT_MAP_BIT) != 0)); +} +<@endfunc@> + +<@func evalMaterialMatcap(texcoord0, materialMatcap, matKey, matcap)@> +{ + if ((<$matKey$> & (MATCAP_VAL_BIT | MATCAP_MAP_BIT)) == 0) { + <$matcap$> = vec3(0.0); + } else { + <$matcap$> = mix(vec3(1.0), <$materialMatcap$>, float((<$matKey$> & MATCAP_VAL_BIT) != 0)); + <$matcap$> *= mix(vec3(1.0), fetchMatcapMap(<$texcoord0$>), float((<$matKey$> & MATCAP_MAP_BIT) != 0)); + } +} +<@endfunc@> + +<@func evalMaterialUVScrollSpeed(fetchedUVScrollMask, materialUVScrollMask, matKey, uvScrollSpeed)@> +{ + <$uvScrollSpeed$> = mix(vec3(1.0), <$materialUVScrollMask$>, float((<$matKey$> & UV_ANIMATION_MASK_MAP_BIT) != 0)); + <$uvScrollSpeed$> *= mix(1.0, <$fetchedUVScrollMask$>.r, float((<$matKey$> & UV_ANIMATION_MASK_MAP_BIT) != 0)); +} +<@endfunc@> +<@endif@> + +<@endif@> diff --git a/libraries/graphics/src/graphics/ShaderConstants.h b/libraries/graphics/src/graphics/ShaderConstants.h index 8fd0df31f0..75eb4d00cc 100644 --- a/libraries/graphics/src/graphics/ShaderConstants.h +++ b/libraries/graphics/src/graphics/ShaderConstants.h @@ -1,6 +1,7 @@ // & textureList) const { if (!lightmapTexture.isNull()) { textureList.insert(lightmapTexture.name); } + + if (isMToonMaterial) { + if (!shadeTexture.isNull()) { + textureList.insert(shadeTexture.name); + } + if (!shadingShiftTexture.isNull()) { + textureList.insert(shadingShiftTexture.name); + } + if (!matcapTexture.isNull()) { + textureList.insert(matcapTexture.name); + } + if (!rimTexture.isNull()) { + textureList.insert(rimTexture.name); + } + if (!uvAnimationTexture.isNull()) { + textureList.insert(uvAnimationTexture.name); + } + } } void HFMMaterial::setMaxNumPixelsPerTexture(int maxNumPixels) { @@ -61,6 +79,12 @@ void HFMMaterial::setMaxNumPixelsPerTexture(int maxNumPixels) { occlusionTexture.maxNumPixels = maxNumPixels; scatteringTexture.maxNumPixels = maxNumPixels; lightmapTexture.maxNumPixels = maxNumPixels; + + shadeTexture.maxNumPixels = maxNumPixels; + shadingShiftTexture.maxNumPixels = maxNumPixels; + matcapTexture.maxNumPixels = maxNumPixels; + rimTexture.maxNumPixels = maxNumPixels; + uvAnimationTexture.maxNumPixels = maxNumPixels; } bool HFMMaterial::needTangentSpace() const { @@ -312,6 +336,13 @@ void HFMModel::debugDump() { qCDebug(modelformat) << " useMetallicMap =" << mat.useMetallicMap; qCDebug(modelformat) << " useEmissiveMap =" << mat.useEmissiveMap; qCDebug(modelformat) << " useOcclusionMap =" << mat.useOcclusionMap; + + qCDebug(modelformat) << " isMToonMaterial =" << mat.isMToonMaterial; + qCDebug(modelformat) << " shadeTexture =" << mat.shadeTexture.filename; + qCDebug(modelformat) << " shadingShiftTexture =" << mat.shadingShiftTexture.filename; + qCDebug(modelformat) << " matcapTexture =" << mat.matcapTexture.filename; + qCDebug(modelformat) << " rimTexture =" << mat.rimTexture.filename; + qCDebug(modelformat) << " uvAnimationTexture =" << mat.uvAnimationTexture.filename; } qCDebug(modelformat) << "---------------- Joints ----------------"; diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h index d082f30dc5..6932572b1d 100644 --- a/libraries/hfm/src/hfm/HFM.h +++ b/libraries/hfm/src/hfm/HFM.h @@ -224,6 +224,14 @@ public: bool useEmissiveMap { false }; bool useOcclusionMap { false }; + + bool isMToonMaterial { false }; + Texture shadeTexture; + Texture shadingShiftTexture; + Texture matcapTexture; + Texture rimTexture; + Texture uvAnimationTexture; + bool needTangentSpace() const; }; diff --git a/libraries/image/src/image/OpenEXRReader.cpp b/libraries/image/src/image/OpenEXRReader.cpp index 66e304e3fa..d1af1c9621 100644 --- a/libraries/image/src/image/OpenEXRReader.cpp +++ b/libraries/image/src/image/OpenEXRReader.cpp @@ -23,6 +23,7 @@ #include #include #include +#include class QIODeviceImfStream : public Imf::IStream { public: @@ -39,11 +40,11 @@ public: return true; } - Imf::Int64 tellg() override { + uint64_t tellg() override { return _device.pos(); } - void seekg(Imf::Int64 pos) override { + void seekg(uint64_t pos) override { _device.seek(pos); } @@ -76,7 +77,7 @@ image::Image image::readOpenEXR(QIODevice& content, const std::string& filename) Image image{ width, height, Image::Format_PACKED_FLOAT }; auto packHDRPixel = getHDRPackingFunction(); - + for (int y = 0; y < height; y++) { const auto srcScanline = pixels[y]; gpu::uint32* dstScanline = (gpu::uint32*) image.editScanLine(y); diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 738c61874f..ddfdeb79d1 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -325,7 +325,11 @@ void GeometryResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const M QHash materialIDAtlas; for (const HFMMaterial& material : _hfmModel->materials) { materialIDAtlas[material.materialID] = _materials.size(); - _materials.push_back(std::make_shared(material, _textureBaseURL)); + if (!material.isMToonMaterial) { + _materials.push_back(std::make_shared(material, _textureBaseURL)); + } else { + _materials.push_back(std::make_shared(material, _textureBaseURL)); + } } std::shared_ptr meshes = std::make_shared(); @@ -355,8 +359,16 @@ void GeometryResource::deleter() { void GeometryResource::setTextures() { if (_hfmModel) { - for (const HFMMaterial& material : _hfmModel->materials) { - _materials.push_back(std::make_shared(material, _textureBaseURL)); + if (DependencyManager::get()) { + for (const HFMMaterial& material : _hfmModel->materials) { + if (!material.isMToonMaterial) { + _materials.push_back(std::make_shared(material, _textureBaseURL)); + } else { + _materials.push_back(std::make_shared(material, _textureBaseURL)); + } + } + } else { + qDebug() << "GeometryResource::setTextures: TextureCache dependency not available, skipping textures"; } } } @@ -432,7 +444,11 @@ Geometry::Geometry(const Geometry& geometry) { _materials.reserve(geometry._materials.size()); for (const auto& material : geometry._materials) { - _materials.push_back(std::make_shared(*material)); + if (!material->isMToon()) { + _materials.push_back(std::make_shared(*material)); + } else if (auto mToonMaterial = std::static_pointer_cast(material)) { + _materials.push_back(std::make_shared(*mToonMaterial)); + } } _animGraphOverrideUrl = geometry._animGraphOverrideUrl; @@ -448,9 +464,13 @@ void Geometry::setTextures(const QVariantMap& textureMap) { // FIXME: The Model currently caches the materials (waste of space!) // so they must be copied in the Geometry copy-ctor - // if (material->isOriginal()) { + //if (material->isOriginal()) { // // Copy the material to avoid mutating the cached version - // material = std::make_shared(*material); + // if (!material->isMToon()) { + // material = std::make_shared(*material); + // } else { + // material = std::make_shared(*material); + // } //} material->setTextures(textureMap); diff --git a/libraries/model-serializers/CMakeLists.txt b/libraries/model-serializers/CMakeLists.txt index 76775896dc..b0b0dd6344 100644 --- a/libraries/model-serializers/CMakeLists.txt +++ b/libraries/model-serializers/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME model-serializers) setup_hifi_library() -link_hifi_libraries(shared graphics networking image hfm) +link_hifi_libraries(shared graphics networking image hfm procedural material-networking ktx shaders) include_hifi_library_headers(gpu image) target_draco() diff --git a/libraries/model-serializers/src/GLTFSerializer.cpp b/libraries/model-serializers/src/GLTFSerializer.cpp index 488a59d7cb..1440fb22d2 100644 --- a/libraries/model-serializers/src/GLTFSerializer.cpp +++ b/libraries/model-serializers/src/GLTFSerializer.cpp @@ -10,6 +10,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#define CGLTF_IMPLEMENTATION + #include "GLTFSerializer.h" #include @@ -28,15 +30,32 @@ #include #include +#include + +#include + #include #include #include #include #include #include +#include #include "FBXSerializer.h" +float atof_locale_independent(char* str) { + //TODO: Once we have C++17 we can use std::from_chars + std::istringstream streamToParse(str); + streamToParse.imbue(std::locale("C")); + float value; + if (!(streamToParse >> value)) { + qDebug(modelformat) << "cgltf: Cannot parse float from string: " << str; + return 0.0f; + } + return value; +} + #define GLTF_GET_INDICIES(accCount) int index1 = (indices[n + 0] * accCount); int index2 = (indices[n + 1] * accCount); int index3 = (indices[n + 2] * accCount); #define GLTF_APPEND_ARRAY_1(newArray, oldArray) GLTF_GET_INDICIES(1) \ @@ -59,749 +78,30 @@ newArray.append(oldArray[index1]); newArray.append(oldArray[index1 + 1]); newArr newArray.append(oldArray[index2]); newArray.append(oldArray[index2 + 1]); newArray.append(oldArray[index2 + 2]); newArray.append(oldArray[index2 + 3]); \ newArray.append(oldArray[index3]); newArray.append(oldArray[index3 + 1]); newArray.append(oldArray[index3 + 2]); newArray.append(oldArray[index3 + 3]); -bool GLTFSerializer::getStringVal(const QJsonObject& object, const QString& fieldname, - QString& value, QMap& defined) { - bool _defined = (object.contains(fieldname) && object[fieldname].isString()); - if (_defined) { - value = object[fieldname].toString(); - } - defined.insert(fieldname, _defined); - return _defined; -} - -bool GLTFSerializer::getBoolVal(const QJsonObject& object, const QString& fieldname, - bool& value, QMap& defined) { - bool _defined = (object.contains(fieldname) && object[fieldname].isBool()); - if (_defined) { - value = object[fieldname].toBool(); - } - defined.insert(fieldname, _defined); - return _defined; -} - -bool GLTFSerializer::getIntVal(const QJsonObject& object, const QString& fieldname, - int& value, QMap& defined) { - bool _defined = (object.contains(fieldname) && !object[fieldname].isNull()); - if (_defined) { - value = object[fieldname].toInt(); - } - defined.insert(fieldname, _defined); - return _defined; -} - -bool GLTFSerializer::getDoubleVal(const QJsonObject& object, const QString& fieldname, - double& value, QMap& defined) { - bool _defined = (object.contains(fieldname) && object[fieldname].isDouble()); - if (_defined) { - value = object[fieldname].toDouble(); - } - defined.insert(fieldname, _defined); - return _defined; -} -bool GLTFSerializer::getObjectVal(const QJsonObject& object, const QString& fieldname, - QJsonObject& value, QMap& defined) { - bool _defined = (object.contains(fieldname) && object[fieldname].isObject()); - if (_defined) { - value = object[fieldname].toObject(); - } - defined.insert(fieldname, _defined); - return _defined; -} - -bool GLTFSerializer::getIntArrayVal(const QJsonObject& object, const QString& fieldname, - QVector& values, QMap& defined) { - bool _defined = (object.contains(fieldname) && object[fieldname].isArray()); - if (_defined) { - QJsonArray arr = object[fieldname].toArray(); - foreach(const QJsonValue & v, arr) { - if (!v.isNull()) { - values.push_back(v.toInt()); - } - } - } - defined.insert(fieldname, _defined); - return _defined; -} - -bool GLTFSerializer::getDoubleArrayVal(const QJsonObject& object, const QString& fieldname, - QVector& values, QMap& defined) { - bool _defined = (object.contains(fieldname) && object[fieldname].isArray()); - if (_defined) { - QJsonArray arr = object[fieldname].toArray(); - foreach(const QJsonValue & v, arr) { - if (v.isDouble()) { - values.push_back(v.toDouble()); - } - } - } - defined.insert(fieldname, _defined); - return _defined; -} - -bool GLTFSerializer::getObjectArrayVal(const QJsonObject& object, const QString& fieldname, - QJsonArray& objects, QMap& defined) { - bool _defined = (object.contains(fieldname) && object[fieldname].isArray()); - if (_defined) { - objects = object[fieldname].toArray(); - } - defined.insert(fieldname, _defined); - return _defined; -} - -hifi::ByteArray GLTFSerializer::setGLBChunks(const hifi::ByteArray& data) { - int byte = 4; - int jsonStart = data.indexOf("JSON", Qt::CaseSensitive); - int binStart = data.indexOf("BIN", Qt::CaseSensitive); - int jsonLength, binLength; - hifi::ByteArray jsonLengthChunk, binLengthChunk; - - jsonLengthChunk = data.mid(jsonStart - byte, byte); - QDataStream tempJsonLen(jsonLengthChunk); - tempJsonLen.setByteOrder(QDataStream::LittleEndian); - tempJsonLen >> jsonLength; - hifi::ByteArray jsonChunk = data.mid(jsonStart + byte, jsonLength); - - if (binStart != -1) { - binLengthChunk = data.mid(binStart - byte, byte); - - QDataStream tempBinLen(binLengthChunk); - tempBinLen.setByteOrder(QDataStream::LittleEndian); - tempBinLen >> binLength; - - _glbBinary = data.mid(binStart + byte, binLength); - } - return jsonChunk; -} - -int GLTFSerializer::getMeshPrimitiveRenderingMode(const QString& type) -{ - if (type == "POINTS") { - return GLTFMeshPrimitivesRenderingMode::POINTS; - } - if (type == "LINES") { - return GLTFMeshPrimitivesRenderingMode::LINES; - } - if (type == "LINE_LOOP") { - return GLTFMeshPrimitivesRenderingMode::LINE_LOOP; - } - if (type == "LINE_STRIP") { - return GLTFMeshPrimitivesRenderingMode::LINE_STRIP; - } - if (type == "TRIANGLES") { - return GLTFMeshPrimitivesRenderingMode::TRIANGLES; - } - if (type == "TRIANGLE_STRIP") { - return GLTFMeshPrimitivesRenderingMode::TRIANGLE_STRIP; - } - if (type == "TRIANGLE_FAN") { - return GLTFMeshPrimitivesRenderingMode::TRIANGLE_FAN; - } - return GLTFMeshPrimitivesRenderingMode::TRIANGLES; -} - -int GLTFSerializer::getAccessorType(const QString& type) -{ - if (type == "SCALAR") { - return GLTFAccessorType::SCALAR; - } - if (type == "VEC2") { - return GLTFAccessorType::VEC2; - } - if (type == "VEC3") { - return GLTFAccessorType::VEC3; - } - if (type == "VEC4") { - return GLTFAccessorType::VEC4; - } - if (type == "MAT2") { - return GLTFAccessorType::MAT2; - } - if (type == "MAT3") { - return GLTFAccessorType::MAT3; - } - if (type == "MAT4") { - return GLTFAccessorType::MAT4; - } - return GLTFAccessorType::SCALAR; -} - -graphics::MaterialKey::OpacityMapMode GLTFSerializer::getMaterialAlphaMode(const QString& type) { - if (type == "OPAQUE") { - return graphics::MaterialKey::OPACITY_MAP_OPAQUE; - } - if (type == "MASK") { - return graphics::MaterialKey::OPACITY_MAP_MASK; - } - if (type == "BLEND") { - return graphics::MaterialKey::OPACITY_MAP_BLEND; - } - return graphics::MaterialKey::OPACITY_MAP_BLEND; -} - -int GLTFSerializer::getCameraType(const QString& type) -{ - if (type == "orthographic") { - return GLTFCameraTypes::ORTHOGRAPHIC; - } - if (type == "perspective") { - return GLTFCameraTypes::PERSPECTIVE; - } - return GLTFCameraTypes::PERSPECTIVE; -} - -int GLTFSerializer::getImageMimeType(const QString& mime) -{ - if (mime == "image/jpeg") { - return GLTFImageMimetype::JPEG; - } - if (mime == "image/png") { - return GLTFImageMimetype::PNG; - } - return GLTFImageMimetype::JPEG; -} - -int GLTFSerializer::getAnimationSamplerInterpolation(const QString& interpolation) -{ - if (interpolation == "LINEAR") { - return GLTFAnimationSamplerInterpolation::LINEAR; - } - return GLTFAnimationSamplerInterpolation::LINEAR; -} - -bool GLTFSerializer::setAsset(const QJsonObject& object) { - QJsonObject jsAsset; - bool isAssetDefined = getObjectVal(object, "asset", jsAsset, _file.defined); - if (isAssetDefined) { - if (!getStringVal(jsAsset, "version", _file.asset.version, - _file.asset.defined) || _file.asset.version != "2.0") { - return false; - } - getStringVal(jsAsset, "generator", _file.asset.generator, _file.asset.defined); - getStringVal(jsAsset, "copyright", _file.asset.copyright, _file.asset.defined); - } - return isAssetDefined; -} - -GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices GLTFSerializer::createAccessorSparseIndices(const QJsonObject& object) { - GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices accessorSparseIndices; - - getIntVal(object, "bufferView", accessorSparseIndices.bufferView, accessorSparseIndices.defined); - getIntVal(object, "byteOffset", accessorSparseIndices.byteOffset, accessorSparseIndices.defined); - getIntVal(object, "componentType", accessorSparseIndices.componentType, accessorSparseIndices.defined); - - return accessorSparseIndices; -} - -GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues GLTFSerializer::createAccessorSparseValues(const QJsonObject& object) { - GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues accessorSparseValues; - - getIntVal(object, "bufferView", accessorSparseValues.bufferView, accessorSparseValues.defined); - getIntVal(object, "byteOffset", accessorSparseValues.byteOffset, accessorSparseValues.defined); - - return accessorSparseValues; -} - -GLTFAccessor::GLTFAccessorSparse GLTFSerializer::createAccessorSparse(const QJsonObject& object) { - GLTFAccessor::GLTFAccessorSparse accessorSparse; - - getIntVal(object, "count", accessorSparse.count, accessorSparse.defined); - QJsonObject sparseIndicesObject; - if (getObjectVal(object, "indices", sparseIndicesObject, accessorSparse.defined)) { - accessorSparse.indices = createAccessorSparseIndices(sparseIndicesObject); - } - QJsonObject sparseValuesObject; - if (getObjectVal(object, "values", sparseValuesObject, accessorSparse.defined)) { - accessorSparse.values = createAccessorSparseValues(sparseValuesObject); - } - - return accessorSparse; -} - -bool GLTFSerializer::addAccessor(const QJsonObject& object) { - GLTFAccessor accessor; - - getIntVal(object, "bufferView", accessor.bufferView, accessor.defined); - getIntVal(object, "byteOffset", accessor.byteOffset, accessor.defined); - getIntVal(object, "componentType", accessor.componentType, accessor.defined); - getIntVal(object, "count", accessor.count, accessor.defined); - getBoolVal(object, "normalized", accessor.normalized, accessor.defined); - QString type; - if (getStringVal(object, "type", type, accessor.defined)) { - accessor.type = getAccessorType(type); - } - - QJsonObject sparseObject; - if (getObjectVal(object, "sparse", sparseObject, accessor.defined)) { - accessor.sparse = createAccessorSparse(sparseObject); - } - - getDoubleArrayVal(object, "max", accessor.max, accessor.defined); - getDoubleArrayVal(object, "min", accessor.min, accessor.defined); - - _file.accessors.push_back(accessor); - - return true; -} - -bool GLTFSerializer::addAnimation(const QJsonObject& object) { - GLTFAnimation animation; - - QJsonArray channels; - if (getObjectArrayVal(object, "channels", channels, animation.defined)) { - foreach(const QJsonValue & v, channels) { - if (v.isObject()) { - GLTFChannel channel; - getIntVal(v.toObject(), "sampler", channel.sampler, channel.defined); - QJsonObject jsChannel; - if (getObjectVal(v.toObject(), "target", jsChannel, channel.defined)) { - getIntVal(jsChannel, "node", channel.target.node, channel.target.defined); - getIntVal(jsChannel, "path", channel.target.path, channel.target.defined); - } - } - } - } - - QJsonArray samplers; - if (getObjectArrayVal(object, "samplers", samplers, animation.defined)) { - foreach(const QJsonValue & v, samplers) { - if (v.isObject()) { - GLTFAnimationSampler sampler; - getIntVal(v.toObject(), "input", sampler.input, sampler.defined); - getIntVal(v.toObject(), "output", sampler.input, sampler.defined); - QString interpolation; - if (getStringVal(v.toObject(), "interpolation", interpolation, sampler.defined)) { - sampler.interpolation = getAnimationSamplerInterpolation(interpolation); - } - } - } - } - - _file.animations.push_back(animation); - - return true; -} - -bool GLTFSerializer::addBufferView(const QJsonObject& object) { - GLTFBufferView bufferview; - - getIntVal(object, "buffer", bufferview.buffer, bufferview.defined); - getIntVal(object, "byteLength", bufferview.byteLength, bufferview.defined); - getIntVal(object, "byteOffset", bufferview.byteOffset, bufferview.defined); - getIntVal(object, "target", bufferview.target, bufferview.defined); - - _file.bufferviews.push_back(bufferview); - - return true; -} - -bool GLTFSerializer::addBuffer(const QJsonObject& object) { - GLTFBuffer buffer; - - getIntVal(object, "byteLength", buffer.byteLength, buffer.defined); - - if (_url.path().endsWith("glb")) { - if (!_glbBinary.isEmpty()) { - buffer.blob = _glbBinary; - } else { - return false; - } - } - if (getStringVal(object, "uri", buffer.uri, buffer.defined)) { - if (!readBinary(buffer.uri, buffer.blob)) { - return false; - } - } - _file.buffers.push_back(buffer); - - return true; -} - -bool GLTFSerializer::addCamera(const QJsonObject& object) { - GLTFCamera camera; - - QJsonObject jsPerspective; - QJsonObject jsOrthographic; - QString type; - getStringVal(object, "name", camera.name, camera.defined); - if (getObjectVal(object, "perspective", jsPerspective, camera.defined)) { - getDoubleVal(jsPerspective, "aspectRatio", camera.perspective.aspectRatio, camera.perspective.defined); - getDoubleVal(jsPerspective, "yfov", camera.perspective.yfov, camera.perspective.defined); - getDoubleVal(jsPerspective, "zfar", camera.perspective.zfar, camera.perspective.defined); - getDoubleVal(jsPerspective, "znear", camera.perspective.znear, camera.perspective.defined); - camera.type = GLTFCameraTypes::PERSPECTIVE; - } else if (getObjectVal(object, "orthographic", jsOrthographic, camera.defined)) { - getDoubleVal(jsOrthographic, "zfar", camera.orthographic.zfar, camera.orthographic.defined); - getDoubleVal(jsOrthographic, "znear", camera.orthographic.znear, camera.orthographic.defined); - getDoubleVal(jsOrthographic, "xmag", camera.orthographic.xmag, camera.orthographic.defined); - getDoubleVal(jsOrthographic, "ymag", camera.orthographic.ymag, camera.orthographic.defined); - camera.type = GLTFCameraTypes::ORTHOGRAPHIC; - } else if (getStringVal(object, "type", type, camera.defined)) { - camera.type = getCameraType(type); - } - - _file.cameras.push_back(camera); - - return true; -} - -bool GLTFSerializer::addImage(const QJsonObject& object) { - GLTFImage image; - - QString mime; - getStringVal(object, "uri", image.uri, image.defined); - if (image.uri.contains("data:image/png;base64,")) { - image.mimeType = getImageMimeType("image/png"); - } else if (image.uri.contains("data:image/jpeg;base64,")) { - image.mimeType = getImageMimeType("image/jpeg"); - } - if (getStringVal(object, "mimeType", mime, image.defined)) { - image.mimeType = getImageMimeType(mime); - } - getIntVal(object, "bufferView", image.bufferView, image.defined); - - _file.images.push_back(image); - - return true; -} - -bool GLTFSerializer::getIndexFromObject(const QJsonObject& object, const QString& field, - int& outidx, QMap& defined) { - QJsonObject subobject; - if (getObjectVal(object, field, subobject, defined)) { - QMap tmpdefined = QMap(); - return getIntVal(subobject, "index", outidx, tmpdefined); - } - return false; -} - -bool GLTFSerializer::addMaterial(const QJsonObject& object) { - GLTFMaterial material; - - getStringVal(object, "name", material.name, material.defined); - getDoubleArrayVal(object, "emissiveFactor", material.emissiveFactor, material.defined); - getIndexFromObject(object, "emissiveTexture", material.emissiveTexture, material.defined); - getIndexFromObject(object, "normalTexture", material.normalTexture, material.defined); - getIndexFromObject(object, "occlusionTexture", material.occlusionTexture, material.defined); - getBoolVal(object, "doubleSided", material.doubleSided, material.defined); - QString alphaMode; - if (getStringVal(object, "alphaMode", alphaMode, material.defined)) { - material.alphaMode = getMaterialAlphaMode(alphaMode); - } - getDoubleVal(object, "alphaCutoff", material.alphaCutoff, material.defined); - QJsonObject jsMetallicRoughness; - if (getObjectVal(object, "pbrMetallicRoughness", jsMetallicRoughness, material.defined)) { - getDoubleArrayVal(jsMetallicRoughness, "baseColorFactor", - material.pbrMetallicRoughness.baseColorFactor, - material.pbrMetallicRoughness.defined); - getIndexFromObject(jsMetallicRoughness, "baseColorTexture", - material.pbrMetallicRoughness.baseColorTexture, - material.pbrMetallicRoughness.defined); - // Undefined metallicFactor used with pbrMetallicRoughness means metallicFactor == 1.0 - if (!getDoubleVal(jsMetallicRoughness, "metallicFactor", - material.pbrMetallicRoughness.metallicFactor, - material.pbrMetallicRoughness.defined)) { - material.pbrMetallicRoughness.metallicFactor = 1.0; - material.pbrMetallicRoughness.defined["metallicFactor"] = true; - } - getDoubleVal(jsMetallicRoughness, "roughnessFactor", - material.pbrMetallicRoughness.roughnessFactor, - material.pbrMetallicRoughness.defined); - getIndexFromObject(jsMetallicRoughness, "metallicRoughnessTexture", - material.pbrMetallicRoughness.metallicRoughnessTexture, - material.pbrMetallicRoughness.defined); - } - _file.materials.push_back(material); - return true; -} - -bool GLTFSerializer::addMesh(const QJsonObject& object) { - GLTFMesh mesh; - - getStringVal(object, "name", mesh.name, mesh.defined); - getDoubleArrayVal(object, "weights", mesh.weights, mesh.defined); - QJsonArray jsPrimitives; - object.keys(); - if (getObjectArrayVal(object, "primitives", jsPrimitives, mesh.defined)) { - foreach(const QJsonValue & prim, jsPrimitives) { - if (prim.isObject()) { - GLTFMeshPrimitive primitive; - QJsonObject jsPrimitive = prim.toObject(); - getIntVal(jsPrimitive, "mode", primitive.mode, primitive.defined); - getIntVal(jsPrimitive, "indices", primitive.indices, primitive.defined); - getIntVal(jsPrimitive, "material", primitive.material, primitive.defined); - - QJsonObject jsAttributes; - if (getObjectVal(jsPrimitive, "attributes", jsAttributes, primitive.defined)) { - QStringList attrKeys = jsAttributes.keys(); - foreach(const QString & attrKey, attrKeys) { - int attrVal; - getIntVal(jsAttributes, attrKey, attrVal, primitive.attributes.defined); - primitive.attributes.values.insert(attrKey, attrVal); - } - } - - QJsonArray jsTargets; - if (getObjectArrayVal(jsPrimitive, "targets", jsTargets, primitive.defined)) { - foreach(const QJsonValue & tar, jsTargets) { - if (tar.isObject()) { - QJsonObject jsTarget = tar.toObject(); - QStringList tarKeys = jsTarget.keys(); - GLTFMeshPrimitiveAttr target; - foreach(const QString & tarKey, tarKeys) { - int tarVal; - getIntVal(jsTarget, tarKey, tarVal, target.defined); - target.values.insert(tarKey, tarVal); - } - primitive.targets.push_back(target); - } - } - } - mesh.primitives.push_back(primitive); - } - } - } - - QJsonObject jsExtras; - GLTFMeshExtra extras; - if (getObjectVal(object, "extras", jsExtras, mesh.defined)) { - QJsonArray jsTargetNames; - if (getObjectArrayVal(jsExtras, "targetNames", jsTargetNames, extras.defined)) { - foreach (const QJsonValue& tarName, jsTargetNames) { - extras.targetNames.push_back(tarName.toString()); - } - } - mesh.extras = extras; - } - - _file.meshes.push_back(mesh); - - return true; -} - -bool GLTFSerializer::addNode(const QJsonObject& object) { - GLTFNode node; - - getStringVal(object, "name", node.name, node.defined); - getIntVal(object, "camera", node.camera, node.defined); - getIntVal(object, "mesh", node.mesh, node.defined); - getIntArrayVal(object, "children", node.children, node.defined); - getDoubleArrayVal(object, "translation", node.translation, node.defined); - getDoubleArrayVal(object, "rotation", node.rotation, node.defined); - getDoubleArrayVal(object, "scale", node.scale, node.defined); - getDoubleArrayVal(object, "matrix", node.matrix, node.defined); - getIntVal(object, "skin", node.skin, node.defined); - getStringVal(object, "jointName", node.jointName, node.defined); - getIntArrayVal(object, "skeletons", node.skeletons, node.defined); - - _file.nodes.push_back(node); - - return true; -} - -bool GLTFSerializer::addSampler(const QJsonObject& object) { - GLTFSampler sampler; - - getIntVal(object, "magFilter", sampler.magFilter, sampler.defined); - getIntVal(object, "minFilter", sampler.minFilter, sampler.defined); - getIntVal(object, "wrapS", sampler.wrapS, sampler.defined); - getIntVal(object, "wrapT", sampler.wrapT, sampler.defined); - - _file.samplers.push_back(sampler); - - return true; - -} - -bool GLTFSerializer::addScene(const QJsonObject& object) { - GLTFScene scene; - - getStringVal(object, "name", scene.name, scene.defined); - getIntArrayVal(object, "nodes", scene.nodes, scene.defined); - - _file.scenes.push_back(scene); - return true; -} - -bool GLTFSerializer::addSkin(const QJsonObject& object) { - GLTFSkin skin; - - getIntVal(object, "inverseBindMatrices", skin.inverseBindMatrices, skin.defined); - getIntVal(object, "skeleton", skin.skeleton, skin.defined); - getIntArrayVal(object, "joints", skin.joints, skin.defined); - - _file.skins.push_back(skin); - - return true; -} - -bool GLTFSerializer::addTexture(const QJsonObject& object) { - GLTFTexture texture; - getIntVal(object, "sampler", texture.sampler, texture.defined); - getIntVal(object, "source", texture.source, texture.defined); - - _file.textures.push_back(texture); - - return true; -} - -bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { - PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xffff0000, nullptr); - - hifi::ByteArray jsonChunk = data; - - if (_url.path().endsWith("glb") && data.indexOf("glTF") == 0 && data.contains("JSON")) { - jsonChunk = setGLBChunks(data); - } - - QJsonDocument d = QJsonDocument::fromJson(jsonChunk); - QJsonObject jsFile = d.object(); - - bool success = setAsset(jsFile); - if (success) { - QJsonArray accessors; - if (getObjectArrayVal(jsFile, "accessors", accessors, _file.defined)) { - foreach(const QJsonValue & accVal, accessors) { - if (accVal.isObject()) { - success = success && addAccessor(accVal.toObject()); - } - } - } - - QJsonArray animations; - if (getObjectArrayVal(jsFile, "animations", animations, _file.defined)) { - foreach(const QJsonValue & animVal, accessors) { - if (animVal.isObject()) { - success = success && addAnimation(animVal.toObject()); - } - } - } - - QJsonArray bufferViews; - if (getObjectArrayVal(jsFile, "bufferViews", bufferViews, _file.defined)) { - foreach(const QJsonValue & bufviewVal, bufferViews) { - if (bufviewVal.isObject()) { - success = success && addBufferView(bufviewVal.toObject()); - } - } - } - - QJsonArray buffers; - if (getObjectArrayVal(jsFile, "buffers", buffers, _file.defined)) { - foreach(const QJsonValue & bufVal, buffers) { - if (bufVal.isObject()) { - success = success && addBuffer(bufVal.toObject()); - } - } - } - - QJsonArray cameras; - if (getObjectArrayVal(jsFile, "cameras", cameras, _file.defined)) { - foreach(const QJsonValue & camVal, cameras) { - if (camVal.isObject()) { - success = success && addCamera(camVal.toObject()); - } - } - } - - QJsonArray images; - if (getObjectArrayVal(jsFile, "images", images, _file.defined)) { - foreach(const QJsonValue & imgVal, images) { - if (imgVal.isObject()) { - success = success && addImage(imgVal.toObject()); - } - } - } - - QJsonArray materials; - if (getObjectArrayVal(jsFile, "materials", materials, _file.defined)) { - foreach(const QJsonValue & matVal, materials) { - if (matVal.isObject()) { - success = success && addMaterial(matVal.toObject()); - } - } - } - - QJsonArray meshes; - if (getObjectArrayVal(jsFile, "meshes", meshes, _file.defined)) { - foreach(const QJsonValue & meshVal, meshes) { - if (meshVal.isObject()) { - success = success && addMesh(meshVal.toObject()); - } - } - } - - QJsonArray nodes; - if (getObjectArrayVal(jsFile, "nodes", nodes, _file.defined)) { - foreach(const QJsonValue & nodeVal, nodes) { - if (nodeVal.isObject()) { - success = success && addNode(nodeVal.toObject()); - } - } - } - - QJsonArray samplers; - if (getObjectArrayVal(jsFile, "samplers", samplers, _file.defined)) { - foreach(const QJsonValue & samVal, samplers) { - if (samVal.isObject()) { - success = success && addSampler(samVal.toObject()); - } - } - } - - QJsonArray scenes; - if (getObjectArrayVal(jsFile, "scenes", scenes, _file.defined)) { - foreach(const QJsonValue & sceneVal, scenes) { - if (sceneVal.isObject()) { - success = success && addScene(sceneVal.toObject()); - } - } - } - - QJsonArray skins; - if (getObjectArrayVal(jsFile, "skins", skins, _file.defined)) { - foreach(const QJsonValue & skinVal, skins) { - if (skinVal.isObject()) { - success = success && addSkin(skinVal.toObject()); - } - } - } - - QJsonArray textures; - if (getObjectArrayVal(jsFile, "textures", textures, _file.defined)) { - foreach(const QJsonValue & texVal, textures) { - if (texVal.isObject()) { - success = success && addTexture(texVal.toObject()); - } - } - } - } - return success; -} - -glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) { +glm::mat4 GLTFSerializer::getModelTransform(const cgltf_node& node) { glm::mat4 tmat = glm::mat4(1.0); - if (node.defined["matrix"] && node.matrix.size() == 16) { + if (node.has_matrix) { tmat = glm::mat4(node.matrix[0], node.matrix[1], node.matrix[2], node.matrix[3], node.matrix[4], node.matrix[5], node.matrix[6], node.matrix[7], node.matrix[8], node.matrix[9], node.matrix[10], node.matrix[11], node.matrix[12], node.matrix[13], node.matrix[14], node.matrix[15]); } else { - if (node.defined["scale"] && node.scale.size() == 3) { + if (node.has_scale) { glm::vec3 scale = glm::vec3(node.scale[0], node.scale[1], node.scale[2]); glm::mat4 s = glm::mat4(1.0); s = glm::scale(s, scale); tmat = s * tmat; } - if (node.defined["rotation"] && node.rotation.size() == 4) { + if (node.has_rotation) { //quat(x,y,z,w) to quat(w,x,y,z) glm::quat rotquat = glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]); tmat = glm::mat4_cast(rotquat) * tmat; } - if (node.defined["translation"] && node.translation.size() == 3) { + if (node.has_translation) { glm::vec3 trans = glm::vec3(node.translation[0], node.translation[1], node.translation[2]); glm::mat4 t = glm::mat4(1.0); t = glm::translate(t, trans); @@ -811,85 +111,151 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) { return tmat; } -void GLTFSerializer::getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues) { - for (auto &skin : _file.skins) { - GLTFAccessor& indicesAccessor = _file.accessors[skin.inverseBindMatrices]; +bool GLTFSerializer::getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues) { + for (size_t i = 0; i < _data->skins_count; i++) { + auto &skin = _data->skins[i]; + + if (skin.inverse_bind_matrices == NULL) { + return false; + } + + cgltf_accessor &matricesAccessor = *skin.inverse_bind_matrices; QVector matrices; - addArrayFromAccessor(indicesAccessor, matrices); + if (matricesAccessor.type != cgltf_type_mat4) { + return false; + } + matrices.resize((int)matricesAccessor.count * 16); + size_t numFloats = cgltf_accessor_unpack_floats(&matricesAccessor, matrices.data(), matricesAccessor.count * 16); + Q_ASSERT(numFloats == matricesAccessor.count * 16); inverseBindMatrixValues.push_back(std::vector(matrices.begin(), matrices.end())); } + return true; } -void GLTFSerializer::generateTargetData(int index, float weight, QVector& returnVector) { - GLTFAccessor& accessor = _file.accessors[index]; +bool GLTFSerializer::generateTargetData(cgltf_accessor *accessor, float weight, QVector& returnVector) { QVector storedValues; - addArrayFromAccessor(accessor, storedValues); + if(accessor == nullptr) { + return false; + } + if (accessor->type != cgltf_type_vec3) { + return false; + } + storedValues.resize((int)accessor->count * 3); + size_t numFloats = cgltf_accessor_unpack_floats(accessor, storedValues.data(), accessor->count * 3); + if (numFloats != accessor->count * 3) { + return false; + } + for (int n = 0; n + 2 < storedValues.size(); n = n + 3) { returnVector.push_back(glm::vec3(weight * storedValues[n], weight * storedValues[n + 1], weight * storedValues[n + 2])); } + return true; +} + +bool findNodeInPointerArray(const cgltf_node *nodePointer, cgltf_node **nodes, size_t arraySize, size_t &index) { + for (size_t i = 0; i < arraySize; i++) { + if (nodes[i] == nodePointer) { + index = i; + return true; + } + } + return false; +} + +template bool findPointerInArray(const T *pointer, const T *array, size_t arraySize, size_t &index) { + for (size_t i = 0; i < arraySize; i++) { + if (&array[i] == pointer) { + index = i; + return true; + } + } + return false; +} + +bool findAttribute(const QString &name, const cgltf_attribute *attributes, size_t numAttributes, size_t &index) { + std::string nameString = name.toStdString(); + for (size_t i = 0; i < numAttributes; i++) { + if (attributes->name == nullptr) { + qDebug(modelformat) << "GLTFSerializer: attribute with a null pointer name string"; + } else { + if (strcmp(nameString.c_str(), attributes->name) == 0) { + index = i; + return true; + } + } + } + return false; } bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mapping, const hifi::URL& url) { hfmModel.originalURL = url.toString(); - int numNodes = _file.nodes.size(); + int numNodes = (int)_data->nodes_count; //Build dependencies QVector parents; QVector sortedNodes; parents.fill(-1, numNodes); sortedNodes.reserve(numNodes); - int nodecount = 0; - foreach(auto &node, _file.nodes) { - foreach(int child, node.children) { - parents[child] = nodecount; + for(int index = 0; index < numNodes; index++) { + auto &node = _data->nodes[index]; + for(size_t childIndexInParent = 0; childIndexInParent < node.children_count; childIndexInParent++) { + cgltf_node *child = node.children[childIndexInParent]; + size_t childIndex = 0; + if (!findPointerInArray(child, _data->nodes, _data->nodes_count, childIndex)) { + qDebug(modelformat) << "findPointerInArray failed for model: " << _url; + hfmModel.loadErrorCount++; + return false; + } + parents[(int)childIndex] = index; } - sortedNodes.push_back(nodecount); - ++nodecount; + sortedNodes.push_back(index); } - // Build transforms - nodecount = 0; - foreach(auto &node, _file.nodes) { + typedef QVector NodeTransforms; + QVector transforms; + transforms.resize(numNodes); + for (int index = 0; index < numNodes; index++) { // collect node transform - _file.nodes[nodecount].transforms.push_back(getModelTransform(node)); - int parentIndex = parents[nodecount]; + auto &node = _data->nodes[index]; + transforms[index].push_back(getModelTransform(node)); + int parentIndex = parents[index]; while (parentIndex != -1) { - const auto& parentNode = _file.nodes[parentIndex]; + const auto& parentNode = _data->nodes[parentIndex]; // collect transforms for a node's parents, grandparents, etc. - _file.nodes[nodecount].transforms.push_back(getModelTransform(parentNode)); + transforms[index].push_back(getModelTransform(parentNode)); parentIndex = parents[parentIndex]; } - ++nodecount; } - // since parent indices must exist in the sorted list before any of their children, sortedNodes might not be initialized in the correct order // therefore we need to re-initialize the order in which nodes will be parsed QVector hasBeenSorted; hasBeenSorted.fill(false, numNodes); - int i = 0; // initial index - while (i < numNodes) { - int currentNode = sortedNodes[i]; - int parentIndex = parents[currentNode]; - if (parentIndex == -1 || hasBeenSorted[parentIndex]) { - hasBeenSorted[currentNode] = true; - ++i; - } else { - int j = i + 1; // index of node to be sorted - while (j < numNodes) { - int nextNode = sortedNodes[j]; - parentIndex = parents[nextNode]; - if (parentIndex == -1 || hasBeenSorted[parentIndex]) { - // swap with currentNode - hasBeenSorted[nextNode] = true; - sortedNodes[i] = nextNode; - sortedNodes[j] = currentNode; - ++i; - currentNode = sortedNodes[i]; + { + int i = 0; // initial index + while (i < numNodes) { + int currentNode = sortedNodes[i]; + int parentIndex = parents[currentNode]; + if (parentIndex == -1 || hasBeenSorted[parentIndex]) { + hasBeenSorted[currentNode] = true; + ++i; + } else { + int j = i + 1; // index of node to be sorted + while (j < numNodes) { + int nextNode = sortedNodes[j]; + parentIndex = parents[nextNode]; + if (parentIndex == -1 || hasBeenSorted[parentIndex]) { + // swap with currentNode + hasBeenSorted[nextNode] = true; + sortedNodes[i] = nextNode; + sortedNodes[j] = currentNode; + ++i; + currentNode = sortedNodes[i]; + } + ++j; } - ++j; } } } @@ -911,13 +277,13 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& globalTransforms.resize(numNodes); for (int nodeIndex : sortedNodes) { - auto& node = _file.nodes[nodeIndex]; + auto& node = _data->nodes[nodeIndex]; joint.parentIndex = parents[nodeIndex]; if (joint.parentIndex != -1) { joint.parentIndex = originalToNewNodeIndexMap[joint.parentIndex]; } - joint.transform = node.transforms.first(); + joint.transform = transforms[nodeIndex].first(); joint.translation = extractTranslation(joint.transform); joint.rotation = glmExtractRotation(joint.transform); glm::vec3 scale = extractScale(joint.transform); @@ -951,24 +317,34 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& jointInverseBindTransforms.resize(numNodes); globalBindTransforms.resize(numNodes); - hfmModel.hasSkeletonJoints = !_file.skins.isEmpty(); + hfmModel.hasSkeletonJoints = _data->skins_count > 0; if (hfmModel.hasSkeletonJoints) { std::vector> inverseBindValues; - getSkinInverseBindMatrices(inverseBindValues); + if (!getSkinInverseBindMatrices(inverseBindValues)) { + qDebug(modelformat) << "GLTFSerializer::getSkinInverseBindMatrices: wrong matrices accessor type for model: " << _url; + hfmModel.loadErrorCount++; + return false; + } for (int jointIndex = 0; jointIndex < numNodes; ++jointIndex) { int nodeIndex = sortedNodes[jointIndex]; auto joint = hfmModel.joints[jointIndex]; - for (int s = 0; s < _file.skins.size(); ++s) { - const auto& skin = _file.skins[s]; - int matrixIndex = skin.joints.indexOf(nodeIndex); - joint.isSkeletonJoint = skin.joints.contains(nodeIndex); + for (size_t s = 0; s < _data->skins_count; ++s) { + const auto& skin = _data->skins[s]; + size_t jointNodeIndex = 0; + joint.isSkeletonJoint = findNodeInPointerArray(&_data->nodes[nodeIndex], skin.joints, skin.joints_count, jointNodeIndex); // build inverse bind matrices if (joint.isSkeletonJoint) { + size_t matrixIndex = jointNodeIndex; std::vector& value = inverseBindValues[s]; - int matrixCount = 16 * matrixIndex; + size_t matrixCount = 16 * matrixIndex; + if (matrixCount + 15 >= value.size()) { + qDebug(modelformat) << "GLTFSerializer::buildGeometry: not enough entries in jointInverseBindTransforms: " << _url; + hfmModel.loadErrorCount++; + return false; + } jointInverseBindTransforms[jointIndex] = glm::mat4(value[matrixCount], value[matrixCount + 1], value[matrixCount + 2], value[matrixCount + 3], value[matrixCount + 4], value[matrixCount + 5], value[matrixCount + 6], value[matrixCount + 7], @@ -992,15 +368,15 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& // Build materials QVector materialIDs; QString unknown = "Default"; - int ukcount = 0; - foreach(auto material, _file.materials) { - if (!material.defined["name"]) { - QString name = unknown + QString::number(++ukcount); - material.name = name; - material.defined.insert("name", true); + for (size_t i = 0; i < _data->materials_count; i++) { + auto &material = _data->materials[i]; + QString mid; + if (material.name != nullptr) { + mid = QString(material.name); + }else{ + mid = QString::number(i); } - QString mid = material.name; materialIDs.push_back(mid); } @@ -1008,19 +384,18 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& QString& matid = materialIDs[i]; hfmModel.materials[matid] = HFMMaterial(); HFMMaterial& hfmMaterial = hfmModel.materials[matid]; - hfmMaterial._material = std::make_shared(); hfmMaterial.name = hfmMaterial.materialID = matid; - setHFMMaterial(hfmMaterial, _file.materials[i]); + setHFMMaterial(hfmMaterial, _data->materials[i]); } // Build meshes - nodecount = 0; + int nodeCount = 0; hfmModel.meshExtents.reset(); for (int nodeIndex : sortedNodes) { - auto& node = _file.nodes[nodeIndex]; + auto& node = _data->nodes[nodeIndex]; - if (node.defined["mesh"]) { + if (node.mesh != nullptr) { hfmModel.meshes.append(HFMMesh()); HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1]; @@ -1028,7 +403,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& if (!hfmModel.hasSkeletonJoints) { HFMCluster cluster; - cluster.jointIndex = nodecount; + cluster.jointIndex = nodeCount; cluster.inverseBindMatrix = glm::mat4(); cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix); mesh.clusters.append(cluster); @@ -1048,27 +423,27 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mesh.clusters.append(root); QList meshAttributes; - foreach(auto &primitive, _file.meshes[node.mesh].primitives) { - QList keys = primitive.attributes.values.keys(); - foreach (auto &key, keys) { + for (size_t primitiveIndex = 0; primitiveIndex < node.mesh->primitives_count; primitiveIndex++) { + auto &primitive = node.mesh->primitives[primitiveIndex]; + for (size_t attributeIndex = 0; attributeIndex < primitive.attributes_count; attributeIndex++) { + auto &attribute = primitive.attributes[attributeIndex]; + QString key(attribute.name); if (!meshAttributes.contains(key)) { meshAttributes.push_back(key); } } } - foreach(auto &primitive, _file.meshes[node.mesh].primitives) { + for (size_t primitiveIndex = 0; primitiveIndex < node.mesh->primitives_count; primitiveIndex++) { + auto &primitive = node.mesh->primitives[primitiveIndex]; HFMMeshPart part = HFMMeshPart(); - int indicesAccessorIdx = primitive.indices; - - if (indicesAccessorIdx > _file.accessors.size()) { - qWarning(modelformat) << "Indices accessor index is out of bounds for model " << _url; + if (primitive.indices == nullptr) { + qDebug() << "No indices accessor for mesh: " << _url; hfmModel.loadErrorCount++; - continue; + return false; } - - GLTFAccessor& indicesAccessor = _file.accessors[indicesAccessorIdx]; + auto &indicesAccessor = primitive.indices; // Buffers QVector indices; @@ -1089,9 +464,10 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& QVector weights; int weightStride = 4; - bool success = addArrayFromAccessor(indicesAccessor, indices); + indices.resize((int)indicesAccessor->count); + size_t readIndicesCount = cgltf_accessor_unpack_indices(indicesAccessor, indices.data(), sizeof(unsigned int), indicesAccessor->count); - if (!success) { + if (readIndicesCount != indicesAccessor->count) { qWarning(modelformat) << "There was a problem reading glTF INDICES data for model " << _url; hfmModel.loadErrorCount++; continue; @@ -1100,51 +476,58 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& // Increment the triangle indices by the current mesh vertex count so each mesh part can all reference the same buffers within the mesh int prevMeshVerticesCount = mesh.vertices.count(); - QList keys = primitive.attributes.values.keys(); + // For each vertex (stride is WEIGHTS_PER_VERTEX), it contains index of the cluster that given weight belongs to. QVector clusterJoints; QVector clusterWeights; - foreach(auto &key, keys) { - int accessorIdx = primitive.attributes.values[key]; - - if (accessorIdx > _file.accessors.size()) { - qWarning(modelformat) << "Accessor index is out of bounds for model " << _url; + for (size_t attributeIndex = 0; attributeIndex < primitive.attributes_count; attributeIndex++) { + if (primitive.attributes[attributeIndex].name == nullptr) { + qDebug() << "Inalid accessor name for mesh: " << _url; hfmModel.loadErrorCount++; - continue; + return false; } + QString key(primitive.attributes[attributeIndex].name); - GLTFAccessor& accessor = _file.accessors[accessorIdx]; + if (primitive.attributes[attributeIndex].data == nullptr) { + qDebug() << "Inalid accessor for mesh: " << _url; + hfmModel.loadErrorCount++; + return false; + } + auto accessor = primitive.attributes[attributeIndex].data; + int accessorCount = (int)accessor->count; if (key == "POSITION") { - if (accessor.type != GLTFAccessorType::VEC3) { + if (accessor->type != cgltf_type_vec3) { qWarning(modelformat) << "Invalid accessor type on glTF POSITION data for model " << _url; hfmModel.loadErrorCount++; continue; } - success = addArrayFromAccessor(accessor, vertices); - if (!success) { + vertices.resize(accessorCount * 3); + size_t floatCount = cgltf_accessor_unpack_floats(accessor, vertices.data(), accessor->count * 3); + if (floatCount != accessor->count * 3) { qWarning(modelformat) << "There was a problem reading glTF POSITION data for model " << _url; hfmModel.loadErrorCount++; continue; } } else if (key == "NORMAL") { - if (accessor.type != GLTFAccessorType::VEC3) { + if (accessor->type != cgltf_type_vec3) { qWarning(modelformat) << "Invalid accessor type on glTF NORMAL data for model " << _url; hfmModel.loadErrorCount++; continue; } - success = addArrayFromAccessor(accessor, normals); - if (!success) { + normals.resize(accessorCount * 3); + size_t floatCount = cgltf_accessor_unpack_floats(accessor, normals.data(), accessor->count * 3); + if (floatCount != accessor->count * 3) { qWarning(modelformat) << "There was a problem reading glTF NORMAL data for model " << _url; hfmModel.loadErrorCount++; continue; } } else if (key == "TANGENT") { - if (accessor.type == GLTFAccessorType::VEC4) { + if (accessor->type == cgltf_type_vec4) { tangentStride = 4; - } else if (accessor.type == GLTFAccessorType::VEC3) { + } else if (accessor->type == cgltf_type_vec3) { tangentStride = 3; } else { qWarning(modelformat) << "Invalid accessor type on glTF TANGENT data for model " << _url; @@ -1152,43 +535,46 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& continue; } - success = addArrayFromAccessor(accessor, tangents); - if (!success) { + tangents.resize(accessorCount * tangentStride); + size_t floatCount = cgltf_accessor_unpack_floats(accessor, tangents.data(), accessor->count * tangentStride); + if (floatCount != accessor->count * tangentStride) { qWarning(modelformat) << "There was a problem reading glTF TANGENT data for model " << _url; hfmModel.loadErrorCount++; tangentStride = 0; continue; } } else if (key == "TEXCOORD_0") { - success = addArrayFromAccessor(accessor, texcoords); - if (!success) { - qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_0 data for model " << _url; - hfmModel.loadErrorCount++; - continue; - } - - if (accessor.type != GLTFAccessorType::VEC2) { + if (accessor->type != cgltf_type_vec2) { qWarning(modelformat) << "Invalid accessor type on glTF TEXCOORD_0 data for model " << _url; hfmModel.loadErrorCount++; continue; } - } else if (key == "TEXCOORD_1") { - success = addArrayFromAccessor(accessor, texcoords2); - if (!success) { - qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_1 data for model " << _url; + + texcoords.resize(accessorCount * 2); + size_t floatCount = cgltf_accessor_unpack_floats(accessor, texcoords.data(), accessor->count * 2); + if (floatCount != accessor->count * 2) { + qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_0 data for model " << _url; hfmModel.loadErrorCount++; continue; } - - if (accessor.type != GLTFAccessorType::VEC2) { + } else if (key == "TEXCOORD_1") { + if (accessor->type != cgltf_type_vec2) { qWarning(modelformat) << "Invalid accessor type on glTF TEXCOORD_1 data for model " << _url; hfmModel.loadErrorCount++; continue; } + + texcoords2.resize(accessorCount * 2); + size_t floatCount = cgltf_accessor_unpack_floats(accessor, texcoords2.data(), accessor->count * 2); + if (floatCount != accessor->count * 2) { + qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_1 data for model " << _url; + hfmModel.loadErrorCount++; + continue; + } } else if (key == "COLOR_0") { - if (accessor.type == GLTFAccessorType::VEC4) { + if (accessor->type == cgltf_type_vec4) { colorStride = 4; - } else if (accessor.type == GLTFAccessorType::VEC3) { + } else if (accessor->type == cgltf_type_vec3) { colorStride = 3; } else { qWarning(modelformat) << "Invalid accessor type on glTF COLOR_0 data for model " << _url; @@ -1196,20 +582,21 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& continue; } - success = addArrayFromAccessor(accessor, colors); - if (!success) { + colors.resize(accessorCount * colorStride); + size_t floatCount = cgltf_accessor_unpack_floats(accessor, colors.data(), accessor->count * colorStride); + if (floatCount != accessor->count * colorStride) { qWarning(modelformat) << "There was a problem reading glTF COLOR_0 data for model " << _url; hfmModel.loadErrorCount++; continue; } } else if (key == "JOINTS_0") { - if (accessor.type == GLTFAccessorType::VEC4) { + if (accessor->type == cgltf_type_vec4) { jointStride = 4; - } else if (accessor.type == GLTFAccessorType::VEC3) { + } else if (accessor->type == cgltf_type_vec3) { jointStride = 3; - } else if (accessor.type == GLTFAccessorType::VEC2) { + } else if (accessor->type == cgltf_type_vec2) { jointStride = 2; - } else if (accessor.type == GLTFAccessorType::SCALAR) { + } else if (accessor->type == cgltf_type_scalar) { jointStride = 1; } else { qWarning(modelformat) << "Invalid accessor type on glTF JOINTS_0 data for model " << _url; @@ -1217,20 +604,23 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& continue; } - success = addArrayFromAccessor(accessor, joints); - if (!success) { - qWarning(modelformat) << "There was a problem reading glTF JOINTS_0 data for model " << _url; - hfmModel.loadErrorCount++; - continue; + joints.resize(accessorCount * jointStride); + cgltf_uint jointIndices[4]; + for (size_t i = 0; i < accessor->count; i++) { + cgltf_accessor_read_uint(accessor, i, jointIndices, jointStride); + for (int component = 0; component < jointStride; component++) { + joints[(int)i * jointStride + component] = (uint16_t)jointIndices[component]; + } } + } else if (key == "WEIGHTS_0") { - if (accessor.type == GLTFAccessorType::VEC4) { + if (accessor->type == cgltf_type_vec4) { weightStride = 4; - } else if (accessor.type == GLTFAccessorType::VEC3) { + } else if (accessor->type == cgltf_type_vec3) { weightStride = 3; - } else if (accessor.type == GLTFAccessorType::VEC2) { + } else if (accessor->type == cgltf_type_vec2) { weightStride = 2; - } else if (accessor.type == GLTFAccessorType::SCALAR) { + } else if (accessor->type == cgltf_type_scalar) { weightStride = 1; } else { qWarning(modelformat) << "Invalid accessor type on glTF WEIGHTS_0 data for model " << _url; @@ -1238,8 +628,9 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& continue; } - success = addArrayFromAccessor(accessor, weights); - if (!success) { + weights.resize(accessorCount * weightStride); + size_t floatCount = cgltf_accessor_unpack_floats(accessor, weights.data(), accessor->count * weightStride); + if (floatCount != accessor->count * weightStride) { qWarning(modelformat) << "There was a problem reading glTF WEIGHTS_0 data for model " << _url; hfmModel.loadErrorCount++; continue; @@ -1280,7 +671,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& if (v1_index + 2 >= vertices.size() || v2_index + 2 >= vertices.size() || v3_index + 2 >= vertices.size()) { qWarning(modelformat) << "Indices out of range for model " << _url; hfmModel.loadErrorCount++; - break; + return false; } glm::vec3 v1 = glm::vec3(vertices[v1_index], vertices[v1_index + 1], vertices[v1_index + 2]); @@ -1534,22 +925,21 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& continue; } - if ( _file.skins.length() <= node.skin ) { - qCWarning(modelformat) << "Trying to read past end of _file.skins at" << node.skin; - hfmModel.loadErrorCount++; - continue; - } - - if ( _file.skins[node.skin].joints.length() <= clusterJoints[c]) { + if ( node.skin->joints_count <= clusterJoints[c]) { qCWarning(modelformat) << "Trying to read past end of _file.skins[node.skin].joints at" << clusterJoints[c] - << "; there are only" << _file.skins[node.skin].joints.length() << "for skin" << node.skin; + << "; there are only" << node.skin->joints_count << "for skin" << node.skin->name; hfmModel.loadErrorCount++; continue; } - + size_t jointIndex = 0; + if (!findPointerInArray(node.skin->joints[clusterJoints[c]], _data->nodes, _data->nodes_count, jointIndex)) { + qCWarning(modelformat) << "Cannot find the joint " << node.skin->joints[clusterJoints[c]]->name <<" in joint array"; + hfmModel.loadErrorCount++; + continue; + } mesh.clusterIndices[prevMeshClusterIndexCount + c] = - originalToNewNodeIndexMap[_file.skins[node.skin].joints[clusterJoints[c]]]; + originalToNewNodeIndexMap[(int)jointIndex]; } // normalize and compress to 16-bits @@ -1568,26 +958,31 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& } else { mesh.clusterWeights[prevMeshClusterWeightCount + j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF); } - for (int clusterIndex = 0; clusterIndex < mesh.clusters.size() - 1; ++clusterIndex) { + for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { + int clusterIndex = mesh.clusterIndices[prevMeshClusterIndexCount + k]; ShapeVertices& points = hfmModel.shapeVertices.at(clusterIndex); glm::vec3 globalMeshScale = extractScale(globalTransforms[nodeIndex]); const glm::mat4 meshToJoint = glm::scale(glm::mat4(), globalMeshScale) * jointInverseBindTransforms[clusterIndex]; - // TODO: The entire clustering is probably broken and detailed collision shapes fail to generate due to it. const uint16_t EXPANSION_WEIGHT_THRESHOLD = UINT16_MAX/4; // Equivalent of 0.25f? - if (mesh.clusterWeights[j] >= EXPANSION_WEIGHT_THRESHOLD) { - // TODO: fix transformed vertices being pushed back - auto& vertex = mesh.vertices[i]; - const glm::mat4 vertexTransform = meshToJoint * (glm::translate(glm::mat4(), vertex)); - glm::vec3 transformedVertex = hfmModel.joints[clusterIndex].translation * (extractTranslation(vertexTransform)); + if (mesh.clusterWeights[prevMeshClusterWeightCount + k] >= EXPANSION_WEIGHT_THRESHOLD) { + auto& vertex = mesh.vertices[prevMeshVerticesCount + i]; + const glm::mat4 vertexTransform = meshToJoint * glm::translate(vertex); + glm::vec3 transformedVertex = extractTranslation(vertexTransform); points.push_back(transformedVertex); } } } } - if (primitive.defined["material"]) { - part.materialID = materialIDs[primitive.material]; + size_t materialIndex = 0; + if (primitive.material != nullptr && !findPointerInArray(primitive.material, _data->materials, _data->materials_count, materialIndex)) { + qCWarning(modelformat) << "GLTFSerializer::buildGeometry: Invalid material pointer"; + hfmModel.loadErrorCount++; + return false; + } + if (primitive.material != nullptr) { + part.materialID = materialIDs[(int)materialIndex]; } mesh.parts.push_back(part); @@ -1599,7 +994,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& } // Build morph targets (blend shapes) - if (!primitive.targets.isEmpty()) { + if (primitive.targets_count) { // Build list of blendshapes from FST and model. typedef QPair WeightedIndex; @@ -1613,21 +1008,30 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& auto mappings = blendshapeMappings.values(blendshapeName); if (mappings.count() > 0) { // Use blendshape from mapping. - foreach(const QVariant& mapping, mappings) { - auto blendshapeMapping = mapping.toList(); + foreach(const QVariant& mappingVariant, mappings) { + auto blendshapeMapping = mappingVariant.toList(); blendshapeIndices.insert(blendshapeMapping.at(0).toString(), WeightedIndex(i, blendshapeMapping.at(1).toFloat())); } } else { // Use blendshape from model. - if (_file.meshes[node.mesh].extras.targetNames.contains(blendshapeName)) { - blendshapeIndices.insert(blendshapeName, WeightedIndex(i, 1.0f)); + std::string blendshapeNameString = blendshapeName.toStdString(); + for (size_t j = 0; j < node.mesh->target_names_count; j++) { + if (strcmp(node.mesh->target_names[j], blendshapeNameString.c_str()) == 0) { + blendshapeIndices.insert(blendshapeName, WeightedIndex(i, 1.0f)); + break; + } } } } // If an FST isn't being used and the model is likely from ReadyPlayerMe, add blendshape synonyms. - auto fileTargetNames = _file.meshes[node.mesh].extras.targetNames; + QVector fileTargetNames; + fileTargetNames.reserve((int)node.mesh->target_names_count); + for (size_t i = 0; i < node.mesh->target_names_count; i++) { + fileTargetNames.push_back(QString(node.mesh->target_names[i])); + } + bool likelyReadyPlayerMeFile = fileTargetNames.contains("browOuterUpLeft") && fileTargetNames.contains("browInnerUp") @@ -1658,7 +1062,11 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& } auto keys = blendshapeIndices.keys(); auto values = blendshapeIndices.values(); - auto names = _file.meshes[node.mesh].extras.targetNames; + QVector names; + names.reserve((int)node.mesh->target_names_count); + for (size_t i = 0; i < node.mesh->target_names_count; i++) { + names.push_back(QString(node.mesh->target_names[i])); + } for (int weightedIndex = 0; weightedIndex < keys.size(); ++weightedIndex) { float weight = 1.0f; @@ -1682,11 +1090,21 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& QVector normals; QVector vertices; - if (target.values.contains((QString) "NORMAL")) { - generateTargetData(target.values.value((QString) "NORMAL"), weight, normals); + size_t normalAttributeIndex = 0; + if (findAttribute("NORMAL", target.attributes, target.attributes_count, normalAttributeIndex)) { + if (!generateTargetData(target.attributes[normalAttributeIndex].data, weight, normals)) { + qWarning(modelformat) << "Invalid NORMAL accessor on generateTargetData vertices for model " << _url; + hfmModel.loadErrorCount++; + return false; + } } - if (target.values.contains((QString) "POSITION")) { - generateTargetData(target.values.value((QString) "POSITION"), weight, vertices); + size_t positionAttributeIndex = 0; + if (findAttribute("POSITION", target.attributes, target.attributes_count, positionAttributeIndex)) { + if (!generateTargetData(target.attributes[positionAttributeIndex].data, weight, vertices)) { + qWarning(modelformat) << "Invalid POSITION accessor on generateTargetData vertices for model " << _url; + hfmModel.loadErrorCount++; + return false; + } } if (blendshape.indices.size() < prevMeshVerticesCount + vertices.size()) { @@ -1694,12 +1112,23 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& blendshape.vertices.resize(prevMeshVerticesCount + vertices.size()); blendshape.normals.resize(prevMeshVerticesCount + vertices.size()); } + //TODO: it looks like this can support sparse encoding, since there are indices? for (int i = 0; i < vertices.size(); i++) { blendshape.indices[prevMeshVerticesCount + i] = prevMeshVerticesCount + i; blendshape.vertices[prevMeshVerticesCount + i] += vertices.value(i); - blendshape.normals[prevMeshVerticesCount + i] += normals.value(i); + // Prevent out-of-bounds access if blendshape normals are not available + if (i < normals.size()) { + blendshape.normals[prevMeshVerticesCount + i] += normals.value(i); + } else { + if (prevMeshVerticesCount + i < mesh.normals.size()) { + blendshape.normals[prevMeshVerticesCount + i] = mesh.normals[prevMeshVerticesCount + i]; + } else { + qWarning(modelformat) << "Blendshape has more vertices than original mesh " << _url; + hfmModel.loadErrorCount++; + return false; + } + } } - } } @@ -1720,7 +1149,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mesh.meshIndex = hfmModel.meshes.size(); } - ++nodecount; + ++nodeCount; } return true; @@ -1752,45 +1181,58 @@ HFMModel::Pointer GLTFSerializer::read(const hifi::ByteArray& data, const hifi:: _url = hifi::URL(QFileInfo(localFileName).absoluteFilePath()); } - if (parseGLTF(data)) { - //_file.dump(); - auto hfmModelPtr = std::make_shared(); - HFMModel& hfmModel = *hfmModelPtr; - buildGeometry(hfmModel, mapping, _url); - - //hfmModel.debugDump(); - //glTFDebugDump(); - - return hfmModelPtr; - } else { + cgltf_options options = {}; + cgltf_result result = cgltf_parse(&options, data.data(), data.size(), &_data); + if (result != cgltf_result_success) { qCDebug(modelformat) << "Error parsing GLTF file."; + return nullptr; + } + cgltf_load_buffers(&options, _data, NULL); + for (size_t i = 0; i < _data->buffers_count; i++) { + cgltf_buffer &buffer = _data->buffers[i]; + if (buffer.data == nullptr) { + if (!readBinary(buffer.uri, buffer)) { + qCDebug(modelformat) << "Error parsing GLTF file."; + return nullptr; + } + } } - return nullptr; + + auto hfmModelPtr = std::make_shared(); + HFMModel& hfmModel = *hfmModelPtr; + buildGeometry(hfmModel, mapping, _url); + + return hfmModelPtr; } -bool GLTFSerializer::readBinary(const QString& url, hifi::ByteArray& outdata) { +bool GLTFSerializer::readBinary(const QString& url, cgltf_buffer &buffer) { bool success; + hifi::ByteArray outdata; + // Is this part already done by cgltf? if (url.contains("data:application/octet-stream;base64,")) { + qDebug() << "GLTFSerializer::readBinary: base64"; outdata = requestEmbeddedData(url); success = !outdata.isEmpty(); } else { hifi::URL binaryUrl = _url.resolved(url); std::tie(success, outdata) = requestData(binaryUrl); } + if (success) { + if(buffer.size == (size_t)outdata.size()) { + _externalData.push_back(outdata); + buffer.data = _externalData.last().data(); + buffer.data_free_method = cgltf_data_free_method_none; + } else { + qDebug() << "Buffer size mismatch for model: " << _url; + success = false; + } + } return success; } -bool GLTFSerializer::doesResourceExist(const QString& url) { - if (_url.isEmpty()) { - return false; - } - hifi::URL candidateUrl = _url.resolved(url); - return DependencyManager::get()->resourceExists(candidateUrl); -} - std::tuple GLTFSerializer::requestData(hifi::URL& url) { auto request = DependencyManager::get()->createResourceRequest( nullptr, url, true, -1, "GLTFSerializer::requestData"); @@ -1841,265 +1283,246 @@ QNetworkReply* GLTFSerializer::request(hifi::URL& url, bool isTest) { return netReply; // trying to sync later on. } -HFMTexture GLTFSerializer::getHFMTexture(const GLTFTexture& texture) { - HFMTexture fbxtex = HFMTexture(); - fbxtex.texcoordSet = 0; +HFMTexture GLTFSerializer::getHFMTexture(const cgltf_texture *texture) { + HFMTexture hfmTex = HFMTexture(); + hfmTex.texcoordSet = 0; - if (texture.defined["source"]) { - QString url = _file.images[texture.source].uri; + auto image = texture->image; - QString fname = hifi::URL(url).fileName(); - hifi::URL textureUrl = _url.resolved(url); - fbxtex.name = fname; - fbxtex.filename = textureUrl.toEncoded(); - - if (_url.path().endsWith("glb") && !_glbBinary.isEmpty()) { - int bufferView = _file.images[texture.source].bufferView; - - GLTFBufferView& imagesBufferview = _file.bufferviews[bufferView]; - int offset = imagesBufferview.byteOffset; - int length = imagesBufferview.byteLength; - - fbxtex.content = _glbBinary.mid(offset, length); - fbxtex.filename = textureUrl.toEncoded().append(texture.source); - } - - if (url.contains("data:image/jpeg;base64,") || url.contains("data:image/png;base64,")) { - fbxtex.content = requestEmbeddedData(url); + // Check for WebP extension + for (size_t i = 0; i < texture->extensions_count; i++) { + auto &extension = texture->extensions[i]; + if (extension.name != nullptr + && strcmp(extension.name, "EXT_texture_webp") == 0 + && extension.data != nullptr) { + QJsonDocument webPExtension = QJsonDocument::fromJson(extension.data); + if (!webPExtension.isNull() && webPExtension["source"].isDouble()) { + int imageIndex = webPExtension["source"].isDouble(); + if (imageIndex > 0 && (size_t)imageIndex < _data->images_count) { + image = &_data->images[(int)(webPExtension["source"].toDouble())]; + break; + } + } } } - return fbxtex; + + if (image) { + QString url = image->uri; + + QString fileName = hifi::URL(url).fileName(); + hifi::URL textureUrl = _url.resolved(url); + hfmTex.name = fileName; + hfmTex.filename = textureUrl.toEncoded(); + + if (_url.path().endsWith("glb")) { + cgltf_buffer_view *bufferView = image->buffer_view; + + size_t offset = bufferView->offset; + int length = (int)bufferView->size; + + size_t imageIndex = 0; + if (!findPointerInArray(image, _data->images, _data->images_count, imageIndex)) { + // This should never happen. It would mean a bug in cgltf library. + qDebug(modelformat) << "GLTFSerializer::getHFMTexture: can't find texture in the array"; + return hfmTex; + } + + if (offset + length > bufferView->buffer->size) { + qDebug(modelformat) << "GLTFSerializer::getHFMTexture: texture data to short"; + return hfmTex; + } + hfmTex.content = QByteArray(static_cast(bufferView->buffer->data) + offset, length); + hfmTex.filename = textureUrl.toEncoded().append(QString::number(imageIndex).toUtf8()); + } + + if (url.contains("data:image/jpeg;base64,") || url.contains("data:image/png;base64,") || url.contains("data:image/webp;base64,")) { + hfmTex.content = requestEmbeddedData(url); + } + } + return hfmTex; } -void GLTFSerializer::setHFMMaterial(HFMMaterial& hfmMat, const GLTFMaterial& material) { - if (material.defined["alphaMode"]) { - hfmMat._material->setOpacityMapMode(material.alphaMode); +void GLTFSerializer::setHFMMaterial(HFMMaterial& hfmMat, const cgltf_material& material) { + hfmMat._material = std::make_shared(); + for (size_t i = 0; i < material.extensions_count; i++) { + auto& extension = material.extensions[i]; + if (extension.name != nullptr) { + if (strcmp(extension.name, "VRMC_materials_mtoon") == 0 && extension.data != nullptr) { + hfmMat.isMToonMaterial = true; + auto mToonMaterial = std::make_shared(); + QJsonDocument mToonExtension = QJsonDocument::fromJson(extension.data); + if (!mToonExtension.isNull()) { + if (mToonExtension["shadeColorFactor"].isArray()) { + auto array = mToonExtension["shadeColorFactor"].toArray(); + glm::vec3 shadeLinear = glm::vec3(array[0].toDouble(), array[1].toDouble(), array[2].toDouble()); + glm::vec3 shade = ColorUtils::tosRGBVec3(shadeLinear); + mToonMaterial->setShade(shade); + } + if (mToonExtension["shadeMultiplyTexture"].isObject()) { + QJsonObject object = mToonExtension["shadeMultiplyTexture"].toObject(); + if (object["index"].isDouble() && object["index"].toInt() < (int)_data->textures_count) { + hfmMat.shadeTexture = getHFMTexture(&_data->textures[object["index"].toInt()]); + } + } + if (mToonExtension["shadingShiftFactor"].isDouble()) { + mToonMaterial->setShadingShift(mToonExtension["shadingShiftFactor"].toDouble()); + } + if (mToonExtension["shadingShiftTexture"].isObject()) { + QJsonObject object = mToonExtension["shadingShiftTexture"].toObject(); + if (object["index"].isDouble() && object["index"].toInt() < (int)_data->textures_count) { + hfmMat.shadingShiftTexture = getHFMTexture(&_data->textures[object["index"].toInt()]); + } + } + if (mToonExtension["shadingToonyFactor"].isDouble()) { + mToonMaterial->setShadingToony(mToonExtension["shadingToonyFactor"].toDouble()); + } + if (mToonExtension["matcapFactor"].isArray()) { + auto array = mToonExtension["matcapFactor"].toArray(); + glm::vec3 matcapLinear = glm::vec3(array[0].toDouble(), array[1].toDouble(), array[2].toDouble()); + glm::vec3 matcap = ColorUtils::tosRGBVec3(matcapLinear); + mToonMaterial->setMatcap(matcap); + } + if (mToonExtension["matcapTexture"].isObject()) { + QJsonObject object = mToonExtension["matcapTexture"].toObject(); + if (object["index"].isDouble() && object["index"].toInt() < (int)_data->textures_count) { + hfmMat.matcapTexture = getHFMTexture(&_data->textures[object["index"].toInt()]); + } + } + if (mToonExtension["parametricRimColorFactor"].isArray()) { + auto array = mToonExtension["parametricRimColorFactor"].toArray(); + glm::vec3 parametricRimLinear = glm::vec3(array[0].toDouble(), array[1].toDouble(), array[2].toDouble()); + glm::vec3 parametricRim = ColorUtils::tosRGBVec3(parametricRimLinear); + mToonMaterial->setParametricRim(parametricRim); + } + if (mToonExtension["parametricRimFresnelPowerFactor"].isDouble()) { + mToonMaterial->setParametricRimFresnelPower(mToonExtension["parametricRimFresnelPowerFactor"].toDouble()); + } + if (mToonExtension["parametricRimLiftFactor"].isDouble()) { + mToonMaterial->setParametricRimLift(mToonExtension["parametricRimLiftFactor"].toDouble()); + } + if (mToonExtension["rimMultiplyTexture"].isObject()) { + QJsonObject object = mToonExtension["rimMultiplyTexture"].toObject(); + if (object["index"].isDouble() && object["index"].toInt() < (int)_data->textures_count) { + hfmMat.rimTexture = getHFMTexture(&_data->textures[object["index"].toInt()]); + } + } + if (mToonExtension["rimLightingMixFactor"].isDouble()) { + mToonMaterial->setRimLightingMix(mToonExtension["rimLightingMixFactor"].toDouble()); + } + // FIXME: Outlines are currently disabled because they're buggy + //if (mToonExtension["outlineWidthMode"].isString()) { + // QString outlineWidthMode = mToonExtension["outlineWidthMode"].toString(); + // if (outlineWidthMode == "none") { + // mToonMaterial->setOutlineWidthMode(NetworkMToonMaterial::OutlineWidthMode::OUTLINE_NONE); + // } else if (outlineWidthMode == "worldCoordinates") { + // mToonMaterial->setOutlineWidthMode(NetworkMToonMaterial::OutlineWidthMode::OUTLINE_WORLD); + // } else if (outlineWidthMode == "screenCoordinates") { + // mToonMaterial->setOutlineWidthMode(NetworkMToonMaterial::OutlineWidthMode::OUTLINE_SCREEN); + // } + //} + if (mToonExtension["outlineWidthFactor"].isDouble()) { + mToonMaterial->setOutlineWidth(mToonExtension["outlineWidthFactor"].toDouble()); + } + if (mToonExtension["outlineColorFactor"].isArray()) { + auto array = mToonExtension["outlineColorFactor"].toArray(); + glm::vec3 outlineLinear = glm::vec3(array[0].toDouble(), array[1].toDouble(), array[2].toDouble()); + glm::vec3 outline = ColorUtils::tosRGBVec3(outlineLinear); + mToonMaterial->setOutline(outline); + } + if (mToonExtension["uvAnimationMaskTexture"].isObject()) { + QJsonObject object = mToonExtension["uvAnimationMaskTexture"].toObject(); + if (object["index"].isDouble() && object["index"].toInt() < (int)_data->textures_count) { + hfmMat.uvAnimationTexture = getHFMTexture(&_data->textures[object["index"].toInt()]); + } + } + if (mToonExtension["uvAnimationScrollXSpeedFactor"].isDouble()) { + mToonMaterial->setUVAnimationScrollXSpeed(mToonExtension["uvAnimationScrollXSpeedFactor"].toDouble()); + } + if (mToonExtension["uvAnimationScrollYSpeedFactor"].isDouble()) { + mToonMaterial->setUVAnimationScrollYSpeed(mToonExtension["uvAnimationScrollYSpeedFactor"].toDouble()); + } + if (mToonExtension["uvAnimationRotationSpeedFactor"].isDouble()) { + mToonMaterial->setUVAnimationRotationSpeed(mToonExtension["uvAnimationRotationSpeedFactor"].toDouble()); + } + } + hfmMat._material = mToonMaterial; + } + } + } + + if (material.alpha_mode == cgltf_alpha_mode_opaque) { + hfmMat._material->setOpacityMapMode(graphics::MaterialKey::OPACITY_MAP_OPAQUE); + } else if (material.alpha_mode == cgltf_alpha_mode_mask) { + hfmMat._material->setOpacityMapMode(graphics::MaterialKey::OPACITY_MAP_MASK); + } else if (material.alpha_mode == cgltf_alpha_mode_blend) { + hfmMat._material->setOpacityMapMode(graphics::MaterialKey::OPACITY_MAP_BLEND); } else { hfmMat._material->setOpacityMapMode(graphics::MaterialKey::OPACITY_MAP_OPAQUE); // GLTF defaults to opaque } - if (material.defined["alphaCutoff"]) { - hfmMat._material->setOpacityCutoff(material.alphaCutoff); + hfmMat._material->setOpacityCutoff(material.alpha_cutoff); + + // VRMC_materials_mtoon takes precedence over KHR_materials_unlit + if (!hfmMat.isMToonMaterial) { + hfmMat._material->setUnlit(material.unlit); } - if (material.defined["doubleSided"] && material.doubleSided) { + if (material.double_sided) { hfmMat._material->setCullFaceMode(graphics::MaterialKey::CullFaceMode::CULL_NONE); } - if (material.defined["emissiveFactor"] && material.emissiveFactor.size() == 3) { - glm::vec3 emissiveLinear = glm::vec3(material.emissiveFactor[0], material.emissiveFactor[1], material.emissiveFactor[2]); - glm::vec3 emissive = ColorUtils::tosRGBVec3(emissiveLinear); - hfmMat._material->setEmissive(emissive); - } + glm::vec3 emissiveLinear = glm::vec3(material.emissive_factor[0], material.emissive_factor[1], material.emissive_factor[2]); + glm::vec3 emissive = ColorUtils::tosRGBVec3(emissiveLinear); + hfmMat._material->setEmissive(emissive); - if (material.defined["emissiveTexture"]) { - hfmMat.emissiveTexture = getHFMTexture(_file.textures[material.emissiveTexture]); + if (material.emissive_texture.texture != nullptr) { + hfmMat.emissiveTexture = getHFMTexture(material.emissive_texture.texture); hfmMat.useEmissiveMap = true; } - if (material.defined["normalTexture"]) { - hfmMat.normalTexture = getHFMTexture(_file.textures[material.normalTexture]); + if (material.normal_texture.texture != nullptr) { + hfmMat.normalTexture = getHFMTexture(material.normal_texture.texture); hfmMat.useNormalMap = true; } - if (material.defined["occlusionTexture"]) { - hfmMat.occlusionTexture = getHFMTexture(_file.textures[material.occlusionTexture]); + if (material.occlusion_texture.texture != nullptr) { + hfmMat.occlusionTexture = getHFMTexture(material.occlusion_texture.texture); hfmMat.useOcclusionMap = true; } - if (material.defined["pbrMetallicRoughness"]) { + if (material.has_pbr_metallic_roughness) { hfmMat.isPBSMaterial = true; - if (material.pbrMetallicRoughness.defined["metallicFactor"]) { - hfmMat.metallic = material.pbrMetallicRoughness.metallicFactor; - hfmMat._material->setMetallic(hfmMat.metallic); - } - if (material.pbrMetallicRoughness.defined["baseColorTexture"]) { - hfmMat.opacityTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); - hfmMat.albedoTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); + hfmMat.metallic = material.pbr_metallic_roughness.metallic_factor; + hfmMat._material->setMetallic(hfmMat.metallic); + + if (material.pbr_metallic_roughness.base_color_texture.texture != nullptr) { + hfmMat.opacityTexture = getHFMTexture(material.pbr_metallic_roughness.base_color_texture.texture); + hfmMat.albedoTexture = getHFMTexture(material.pbr_metallic_roughness.base_color_texture.texture); hfmMat.useAlbedoMap = true; } - if (material.pbrMetallicRoughness.defined["metallicRoughnessTexture"]) { - hfmMat.roughnessTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); + if (material.pbr_metallic_roughness.metallic_roughness_texture.texture) { + hfmMat.roughnessTexture = getHFMTexture(material.pbr_metallic_roughness.metallic_roughness_texture.texture); hfmMat.roughnessTexture.sourceChannel = image::ColorChannel::GREEN; hfmMat.useRoughnessMap = true; - hfmMat.metallicTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); + hfmMat.metallicTexture = getHFMTexture(material.pbr_metallic_roughness.metallic_roughness_texture.texture); hfmMat.metallicTexture.sourceChannel = image::ColorChannel::BLUE; hfmMat.useMetallicMap = true; } - if (material.pbrMetallicRoughness.defined["roughnessFactor"]) { - hfmMat._material->setRoughness(material.pbrMetallicRoughness.roughnessFactor); - } - if (material.pbrMetallicRoughness.defined["baseColorFactor"] && - material.pbrMetallicRoughness.baseColorFactor.size() == 4) { - glm::vec3 lcolor = glm::vec3(material.pbrMetallicRoughness.baseColorFactor[0], material.pbrMetallicRoughness.baseColorFactor[1], - material.pbrMetallicRoughness.baseColorFactor[2]); - glm::vec3 dcolor = ColorUtils::tosRGBVec3(lcolor); - hfmMat.diffuseColor = dcolor; - hfmMat._material->setAlbedo(dcolor); - hfmMat._material->setOpacity(material.pbrMetallicRoughness.baseColorFactor[3]); - } + + hfmMat._material->setRoughness(material.pbr_metallic_roughness.roughness_factor); + + glm::vec3 lcolor = glm::vec3(material.pbr_metallic_roughness.base_color_factor[0], + material.pbr_metallic_roughness.base_color_factor[1], + material.pbr_metallic_roughness.base_color_factor[2]); + glm::vec3 dcolor = ColorUtils::tosRGBVec3(lcolor); + hfmMat.diffuseColor = dcolor; + hfmMat._material->setAlbedo(dcolor); + hfmMat._material->setOpacity(material.pbr_metallic_roughness.base_color_factor[3]); } } -template -bool GLTFSerializer::readArray(const hifi::ByteArray& bin, int byteOffset, int count, - QVector& outarray, int accessorType, bool normalized) { - - QDataStream blobstream(bin); - blobstream.setByteOrder(QDataStream::LittleEndian); - blobstream.setVersion(QDataStream::Qt_5_9); - blobstream.setFloatingPointPrecision(QDataStream::FloatingPointPrecision::SinglePrecision); - blobstream.skipRawData(byteOffset); - - int bufferCount = 0; - switch (accessorType) { - case GLTFAccessorType::SCALAR: - bufferCount = 1; - break; - case GLTFAccessorType::VEC2: - bufferCount = 2; - break; - case GLTFAccessorType::VEC3: - bufferCount = 3; - break; - case GLTFAccessorType::VEC4: - bufferCount = 4; - break; - case GLTFAccessorType::MAT2: - bufferCount = 4; - break; - case GLTFAccessorType::MAT3: - bufferCount = 9; - break; - case GLTFAccessorType::MAT4: - bufferCount = 16; - break; - default: - qWarning(modelformat) << "Unknown accessorType: " << accessorType; - blobstream.setDevice(nullptr); - return false; - } - - float scale = 1.0f; // Normalized output values should always be floats. - if (normalized) { - scale = (float)(std::numeric_limits::max)(); - } - - for (int i = 0; i < count; ++i) { - for (int j = 0; j < bufferCount; ++j) { - if (!blobstream.atEnd()) { - T value; - blobstream >> value; - if (normalized) { - outarray.push_back(std::max((float)value / scale, -1.0f)); - } else { - outarray.push_back(value); - } - } else { - blobstream.setDevice(nullptr); - return false; - } - } - } - - blobstream.setDevice(nullptr); - return true; -} -template -bool GLTFSerializer::addArrayOfType(const hifi::ByteArray& bin, int byteOffset, int count, - QVector& outarray, int accessorType, int componentType, bool normalized) { - - switch (componentType) { - case GLTFAccessorComponentType::BYTE: {} - case GLTFAccessorComponentType::UNSIGNED_BYTE: { - return readArray(bin, byteOffset, count, outarray, accessorType, normalized); - } - case GLTFAccessorComponentType::SHORT: { - return readArray(bin, byteOffset, count, outarray, accessorType, normalized); - } - case GLTFAccessorComponentType::UNSIGNED_INT: { - return readArray(bin, byteOffset, count, outarray, accessorType, normalized); - } - case GLTFAccessorComponentType::UNSIGNED_SHORT: { - return readArray(bin, byteOffset, count, outarray, accessorType, normalized); - } - case GLTFAccessorComponentType::FLOAT: { - return readArray(bin, byteOffset, count, outarray, accessorType, normalized); - } - } - return false; -} - -template -bool GLTFSerializer::addArrayFromAccessor(GLTFAccessor& accessor, QVector& outarray) { - bool success = true; - - if (accessor.defined["bufferView"]) { - GLTFBufferView& bufferview = _file.bufferviews[accessor.bufferView]; - GLTFBuffer& buffer = _file.buffers[bufferview.buffer]; - - int accBoffset = accessor.defined["byteOffset"] ? accessor.byteOffset : 0; - - success = addArrayOfType(buffer.blob, bufferview.byteOffset + accBoffset, accessor.count, outarray, accessor.type, - accessor.componentType, accessor.normalized); - } else { - for (int i = 0; i < accessor.count; ++i) { - T value; - memset(&value, 0, sizeof(T)); // Make sure the dummy array is initialized to zero. - outarray.push_back(value); - } - } - - if (success) { - if (accessor.defined["sparse"]) { - QVector out_sparse_indices_array; - - GLTFBufferView& sparseIndicesBufferview = _file.bufferviews[accessor.sparse.indices.bufferView]; - GLTFBuffer& sparseIndicesBuffer = _file.buffers[sparseIndicesBufferview.buffer]; - - int accSIBoffset = accessor.sparse.indices.defined["byteOffset"] ? accessor.sparse.indices.byteOffset : 0; - - success = addArrayOfType(sparseIndicesBuffer.blob, sparseIndicesBufferview.byteOffset + accSIBoffset, - accessor.sparse.count, out_sparse_indices_array, GLTFAccessorType::SCALAR, - accessor.sparse.indices.componentType, false); - if (success) { - QVector out_sparse_values_array; - - GLTFBufferView& sparseValuesBufferview = _file.bufferviews[accessor.sparse.values.bufferView]; - GLTFBuffer& sparseValuesBuffer = _file.buffers[sparseValuesBufferview.buffer]; - - int accSVBoffset = accessor.sparse.values.defined["byteOffset"] ? accessor.sparse.values.byteOffset : 0; - - success = addArrayOfType(sparseValuesBuffer.blob, sparseValuesBufferview.byteOffset + accSVBoffset, - accessor.sparse.count, out_sparse_values_array, accessor.type, accessor.componentType, - accessor.normalized); - - if (success) { - for (int i = 0; i < accessor.sparse.count; ++i) { - if ((i * 3) + 2 < out_sparse_values_array.size()) { - if ((out_sparse_indices_array[i] * 3) + 2 < outarray.length()) { - for (int j = 0; j < 3; ++j) { - outarray[(out_sparse_indices_array[i] * 3) + j] = out_sparse_values_array[(i * 3) + j]; - } - } else { - success = false; - break; - } - } else { - success = false; - break; - } - } - } - } - } - } - - return success; -} - void GLTFSerializer::retriangulate(const QVector& inIndices, const QVector& in_vertices, const QVector& in_normals, QVector& outIndices, QVector& out_vertices, QVector& out_normals) { @@ -2123,29 +1546,6 @@ void GLTFSerializer::retriangulate(const QVector& inIndices, const QVector< } } -void GLTFSerializer::glTFDebugDump() { - qCDebug(modelformat) << "\n"; - qCDebug(modelformat) << "---------------- GLTF Model ----------------"; - - qCDebug(modelformat) << "---------------- Nodes ----------------"; - for (GLTFNode node : _file.nodes) { - if (node.defined["mesh"]) { - qCDebug(modelformat) << " node_transforms" << node.transforms; - } - } - - qCDebug(modelformat) << "---------------- Accessors ----------------"; - for (GLTFAccessor accessor : _file.accessors) { - qCDebug(modelformat) << "count: " << accessor.count; - qCDebug(modelformat) << "byteOffset: " << accessor.byteOffset; - } - - qCDebug(modelformat) << "---------------- Textures ----------------"; - for (GLTFTexture texture : _file.textures) { - if (texture.defined["source"]) { - QString url = _file.images[texture.source].uri; - QString fname = hifi::URL(url).fileName(); - qCDebug(modelformat) << "fname: " << fname; - } - } -} +GLTFSerializer::~GLTFSerializer() { + cgltf_free(_data); +} \ No newline at end of file diff --git a/libraries/model-serializers/src/GLTFSerializer.h b/libraries/model-serializers/src/GLTFSerializer.h index da5284d0b3..18b3396c45 100644 --- a/libraries/model-serializers/src/GLTFSerializer.h +++ b/libraries/model-serializers/src/GLTFSerializer.h @@ -4,7 +4,7 @@ // // Created by Luis Cuenca on 8/30/17. // Copyright 2017 High Fidelity, Inc. -// Copyright 2023 Overte e.V. +// Copyright 2023-2024 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 @@ -19,748 +19,12 @@ #include #include +float atof_locale_independent(char* str); -struct GLTFAsset { - QString generator; - QString version; //required - QString copyright; - QMap defined; - void dump() { - if (defined["generator"]) { - qCDebug(modelformat) << "generator: " << generator; - } - if (defined["version"]) { - qCDebug(modelformat) << "version: " << version; - } - if (defined["copyright"]) { - qCDebug(modelformat) << "copyright: " << copyright; - } - } -}; +#define CGLTF_ATOF(str) atof_locale_independent(str) -struct GLTFNode { - QString name; - int camera; - int mesh; - QVector children; - QVector translation; - QVector rotation; - QVector scale; - QVector matrix; - QVector transforms; - int skin; - QVector skeletons; - QString jointName; - QMap defined; - void dump() { - if (defined["name"]) { - qCDebug(modelformat) << "name: " << name; - } - if (defined["camera"]) { - qCDebug(modelformat) << "camera: " << camera; - } - if (defined["mesh"]) { - qCDebug(modelformat) << "mesh: " << mesh; - } - if (defined["skin"]) { - qCDebug(modelformat) << "skin: " << skin; - } - if (defined["jointName"]) { - qCDebug(modelformat) << "jointName: " << jointName; - } - if (defined["children"]) { - qCDebug(modelformat) << "children: " << children; - } - if (defined["translation"]) { - qCDebug(modelformat) << "translation: " << translation; - } - if (defined["rotation"]) { - qCDebug(modelformat) << "rotation: " << rotation; - } - if (defined["scale"]) { - qCDebug(modelformat) << "scale: " << scale; - } - if (defined["matrix"]) { - qCDebug(modelformat) << "matrix: " << matrix; - } - if (defined["skeletons"]) { - qCDebug(modelformat) << "skeletons: " << skeletons; - } - } -}; +#include "cgltf.h" -// Meshes - -struct GLTFMeshPrimitivesTarget { - int normal; - int position; - int tangent; - QMap defined; - void dump() { - if (defined["normal"]) { - qCDebug(modelformat) << "normal: " << normal; - } - if (defined["position"]) { - qCDebug(modelformat) << "position: " << position; - } - if (defined["tangent"]) { - qCDebug(modelformat) << "tangent: " << tangent; - } - } -}; - -namespace GLTFMeshPrimitivesRenderingMode { - enum Values { - POINTS = 0, - LINES, - LINE_LOOP, - LINE_STRIP, - TRIANGLES, - TRIANGLE_STRIP, - TRIANGLE_FAN - }; -} - -struct GLTFMeshPrimitiveAttr { - QMap values; - QMap defined; - void dump() { - QList keys = values.keys(); - qCDebug(modelformat) << "values: "; - foreach(auto k, keys) { - qCDebug(modelformat) << k << ": " << values[k]; - } - } -}; - -struct GLTFMeshPrimitive { - GLTFMeshPrimitiveAttr attributes; - int indices; - int material; - int mode{ GLTFMeshPrimitivesRenderingMode::TRIANGLES }; - QVector targets; - QMap defined; - void dump() { - if (defined["attributes"]) { - qCDebug(modelformat) << "attributes: "; - attributes.dump(); - } - if (defined["indices"]) { - qCDebug(modelformat) << "indices: " << indices; - } - if (defined["material"]) { - qCDebug(modelformat) << "material: " << material; - } - if (defined["mode"]) { - qCDebug(modelformat) << "mode: " << mode; - } - if (defined["targets"]) { - qCDebug(modelformat) << "targets: "; - foreach(auto t, targets) t.dump(); - } - } -}; - -struct GLTFMeshExtra { - QVector targetNames; - QMap defined; - void dump() { - if (defined["targetNames"]) { - qCDebug(modelformat) << "targetNames: " << targetNames; - } - } -}; - -struct GLTFMesh { - QString name; - QVector primitives; - GLTFMeshExtra extras; - QVector weights; - QMap defined; - void dump() { - if (defined["name"]) { - qCDebug(modelformat) << "name: " << name; - } - if (defined["primitives"]) { - qCDebug(modelformat) << "primitives: "; - foreach(auto prim, primitives) prim.dump(); - } - if (defined["extras"]) { - qCDebug(modelformat) << "extras: "; - extras.dump(); - } - if (defined["weights"]) { - qCDebug(modelformat) << "weights: " << weights; - } - } -}; - -// BufferViews - -namespace GLTFBufferViewTarget { - enum Values { - ARRAY_BUFFER = 34962, - ELEMENT_ARRAY_BUFFER = 34963 - }; -} - -struct GLTFBufferView { - int buffer; //required - int byteLength; //required - int byteOffset { 0 }; - int target; - QMap defined; - void dump() { - if (defined["buffer"]) { - qCDebug(modelformat) << "buffer: " << buffer; - } - if (defined["byteLength"]) { - qCDebug(modelformat) << "byteLength: " << byteLength; - } - if (defined["byteOffset"]) { - qCDebug(modelformat) << "byteOffset: " << byteOffset; - } - if (defined["target"]) { - qCDebug(modelformat) << "target: " << target; - } - } -}; - -// Buffers - -struct GLTFBuffer { - int byteLength; //required - QString uri; - hifi::ByteArray blob; - QMap defined; - void dump() { - if (defined["byteLength"]) { - qCDebug(modelformat) << "byteLength: " << byteLength; - } - if (defined["uri"]) { - qCDebug(modelformat) << "uri: " << uri; - } - if (defined["blob"]) { - qCDebug(modelformat) << "blob: " << "DEFINED"; - } - } -}; - -// Samplers -namespace GLTFSamplerFilterType { - enum Values { - NEAREST = 9728, - LINEAR = 9729, - NEAREST_MIPMAP_NEAREST = 9984, - LINEAR_MIPMAP_NEAREST = 9985, - NEAREST_MIPMAP_LINEAR = 9986, - LINEAR_MIPMAP_LINEAR = 9987 - }; -} - -namespace GLTFSamplerWrapType { - enum Values { - CLAMP_TO_EDGE = 33071, - MIRRORED_REPEAT = 33648, - REPEAT = 10497 - }; -} - -struct GLTFSampler { - int magFilter; - int minFilter; - int wrapS; - int wrapT; - QMap defined; - void dump() { - if (defined["magFilter"]) { - qCDebug(modelformat) << "magFilter: " << magFilter; - } - if (defined["minFilter"]) { - qCDebug(modelformat) << "minFilter: " << minFilter; - } - if (defined["wrapS"]) { - qCDebug(modelformat) << "wrapS: " << wrapS; - } - if (defined["wrapT"]) { - qCDebug(modelformat) << "wrapT: " << wrapT; - } - } -}; - -// Cameras - -struct GLTFCameraPerspective { - double aspectRatio; - double yfov; //required - double zfar; - double znear; //required - QMap defined; - void dump() { - if (defined["zfar"]) { - qCDebug(modelformat) << "zfar: " << zfar; - } - if (defined["znear"]) { - qCDebug(modelformat) << "znear: " << znear; - } - if (defined["aspectRatio"]) { - qCDebug(modelformat) << "aspectRatio: " << aspectRatio; - } - if (defined["yfov"]) { - qCDebug(modelformat) << "yfov: " << yfov; - } - } -}; - -struct GLTFCameraOrthographic { - double zfar; //required - double znear; //required - double xmag; //required - double ymag; //required - QMap defined; - void dump() { - if (defined["zfar"]) { - qCDebug(modelformat) << "zfar: " << zfar; - } - if (defined["znear"]) { - qCDebug(modelformat) << "znear: " << znear; - } - if (defined["xmag"]) { - qCDebug(modelformat) << "xmag: " << xmag; - } - if (defined["ymag"]) { - qCDebug(modelformat) << "ymag: " << ymag; - } - } -}; - -namespace GLTFCameraTypes { - enum Values { - ORTHOGRAPHIC = 0, - PERSPECTIVE - }; -} - -struct GLTFCamera { - QString name; - GLTFCameraPerspective perspective; //required (or) - GLTFCameraOrthographic orthographic; //required (or) - int type; - QMap defined; - void dump() { - if (defined["name"]) { - qCDebug(modelformat) << "name: " << name; - } - if (defined["type"]) { - qCDebug(modelformat) << "type: " << type; - } - if (defined["perspective"]) { - perspective.dump(); - } - if (defined["orthographic"]) { - orthographic.dump(); - } - } -}; - -// Images - -namespace GLTFImageMimetype { - enum Values { - JPEG = 0, - PNG - }; -}; - -struct GLTFImage { - QString uri; //required (or) - int mimeType; - int bufferView; //required (or) - QMap defined; - void dump() { - if (defined["mimeType"]) { - qCDebug(modelformat) << "mimeType: " << mimeType; - } - if (defined["bufferView"]) { - qCDebug(modelformat) << "bufferView: " << bufferView; - } - } -}; - -// Materials - -struct GLTFpbrMetallicRoughness { - QVector baseColorFactor; - int baseColorTexture; - int metallicRoughnessTexture; - double metallicFactor; - double roughnessFactor; - QMap defined; - void dump() { - if (defined["baseColorFactor"]) { - qCDebug(modelformat) << "baseColorFactor: " << baseColorFactor; - } - if (defined["baseColorTexture"]) { - qCDebug(modelformat) << "baseColorTexture: " << baseColorTexture; - } - if (defined["metallicRoughnessTexture"]) { - qCDebug(modelformat) << "metallicRoughnessTexture: " << metallicRoughnessTexture; - } - if (defined["metallicFactor"]) { - qCDebug(modelformat) << "metallicFactor: " << metallicFactor; - } - if (defined["roughnessFactor"]) { - qCDebug(modelformat) << "roughnessFactor: " << roughnessFactor; - } - if (defined["baseColorFactor"]) { - qCDebug(modelformat) << "baseColorFactor: " << baseColorFactor; - } - } -}; - -struct GLTFMaterial { - QString name; - QVector emissiveFactor; - int emissiveTexture; - int normalTexture; - int occlusionTexture; - graphics::MaterialKey::OpacityMapMode alphaMode { graphics::MaterialKey::OPACITY_MAP_OPAQUE }; - double alphaCutoff; - bool doubleSided { false }; - GLTFpbrMetallicRoughness pbrMetallicRoughness; - QMap defined; - void dump() { - if (defined["name"]) { - qCDebug(modelformat) << "name: " << name; - } - if (defined["emissiveTexture"]) { - qCDebug(modelformat) << "emissiveTexture: " << emissiveTexture; - } - if (defined["normalTexture"]) { - qCDebug(modelformat) << "normalTexture: " << normalTexture; - } - if (defined["occlusionTexture"]) { - qCDebug(modelformat) << "occlusionTexture: " << occlusionTexture; - } - if (defined["emissiveFactor"]) { - qCDebug(modelformat) << "emissiveFactor: " << emissiveFactor; - } - if (defined["alphaMode"]) { - qCDebug(modelformat) << "alphaMode: " << alphaMode; - } - if (defined["alphaCutoff"]) { - qCDebug(modelformat) << "alphaCutoff: " << alphaCutoff; - } - if (defined["pbrMetallicRoughness"]) { - pbrMetallicRoughness.dump(); - } - } -}; - -// Accesors - -namespace GLTFAccessorType { - enum Values { - SCALAR = 0, - VEC2, - VEC3, - VEC4, - MAT2, - MAT3, - MAT4 - }; -} -namespace GLTFAccessorComponentType { - enum Values { - BYTE = 5120, - UNSIGNED_BYTE = 5121, - SHORT = 5122, - UNSIGNED_SHORT = 5123, - UNSIGNED_INT = 5125, - FLOAT = 5126 - }; -} -struct GLTFAccessor { - struct GLTFAccessorSparse { - struct GLTFAccessorSparseIndices { - int bufferView; - int byteOffset{ 0 }; - int componentType; - - QMap defined; - void dump() { - if (defined["bufferView"]) { - qCDebug(modelformat) << "bufferView: " << bufferView; - } - if (defined["byteOffset"]) { - qCDebug(modelformat) << "byteOffset: " << byteOffset; - } - if (defined["componentType"]) { - qCDebug(modelformat) << "componentType: " << componentType; - } - } - }; - struct GLTFAccessorSparseValues { - int bufferView; - int byteOffset{ 0 }; - - QMap defined; - void dump() { - if (defined["bufferView"]) { - qCDebug(modelformat) << "bufferView: " << bufferView; - } - if (defined["byteOffset"]) { - qCDebug(modelformat) << "byteOffset: " << byteOffset; - } - } - }; - - int count; - GLTFAccessorSparseIndices indices; - GLTFAccessorSparseValues values; - - QMap defined; - void dump() { - - } - }; - int bufferView; - int byteOffset { 0 }; - int componentType; //required - int count; //required - int type; //required - bool normalized { false }; - QVector max; - QVector min; - GLTFAccessorSparse sparse; - QMap defined; - void dump() { - if (defined["bufferView"]) { - qCDebug(modelformat) << "bufferView: " << bufferView; - } - if (defined["byteOffset"]) { - qCDebug(modelformat) << "byteOffset: " << byteOffset; - } - if (defined["componentType"]) { - qCDebug(modelformat) << "componentType: " << componentType; - } - if (defined["count"]) { - qCDebug(modelformat) << "count: " << count; - } - if (defined["type"]) { - qCDebug(modelformat) << "type: " << type; - } - if (defined["normalized"]) { - qCDebug(modelformat) << "normalized: " << (normalized ? "TRUE" : "FALSE"); - } - if (defined["max"]) { - qCDebug(modelformat) << "max: "; - foreach(float m, max) { - qCDebug(modelformat) << m; - } - } - if (defined["min"]) { - qCDebug(modelformat) << "min: "; - foreach(float m, min) { - qCDebug(modelformat) << m; - } - } - if (defined["sparse"]) { - qCDebug(modelformat) << "sparse: "; - sparse.dump(); - } - } -}; - -// Animation - -namespace GLTFChannelTargetPath { - enum Values { - TRANSLATION = 0, - ROTATION, - SCALE - }; -} - -struct GLTFChannelTarget { - int node; - int path; - QMap defined; - void dump() { - if (defined["node"]) { - qCDebug(modelformat) << "node: " << node; - } - if (defined["path"]) { - qCDebug(modelformat) << "path: " << path; - } - } -}; - -struct GLTFChannel { - int sampler; - GLTFChannelTarget target; - QMap defined; - void dump() { - if (defined["sampler"]) { - qCDebug(modelformat) << "sampler: " << sampler; - } - if (defined["target"]) { - target.dump(); - } - } -}; - -namespace GLTFAnimationSamplerInterpolation { - enum Values{ - LINEAR = 0 - }; -} - -struct GLTFAnimationSampler { - int input; - int output; - int interpolation; - QMap defined; - void dump() { - if (defined["input"]) { - qCDebug(modelformat) << "input: " << input; - } - if (defined["output"]) { - qCDebug(modelformat) << "output: " << output; - } - if (defined["interpolation"]) { - qCDebug(modelformat) << "interpolation: " << interpolation; - } - } -}; - -struct GLTFAnimation { - QVector channels; - QVector samplers; - QMap defined; - void dump() { - if (defined["channels"]) { - foreach(auto channel, channels) channel.dump(); - } - if (defined["samplers"]) { - foreach(auto sampler, samplers) sampler.dump(); - } - } -}; - -struct GLTFScene { - QString name; - QVector nodes; - QMap defined; - void dump() { - if (defined["name"]) { - qCDebug(modelformat) << "name: " << name; - } - if (defined["nodes"]) { - qCDebug(modelformat) << "nodes: "; - foreach(int node, nodes) qCDebug(modelformat) << node; - } - } -}; - -struct GLTFSkin { - int inverseBindMatrices; - QVector joints; - int skeleton; - QMap defined; - void dump() { - if (defined["inverseBindMatrices"]) { - qCDebug(modelformat) << "inverseBindMatrices: " << inverseBindMatrices; - } - if (defined["skeleton"]) { - qCDebug(modelformat) << "skeleton: " << skeleton; - } - if (defined["joints"]) { - qCDebug(modelformat) << "joints: "; - foreach(int joint, joints) qCDebug(modelformat) << joint; - } - } -}; - -struct GLTFTexture { - int sampler; - int source; - QMap defined; - void dump() { - if (defined["sampler"]) { - qCDebug(modelformat) << "sampler: " << sampler; - } - if (defined["source"]) { - qCDebug(modelformat) << "source: " << sampler; - } - } -}; - -struct GLTFFile { - GLTFAsset asset; - int scene = 0; - QVector accessors; - QVector animations; - QVector bufferviews; - QVector buffers; - QVector cameras; - QVector images; - QVector materials; - QVector meshes; - QVector nodes; - QVector samplers; - QVector scenes; - QVector skins; - QVector textures; - QMap defined; - void dump() { - if (defined["asset"]) { - asset.dump(); - } - if (defined["scene"]) { - qCDebug(modelformat) << "scene: " << scene; - } - if (defined["accessors"]) { - foreach(auto acc, accessors) acc.dump(); - } - if (defined["animations"]) { - foreach(auto ani, animations) ani.dump(); - } - if (defined["bufferviews"]) { - foreach(auto bv, bufferviews) bv.dump(); - } - if (defined["buffers"]) { - foreach(auto b, buffers) b.dump(); - } - if (defined["cameras"]) { - foreach(auto c, cameras) c.dump(); - } - if (defined["images"]) { - foreach(auto i, images) i.dump(); - } - if (defined["materials"]) { - foreach(auto mat, materials) mat.dump(); - } - if (defined["meshes"]) { - foreach(auto mes, meshes) mes.dump(); - } - if (defined["nodes"]) { - foreach(auto nod, nodes) nod.dump(); - } - if (defined["samplers"]) { - foreach(auto sa, samplers) sa.dump(); - } - if (defined["scenes"]) { - foreach(auto sc, scenes) sc.dump(); - } - if (defined["skins"]) { - foreach(auto sk, nodes) sk.dump(); - } - if (defined["textures"]) { - foreach(auto tex, textures) tex.dump(); - } - } -}; class GLTFSerializer : public QObject, public HFMSerializer { Q_OBJECT @@ -769,79 +33,19 @@ public: std::unique_ptr getFactory() const override; HFMModel::Pointer read(const hifi::ByteArray& data, const hifi::VariantHash& mapping, const hifi::URL& url = hifi::URL()) override; + ~GLTFSerializer(); private: - GLTFFile _file; + cgltf_data* _data {nullptr}; hifi::URL _url; - hifi::ByteArray _glbBinary; + QVector _externalData; - glm::mat4 getModelTransform(const GLTFNode& node); - void getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues); - void generateTargetData(int index, float weight, QVector& returnVector); + glm::mat4 getModelTransform(const cgltf_node& node); + bool getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues); + bool generateTargetData(cgltf_accessor *accessor, float weight, QVector& returnVector); bool buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mapping, const hifi::URL& url); - bool parseGLTF(const hifi::ByteArray& data); - bool getStringVal(const QJsonObject& object, const QString& fieldname, - QString& value, QMap& defined); - bool getBoolVal(const QJsonObject& object, const QString& fieldname, - bool& value, QMap& defined); - bool getIntVal(const QJsonObject& object, const QString& fieldname, - int& value, QMap& defined); - bool getDoubleVal(const QJsonObject& object, const QString& fieldname, - double& value, QMap& defined); - bool getObjectVal(const QJsonObject& object, const QString& fieldname, - QJsonObject& value, QMap& defined); - bool getIntArrayVal(const QJsonObject& object, const QString& fieldname, - QVector& values, QMap& defined); - bool getDoubleArrayVal(const QJsonObject& object, const QString& fieldname, - QVector& values, QMap& defined); - bool getObjectArrayVal(const QJsonObject& object, const QString& fieldname, - QJsonArray& objects, QMap& defined); - - hifi::ByteArray setGLBChunks(const hifi::ByteArray& data); - - graphics::MaterialKey::OpacityMapMode getMaterialAlphaMode(const QString& type); - int getAccessorType(const QString& type); - int getAnimationSamplerInterpolation(const QString& interpolation); - int getCameraType(const QString& type); - int getImageMimeType(const QString& mime); - int getMeshPrimitiveRenderingMode(const QString& type); - - bool getIndexFromObject(const QJsonObject& object, const QString& field, - int& outidx, QMap& defined); - - bool setAsset(const QJsonObject& object); - - GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices createAccessorSparseIndices(const QJsonObject& object); - GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues createAccessorSparseValues(const QJsonObject& object); - GLTFAccessor::GLTFAccessorSparse createAccessorSparse(const QJsonObject& object); - - bool addAccessor(const QJsonObject& object); - bool addAnimation(const QJsonObject& object); - bool addBufferView(const QJsonObject& object); - bool addBuffer(const QJsonObject& object); - bool addCamera(const QJsonObject& object); - bool addImage(const QJsonObject& object); - bool addMaterial(const QJsonObject& object); - bool addMesh(const QJsonObject& object); - bool addNode(const QJsonObject& object); - bool addSampler(const QJsonObject& object); - bool addScene(const QJsonObject& object); - bool addSkin(const QJsonObject& object); - bool addTexture(const QJsonObject& object); - - bool readBinary(const QString& url, hifi::ByteArray& outdata); - - template - bool readArray(const hifi::ByteArray& bin, int byteOffset, int count, - QVector& outarray, int accessorType, bool normalized); - - template - bool addArrayOfType(const hifi::ByteArray& bin, int byteOffset, int count, - QVector& outarray, int accessorType, int componentType, bool normalized); - - template - bool addArrayFromAccessor(GLTFAccessor& accessor, QVector& outarray); + bool readBinary(const QString& url, cgltf_buffer &buffer); void retriangulate(const QVector& in_indices, const QVector& in_vertices, const QVector& in_normals, QVector& out_indices, @@ -851,12 +55,9 @@ private: hifi::ByteArray requestEmbeddedData(const QString& url); QNetworkReply* request(hifi::URL& url, bool isTest); - bool doesResourceExist(const QString& url); - void setHFMMaterial(HFMMaterial& hfmMat, const GLTFMaterial& material); - HFMTexture getHFMTexture(const GLTFTexture& texture); - - void glTFDebugDump(); + void setHFMMaterial(HFMMaterial& hfmMat, const cgltf_material& material); + HFMTexture getHFMTexture(const cgltf_texture *texture); }; #endif // hifi_GLTFSerializer_h diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index cab8cde832..5bb530d332 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -42,7 +42,7 @@ static const int HASH_ITERATIONS = 65535; // Salt string for the hardware ID, makes our IDs unique to our project. // Changing this results in different hardware IDs being computed. -static const QByteArray HASH_SALT{"Vircadia"}; +static const QByteArray HASH_SALT{"Overte"}; static const QString FALLBACK_FINGERPRINT_KEY = "fallbackFingerprint"; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index e4d3f0a207..d182e7ea94 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -41,7 +41,7 @@ #if defined(Q_OS_WIN) #include -#else +#else #include #endif @@ -197,6 +197,10 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) { newPermissions.can(NodePermissions::Permission::canRezAvatarEntities)) { emit canRezAvatarEntitiesChanged(_permissions.can(NodePermissions::Permission::canRezAvatarEntities)); } + if (originalPermissions.can(NodePermissions::Permission::canViewAssetURLs) != + newPermissions.can(NodePermissions::Permission::canViewAssetURLs)) { + emit canViewAssetURLsChanged(_permissions.can(NodePermissions::Permission::canViewAssetURLs)); + } } void LimitedNodeList::setSocketLocalPort(SocketType socketType, quint16 socketLocalPort) { @@ -497,7 +501,7 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi } return bytesSent; } else { - qCDebug(networking) << "LimitedNodeList::sendUnreliableUnorderedPacketList called without active socket for node" + qCDebug(networking) << "LimitedNodeList::sendUnreliableUnorderedPacketList called without active socket for node" << destinationNode << " - not sending."; return ERROR_SENDING_PACKET_BYTES; } @@ -1139,7 +1143,7 @@ void LimitedNodeList::startSTUNPublicSocketUpdate() { if (_stunSockAddr.getAddress().isNull()) { // if we fail to lookup the socket then timeout the STUN address lookup - connect(&_stunSockAddr, &SockAddr::lookupFailed, this, &LimitedNodeList::possiblyTimeoutSTUNAddressLookup); + connect(&_stunSockAddr, &SockAddr::lookupFailed, this, &LimitedNodeList::STUNAddressLookupFailed); // immediately send a STUN request once we know the socket connect(&_stunSockAddr, &SockAddr::lookupCompleted, this, &LimitedNodeList::sendSTUNRequest); @@ -1153,7 +1157,7 @@ void LimitedNodeList::startSTUNPublicSocketUpdate() { QTimer* lookupTimeoutTimer = new QTimer { this }; lookupTimeoutTimer->setSingleShot(true); - connect(lookupTimeoutTimer, &QTimer::timeout, this, &LimitedNodeList::possiblyTimeoutSTUNAddressLookup); + connect(lookupTimeoutTimer, &QTimer::timeout, this, &LimitedNodeList::STUNAddressLookupTimeout); // delete the lookup timeout timer once it has fired connect(lookupTimeoutTimer, &QTimer::timeout, lookupTimeoutTimer, &QTimer::deleteLater); @@ -1168,10 +1172,18 @@ void LimitedNodeList::startSTUNPublicSocketUpdate() { } } -void LimitedNodeList::possiblyTimeoutSTUNAddressLookup() { +void LimitedNodeList::STUNAddressLookupFailed() { + if (_stunSockAddr.getAddress().isNull()) { + // got a lookup failure + qCCritical(networking) << "PAGE: Failed to lookup address of STUN server" << STUN_SERVER_HOSTNAME; + stopInitialSTUNUpdate(false); + } +} + +void LimitedNodeList::STUNAddressLookupTimeout() { if (_stunSockAddr.getAddress().isNull()) { // our stun address is still NULL, but we've been waiting for long enough - time to force a fail - qCCritical(networking) << "PAGE: Failed to lookup address of STUN server" << STUN_SERVER_HOSTNAME; + qCCritical(networking) << "PAGE: Address lookup of STUN server" << STUN_SERVER_HOSTNAME << "timed out"; stopInitialSTUNUpdate(false); } } diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 0634538880..cdb742a3c3 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -132,6 +132,7 @@ public: bool getThisNodeCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); } bool getThisNodeCanGetAndSetPrivateUserData() const { return _permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData); } bool getThisNodeCanRezAvatarEntities() const { return _permissions.can(NodePermissions::Permission::canRezAvatarEntities); } + bool getThisNodeCanViewAssetURLs() const { return _permissions.can(NodePermissions::Permission::canViewAssetURLs); } quint16 getSocketLocalPort(SocketType socketType) const { return _nodeSocket.localPort(socketType); } Q_INVOKABLE void setSocketLocalPort(SocketType socketType, quint16 socketLocalPort); @@ -392,6 +393,7 @@ signals: void canReplaceContentChanged(bool canReplaceContent); void canGetAndSetPrivateUserDataChanged(bool canGetAndSetPrivateUserData); void canRezAvatarEntitiesChanged(bool canRezAvatarEntities); + void canViewAssetURLsChanged(bool canViewAssetURLs); protected slots: void connectedForLocalSocketTest(); @@ -481,7 +483,8 @@ protected: private slots: void flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp); - void possiblyTimeoutSTUNAddressLookup(); + void STUNAddressLookupTimeout(); + void STUNAddressLookupFailed(); void addSTUNHandlerToUnfiltered(); // called once STUN socket known private: diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index 4287da92d4..b10e09497a 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -58,6 +58,8 @@ namespace NetworkingConstants { const QString HF_PUBLIC_CDN_URL = ""; const QString HF_MARKETPLACE_CDN_HOSTNAME = ""; const QString OVERTE_CONTENT_CDN_URL = "https://content.overte.org/"; + const QString OVERTE_COMMUNITY_APPLICATIONS = { "https://more.overte.org/applications" }; + const QString OVERTE_TUTORIAL_SCRIPTS = { "https://more.overte.org/tutorial" }; #if USE_STABLE_GLOBAL_SERVICES const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.overte.org"; diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index cc056ea7d0..92a4c424c8 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -84,6 +84,7 @@ public: bool getCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); } bool getCanGetAndSetPrivateUserData() const { return _permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData); } bool getCanRezAvatarEntities() const { return _permissions.can(NodePermissions::Permission::canRezAvatarEntities); } + bool getCanViewAssetURLs() const { return _permissions.can(NodePermissions::Permission::canViewAssetURLs); } using NodesIgnoredPair = std::pair, bool>; diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index e7cf953645..8a23c7dbd3 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -68,6 +68,7 @@ NodePermissions::NodePermissions(QMap perms) { permissions |= perms["id_can_replace_content"].toBool() ? Permission::canReplaceDomainContent : Permission::none; permissions |= perms["id_can_get_and_set_private_user_data"].toBool() ? Permission::canGetAndSetPrivateUserData : Permission::none; + permissions |= perms["id_can_view_asset_urls"].toBool() ? Permission::canViewAssetURLs : Permission::none; } QVariant NodePermissions::toVariant(QHash groupRanks) { @@ -95,6 +96,7 @@ QVariant NodePermissions::toVariant(QHash groupRanks) { values["id_can_kick"] = can(Permission::canKick); values["id_can_replace_content"] = can(Permission::canReplaceDomainContent); values["id_can_get_and_set_private_user_data"] = can(Permission::canGetAndSetPrivateUserData); + values["id_can_view_asset_urls"] = can(Permission::canViewAssetURLs); return QVariant(values); } @@ -167,6 +169,9 @@ QDebug operator<<(QDebug debug, const NodePermissions& perms) { if (perms.can(NodePermissions::Permission::canGetAndSetPrivateUserData)) { debug << " get-and-set-private-user-data"; } + if (perms.can(NodePermissions::Permission::canViewAssetURLs)) { + debug << " can-view-asset-urls"; + } debug.nospace() << "]"; return debug.nospace(); } diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 29baf130e6..81eaf68457 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -79,7 +79,8 @@ public: canKick = 64, canReplaceDomainContent = 128, canGetAndSetPrivateUserData = 1024, - canRezAvatarEntities = 2048 + canRezAvatarEntities = 2048, + canViewAssetURLs = 4096 }; Q_DECLARE_FLAGS(Permissions, Permission) Permissions permissions; diff --git a/libraries/networking/src/SockAddr.cpp b/libraries/networking/src/SockAddr.cpp index 29fbde3934..1f049d14c7 100644 --- a/libraries/networking/src/SockAddr.cpp +++ b/libraries/networking/src/SockAddr.cpp @@ -67,12 +67,13 @@ SockAddr::SockAddr(SocketType socketType, const QString& hostname, quint16 hostO if (_address.protocol() != QAbstractSocket::IPv4Protocol) { // lookup the IP by the hostname if (shouldBlockForLookup) { - qCDebug(networking) << "Synchronously looking up IP address for hostname" << hostname; + qCDebug(networking) << "Synchronously looking up IP address for hostname" << hostname << "for" << socketType << "socket on port" << hostOrderPort; QHostInfo result = QHostInfo::fromName(hostname); handleLookupResult(result); } else { - int lookupID = QHostInfo::lookupHost(hostname, this, SLOT(handleLookupResult(QHostInfo))); - qCDebug(networking) << "Asynchronously looking up IP address for hostname" << hostname << "- lookup ID is" << lookupID; + qCDebug(networking) << "Asynchronously looking up IP address for hostname" << hostname << "for" << socketType << "socket on port" << hostOrderPort; + int lookupID = QHostInfo::lookupHost(hostname, this, &SockAddr::handleLookupResult); + qCDebug(networking) << "Lookup ID for " << hostname << "is" << lookupID; } } } @@ -95,6 +96,8 @@ bool SockAddr::operator==(const SockAddr& rhsSockAddr) const { } void SockAddr::handleLookupResult(const QHostInfo& hostInfo) { + qCDebug(networking) << "handleLookupResult for" << hostInfo.lookupId(); + if (hostInfo.error() != QHostInfo::NoError) { qCDebug(networking) << "Lookup failed for" << hostInfo.lookupId() << ":" << hostInfo.errorString(); emit lookupFailed(); diff --git a/libraries/networking/src/SocketType.h b/libraries/networking/src/SocketType.h index 399940654d..33da7c4ea0 100644 --- a/libraries/networking/src/SocketType.h +++ b/libraries/networking/src/SocketType.h @@ -36,6 +36,10 @@ public: } }; +inline QDebug operator<<(QDebug debug, SocketType type) { + debug << SocketTypeToString::socketTypeToString(type); + return debug; +} /// @} #endif // overte_SocketType_h diff --git a/libraries/networking/src/udt/NetworkSocket.cpp b/libraries/networking/src/udt/NetworkSocket.cpp index 179abe9f78..298455e33c 100644 --- a/libraries/networking/src/udt/NetworkSocket.cpp +++ b/libraries/networking/src/udt/NetworkSocket.cpp @@ -45,7 +45,7 @@ void NetworkSocket::setSocketOption(SocketType socketType, QAbstractSocket::Sock break; #endif default: - qCCritical(networking) << "Socket type not specified in setSocketOption()"; + qCCritical(networking) << "Socket type" << socketType << "not recognized in setSocketOption()"; } } @@ -58,7 +58,7 @@ QVariant NetworkSocket::socketOption(SocketType socketType, QAbstractSocket::Soc return _webrtcSocket.socketOption(option); #endif default: - qCCritical(networking) << "Socket type not specified in socketOption()"; + qCCritical(networking) << "Socket type" << socketType << "not recognized in socketOption()"; return ""; } } @@ -75,7 +75,7 @@ void NetworkSocket::bind(SocketType socketType, const QHostAddress& address, qui break; #endif default: - qCCritical(networking) << "Socket type not specified in bind()"; + qCCritical(networking) << "Socket type" << socketType << "not recognized in bind()"; } } @@ -90,7 +90,7 @@ void NetworkSocket::abort(SocketType socketType) { break; #endif default: - qCCritical(networking) << "Socket type not specified in abort()"; + qCCritical(networking) << "Socket type" << socketType << "not recognized in abort()"; } } @@ -104,7 +104,7 @@ quint16 NetworkSocket::localPort(SocketType socketType) const { return _webrtcSocket.localPort(); #endif default: - qCCritical(networking) << "Socket type not specified in localPort()"; + qCCritical(networking) << "Socket type" << socketType << "not recognized in localPort()"; return 0; } } @@ -119,7 +119,7 @@ qintptr NetworkSocket::socketDescriptor(SocketType socketType) const { return 0; #endif default: - qCCritical(networking) << "Socket type not specified in socketDescriptor()"; + qCCritical(networking) << "Socket type" << socketType << "not recognized in socketDescriptor()"; return 0; } } @@ -136,7 +136,7 @@ qint64 NetworkSocket::writeDatagram(const QByteArray& datagram, const SockAddr& return _webrtcSocket.writeDatagram(datagram, sockAddr); #endif default: - qCCritical(networking) << "Socket type not specified in writeDatagram() address"; + qCCritical(networking) << "Socket type" << sockAddr.getType() << "not recognized in writeDatagram() address"; return 0; } } @@ -150,7 +150,7 @@ qint64 NetworkSocket::bytesToWrite(SocketType socketType, const SockAddr& addres return _webrtcSocket.bytesToWrite(address); #endif default: - qCCritical(networking) << "Socket type not specified in bytesToWrite()"; + qCCritical(networking) << "Socket type" << socketType << "not recognized in bytesToWrite()"; return 0; } } @@ -232,7 +232,7 @@ QAbstractSocket::SocketState NetworkSocket::state(SocketType socketType) const { return _webrtcSocket.state(); #endif default: - qCCritical(networking) << "Socket type not specified in state()"; + qCCritical(networking) << "Socket type" << socketType << "not recognized in state()"; return QAbstractSocket::SocketState::UnconnectedState; } } @@ -247,7 +247,7 @@ QAbstractSocket::SocketError NetworkSocket::error(SocketType socketType) const { return _webrtcSocket.error(); #endif default: - qCCritical(networking) << "Socket type not specified in error()"; + qCCritical(networking) << "Socket type" << socketType << "not recognized in error()"; return QAbstractSocket::SocketError::UnknownSocketError; } } @@ -261,7 +261,7 @@ QString NetworkSocket::errorString(SocketType socketType) const { return _webrtcSocket.errorString(); #endif default: - qCCritical(networking) << "Socket type not specified in errorString()"; + qCCritical(networking) << "Socket type" << socketType << "not recognized in errorString()"; return ""; } } diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 4beeb5b22f..25cb1404f0 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -587,7 +587,7 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const } // it's ok to change offset immediately -- there are no thread safety issues here - _shapeLocalOffset = minCorner + 0.5f * scale; + _shapeLocalOffset = glm::vec3((minCorner + 0.5f * scale).x, (minCorner + 0.5f * scale).y, -(minCorner + 0.5f * scale).z); if (_rigidBody) { // update CCD with new _radius diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 8242ae4b97..90aa7ad74b 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -54,7 +54,7 @@ class CharacterController : public btCharacterControllerInterface { public: enum class FollowType : uint8_t { - Rotation, + Rotation = 0, Horizontal, Vertical, Count diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index f3d129871f..ac36d37aaa 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -57,10 +57,10 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer _type = MOTIONSTATE_TYPE_ENTITY; assert(_entity); - setMass(_entity->computeMass()); // we need the side-effects of EntityMotionState::setShape() so we call it explicitly here // rather than pass the legit shape pointer to the ObjectMotionState ctor above. setShape(shape); + setMass(_entity->computeMass()); if (_entity->isAvatarEntity() && !_entity->isMyAvatarEntity()) { // avatar entities are always thus, so we cache this fact in _ownershipState @@ -178,6 +178,11 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { _body->activate(); } } + + if (flags & Simulation::DIRTY_MASS) { + setMass(_entity->computeMass()); + updateBodyMassProperties(); + } } diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index ad7332cb15..5376d296aa 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -284,10 +284,6 @@ void ObjectMotionState::handleEasyChanges(uint32_t& flags) { if (flags & Simulation::DIRTY_MATERIAL) { updateBodyMaterialProperties(); } - - if (flags & Simulation::DIRTY_MASS) { - updateBodyMassProperties(); - } } void ObjectMotionState::updateBodyMaterialProperties() { diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp index c1ca853563..0281a012d2 100644 --- a/libraries/plugins/src/plugins/PluginManager.cpp +++ b/libraries/plugins/src/plugins/PluginManager.cpp @@ -72,12 +72,13 @@ int getPluginInterfaceVersionFromMetaData(const QJsonObject& object) { QStringList preferredDisplayPlugins; QStringList disabledDisplays; QStringList disabledInputs; +std::vector pluginInfo; bool isDisabled(QJsonObject metaData) { auto name = getPluginNameFromMetaData(metaData); auto iid = getPluginIIDFromMetaData(metaData); - if (iid == DisplayProvider_iid) { + if (iid == DisplayProvider_iid || iid == SteamClientProvider_iid || iid == OculusPlatformProvider_iid) { return disabledDisplays.contains(name); } else if (iid == InputProvider_iid) { return disabledInputs.contains(name); @@ -126,18 +127,28 @@ int PluginManager::instantiate() { qCDebug(plugins) << "Attempting plugin" << qPrintable(plugin); auto loader = QSharedPointer::create(pluginPath + plugin); const QJsonObject pluginMetaData = loader->metaData(); + + PluginInfo info; + info.name = plugin; + info.metaData = pluginMetaData; + #if defined(HIFI_PLUGINMANAGER_DEBUG) QJsonDocument metaDataDoc(pluginMetaData); qCInfo(plugins) << "Metadata for " << qPrintable(plugin) << ": " << QString(metaDataDoc.toJson()); #endif if (isDisabled(pluginMetaData)) { qCWarning(plugins) << "Plugin" << qPrintable(plugin) << "is disabled"; + info.disabled = true; + pluginInfo.push_back(info); + // Skip this one, it's disabled continue; } if (!_pluginFilter(pluginMetaData)) { qCDebug(plugins) << "Plugin" << qPrintable(plugin) << "doesn't pass provided filter"; + info.filteredOut = true; + pluginInfo.push_back(info); continue; } @@ -145,16 +156,22 @@ int PluginManager::instantiate() { qCWarning(plugins) << "Plugin" << qPrintable(plugin) << "interface version doesn't match, not loading:" << getPluginInterfaceVersionFromMetaData(pluginMetaData) << "doesn't match" << HIFI_PLUGIN_INTERFACE_VERSION; + + info.wrongVersion = true; + pluginInfo.push_back(info); continue; } if (loader->load()) { qCDebug(plugins) << "Plugin" << qPrintable(plugin) << "loaded successfully"; + info.loaded = true; loadedPlugins.push_back(loader); } else { qCDebug(plugins) << "Plugin" << qPrintable(plugin) << "failed to load:"; qCDebug(plugins) << " " << qPrintable(loader->errorString()); } + + pluginInfo.push_back(info); } } else { qWarning() << "pluginPath does not exit..." << pluginDir; @@ -163,6 +180,11 @@ int PluginManager::instantiate() { return loadedPlugins; } +std::vector PluginManager::getPluginInfo() const { + getLoadedPlugins(); // This builds the pluginInfo list + return pluginInfo; +} + const CodecPluginList& PluginManager::getCodecPlugins() { static CodecPluginList codecPlugins; static std::once_flag once; @@ -272,14 +294,6 @@ DisplayPluginList PluginManager::getAllDisplayPlugins() { return _displayPlugins; } -void PluginManager::disableDisplayPlugin(const QString& name) { - auto it = std::remove_if(_displayPlugins.begin(), _displayPlugins.end(), [&](const DisplayPluginPointer& plugin){ - return plugin->getName() == name; - }); - _displayPlugins.erase(it, _displayPlugins.end()); -} - - const InputPluginList& PluginManager::getInputPlugins() { static std::once_flag once; static auto deviceAddedCallback = [&](QString deviceName) { diff --git a/libraries/plugins/src/plugins/PluginManager.h b/libraries/plugins/src/plugins/PluginManager.h index 26c98ce5db..b529472e1f 100644 --- a/libraries/plugins/src/plugins/PluginManager.h +++ b/libraries/plugins/src/plugins/PluginManager.h @@ -12,54 +12,268 @@ #include #include +#include +#include #include "Forward.h" class QPluginLoader; using PluginManagerPointer = QSharedPointer; +/** + * @brief Manages loadable plugins + * + * The current implementation does initialization only once, as soon as it's needed. + * Once things are initialized the configuration is made permanent. + * + * Both loadable and statically modules are supported. Static modules have to be provided + * with setDisplayPluginProvider, setInputPluginProvider and setCodecPluginProvider. + * + * @warning Users of the PluginManager must take care to do any configuration very early + * on, because changes become impossible once initialization is done. Plugins can't be + * added or removed once that happens. + * + * Initialization is performed in the getDisplayPlugins, getInputPlugins and getCodecPlugins + * functions. + */ class PluginManager : public QObject, public Dependency { SINGLETON_DEPENDENCY Q_OBJECT public: + + /** + * @brief Information about known plugins + * + */ + struct PluginInfo { + /** + * @brief Plugin metadata + */ + QJsonObject metaData; + + /** + * @brief Filename + * + */ + QString name; + + /** + * @brief Whether the plugin has been disabled + * + */ + bool disabled = false; + + /** + * @brief Whether the plugin has been filtered out by a filter + * + */ + bool filteredOut = false; + + /** + * @brief Whether the plugin has been not loaded because it's the wrong version + * + */ + bool wrongVersion = false; + + /** + * @brief Whether the plugin has been loaded successfully + * + */ + bool loaded = false; + }; + + static PluginManagerPointer getInstance(); + /** + * @brief Get the list of display plugins + * + * @note Calling this function will perform initialization and + * connects events to all the known the plugins on the first call. + * + * @return const DisplayPluginList& + */ const DisplayPluginList& getDisplayPlugins(); + + /** + * @brief Get the list of input plugins + * + * @note Calling this function will perform initialization and + * connects events to all the known the plugins on the first call. + * + * @return const InputPluginList& + */ const InputPluginList& getInputPlugins(); + + /** + * @brief Get the list of audio codec plugins + * + * @note Calling this function will perform initialization and + * connects events to all the known the plugins on the first call. + * + * @return const CodecPluginList& + */ const CodecPluginList& getCodecPlugins(); + + /** + * @brief Get the pointer to the Steam client plugin + * + * This may return a null pointer if Steam support isn't built in. + * + * @return const SteamClientPluginPointer + */ const SteamClientPluginPointer getSteamClientPlugin(); + + /** + * @brief Get the pointer to the Oculus Platform Plugin + * + * This may return a null pointer if Oculus support isn't built in. + * + * @return const OculusPlatformPluginPointer + */ const OculusPlatformPluginPointer getOculusPlatformPlugin(); + /** + * @brief Returns the list of preferred display plugins + * + * The preferred display plugins are set by setPreferredDisplayPlugins. + * + * @return DisplayPluginList + */ DisplayPluginList getPreferredDisplayPlugins(); + + /** + * @brief Sets the list of preferred display plugins + * + * @note This must be called early, before any call to getPreferredDisplayPlugins. + * + * @param displays + */ void setPreferredDisplayPlugins(const QStringList& displays); - void disableDisplayPlugin(const QString& name); + /** + * @brief Disable a list of displays + * + * This adds the display to a list of displays not to be used. + * + * @param displays + */ void disableDisplays(const QStringList& displays); + + /** + * @brief Disable a list of inputs + * + * This adds the input to a list of inputs not to be used. + * @param inputs + */ void disableInputs(const QStringList& inputs); + + /** + * @brief Save the settings + * + */ void saveSettings(); + + /** + * @brief Set the container for plugins + * + * This will be passed to all active plugins on initialization. + * + * @param container + */ void setContainer(PluginContainer* container) { _container = container; } int instantiate(); void shutdown(); - // Application that have statically linked plugins can expose them to the plugin manager with these function + + /** + * @brief Provide a list of statically linked plugins. + * + * This is used to provide a list of statically linked plugins to the plugin manager. + * + * @note This must be called very early on, and only works once. Once the plugin manager + * builds its internal list of plugins, the final list becomes set in stone. + * + * @param provider A std::function that returns a list of display plugins + */ void setDisplayPluginProvider(const DisplayPluginProvider& provider); + + /** + * @brief Provide a list of statically linked plugins. + * + * This is used to provide a list of statically linked plugins to the plugin manager. + * + * @note This must be called very early on, and only works once. Once the plugin manager + * builds its internal list of plugins, the final list becomes set in stone. + * + * @param provider A std::function that returns a list of input plugins + */ void setInputPluginProvider(const InputPluginProvider& provider); + + /** + * @brief Provide a list of statically linked plugins. + * + * This is used to provide a list of statically linked plugins to the plugin manager. + * + * @note This must be called very early on, and only works once. Once the plugin manager + * builds its internal list of plugins, the final list becomes set in stone. + * + * @param provider A std::function that returns a list of codec plugins + */ void setCodecPluginProvider(const CodecPluginProvider& provider); + + /** + * @brief Set the input plugin persister + * + * @param persister A std::function that saves input plugin settings + */ void setInputPluginSettingsPersister(const InputPluginSettingsPersister& persister); + + /** + * @brief Get the list of running input devices + * + * @return QStringList List of input devices in running state + */ QStringList getRunningInputDeviceNames() const; using PluginFilter = std::function; + + /** + * @brief Set the plugin filter that determines whether a plugin will be used or not + * + * @note This must be called very early on. Once the plugin manager + * builds its internal list of plugins, the final list becomes set in stone. + * + * As of writing, this is used in the audio mixer. + * + * @param pluginFilter + */ void setPluginFilter(PluginFilter pluginFilter) { _pluginFilter = pluginFilter; } + + /** + * @brief Get a list of all the display plugins + * + * @return DisplayPluginList List of display plugins + */ Q_INVOKABLE DisplayPluginList getAllDisplayPlugins(); bool getEnableOculusPluginSetting() { return _enableOculusPluginSetting.get(); } void setEnableOculusPluginSetting(bool value); + /** + * @brief Returns information about known plugins + * + * This is a function for informative/debugging purposes. + * + * @return std::vector + */ + std::vector getPluginInfo() const; + signals: void inputDeviceRunningChanged(const QString& pluginName, bool isRunning, const QStringList& runningDevices); - + private: PluginManager() = default; diff --git a/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp b/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp index f175a65452..f76200ea57 100644 --- a/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp +++ b/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp @@ -1,6 +1,7 @@ // // Created by Sam Gondelman on 2/9/2018 // Copyright 2018 High Fidelity, Inc. +// Copyright 2024 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 @@ -128,36 +129,35 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseMaterialF * @typedef {object} Entities.Material * @property {string} name="" - A name for the material. Supported by all material models. * @property {string} model="hifi_pbr" - Different material models support different properties and rendering modes. - * Supported models are: "hifi_pbr", "hifi_shader_simple". + * Supported models are: "hifi_pbr", "hifi_shader_simple", and "vrm_mtoon". * @property {ColorFloat|RGBS|string} emissive - The emissive color, i.e., the color that the material emits. A * {@link ColorFloat} value is treated as sRGB and must have component values in the range 0.0 – * 1.0. A {@link RGBS} value can be either RGB or sRGB. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr", + * "vrm_mtoon". * @property {number|string} opacity=1.0 - The opacity, range 0.01.0. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" and - * "hifi_shader_simple" models only. + * Set to "fallthrough" to fall through to the material below. Supported models: all. * @property {boolean|string} unlit=false - true if the material is unaffected by lighting, false if * it is lit by the key light and local lights. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {ColorFloat|RGBS|string} albedo - The albedo color. A {@link ColorFloat} value is treated as sRGB and must have * component values in the range 0.01.0. A {@link RGBS} value can be either RGB or sRGB. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" and - * "hifi_shader_simple" models only. + * Set to "fallthrough" to fall through to the material below. Supported models: all. * @property {number|string} roughness - The roughness, range 0.01.0. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {number|string} metallic - The metallicness, range 0.01.0. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {number|string} scattering - The scattering, range 0.01.0. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {string} emissiveMap - The URL of the emissive texture image, or an entity ID. An entity ID may be that of an - * Image or Web entity. Set to "fallthrough" to fall through to the material below. - * "hifi_pbr" model only. + * Image or Web entity. Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr", + * "vrm_mtoon". * @property {string} albedoMap - The URL of the albedo texture image, or an entity ID. An entity ID may be that of an Image - * or Web entity. Set to "fallthrough" to fall through to the material below. "hifi_pbr" - * model only. + * or Web entity. Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr", + * "vrm_mtoon". * @property {string} opacityMap - The URL of the opacity texture image, or an entity ID. An entity ID may be that of an Image - * or Web entity. Set the value the same as the albedoMap value for transparency. - * "hifi_pbr" model only. + * or Web entity. Set the value the same as the albedoMap value for transparency. Supported models: "hifi_pbr", + * "vrm_mtoon". * @property {string} opacityMapMode - The mode defining the interpretation of the opacity map. Values can be: *
    *
  • "OPACITY_MAP_OPAQUE" for ignoring the opacity map information.
  • @@ -166,67 +166,113 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseMaterialF *
  • "OPACITY_MAP_BLEND" for using the opacityMap for alpha blending the material surface * with the background.
  • *
- * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: all. * @property {number|string} opacityCutoff - The opacity cutoff threshold used to determine the opaque texels of the * opacityMap when opacityMapMode is "OPACITY_MAP_MASK". Range 0.0 * – 1.0. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: all. * @property {string} cullFaceMode="CULL_BACK" - The mode defining which side of the geometry should be rendered. Values can be: *
    *
  • "CULL_NONE" to render both sides of the geometry.
  • *
  • "CULL_FRONT" to cull the front faces of the geometry.
  • *
  • "CULL_BACK" (the default) to cull the back faces of the geometry.
  • *
- * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. - * @property {string} cullFaceMode - The mode defining which side of the geometry should be rendered. Values can be: - *
    - *
  • "CULL_NONE" for rendering both sides of the geometry.
  • - *
  • "CULL_FRONT" for culling the front faces of the geometry.
  • - *
  • "CULL_BACK" (the default) for culling the back faces of the geometry.
  • - *
- * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: all. * @property {string} roughnessMap - The URL of the roughness texture image. You can use this or glossMap, but not * both. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {string} glossMap - The URL of the gloss texture image. You can use this or roughnessMap, but not * both. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {string} metallicMap - The URL of the metallic texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. You can use this or specularMap, but not both. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {string} specularMap - The URL of the specular texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. You can use this or metallicMap, but not both. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {string} normalMap - The URL of the normal texture image, or an entity ID. An entity ID may be that of an Image * or Web entity. You can use this or bumpMap, but not both. Set to "fallthrough" to fall - * through to the material below. "hifi_pbr" model only. + * through to the material below. Supported models: "hifi_pbr", "vrm_mtoon". * @property {string} bumpMap - The URL of the bump texture image, or an entity ID. An entity ID may be that of an Image * or Web entity. You can use this or normalMap, but not both. Set to "fallthrough" to - * fall through to the material below. "hifi_pbr" model only. + * fall through to the material below. Supported models: "hifi_pbr", "vrm_mtoon". * @property {string} occlusionMap - The URL of the occlusion texture image, or an entity ID. An entity ID may be that of * an Image or Web entity. Set to "fallthrough" to fall through to the material below. - * "hifi_pbr" model only. + * Supported models: "hifi_pbr". * @property {string} scatteringMap - The URL of the scattering texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. Only used if normalMap or bumpMap is specified. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {string} lightMap - The URL of the light map texture image, or an entity ID. An entity ID may be that of an Image - * or Web entity. Set to "fallthrough" to fall through to the material below. "hifi_pbr" - * model only. + * or Web entity. Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {Mat4|string} texCoordTransform0 - The transform to use for all of the maps apart from occlusionMap * and lightMap. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr", "vrm_mtoon". * @property {Mat4|string} texCoordTransform1 - The transform to use for occlusionMap and lightMap. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr", "vrm_mtoon". * @property {string} lightmapParams - Parameters for controlling how lightMap is used. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". *

Currently not used.

* @property {string} materialParams - Parameters for controlling the material projection and repetition. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr", "vrm_mtoon". *

Currently not used.

* @property {boolean} defaultFallthrough=false - true if all properties fall through to the material below * unless they are set, false if properties respect their individual fall-through settings. - * "hifi_pbr" and "hifi_shader_simple" models only. - * @property {ProceduralData} procedural - The definition of a procedural shader material. "hifi_shader_simple" model only. + * Supported models: all. + * @property {ProceduralData} procedural - The definition of a procedural shader material. Supported models: "hifi_shader_simple". + * @property {ColorFloat|RGBS|string} shade - The shade color. A {@link ColorFloat} value is treated as sRGB and must have + * component values in the range 0.01.0. A {@link RGBS} value can be either RGB or sRGB. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {string} shadeMap - The URL of the shade texture image, or an entity ID. An entity ID may be that of an + * Image or Web entity. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} shadingShift - The shading shift. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {string} shadingShiftMap - The URL of the shading shift texture image, or an entity ID. An entity ID may be that of an + * Image or Web entity. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} shadingToony - The shading toony factor. Range 0.01.0. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {ColorFloat|RGBS|string} matcap - The matcap color. A {@link ColorFloat} value is treated as sRGB and must have + * component values in the range 0.01.0. A {@link RGBS} value can be either RGB or sRGB. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {string} matcapMap - The URL of the matcap texture image, or an entity ID. An entity ID may be that of an + * Image or Web entity. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {ColorFloat|RGBS|string} parametricRim - The rim color. A {@link ColorFloat} value is treated as sRGB and must have + * component values in the range 0.01.0. A {@link RGBS} value can be either RGB or sRGB. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} parametricRimFresnelPower - The parametric rim fresnel exponent. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} parametricRimLift - The parametric rim lift factor. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {string} rimMap - The URL of the rim texture image, or an entity ID. An entity ID may be that of an + * Image or Web entity. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} rimLightingMix - How much to mix between the rim color and normal lighting. Range 0.0 + * – 1.0. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {string} outlineWidthMode="none" - The mode defining how to render the outline. Values can be: + *
    + *
  • "none" (the default) to not render an outline.
  • + *
  • "worldCoordinates" to render an outline with a constant world size, i.e. its apparent size depends on distance.
  • + *
  • "screenCoordinates" to render an outline with a constant screen size, i.e. its apparent size remains constant.
  • + *
+ * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} outlineWidth - The width of the outline, in meters if outlineWidthMode is "worldCoordinates", + * or a ratio of the screen height if outlineWidthMode is "screenCoordinates". + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {ColorFloat|RGBS|string} outline - The outline color. A {@link ColorFloat} value is treated as sRGB and must have + * component values in the range 0.01.0. A {@link RGBS} value can be either RGB or sRGB. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {string} uvAnimationMaskMap - The URL of the UV animation mask texture image, or an entity ID. An entity ID may be that of an + * Image or Web entity. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} uvAnimationScrollXSpeed - The speed of the UV scrolling animation in the X dimension, in UV units per second. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} uvAnimationScrollYSpeed - The speed of the UV scrolling animation in the Y dimension, in UV units per second. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} uvAnimationRotationSpeed - The speed of the UV scrolling rotation about (0.5, 0.5), in radians per second. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". */ // Note: See MaterialEntityItem.h for default values used in practice. std::pair> NetworkMaterialResource::parseJSONMaterial(const QJsonValue& materialJSONValue, const QUrl& baseUrl) { @@ -254,8 +300,13 @@ std::pair> NetworkMaterialResource std::array texcoordTransforms; const QString FALLTHROUGH("fallthrough"); - if (modelString == graphics::Material::HIFI_PBR) { - auto material = std::make_shared(); + if (modelString == graphics::Material::HIFI_PBR || modelString == graphics::Material::VRM_MTOON) { + std::shared_ptr material; + if (modelString == graphics::Material::HIFI_PBR) { + material = std::make_shared(); + } else { + material = std::make_shared(); + } for (auto& key : materialJSON.keys()) { if (key == "name") { auto nameJSON = materialJSON.value(key); @@ -282,13 +333,6 @@ std::pair> NetworkMaterialResource } else if (value.isDouble()) { material->setOpacity(value.toDouble()); } - } else if (key == "unlit") { - auto value = materialJSON.value(key); - if (value.isString() && value.toString() == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::UNLIT_VAL_BIT); - } else if (value.isBool()) { - material->setUnlit(value.toBool()); - } } else if (key == "albedo") { auto value = materialJSON.value(key); if (value.isString() && value.toString() == FALLTHROUGH) { @@ -301,21 +345,7 @@ std::pair> NetworkMaterialResource material->setAlbedo(color, isSRGB); } } - } else if (key == "roughness") { - auto value = materialJSON.value(key); - if (value.isString() && value.toString() == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::GLOSSY_VAL_BIT); - } else if (value.isDouble()) { - material->setRoughness(value.toDouble()); - } - } else if (key == "metallic") { - auto value = materialJSON.value(key); - if (value.isString() && value.toString() == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_VAL_BIT); - } else if (value.isDouble()) { - material->setMetallic(value.toDouble()); - } - } else if (key == "opacityMapMode") { + } else if (key == "opacityMapMode") { auto value = materialJSON.value(key); if (value.isString()) { auto valueString = value.toString(); @@ -348,14 +378,7 @@ std::pair> NetworkMaterialResource } } } - } else if (key == "scattering") { - auto value = materialJSON.value(key); - if (value.isString() && value.toString() == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::SCATTERING_VAL_BIT); - } else if (value.isDouble()) { - material->setScattering(value.toDouble()); - } - } else if (key == "emissiveMap") { + } else if (key == "emissiveMap") { auto value = materialJSON.value(key); if (value.isString()) { auto valueString = value.toString(); @@ -380,46 +403,6 @@ std::pair> NetworkMaterialResource material->setAlbedoMap(baseUrl.resolved(valueString), useAlphaChannel); } } - } else if (key == "roughnessMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT); - } else { - material->setRoughnessMap(baseUrl.resolved(valueString), false); - } - } - } else if (key == "glossMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT); - } else { - material->setRoughnessMap(baseUrl.resolved(valueString), true); - } - } - } else if (key == "metallicMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT); - } else { - material->setMetallicMap(baseUrl.resolved(valueString), false); - } - } - } else if (key == "specularMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT); - } else { - material->setMetallicMap(baseUrl.resolved(valueString), true); - } - } } else if (key == "normalMap") { auto value = materialJSON.value(key); if (value.isString()) { @@ -440,36 +423,6 @@ std::pair> NetworkMaterialResource material->setNormalMap(baseUrl.resolved(valueString), true); } } - } else if (key == "occlusionMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OCCLUSION_MAP_BIT); - } else { - material->setOcclusionMap(baseUrl.resolved(valueString)); - } - } - } else if (key == "scatteringMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::SCATTERING_MAP_BIT); - } else { - material->setScatteringMap(baseUrl.resolved(valueString)); - } - } - } else if (key == "lightMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::LIGHT_MAP_BIT); - } else { - material->setLightMap(baseUrl.resolved(valueString)); - } - } } else if (key == "texCoordTransform0") { auto value = materialJSON.value(key); if (value.isString()) { @@ -494,15 +447,6 @@ std::pair> NetworkMaterialResource glm::mat4 transform = mat4FromVariant(valueVariant); texcoordTransforms[1] = transform; } - } else if (key == "lightmapParams") { - auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::LIGHTMAP_PARAMS); - } - } - // TODO: implement lightmapParams and update JSDoc } else if (key == "materialParams") { auto value = materialJSON.value(key); if (value.isString()) { @@ -518,6 +462,296 @@ std::pair> NetworkMaterialResource material->setDefaultFallthrough(value.toBool()); } } + + if (modelString == graphics::Material::HIFI_PBR) { + if (key == "unlit") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::UNLIT_VAL_BIT); + } else if (value.isBool()) { + material->setUnlit(value.toBool()); + } + } else if (key == "roughness") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::GLOSSY_VAL_BIT); + } else if (value.isDouble()) { + material->setRoughness(value.toDouble()); + } + } else if (key == "metallic") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_VAL_BIT); + } else if (value.isDouble()) { + material->setMetallic(value.toDouble()); + } + } else if (key == "scattering") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::SCATTERING_VAL_BIT); + } else if (value.isDouble()) { + material->setScattering(value.toDouble()); + } + } else if (key == "roughnessMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT); + } else { + material->setRoughnessMap(baseUrl.resolved(valueString), false); + } + } + } else if (key == "glossMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT); + } else { + material->setRoughnessMap(baseUrl.resolved(valueString), true); + } + } + } else if (key == "metallicMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT); + } else { + material->setMetallicMap(baseUrl.resolved(valueString), false); + } + } + } else if (key == "specularMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT); + } else { + material->setMetallicMap(baseUrl.resolved(valueString), true); + } + } + } else if (key == "occlusionMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OCCLUSION_MAP_BIT); + } else { + material->setOcclusionMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "scatteringMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::SCATTERING_MAP_BIT); + } else { + material->setScatteringMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "lightMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::LIGHT_MAP_BIT); + } else { + material->setLightMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "lightmapParams") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::LIGHTMAP_PARAMS); + } + } + // TODO: implement lightmapParams and update JSDoc + } + } else if (modelString == graphics::Material::VRM_MTOON) { + auto toonMaterial = std::static_pointer_cast(material); + if (key == "shade") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::SHADE_VAL_BIT); + } else { + glm::vec3 color; + bool isSRGB; + bool valid = parseJSONColor(value, color, isSRGB); + if (valid) { + toonMaterial->setShade(color, isSRGB); + } + } + } else if (key == "shadeMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::SHADE_MAP_BIT); + } else { + toonMaterial->setShadeMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "shadingShift") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setShadingShift(value.toDouble()); + } + } else if (key == "shadingShiftMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_MAP_BIT); + } else { + toonMaterial->setShadingShiftMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "shadingToony") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::SHADING_TOONY_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setShadingToony(value.toDouble()); + } + } else if (key == "matcap") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::MATCAP_VAL_BIT); + } else { + glm::vec3 color; + bool isSRGB; + bool valid = parseJSONColor(value, color, isSRGB); + if (valid) { + toonMaterial->setMatcap(color, isSRGB); + } + } + } else if (key == "matcapMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::MATCAP_MAP_BIT); + } else { + toonMaterial->setMatcapMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "parametricRim") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_VAL_BIT); + } else { + glm::vec3 color; + bool isSRGB; + bool valid = parseJSONColor(value, color, isSRGB); + if (valid) { + toonMaterial->setParametricRim(color, isSRGB); + } + } + } else if (key == "parametricRimFresnelPower") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_POWER_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setParametricRimFresnelPower(value.toDouble()); + } + } else if (key == "parametricRimLift") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_LIFT_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setParametricRimLift(value.toDouble()); + } + } else if (key == "rimMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::RIM_MAP_BIT); + } else { + toonMaterial->setRimMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "rimLightingMix") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::RIM_LIGHTING_MIX_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setRimLightingMix(value.toDouble()); + } + } else if (key == "uvAnimationMaskMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_MASK_MAP_BIT); + } else { + toonMaterial->setUVAnimationMaskMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "uvAnimationScrollXSpeed") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setUVAnimationScrollXSpeed(value.toDouble()); + } + } else if (key == "uvAnimationScrollYSpeed") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setUVAnimationScrollYSpeed(value.toDouble()); + } + } else if (key == "uvAnimationRotationSpeed") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setUVAnimationRotationSpeed(value.toDouble()); + } + } else if (key == "outlineWidthMode") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::OUTLINE_WIDTH_MODE_VAL_BIT); + } else { + NetworkMToonMaterial::OutlineWidthMode mode; + if (NetworkMToonMaterial::getOutlineWidthModeFromName(valueString.toStdString(), mode)) { + // FIXME: Outlines are currently disabled because they're buggy + //toonMaterial->setOutlineWidthMode(mode); + } + } + } + } else if (key == "outlineWidth") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::OUTLINE_WIDTH_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setOutlineWidth(value.toDouble()); + } + } else if (key == "outline") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::OUTLINE_VAL_BIT); + } else { + glm::vec3 color; + bool isSRGB; + bool valid = parseJSONColor(value, color, isSRGB); + if (valid) { + toonMaterial->setOutline(color, isSRGB); + } + } + } + // TODO: support outlineWidthTexture and outlineLightingMix + } } // Do this after the texture maps are defined, so it overrides the default transforms @@ -592,9 +826,9 @@ NetworkMaterial::NetworkMaterial(const NetworkMaterial& m) : Material(m), _textures(m._textures), _albedoTransform(m._albedoTransform), + _isOriginal(m._isOriginal), _lightmapTransform(m._lightmapTransform), - _lightmapParams(m._lightmapParams), - _isOriginal(m._isOriginal) + _lightmapParams(m._lightmapParams) {} const QString NetworkMaterial::NO_TEXTURE = QString(); @@ -631,7 +865,13 @@ graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl } const auto url = getTextureUrl(baseUrl, hfmTexture); - const auto texture = DependencyManager::get()->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels, hfmTexture.sourceChannel); + auto textureCache = DependencyManager::get(); + NetworkTexturePointer texture; + if (textureCache) { + texture = textureCache->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels, hfmTexture.sourceChannel); + } else { + qDebug() << "GeometryResource::setTextures: TextureCache dependency not available, skipping textures"; + } _textures[channel] = Texture { hfmTexture.name, texture }; auto map = std::make_shared(); @@ -893,3 +1133,235 @@ bool NetworkMaterial::checkResetOpacityMap() { } return false; } + +NetworkMToonMaterial::NetworkMToonMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl) : + NetworkMaterial(material, textureBaseUrl) // handles _name, albedoMap, normalMap, and emissiveMap +{ + _model = VRM_MTOON; + + if (!material.shadeTexture.filename.isEmpty()) { + auto map = fetchTextureMap(textureBaseUrl, material.shadeTexture, image::TextureUsage::ALBEDO_TEXTURE, (MapChannel)MToonMapChannel::SHADE_MAP); + setTextureMap((MapChannel)MToonMapChannel::SHADE_MAP, map); + } + + if (!material.shadingShiftTexture.filename.isEmpty()) { + auto map = fetchTextureMap(textureBaseUrl, material.shadingShiftTexture, image::TextureUsage::ROUGHNESS_TEXTURE, (MapChannel)MToonMapChannel::SHADING_SHIFT_MAP); + setTextureMap((MapChannel)MToonMapChannel::SHADING_SHIFT_MAP, map); + } + + if (!material.matcapTexture.filename.isEmpty()) { + auto map = fetchTextureMap(textureBaseUrl, material.matcapTexture, image::TextureUsage::EMISSIVE_TEXTURE, (MapChannel)MToonMapChannel::MATCAP_MAP); + setTextureMap((MapChannel)MToonMapChannel::MATCAP_MAP, map); + } + + if (!material.rimTexture.filename.isEmpty()) { + auto map = fetchTextureMap(textureBaseUrl, material.rimTexture, image::TextureUsage::ALBEDO_TEXTURE, (MapChannel)MToonMapChannel::RIM_MAP); + setTextureMap((MapChannel)MToonMapChannel::RIM_MAP, map); + } + + if (!material.uvAnimationTexture.filename.isEmpty()) { + auto map = fetchTextureMap(textureBaseUrl, material.uvAnimationTexture, image::TextureUsage::ROUGHNESS_TEXTURE, (MapChannel)MToonMapChannel::UV_ANIMATION_MASK_MAP); + setTextureMap((MapChannel)MToonMapChannel::UV_ANIMATION_MASK_MAP, map); + } +} + +NetworkMToonMaterial::NetworkMToonMaterial(const NetworkMToonMaterial& material) : + NetworkMaterial(material), + _shade(material._shade), + _shadingShift(material._shadingShift), + _shadingToony(material._shadingToony), + _matcap(material._matcap), + _parametricRim(material._parametricRim), + _parametricRimFresnelPower(material._parametricRimFresnelPower), + _parametricRimLift(material._parametricRimLift), + _rimLightingMix(material._rimLightingMix), + _uvAnimationScrollXSpeed(material._uvAnimationScrollXSpeed), + _uvAnimationScrollYSpeed(material._uvAnimationScrollYSpeed), + _uvAnimationRotationSpeed(material._uvAnimationRotationSpeed), + _outlineWidthMode(material._outlineWidthMode), + _outlineWidth(material._outlineWidth), + _outline(material._outline) +{} + +void NetworkMToonMaterial::setTextures(const QVariantMap& textureMap) { + _isOriginal = false; + + const auto& albedoName = getTextureName(MapChannel::ALBEDO_MAP); + const auto& normalName = getTextureName(MapChannel::NORMAL_MAP); + const auto& emissiveName = getTextureName(MapChannel::EMISSIVE_MAP); + const auto& shadeName = getTextureName((MapChannel)MToonMapChannel::SHADE_MAP); + const auto& shadingShiftName = getTextureName((MapChannel)MToonMapChannel::SHADING_SHIFT_MAP); + const auto& matcapName = getTextureName((MapChannel)MToonMapChannel::MATCAP_MAP); + const auto& rimName = getTextureName((MapChannel)MToonMapChannel::RIM_MAP); + const auto& uvAnimationMaskName = getTextureName((MapChannel)MToonMapChannel::UV_ANIMATION_MASK_MAP); + + if (!albedoName.isEmpty()) { + auto url = textureMap.contains(albedoName) ? textureMap[albedoName].toUrl() : QUrl(); + auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); + if (map) { + map->setTextureTransform(_albedoTransform); + // when reassigning the albedo texture we also check for the alpha channel used as opacity + map->setUseAlphaChannel(true); + } + setTextureMap(MapChannel::ALBEDO_MAP, map); + } + + if (!normalName.isEmpty()) { + auto url = textureMap.contains(normalName) ? textureMap[normalName].toUrl() : QUrl(); + auto map = fetchTextureMap(url, image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP); + setTextureMap(MapChannel::NORMAL_MAP, map); + } + + if (!emissiveName.isEmpty()) { + auto url = textureMap.contains(emissiveName) ? textureMap[emissiveName].toUrl() : QUrl(); + auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); + setTextureMap(MapChannel::EMISSIVE_MAP, map); + } + + if (!shadeName.isEmpty()) { + auto url = textureMap.contains(shadeName) ? textureMap[shadeName].toUrl() : QUrl(); + auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, (MapChannel)MToonMapChannel::SHADE_MAP); + setTextureMap((MapChannel)MToonMapChannel::SHADE_MAP, map); + } + + if (!shadingShiftName.isEmpty()) { + auto url = textureMap.contains(shadingShiftName) ? textureMap[shadingShiftName].toUrl() : QUrl(); + auto map = fetchTextureMap(url, image::TextureUsage::ROUGHNESS_TEXTURE, (MapChannel)MToonMapChannel::SHADING_SHIFT_MAP); + setTextureMap((MapChannel)MToonMapChannel::SHADING_SHIFT_MAP, map); + } + + if (!matcapName.isEmpty()) { + auto url = textureMap.contains(matcapName) ? textureMap[matcapName].toUrl() : QUrl(); + auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, (MapChannel)MToonMapChannel::MATCAP_MAP); + setTextureMap((MapChannel)MToonMapChannel::MATCAP_MAP, map); + } + + if (!rimName.isEmpty()) { + auto url = textureMap.contains(rimName) ? textureMap[rimName].toUrl() : QUrl(); + auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, (MapChannel)MToonMapChannel::RIM_MAP); + setTextureMap((MapChannel)MToonMapChannel::RIM_MAP, map); + } + + if (!uvAnimationMaskName.isEmpty()) { + auto url = textureMap.contains(uvAnimationMaskName) ? textureMap[uvAnimationMaskName].toUrl() : QUrl(); + auto map = fetchTextureMap(url, image::TextureUsage::ROUGHNESS_TEXTURE, (MapChannel)MToonMapChannel::UV_ANIMATION_MASK_MAP); + setTextureMap((MapChannel)MToonMapChannel::UV_ANIMATION_MASK_MAP, map); + } +} + +std::string NetworkMToonMaterial::getOutlineWidthModeName(OutlineWidthMode mode) { + const std::string names[3] = { "none", "worldCoordinates", "screenCoordinates" }; + return names[mode]; +} + +bool NetworkMToonMaterial::getOutlineWidthModeFromName(const std::string& modeName, OutlineWidthMode& mode) { + for (int i = OUTLINE_NONE; i < NUM_OUTLINE_MODES; i++) { + mode = (OutlineWidthMode)i; + if (modeName == getOutlineWidthModeName(mode)) { + return true; + } + } + return false; +} + +void NetworkMToonMaterial::setShadeMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, (MapChannel) MToonMapChannel::SHADE_MAP); + if (map) { + setTextureMap((MapChannel) MToonMapChannel::SHADE_MAP, map); + } +} + +void NetworkMToonMaterial::setShadingShiftMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::ROUGHNESS_TEXTURE, (MapChannel) MToonMapChannel::SHADING_SHIFT_MAP); + if (map) { + setTextureMap((MapChannel) MToonMapChannel::SHADING_SHIFT_MAP, map); + } +} + +void NetworkMToonMaterial::setMatcapMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, (MapChannel)MToonMapChannel::MATCAP_MAP); + if (map) { + setTextureMap((MapChannel) MToonMapChannel::MATCAP_MAP, map); + } +} + +void NetworkMToonMaterial::setRimMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, (MapChannel)MToonMapChannel::RIM_MAP); + if (map) { + setTextureMap((MapChannel) MToonMapChannel::RIM_MAP, map); + } +} + +void NetworkMToonMaterial::setUVAnimationMaskMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::ROUGHNESS_TEXTURE, (MapChannel)MToonMapChannel::UV_ANIMATION_MASK_MAP); + if (map) { + setTextureMap((MapChannel) MToonMapChannel::UV_ANIMATION_MASK_MAP, map); + } +} + +void NetworkMToonMaterial::setShade(const glm::vec3& shade, bool isSRGB) { + _key._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADE_VAL_BIT, true); + _shade = (isSRGB ? ColorUtils::sRGBToLinearVec3(shade) : shade); +} + +void NetworkMToonMaterial::setShadingShift(float shadingShift) { + _key._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_VAL_BIT, true); + _shadingShift = shadingShift; +} + +void NetworkMToonMaterial::setShadingToony(float shadingToony) { + _key._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADING_TOONY_VAL_BIT, true); + _shadingToony = shadingToony; +} + +void NetworkMToonMaterial::setMatcap(const glm::vec3& matcap, bool isSRGB) { + _key._flags.set(MToonFlagBit::MATCAP_VAL_BIT, true); + _matcap = (isSRGB ? ColorUtils::sRGBToLinearVec3(matcap) : matcap); +} + +void NetworkMToonMaterial::setParametricRim(const glm::vec3& parametricRim, bool isSRGB) { + _key._flags.set(MToonFlagBit::PARAMETRIC_RIM_VAL_BIT, true); + _parametricRim = (isSRGB ? ColorUtils::sRGBToLinearVec3(parametricRim) : parametricRim); +} + +void NetworkMToonMaterial::setParametricRimFresnelPower(float parametricRimFresnelPower) { + _key._flags.set(MToonFlagBit::PARAMETRIC_RIM_POWER_VAL_BIT, true); + _parametricRimFresnelPower = parametricRimFresnelPower; +} + +void NetworkMToonMaterial::setParametricRimLift(float parametricRimLift) { + _key._flags.set(MToonFlagBit::PARAMETRIC_RIM_LIFT_VAL_BIT, true); + _parametricRimLift = parametricRimLift; +} + +void NetworkMToonMaterial::setRimLightingMix(float rimLightingMix) { + _key._flags.set(MToonFlagBit::RIM_LIGHTING_MIX_VAL_BIT, true); + _rimLightingMix = rimLightingMix; +} + +void NetworkMToonMaterial::setUVAnimationScrollXSpeed(float uvAnimationScrollXSpeed) { + _key._flags.set(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT, true); + _uvAnimationScrollXSpeed = uvAnimationScrollXSpeed; +} + +void NetworkMToonMaterial::setUVAnimationScrollYSpeed(float uvAnimationScrollYSpeed) { + _key._flags.set(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT, true); + _uvAnimationScrollYSpeed = uvAnimationScrollYSpeed; +} + +void NetworkMToonMaterial::setUVAnimationRotationSpeed(float uvAnimationRotationSpeed) { + _key._flags.set(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT, true); + _uvAnimationRotationSpeed = uvAnimationRotationSpeed; +} + +void NetworkMToonMaterial::setOutlineWidthMode(OutlineWidthMode mode) { + _outlineWidthMode = mode; +} + +void NetworkMToonMaterial::setOutlineWidth(float width) { + _outlineWidth = width; +} + +void NetworkMToonMaterial::setOutline(const glm::vec3& outline, bool isSRGB) { + _outline = (isSRGB ? ColorUtils::sRGBToLinearVec3(outline) : outline); +} diff --git a/libraries/procedural/src/procedural/ProceduralMaterialCache.h b/libraries/procedural/src/procedural/ProceduralMaterialCache.h index 7d6a6ecdf3..a7c5baa011 100644 --- a/libraries/procedural/src/procedural/ProceduralMaterialCache.h +++ b/libraries/procedural/src/procedural/ProceduralMaterialCache.h @@ -1,6 +1,7 @@ // // Created by Sam Gondelman on 2/9/2018 // Copyright 2018 High Fidelity, Inc. +// Copyright 2024 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 @@ -59,22 +60,141 @@ protected: static const QString NO_TEXTURE; const QString& getTextureName(MapChannel channel); - void setTextures(const QVariantMap& textureMap); + virtual void setTextures(const QVariantMap& textureMap); const bool& isOriginal() const { return _isOriginal; } -private: - // Helpers for the ctors - QUrl getTextureUrl(const QUrl& baseUrl, const HFMTexture& hfmTexture); graphics::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture, image::TextureUsage::Type type, MapChannel channel); graphics::TextureMapPointer fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel); Transform _albedoTransform; - Transform _lightmapTransform; - vec2 _lightmapParams; bool _isOriginal { true }; + +private: + // Helpers for the ctors + QUrl getTextureUrl(const QUrl& baseUrl, const HFMTexture& hfmTexture); + + Transform _lightmapTransform; + vec2 _lightmapParams; +}; + +class NetworkMToonMaterial : public NetworkMaterial { +public: + NetworkMToonMaterial() : NetworkMaterial() { _model = VRM_MTOON; } + NetworkMToonMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl); + NetworkMToonMaterial(const NetworkMToonMaterial& material); + + void setTextures(const QVariantMap& textureMap) override; + + enum MToonMapChannel { + // Keep aligned with graphics/ShaderConstants.h and graphics-scripting/ScriptableModel.cpp + SHADE_MAP = MapChannel::ROUGHNESS_MAP, + SHADING_SHIFT_MAP = MapChannel::METALLIC_MAP, + MATCAP_MAP = MapChannel::OCCLUSION_MAP, + RIM_MAP = MapChannel::SCATTERING_MAP, + UV_ANIMATION_MASK_MAP = MapChannel::LIGHT_MAP, + }; + + enum MToonFlagBit { + // Must match mappings in GraphicsScriptingInterface.cpp + SHADE_MAP_BIT = graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT, + SHADING_SHIFT_MAP_BIT = graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT, + MATCAP_MAP_BIT = graphics::MaterialKey::FlagBit::OCCLUSION_MAP_BIT, + RIM_MAP_BIT = graphics::MaterialKey::FlagBit::SCATTERING_MAP_BIT, + UV_ANIMATION_MASK_MAP_BIT = graphics::MaterialKey::FlagBit::LIGHT_MAP_BIT, + + SHADE_VAL_BIT = graphics::MaterialKey::FlagBit::UNLIT_VAL_BIT, + SHADING_SHIFT_VAL_BIT = graphics::MaterialKey::FlagBit::METALLIC_VAL_BIT, + SHADING_TOONY_VAL_BIT = graphics::MaterialKey::FlagBit::GLOSSY_VAL_BIT, + UV_ANIMATION_SCROLL_VAL_BIT = graphics::MaterialKey::FlagBit::SCATTERING_VAL_BIT, + MATCAP_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_1_BIT, + PARAMETRIC_RIM_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_2_BIT, + PARAMETRIC_RIM_POWER_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_3_BIT, + PARAMETRIC_RIM_LIFT_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_4_BIT, + RIM_LIGHTING_MIX_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_5_BIT, + + OUTLINE_WIDTH_MODE_VAL_BIT = graphics::Material::ExtraFlagBit::EXTRA_1_BIT, + OUTLINE_WIDTH_VAL_BIT = graphics::Material::ExtraFlagBit::EXTRA_2_BIT, + OUTLINE_VAL_BIT = graphics::Material::ExtraFlagBit::EXTRA_3_BIT, + }; + + enum OutlineWidthMode { + OUTLINE_NONE = 0, + OUTLINE_WORLD, + OUTLINE_SCREEN, + + NUM_OUTLINE_MODES + }; + static std::string getOutlineWidthModeName(OutlineWidthMode mode); + // find the enum value from a string, return true if match found + static bool getOutlineWidthModeFromName(const std::string& modeName, OutlineWidthMode& mode); + + bool isMToon() const override { return true; } + + void setShadeMap(const QUrl& url); + void setShadingShiftMap(const QUrl& url); + void setMatcapMap(const QUrl& url); + void setRimMap(const QUrl& url); + void setUVAnimationMaskMap(const QUrl& url); + + void setShade(const glm::vec3& shade, bool isSRGB = true); + glm::vec3 getShade(bool SRGB = true) const override { return (SRGB ? ColorUtils::tosRGBVec3(_shade) : _shade); } + + void setShadingShift(float shadeShift); + float getShadingShift() const override { return _shadingShift; } + + void setShadingToony(float shadingToony); + float getShadingToony() const override { return _shadingToony; } + + void setMatcap(const glm::vec3& matcap, bool isSRGB = true); + glm::vec3 getMatcap(bool SRGB = true) const override { return (SRGB ? ColorUtils::tosRGBVec3(_matcap) : _matcap); } + + void setParametricRim(const glm::vec3& parametricRim, bool isSRGB = true); + glm::vec3 getParametricRim(bool SRGB = true) const override { return (SRGB ? ColorUtils::tosRGBVec3(_parametricRim) : _parametricRim); } + + void setParametricRimFresnelPower(float parametricRimFresnelPower); + float getParametricRimFresnelPower() const override { return _parametricRimFresnelPower; } + + void setParametricRimLift(float parametricRimLift); + float getParametricRimLift() const override { return _parametricRimLift; } + + void setRimLightingMix(float rimLightingMix); + float getRimLightingMix() const override { return _rimLightingMix; } + + void setUVAnimationScrollXSpeed(float uvAnimationScrollXSpeed); + float getUVAnimationScrollXSpeed() const override { return _uvAnimationScrollXSpeed; } + void setUVAnimationScrollYSpeed(float uvAnimationScrollYSpeed); + float getUVAnimationScrollYSpeed() const override { return _uvAnimationScrollYSpeed; } + void setUVAnimationRotationSpeed(float uvAnimationRotationSpeed); + float getUVAnimationRotationSpeed() const override { return _uvAnimationRotationSpeed; } + + void setOutlineWidthMode(OutlineWidthMode mode); + uint8_t getOutlineWidthMode() override { return _outlineWidthMode; } + void setOutlineWidth(float width); + float getOutlineWidth() override { return _outlineWidth; } + void setOutline(const glm::vec3& outline, bool isSRGB = true); + glm::vec3 getOutline(bool SRGB = true) const override { return (SRGB ? ColorUtils::tosRGBVec3(_outline) : _outline); } + +private: + glm::vec3 _shade { DEFAULT_SHADE }; + float _shadingShift { DEFAULT_SHADING_SHIFT }; + float _shadingToony { DEFAULT_SHADING_TOONY }; + + glm::vec3 _matcap { DEFAULT_MATCAP }; + glm::vec3 _parametricRim { DEFAULT_PARAMETRIC_RIM }; + float _parametricRimFresnelPower { DEFAULT_PARAMETRIC_RIM_FRESNEL_POWER }; + float _parametricRimLift { DEFAULT_PARAMETRIC_RIM_LIFT }; + float _rimLightingMix { DEFAULT_RIM_LIGHTING_MIX }; + + float _uvAnimationScrollXSpeed { DEFAULT_UV_ANIMATION_SCROLL_SPEED }; + float _uvAnimationScrollYSpeed { DEFAULT_UV_ANIMATION_SCROLL_SPEED }; + float _uvAnimationRotationSpeed { DEFAULT_UV_ANIMATION_SCROLL_SPEED }; + + OutlineWidthMode _outlineWidthMode { OutlineWidthMode::OUTLINE_NONE }; + float _outlineWidth { 0.0f }; + glm::vec3 _outline { DEFAULT_OUTLINE }; }; class NetworkMaterialResource : public Resource { diff --git a/libraries/procedural/src/procedural/ReferenceMaterial.cpp b/libraries/procedural/src/procedural/ReferenceMaterial.cpp index 97211eb737..65bea1bf7a 100644 --- a/libraries/procedural/src/procedural/ReferenceMaterial.cpp +++ b/libraries/procedural/src/procedural/ReferenceMaterial.cpp @@ -1,6 +1,7 @@ // // Created by HifiExperiments on 3/14/2021 // Copyright 2021 Vircadia contributors. +// Copyright 2024 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 @@ -162,7 +163,7 @@ bool ReferenceMaterial::isReady() const { QString ReferenceMaterial::getProceduralString() const { return resultWithLock([&] { - auto material = getMaterial(); + auto material = getProceduralMaterial(); return material ? material->getProceduralString() : QString(); }); } @@ -212,6 +213,112 @@ void ReferenceMaterial::initializeProcedural() { }); } +// MToonMaterial +bool ReferenceMaterial::isMToon() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->isMToon() : false; + }); +} + +glm::vec3 ReferenceMaterial::getShade(bool SRGB) const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getShade(SRGB) : glm::vec3(); + }); +} + +float ReferenceMaterial::getShadingShift() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getShadingShift() : 0.0f; + }); +} + +float ReferenceMaterial::getShadingToony() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getShadingToony() : 0.0f; + }); +} + +glm::vec3 ReferenceMaterial::getMatcap(bool SRGB) const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getMatcap(SRGB) : glm::vec3(); + }); +} + +glm::vec3 ReferenceMaterial::getParametricRim(bool SRGB) const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getParametricRim(SRGB) : glm::vec3(); + }); +} + +float ReferenceMaterial::getParametricRimFresnelPower() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getParametricRimFresnelPower() : 0.0f; + }); +} + +float ReferenceMaterial::getParametricRimLift() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getParametricRimLift() : 0.0f; + }); +} + +float ReferenceMaterial::getRimLightingMix() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getRimLightingMix() : 0.0f; + }); +} + +float ReferenceMaterial::getUVAnimationScrollXSpeed() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getUVAnimationScrollXSpeed() : 0.0f; + }); +} + +float ReferenceMaterial::getUVAnimationScrollYSpeed() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getUVAnimationScrollYSpeed() : 0.0f; + }); +} + +float ReferenceMaterial::getUVAnimationRotationSpeed() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getUVAnimationRotationSpeed() : 0.0f; + }); +} + +uint8_t ReferenceMaterial::getOutlineWidthMode() { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getOutlineWidthMode() : 0; + }); +} + +float ReferenceMaterial::getOutlineWidth() { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getOutlineWidth() : 0.0f; + }); +} + +glm::vec3 ReferenceMaterial::getOutline(bool SRGB) const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getOutline() : glm::vec3(0.0f); + }); +} + void ReferenceMaterial::setMaterialForUUIDOperator(std::function materialForUUIDOperator) { _unboundMaterialForUUIDOperator = materialForUUIDOperator; } @@ -244,6 +351,16 @@ graphics::ProceduralMaterialPointer ReferenceMaterial::getProceduralMaterial() c return nullptr; } +std::shared_ptr ReferenceMaterial::getMToonMaterial() const { + if (_materialForUUIDOperator) { + std::shared_ptr result = nullptr; + if (auto material = _materialForUUIDOperator()) { + return std::static_pointer_cast(material); + } + } + return nullptr; +} + template inline T ReferenceMaterial::resultWithLock(F&& f) const { if (_locked) { diff --git a/libraries/procedural/src/procedural/ReferenceMaterial.h b/libraries/procedural/src/procedural/ReferenceMaterial.h index ac778f94b1..140b86fe33 100644 --- a/libraries/procedural/src/procedural/ReferenceMaterial.h +++ b/libraries/procedural/src/procedural/ReferenceMaterial.h @@ -1,6 +1,7 @@ // // Created by HifiExperiments on 3/14/2021 // Copyright 2021 Vircadia contributors. +// Copyright 2024 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 @@ -52,6 +53,23 @@ public: const uint64_t& created, const ProceduralProgramKey key = ProceduralProgramKey()) override; void initializeProcedural() override; + // MToonMaterial + bool isMToon() const override; + glm::vec3 getShade(bool SRGB = true) const override; + float getShadingShift() const override; + float getShadingToony() const override; + glm::vec3 getMatcap(bool SRGB = true) const override; + glm::vec3 getParametricRim(bool SRGB = true) const override; + float getParametricRimFresnelPower() const override; + float getParametricRimLift() const override; + float getRimLightingMix() const override; + float getUVAnimationScrollXSpeed() const override; + float getUVAnimationScrollYSpeed() const override; + float getUVAnimationRotationSpeed() const override; + uint8_t getOutlineWidthMode() override; + float getOutlineWidth() override; + glm::vec3 getOutline(bool SRGB = true) const override; + bool isReference() const override { return true; } std::function getReferenceOperator() const { return _materialForUUIDOperator; } @@ -65,6 +83,7 @@ private: graphics::MaterialPointer getMaterial() const; std::shared_ptr getNetworkMaterial() const; graphics::ProceduralMaterialPointer getProceduralMaterial() const; + std::shared_ptr getMToonMaterial() const; template T resultWithLock(F&& f) const; diff --git a/libraries/recording/src/recording/RecordingScriptingInterface.cpp b/libraries/recording/src/recording/RecordingScriptingInterface.cpp index 05cfa8b851..a05ee60604 100644 --- a/libraries/recording/src/recording/RecordingScriptingInterface.cpp +++ b/libraries/recording/src/recording/RecordingScriptingInterface.cpp @@ -38,28 +38,36 @@ using namespace recording; static const QString HFR_EXTENSION = "hfr"; RecordingScriptingInterface::RecordingScriptingInterface() { + Locker lock(_mutex); _player = DependencyManager::get(); _recorder = DependencyManager::get(); } bool RecordingScriptingInterface::isPlaying() const { + Locker lock(_mutex); return _player->isPlaying(); } bool RecordingScriptingInterface::isPaused() const { + Locker lock(_mutex); return _player->isPaused(); } float RecordingScriptingInterface::playerElapsed() const { + Locker lock(_mutex); return _player->position(); } float RecordingScriptingInterface::playerLength() const { + Locker lock(_mutex); return _player->length(); } void RecordingScriptingInterface::playClip(NetworkClipLoaderPointer clipLoader, const QString& url, const ScriptValue& callback) { - _player->queueClip(clipLoader->getClip()); + { + Locker lock(_mutex); + _player->queueClip(clipLoader->getClip()); + } if (callback.isFunction()) { auto engine = callback.engine(); @@ -69,11 +77,6 @@ void RecordingScriptingInterface::playClip(NetworkClipLoaderPointer clipLoader, } void RecordingScriptingInterface::loadRecording(const QString& url, const ScriptValue& callback) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "loadRecording", Q_ARG(const QString&, url), Q_ARG(const ScriptValue&, callback)); - return; - } - auto clipLoader = DependencyManager::get()->getClipLoader(url); if (clipLoader->isLoaded()) { @@ -82,6 +85,8 @@ void RecordingScriptingInterface::loadRecording(const QString& url, const Script return; } + Locker lock(_mutex); + // hold a strong pointer to the loading clip so that it has a chance to load _clipLoaders.insert(clipLoader); @@ -131,15 +136,12 @@ void RecordingScriptingInterface::startPlaying() { return; } + Locker lock(_mutex); _player->play(); } void RecordingScriptingInterface::setPlayerVolume(float volume) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "setPlayerVolume", Q_ARG(float, volume)); - return; - } - + Locker lock(_mutex); _player->setVolume(std::min(std::max(volume, 0.0f), 1.0f)); } @@ -152,6 +154,8 @@ void RecordingScriptingInterface::setPlayerTime(float time) { BLOCKING_INVOKE_METHOD(this, "setPlayerTime", Q_ARG(float, time)); return; } + + Locker lock(_mutex); _player->seek(time); } @@ -160,11 +164,7 @@ void RecordingScriptingInterface::setPlayFromCurrentLocation(bool playFromCurren } void RecordingScriptingInterface::setPlayerLoop(bool loop) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "setPlayerLoop", Q_ARG(bool, loop)); - return; - } - + Locker lock(_mutex); _player->loop(loop); } @@ -185,10 +185,7 @@ void RecordingScriptingInterface::setPlayerUseSkeletonModel(bool useSkeletonMode } void RecordingScriptingInterface::pausePlayer() { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "pausePlayer"); - return; - } + Locker lock(_mutex); _player->pause(); } @@ -197,6 +194,8 @@ void RecordingScriptingInterface::stopPlaying() { BLOCKING_INVOKE_METHOD(this, "stopPlaying"); return; } + + Locker lock(_mutex); _player->stop(); } @@ -214,11 +213,7 @@ void RecordingScriptingInterface::startRecording() { return; } - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "startRecording"); - return; - } - + Locker lock(_mutex); _recorder->start(); } @@ -228,11 +223,7 @@ void RecordingScriptingInterface::stopRecording() { return; } - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "stopRecording"); - return; - } - + Locker lock(_mutex); _recorder->stop(); _lastClip = _recorder->getClip(); _lastClip->seek(0); @@ -247,12 +238,7 @@ QString RecordingScriptingInterface::getDefaultRecordingSaveDirectory() { } void RecordingScriptingInterface::saveRecording(const QString& filename) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "saveRecording", - Q_ARG(QString, filename)); - return; - } - + Locker lock(_mutex); if (!_lastClip) { qWarning() << "There is no recording to save"; return; @@ -267,14 +253,7 @@ bool RecordingScriptingInterface::saveRecordingToAsset(const ScriptValue& getCli return false; } - if (QThread::currentThread() != thread()) { - bool result; - BLOCKING_INVOKE_METHOD(this, "saveRecordingToAsset", - Q_RETURN_ARG(bool, result), - Q_ARG(const ScriptValue&, getClipAtpUrl)); - return result; - } - + Locker lock(_mutex); if (!_lastClip) { qWarning() << "There is no recording to save"; return false; @@ -316,6 +295,8 @@ void RecordingScriptingInterface::loadLastRecording() { return; } + Locker lock(_mutex); + if (!_lastClip) { qCDebug(scriptengine) << "There is no recording to load"; return; diff --git a/libraries/recording/src/recording/RecordingScriptingInterface.h b/libraries/recording/src/recording/RecordingScriptingInterface.h index 42dc665706..394c3e230d 100644 --- a/libraries/recording/src/recording/RecordingScriptingInterface.h +++ b/libraries/recording/src/recording/RecordingScriptingInterface.h @@ -357,6 +357,8 @@ protected: using Locker = std::unique_lock; using Flag = std::atomic; + mutable Mutex _mutex; + QSharedPointer _player; QSharedPointer _recorder; diff --git a/libraries/render-utils/res/fonts/CourierPrime-OFL.txt b/libraries/render-utils/res/fonts/CourierPrime-OFL.txt new file mode 100644 index 0000000000..28f87c184d --- /dev/null +++ b/libraries/render-utils/res/fonts/CourierPrime-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2015 The Courier Prime Project Authors (https://github.com/quoteunquoteapps/CourierPrime). + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/libraries/render-utils/res/fonts/CourierPrime.arfont b/libraries/render-utils/res/fonts/CourierPrime.arfont new file mode 100644 index 0000000000000000000000000000000000000000..3396ccfb866774ec5c6a67560b1b888dfd507f7f GIT binary patch literal 54316 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

29l^&lQT(SdCB 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 z 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 S000000000i)BphGf%-LqcN>NP literal 0 HcmV?d00001 diff --git a/libraries/render-utils/res/fonts/CourierPrime.license b/libraries/render-utils/res/fonts/CourierPrime.license deleted file mode 100644 index 661d34a312..0000000000 --- a/libraries/render-utils/res/fonts/CourierPrime.license +++ /dev/null @@ -1,51 +0,0 @@ -/* - -SIL Open Font License v1.10 - -This license can also be found at this permalink: http://www.fontsquirrel.com/license/courier-prime - -Copyright (c) 2013, Quote-Unquote Apps (http://quoteunquoteapps.com), -with Reserved Font Name Courier Prime. - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL - -—————————————————————————————- -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 -—————————————————————————————- - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. - -The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. - -DEFINITIONS -“Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. - -“Reserved Font Name” refers to any names specified as such after the copyright statement(s). - -“Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s). - -“Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. - -“Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. - -5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. -*/ \ No newline at end of file diff --git a/libraries/render-utils/res/fonts/CourierPrime.sdff b/libraries/render-utils/res/fonts/CourierPrime.sdff deleted file mode 100644 index f8e6687323e4cfca2c50dd44412865ed74afaeea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 380376 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%0DF(i^ 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 VriF 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 DDhPvI diff --git a/libraries/render-utils/res/fonts/Inconsolata-OFL.txt b/libraries/render-utils/res/fonts/Inconsolata-OFL.txt new file mode 100644 index 0000000000..1089cbf838 --- /dev/null +++ b/libraries/render-utils/res/fonts/Inconsolata-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2006 The Inconsolata Project Authors + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/libraries/render-utils/res/fonts/InconsolataMedium.arfont b/libraries/render-utils/res/fonts/InconsolataMedium.arfont new file mode 100644 index 0000000000000000000000000000000000000000..0b0e3750fe0c2a475124d1c2288f7a9c2c8b90e6 GIT binary patch literal 55452 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 ze+B;n%S8TYe3g&800000NkvXXu0mjf000000001Ev&>Zh0000000000000000001- K*Z=@OPnRqXGF}J( literal 0 HcmV?d00001 diff --git a/libraries/render-utils/res/fonts/InconsolataMedium.sdff b/libraries/render-utils/res/fonts/InconsolataMedium.sdff deleted file mode 100644 index d66e25b34b30c7a6558ebe7dbead6863cbdafe11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 129710 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 diff --git a/libraries/render-utils/res/fonts/Timeless.arfont b/libraries/render-utils/res/fonts/Timeless.arfont new file mode 100644 index 0000000000000000000000000000000000000000..6d2674deeb12f95f9e5b65be432d39c7052c26af GIT binary patch literal 110692 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 diff --git a/libraries/render-utils/res/fonts/fonts.qrc b/libraries/render-utils/res/fonts/fonts.qrc index a7d9e6c3df..2c31697d5e 100644 --- a/libraries/render-utils/res/fonts/fonts.qrc +++ b/libraries/render-utils/res/fonts/fonts.qrc @@ -1,9 +1,9 @@ - CourierPrime.sdff - InconsolataMedium.sdff - Roboto.sdff - Timeless.sdff + CourierPrime.arfont + InconsolataMedium.arfont + Roboto.arfont + Timeless.arfont \ No newline at end of file diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 4ac097e31d..4d4f9c0680 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -99,14 +99,9 @@ static const gpu::Element NORMAL_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; static const gpu::Element TEXCOORD0_ELEMENT { gpu::VEC2, gpu::FLOAT, gpu::UV }; static const gpu::Element TANGENT_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; static const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA }; -static const gpu::Element TEXCOORD4_ELEMENT { gpu::VEC4, gpu::FLOAT, gpu::XYZW }; static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT; -static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT; -static gpu::Stream::FormatPointer INSTANCED_SOLID_FADE_STREAM_FORMAT; static gpu::Stream::FormatPointer WIRE_STREAM_FORMAT; -static gpu::Stream::FormatPointer INSTANCED_WIRE_STREAM_FORMAT; -static gpu::Stream::FormatPointer INSTANCED_WIRE_FADE_STREAM_FORMAT; static const uint SHAPE_VERTEX_STRIDE = sizeof(GeometryCache::ShapeVertex); // position, normal, texcoords, tangent static const uint SHAPE_NORMALS_OFFSET = offsetof(GeometryCache::ShapeVertex, normal); @@ -259,14 +254,6 @@ size_t GeometryCache::getShapeTriangleCount(Shape shape) { return _shapes[shape]._indicesView.getNumElements() / VERTICES_PER_TRIANGLE; } -size_t GeometryCache::getSphereTriangleCount() { - return getShapeTriangleCount(Sphere); -} - -size_t GeometryCache::getCubeTriangleCount() { - return getShapeTriangleCount(Cube); -} - using IndexPair = uint64_t; using IndexPairs = std::unordered_set; @@ -647,83 +634,24 @@ gpu::Stream::FormatPointer& getSolidStreamFormat() { SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD0, gpu::Stream::TEXCOORD0, TEXCOORD0_ELEMENT); SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TANGENT, gpu::Stream::TANGENT, TANGENT_ELEMENT); + SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); } return SOLID_STREAM_FORMAT; } -gpu::Stream::FormatPointer& getInstancedSolidStreamFormat() { - if (!INSTANCED_SOLID_STREAM_FORMAT) { - INSTANCED_SOLID_STREAM_FORMAT = std::make_shared(); // 1 for everyone - INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); - INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); - INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD0, gpu::Stream::TEXCOORD0, TEXCOORD0_ELEMENT); - INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TANGENT, gpu::Stream::TANGENT, TANGENT_ELEMENT); - INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - } - return INSTANCED_SOLID_STREAM_FORMAT; -} - -gpu::Stream::FormatPointer& getInstancedSolidFadeStreamFormat() { - if (!INSTANCED_SOLID_FADE_STREAM_FORMAT) { - INSTANCED_SOLID_FADE_STREAM_FORMAT = std::make_shared(); // 1 for everyone - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD0, gpu::Stream::TEXCOORD0, TEXCOORD0_ELEMENT); - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TANGENT, gpu::Stream::TANGENT, TANGENT_ELEMENT); - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD2, gpu::Stream::TEXCOORD2, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD3, gpu::Stream::TEXCOORD3, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD4, gpu::Stream::TEXCOORD4, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - } - return INSTANCED_SOLID_FADE_STREAM_FORMAT; -} - gpu::Stream::FormatPointer& getWireStreamFormat() { if (!WIRE_STREAM_FORMAT) { WIRE_STREAM_FORMAT = std::make_shared(); // 1 for everyone WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); + WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); } return WIRE_STREAM_FORMAT; } -gpu::Stream::FormatPointer& getInstancedWireStreamFormat() { - if (!INSTANCED_WIRE_STREAM_FORMAT) { - INSTANCED_WIRE_STREAM_FORMAT = std::make_shared(); // 1 for everyone - INSTANCED_WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); - INSTANCED_WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); - INSTANCED_WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - } - return INSTANCED_WIRE_STREAM_FORMAT; -} - -gpu::Stream::FormatPointer& getInstancedWireFadeStreamFormat() { - if (!INSTANCED_WIRE_FADE_STREAM_FORMAT) { - INSTANCED_WIRE_FADE_STREAM_FORMAT = std::make_shared(); // 1 for everyone - INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); - INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); - INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD2, gpu::Stream::TEXCOORD2, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD3, gpu::Stream::TEXCOORD3, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD4, gpu::Stream::TEXCOORD4, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - } - return INSTANCED_WIRE_FADE_STREAM_FORMAT; -} - -QHash GeometryCache::_simplePrograms; - -gpu::ShaderPointer GeometryCache::_simpleShader; -gpu::ShaderPointer GeometryCache::_transparentShader; -gpu::ShaderPointer GeometryCache::_unlitShader; -gpu::ShaderPointer GeometryCache::_simpleFadeShader; -gpu::ShaderPointer GeometryCache::_unlitFadeShader; -gpu::ShaderPointer GeometryCache::_forwardSimpleShader; -gpu::ShaderPointer GeometryCache::_forwardTransparentShader; -gpu::ShaderPointer GeometryCache::_forwardUnlitShader; -gpu::ShaderPointer GeometryCache::_forwardSimpleFadeShader; -gpu::ShaderPointer GeometryCache::_forwardUnlitFadeShader; - +std::map, gpu::ShaderPointer> GeometryCache::_shapeShaders; std::map, render::ShapePipelinePointer> GeometryCache::_shapePipelines; +QHash GeometryCache::_simplePrograms; GeometryCache::GeometryCache() : _nextID(0) { @@ -809,103 +737,35 @@ render::ShapePipelinePointer GeometryCache::getFadingShapePipeline(bool textured ); } -void GeometryCache::renderShape(gpu::Batch& batch, Shape shape) { - batch.setInputFormat(getSolidStreamFormat()); - _shapes[shape].draw(batch); -} - -void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape) { - batch.setInputFormat(getWireStreamFormat()); - _shapes[shape].drawWire(batch); -} - -void GeometryCache::renderShape(gpu::Batch& batch, Shape shape, const glm::vec4& color) { - batch.setInputFormat(getSolidStreamFormat()); - batch._glColor4f(color.r, color.g, color.b, color.a); - _shapes[shape].draw(batch); -} - -void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape, const glm::vec4& color) { - batch.setInputFormat(getWireStreamFormat()); - batch._glColor4f(color.r, color.g, color.b, color.a); - _shapes[shape].drawWire(batch); -} - -void setupBatchInstance(gpu::Batch& batch, gpu::BufferPointer colorBuffer) { +void setupColorInputBuffer(gpu::Batch& batch, gpu::BufferPointer colorBuffer) { gpu::BufferView colorView(colorBuffer, COLOR_ELEMENT); batch.setInputBuffer(gpu::Stream::COLOR, colorView); } +void GeometryCache::renderShape(gpu::Batch& batch, Shape shape, gpu::BufferPointer& colorBuffer) { + batch.setInputFormat(getSolidStreamFormat()); + setupColorInputBuffer(batch, colorBuffer); + _shapes[shape].draw(batch); +} + +void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape, gpu::BufferPointer& colorBuffer) { + batch.setInputFormat(getWireStreamFormat()); + setupColorInputBuffer(batch, colorBuffer); + _shapes[shape].drawWire(batch); +} + void GeometryCache::renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer) { - batch.setInputFormat(getInstancedSolidStreamFormat()); - setupBatchInstance(batch, colorBuffer); + batch.setInputFormat(getSolidStreamFormat()); + setupColorInputBuffer(batch, colorBuffer); _shapes[shape].drawInstances(batch, count); } void GeometryCache::renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer) { - batch.setInputFormat(getInstancedWireStreamFormat()); - setupBatchInstance(batch, colorBuffer); + batch.setInputFormat(getWireStreamFormat()); + setupColorInputBuffer(batch, colorBuffer); _shapes[shape].drawWireInstances(batch, count); } -void setupBatchFadeInstance(gpu::Batch& batch, gpu::BufferPointer colorBuffer, - gpu::BufferPointer fadeBuffer1, gpu::BufferPointer fadeBuffer2, gpu::BufferPointer fadeBuffer3) { - gpu::BufferView colorView(colorBuffer, COLOR_ELEMENT); - gpu::BufferView texCoord2View(fadeBuffer1, TEXCOORD4_ELEMENT); - gpu::BufferView texCoord3View(fadeBuffer2, TEXCOORD4_ELEMENT); - gpu::BufferView texCoord4View(fadeBuffer3, TEXCOORD4_ELEMENT); - batch.setInputBuffer(gpu::Stream::COLOR, colorView); - batch.setInputBuffer(gpu::Stream::TEXCOORD2, texCoord2View); - batch.setInputBuffer(gpu::Stream::TEXCOORD3, texCoord3View); - batch.setInputBuffer(gpu::Stream::TEXCOORD4, texCoord4View); -} - -void GeometryCache::renderFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer, - gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3) { - batch.setInputFormat(getInstancedSolidFadeStreamFormat()); - setupBatchFadeInstance(batch, colorBuffer, fadeBuffer1, fadeBuffer2, fadeBuffer3); - _shapes[shape].drawInstances(batch, count); -} - -void GeometryCache::renderWireFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer, - gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3) { - batch.setInputFormat(getInstancedWireFadeStreamFormat()); - setupBatchFadeInstance(batch, colorBuffer, fadeBuffer1, fadeBuffer2, fadeBuffer3); - _shapes[shape].drawWireInstances(batch, count); -} - -void GeometryCache::renderCube(gpu::Batch& batch) { - renderShape(batch, Cube); -} - -void GeometryCache::renderWireCube(gpu::Batch& batch) { - renderWireShape(batch, Cube); -} - -void GeometryCache::renderCube(gpu::Batch& batch, const glm::vec4& color) { - renderShape(batch, Cube, color); -} - -void GeometryCache::renderWireCube(gpu::Batch& batch, const glm::vec4& color) { - renderWireShape(batch, Cube, color); -} - -void GeometryCache::renderSphere(gpu::Batch& batch) { - renderShape(batch, Sphere); -} - -void GeometryCache::renderWireSphere(gpu::Batch& batch) { - renderWireShape(batch, Sphere); -} - -void GeometryCache::renderSphere(gpu::Batch& batch, const glm::vec4& color) { - renderShape(batch, Sphere, color); -} - -void GeometryCache::renderWireSphere(gpu::Batch& batch, const glm::vec4& color) { - renderWireShape(batch, Sphere, color); -} - void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, int majorRows, int majorCols, float majorEdge, int minorRows, int minorCols, float minorEdge, const glm::vec4& color, bool forward, int id) { @@ -963,33 +823,30 @@ void GeometryCache::updateVertices(int id, const QVector& points, con #endif // def WANT_DEBUG } - const int FLOATS_PER_VERTEX = 2 + 3; // vertices + normals - const int NUM_POS_COORDS = 2; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); + const int FLOATS_PER_VERTEX = 2; // vertices details.isCreated = true; details.vertices = points.size(); details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); - // TODO: circle3D overlays use this to define their vertices, so they need tex coords - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - - details.vertices = points.size(); - details.vertexSize = FLOATS_PER_VERTEX; + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; @@ -997,7 +854,6 @@ void GeometryCache::updateVertices(int id, const QVector& points, con int* colorData = new int[details.vertices]; int* colorDataAt = colorData; - const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); auto pointCount = points.size(); auto colorCount = colors.size(); int compactColor = 0; @@ -1005,19 +861,16 @@ void GeometryCache::updateVertices(int id, const QVector& points, con const auto& point = points[i]; *(vertex++) = point.x; *(vertex++) = point.y; - *(vertex++) = NORMAL.x; - *(vertex++) = NORMAL.y; - *(vertex++) = NORMAL.z; if (i < colorCount) { const auto& color = colors[i]; - compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); + compactColor = GeometryCache::toCompactColor(color); } *(colorDataAt++) = compactColor; } + details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); + const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData); delete[] vertexData; delete[] colorData; @@ -1040,32 +893,30 @@ void GeometryCache::updateVertices(int id, const QVector& points, con #endif // def WANT_DEBUG } - const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals - const int NUM_POS_COORDS = 3; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); + const int FLOATS_PER_VERTEX = 3; // vertices details.isCreated = true; details.vertices = points.size(); details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - - details.vertices = points.size(); - details.vertexSize = FLOATS_PER_VERTEX; + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); // Default to white int compactColor = 0xFFFFFFFF; @@ -1075,28 +926,23 @@ void GeometryCache::updateVertices(int id, const QVector& points, con int* colorData = new int[details.vertices]; int* colorDataAt = colorData; - const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); auto pointCount = points.size(); auto colorCount = colors.size(); for (auto i = 0; i < pointCount; i++) { const glm::vec3& point = points[i]; - if (i < colorCount) { - const glm::vec4& color = colors[i]; - compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - } *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; - *(vertex++) = NORMAL.x; - *(vertex++) = NORMAL.y; - *(vertex++) = NORMAL.z; + if (i < colorCount) { + const glm::vec4& color = colors[i]; + compactColor = GeometryCache::toCompactColor(color); + } *(colorDataAt++) = compactColor; } details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); + const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData); delete[] vertexData; delete[] colorData; @@ -1120,69 +966,55 @@ void GeometryCache::updateVertices(int id, const QVector& points, con #endif // def WANT_DEBUG } - const int FLOATS_PER_VERTEX = 3 + 3 + 2; // vertices + normals + tex coords + const int FLOATS_PER_VERTEX = 3 + 2; // vertices + tex coords const int NUM_POS_COORDS = 3; - const int NUM_NORMAL_COORDS = 3; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); - const int VERTEX_TEX_OFFSET = VERTEX_NORMAL_OFFSET + NUM_NORMAL_COORDS * sizeof(float); + const int VERTEX_TEX_OFFSET = NUM_POS_COORDS * sizeof(float); details.isCreated = true; details.vertices = points.size(); details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); + details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEX_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, 1, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, 2, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(2)._stride); assert(points.size() == texCoords.size()); - details.vertices = points.size(); - details.vertexSize = FLOATS_PER_VERTEX; - - int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; - int* colorData = new int[details.vertices]; - int* colorDataAt = colorData; - - const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); for (int i = 0; i < points.size(); i++) { glm::vec3 point = points[i]; glm::vec2 texCoord = texCoords[i]; *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; - *(vertex++) = NORMAL.x; - *(vertex++) = NORMAL.y; - *(vertex++) = NORMAL.z; *(vertex++) = texCoord.x; *(vertex++) = texCoord.y; - - *(colorDataAt++) = compactColor; } - details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); - details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData); + details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*)vertexData); + const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); + int compactColor = GeometryCache::toCompactColor(color); + details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); delete[] vertexData; - delete[] colorData; #ifdef WANT_DEBUG qCDebug(renderutils) << "new registered linestrip buffer made -- _registeredVertices.size():" << _registeredVertices.size(); @@ -1239,12 +1071,11 @@ void GeometryCache::renderBevelCornersRect(gpu::Batch& batch, int x, int y, int details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); + details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); float vertexBuffer[NUM_FLOATS]; // only vertices, no normals because we're a 2D quad int vertexPoint = 0; @@ -1283,16 +1114,9 @@ void GeometryCache::renderBevelCornersRect(gpu::Batch& batch, int x, int y, int vertexBuffer[vertexPoint++] = x + width; vertexBuffer[vertexPoint++] = y + bevelDistance; - int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - int colors[NUM_VERTICES] = { compactColor, compactColor, compactColor, compactColor, - compactColor, compactColor, compactColor, compactColor }; - - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); - details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + int compactColor = GeometryCache::toCompactColor(color); + details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); @@ -1322,52 +1146,46 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co #endif // def WANT_DEBUG } - const int FLOATS_PER_VERTEX = 2 + 3; // vertices + normals - const int VERTICES = 4; // 1 quad = 4 vertices - const int NUM_POS_COORDS = 2; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); - if (!details.isCreated) { + static const int FLOATS_PER_VERTEX = 2; // vertices + static const int VERTICES = 4; // 1 quad = 4 vertices details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); - - const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { - minCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, - maxCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, - minCorner.x, maxCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, - maxCorner.x, maxCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, + minCorner.x, minCorner.y, + maxCorner.x, minCorner.y, + minCorner.x, maxCorner.y, + maxCorner.x, maxCorner.y, }; - const int NUM_COLOR_SCALARS_PER_QUAD = 4; - int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); - details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); + int compactColor = GeometryCache::toCompactColor(color); + details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); @@ -1409,59 +1227,49 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co #endif // def WANT_DEBUG } - const int FLOATS_PER_VERTEX = 2 + 3 + 2; // vertices + normals + tex coords - const int VERTICES = 4; // 1 quad = 4 vertices - const int NUM_POS_COORDS = 2; - const int NUM_NORMAL_COORDS = 3; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); - const int VERTEX_TEXCOORD_OFFSET = VERTEX_NORMAL_OFFSET + NUM_NORMAL_COORDS * sizeof(float); - if (!details.isCreated) { + static const int FLOATS_PER_VERTEX = 2 + 2; // vertices + tex coords + static const int VERTICES = 4; // 1 quad = 4 vertices + static const int NUM_POS_COORDS = 2; + static const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); - auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; - details.streamFormat = streamFormat; details.stream = stream; - // zzmp: fix the normal across all renderQuad - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); + details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEXCOORD_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, 1, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, 2, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(2)._stride); - - const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { - minCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMinCorner.x, texCoordMinCorner.y, - maxCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMaxCorner.x, texCoordMinCorner.y, - minCorner.x, maxCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMinCorner.x, texCoordMaxCorner.y, - maxCorner.x, maxCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMaxCorner.x, texCoordMaxCorner.y, + minCorner.x, minCorner.y, texCoordMinCorner.x, texCoordMinCorner.y, + maxCorner.x, minCorner.y, texCoordMaxCorner.x, texCoordMinCorner.y, + minCorner.x, maxCorner.y, texCoordMinCorner.x, texCoordMaxCorner.y, + maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y, }; - - const int NUM_COLOR_SCALARS_PER_QUAD = 4; - int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); - details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); + int compactColor = GeometryCache::toCompactColor(color); + details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); @@ -1491,54 +1299,46 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co #endif // def WANT_DEBUG } - const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals - const int VERTICES = 4; // 1 quad = 4 vertices - const int NUM_POS_COORDS = 3; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); - if (!details.isCreated) { + static const int FLOATS_PER_VERTEX = 3; // vertices + static const int VERTICES = 4; // 1 quad = 4 vertices details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); - auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; - details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); - - const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { - minCorner.x, minCorner.y, minCorner.z, NORMAL.x, NORMAL.y, NORMAL.z, - maxCorner.x, minCorner.y, minCorner.z, NORMAL.x, NORMAL.y, NORMAL.z, - minCorner.x, maxCorner.y, maxCorner.z, NORMAL.x, NORMAL.y, NORMAL.z, - maxCorner.x, maxCorner.y, maxCorner.z, NORMAL.x, NORMAL.y, NORMAL.z, + minCorner.x, minCorner.y, minCorner.z, + maxCorner.x, minCorner.y, minCorner.z, + minCorner.x, maxCorner.y, maxCorner.z, + maxCorner.x, maxCorner.y, maxCorner.z, }; - const int NUM_COLOR_SCALARS_PER_QUAD = 4; - int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); - details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); + int compactColor = GeometryCache::toCompactColor(color); + details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); @@ -1587,56 +1387,49 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons #endif // def WANT_DEBUG } - const int FLOATS_PER_VERTEX = 3 + 3 + 2; // vertices + normals + tex coords - const int VERTICES = 4; // 1 quad = 4 vertices - const int NUM_POS_COORDS = 3; - const int NUM_NORMAL_COORDS = 3; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); - const int VERTEX_TEXCOORD_OFFSET = VERTEX_NORMAL_OFFSET + NUM_NORMAL_COORDS * sizeof(float); - - if (!details.isCreated) { + static const int FLOATS_PER_VERTEX = 3 + 2; // vertices + tex coords + static const int VERTICES = 4; // 1 quad = 4 vertices + static const int NUM_POS_COORDS = 3; + static const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; // NOTE: this isn't used for BatchItemDetails maybe we can get rid of it auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); - details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; + details.colorBuffer = colorBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); + details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEXCOORD_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, 1, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, 2, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(2)._stride); - - const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { - bottomLeft.x, bottomLeft.y, bottomLeft.z, NORMAL.x, NORMAL.y, NORMAL.z, texCoordBottomLeft.x, texCoordBottomLeft.y, - bottomRight.x, bottomRight.y, bottomRight.z, NORMAL.x, NORMAL.y, NORMAL.z, texCoordBottomRight.x, texCoordBottomRight.y, - topLeft.x, topLeft.y, topLeft.z, NORMAL.x, NORMAL.y, NORMAL.z, texCoordTopLeft.x, texCoordTopLeft.y, - topRight.x, topRight.y, topRight.z, NORMAL.x, NORMAL.y, NORMAL.z, texCoordTopRight.x, texCoordTopRight.y, + bottomLeft.x, bottomLeft.y, bottomLeft.z, texCoordBottomLeft.x, texCoordBottomLeft.y, + bottomRight.x, bottomRight.y, bottomRight.z, texCoordBottomRight.x, texCoordBottomRight.y, + topLeft.x, topLeft.y, topLeft.z, texCoordTopLeft.x, texCoordTopLeft.y, + topRight.x, topRight.y, topRight.z, texCoordTopRight.x, texCoordTopRight.y, }; - const int NUM_COLOR_SCALARS_PER_QUAD = 4; - int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); - details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); + int compactColor = GeometryCache::toCompactColor(color); + details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); @@ -1644,6 +1437,88 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } +void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, + const glm::vec4& color1, const glm::vec4& color2, int id) { + + bool registered = (id != UNKNOWN_ID); + Vec3Pair key(p1, p2); + + BatchItemDetails& details = _registeredLine3DVBOs[id]; + + // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed + if (registered && details.isCreated) { + Vec3Pair& lastKey = _lastRegisteredLine3D[id]; + if (lastKey != key) { + details.clear(); + _lastRegisteredLine3D[id] = key; +#ifdef WANT_DEBUG + qCDebug(renderutils) << "renderLine() 3D ... RELEASING REGISTERED line"; +#endif // def WANT_DEBUG + } +#ifdef WANT_DEBUG + else { + qCDebug(renderutils) << "renderLine() 3D ... REUSING PREVIOUSLY REGISTERED line"; + } +#endif // def WANT_DEBUG + } + + if (!details.isCreated) { + static const int FLOATS_PER_VERTEX = 3; // vertices + static const int vertices = 2; + + details.isCreated = true; + details.vertices = vertices; + details.vertexSize = FLOATS_PER_VERTEX; + + auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); + auto colorBuffer = std::make_shared(); + auto streamFormat = std::make_shared(); + auto stream = std::make_shared(); + + details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; + details.colorBuffer = colorBuffer; + details.streamFormat = streamFormat; + details.stream = stream; + + details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); + + float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { + p1.x, p1.y, p1.z, + p2.x, p2.y, p2.z + }; + + const int NUM_COLOR_SCALARS = 2; + int compactColor1 = GeometryCache::toCompactColor(color1); + int compactColor2 = GeometryCache::toCompactColor(color2); + int colors[NUM_COLOR_SCALARS] = { compactColor1, compactColor2 }; + + details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); + const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); + details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + +#ifdef WANT_DEBUG + if (id == UNKNOWN_ID) { + qCDebug(renderutils) << "new renderLine() 3D VBO made -- _line3DVBOs.size():" << _line3DVBOs.size(); + } else { + qCDebug(renderutils) << "new registered renderLine() 3D VBO made -- _registeredLine3DVBOs.size():" << _registeredLine3DVBOs.size(); + } +#endif + } + + batch.setInputFormat(details.streamFormat); + batch.setInputStream(0, *details.stream); + batch.draw(gpu::LINES, 2, 0); +} + void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, const float dash_length, const float gap_length, int id) { @@ -1664,11 +1539,6 @@ void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, if (!details.isCreated) { - int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - // draw each line segment with appropriate gaps const float SEGMENT_LENGTH = dash_length + gap_length; float length = glm::distance(start, end); @@ -1679,77 +1549,60 @@ void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, glm::vec3 dashVector = segmentVector / SEGMENT_LENGTH * dash_length; glm::vec3 gapVector = segmentVector / SEGMENT_LENGTH * gap_length; - const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals - const int NUM_POS_COORDS = 3; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); + const int FLOATS_PER_VERTEX = 3; // vertices + details.isCreated = true; details.vertices = (segmentCountFloor + 1) * 2; details.vertexSize = FLOATS_PER_VERTEX; - details.isCreated = true; auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - - int* colorData = new int[details.vertices]; - int* colorDataAt = colorData; + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; - const glm::vec3 NORMAL(1.0f, 0.0f, 0.0f); glm::vec3 point = start; *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; - *(vertex++) = NORMAL.x; - *(vertex++) = NORMAL.y; - *(vertex++) = NORMAL.z; - *(colorDataAt++) = compactColor; for (int i = 0; i < segmentCountFloor; i++) { point += dashVector; *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; - *(vertex++) = NORMAL.x; - *(vertex++) = NORMAL.y; - *(vertex++) = NORMAL.z; - *(colorDataAt++) = compactColor; point += gapVector; *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; - *(vertex++) = NORMAL.x; - *(vertex++) = NORMAL.y; - *(vertex++) = NORMAL.z; - *(colorDataAt++) = compactColor; } *(vertex++) = end.x; *(vertex++) = end.y; *(vertex++) = end.z; - *(vertex++) = NORMAL.x; - *(vertex++) = NORMAL.y; - *(vertex++) = NORMAL.z; - *(colorDataAt++) = compactColor; details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); - details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData); + const glm::vec3 NORMAL(1.0f, 0.0f, 0.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); + int compactColor = GeometryCache::toCompactColor(color); + details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); delete[] vertexData; - delete[] colorData; #ifdef WANT_DEBUG if (registered) { @@ -1770,6 +1623,7 @@ int GeometryCache::BatchItemDetails::population = 0; GeometryCache::BatchItemDetails::BatchItemDetails() : verticesBuffer(NULL), +normalBuffer(NULL), colorBuffer(NULL), streamFormat(NULL), stream(NULL), @@ -1784,6 +1638,7 @@ isCreated(false) { GeometryCache::BatchItemDetails::BatchItemDetails(const GeometryCache::BatchItemDetails& other) : verticesBuffer(other.verticesBuffer), +normalBuffer(other.normalBuffer), colorBuffer(other.colorBuffer), streamFormat(other.streamFormat), stream(other.stream), @@ -1808,184 +1663,12 @@ void GeometryCache::BatchItemDetails::clear() { isCreated = false; uniformBuffer.reset(); verticesBuffer.reset(); + normalBuffer.reset(); colorBuffer.reset(); streamFormat.reset(); stream.reset(); } -void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, - const glm::vec4& color1, const glm::vec4& color2, int id) { - - bool registered = (id != UNKNOWN_ID); - Vec3Pair key(p1, p2); - - BatchItemDetails& details = _registeredLine3DVBOs[id]; - - int compactColor1 = ((int(color1.x * 255.0f) & 0xFF)) | - ((int(color1.y * 255.0f) & 0xFF) << 8) | - ((int(color1.z * 255.0f) & 0xFF) << 16) | - ((int(color1.w * 255.0f) & 0xFF) << 24); - - int compactColor2 = ((int(color2.x * 255.0f) & 0xFF)) | - ((int(color2.y * 255.0f) & 0xFF) << 8) | - ((int(color2.z * 255.0f) & 0xFF) << 16) | - ((int(color2.w * 255.0f) & 0xFF) << 24); - - - // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed - if (registered && details.isCreated) { - Vec3Pair& lastKey = _lastRegisteredLine3D[id]; - if (lastKey != key) { - details.clear(); - _lastRegisteredLine3D[id] = key; -#ifdef WANT_DEBUG - qCDebug(renderutils) << "renderLine() 3D ... RELEASING REGISTERED line"; -#endif // def WANT_DEBUG - } -#ifdef WANT_DEBUG - else { - qCDebug(renderutils) << "renderLine() 3D ... REUSING PREVIOUSLY REGISTERED line"; - } -#endif // def WANT_DEBUG - } - - const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals - const int NUM_POS_COORDS = 3; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); - const int vertices = 2; - if (!details.isCreated) { - - details.isCreated = true; - details.vertices = vertices; - details.vertexSize = FLOATS_PER_VERTEX; - - auto verticesBuffer = std::make_shared(); - auto colorBuffer = std::make_shared(); - auto streamFormat = std::make_shared(); - auto stream = std::make_shared(); - - details.verticesBuffer = verticesBuffer; - details.colorBuffer = colorBuffer; - details.streamFormat = streamFormat; - details.stream = stream; - - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - - const glm::vec3 NORMAL(1.0f, 0.0f, 0.0f); - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { - p1.x, p1.y, p1.z, NORMAL.x, NORMAL.y, NORMAL.z, - p2.x, p2.y, p2.z, NORMAL.x, NORMAL.y, NORMAL.z }; - - const int NUM_COLOR_SCALARS = 2; - int colors[NUM_COLOR_SCALARS] = { compactColor1, compactColor2 }; - - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); - details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); - -#ifdef WANT_DEBUG - if (id == UNKNOWN_ID) { - qCDebug(renderutils) << "new renderLine() 3D VBO made -- _line3DVBOs.size():" << _line3DVBOs.size(); - } else { - qCDebug(renderutils) << "new registered renderLine() 3D VBO made -- _registeredLine3DVBOs.size():" << _registeredLine3DVBOs.size(); - } -#endif - } - - // this is what it takes to render a quad - batch.setInputFormat(details.streamFormat); - batch.setInputStream(0, *details.stream); - batch.draw(gpu::LINES, 2, 0); -} - -void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, - const glm::vec4& color1, const glm::vec4& color2, int id) { - - bool registered = (id != UNKNOWN_ID); - Vec2Pair key(p1, p2); - - BatchItemDetails& details = _registeredLine2DVBOs[id]; - - int compactColor1 = ((int(color1.x * 255.0f) & 0xFF)) | - ((int(color1.y * 255.0f) & 0xFF) << 8) | - ((int(color1.z * 255.0f) & 0xFF) << 16) | - ((int(color1.w * 255.0f) & 0xFF) << 24); - - int compactColor2 = ((int(color2.x * 255.0f) & 0xFF)) | - ((int(color2.y * 255.0f) & 0xFF) << 8) | - ((int(color2.z * 255.0f) & 0xFF) << 16) | - ((int(color2.w * 255.0f) & 0xFF) << 24); - - - // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed - if (registered && details.isCreated) { - Vec2Pair& lastKey = _lastRegisteredLine2D[id]; - if (lastKey != key) { - details.clear(); - _lastRegisteredLine2D[id] = key; -#ifdef WANT_DEBUG - qCDebug(renderutils) << "renderLine() 2D ... RELEASING REGISTERED line"; -#endif // def WANT_DEBUG - } -#ifdef WANT_DEBUG - else { - qCDebug(renderutils) << "renderLine() 2D ... REUSING PREVIOUSLY REGISTERED line"; - } -#endif // def WANT_DEBUG - } - - const int FLOATS_PER_VERTEX = 2; - const int vertices = 2; - if (!details.isCreated) { - - details.isCreated = true; - details.vertices = vertices; - details.vertexSize = FLOATS_PER_VERTEX; - - auto verticesBuffer = std::make_shared(); - auto colorBuffer = std::make_shared(); - auto streamFormat = std::make_shared(); - auto stream = std::make_shared(); - - details.verticesBuffer = verticesBuffer; - details.colorBuffer = colorBuffer; - details.streamFormat = streamFormat; - details.stream = stream; - - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - - - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { p1.x, p1.y, p2.x, p2.y }; - - const int NUM_COLOR_SCALARS = 2; - int colors[NUM_COLOR_SCALARS] = { compactColor1, compactColor2 }; - - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); - details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); - -#ifdef WANT_DEBUG - if (id == UNKNOWN_ID) { - qCDebug(renderutils) << "new renderLine() 2D VBO made -- _line3DVBOs.size():" << _line2DVBOs.size(); - } else { - qCDebug(renderutils) << "new registered renderLine() 2D VBO made -- _registeredLine2DVBOs.size():" << _registeredLine2DVBOs.size(); - } -#endif - } - - // this is what it takes to render a quad - batch.setInputFormat(details.streamFormat); - batch.setInputStream(0, *details.stream); - batch.draw(gpu::LINES, 2, 0); -} - void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { static std::once_flag once; std::call_once(once, [&]() { @@ -2175,24 +1858,24 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transp std::call_once(once, [&]() { using namespace shader::render_utils::program; - _forwardSimpleShader = gpu::Shader::createProgram(simple_forward); - _forwardTransparentShader = gpu::Shader::createProgram(simple_translucent_forward); - _forwardUnlitShader = gpu::Shader::createProgram(simple_unlit_forward); + _shapeShaders[std::make_tuple(false, false, true, false)] = gpu::Shader::createProgram(simple_forward); + _shapeShaders[std::make_tuple(true, false, true, false)] = gpu::Shader::createProgram(simple_translucent_forward); + _shapeShaders[std::make_tuple(false, true, true, false)] = gpu::Shader::createProgram(simple_unlit_forward); - _simpleShader = gpu::Shader::createProgram(simple); - _transparentShader = gpu::Shader::createProgram(simple_translucent); - _unlitShader = gpu::Shader::createProgram(simple_unlit); + _shapeShaders[std::make_tuple(false, false, false, false)] = gpu::Shader::createProgram(simple); + _shapeShaders[std::make_tuple(true, false, false, false)] = gpu::Shader::createProgram(simple_translucent); + _shapeShaders[std::make_tuple(false, true, false, false)] = gpu::Shader::createProgram(simple_unlit); }); } else { static std::once_flag once; std::call_once(once, [&]() { using namespace shader::render_utils::program; // Fading is currently disabled during forward rendering - _forwardSimpleFadeShader = gpu::Shader::createProgram(simple_forward); - _forwardUnlitFadeShader = gpu::Shader::createProgram(simple_unlit_forward); + _shapeShaders[std::make_tuple(false, false, true, true)] = gpu::Shader::createProgram(simple_forward); + _shapeShaders[std::make_tuple(false, true, true, true)] = gpu::Shader::createProgram(simple_unlit_forward); - _simpleFadeShader = gpu::Shader::createProgram(simple_fade); - _unlitFadeShader = gpu::Shader::createProgram(simple_unlit_fade); + _shapeShaders[std::make_tuple(false, false, false, true)] = gpu::Shader::createProgram(simple_fade); + _shapeShaders[std::make_tuple(false, true, false, true)] = gpu::Shader::createProgram(simple_unlit_fade); }); } @@ -2220,20 +1903,13 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transp PrepareStencil::testMaskDrawShapeNoAA(*state); } - gpu::ShaderPointer program; - if (config.isForward()) { - program = (config.isUnlit()) ? (config.isFading() ? _forwardUnlitFadeShader : _forwardUnlitShader) : - (config.isFading() ? _forwardSimpleFadeShader : (config.isTransparent() ? _forwardTransparentShader : _forwardSimpleShader)); - } else { - program = (config.isUnlit()) ? (config.isFading() ? _unlitFadeShader : _unlitShader) : - (config.isFading() ? _simpleFadeShader : (config.isTransparent() ? _transparentShader : _simpleShader)); - } + gpu::ShaderPointer program = _shapeShaders[std::make_tuple(config.isTransparent(), config.isUnlit(), config.isForward(), config.isFading())]; gpu::PipelinePointer pipeline = gpu::Pipeline::create(program, state); _simplePrograms.insert(config, pipeline); return pipeline; } -uint32_t toCompactColor(const glm::vec4& color) { +uint32_t GeometryCache::toCompactColor(const glm::vec4& color) { uint32_t compactColor = ((int(color.x * 255.0f) & 0xFF)) | ((int(color.y * 255.0f) & 0xFF) << 8) | ((int(color.z * 255.0f) & 0xFF) << 16) | @@ -2251,7 +1927,7 @@ void renderInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color // Add color to named buffer { gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER); - auto compactColor = toCompactColor(color); + const uint32_t compactColor = GeometryCache::toCompactColor(color); instanceColorBuffer->append(compactColor); } @@ -2268,55 +1944,6 @@ void renderInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color }); } -static const size_t INSTANCE_FADE_BUFFER1 = 1; -static const size_t INSTANCE_FADE_BUFFER2 = 2; -static const size_t INSTANCE_FADE_BUFFER3 = 3; - -void renderFadeInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, int fadeCategory, float fadeThreshold, - const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, bool isWire, - const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) { - // Add pipeline to name - std::string instanceName = (isWire ? "wire_shapes_" : "solid_shapes_") + std::to_string(shape) + "_" + std::to_string(std::hash()(pipeline)); - - // Add color to named buffer - { - gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER); - auto compactColor = toCompactColor(color); - instanceColorBuffer->append(compactColor); - } - // Add fade parameters to named buffers - { - gpu::BufferPointer fadeBuffer1 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER1); - gpu::BufferPointer fadeBuffer2 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER2); - gpu::BufferPointer fadeBuffer3 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER3); - // Pack parameters in 3 vec4s - glm::vec4 fadeData1; - glm::vec4 fadeData2; - glm::vec4 fadeData3; - FadeEffect::packToAttributes(fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize, - fadeData1, fadeData2, fadeData3); - fadeBuffer1->append(fadeData1); - fadeBuffer2->append(fadeData2); - fadeBuffer3->append(fadeData3); - } - - // Add call to named buffer - batch.setupNamedCalls(instanceName, [args, isWire, pipeline, shape](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { - auto& buffers = data.buffers; - batch.setPipeline(pipeline->pipeline); - pipeline->prepare(batch, args); - - if (isWire) { - DependencyManager::get()->renderWireFadeShapeInstances(batch, shape, data.count(), - buffers[INSTANCE_COLOR_BUFFER], buffers[INSTANCE_FADE_BUFFER1], buffers[INSTANCE_FADE_BUFFER2], buffers[INSTANCE_FADE_BUFFER3]); - } - else { - DependencyManager::get()->renderFadeShapeInstances(batch, shape, data.count(), - buffers[INSTANCE_COLOR_BUFFER], buffers[INSTANCE_FADE_BUFFER1], buffers[INSTANCE_FADE_BUFFER2], buffers[INSTANCE_FADE_BUFFER3]); - } - }); -} - void GeometryCache::renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, false, pipeline, shape); @@ -2327,74 +1954,11 @@ void GeometryCache::renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, renderInstances(args, batch, color, true, pipeline, shape); } -void GeometryCache::renderSolidFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, - int fadeCategory, float fadeThreshold, const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, - const render::ShapePipelinePointer& pipeline) { - assert(pipeline != nullptr); - renderFadeInstances(args, batch, color, fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize, false, pipeline, shape); -} - -void GeometryCache::renderWireFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, - int fadeCategory, float fadeThreshold, const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, - const render::ShapePipelinePointer& pipeline) { - assert(pipeline != nullptr); - renderFadeInstances(args, batch, color, fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize, true, pipeline, shape); -} - void GeometryCache::renderSolidSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, false, pipeline, GeometryCache::Sphere); } -void GeometryCache::renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { - assert(pipeline != nullptr); - renderInstances(args, batch, color, true, pipeline, GeometryCache::Sphere); -} - -// Enable this in a debug build to cause 'box' entities to iterate through all the -// available shape types, both solid and wireframes -//#define DEBUG_SHAPES - - -void GeometryCache::renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { - assert(pipeline != nullptr); -#ifdef DEBUG_SHAPES - static auto startTime = usecTimestampNow(); - renderInstances(INSTANCE_NAME, batch, color, pipeline, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { - - auto usecs = usecTimestampNow(); - usecs -= startTime; - auto msecs = usecs / USECS_PER_MSEC; - float seconds = msecs; - seconds /= MSECS_PER_SECOND; - float fractionalSeconds = seconds - floor(seconds); - int shapeIndex = (int)seconds; - - // Every second we flip to the next shape. - static const int SHAPE_COUNT = 5; - GeometryCache::Shape shapes[SHAPE_COUNT] = { - GeometryCache::Cube, - GeometryCache::Tetrahedron, - GeometryCache::Sphere, - GeometryCache::Icosahedron, - GeometryCache::Line, - }; - - shapeIndex %= SHAPE_COUNT; - GeometryCache::Shape shape = shapes[shapeIndex]; - - // For the first half second for a given shape, show the wireframe, for the second half, show the solid. - if (fractionalSeconds > 0.5f) { - renderInstances(INSTANCE_NAME, batch, color, true, pipeline, shape); - } else { - renderInstances(INSTANCE_NAME, batch, color, false, pipeline, shape); - } - }); -#else - renderInstances(args, batch, color, false, pipeline, GeometryCache::Cube); -#endif -} - void GeometryCache::renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { static const std::string INSTANCE_NAME = __FUNCTION__; assert(pipeline != nullptr); diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 179d49c076..3f06a6b1a3 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -180,31 +180,10 @@ public: void renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer); void renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer); - void renderFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer, - gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3); - void renderWireFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer, - gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3); - void renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color, - const render::ShapePipelinePointer& pipeline); - void renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline) { - renderSolidShapeInstance(args, batch, shape, glm::vec4(color, 1.0f), pipeline); - } - + const render::ShapePipelinePointer& pipeline); void renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline); - void renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline) { - renderWireShapeInstance(args, batch, shape, glm::vec4(color, 1.0f), pipeline); - } - - void renderSolidFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color, int fadeCategory, float fadeThreshold, - const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, - const render::ShapePipelinePointer& pipeline); - void renderWireFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color, int fadeCategory, float fadeThreshold, - const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, - const render::ShapePipelinePointer& pipeline); void renderSolidSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline); @@ -213,20 +192,6 @@ public: renderSolidSphereInstance(args, batch, glm::vec4(color, 1.0f), pipeline); } - void renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, - const render::ShapePipelinePointer& pipeline); - void renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline) { - renderWireSphereInstance(args, batch, glm::vec4(color, 1.0f), pipeline); - } - - void renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, - const render::ShapePipelinePointer& pipeline); - void renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline) { - renderSolidCubeInstance(args, batch, glm::vec4(color, 1.0f), pipeline); - } - void renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline); void renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color, @@ -235,24 +200,10 @@ public: } // Dynamic geometry - void renderShape(gpu::Batch& batch, Shape shape); - void renderWireShape(gpu::Batch& batch, Shape shape); - void renderShape(gpu::Batch& batch, Shape shape, const glm::vec4& color); - void renderWireShape(gpu::Batch& batch, Shape shape, const glm::vec4& color); + void renderShape(gpu::Batch& batch, Shape shape, gpu::BufferPointer& colorBuffer); + void renderWireShape(gpu::Batch& batch, Shape shape, gpu::BufferPointer& colorBuffer); size_t getShapeTriangleCount(Shape shape); - void renderCube(gpu::Batch& batch); - void renderWireCube(gpu::Batch& batch); - void renderCube(gpu::Batch& batch, const glm::vec4& color); - void renderWireCube(gpu::Batch& batch, const glm::vec4& color); - size_t getCubeTriangleCount(); - - void renderSphere(gpu::Batch& batch); - void renderWireSphere(gpu::Batch& batch); - void renderSphere(gpu::Batch& batch, const glm::vec4& color); - void renderWireSphere(gpu::Batch& batch, const glm::vec4& color); - size_t getSphereTriangleCount(); - void renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, int majorRows, int majorCols, float majorEdge, int minorRows, int minorCols, float minorEdge, @@ -262,10 +213,6 @@ public: void renderUnitQuad(gpu::Batch& batch, const glm::vec4& color, int id); - void renderUnitQuad(gpu::Batch& batch, int id) { - renderUnitQuad(batch, glm::vec4(1), id); - } - void renderQuad(gpu::Batch& batch, int x, int y, int width, int height, const glm::vec4& color, int id) { renderQuad(batch, glm::vec2(x,y), glm::vec2(x + width, y + height), color, id); } @@ -307,19 +254,6 @@ public: void renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, const float dash_length, const float gap_length, int id); - void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec3& color, int id) - { renderLine(batch, p1, p2, glm::vec4(color, 1.0f), id); } - - void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec4& color, int id) - { renderLine(batch, p1, p2, color, color, id); } - - void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, - const glm::vec3& color1, const glm::vec3& color2, int id) - { renderLine(batch, p1, p2, glm::vec4(color1, 1.0f), glm::vec4(color2, 1.0f), id); } - - void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, - const glm::vec4& color1, const glm::vec4& color2, int id); - void updateVertices(int id, const QVector& points, const glm::vec4& color); void updateVertices(int id, const QVector& points, const QVector& colors); void updateVertices(int id, const QVector& points, const glm::vec4& color); @@ -364,6 +298,8 @@ public: graphics::MeshPointer meshFromShape(Shape geometryShape, glm::vec3 color); + static uint32_t toCompactColor(const glm::vec4& color); + private: GeometryCache(); @@ -397,6 +333,7 @@ private: public: static int population; gpu::BufferPointer verticesBuffer; + gpu::BufferPointer normalBuffer; gpu::BufferPointer colorBuffer; gpu::BufferPointer uniformBuffer; gpu::Stream::FormatPointer streamFormat; @@ -445,18 +382,9 @@ private: QHash _lastRegisteredGridBuffer; QHash _registeredGridBuffers; - // FIXME: clean these up - static gpu::ShaderPointer _simpleShader; - static gpu::ShaderPointer _transparentShader; - static gpu::ShaderPointer _unlitShader; - static gpu::ShaderPointer _simpleFadeShader; - static gpu::ShaderPointer _unlitFadeShader; - static gpu::ShaderPointer _forwardSimpleShader; - static gpu::ShaderPointer _forwardTransparentShader; - static gpu::ShaderPointer _forwardUnlitShader; - static gpu::ShaderPointer _forwardSimpleFadeShader; - static gpu::ShaderPointer _forwardUnlitFadeShader; - + // transparent, unlit, forward, fade + static std::map, gpu::ShaderPointer> _shapeShaders; + // transparent, unlit, forward static std::map, render::ShapePipelinePointer> _shapePipelines; static QHash _simplePrograms; diff --git a/libraries/render-utils/src/GlobalLight.slh b/libraries/render-utils/src/GlobalLight.slh index 6702270a5a..de8702ea8c 100644 --- a/libraries/render-utils/src/GlobalLight.slh +++ b/libraries/render-utils/src/GlobalLight.slh @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 2/5/15. // Copyright 2013 High Fidelity, Inc. +// Copyright 2024 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 @@ -286,5 +287,78 @@ vec3 evalGlobalLightingAlphaBlended( } <@endfunc@> +<@if HIFI_USE_MTOON@> +<@func declareEvalGlobalLightingAlphaBlendedMToon()@> + +<$declareLightingAmbient(1, 1, 1)$> +<$declareLightingDirectional()$> + +float linearstep(float a, float b, float t) { + return clamp((t - a) / (b - a), 0.0, 1.0); +} + +vec3 evalGlobalLightingAlphaBlendedMToon( + mat4 invViewMat, float obscurance, vec3 positionES, vec3 normalWS, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, + float roughness, float opacity, vec3 shade, float shadingShift, float shadingToony, vec3 matcap, + vec3 parametricRim, float parametricRimFresnelPower, float parametricRimLift, vec3 rim, float rimMix, BITFIELD matKey) +{ + <$prepareGlobalLight(positionES, normalWS)$> + + SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragEyeDirWS); + + color += emissive * isEmissiveEnabled(); + + // Ambient + vec3 ambientDiffuse; + vec3 ambientSpecular; + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surfaceWS, metallic, fresnel, albedo, obscurance); + color += ambientDiffuse; + color += evalSpecularWithOpacity(ambientSpecular, opacity); + + // Directional MToon Shading + updateSurfaceDataWithLight(surfaceWS, lightDirection); + + float shading = surfaceWS.ndotl; + shading += shadingShift; // shadingShift includes both the scalar and texture values + shading = linearstep(-1.0 + shadingToony, 1.0 - shadingToony, shading); + + color += lightIrradiance * mix(albedo, shade, shading) * isDirectionalEnabled(); + + vec3 worldViewX = normalize(vec3(surfaceWS.eyeDir.z, 0.0, -surfaceWS.eyeDir.x)); + vec3 worldViewY = cross(surfaceWS.eyeDir, worldViewX); + vec2 matcapUV = vec2(dot(worldViewX, surfaceWS.normal), dot(worldViewY, surfaceWS.normal)) * 0.495 + 0.5; + const float epsilon = 0.00001; + + vec3 rimDiffuse; + <$evalMaterialMatcap(matcapUV, matcap, matKey, rimDiffuse)$>; + float rimColor = clamp(1.0 - dot(surfaceWS.normal, surfaceWS.eyeDir) + parametricRimLift, 0.0, 1.0); + rimColor = pow(rimColor, max(parametricRimFresnelPower, epsilon)); + rimDiffuse += rimColor * parametricRim; + rimDiffuse *= rim; + rimDiffuse = rimDiffuse * mix(vec3(1.0), vec3(0.0), rimMix); + color += rimDiffuse; + + // Haze + if (isHazeEnabled() > 0.0) { + if ((hazeParams.hazeMode & HAZE_MODE_IS_KEYLIGHT_ATTENUATED) == HAZE_MODE_IS_KEYLIGHT_ATTENUATED) { + color = computeHazeColorKeyLightAttenuation(color, lightDirection, fragPositionWS); + } + + if ((hazeParams.hazeMode & HAZE_MODE_IS_ACTIVE) == HAZE_MODE_IS_ACTIVE) { + vec4 hazeColor = computeHazeColor( + positionES, // fragment position in eye coordinates + fragPositionWS, // fragment position in world coordinates + invViewMat[3].xyz, // eye position in world coordinates + lightDirection // keylight direction vector in world coordinates + ); + + color = mix(color.rgb, hazeColor.rgb, hazeColor.a); + } + } + + return color; +} +<@endfunc@> +<@endif@> <@endif@> diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index b4aced626d..754c99c425 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -4,6 +4,7 @@ // // Created by Olivier Prat on 08/08/17. // Copyright 2017 High Fidelity, Inc. +// Copyright 2024 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 @@ -40,6 +41,7 @@ namespace gr { #define OUTLINE_STENCIL_MASK 1 extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); +extern void sortAndRenderZPassShapes(const ShapePlumberPointer& shapePlumber, const render::RenderContextPointer& renderContext, const render::ShapeBounds& inShapes, render::ItemBounds &itemBounds); HighlightResources::HighlightResources() { } @@ -108,10 +110,14 @@ PrepareDrawHighlight::PrepareDrawHighlight() { } void PrepareDrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { - auto destinationFrameBuffer = inputs; + RenderArgs* args = renderContext->args; + auto destinationFrameBuffer = inputs; _resources->update(destinationFrameBuffer); - outputs = _resources; + outputs.edit0() = _resources; + + outputs.edit1() = args->_renderMode; + args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; } gpu::PipelinePointer DrawHighlightMask::_stencilMaskPipeline; @@ -188,61 +194,7 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c batch.setProjectionJitter(jitter.x, jitter.y); batch.setViewTransform(viewMat); - const std::vector keys = { - ShapeKey::Builder(), ShapeKey::Builder().withFade(), - ShapeKey::Builder().withDeformed(), ShapeKey::Builder().withDeformed().withFade(), - ShapeKey::Builder().withDeformed().withDualQuatSkinned(), ShapeKey::Builder().withDeformed().withDualQuatSkinned().withFade(), - ShapeKey::Builder().withOwnPipeline(), ShapeKey::Builder().withOwnPipeline().withFade(), - ShapeKey::Builder().withDeformed().withOwnPipeline(), ShapeKey::Builder().withDeformed().withOwnPipeline().withFade(), - ShapeKey::Builder().withDeformed().withDualQuatSkinned().withOwnPipeline(), ShapeKey::Builder().withDeformed().withDualQuatSkinned().withOwnPipeline().withFade(), - }; - std::vector> sortedShapeKeys(keys.size()); - - const int OWN_PIPELINE_INDEX = 6; - for (const auto& items : inShapes) { - itemBounds.insert(itemBounds.end(), items.second.begin(), items.second.end()); - - int index = items.first.hasOwnPipeline() ? OWN_PIPELINE_INDEX : 0; - if (items.first.isDeformed()) { - index += 2; - if (items.first.isDualQuatSkinned()) { - index += 2; - } - } - - if (items.first.isFaded()) { - index += 1; - } - - sortedShapeKeys[index].push_back(items.first); - } - - // Render non-withOwnPipeline things - for (size_t i = 0; i < OWN_PIPELINE_INDEX; i++) { - auto& shapeKeys = sortedShapeKeys[i]; - if (shapeKeys.size() > 0) { - const auto& shapePipeline = _shapePlumber->pickPipeline(args, keys[i]); - args->_shapePipeline = shapePipeline; - for (const auto& key : shapeKeys) { - renderShapes(renderContext, _shapePlumber, inShapes.at(key)); - } - } - } - - // Render withOwnPipeline things - for (size_t i = OWN_PIPELINE_INDEX; i < keys.size(); i++) { - auto& shapeKeys = sortedShapeKeys[i]; - if (shapeKeys.size() > 0) { - args->_shapePipeline = nullptr; - for (const auto& key : shapeKeys) { - args->_itemShapeKey = key._flags.to_ulong(); - renderShapes(renderContext, _shapePlumber, inShapes.at(key)); - } - } - } - - args->_shapePipeline = nullptr; - args->_batch = nullptr; + sortAndRenderZPassShapes(_shapePlumber, renderContext, inShapes, itemBounds); }); _boundsBuffer->setData(itemBounds.size() * sizeof(render::ItemBound), (const gpu::Byte*) itemBounds.data()); @@ -452,13 +404,28 @@ const gpu::PipelinePointer& DebugHighlight::getDepthPipeline() { return _depthPipeline; } -void SelectionToHighlight::run(const render::RenderContextPointer& renderContext, Outputs& outputs) { +void SelectionToHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { auto scene = renderContext->_scene; auto highlightStage = scene->getStage(render::HighlightStage::getName()); - outputs.clear(); + auto outlines = inputs.get0(); + auto framebuffer = inputs.get1(); + + outputs.edit0().clear(); + outputs.edit1().clear(); _sharedParameters->_highlightIds.fill(render::HighlightStage::INVALID_INDEX); + outputs.edit1().reserve(outlines.size()); + for (auto item : outlines) { + render::HighlightStyle style = scene->getOutlineStyle(item.id, renderContext->args->getViewFrustum() , framebuffer->getHeight()); + auto selectionName = "__OUTLINE_MATERIAL" + style.toString(); + if (highlightStage->getHighlightIdBySelection(selectionName) == HighlightStage::INVALID_INDEX) { + HighlightStage::Index newIndex = highlightStage->addHighlight(selectionName, style); + outputs.edit1().push_back(newIndex); + } + scene->addItemToSelection(selectionName, item.id); + } + int numLayers = 0; auto highlightList = highlightStage->getActiveHighlightIds(); @@ -467,8 +434,8 @@ void SelectionToHighlight::run(const render::RenderContextPointer& renderContext if (!scene->isSelectionEmpty(highlight._selectionName)) { auto highlightId = highlightStage->getHighlightIdBySelection(highlight._selectionName); - _sharedParameters->_highlightIds[outputs.size()] = highlightId; - outputs.emplace_back(highlight._selectionName); + _sharedParameters->_highlightIds[outputs.edit0().size()] = highlightId; + outputs.edit0().emplace_back(highlight._selectionName); numLayers++; if (numLayers == HighlightSharedParameters::MAX_PASS_COUNT) { @@ -500,6 +467,7 @@ void DrawHighlightTask::configure(const Config& config) { void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { const auto items = inputs.getN(0).get(); + const auto& outlines = items[RenderFetchCullSortTask::OUTLINE]; const auto sceneFrameBuffer = inputs.getN(1); const auto primaryFramebuffer = inputs.getN(2); const auto deferredFrameTransform = inputs.getN(3); @@ -510,25 +478,27 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren 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); - - auto fadeEffect = DependencyManager::get(); initZPassPipelines(*shapePlumber, state, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); }); auto sharedParameters = std::make_shared(); - const auto highlightSelectionNames = task.addJob("SelectionToHighlight", sharedParameters); + const auto selectionToHighlightInputs = SelectionToHighlight::Inputs(outlines, primaryFramebuffer).asVarying(); + const auto highlightSelectionOutputs = task.addJob("SelectionToHighlight", selectionToHighlightInputs, sharedParameters); // Prepare for highlight group rendering. - const auto highlightResources = task.addJob("PrepareHighlight", primaryFramebuffer); + const auto prepareOutputs = task.addJob("PrepareHighlight", primaryFramebuffer); + const auto highlightResources = prepareOutputs.getN(0); render::Varying highlight0Rect; + const auto extractSelectionNameInput = Varying(highlightSelectionOutputs.getN(0)); for (auto i = 0; i < HighlightSharedParameters::MAX_PASS_COUNT; i++) { - const auto selectionName = task.addJob("ExtractSelectionName", highlightSelectionNames, i); + const auto selectionName = task.addJob("ExtractSelectionName", extractSelectionNameInput, i); const auto groupItems = addSelectItemJobs(task, selectionName, items); - const auto highlightedItemIDs = task.addJob("HighlightMetaToSubItemIDs", groupItems); + const auto highlightedSubItemIDs = task.addJob("HighlightMetaToSubItemIDs", groupItems); + const auto appendNonMetaOutlinesInput = AppendNonMetaOutlines::Inputs(highlightedSubItemIDs, groupItems).asVarying(); + const auto highlightedItemIDs = task.addJob("AppendNonMetaOutlines", appendNonMetaOutlinesInput); const auto highlightedItems = task.addJob("HighlightMetaToSubItems", highlightedItemIDs); // Sort @@ -558,6 +528,10 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren task.addJob(name, drawHighlightInputs, i, sharedParameters); } + // Cleanup + const auto cleanupInput = HighlightCleanup::Inputs(highlightSelectionOutputs.getN(1), prepareOutputs.getN(1)).asVarying(); + task.addJob("HighlightCleanup", cleanupInput); + // Debug highlight const auto debugInputs = DebugHighlight::Inputs(highlightResources, const_cast(highlight0Rect), jitter, primaryFramebuffer).asVarying(); task.addJob("HighlightDebug", debugInputs); @@ -577,3 +551,31 @@ const render::Varying DrawHighlightTask::addSelectItemJobs(JobModel& task, const return task.addJob("TransparentSelection", selectItemInput); } +void AppendNonMetaOutlines::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { + auto& scene = renderContext->_scene; + + outputs = inputs.get0(); + + const auto& groupItems = inputs.get1(); + for (auto idBound : groupItems) { + auto& item = scene->getItem(idBound.id); + if (item.exist() && !item.getKey().isMeta()) { + outputs.push_back(idBound.id); + } + } +} + + +void HighlightCleanup::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { + auto scene = renderContext->_scene; + auto highlightStage = scene->getStage(render::HighlightStage::getName()); + + for (auto index : inputs.get0()) { + std::string selectionName = highlightStage->getHighlight(index)._selectionName; + highlightStage->removeHighlight(index); + scene->removeSelection(selectionName); + } + + // Reset the render mode + renderContext->args->_renderMode = inputs.get1(); +} diff --git a/libraries/render-utils/src/HighlightEffect.h b/libraries/render-utils/src/HighlightEffect.h index 933503fdb5..a55ecdf46d 100644 --- a/libraries/render-utils/src/HighlightEffect.h +++ b/libraries/render-utils/src/HighlightEffect.h @@ -4,6 +4,7 @@ // // Created by Olivier Prat on 08/08/17. // Copyright 2017 High Fidelity, Inc. +// Copyright 2024 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 @@ -65,7 +66,7 @@ using HighlightSharedParametersPointer = std::shared_ptr; using JobModel = render::Job::ModelIO; PrepareDrawHighlight(); @@ -81,12 +82,13 @@ private: class SelectionToHighlight { public: - using Outputs = std::vector; - using JobModel = render::Job::ModelO; + using Inputs = render::VaryingSet2; + using Outputs = render::VaryingSet2, std::vector>; + using JobModel = render::Job::ModelIO; SelectionToHighlight(HighlightSharedParametersPointer parameters) : _sharedParameters{ parameters } {} - void run(const render::RenderContextPointer& renderContext, Outputs& outputs); + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); private: @@ -96,7 +98,7 @@ private: class ExtractSelectionName { public: - using Inputs = SelectionToHighlight::Outputs; + using Inputs = std::vector; using Outputs = std::string; using JobModel = render::Job::ModelIO; @@ -209,6 +211,25 @@ private: }; +class AppendNonMetaOutlines { +public: + using Inputs = render::VaryingSet2; + using Outputs = render::ItemIDs; + using JobModel = render::Job::ModelIO; + + AppendNonMetaOutlines() {} + + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); +}; + +class HighlightCleanup { +public: + using Inputs = render::VaryingSet2, render::Args::RenderMode>; + using JobModel = render::Job::ModelI; + + HighlightCleanup() {} + + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); +}; + #endif // hifi_render_utils_HighlightEffect_h - - diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 9adeb39e7c..7cd5646db3 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 10/3/15. // Copyright 2015 High Fidelity, Inc. +// Copyright 2024 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 @@ -225,6 +226,10 @@ void ModelMeshPartPayload::updateKey(const render::ItemKey& key) { builder.withMirror(); } + if (_drawMaterials.hasOutline()) { + builder.withOutline(); + } + _itemKey = builder.build(); } @@ -273,11 +278,15 @@ void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, PrimitiveMode pr if (hasTangents) { builder.withTangents(); } - if (hasLightmap) { - builder.withLightMap(); - } - if (isUnlit) { - builder.withUnlit(); + if (!_drawMaterials.isMToon()) { + if (hasLightmap) { + builder.withLightMap(); + } + if (isUnlit) { + builder.withUnlit(); + } + } else { + builder.withMToon(); } if (material) { builder.withCullFaceMode(material->getCullFaceMode()); @@ -352,12 +361,17 @@ void ModelMeshPartPayload::render(RenderArgs* args) { outColor = procedural->getColor(outColor); procedural->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f, _shapeKey.isDeformed(), _shapeKey.isDualQuatSkinned())); - batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); + + const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + _drawMesh->getColorBuffer()->setData(sizeof(compactColor), (const gpu::Byte*)&compactColor); } else if (!_itemKey.isMirror()) { // apply material properties if (RenderPipelines::bindMaterials(_drawMaterials, batch, args->_renderMode, args->_enableTexturing)) { args->_details._materialSwitches++; } + + const uint32_t compactColor = 0xFFFFFFFF; + _drawMesh->getColorBuffer()->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); } // Draw! @@ -390,6 +404,11 @@ ItemID ModelMeshPartPayload::computeMirrorView(ViewFrustum& viewFrustum) const { return MirrorModeHelpers::computeMirrorView(viewFrustum, transform.getTranslation(), transform.getRotation(), _mirrorMode, _portalExitID); } +render::HighlightStyle ModelMeshPartPayload::getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const { + return render::HighlightStyle::calculateOutlineStyle(_drawMaterials.getOutlineWidthMode(), _drawMaterials.getOutlineWidth(), _drawMaterials.getOutline(), + _parentTransform.getTranslation(), viewFrustum, height); +} + void ModelMeshPartPayload::setBlendshapeBuffer(const std::unordered_map& blendshapeBuffers, const QVector& blendedMeshSizes) { if (_meshIndex < blendedMeshSizes.length() && blendedMeshSizes.at(_meshIndex) == _meshNumVertices) { auto blendshapeBuffer = blendshapeBuffers.find(_meshIndex); @@ -446,4 +465,11 @@ template <> ItemID payloadComputeMirrorView(const ModelMeshPartPayload::Pointer& } return Item::INVALID_ITEM_ID; } + +template <> HighlightStyle payloadGetOutlineStyle(const ModelMeshPartPayload::Pointer& payload, const ViewFrustum& viewFrustum, const size_t height) { + if (payload) { + return payload->getOutlineStyle(viewFrustum, height); + } + return HighlightStyle(); +} } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 7e331a9497..0482212f84 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 10/3/15. // Copyright 2015 High Fidelity, Inc. +// Copyright 2024 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 @@ -17,6 +18,7 @@ #include #include #include +#include class ModelMeshPartPayload { public: @@ -62,6 +64,7 @@ public: void setPortalExitID(const QUuid& portalExitID) { _portalExitID = portalExitID; } bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const; render::ItemID computeMirrorView(ViewFrustum& viewFrustum) const; + render::HighlightStyle getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const; void addMaterial(graphics::MaterialLayer material) { _drawMaterials.push(material); } void removeMaterial(graphics::MaterialPointer material) { _drawMaterials.remove(material); } @@ -113,7 +116,7 @@ namespace render { template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args); template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Pointer& payload, const std::unordered_set& containingZones); template <> ItemID payloadComputeMirrorView(const ModelMeshPartPayload::Pointer& payload, ViewFrustum& viewFrustum); - + template <> HighlightStyle payloadGetOutlineStyle(const ModelMeshPartPayload::Pointer& payload, const ViewFrustum& viewFrustum, const size_t height); } #endif // hifi_MeshPartPayload_h diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7d622ab489..b7b645dd8a 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1846,8 +1846,8 @@ public: }; static void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& packed, const BlendshapeOffsetUnpacked& unpacked) { - float len = glm::compMax(glm::abs(unpacked.positionOffset)); - glm::vec3 normalizedPos(unpacked.positionOffset); + float len = max(abs(unpacked.positionOffsetX), max(abs(unpacked.positionOffsetY), abs(unpacked.positionOffsetZ))); + glm::vec3 normalizedPos(unpacked.positionOffsetX, unpacked.positionOffsetY, unpacked.positionOffsetZ); if (len > 0.0f) { normalizedPos /= len; } else { @@ -1857,8 +1857,8 @@ static void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uve packed = glm::uvec4( glm::floatBitsToUint(len), glm_packSnorm3x10_1x2(glm::vec4(normalizedPos, 0.0f)), - glm_packSnorm3x10_1x2(glm::vec4(unpacked.normalOffset, 0.0f)), - glm_packSnorm3x10_1x2(glm::vec4(unpacked.tangentOffset, 0.0f)) + glm_packSnorm3x10_1x2(glm::vec4(unpacked.normalOffsetX, unpacked.normalOffsetY, unpacked.normalOffsetZ, 0.0f)), + glm_packSnorm3x10_1x2(glm::vec4(unpacked.tangentOffsetX, unpacked.tangentOffsetY, unpacked.tangentOffsetZ, 0.0f)) ); } @@ -1966,10 +1966,19 @@ void Blender::run() { int index = blendshape.indices.at(j); auto& currentBlendshapeOffset = unpackedBlendshapeOffsets[index]; - currentBlendshapeOffset.positionOffset += blendshape.vertices.at(j) * vertexCoefficient; - currentBlendshapeOffset.normalOffset += blendshape.normals.at(j) * normalCoefficient; + glm::vec3 blendshapePosition = blendshape.vertices.at(j) * vertexCoefficient; + currentBlendshapeOffset.positionOffsetX += blendshapePosition.x; + currentBlendshapeOffset.positionOffsetY += blendshapePosition.y; + currentBlendshapeOffset.positionOffsetZ += blendshapePosition.z; + glm::vec3 blendshapeNormal = blendshape.normals.at(j) * normalCoefficient; + currentBlendshapeOffset.normalOffsetX += blendshapeNormal.x; + currentBlendshapeOffset.normalOffsetY += blendshapeNormal.y; + currentBlendshapeOffset.normalOffsetZ += blendshapeNormal.z; if (j < blendshape.tangents.size()) { - currentBlendshapeOffset.tangentOffset += blendshape.tangents.at(j) * normalCoefficient; + glm::vec3 blendshapeTangent = blendshape.tangents.at(j) * normalCoefficient; + currentBlendshapeOffset.tangentOffsetX += blendshapeTangent.x; + currentBlendshapeOffset.tangentOffsetY += blendshapeTangent.y; + currentBlendshapeOffset.tangentOffsetZ += blendshapeTangent.z; } } } diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index bffa6eb366..1055a4256d 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -94,6 +94,8 @@ void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs& } if (!inItems.empty()) { + auto deferredLightingEffect = DependencyManager::get(); + // Render the items gpu::doInBatch("DrawLayered3D::main", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; @@ -117,11 +119,20 @@ void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs& batch.setUniformBuffer(graphics::slot::buffer::Buffer::HazeParams, haze->getHazeParametersBuffer()); } + // Set the light + deferredLightingEffect->setupKeyLightBatch(args, batch); + + auto renderMethod = args->_renderMethod; + args->_renderMethod = Args::RenderMethod::FORWARD; if (_opaquePass) { renderStateSortShapes(renderContext, _shapePlumber, inItems, _maxDrawn); } else { renderShapes(renderContext, _shapePlumber, inItems, _maxDrawn); } + + deferredLightingEffect->unsetLocalLightsBatch(batch); + + args->_renderMethod = renderMethod; args->_batch = nullptr; }); } diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index b5a88b0ba9..e7253f6adc 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -5,6 +5,7 @@ // // Created by Zach Pomerantz on 1/28/2016. // Copyright 2016 High Fidelity, Inc. +// Copyright 2024 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 @@ -28,6 +29,8 @@ using namespace render; using namespace std::placeholders; +using namespace shader::render_utils::program; +using Key = render::ShapeKey; namespace ru { using render_utils::slot::texture::Texture; @@ -43,140 +46,167 @@ void initDeferredPipelines(ShapePlumber& plumber, const render::ShapePipeline::B 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 sortAndRenderZPassShapes(const ShapePlumberPointer& shapePlumber, const render::RenderContextPointer& renderContext, const render::ShapeBounds& inShapes, render::ItemBounds &itemBounds); void addPlumberPipeline(ShapePlumber& plumber, - const ShapeKey& key, int programId, + const ShapeKey& key, int programId, gpu::StatePointer& baseState, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* args); void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* args); static bool forceLightBatchSetter{ false }; +// TOOD: build this list algorithmically so we don't have to maintain it +std::vector> ALL_PIPELINES = { + // Simple + { Key::Builder(), simple, model_shadow }, + { Key::Builder().withTranslucent(), simple_translucent, model_shadow }, + { Key::Builder().withUnlit(), simple_unlit, model_shadow }, + { Key::Builder().withTranslucent().withUnlit(), simple_translucent_unlit, model_shadow }, + // Simple Fade + { Key::Builder().withFade(), simple_fade, model_shadow_fade }, + { Key::Builder().withTranslucent().withFade(), simple_translucent_fade, model_shadow_fade }, + { Key::Builder().withUnlit().withFade(), simple_unlit_fade, model_shadow_fade }, + { Key::Builder().withTranslucent().withUnlit().withFade(), simple_translucent_unlit_fade, model_shadow_fade }, + + // Unskinned + { Key::Builder().withMaterial(), model, model_shadow }, + { Key::Builder().withMaterial().withTangents(), model_normalmap, model_shadow }, + { Key::Builder().withMaterial().withTranslucent(), model_translucent, model_shadow }, + { Key::Builder().withMaterial().withTangents().withTranslucent(), model_normalmap_translucent, model_shadow }, + // Unskinned Unlit + { Key::Builder().withMaterial().withUnlit(), model_unlit, model_shadow }, + { Key::Builder().withMaterial().withTangents().withUnlit(), model_normalmap_unlit, model_shadow }, + { Key::Builder().withMaterial().withTranslucent().withUnlit(), model_translucent_unlit, model_shadow }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withUnlit(), model_normalmap_translucent_unlit, model_shadow }, + // Unskinned Lightmapped + { Key::Builder().withMaterial().withLightMap(), model_lightmap, model_shadow }, + { Key::Builder().withMaterial().withTangents().withLightMap(), model_normalmap_lightmap, model_shadow }, + { Key::Builder().withMaterial().withTranslucent().withLightMap(), model_translucent_lightmap, model_shadow }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap(), model_normalmap_translucent_lightmap, model_shadow }, + // Unskinned MToon + { Key::Builder().withMaterial().withMToon(), model_mtoon, model_shadow_mtoon }, + { Key::Builder().withMaterial().withTangents().withMToon(), model_normalmap_mtoon, model_shadow_mtoon }, + { Key::Builder().withMaterial().withTranslucent().withMToon(), model_translucent_mtoon, model_shadow_mtoon }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon(), model_normalmap_translucent_mtoon, model_shadow_mtoon }, + // Unskinned Fade + { Key::Builder().withMaterial().withFade(), model_fade, model_shadow_fade }, + { Key::Builder().withMaterial().withTangents().withFade(), model_normalmap_fade, model_shadow_fade }, + { Key::Builder().withMaterial().withTranslucent().withFade(), model_translucent_fade, model_shadow_fade }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withFade(), model_normalmap_translucent_fade, model_shadow_fade }, + // Unskinned Unlit Fade + { Key::Builder().withMaterial().withUnlit().withFade(), model_unlit_fade, model_shadow_fade }, + { Key::Builder().withMaterial().withTangents().withUnlit().withFade(), model_normalmap_unlit_fade, model_shadow_fade }, + { Key::Builder().withMaterial().withTranslucent().withUnlit().withFade(), model_translucent_unlit_fade, model_shadow_fade }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withUnlit().withFade(), model_normalmap_translucent_unlit_fade, model_shadow_fade }, + // Unskinned Lightmapped Fade + { Key::Builder().withMaterial().withLightMap().withFade(), model_lightmap_fade, model_shadow_fade }, + { Key::Builder().withMaterial().withTangents().withLightMap().withFade(), model_normalmap_lightmap_fade, model_shadow_fade }, + { Key::Builder().withMaterial().withTranslucent().withLightMap().withFade(), model_translucent_lightmap_fade, model_shadow_fade }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withFade(), model_normalmap_translucent_lightmap_fade, model_shadow_fade }, + // Unskinned MToon Fade + { Key::Builder().withMaterial().withMToon().withFade(), model_mtoon_fade, model_shadow_mtoon_fade }, + { Key::Builder().withMaterial().withTangents().withMToon().withFade(), model_normalmap_mtoon_fade, model_shadow_mtoon_fade }, + { Key::Builder().withMaterial().withTranslucent().withMToon().withFade(), model_translucent_mtoon_fade, model_shadow_mtoon_fade }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon().withFade(), model_normalmap_translucent_mtoon_fade, model_shadow_mtoon_fade }, + + // Matrix palette skinned + { Key::Builder().withMaterial().withDeformed(), model_deformed, model_shadow_deformed }, + { Key::Builder().withMaterial().withTangents().withDeformed(), model_normalmap_deformed, model_shadow_deformed }, + { Key::Builder().withMaterial().withTranslucent().withDeformed(), model_translucent_deformed, model_shadow_deformed }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withDeformed(), model_normalmap_translucent_deformed, model_shadow_deformed }, + // Matrix palette skinned Unlit + { Key::Builder().withMaterial().withUnlit().withDeformed(), model_unlit_deformed, model_shadow_deformed }, + { Key::Builder().withMaterial().withTangents().withUnlit().withDeformed(), model_normalmap_unlit_deformed, model_shadow_deformed }, + { Key::Builder().withMaterial().withTranslucent().withUnlit().withDeformed(), model_translucent_unlit_deformed, model_shadow_deformed }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withUnlit().withDeformed(), model_normalmap_translucent_unlit_deformed, model_shadow_deformed }, + // Matrix palette skinned Lightmapped + { Key::Builder().withMaterial().withLightMap().withDeformed(), model_lightmap_deformed, model_shadow_deformed }, + { Key::Builder().withMaterial().withTangents().withLightMap().withDeformed(), model_normalmap_lightmap_deformed, model_shadow_deformed }, + { Key::Builder().withMaterial().withTranslucent().withLightMap().withDeformed(), model_translucent_lightmap_deformed, model_shadow_deformed }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withDeformed(), model_normalmap_translucent_lightmap_deformed, model_shadow_deformed }, + // Matrix palette skinned MToon + { Key::Builder().withMaterial().withMToon().withDeformed(), model_mtoon_deformed, model_shadow_mtoon_deformed }, + { Key::Builder().withMaterial().withTangents().withMToon().withDeformed(), model_normalmap_mtoon_deformed, model_shadow_mtoon_deformed }, + { Key::Builder().withMaterial().withTranslucent().withMToon().withDeformed(), model_translucent_mtoon_deformed, model_shadow_mtoon_deformed }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon().withDeformed(), model_normalmap_translucent_mtoon_deformed, model_shadow_mtoon_deformed }, + // Matrix palette skinned Fade + { Key::Builder().withMaterial().withFade().withDeformed(), model_fade_deformed, model_shadow_fade_deformed }, + { Key::Builder().withMaterial().withTangents().withFade().withDeformed(), model_normalmap_fade_deformed, model_shadow_fade_deformed }, + { Key::Builder().withMaterial().withTranslucent().withFade().withDeformed(), model_translucent_fade_deformed, model_shadow_fade_deformed }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withFade().withDeformed(), model_normalmap_translucent_fade_deformed, model_shadow_fade_deformed }, + // Matrix palette skinned Unlit Fade + { Key::Builder().withMaterial().withUnlit().withFade().withDeformed(), model_unlit_fade_deformed, model_shadow_fade_deformed }, + { Key::Builder().withMaterial().withTangents().withUnlit().withFade().withDeformed(), model_normalmap_unlit_fade_deformed, model_shadow_fade_deformed }, + { Key::Builder().withMaterial().withTranslucent().withUnlit().withFade().withDeformed(), model_translucent_unlit_fade_deformed, model_shadow_fade_deformed }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withUnlit().withFade().withDeformed(), model_normalmap_translucent_unlit_fade_deformed, model_shadow_fade_deformed }, + // Matrix palette skinned Lightmapped Fade + { Key::Builder().withMaterial().withLightMap().withFade().withDeformed(), model_lightmap_fade_deformed, model_shadow_fade_deformed }, + { Key::Builder().withMaterial().withTangents().withLightMap().withFade().withDeformed(), model_normalmap_lightmap_fade_deformed, model_shadow_fade_deformed }, + { Key::Builder().withMaterial().withTranslucent().withLightMap().withFade().withDeformed(), model_translucent_lightmap_fade_deformed, model_shadow_fade_deformed }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withFade().withDeformed(), model_normalmap_translucent_lightmap_fade_deformed, model_shadow_fade_deformed }, + // Matrix palette skinned MToon Fade + { Key::Builder().withMaterial().withMToon().withFade().withDeformed(), model_mtoon_fade_deformed, model_shadow_mtoon_fade_deformed }, + { Key::Builder().withMaterial().withTangents().withMToon().withFade().withDeformed(), model_normalmap_mtoon_fade_deformed, model_shadow_mtoon_fade_deformed }, + { Key::Builder().withMaterial().withTranslucent().withMToon().withFade().withDeformed(), model_translucent_mtoon_fade_deformed, model_shadow_mtoon_fade_deformed }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon().withFade().withDeformed(), model_normalmap_translucent_mtoon_fade_deformed, model_shadow_mtoon_fade_deformed }, + + // Dual quaternion skinned + { Key::Builder().withMaterial().withDeformed().withDualQuatSkinned(), model_deformeddq, model_shadow_deformeddq }, + { Key::Builder().withMaterial().withTangents().withDeformed().withDualQuatSkinned(), model_normalmap_deformeddq, model_shadow_deformeddq }, + { Key::Builder().withMaterial().withTranslucent().withDeformed().withDualQuatSkinned(), model_translucent_deformeddq, model_shadow_deformeddq }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_deformeddq, model_shadow_deformeddq }, + // Dual quaternion skinned Unlit + { Key::Builder().withMaterial().withUnlit().withDeformed().withDualQuatSkinned(), model_unlit_deformeddq, model_shadow_deformeddq }, + { Key::Builder().withMaterial().withTangents().withUnlit().withDeformed().withDualQuatSkinned(), model_normalmap_unlit_deformeddq, model_shadow_deformeddq }, + { Key::Builder().withMaterial().withTranslucent().withUnlit().withDeformed().withDualQuatSkinned(), model_translucent_unlit_deformeddq, model_shadow_deformeddq }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withUnlit().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_unlit_deformeddq, model_shadow_deformeddq }, + // Dual quaternion skinned Lightmapped + { Key::Builder().withMaterial().withLightMap().withDeformed().withDualQuatSkinned(), model_lightmap_deformeddq, model_shadow_deformeddq }, + { Key::Builder().withMaterial().withTangents().withLightMap().withDeformed().withDualQuatSkinned(), model_normalmap_lightmap_deformeddq, model_shadow_deformeddq }, + { Key::Builder().withMaterial().withTranslucent().withLightMap().withDeformed().withDualQuatSkinned(), model_translucent_lightmap_deformeddq, model_shadow_deformeddq }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_lightmap_deformeddq, model_shadow_deformeddq }, + // Dual quaternion skinned MToon + { Key::Builder().withMaterial().withMToon().withDeformed().withDualQuatSkinned(), model_mtoon_deformeddq, model_shadow_mtoon_deformeddq }, + { Key::Builder().withMaterial().withTangents().withMToon().withDeformed().withDualQuatSkinned(), model_normalmap_mtoon_deformeddq, model_shadow_mtoon_deformeddq }, + { Key::Builder().withMaterial().withTranslucent().withMToon().withDeformed().withDualQuatSkinned(), model_translucent_mtoon_deformeddq, model_shadow_mtoon_deformeddq }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_mtoon_deformeddq, model_shadow_mtoon_deformeddq }, + // Dual quaternion skinned Fade + { Key::Builder().withMaterial().withFade().withDeformed().withDualQuatSkinned(), model_fade_deformeddq, model_shadow_fade_deformeddq }, + { Key::Builder().withMaterial().withTangents().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_fade_deformeddq, model_shadow_fade_deformeddq }, + { Key::Builder().withMaterial().withTranslucent().withFade().withDeformed().withDualQuatSkinned(), model_translucent_fade_deformeddq, model_shadow_fade_deformeddq }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_fade_deformeddq, model_shadow_fade_deformeddq }, + // Dual quaternion skinned Unlit Fade + { Key::Builder().withMaterial().withUnlit().withFade().withDeformed().withDualQuatSkinned(), model_unlit_fade_deformeddq, model_shadow_fade_deformeddq }, + { Key::Builder().withMaterial().withTangents().withUnlit().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_unlit_fade_deformeddq, model_shadow_fade_deformeddq }, + { Key::Builder().withMaterial().withTranslucent().withUnlit().withFade().withDeformed().withDualQuatSkinned(), model_translucent_unlit_fade_deformeddq, model_shadow_fade_deformeddq }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withUnlit().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_unlit_fade_deformeddq, model_shadow_fade_deformeddq }, + // Dual quaternion skinned Lightmapped Fade + { Key::Builder().withMaterial().withLightMap().withFade().withDeformed().withDualQuatSkinned(), model_lightmap_fade_deformeddq, model_shadow_fade_deformeddq }, + { Key::Builder().withMaterial().withTangents().withLightMap().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_lightmap_fade_deformeddq, model_shadow_fade_deformeddq }, + { Key::Builder().withMaterial().withTranslucent().withLightMap().withFade().withDeformed().withDualQuatSkinned(), model_translucent_lightmap_fade_deformeddq, model_shadow_fade_deformeddq }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_lightmap_fade_deformeddq, model_shadow_fade_deformeddq }, + // Dual quaternion skinned MToon Fade + { Key::Builder().withMaterial().withMToon().withFade().withDeformed().withDualQuatSkinned(), model_mtoon_fade_deformeddq, model_shadow_mtoon_fade_deformeddq }, + { Key::Builder().withMaterial().withTangents().withMToon().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_mtoon_fade_deformeddq, model_shadow_mtoon_fade_deformeddq }, + { Key::Builder().withMaterial().withTranslucent().withMToon().withFade().withDeformed().withDualQuatSkinned(), model_translucent_mtoon_fade_deformeddq, model_shadow_mtoon_fade_deformeddq }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_mtoon_fade_deformeddq, model_shadow_mtoon_fade_deformeddq }, +}; + void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter) { - using namespace shader::render_utils::program; - using Key = render::ShapeKey; - auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3, _4); + auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, std::make_shared(), _3, _4); - // TOOD: build this list algorithmically so we don't have to maintain it - std::vector> pipelines = { - // Simple - { Key::Builder(), simple }, - { Key::Builder().withTranslucent(), simple_translucent }, - { Key::Builder().withUnlit(), simple_unlit }, - { Key::Builder().withTranslucent().withUnlit(), simple_translucent_unlit }, - // Simple Fade - { Key::Builder().withFade(), simple_fade }, - { Key::Builder().withTranslucent().withFade(), simple_translucent_fade }, - { Key::Builder().withUnlit().withFade(), simple_unlit_fade }, - { Key::Builder().withTranslucent().withUnlit().withFade(), simple_translucent_unlit_fade }, - - // Unskinned - { Key::Builder().withMaterial(), model }, - { Key::Builder().withMaterial().withTangents(), model_normalmap }, - { Key::Builder().withMaterial().withTranslucent(), model_translucent }, - { Key::Builder().withMaterial().withTangents().withTranslucent(), model_normalmap_translucent }, - // Unskinned Unlit - { Key::Builder().withMaterial().withUnlit(), model_unlit }, - { Key::Builder().withMaterial().withTangents().withUnlit(), model_normalmap_unlit }, - { Key::Builder().withMaterial().withTranslucent().withUnlit(), model_translucent_unlit }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withUnlit(), model_normalmap_translucent_unlit }, - // Unskinned Lightmapped - { Key::Builder().withMaterial().withLightMap(), model_lightmap }, - { Key::Builder().withMaterial().withTangents().withLightMap(), model_normalmap_lightmap }, - { Key::Builder().withMaterial().withTranslucent().withLightMap(), model_translucent_lightmap }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap(), model_normalmap_translucent_lightmap }, - // Unskinned Fade - { Key::Builder().withMaterial().withFade(), model_fade }, - { Key::Builder().withMaterial().withTangents().withFade(), model_normalmap_fade }, - { Key::Builder().withMaterial().withTranslucent().withFade(), model_translucent_fade }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withFade(), model_normalmap_translucent_fade }, - // Unskinned Unlit Fade - { Key::Builder().withMaterial().withUnlit().withFade(), model_unlit_fade }, - { Key::Builder().withMaterial().withTangents().withUnlit().withFade(), model_normalmap_unlit_fade }, - { Key::Builder().withMaterial().withTranslucent().withUnlit().withFade(), model_translucent_unlit_fade }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withUnlit().withFade(), model_normalmap_translucent_unlit_fade }, - // Unskinned Lightmapped Fade - { Key::Builder().withMaterial().withLightMap().withFade(), model_lightmap_fade }, - { Key::Builder().withMaterial().withTangents().withLightMap().withFade(), model_normalmap_lightmap_fade }, - { Key::Builder().withMaterial().withTranslucent().withLightMap().withFade(), model_translucent_lightmap_fade }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withFade(), model_normalmap_translucent_lightmap_fade }, - - // Matrix palette skinned - { Key::Builder().withMaterial().withDeformed(), model_deformed }, - { Key::Builder().withMaterial().withTangents().withDeformed(), model_normalmap_deformed }, - { Key::Builder().withMaterial().withTranslucent().withDeformed(), model_translucent_deformed }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withDeformed(), model_normalmap_translucent_deformed }, - // Matrix palette skinned Unlit - { Key::Builder().withMaterial().withUnlit().withDeformed(), model_unlit_deformed }, - { Key::Builder().withMaterial().withTangents().withUnlit().withDeformed(), model_normalmap_unlit_deformed }, - { Key::Builder().withMaterial().withTranslucent().withUnlit().withDeformed(), model_translucent_unlit_deformed }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withUnlit().withDeformed(), model_normalmap_translucent_unlit_deformed }, - // Matrix palette skinned Lightmapped - { Key::Builder().withMaterial().withLightMap().withDeformed(), model_lightmap_deformed }, - { Key::Builder().withMaterial().withTangents().withLightMap().withDeformed(), model_normalmap_lightmap_deformed }, - { Key::Builder().withMaterial().withTranslucent().withLightMap().withDeformed(), model_translucent_lightmap_deformed }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withDeformed(), model_normalmap_translucent_lightmap_deformed }, - // Matrix palette skinned Fade - { Key::Builder().withMaterial().withFade().withDeformed(), model_fade_deformed }, - { Key::Builder().withMaterial().withTangents().withFade().withDeformed(), model_normalmap_fade_deformed }, - { Key::Builder().withMaterial().withTranslucent().withFade().withDeformed(), model_translucent_fade_deformed }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withFade().withDeformed(), model_normalmap_translucent_fade_deformed }, - // Matrix palette skinned Unlit Fade - { Key::Builder().withMaterial().withUnlit().withFade().withDeformed(), model_unlit_fade_deformed }, - { Key::Builder().withMaterial().withTangents().withUnlit().withFade().withDeformed(), model_normalmap_unlit_fade_deformed }, - { Key::Builder().withMaterial().withTranslucent().withUnlit().withFade().withDeformed(), model_translucent_unlit_fade_deformed }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withUnlit().withFade().withDeformed(), model_normalmap_translucent_unlit_fade_deformed }, - // Matrix palette skinned Lightmapped Fade - { Key::Builder().withMaterial().withLightMap().withFade().withDeformed(), model_lightmap_fade_deformed }, - { Key::Builder().withMaterial().withTangents().withLightMap().withFade().withDeformed(), model_normalmap_lightmap_fade_deformed }, - { Key::Builder().withMaterial().withTranslucent().withLightMap().withFade().withDeformed(), model_translucent_lightmap_fade_deformed }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withFade().withDeformed(), model_normalmap_translucent_lightmap_fade_deformed }, - - // Dual quaternion skinned - { Key::Builder().withMaterial().withDeformed().withDualQuatSkinned(), model_deformeddq }, - { Key::Builder().withMaterial().withTangents().withDeformed().withDualQuatSkinned(), model_normalmap_deformeddq }, - { Key::Builder().withMaterial().withTranslucent().withDeformed().withDualQuatSkinned(), model_translucent_deformeddq }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_deformeddq }, - // Dual quaternion skinned Unlit - { Key::Builder().withMaterial().withUnlit().withDeformed().withDualQuatSkinned(), model_unlit_deformeddq }, - { Key::Builder().withMaterial().withTangents().withUnlit().withDeformed().withDualQuatSkinned(), model_normalmap_unlit_deformeddq }, - { Key::Builder().withMaterial().withTranslucent().withUnlit().withDeformed().withDualQuatSkinned(), model_translucent_unlit_deformeddq }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withUnlit().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_unlit_deformeddq }, - // Dual quaternion skinned Lightmapped - { Key::Builder().withMaterial().withLightMap().withDeformed().withDualQuatSkinned(), model_lightmap_deformeddq }, - { Key::Builder().withMaterial().withTangents().withLightMap().withDeformed().withDualQuatSkinned(), model_normalmap_lightmap_deformeddq }, - { Key::Builder().withMaterial().withTranslucent().withLightMap().withDeformed().withDualQuatSkinned(), model_translucent_lightmap_deformeddq }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_lightmap_deformeddq }, - // Dual quaternion skinned Fade - { Key::Builder().withMaterial().withFade().withDeformed().withDualQuatSkinned(), model_fade_deformeddq }, - { Key::Builder().withMaterial().withTangents().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_fade_deformeddq }, - { Key::Builder().withMaterial().withTranslucent().withFade().withDeformed().withDualQuatSkinned(), model_translucent_fade_deformeddq }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_fade_deformeddq }, - // Dual quaternion skinned Unlit Fade - { Key::Builder().withMaterial().withUnlit().withFade().withDeformed().withDualQuatSkinned(), model_unlit_fade_deformeddq }, - { Key::Builder().withMaterial().withTangents().withUnlit().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_unlit_fade_deformeddq }, - { Key::Builder().withMaterial().withTranslucent().withUnlit().withFade().withDeformed().withDualQuatSkinned(), model_translucent_unlit_fade_deformeddq }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withUnlit().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_unlit_fade_deformeddq }, - // Dual quaternion skinned Lightmapped Fade - { Key::Builder().withMaterial().withLightMap().withFade().withDeformed().withDualQuatSkinned(), model_lightmap_fade_deformeddq }, - { Key::Builder().withMaterial().withTangents().withLightMap().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_lightmap_fade_deformeddq }, - { Key::Builder().withMaterial().withTranslucent().withLightMap().withFade().withDeformed().withDualQuatSkinned(), model_translucent_lightmap_fade_deformeddq }, - { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_lightmap_fade_deformeddq }, - }; - - for (auto& pipeline : pipelines) { - if (pipeline.first.build().isFaded()) { - addPipeline(pipeline.first, pipeline.second, batchSetter, itemSetter); + for (auto& pipeline : ALL_PIPELINES) { + if (std::get<0>(pipeline).build().isFaded()) { + addPipeline(std::get<0>(pipeline), std::get<1>(pipeline), batchSetter, itemSetter); } else { - addPipeline(pipeline.first, pipeline.second, nullptr, nullptr); + addPipeline(std::get<0>(pipeline), std::get<1>(pipeline), nullptr, nullptr); } } } void initForwardPipelines(ShapePlumber& plumber) { - using namespace shader::render_utils::program; - using Key = render::ShapeKey; - auto addPipelineBind = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3, _4); + auto addPipelineBind = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, std::make_shared(), _3, _4); // Disable fade on the forward pipeline, all shaders get added twice, once with the fade key and once without auto addPipeline = [&](const ShapeKey& key, int programId) { @@ -188,7 +218,7 @@ void initForwardPipelines(ShapePlumber& plumber) { forceLightBatchSetter = true; // TOOD: build this list algorithmically so we don't have to maintain it - std::vector> pipelines = { + std::vector> pipelines = { // Simple { Key::Builder(), simple_forward }, { Key::Builder().withTranslucent(), simple_translucent_forward }, @@ -210,6 +240,11 @@ void initForwardPipelines(ShapePlumber& plumber) { { Key::Builder().withMaterial().withTangents().withLightMap(), model_normalmap_lightmap_forward }, { Key::Builder().withMaterial().withTranslucent().withLightMap(), model_translucent_lightmap_forward }, { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap(), model_normalmap_translucent_lightmap_forward }, + // Unskinned MToon + { Key::Builder().withMaterial().withMToon(), model_mtoon_forward }, + { Key::Builder().withMaterial().withTangents().withMToon(), model_normalmap_mtoon_forward }, + { Key::Builder().withMaterial().withTranslucent().withMToon(), model_translucent_mtoon_forward }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon(), model_normalmap_translucent_mtoon_forward }, // Matrix palette skinned { Key::Builder().withMaterial().withDeformed(), model_forward_deformed }, @@ -226,6 +261,11 @@ void initForwardPipelines(ShapePlumber& plumber) { { Key::Builder().withMaterial().withTangents().withLightMap().withDeformed(), model_normalmap_lightmap_forward_deformed }, { Key::Builder().withMaterial().withTranslucent().withLightMap().withDeformed(), model_translucent_lightmap_forward_deformed }, { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withDeformed(), model_normalmap_translucent_lightmap_forward_deformed }, + // Matrix palette skinned MToon + { Key::Builder().withMaterial().withMToon().withDeformed(), model_mtoon_forward_deformed }, + { Key::Builder().withMaterial().withTangents().withMToon().withDeformed(), model_normalmap_mtoon_forward_deformed }, + { Key::Builder().withMaterial().withTranslucent().withMToon().withDeformed(), model_translucent_mtoon_forward_deformed }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon().withDeformed(), model_normalmap_translucent_mtoon_forward_deformed }, // Dual quaternion skinned { Key::Builder().withMaterial().withDeformed().withDualQuatSkinned(), model_forward_deformeddq }, @@ -242,6 +282,11 @@ void initForwardPipelines(ShapePlumber& plumber) { { Key::Builder().withMaterial().withTangents().withLightMap().withDeformed().withDualQuatSkinned(), model_normalmap_lightmap_forward_deformeddq }, { Key::Builder().withMaterial().withTranslucent().withLightMap().withDeformed().withDualQuatSkinned(), model_translucent_lightmap_forward_deformeddq }, { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_lightmap_forward_deformeddq }, + // Dual quaternion skinned MToon + { Key::Builder().withMaterial().withMToon().withDeformed().withDualQuatSkinned(), model_mtoon_forward_deformeddq }, + { Key::Builder().withMaterial().withTangents().withMToon().withDeformed().withDualQuatSkinned(), model_normalmap_mtoon_forward_deformeddq }, + { Key::Builder().withMaterial().withTranslucent().withMToon().withDeformed().withDualQuatSkinned(), model_translucent_mtoon_forward_deformeddq }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_mtoon_forward_deformeddq }, }; for (auto& pipeline : pipelines) { @@ -252,7 +297,7 @@ void initForwardPipelines(ShapePlumber& plumber) { } void addPlumberPipeline(ShapePlumber& plumber, - const ShapeKey& key, int programId, + const ShapeKey& key, int programId, gpu::StatePointer& baseState, const render::ShapePipeline::BatchSetter& extraBatchSetter, const render::ShapePipeline::ItemSetter& itemSetter) { // These key-values' pipelines are added by this functor in addition to the key passed assert(!key.isWireframe()); @@ -265,7 +310,7 @@ void addPlumberPipeline(ShapePlumber& plumber, bool isBiased = (i & 1); bool isWireframed = (i & 2); for (int cullFaceMode = graphics::MaterialKey::CullFaceMode::CULL_NONE; cullFaceMode < graphics::MaterialKey::CullFaceMode::NUM_CULL_FACE_MODES; cullFaceMode++) { - auto state = std::make_shared(); + auto state = std::make_shared(*baseState); key.isTranslucent() ? PrepareStencil::testMask(*state) : PrepareStencil::testMaskDrawShape(*state); // Depth test depends on transparency @@ -287,7 +332,7 @@ void addPlumberPipeline(ShapePlumber& plumber, state->setDepthBiasSlopeScale(1.0f); } - auto baseBatchSetter = (forceLightBatchSetter || key.isTranslucent()) ? &lightBatchSetter : &batchSetter; + auto baseBatchSetter = (forceLightBatchSetter || key.isTranslucent() || key.isMToon()) ? &lightBatchSetter : &batchSetter; render::ShapePipeline::BatchSetter finalBatchSetter; if (extraBatchSetter) { finalBatchSetter = [baseBatchSetter, extraBatchSetter](const ShapePipeline& pipeline, gpu::Batch& batch, render::Args* args) { @@ -344,29 +389,97 @@ void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderAr } } -void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& extraBatchSetter, const render::ShapePipeline::ItemSetter& itemSetter) { - using namespace shader::render_utils::program; +void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter) { + auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, state, _3, _4); - shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withoutDeformed().withoutFade(), - gpu::Shader::createProgram(model_shadow), state); - shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withoutDeformed().withFade(), - gpu::Shader::createProgram(model_shadow_fade), state, extraBatchSetter, itemSetter); + for (auto& pipeline : ALL_PIPELINES) { + if (std::get<0>(pipeline).build().isFaded()) { + addPipeline(std::get<0>(pipeline), std::get<2>(pipeline), batchSetter, itemSetter); + } else { + addPipeline(std::get<0>(pipeline), std::get<2>(pipeline), nullptr, nullptr); + } + } +} - shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withDeformed().withoutDualQuatSkinned().withoutFade(), - gpu::Shader::createProgram(model_shadow_deformed), state); - shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withDeformed().withoutDualQuatSkinned().withFade(), - gpu::Shader::createProgram(model_shadow_fade_deformed), state, extraBatchSetter, itemSetter); - shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withDeformed().withDualQuatSkinned().withoutFade(), - gpu::Shader::createProgram(model_shadow_deformeddq), state); - shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withDeformed().withDualQuatSkinned().withFade(), - gpu::Shader::createProgram(model_shadow_fade_deformeddq), state, extraBatchSetter, itemSetter); +void sortAndRenderZPassShapes(const ShapePlumberPointer& shapePlumber, const render::RenderContextPointer& renderContext, const render::ShapeBounds& inShapes, render::ItemBounds &itemBounds) { + std::unordered_map, ShapeKey::Hash, ShapeKey::KeyEqual> sortedShapeKeys; + std::unordered_map, ShapeKey::Hash, ShapeKey::KeyEqual>> sortedCustomShapeKeys; + std::unordered_map, ShapeKey::Hash, ShapeKey::KeyEqual> sortedOwnPipelineShapeKeys; + + for (const auto& items : inShapes) { + itemBounds.insert(itemBounds.end(), items.second.begin(), items.second.end()); + + ShapeKey::Builder variantKey = ShapeKey::Builder(); + + // The keys we need to check here have to match the ones set up in initZPassPipelines (+ addPlumberPipeline) + if (items.first.isDeformed()) { + variantKey.withDeformed(); + if (items.first.isDualQuatSkinned()) { + variantKey.withDualQuatSkinned(); + } + } + + if (items.first.isFaded()) { + variantKey.withFade(); + } + + if (items.first.isMToon()) { + variantKey.withMToon(); + } + + if (items.first.isCullFace()) { + variantKey.withCullFaceMode(graphics::MaterialKey::CULL_BACK); + } else if (items.first.isCullFaceFront()) { + variantKey.withCullFaceMode(graphics::MaterialKey::CULL_FRONT); + } else if (items.first.isCullFaceNone()) { + variantKey.withCullFaceMode(graphics::MaterialKey::CULL_NONE); + } + + if (items.first.isWireframe()) { + variantKey.withWireframe(); + } + + if (items.first.isDepthBiased()) { + variantKey.withDepthBias(); + } + + if (items.first.hasOwnPipeline()) { + sortedOwnPipelineShapeKeys[variantKey.build()].push_back(items.first); + } else if (items.first.isCustom()) { + const uint8_t custom = items.first.getCustom(); + variantKey.withCustom(custom); + sortedCustomShapeKeys[custom][variantKey.build()].push_back(items.first); + } else { + sortedShapeKeys[variantKey.build()].push_back(items.first); + } + } + + // Render non-withCustom, non-withOwnPipeline things + for (const auto& variantAndKeys : sortedShapeKeys) { + for (const auto& key : variantAndKeys.second) { + renderShapes(renderContext, shapePlumber, inShapes.at(key)); + } + } + + // Render withCustom things + for (const auto& customAndSortedCustomKeys : sortedCustomShapeKeys) { + for (const auto& variantAndKeys : customAndSortedCustomKeys.second) { + for (const auto& key : variantAndKeys.second) { + renderShapes(renderContext, shapePlumber, inShapes.at(key)); + } + } + } + + // Render withOwnPipeline things + for (const auto& variantAndKeys : sortedOwnPipelineShapeKeys) { + for (const auto& key : variantAndKeys.second) { + renderShapes(renderContext, shapePlumber, inShapes.at(key)); + } + } + + renderContext->args->_shapePipeline = nullptr; + renderContext->args->_batch = nullptr; } void initMirrorPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& extraBatchSetter, const render::ShapePipeline::ItemSetter& itemSetter, bool forward) { @@ -415,11 +528,11 @@ bool RenderPipelines::bindMaterial(graphics::MaterialPointer& material, gpu::Bat } void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial) { - auto& schemaBuffer = multiMaterial.getSchemaBuffer(); - auto& drawMaterialTextures = multiMaterial.getTextureTable(); multiMaterial.setTexturesLoading(false); multiMaterial.resetReferenceTexturesAndMaterials(); + multiMaterial.setisMToon(!multiMaterial.empty() && multiMaterial.top().material && multiMaterial.top().material->isMToon()); + multiMaterial.resetOutline(); // The total list of things we need to look for static std::set allFlags; @@ -438,6 +551,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial graphics::MultiMaterial materials = multiMaterial; graphics::MultiMaterial::Schema schema; + graphics::MultiMaterial::MToonSchema toonSchema; graphics::MaterialKey schemaKey; std::set flagsToCheck = allFlags; @@ -465,261 +579,570 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial bool wasSet = false; bool forceDefault = false; - switch (flag) { - case graphics::MaterialKey::EMISSIVE_VAL_BIT: - if (materialKey.isEmissive()) { - schema._emissive = material->getEmissive(false); - schemaKey.setEmissive(true); - wasSet = true; - } - break; - case graphics::MaterialKey::UNLIT_VAL_BIT: - if (materialKey.isUnlit()) { - schemaKey.setUnlit(true); - wasSet = true; - } - break; - case graphics::MaterialKey::ALBEDO_VAL_BIT: - if (materialKey.isAlbedo()) { - schema._albedo = material->getAlbedo(false); - schemaKey.setAlbedo(true); - wasSet = true; - } - break; - case graphics::MaterialKey::METALLIC_VAL_BIT: - if (materialKey.isMetallic()) { - schema._metallic = material->getMetallic(); - schemaKey.setMetallic(true); - wasSet = true; - } - break; - case graphics::MaterialKey::GLOSSY_VAL_BIT: - if (materialKey.isRough() || materialKey.isGlossy()) { - schema._roughness = material->getRoughness(); - schemaKey.setGlossy(materialKey.isGlossy()); - wasSet = true; - } - break; - case graphics::MaterialKey::OPACITY_VAL_BIT: - if (materialKey.isTranslucentFactor()) { - schema._opacity = material->getOpacity(); - schemaKey.setTranslucentFactor(true); - wasSet = true; - } - break; - case graphics::MaterialKey::OPACITY_CUTOFF_VAL_BIT: - if (materialKey.isOpacityCutoff()) { - schema._opacityCutoff = material->getOpacityCutoff(); - schemaKey.setOpacityCutoff(true); - wasSet = true; - } - break; - case graphics::MaterialKey::SCATTERING_VAL_BIT: - if (materialKey.isScattering()) { - schema._scattering = material->getScattering(); - schemaKey.setScattering(true); - wasSet = true; - } - break; - case graphics::MaterialKey::ALBEDO_MAP_BIT: - if (materialKey.isAlbedoMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP); - if (itr != textureMaps.end()) { - if (itr->second->isDefined()) { - material->resetOpacityMap(); - drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (!multiMaterial.isMToon()) { + switch (flag) { + case graphics::MaterialKey::EMISSIVE_VAL_BIT: + if (materialKey.isEmissive()) { + schema._emissive = material->getEmissive(false); + schemaKey.setEmissive(true); + wasSet = true; + } + break; + case graphics::MaterialKey::UNLIT_VAL_BIT: + if (materialKey.isUnlit()) { + schemaKey.setUnlit(true); + wasSet = true; + } + break; + case graphics::MaterialKey::ALBEDO_VAL_BIT: + if (materialKey.isAlbedo()) { + schema._albedo = material->getAlbedo(false); + schemaKey.setAlbedo(true); + wasSet = true; + } + break; + case graphics::MaterialKey::METALLIC_VAL_BIT: + if (materialKey.isMetallic()) { + schema._metallic = material->getMetallic(); + schemaKey.setMetallic(true); + wasSet = true; + } + break; + case graphics::MaterialKey::GLOSSY_VAL_BIT: + if (materialKey.isRough() || materialKey.isGlossy()) { + schema._roughness = material->getRoughness(); + schemaKey.setGlossy(materialKey.isGlossy()); + wasSet = true; + } + break; + case graphics::MaterialKey::OPACITY_VAL_BIT: + if (materialKey.isTranslucentFactor()) { + schema._opacity = material->getOpacity(); + schemaKey.setTranslucentFactor(true); + wasSet = true; + } + break; + case graphics::MaterialKey::OPACITY_CUTOFF_VAL_BIT: + if (materialKey.isOpacityCutoff()) { + schema._opacityCutoff = material->getOpacityCutoff(); + schemaKey.setOpacityCutoff(true); + wasSet = true; + } + break; + case graphics::MaterialKey::SCATTERING_VAL_BIT: + if (materialKey.isScattering()) { + schema._scattering = material->getScattering(); + schemaKey.setScattering(true); + wasSet = true; + } + break; + case graphics::MaterialKey::ALBEDO_MAP_BIT: + if (materialKey.isAlbedoMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + material->resetOpacityMap(); + drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setAlbedoMap(true); + schemaKey.setOpacityMaskMap(material->getKey().isOpacityMaskMap()); + schemaKey.setTranslucentMap(material->getKey().isTranslucentMap()); } - schemaKey.setAlbedoMap(true); - schemaKey.setOpacityMaskMap(material->getKey().isOpacityMaskMap()); - schemaKey.setTranslucentMap(material->getKey().isTranslucentMap()); - } - break; - case graphics::MaterialKey::METALLIC_MAP_BIT: - if (materialKey.isMetallicMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP); - if (itr != textureMaps.end()) { - if (itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, itr->second->getTextureView()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + break; + case graphics::MaterialKey::METALLIC_MAP_BIT: + if (materialKey.isMetallicMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setMetallicMap(true); } - schemaKey.setMetallicMap(true); - } - break; - case graphics::MaterialKey::ROUGHNESS_MAP_BIT: - if (materialKey.isRoughnessMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP); - if (itr != textureMaps.end()) { - if (itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, itr->second->getTextureView()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + break; + case graphics::MaterialKey::ROUGHNESS_MAP_BIT: + if (materialKey.isRoughnessMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setRoughnessMap(true); } - schemaKey.setRoughnessMap(true); - } - break; - case graphics::MaterialKey::NORMAL_MAP_BIT: - if (materialKey.isNormalMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP); - if (itr != textureMaps.end()) { - if (itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + break; + case graphics::MaterialKey::NORMAL_MAP_BIT: + if (materialKey.isNormalMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setNormalMap(true); } - schemaKey.setNormalMap(true); - } - break; - case graphics::MaterialKey::OCCLUSION_MAP_BIT: - if (materialKey.isOcclusionMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP); - if (itr != textureMaps.end()) { - if (itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, itr->second->getTextureView()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + break; + case graphics::MaterialKey::OCCLUSION_MAP_BIT: + if (materialKey.isOcclusionMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setOcclusionMap(true); } - schemaKey.setOcclusionMap(true); - } - break; - case graphics::MaterialKey::SCATTERING_MAP_BIT: - if (materialKey.isScatteringMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP); - if (itr != textureMaps.end()) { - if (itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, itr->second->getTextureView()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + break; + case graphics::MaterialKey::SCATTERING_MAP_BIT: + if (materialKey.isScatteringMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setScatteringMap(true); } - schemaKey.setScatteringMap(true); - } - break; - case graphics::MaterialKey::EMISSIVE_MAP_BIT: - // Lightmap takes precendence over emissive map for legacy reasons - if (materialKey.isEmissiveMap() && !materialKey.isLightMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); - if (itr != textureMaps.end()) { - if (itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + break; + case graphics::MaterialKey::EMISSIVE_MAP_BIT: + // Lightmap takes precendence over emissive map for legacy reasons + if (materialKey.isEmissiveMap() && !materialKey.isLightMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setEmissiveMap(true); + } else if (materialKey.isLightMap()) { + // We'll set this later when we check the lightmap + wasSet = true; } - schemaKey.setEmissiveMap(true); - } else if (materialKey.isLightMap()) { - // We'll set this later when we check the lightmap - wasSet = true; - } - break; - case graphics::MaterialKey::LIGHT_MAP_BIT: - if (materialKey.isLightMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::LIGHT_MAP); - if (itr != textureMaps.end()) { - if (itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + break; + case graphics::MaterialKey::LIGHT_MAP_BIT: + if (materialKey.isLightMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::LIGHT_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setLightMap(true); } - schemaKey.setLightMap(true); - } - break; - case graphics::Material::TEXCOORDTRANSFORM0: - if (!fallthrough) { - schema._texcoordTransforms[0] = material->getTexCoordTransform(0); - wasSet = true; - } - break; - case graphics::Material::TEXCOORDTRANSFORM1: - if (!fallthrough) { - schema._texcoordTransforms[1] = material->getTexCoordTransform(1); - wasSet = true; - } - break; - case graphics::Material::LIGHTMAP_PARAMS: - if (!fallthrough) { - schema._lightmapParams = material->getLightmapParams(); - wasSet = true; - } - break; - case graphics::Material::MATERIAL_PARAMS: - if (!fallthrough) { - schema._materialParams = material->getMaterialParams(); - wasSet = true; - } - break; - case graphics::Material::CULL_FACE_MODE: - if (!fallthrough) { - multiMaterial.setCullFaceMode(material->getCullFaceMode()); - wasSet = true; - } - break; - default: - break; + break; + case graphics::Material::TEXCOORDTRANSFORM0: + if (!fallthrough) { + schema._texcoordTransforms[0] = material->getTexCoordTransform(0); + wasSet = true; + } + break; + case graphics::Material::TEXCOORDTRANSFORM1: + if (!fallthrough) { + schema._texcoordTransforms[1] = material->getTexCoordTransform(1); + wasSet = true; + } + break; + case graphics::Material::LIGHTMAP_PARAMS: + if (!fallthrough) { + schema._lightmapParams = material->getLightmapParams(); + wasSet = true; + } + break; + case graphics::Material::MATERIAL_PARAMS: + if (!fallthrough) { + schema._materialParams = material->getMaterialParams(); + wasSet = true; + } + break; + case graphics::Material::CULL_FACE_MODE: + if (!fallthrough) { + multiMaterial.setCullFaceMode(material->getCullFaceMode()); + wasSet = true; + } + break; + default: + break; + } + } else { + switch (flag) { + case graphics::MaterialKey::EMISSIVE_VAL_BIT: + if (materialKey.isEmissive()) { + toonSchema._emissive = material->getEmissive(false); + schemaKey.setEmissive(true); + wasSet = true; + } + break; + case graphics::MaterialKey::ALBEDO_VAL_BIT: + if (materialKey.isAlbedo()) { + toonSchema._albedo = material->getAlbedo(false); + schemaKey.setAlbedo(true); + wasSet = true; + } + break; + case graphics::MaterialKey::OPACITY_VAL_BIT: + if (materialKey.isTranslucentFactor()) { + toonSchema._opacity = material->getOpacity(); + schemaKey.setTranslucentFactor(true); + wasSet = true; + } + break; + case graphics::MaterialKey::OPACITY_CUTOFF_VAL_BIT: + if (materialKey.isOpacityCutoff()) { + toonSchema._opacityCutoff = material->getOpacityCutoff(); + schemaKey.setOpacityCutoff(true); + wasSet = true; + } + break; + case graphics::MaterialKey::ALBEDO_MAP_BIT: + if (materialKey.isAlbedoMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + material->resetOpacityMap(); + drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey.setAlbedoMap(true); + schemaKey.setOpacityMaskMap(material->getKey().isOpacityMaskMap()); + schemaKey.setTranslucentMap(material->getKey().isTranslucentMap()); + } + break; + case graphics::MaterialKey::NORMAL_MAP_BIT: + if (materialKey.isNormalMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey.setNormalMap(true); + } + break; + case graphics::MaterialKey::EMISSIVE_MAP_BIT: + // Lightmap takes precendence over emissive map for legacy reasons + if (materialKey.isEmissiveMap() && !materialKey.isLightMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey.setEmissiveMap(true); + } else if (materialKey.isLightMap()) { + // We'll set this later when we check the lightmap + wasSet = true; + } + break; + case graphics::Material::TEXCOORDTRANSFORM0: + if (!fallthrough) { + toonSchema._texcoordTransforms[0] = material->getTexCoordTransform(0); + wasSet = true; + } + break; + case graphics::Material::TEXCOORDTRANSFORM1: + if (!fallthrough) { + toonSchema._texcoordTransforms[1] = material->getTexCoordTransform(1); + wasSet = true; + } + break; + case graphics::Material::MATERIAL_PARAMS: + if (!fallthrough) { + toonSchema._materialParams = material->getMaterialParams(); + wasSet = true; + } + break; + case graphics::Material::CULL_FACE_MODE: + if (!fallthrough) { + multiMaterial.setCullFaceMode(material->getCullFaceMode()); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::SHADE_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::SHADE_VAL_BIT]) { + toonSchema._shade = material->getShade(); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADE_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_VAL_BIT]) { + toonSchema._shadingShift = material->getShadingShift(); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::SHADING_TOONY_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::SHADING_TOONY_VAL_BIT]) { + toonSchema._shadingToony = material->getShadingToony(); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADING_TOONY_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT]) { + toonSchema._uvAnimationScrollSpeed.x = material->getUVAnimationScrollXSpeed(); + toonSchema._uvAnimationScrollSpeed.y = material->getUVAnimationScrollYSpeed(); + toonSchema._uvAnimationScrollRotationSpeed = material->getUVAnimationRotationSpeed(); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::SHADE_MAP_BIT : + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::SHADE_MAP_BIT]) { + auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::SHADE_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialShade, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADE_MAP_BIT, true); + } + break; + case NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_MAP_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_MAP_BIT]) { + auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::SHADING_SHIFT_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialShadingShift, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_MAP_BIT, true); + } + break; + case NetworkMToonMaterial::MToonFlagBit::MATCAP_MAP_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::MATCAP_MAP_BIT]) { + auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::MATCAP_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialMatcap, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::MATCAP_MAP_BIT, true); + } + break; + case NetworkMToonMaterial::MToonFlagBit::RIM_MAP_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::RIM_MAP_BIT]) { + auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::RIM_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialRim, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::RIM_MAP_BIT, true); + } + break; + case NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_MASK_MAP_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_MASK_MAP_BIT]) { + auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::UV_ANIMATION_MASK_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialUVAnimationMask, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_MASK_MAP_BIT, true); + } + break; + case NetworkMToonMaterial::MToonFlagBit::MATCAP_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::MATCAP_VAL_BIT]) { + toonSchema._matcap = material->getMatcap(false); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::MATCAP_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_VAL_BIT]) { + toonSchema._parametricRim = material->getParametricRim(false); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_POWER_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_POWER_VAL_BIT]) { + toonSchema._parametricRimFresnelPower = material->getParametricRimFresnelPower(); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_POWER_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_LIFT_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_LIFT_VAL_BIT]) { + toonSchema._parametricRimLift = material->getParametricRimLift(); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_LIFT_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::RIM_LIGHTING_MIX_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::RIM_LIGHTING_MIX_VAL_BIT]) { + toonSchema._rimLightingMix = material->getRimLightingMix(); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::RIM_LIGHTING_MIX_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::OUTLINE_WIDTH_MODE_VAL_BIT: + if (!fallthrough) { + multiMaterial.setOutlineWidthMode(material->getOutlineWidthMode()); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::OUTLINE_WIDTH_VAL_BIT: + if (!fallthrough) { + multiMaterial.setOutlineWidth(material->getOutlineWidth()); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::OUTLINE_VAL_BIT: + if (!fallthrough) { + multiMaterial.setOutline(material->getOutline(false)); + wasSet = true; + } + break; + default: + break; + } } if (wasSet) { @@ -744,71 +1167,115 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto textureCache = DependencyManager::get(); // Handle defaults for (auto flag : flagsToSetDefault) { - switch (flag) { - case graphics::MaterialKey::EMISSIVE_VAL_BIT: - case graphics::MaterialKey::UNLIT_VAL_BIT: - case graphics::MaterialKey::ALBEDO_VAL_BIT: - case graphics::MaterialKey::METALLIC_VAL_BIT: - case graphics::MaterialKey::GLOSSY_VAL_BIT: - case graphics::MaterialKey::OPACITY_VAL_BIT: - case graphics::MaterialKey::OPACITY_CUTOFF_VAL_BIT: - case graphics::MaterialKey::SCATTERING_VAL_BIT: - case graphics::Material::TEXCOORDTRANSFORM0: - case graphics::Material::TEXCOORDTRANSFORM1: - case graphics::Material::LIGHTMAP_PARAMS: - case graphics::Material::MATERIAL_PARAMS: - // these are initialized to the correct default values in Schema() - break; - case graphics::Material::CULL_FACE_MODE: - multiMaterial.setCullFaceMode(graphics::Material::DEFAULT_CULL_FACE_MODE); - break; - case graphics::MaterialKey::ALBEDO_MAP_BIT: - if (schemaKey.isAlbedoMap()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); - } - break; - case graphics::MaterialKey::METALLIC_MAP_BIT: - if (schemaKey.isMetallicMap()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, textureCache->getBlackTexture()); - } - break; - case graphics::MaterialKey::ROUGHNESS_MAP_BIT: - if (schemaKey.isRoughnessMap()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, textureCache->getWhiteTexture()); - } - break; - case graphics::MaterialKey::NORMAL_MAP_BIT: - if (schemaKey.isNormalMap()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, textureCache->getBlueTexture()); - } - break; - case graphics::MaterialKey::OCCLUSION_MAP_BIT: - if (schemaKey.isOcclusionMap()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, textureCache->getWhiteTexture()); - } - break; - case graphics::MaterialKey::SCATTERING_MAP_BIT: - if (schemaKey.isScatteringMap()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, textureCache->getWhiteTexture()); - } - break; - case graphics::MaterialKey::EMISSIVE_MAP_BIT: - if (schemaKey.isEmissiveMap() && !schemaKey.isLightMap()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); - } - break; - case graphics::MaterialKey::LIGHT_MAP_BIT: - if (schemaKey.isLightMap()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); - } - break; - default: - break; + if (!multiMaterial.isMToon()) { + switch (flag) { + case graphics::Material::CULL_FACE_MODE: + multiMaterial.setCullFaceMode(graphics::Material::DEFAULT_CULL_FACE_MODE); + break; + case graphics::MaterialKey::ALBEDO_MAP_BIT: + if (schemaKey.isAlbedoMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); + } + break; + case graphics::MaterialKey::METALLIC_MAP_BIT: + if (schemaKey.isMetallicMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, textureCache->getBlackTexture()); + } + break; + case graphics::MaterialKey::ROUGHNESS_MAP_BIT: + if (schemaKey.isRoughnessMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, textureCache->getWhiteTexture()); + } + break; + case graphics::MaterialKey::NORMAL_MAP_BIT: + if (schemaKey.isNormalMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, textureCache->getBlueTexture()); + } + break; + case graphics::MaterialKey::OCCLUSION_MAP_BIT: + if (schemaKey.isOcclusionMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, textureCache->getWhiteTexture()); + } + break; + case graphics::MaterialKey::SCATTERING_MAP_BIT: + if (schemaKey.isScatteringMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, textureCache->getWhiteTexture()); + } + break; + case graphics::MaterialKey::EMISSIVE_MAP_BIT: + if (schemaKey.isEmissiveMap() && !schemaKey.isLightMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); + } + break; + case graphics::MaterialKey::LIGHT_MAP_BIT: + if (schemaKey.isLightMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); + } + break; + default: + // everything else is initialized to the correct default values in Schema() + break; + } + } else { + switch (flag) { + case graphics::Material::CULL_FACE_MODE: + multiMaterial.setCullFaceMode(graphics::Material::DEFAULT_CULL_FACE_MODE); + break; + case graphics::MaterialKey::ALBEDO_MAP_BIT: + if (schemaKey.isAlbedoMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); + } + break; + case graphics::MaterialKey::NORMAL_MAP_BIT: + if (schemaKey.isNormalMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, textureCache->getBlueTexture()); + } + break; + case graphics::MaterialKey::EMISSIVE_MAP_BIT: + if (schemaKey.isEmissiveMap() && !schemaKey.isLightMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); + } + break; + case NetworkMToonMaterial::MToonFlagBit::SHADE_MAP_BIT: + if (schemaKey._flags[NetworkMToonMaterial::MToonFlagBit::SHADE_MAP_BIT]) { + drawMaterialTextures->setTexture(gr::Texture::MaterialShade, textureCache->getWhiteTexture()); + } + break; + case NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_MAP_BIT: + if (schemaKey._flags[NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_MAP_BIT]) { + drawMaterialTextures->setTexture(gr::Texture::MaterialShadingShift, textureCache->getBlackTexture()); + } + break; + case NetworkMToonMaterial::MToonFlagBit::MATCAP_MAP_BIT: + if (schemaKey._flags[NetworkMToonMaterial::MToonFlagBit::MATCAP_MAP_BIT]) { + drawMaterialTextures->setTexture(gr::Texture::MaterialMatcap, textureCache->getBlackTexture()); + } + break; + case NetworkMToonMaterial::MToonFlagBit::RIM_MAP_BIT: + if (schemaKey._flags[NetworkMToonMaterial::MToonFlagBit::RIM_MAP_BIT]) { + drawMaterialTextures->setTexture(gr::Texture::MaterialRim, textureCache->getWhiteTexture()); + } + break; + case NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_MASK_MAP_BIT: + if (schemaKey._flags[NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_MASK_MAP_BIT]) { + drawMaterialTextures->setTexture(gr::Texture::MaterialUVAnimationMask, textureCache->getWhiteTexture()); + } + break; + default: + // everything else is initialized to the correct default values in ToonSchema() + break; + } } } - schema._key = (uint32_t)schemaKey._flags.to_ulong(); - schemaBuffer.edit() = schema; + auto& schemaBuffer = multiMaterial.getSchemaBuffer(); + if (multiMaterial.isMToon()) { + toonSchema._key = (uint32_t)schemaKey._flags.to_ulong(); + schemaBuffer.edit() = toonSchema; + } else { + schema._key = (uint32_t)schemaKey._flags.to_ulong(); + schemaBuffer.edit() = schema; + } multiMaterial.setNeedsUpdate(false); multiMaterial.setInitialized(); } @@ -818,10 +1285,16 @@ bool RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu: updateMultiMaterial(multiMaterial); } + if (multiMaterial.isMToon()) { + multiMaterial.setMToonTime(); + } + auto textureCache = DependencyManager::get(); static gpu::TextureTablePointer defaultMaterialTextures = std::make_shared(); static gpu::BufferView defaultMaterialSchema; + static gpu::TextureTablePointer defaultMToonMaterialTextures = std::make_shared(); + static gpu::BufferView defaultMToonMaterialSchema; static std::once_flag once; std::call_once(once, [textureCache] { @@ -835,6 +1308,18 @@ bool RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu: defaultMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, textureCache->getWhiteTexture()); defaultMaterialTextures->setTexture(gr::Texture::MaterialScattering, textureCache->getWhiteTexture()); // MaterialEmissiveLightmap has to be set later + + graphics::MultiMaterial::MToonSchema toonSchema; + defaultMToonMaterialSchema = gpu::BufferView(std::make_shared(sizeof(toonSchema), (const gpu::Byte*) &toonSchema, sizeof(toonSchema))); + + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialNormal, textureCache->getBlueTexture()); + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialShade, textureCache->getWhiteTexture()); + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialShadingShift, textureCache->getBlackTexture()); + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialMatcap, textureCache->getBlackTexture()); + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialRim, textureCache->getWhiteTexture()); + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialUVAnimationMask, textureCache->getWhiteTexture()); }); // For shadows, we only need opacity mask information @@ -845,20 +1330,24 @@ bool RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu: if (enableTextures) { batch.setResourceTextureTable(multiMaterial.getTextureTable()); } else { - if (renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { - if (key.isLightMap()) { - defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); - } else if (key.isEmissiveMap()) { - defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); + if (!multiMaterial.isMToon()) { + if (renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { + if (key.isLightMap()) { + defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); + } else if (key.isEmissiveMap()) { + defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); + } } - } - batch.setResourceTextureTable(defaultMaterialTextures); + batch.setResourceTextureTable(defaultMaterialTextures); + } else { + batch.setResourceTextureTable(defaultMToonMaterialTextures); + } } return true; } else { - batch.setResourceTextureTable(defaultMaterialTextures); - batch.setUniformBuffer(gr::Buffer::Material, defaultMaterialSchema); + batch.setResourceTextureTable(!multiMaterial.isMToon() ? defaultMaterialTextures : defaultMToonMaterialTextures); + batch.setUniformBuffer(gr::Buffer::Material, !multiMaterial.isMToon() ? defaultMaterialSchema : defaultMToonMaterialSchema); return false; } } diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 9fabad5fd0..d6c50bc6fb 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -37,6 +37,7 @@ using namespace render; extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); +extern void sortAndRenderZPassShapes(const ShapePlumberPointer& shapePlumber, const render::RenderContextPointer& renderContext, const render::ShapeBounds& inShapes, render::ItemBounds &itemBounds); void RenderShadowTask::configure(const Config& configuration) { //DependencyManager::get()->setShadowMapEnabled(configuration.isEnabled()); @@ -49,13 +50,10 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende 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()); + initZPassPipelines(*shapePlumber, std::make_shared(), fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); }); + const auto setupOutput = task.addJob("ShadowSetup", input); const auto queryResolution = setupOutput.getN(1); const auto shadowFrame = setupOutput.getN(3); @@ -254,58 +252,8 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat, false); - const std::vector keys = { - ShapeKey::Builder(), ShapeKey::Builder().withFade(), - ShapeKey::Builder().withDeformed(), ShapeKey::Builder().withDeformed().withFade(), - ShapeKey::Builder().withDeformed().withDualQuatSkinned(), ShapeKey::Builder().withDeformed().withDualQuatSkinned().withFade(), - ShapeKey::Builder().withOwnPipeline(), ShapeKey::Builder().withOwnPipeline().withFade(), - ShapeKey::Builder().withDeformed().withOwnPipeline(), ShapeKey::Builder().withDeformed().withOwnPipeline().withFade(), - ShapeKey::Builder().withDeformed().withDualQuatSkinned().withOwnPipeline(), ShapeKey::Builder().withDeformed().withDualQuatSkinned().withOwnPipeline().withFade(), - }; - std::vector> sortedShapeKeys(keys.size()); - - const int OWN_PIPELINE_INDEX = 6; - for (const auto& items : inShapes) { - int index = items.first.hasOwnPipeline() ? OWN_PIPELINE_INDEX : 0; - if (items.first.isDeformed()) { - index += 2; - if (items.first.isDualQuatSkinned()) { - index += 2; - } - } - - if (items.first.isFaded()) { - index += 1; - } - - sortedShapeKeys[index].push_back(items.first); - } - - // Render non-withOwnPipeline things - for (size_t i = 0; i < OWN_PIPELINE_INDEX; i++) { - auto& shapeKeys = sortedShapeKeys[i]; - if (shapeKeys.size() > 0) { - const auto& shapePipeline = _shapePlumber->pickPipeline(args, keys[i]); - args->_shapePipeline = shapePipeline; - for (const auto& key : shapeKeys) { - renderShapes(renderContext, _shapePlumber, inShapes.at(key)); - } - } - } - - // Render withOwnPipeline things - for (size_t i = OWN_PIPELINE_INDEX; i < keys.size(); i++) { - auto& shapeKeys = sortedShapeKeys[i]; - if (shapeKeys.size() > 0) { - args->_shapePipeline = nullptr; - for (const auto& key : shapeKeys) { - args->_itemShapeKey = key._flags.to_ulong(); - renderShapes(renderContext, _shapePlumber, inShapes.at(key)); - } - } - } - - args->_shapePipeline = nullptr; + render::ItemBounds itemBounds; + sortAndRenderZPassShapes(_shapePlumber, renderContext, inShapes, itemBounds); } args->_batch = nullptr; diff --git a/libraries/render-utils/src/model.slf b/libraries/render-utils/src/model.slf index 2f80fbde99..48e483edf7 100644 --- a/libraries/render-utils/src/model.slf +++ b/libraries/render-utils/src/model.slf @@ -5,6 +5,7 @@ // // Created by Andrzej Kapolka on 5/6/14. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 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 @@ -15,8 +16,51 @@ <@include render-utils/ShaderConstants.h@> <@include CullFace.slh@> +<@if HIFI_USE_MTOON@> + <@if HIFI_USE_SHADOW@> + <$declareMToonMaterialTextures(ALBEDO)$> + <@if HIFI_USE_MIRROR@> + LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_MIRROR) uniform sampler2D mirrorMap; + <@endif@> + <@else@> + <$declareMToonMaterialTextures(ALBEDO, HIFI_USE_NORMALMAP, SHADE, EMISSIVE, SHADING_SHIFT, MATCAP, RIM, UV_ANIMATION_MASK)$> + <@endif@> +<@else@> + <@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_TRANSLUCENT@> + <$declareMaterialTextures(ALBEDO, ROUGHNESS, HIFI_USE_NORMALMAP, METALLIC, EMISSIVE, OCCLUSION)$> + <@else@> + <$declareMaterialTextures(ALBEDO, ROUGHNESS, HIFI_USE_NORMALMAP, METALLIC, EMISSIVE, OCCLUSION, SCATTERING)$> + <@endif@> + <@else@> + <$declareMaterialTextures(ALBEDO, ROUGHNESS, HIFI_USE_NORMALMAP, METALLIC)$> + <$declareMaterialLightmap()$> + <@endif@> + <@endif@> +<@endif@> + <@if not HIFI_USE_SHADOW@> - <@if HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> + <@if HIFI_USE_MTOON@> + + <@include DefaultMaterials.slh@> + <@include GlobalLight.slh@> + <$declareEvalGlobalLightingAlphaBlendedMToon()$> + + <@include gpu/Transform.slh@> + <$declareStandardCameraTransform()$> + + <@if HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> + layout(location=0) out vec4 _fragColor0; + <@else@> + <@include DeferredBufferWrite.slh@> + <@endif@> + <@elif HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> <@include DefaultMaterials.slh@> <@include GlobalLight.slh@> <@if HIFI_USE_LIGHTMAP@> @@ -31,6 +75,7 @@ <@endif@> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> + layout(location=0) out vec4 _fragColor0; <@else@> <@include DeferredBufferWrite.slh@> @@ -51,32 +96,6 @@ <@include LightingModel.slh@> <@endif@> -<@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@> - <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL , METALLIC, EMISSIVE, OCCLUSION)$> - <@elif HIFI_USE_NORMALMAP@> - <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL , METALLIC, EMISSIVE, OCCLUSION, SCATTERING)$> - <@elif HIFI_USE_TRANSLUCENT@> - <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL , METALLIC, EMISSIVE, OCCLUSION)$> - <@else@> - <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL , METALLIC, EMISSIVE, OCCLUSION, SCATTERING)$> - <@endif@> - <@else@> - <@if HIFI_USE_NORMALMAP@> - <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC)$> - <@else@> - <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC)$> - <@endif@> - <$declareMaterialLightmap()$> - <@endif@> -<@endif@> - <@if HIFI_USE_FADE@> <@include Fade.slh@> <$declareFadeFragment()$> @@ -89,7 +108,9 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; <@if not HIFI_USE_SHADOW@> layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES; layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; - layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; + <@if not HIFI_USE_MTOON@> + layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; + <@endif@> <@if HIFI_USE_NORMALMAP@> layout(location=RENDER_UTILS_ATTR_TANGENT_WS) in vec3 _tangentWS; <@endif@> @@ -112,15 +133,21 @@ void main(void) { Material mat = getMaterial(); BITFIELD matKey = getMaterialKey(mat); <@if HIFI_USE_SHADOW or HIFI_USE_UNLIT@> + <@if not HIFI_USE_MTOON@> <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex)$> + <@else@> + <$fetchMToonMaterialTexturesCoord0(matKey, _texCoord0, albedoTex)$> + <@endif@> - <@if HIFI_USE_TRANSLUCENT@> float cutoff = getMaterialOpacityCutoff(mat); - float opacity = getMaterialOpacity(mat) * _color.a; + <@if HIFI_USE_TRANSLUCENT@> + float opacity = getMaterialOpacity(mat); + <@if not HIFI_USE_MTOON@> + opacity *= _color.a; + <@endif@> <$evalMaterialOpacity(albedoTex.a, cutoff, opacity, matKey, opacity)$>; <$discardInvisible(opacity)$>; <@else@> - float cutoff = getMaterialOpacityCutoff(mat); float opacity = 1.0; <$evalMaterialOpacityMask(albedoTex.a, cutoff, opacity, matKey, opacity)$>; <$discardTransparent(opacity)$>; @@ -129,7 +156,9 @@ void main(void) { <@if not HIFI_USE_SHADOW@> vec3 albedo = getMaterialAlbedo(mat); <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; - albedo *= _color.rgb; + <@if not HIFI_USE_MTOON@> + albedo *= _color.rgb; + <@endif@> <@if HIFI_USE_FADE@> albedo += fadeEmissive; <@endif@> @@ -152,6 +181,73 @@ void main(void) { opacity, albedo * isUnlitEnabled()); <@endif@> +<@elif HIFI_USE_MTOON@> + vec3 uvScrollSpeed = getMaterialUVScrollSpeed(mat); + float time = getMaterialTime(mat); + <@if HIFI_USE_NORMALMAP@> + <$fetchMToonMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, normalTex, shadeTex, emissiveTex, shadingShiftTex, rimTex, uvScrollSpeed, time)$> + <@else@> + <$fetchMToonMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, _SCRIBE_NULL, shadeTex, emissiveTex, shadingShiftTex, rimTex, uvScrollSpeed, time)$> + <@endif@> + + float cutoff = getMaterialOpacityCutoff(mat); + <@if HIFI_USE_TRANSLUCENT@> + float opacity = getMaterialOpacity(mat); + <$evalMaterialOpacity(albedoTex.a, cutoff, opacity, matKey, opacity)$>; + <$discardInvisible(opacity)$>; + <@else@> + float opacity = 1.0; + <$evalMaterialOpacityMask(albedoTex.a, cutoff, opacity, matKey, opacity)$>; + <$discardTransparent(opacity)$>; + <@endif@> + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + <@if HIFI_USE_NORMALMAP@> + vec3 fragNormalWS; + <$evalMaterialNormalLOD(_positionES, normalTex, _normalWS, _tangentWS, fragNormalWS)$> + <@else@> + vec3 fragNormalWS = _normalWS; + <@endif@> + fragNormalWS = evalFrontOrBackFaceNormal(normalize(fragNormalWS)); + + vec3 shade = getMaterialShade(mat); + <$evalMaterialShade(shadeTex, shade, matKey, shade)$>; + + float shadingShift = getMaterialShadingShift(mat); + <$evalMaterialShadingShift(shadingShiftTex, shadingShift, matKey, shadingShift)$>; + + TransformCamera cam = getTransformCamera(); + float metallic = DEFAULT_METALLIC; + vec3 fresnel = getFresnelF0(metallic, albedo); + + vec4 color = vec4(evalGlobalLightingAlphaBlendedMToon( + cam._viewInverse, 1.0, _positionES.xyz, fragNormalWS, + albedo, fresnel, metallic, emissive, DEFAULT_ROUGHNESS, opacity, + shade, shadingShift, getMaterialShadingToony(mat), getMaterialMatcap(mat), getMaterialParametricRim(mat), + getMaterialParametricRimFresnelPower(mat), getMaterialParametricRimLift(mat), rimTex, getMaterialRimLightingMix(mat), matKey), opacity); + + <@if HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> + _fragColor0 = isUnlitEnabled() * vec4(color.rgb + <@if HIFI_USE_FADE@> + + fadeEmissive + <@endif@> + , color.a); + <@else@> + packDeferredFragmentUnlit( + fragNormalWS, + 1.0, + color.rgb + <@if HIFI_USE_FADE@> + + fadeEmissive + <@endif@> + ); + <@endif@> + <@else@> <@if not HIFI_USE_LIGHTMAP@> <@if HIFI_USE_NORMALMAP and HIFI_USE_TRANSLUCENT@> @@ -172,14 +268,13 @@ void main(void) { <@endif@> <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmap)$> <@endif@> - - <@if HIFI_USE_TRANSLUCENT@> + float cutoff = getMaterialOpacityCutoff(mat); + <@if HIFI_USE_TRANSLUCENT@> float opacity = getMaterialOpacity(mat) * _color.a; <$evalMaterialOpacity(albedoTex.a, cutoff, opacity, matKey, opacity)$>; <$discardInvisible(opacity)$>; <@else@> - float cutoff = getMaterialOpacityCutoff(mat); float opacity = 1.0; <$evalMaterialOpacityMask(albedoTex.a, cutoff, opacity, matKey, opacity)$>; <$discardTransparent(opacity)$>; diff --git a/libraries/render-utils/src/model.slv b/libraries/render-utils/src/model.slv index 319711eac2..848acfc331 100644 --- a/libraries/render-utils/src/model.slv +++ b/libraries/render-utils/src/model.slv @@ -5,6 +5,7 @@ // // Created by Hifi Engine Team // Copyright 2019 High Fidelity, Inc. +// Copyright 2024 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 @@ -47,7 +48,9 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; <@if not HIFI_USE_SHADOW@> layout(location=RENDER_UTILS_ATTR_POSITION_ES) out vec4 _positionES; layout(location=RENDER_UTILS_ATTR_NORMAL_WS) out vec3 _normalWS; - layout(location=RENDER_UTILS_ATTR_COLOR) out vec4 _color; + <@if not HIFI_USE_MTOON@> + layout(location=RENDER_UTILS_ATTR_COLOR) out vec4 _color; + <@endif@> <@if HIFI_USE_NORMALMAP@> layout(location=RENDER_UTILS_ATTR_TANGENT_WS) out vec3 _tangentWS; <@endif@> @@ -96,7 +99,9 @@ void main(void) { _texCoord01 = vec4(0.0); } <@else@> +<@if not HIFI_USE_MTOON@> _color = color_sRGBAToLinear(inColor); +<@endif@> TexMapArray texMapArray = getTexMapArray(); <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _positionWS, _texCoord01.xy)$> diff --git a/libraries/render-utils/src/render-utils/model.slp b/libraries/render-utils/src/render-utils/model.slp index a3c28631e9..f2b1a0d588 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 mirror:f) 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) mtoon fade:f/forward:f deformed:v/deformeddq:v diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf index bf9bb0babd..f606c1be81 100644 --- a/libraries/render-utils/src/sdf_text3D.slf +++ b/libraries/render-utils/src/sdf_text3D.slf @@ -32,6 +32,7 @@ <@include sdf_text3D.slh@> <$declareEvalSDFSuperSampled()$> +layout(location=RENDER_UTILS_ATTR_POSITION_MS) in vec2 _positionMS; <@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@> layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES; <@endif@> @@ -48,7 +49,7 @@ layout(location=RENDER_UTILS_ATTR_FADE1) flat in vec4 _glyphBounds; // we're reu <@endif@> void main() { - vec4 color = evalSDFSuperSampled(_texCoord0, _glyphBounds); + vec4 color = evalSDFSuperSampled(_texCoord0, _positionMS, _glyphBounds); <@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@> color.a *= params.color.a; diff --git a/libraries/render-utils/src/sdf_text3D.slh b/libraries/render-utils/src/sdf_text3D.slh index 76ace99182..c927070a4d 100644 --- a/libraries/render-utils/src/sdf_text3D.slh +++ b/libraries/render-utils/src/sdf_text3D.slh @@ -17,13 +17,16 @@ LAYOUT(binding=0) uniform sampler2D fontTexture; struct TextParams { + vec4 bounds; vec4 color; - vec3 effectColor; - float effectThickness; + vec2 unitRange; int effect; - vec3 spare; + float effectThickness; + + vec3 effectColor; + float spare; }; LAYOUT(binding=0) uniform textParamsBuffer { @@ -37,51 +40,75 @@ LAYOUT(binding=0) uniform textParamsBuffer { const float interiorCutoff = 0.5; const float taaBias = pow(2.0, TAA_TEXTURE_LOD_BIAS); -vec4 evalSDF(vec2 texCoord, vec4 glyphBounds) { +// MSDF logic from: https://github.com/Chlumsky/msdfgen?tab=readme-ov-file#using-a-multi-channel-distance-field +float median(float r, float g, float b) { + return max(min(r, g), min(max(r, g), b)); +} + +float screenPxRange(vec2 texCoord) { + vec2 screenTexSize = vec2(1.0) / fwidth(texCoord); + return max(0.5 * dot(params.unitRange, screenTexSize), 1.0); +} + +vec2 evalSDF(vec2 texCoord) { + vec4 msdf = textureLod(fontTexture, texCoord, TAA_TEXTURE_LOD_BIAS); + float sdf = median(msdf.r, msdf.g, msdf.b); + float screenPxDistance = screenPxRange(texCoord) * (sdf - 0.5); + float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0); + return vec2(opacity, msdf.a); +} + +vec4 evalSDFColor(vec2 texCoord, vec4 glyphBounds) { vec3 color = params.color.rgb; - float sdf = textureLod(fontTexture, texCoord, TAA_TEXTURE_LOD_BIAS).g; + vec2 sdf = evalSDF(texCoord); // Outline if (params.effect == 1 || params.effect == 2) { - float outline = float(sdf < interiorCutoff); + float outline = float(sdf.x < interiorCutoff); color = mix(color, params.effectColor, outline); // with or without fill - sdf = mix(sdf, 0.0, float(params.effect == 1) * (1.0 - outline)); + sdf.x = mix(sdf.y, 0.0, float(params.effect == 1) * (1.0 - outline)); const float EPSILON = 0.00001; - sdf += mix(0.0, params.effectThickness - EPSILON, outline); + sdf.x += mix(0.0, params.effectThickness - EPSILON, outline); } else if (params.effect == 3) { // Shadow // don't sample from outside of our glyph bounds - sdf *= mix(1.0, 0.0, float(clamp(texCoord, glyphBounds.xy, glyphBounds.xy + glyphBounds.zw) != texCoord)); + sdf.x *= mix(1.0, 0.0, float(clamp(texCoord, glyphBounds.xy, glyphBounds.xy + glyphBounds.zw) != texCoord)); - if (sdf < interiorCutoff) { + if (sdf.x < interiorCutoff) { color = params.effectColor; const float DOUBLE_MAX_OFFSET_PIXELS = 20.0; // must match value in Font.cpp // FIXME: TAA_TEXTURE_LOD_BIAS doesn't have any effect because we're only generating one mip, so here we need to use 0, but it should // really match the LOD that we use in the textureLod call below vec2 textureOffset = vec2(params.effectThickness * DOUBLE_MAX_OFFSET_PIXELS) / vec2(textureSize(fontTexture, 0/*int(TAA_TEXTURE_LOD_BIAS)*/)); vec2 shadowTexCoords = texCoord - textureOffset; - sdf = textureLod(fontTexture, shadowTexCoords, TAA_TEXTURE_LOD_BIAS).g; + sdf.x = evalSDF(shadowTexCoords).x; // don't sample from outside of our glyph bounds - sdf *= mix(1.0, 0.0, float(clamp(shadowTexCoords, glyphBounds.xy, glyphBounds.xy + glyphBounds.zw) != shadowTexCoords)); + sdf.x *= mix(1.0, 0.0, float(clamp(shadowTexCoords, glyphBounds.xy, glyphBounds.xy + glyphBounds.zw) != shadowTexCoords)); } } - return vec4(color, sdf); + return vec4(color, sdf.x); } -vec4 evalSDFSuperSampled(vec2 texCoord, vec4 glyphBounds) { +vec4 evalSDFSuperSampled(vec2 texCoord, vec2 positionMS, vec4 glyphBounds) { + // Clip to edges. Note: We don't need to check the top edge. + if (positionMS.x < params.bounds.x || positionMS.x > (params.bounds.x + params.bounds.z) || + positionMS.y < params.bounds.y - params.bounds.w) { + return vec4(0.0); + } + vec2 dxTexCoord = dFdx(texCoord) * 0.5 * taaBias; vec2 dyTexCoord = dFdy(texCoord) * 0.5 * taaBias; // Perform 4x supersampling for anisotropic filtering vec4 color; - color = evalSDF(texCoord, glyphBounds); - color += evalSDF(texCoord + dxTexCoord, glyphBounds); - color += evalSDF(texCoord + dyTexCoord, glyphBounds); - color += evalSDF(texCoord + dxTexCoord + dyTexCoord, glyphBounds); + color = evalSDFColor(texCoord, glyphBounds); + color += evalSDFColor(texCoord + dxTexCoord, glyphBounds); + color += evalSDFColor(texCoord + dyTexCoord, glyphBounds); + color += evalSDFColor(texCoord + dxTexCoord + dyTexCoord, glyphBounds); color *= 0.25; return vec4(color.rgb, step(interiorCutoff, color.a)); diff --git a/libraries/render-utils/src/sdf_text3D.slv b/libraries/render-utils/src/sdf_text3D.slv index 9ac3b871f9..db13e170e9 100644 --- a/libraries/render-utils/src/sdf_text3D.slv +++ b/libraries/render-utils/src/sdf_text3D.slv @@ -19,6 +19,7 @@ <@include sdf_text3D.slh@> +layout(location=RENDER_UTILS_ATTR_POSITION_MS) out vec2 _positionMS; <@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@> layout(location=RENDER_UTILS_ATTR_POSITION_ES) out vec4 _positionES; <@endif@> @@ -27,6 +28,7 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; layout(location=RENDER_UTILS_ATTR_FADE1) flat out vec4 _glyphBounds; // we're reusing the fade texcoord locations here void main() { + _positionMS = inPosition.xy; _texCoord01 = vec4(inTexCoord0.st, 0.0, 0.0); _glyphBounds = inTexCoord1; diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index dd7074a071..3019b8f1c3 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -13,6 +13,10 @@ #include #include #include +#include + +#include "artery-font/artery-font.h" +#include "artery-font/std-artery-font.h" #include @@ -77,12 +81,150 @@ struct QuadBuilder { } }; -Font::Pointer Font::load(QIODevice& fontFile) { - Pointer font = std::make_shared(); +Font::Pointer Font::load(const QString& family, QIODevice& fontFile) { + Pointer font = std::make_shared(family); font->read(fontFile); return font; } +void Font::handleFontNetworkReply() { + auto requestReply = qobject_cast(sender()); + Q_ASSERT(requestReply != nullptr); + + if (requestReply->error() == QNetworkReply::NoError) { + read(*requestReply); + } else { + qDebug() << "Error downloading " << requestReply->url() << " - " << requestReply->errorString(); + } +} + +QThreadStorage _readOffset; +QThreadStorage _readMax; +int readHelper(void* dst, int length, void* data) { + if (_readOffset.localData() + length > _readMax.localData()) { + return -1; + } + memcpy(dst, (char *)data + _readOffset.localData(), length); + _readOffset.setLocalData(_readOffset.localData() + length); + return length; +}; + +void Font::read(QIODevice& in) { + _loaded = false; + + QByteArray data = in.readAll(); + _readOffset.setLocalData(0); + _readMax.setLocalData(data.length()); + artery_font::StdArteryFont arteryFont; + bool success = artery_font::decode<&readHelper, float, artery_font::StdList, artery_font::StdByteArray, artery_font::StdString>(arteryFont, (void *)data.data()); + + if (!success) { + qDebug() << "Font" << _family << "failed to decode."; + return; + } + + if (arteryFont.variants.length() == 0) { + qDebug() << "Font" << _family << "has 0 variants."; + return; + } + + _distanceRange = glm::vec2(arteryFont.variants[0].metrics.distanceRange); + _fontSize = arteryFont.variants[0].metrics.ascender + fabs(arteryFont.variants[0].metrics.descender); + _leading = arteryFont.variants[0].metrics.lineHeight; + _spaceWidth = 0.5f * arteryFont.variants[0].metrics.emSize; // We use half the emSize as a first guess for _spaceWidth + + if (arteryFont.variants[0].glyphs.length() == 0) { + qDebug() << "Font" << _family << "has 0 glyphs."; + return; + } + + QVector glyphs; + glyphs.reserve(arteryFont.variants[0].glyphs.length()); + for (int i = 0; i < arteryFont.variants[0].glyphs.length(); i++) { + auto& g = arteryFont.variants[0].glyphs[i]; + + Glyph glyph; + glyph.c = g.codepoint; + glyph.texOffset = glm::vec2(g.imageBounds.l, g.imageBounds.b); + glyph.texSize = glm::vec2(g.imageBounds.r, g.imageBounds.t) - glyph.texOffset; + glyph.offset = glm::vec2(g.planeBounds.l, g.planeBounds.b); + glyph.size = glm::vec2(g.planeBounds.r, g.planeBounds.t) - glyph.offset; + glyph.d = g.advance.h; + glyphs.push_back(glyph); + + // If we find the space character, we save its size in _spaceWidth for later + if (glyph.c == ' ') { + _spaceWidth = glyph.d; + } + } + + if (arteryFont.images.length() == 0) { + qDebug() << "Font" << _family << "has 0 images."; + return; + } + + if (arteryFont.images[0].imageType != artery_font::ImageType::IMAGE_MTSDF) { + qDebug() << "Font" << _family << "has the wrong image type. Expected MTSDF (7), got" << arteryFont.images[0].imageType; + return; + } + + if (arteryFont.images[0].encoding != artery_font::ImageEncoding::IMAGE_PNG) { + qDebug() << "Font" << _family << "has the wrong encoding. Expected PNG (8), got" << arteryFont.images[0].encoding; + return; + } + + if (arteryFont.images[0].pixelFormat != artery_font::PixelFormat::PIXEL_UNSIGNED8) { + qDebug() << "Font" << _family << "has the wrong pixel format. Expected unsigned char (8), got" << arteryFont.images[0].pixelFormat; + return; + } + + if (arteryFont.images[0].width == 0 || arteryFont.images[0].height == 0) { + qDebug() << "Font" << _family << "has image with width or height of 0. Width:" << arteryFont.images[0].width << ", height:"<< arteryFont.images[0].height; + return; + } + + // read image data + QImage image; + if (!image.loadFromData((const unsigned char*)arteryFont.images[0].data, arteryFont.images[0].data.length(), "PNG")) { + qDebug() << "Failed to read image for font" << _family; + return; + } + + _glyphs.clear(); + glm::vec2 imageSize = toGlm(image.size()); + _distanceRange /= imageSize; + foreach(Glyph g, glyphs) { + // Adjust the pixel texture coordinates into UV coordinates, + g.texSize /= imageSize; + g.texOffset /= imageSize; + // Y flip + g.texOffset.y = 1.0f - (g.texOffset.y + g.texSize.y); + g.offset.y = -(1.0f - (g.offset.y + g.size.y)); + // store in the character to glyph hash + _glyphs[g.c] = g; + }; + + image = image.convertToFormat(QImage::Format_RGBA8888); + + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); + if (image.hasAlphaChannel()) { + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::BGRA); + } + // FIXME: We're forcing this to use only one mip, and then manually doing anisotropic filtering in the shader, + // and also calling textureLod. Shouldn't this just use anisotropic filtering and auto-generate mips? + // We should also use smoothstep for anti-aliasing, as explained here: https://github.com/libgdx/libgdx/wiki/Distance-field-fonts + _texture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::SINGLE_MIP, + gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR)); + _texture->setStoredMipFormat(formatMip); + _texture->assignStoredMip(0, image.sizeInBytes(), image.constBits()); + _texture->setImportant(true); + + _loaded = true; + _needsParamsUpdate = true; +} + static QHash LOADED_FONTS; Font::Pointer Font::load(const QString& family) { @@ -91,16 +233,15 @@ Font::Pointer Font::load(const QString& family) { QString loadFilename; if (family == ROBOTO_FONT_FAMILY) { - loadFilename = ":/Roboto.sdff"; + loadFilename = ":/Roboto.arfont"; } else if (family == INCONSOLATA_FONT_FAMILY) { - loadFilename = ":/InconsolataMedium.sdff"; + loadFilename = ":/InconsolataMedium.arfont"; } else if (family == COURIER_FONT_FAMILY) { - loadFilename = ":/CourierPrime.sdff"; + loadFilename = ":/CourierPrime.arfont"; } else if (family == TIMELESS_FONT_FAMILY) { - loadFilename = ":/Timeless.sdff"; + loadFilename = ":/Timeless.arfont"; } else if (family.startsWith("http")) { - auto loadingFont = std::make_shared(); - loadingFont->setLoaded(false); + auto loadingFont = std::make_shared(family); LOADED_FONTS[family] = loadingFont; auto& networkAccessManager = NetworkAccessManager::getInstance(); @@ -114,7 +255,7 @@ Font::Pointer Font::load(const QString& family) { connect(networkReply, &QNetworkReply::finished, loadingFont.get(), &Font::handleFontNetworkReply); } else if (!LOADED_FONTS.contains(ROBOTO_FONT_FAMILY)) { // Unrecognized font and we haven't loaded Roboto yet - loadFilename = ":/Roboto.sdff"; + loadFilename = ":/Roboto.arfont"; } else { // Unrecognized font but we've already loaded Roboto LOADED_FONTS[family] = LOADED_FONTS[ROBOTO_FONT_FAMILY]; @@ -126,25 +267,13 @@ Font::Pointer Font::load(const QString& family) { qCDebug(renderutils) << "Loaded font" << loadFilename << "from Qt Resource System."; - LOADED_FONTS[family] = load(fontFile); + LOADED_FONTS[family] = load(family, fontFile); } } return LOADED_FONTS[family]; } -void Font::handleFontNetworkReply() { - auto requestReply = qobject_cast(sender()); - Q_ASSERT(requestReply != nullptr); - - if (requestReply->error() == QNetworkReply::NoError) { - setLoaded(true); - read(*requestReply); - } else { - qDebug() << "Error downloading " << requestReply->url() << " - " << requestReply->errorString(); - } -} - -Font::Font() { +Font::Font(const QString& family) : _family(family) { static std::once_flag once; std::call_once(once, []{ Q_INIT_RESOURCE(fonts); @@ -197,82 +326,6 @@ glm::vec2 Font::computeExtent(const QString& str) const { return extent; } -void Font::read(QIODevice& in) { - uint8_t header[4]; - readStream(in, header); - if (memcmp(header, "SDFF", 4)) { - qDebug() << "Bad SDFF file"; - _loaded = false; - return; - } - - uint16_t version; - readStream(in, version); - - // read font name - _family = ""; - if (version > 0x0001) { - char c; - readStream(in, c); - while (c) { - _family += c; - readStream(in, c); - } - } - - // read font data - readStream(in, _leading); - readStream(in, _ascent); - readStream(in, _descent); - readStream(in, _spaceWidth); - _fontSize = _ascent + _descent; - - // Read character count - uint16_t count; - readStream(in, count); - // read metrics data for each character - QVector glyphs(count); - // std::for_each instead of Qt foreach because we need non-const references - std::for_each(glyphs.begin(), glyphs.end(), [&](Glyph& g) { - g.read(in); - }); - - // read image data - QImage image; - if (!image.loadFromData(in.readAll(), "PNG")) { - qDebug() << "Failed to read SDFF image"; - _loaded = false; - return; - } - - _glyphs.clear(); - glm::vec2 imageSize = toGlm(image.size()); - foreach(Glyph g, glyphs) { - // Adjust the pixel texture coordinates into UV coordinates, - g.texSize /= imageSize; - g.texOffset /= imageSize; - // store in the character to glyph hash - _glyphs[g.c] = g; - }; - - image = image.convertToFormat(QImage::Format_RGBA8888); - - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); - if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); - formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::BGRA); - } - // FIXME: We're forcing this to use only one mip, and then manually doing anisotropic filtering in the shader, - // and also calling textureLod. Shouldn't this just use anisotropic filtering and auto-generate mips? - // We should also use smoothstep for anti-aliasing, as explained here: https://github.com/libgdx/libgdx/wiki/Distance-field-fonts - _texture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::SINGLE_MIP, - gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR)); - _texture->setStoredMipFormat(formatMip); - _texture->assignStoredMip(0, image.sizeInBytes(), image.constBits()); - _texture->setImportant(true); -} - void Font::setupGPU() { if (_pipelines.empty()) { using namespace shader::render_utils::program; @@ -340,13 +393,21 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm drawInfo.bounds = bounds; drawInfo.origin = origin; - float enlargedBoundsX = bounds.x - 0.5f * DOUBLE_MAX_OFFSET_PIXELS * float(enlargeForShadows); - float rightEdge = origin.x + enlargedBoundsX; + float rightEdge = origin.x + bounds.x; // Top left of text + bool firstTokenOfLine = true; glm::vec2 advance = origin; std::vector> glyphsAndCorners; - foreach(const QString& token, tokenizeForWrapping(str)) { + const QStringList tokens = tokenizeForWrapping(str); + for (int i = 0; i < tokens.length(); i++) { + const QString& token = tokens[i]; + + if ((bounds.y != -1) && (advance.y < origin.y - bounds.y)) { + // We are out of the y bound, stop drawing + break; + } + bool isNewLine = (token == QString('\n')); bool forceNewLine = false; @@ -355,43 +416,50 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm // We are out of the x bound, force new line forceNewLine = true; } - if (isNewLine || forceNewLine) { + + if (isNewLine || (forceNewLine && !firstTokenOfLine)) { + if (forceNewLine && !firstTokenOfLine) { + // We want to try this token again on the new line + i--; + } + // Character return, move the advance to a new line advance = glm::vec2(origin.x, advance.y - _leading); - - if (isNewLine) { - // No need to draw anything, go directly to next token - continue; - } else if (computeExtent(token).x > enlargedBoundsX) { - // token will never fit, stop drawing - break; - } - } - if ((bounds.y != -1) && (advance.y - _fontSize < origin.y - bounds.y)) { - // We are out of the y bound, stop drawing - break; + firstTokenOfLine = true; + // No need to draw anything, go directly to next token + continue; } // Draw the token - if (!isNewLine) { - for (auto c : token) { - auto glyph = _glyphs[c]; - - glyphsAndCorners.emplace_back(glyph, advance - glm::vec2(0.0f, _ascent)); - - // Advance by glyph size - advance.x += glyph.d; + for (const QChar& c : token) { + if (advance.x > rightEdge) { + break; } + const Glyph& glyph = _glyphs[c]; + glyphsAndCorners.emplace_back(glyph, advance); + + // Advance by glyph size + advance.x += glyph.d; + } + + if (forceNewLine && firstTokenOfLine) { + // If the first word of a line didn't fit, we draw as many characters as we could, now go to the next line + // Character return, move the advance to a new line + advance = glm::vec2(origin.x, advance.y - _leading); + firstTokenOfLine = true; + } else { // Add space after all non return tokens advance.x += _spaceWidth; + // Our token fits in the x direction! Any subsequent tokens won't be the first for this line. + firstTokenOfLine = false; } } std::vector quadBuilders; quadBuilders.reserve(glyphsAndCorners.size()); { - int i = glyphsAndCorners.size() - 1; + int i = (int)glyphsAndCorners.size() - 1; while (i >= 0) { auto nextGlyphAndCorner = glyphsAndCorners[i]; float rightSpacing = rightEdge - (nextGlyphAndCorner.second.x + nextGlyphAndCorner.first.d); @@ -399,7 +467,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm alignment, rightSpacing)); i--; while (i >= 0) { - auto prevGlyphAndCorner = glyphsAndCorners[i]; + const auto& prevGlyphAndCorner = glyphsAndCorners[i]; // We're to the right of the last character we checked, which means we're on a previous line, so we need to // recalculate the spacing, so we exit this loop if (prevGlyphAndCorner.second.x >= nextGlyphAndCorner.second.x) { @@ -416,7 +484,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm } // The quadBuilders is backwards now because we looped over the glyphs backwards to adjust their alignment - for (int i = quadBuilders.size() - 1; i >= 0; i--) { + for (int i = (int)quadBuilders.size() - 1; i >= 0; i--) { quint16 verticesOffset = numVertices; drawInfo.verticesBuffer->append(quadBuilders[i]); numVertices += VERTICES_PER_QUAD; @@ -458,8 +526,10 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const DrawPro int textEffect = (int)props.effect; const int SHADOW_EFFECT = (int)TextEffect::SHADOW_EFFECT; + const bool boundsChanged = props.bounds != drawInfo.bounds || props.origin != drawInfo.origin; + // If we're switching to or from shadow effect mode, we need to rebuild the vertices - if (props.str != drawInfo.string || props.bounds != drawInfo.bounds || props.origin != drawInfo.origin || props.alignment != _alignment || + if (props.str != drawInfo.string || boundsChanged || props.alignment != _alignment || (drawInfo.params.effect != textEffect && (textEffect == SHADOW_EFFECT || drawInfo.params.effect == SHADOW_EFFECT)) || (textEffect == SHADOW_EFFECT && props.scale != _scale)) { _scale = props.scale; @@ -469,8 +539,9 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const DrawPro setupGPU(); - if (!drawInfo.paramsBuffer || drawInfo.params.color != props.color || drawInfo.params.effectColor != props.effectColor || - drawInfo.params.effectThickness != props.effectThickness || drawInfo.params.effect != textEffect) { + if (!drawInfo.paramsBuffer || boundsChanged || _needsParamsUpdate || drawInfo.params.color != props.color || + drawInfo.params.effectColor != props.effectColor || drawInfo.params.effectThickness != props.effectThickness || + drawInfo.params.effect != textEffect) { drawInfo.params.color = props.color; drawInfo.params.effectColor = props.effectColor; drawInfo.params.effectThickness = props.effectThickness; @@ -478,14 +549,18 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const DrawPro // need the gamma corrected color here DrawParams gpuDrawParams; + gpuDrawParams.bounds = glm::vec4(props.origin, props.bounds); gpuDrawParams.color = ColorUtils::sRGBToLinearVec4(drawInfo.params.color); - gpuDrawParams.effectColor = ColorUtils::sRGBToLinearVec3(drawInfo.params.effectColor); - gpuDrawParams.effectThickness = drawInfo.params.effectThickness; + gpuDrawParams.unitRange = _distanceRange; gpuDrawParams.effect = drawInfo.params.effect; + gpuDrawParams.effectThickness = drawInfo.params.effectThickness; + gpuDrawParams.effectColor = ColorUtils::sRGBToLinearVec3(drawInfo.params.effectColor); if (!drawInfo.paramsBuffer) { drawInfo.paramsBuffer = std::make_shared(sizeof(DrawParams), nullptr); } drawInfo.paramsBuffer->setSubData(0, sizeof(DrawParams), (const gpu::Byte*)&gpuDrawParams); + + _needsParamsUpdate = false; } batch.setPipeline(_pipelines[std::make_tuple(props.color.a < 1.0f, props.unlit, props.forward, props.mirror)]); diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h index e8a353a686..c9d96bc6f6 100644 --- a/libraries/render-utils/src/text/Font.h +++ b/libraries/render-utils/src/text/Font.h @@ -24,22 +24,25 @@ class Font : public QObject { public: using Pointer = std::shared_ptr; - Font(); + Font(const QString& family); void read(QIODevice& path); struct DrawParams { - vec4 color { 0 }; + vec4 bounds { 0.0f }; + vec4 color { 0.0f }; - vec3 effectColor { 0 }; - float effectThickness { 0 }; + vec2 unitRange { 1.0f }; int effect { 0 }; + float effectThickness { 0.0f }; + + vec3 effectColor { 0.0f }; #if defined(__clang__) __attribute__((unused)) #endif - vec3 _spare; + float _spare; }; struct DrawInfo { @@ -85,13 +88,12 @@ public: static Pointer load(const QString& family); bool isLoaded() const { return _loaded; } - void setLoaded(bool loaded) { _loaded = loaded; } public slots: void handleFontNetworkReply(); private: - static Pointer load(QIODevice& fontFile); + static Pointer load(const QString& family, QIODevice& fontFile); QStringList tokenizeForWrapping(const QString& str) const; QStringList splitLines(const QString& str) const; glm::vec2 computeTokenExtent(const QString& str) const; @@ -111,16 +113,16 @@ private: // Font characteristics QString _family; + glm::vec2 _distanceRange { 1.0f }; float _fontSize { 0.0f }; float _leading { 0.0f }; - float _ascent { 0.0f }; - float _descent { 0.0f }; float _spaceWidth { 0.0f }; float _scale { 0.0f }; - TextAlignment _alignment; + TextAlignment _alignment { TextAlignment::LEFT }; - bool _loaded { true }; + bool _loaded { false }; + bool _needsParamsUpdate { false }; gpu::TexturePointer _texture; gpu::BufferStreamPointer _stream; diff --git a/libraries/render-utils/src/text/Glyph.cpp b/libraries/render-utils/src/text/Glyph.cpp index ee3656bc00..c54f0422f3 100644 --- a/libraries/render-utils/src/text/Glyph.cpp +++ b/libraries/render-utils/src/text/Glyph.cpp @@ -9,15 +9,3 @@ QRectF Glyph::bounds() const { QRectF Glyph::textureBounds() const { return glmToRect(texOffset, texSize); } - -void Glyph::read(QIODevice& in) { - uint16_t charcode; - readStream(in, charcode); - c = charcode; - readStream(in, texOffset); - readStream(in, size); - readStream(in, offset); - readStream(in, d); - // texSize is divided by the image size later - texSize = size; -} diff --git a/libraries/render-utils/src/text/Glyph.h b/libraries/render-utils/src/text/Glyph.h index 3cb08cc7e2..5aeb96b2c6 100644 --- a/libraries/render-utils/src/text/Glyph.h +++ b/libraries/render-utils/src/text/Glyph.h @@ -26,13 +26,10 @@ struct Glyph { vec2 size; vec2 offset; float d; // xadvance - adjusts character positioning - size_t indexOffset; // We adjust bounds because offset is the bottom left corner of the font but the top left corner of a QRect QRectF bounds() const; QRectF textureBounds() const; - - void read(QIODevice& in); }; #endif diff --git a/libraries/render/src/render/FilterTask.cpp b/libraries/render/src/render/FilterTask.cpp index 90720f5666..22f61a42fe 100644 --- a/libraries/render/src/render/FilterTask.cpp +++ b/libraries/render/src/render/FilterTask.cpp @@ -139,12 +139,12 @@ void IDsToBounds::run(const RenderContextPointer& renderContext, const ItemIDs& for (auto id : inItems) { auto& item = scene->getItem(id); if (item.exist()) { - outItems.emplace_back(ItemBound{ id, item.getBound(renderContext->args) }); + outItems.emplace_back(ItemBound(id, item.getBound(renderContext->args))); } } } else { for (auto id : inItems) { - outItems.emplace_back(ItemBound{ id }); + outItems.emplace_back(ItemBound(id)); } } } diff --git a/libraries/render/src/render/HighlightStyle.h b/libraries/render/src/render/HighlightStyle.h index 8bef5c33c3..138674ffbb 100644 --- a/libraries/render/src/render/HighlightStyle.h +++ b/libraries/render/src/render/HighlightStyle.h @@ -3,6 +3,7 @@ // Created by Olivier Prat on 11/06/2017. // Copyright 2017 High Fidelity, Inc. +// Copyright 2024 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 @@ -12,6 +13,7 @@ #define hifi_render_utils_HighlightStyle_h #include +#include #include @@ -21,23 +23,59 @@ namespace render { class HighlightStyle { public: struct RGBA { - glm::vec3 color{ 1.0f, 0.7f, 0.2f }; - float alpha{ 0.9f }; + glm::vec3 color { 1.0f, 0.7f, 0.2f }; + float alpha { 0.9f }; RGBA(const glm::vec3& c, float a) : color(c), alpha(a) {} + + std::string toString() const { return glm::to_string(color) + " " + std::to_string(alpha); } }; - RGBA _outlineUnoccluded{ { 1.0f, 0.7f, 0.2f }, 0.9f }; - RGBA _outlineOccluded{ { 1.0f, 0.7f, 0.2f }, 0.9f }; - RGBA _fillUnoccluded{ { 0.2f, 0.7f, 1.0f }, 0.0f }; - RGBA _fillOccluded{ { 0.2f, 0.7f, 1.0f }, 0.0f }; + RGBA _outlineUnoccluded { { 1.0f, 0.7f, 0.2f }, 0.9f }; + RGBA _outlineOccluded { { 1.0f, 0.7f, 0.2f }, 0.9f }; + RGBA _fillUnoccluded { { 0.2f, 0.7f, 1.0f }, 0.0f }; + RGBA _fillOccluded { { 0.2f, 0.7f, 1.0f }, 0.0f }; - float _outlineWidth{ 2.0f }; - bool _isOutlineSmooth{ false }; + float _outlineWidth { 2.0f }; + bool _isOutlineSmooth { false }; bool isFilled() const { return _fillUnoccluded.alpha > 5e-3f || _fillOccluded.alpha > 5e-3f; } + + std::string toString() const { + return _outlineUnoccluded.toString() + _outlineOccluded.toString() + _fillUnoccluded.toString() + + _fillOccluded.toString() + std::to_string(_outlineWidth) + std::to_string(_isOutlineSmooth); + } + + static HighlightStyle calculateOutlineStyle(uint8_t mode, float outlineWidth, const glm::vec3& outline, + const glm::vec3& position, const ViewFrustum& viewFrustum, size_t screenHeight) { + HighlightStyle style; + style._outlineUnoccluded.color = outline; + style._outlineUnoccluded.alpha = 1.0f; + style._outlineOccluded.alpha = 0.0f; + style._fillUnoccluded.alpha = 0.0f; + style._fillOccluded.alpha = 0.0f; + style._isOutlineSmooth = false; + + if (mode == 1) { // OUTLINE_WORLD + // FIXME: this is a hacky approximation, which gives us somewhat accurate widths with distance based falloff. + // Our outline implementation doesn't support the necessary vertex based extrusion to do real world based outlines. + glm::vec4 viewPos = glm::inverse(viewFrustum.getView()) * glm::vec4(position, 1.0f); + + const glm::mat4& projection = viewFrustum.getProjection(); + glm::vec4 p1 = projection * (viewPos + glm::vec4(0.0f, 0.5f * outlineWidth, 0.0f, 0.0f)); + p1 /= p1.w; + glm::vec4 p2 = projection * (viewPos - glm::vec4(0.0f, 0.5f * outlineWidth, 0.0f, 0.0f)); + p2 /= p2.w; + + style._outlineWidth = floor(0.5f * (float)screenHeight * fabs(p1.y - p2.y)); + } else { // OUTLINE_SCREEN + style._outlineWidth = floor(outlineWidth * (float)screenHeight); + } + + return style; + } }; } diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index f169de5c98..b5a6befdcb 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 1/26/16. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 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 @@ -174,4 +175,11 @@ namespace render { } return payload->computeMirrorView(viewFrustum); } + + template <> HighlightStyle payloadGetOutlineStyle(const PayloadProxyInterface::Pointer& payload, const ViewFrustum& viewFrustum, const size_t height) { + if (!payload) { + return HighlightStyle(); + } + return payload->getOutlineStyle(viewFrustum, height); + } } diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index a5cda7cedf..791b180e04 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 1/26/16. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 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 @@ -29,6 +30,7 @@ #include "ShapePipeline.h" #include "BlendshapeConstants.h" +#include "HighlightStyle.h" namespace render { @@ -104,18 +106,19 @@ public: META_CULL_GROUP, // As a meta item, the culling of my sub items is based solely on my bounding box and my visibility in the view SUB_META_CULLED, // As a sub item of a meta render item set as cull group, need to be set to my culling to the meta render it - FIRST_TAG_BIT, // 8 Tags available to organize the items and filter them against + FIRST_TAG_BIT, // 8 Tags available to organize the items and filter them against LAST_TAG_BIT = FIRST_TAG_BIT + NUM_TAGS, - 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, - MIRROR, - SIMULATE, + MIRROR, // Item is a mirror + SIMULATE, // Item requires simulation + OUTLINE, // Item has an outline __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 }; typedef std::bitset Flags; @@ -175,6 +178,9 @@ public: Builder& withLayer(uint8_t layer) { _flags = evalLayerBitsWithKeyBits(layer, _flags.to_ulong()); return (*this); } Builder& withoutLayer() { return withLayer(LAYER_DEFAULT); } + Builder& withOutline() { _flags.set(OUTLINE); return (*this); } + Builder& withoutOutline() { _flags.reset(OUTLINE); return (*this); } + // Convenient standard keys that we will keep on using all over the place static Builder opaqueShape() { return Builder().withTypeShape(); } static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); } @@ -224,6 +230,8 @@ public: bool isLayered() const { return getLayer() != LAYER_DEFAULT; } bool isSpatial() const { return !isLayered(); } + bool isOutline() const { return _flags[OUTLINE]; } + // Probably not public, flags used by the scene bool isSmall() const { return _flags[__SMALLER]; } void setSmaller(bool smaller) { (smaller ? _flags.set(__SMALLER) : _flags.reset(__SMALLER)); } @@ -301,6 +309,9 @@ public: Builder& withoutLayered() { _value = ItemKey::evalLayerBitsWithKeyBits(ItemKey::LAYER_DEFAULT, _value.to_ulong()); _mask |= ItemKey::KEY_LAYER_BITS_MASK; return (*this); } Builder& withLayer(uint8_t layer) { _value = ItemKey::evalLayerBitsWithKeyBits(layer, _value.to_ulong()); _mask |= ItemKey::KEY_LAYER_BITS_MASK; return (*this); } + Builder& withoutOutline() { _value.reset(ItemKey::OUTLINE); _mask.set(ItemKey::OUTLINE); return (*this); } + Builder& withOutline() { _value.set(ItemKey::OUTLINE); _mask.set(ItemKey::OUTLINE); return (*this); } + Builder& withNothing() { _value.reset(); _mask.reset(); return (*this); } // Convenient standard keys that we will keep on using all over the place @@ -345,9 +356,9 @@ class ItemBound { ItemBound(ItemID id) : id(id) { } ItemBound(ItemID id, const AABox& bound) : id(id), bound(bound) { } - ItemID id; + ItemID id { 0 }; AABox bound; - uint32_t padding; + uint32_t padding { 0 }; }; // many Item Bounds in a vector @@ -461,6 +472,8 @@ public: virtual ItemID computeMirrorView(ViewFrustum& viewFrustum) const = 0; + virtual HighlightStyle getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const = 0; + ~PayloadInterface() {} // Status interface is local to the base class @@ -519,6 +532,8 @@ public: ItemID computeMirrorView(ViewFrustum& viewFrustum) const { return _payload->computeMirrorView(viewFrustum); } + HighlightStyle getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const { return _payload->getOutlineStyle(viewFrustum, height); } + // Access the status const StatusPointer& getStatus() const { return _payload->getStatus(); } @@ -577,6 +592,12 @@ template bool payloadPassesZoneOcclusionTest(const std::shared_ptr& // Mirror Interface template ItemID payloadComputeMirrorView(const std::shared_ptr& payloadData, ViewFrustum& viewFrustum) { return Item::INVALID_ITEM_ID; } +// Outline Interface +// Allows payloads to specify an outline style +template HighlightStyle payloadGetOutlineStyle(const std::shared_ptr& payloadData, const ViewFrustum& viewFrustum, const size_t height) { + return HighlightStyle(); +} + // 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" @@ -606,6 +627,8 @@ public: virtual ItemID computeMirrorView(ViewFrustum& viewFrustum) const override { return payloadComputeMirrorView(_data, viewFrustum); } + virtual HighlightStyle getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const override { return payloadGetOutlineStyle(_data, viewFrustum, height); } + protected: DataPointer _data; @@ -663,6 +686,7 @@ public: virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const = 0; virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const = 0; virtual ItemID computeMirrorView(ViewFrustum& viewFrustum) const = 0; + virtual HighlightStyle getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) 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, @@ -677,6 +701,7 @@ template <> uint32_t metaFetchMetaSubItems(const PayloadProxyInterface::Pointer& template <> const ShapeKey shapeGetShapeKey(const PayloadProxyInterface::Pointer& payload); template <> bool payloadPassesZoneOcclusionTest(const PayloadProxyInterface::Pointer& payload, const std::unordered_set& containingZones); template <> ItemID payloadComputeMirrorView(const PayloadProxyInterface::Pointer& payload, ViewFrustum& viewFrustum); +template <> HighlightStyle payloadGetOutlineStyle(const PayloadProxyInterface::Pointer& payload, const ViewFrustum& viewFrustum, const size_t height); 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 4619c30de4..e8954f4161 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -4,6 +4,7 @@ // // Created by Zach Pomerantz on 12/22/2016. // Copyright 2016 High Fidelity, Inc. +// Copyright 2024 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 @@ -35,7 +36,7 @@ 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 = 6; + const int NUM_SPATIAL_FILTERS = 7; const int NUM_NON_SPATIAL_FILTERS = 4; const int OPAQUE_SHAPE_BUCKET = 0; const int TRANSPARENT_SHAPE_BUCKET = 1; @@ -43,6 +44,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin const int LIGHT_BUCKET = 3; const int META_BUCKET = 4; const int MIRROR_BUCKET = 5; + const int OUTLINE_BUCKET = 6; const int BACKGROUND_BUCKET = 3; MultiFilterItems::ItemFilterArray spatialFilters = { { ItemFilter::Builder::opaqueShape().withoutMirror(), @@ -50,7 +52,8 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin ItemFilter::Builder().withSimulate(), ItemFilter::Builder::light(), ItemFilter::Builder::meta().withoutMirror(), - ItemFilter::Builder::mirror() + ItemFilter::Builder::mirror(), + ItemFilter::Builder().withVisible().withOutline() } }; MultiFilterItems::ItemFilterArray nonspatialFilters = { { ItemFilter::Builder::opaqueShape(), @@ -86,7 +89,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin task.addJob("ClearContainingZones"); - output = Output(BucketList{ opaques, transparents, lights, metas, mirrors, simulate, + output = Output(BucketList{ opaques, transparents, lights, metas, mirrors, simulate, filteredSpatialBuckets[OUTLINE_BUCKET], 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 ffbd815167..5802245b90 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -4,6 +4,7 @@ // // Created by Zach Pomerantz on 12/22/2016. // Copyright 2016 High Fidelity, Inc. +// Copyright 2024 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 @@ -25,6 +26,7 @@ public: META, MIRROR, SIMULATE, + OUTLINE, LAYER_FRONT_OPAQUE_SHAPE, LAYER_FRONT_TRANSPARENT_SHAPE, LAYER_HUD_OPAQUE_SHAPE, diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 5500183196..f09a646ce6 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 1/11/15. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 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 @@ -608,6 +609,22 @@ void Scene::resetSelections(const Transaction::SelectionResets& transactions) { } } +void Scene::addItemToSelection(const std::string& selectionName, ItemID itemID) { + std::unique_lock lock(_selectionsMutex); + auto found = _selections.find(selectionName); + if (found == _selections.end()) { + Selection selection = Selection(selectionName, { itemID }); + _selections[selectionName] = selection; + } else { + _selections[selectionName].add(itemID); + } +} + +void Scene::removeSelection(const std::string& selectionName) { + std::unique_lock lock(_selectionsMutex); + _selections.erase(selectionName); +} + // Access a particular Stage (empty if doesn't exist) // Thread safe StagePointer Scene::getStage(const Stage::Name& name) const { diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 4dbcaa4e8f..fb88fbfdce 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 1/11/15. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 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 @@ -166,6 +167,14 @@ public: // Thread safe bool isSelectionEmpty(const Selection::Name& name) const; + // Add a single item to a selection by name + // Thread safe + void addItemToSelection(const std::string& selectionName, ItemID itemID); + + // Remove a selection by name + // Thread safe + void removeSelection(const std::string& selectionName); + // This next call are NOT threadsafe, you have to call them from the correct thread to avoid any potential issues // Access a particular item from its ID @@ -196,6 +205,8 @@ public: void simulate(ItemID id, RenderArgs* args) { _items[id].renderSimulate(args); } + HighlightStyle getOutlineStyle(ItemID id, const ViewFrustum& viewFrustum, uint16_t height) { return _items[id].getOutlineStyle(viewFrustum, height); } + protected: // Thread safe elements that can be accessed from anywhere diff --git a/libraries/render/src/render/Selection.h b/libraries/render/src/render/Selection.h index 05b2395b42..0e3ef0eb77 100644 --- a/libraries/render/src/render/Selection.h +++ b/libraries/render/src/render/Selection.h @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 4/4/2017. // Copyright 2017 High Fidelity, Inc. +// Copyright 2024 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 @@ -37,7 +38,8 @@ namespace render { // Test if the ID is in the selection, return the index or -1 if not present static const int NOT_FOUND{ -1 }; - + + void add(ItemID id) { _items.push_back(id); } int find(ItemID id) const; bool contains(ItemID id) const { return find(id) > NOT_FOUND; } diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp index 048e08e959..4d1682de9a 100644 --- a/libraries/render/src/render/ShapePipeline.cpp +++ b/libraries/render/src/render/ShapePipeline.cpp @@ -60,8 +60,8 @@ ShapeKey::Filter::Builder::Builder() { } void ShapePlumber::addPipelineHelper(const Filter& filter, ShapeKey key, int bit, const PipelinePointer& pipeline) const { - // Iterate over all keys - if (bit < (int)ShapeKey::FlagBit::NUM_FLAGS) { + // Iterate over all non-custom keys + if (bit < (int)ShapeKey::FlagBit::NUM_NON_CUSTOM - 1) { addPipelineHelper(filter, key, bit + 1, pipeline); if (!filter._mask[bit]) { // Toggle bits set as insignificant in filter._mask diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index 04b9919140..fd8b729ffa 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -4,6 +4,7 @@ // // Created by Zach Pomerantz on 12/31/15. // Copyright 2015 High Fidelity, Inc. +// Copyright 2024 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 @@ -38,6 +39,7 @@ public: FADE, CULL_FACE_NONE, // if neither of these are set, we're CULL_FACE_BACK CULL_FACE_FRONT, + MTOON, OWN_PIPELINE, INVALID, @@ -52,6 +54,7 @@ public: CUSTOM_7, NUM_FLAGS, // Not a valid flag + NUM_NON_CUSTOM = INVALID, CUSTOM_MASK = (0xFF << CUSTOM_0), @@ -84,6 +87,7 @@ public: Builder& withDepthBias() { _flags.set(DEPTH_BIAS); return (*this); } Builder& withWireframe() { _flags.set(WIREFRAME); return (*this); } Builder& withFade() { _flags.set(FADE); return (*this); } + Builder& withMToon() { _flags.set(MTOON); return (*this); } Builder& withoutCullFace() { return withCullFaceMode(graphics::MaterialKey::CullFaceMode::CULL_NONE); } Builder& withCullFaceMode(graphics::MaterialKey::CullFaceMode cullFaceMode) { @@ -109,7 +113,7 @@ public: Builder& withOwnPipeline() { _flags.set(OWN_PIPELINE); return (*this); } Builder& invalidate() { _flags.set(INVALID); return (*this); } - Builder& withCustom(uint8_t custom) { _flags &= (~CUSTOM_MASK); _flags |= (custom << CUSTOM_0); return (*this); } + Builder& withCustom(uint8_t custom) { _flags &= (~CUSTOM_MASK); _flags |= (custom << CUSTOM_0); return (*this); } static const ShapeKey ownPipeline() { return Builder().withOwnPipeline(); } static const ShapeKey invalid() { return Builder().invalidate(); } @@ -184,6 +188,9 @@ public: Builder& withFade() { _flags.set(FADE); _mask.set(FADE); return (*this); } Builder& withoutFade() { _flags.reset(FADE); _mask.set(FADE); return (*this); } + Builder& withMToon() { _flags.set(MTOON); _mask.set(MTOON); return (*this); } + Builder& withoutMToon() { _flags.reset(MTOON); _mask.set(MTOON); return (*this); } + Builder& withCustom(uint8_t custom) { _flags &= (~CUSTOM_MASK); _flags |= (custom << CUSTOM_0); _mask |= (CUSTOM_MASK); return (*this); } Builder& withoutCustom() { _flags &= (~CUSTOM_MASK); _mask |= (CUSTOM_MASK); return (*this); } @@ -210,7 +217,10 @@ public: bool isDepthBiased() const { return _flags[DEPTH_BIAS]; } bool isWireframe() const { return _flags[WIREFRAME]; } bool isCullFace() const { return !_flags[CULL_FACE_NONE] && !_flags[CULL_FACE_FRONT]; } + bool isCullFaceNone() const { return _flags[CULL_FACE_NONE] && !_flags[CULL_FACE_FRONT]; } + bool isCullFaceFront() const { return !_flags[CULL_FACE_NONE] && _flags[CULL_FACE_FRONT]; } bool isFaded() const { return _flags[FADE]; } + bool isMToon() const { return _flags[MTOON]; } bool hasOwnPipeline() const { return _flags[OWN_PIPELINE]; } bool isValid() const { return !_flags[INVALID]; } @@ -250,6 +260,7 @@ inline QDebug operator<<(QDebug debug, const ShapeKey& key) { << "isWireframe:" << key.isWireframe() << "isCullFace:" << key.isCullFace() << "isFaded:" << key.isFaded() + << "isMToon:" << key.isMToon() << "]"; } } else { diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index dd28cf74ee..fe11582abf 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -74,7 +74,7 @@ void AssetScriptingInterface::uploadData(QString data, const ScriptValue& callba auto upload = DependencyManager::get()->createUpload(dataByteArray); Promise deferred = makePromise(__FUNCTION__); - Q_ASSERT(engine); + Q_ASSERT(engine()); auto scriptEngine = engine(); deferred->ready([=](QString error, QVariantMap result) { auto url = result.value("url").toString(); @@ -98,7 +98,7 @@ void AssetScriptingInterface::setMapping(QString path, QString hash, const Scrip auto handler = jsBindCallback(thisObject(), callback); auto setMappingRequest = assetClient()->createSetMappingRequest(path, hash); Promise deferred = makePromise(__FUNCTION__); - Q_ASSERT(engine); + Q_ASSERT(engine()); auto scriptEngine = engine(); deferred->ready([=](QString error, QVariantMap result) { jsCallback(handler, scriptEngine->newValue(error), result); @@ -136,7 +136,7 @@ void AssetScriptingInterface::downloadData(QString urlString, const ScriptValue& auto assetRequest = assetClient->createRequest(hash); Promise deferred = makePromise(__FUNCTION__); - Q_ASSERT(engine); + Q_ASSERT(engine()); auto scriptEngine = engine(); deferred->ready([=](QString error, QVariantMap result) { // FIXME: to remain backwards-compatible the signature here is "callback(data, n/a)" @@ -200,7 +200,7 @@ void AssetScriptingInterface::getMapping(QString asset, const ScriptValue& callb JS_VERIFY(AssetUtils::isValidFilePath(path), "invalid ATP file path: " + asset + "(path:"+path+")"); JS_VERIFY(callback.isFunction(), "expected second parameter to be a callback function"); Promise promise = getAssetInfo(path); - Q_ASSERT(engine); + Q_ASSERT(engine()); auto scriptEngine = engine(); promise->ready([=](QString error, QVariantMap result) { jsCallback(handler, scriptEngine->newValue(error), scriptEngine->newValue(result.value("hash").toString())); @@ -234,7 +234,7 @@ Promise AssetScriptingInterface::jsPromiseReady(Promise promise, const ScriptVal if (!jsVerify(handler.isValid(), "jsPromiseReady -- invalid callback handler")) { return nullptr; } - Q_ASSERT(engine); + Q_ASSERT(engine()); auto scriptEngine = engine(); return promise->ready([this, handler, scriptEngine](QString error, QVariantMap result) { jsCallback(handler, scriptEngine->newValue(error), result); @@ -244,7 +244,6 @@ Promise AssetScriptingInterface::jsPromiseReady(Promise promise, const ScriptVal void AssetScriptingInterface::jsCallback(const ScriptValue& handler, const ScriptValue& error, const ScriptValue& result) { Q_ASSERT(thread() == QThread::currentThread()); - Q_ASSERT(engine); //V8TODO: which kind of script context guard needs to be used here? ScriptContextGuard scriptContextGuard(_scriptManager->engine()->currentContext()); auto errorValue = !error.toBool() ? engine()->nullValue() : error; @@ -546,7 +545,7 @@ void AssetScriptingInterface::loadFromCache(const ScriptValue& options, const Sc } bool AssetScriptingInterface::canWriteCacheValue(const QUrl& url) { - Q_ASSERT(engine); + Q_ASSERT(engine()); auto scriptManager = engine()->manager(); if (!scriptManager) { return false; diff --git a/libraries/script-engine/src/ConsoleScriptingInterface.cpp b/libraries/script-engine/src/ConsoleScriptingInterface.cpp index 2d9d5c7ad6..ac55ec423e 100644 --- a/libraries/script-engine/src/ConsoleScriptingInterface.cpp +++ b/libraries/script-engine/src/ConsoleScriptingInterface.cpp @@ -34,7 +34,7 @@ QList ConsoleScriptingInterface::_groupDetails = QList(); ScriptValue ConsoleScriptingInterface::info(ScriptContext* context, ScriptEngine* engine) { if (ScriptManager* scriptManager = engine->manager()) { - scriptManager->scriptInfoMessage(appendArguments(context)); + scriptManager->scriptInfoMessage(appendArguments(context), context->currentFileName(), context->currentLineNumber()); } return engine->nullValue(); } @@ -43,7 +43,7 @@ ScriptValue ConsoleScriptingInterface::log(ScriptContext* context, ScriptEngine* QString message = appendArguments(context); if (_groupDetails.count() == 0) { if (ScriptManager* scriptManager = engine->manager()) { - scriptManager->scriptPrintedMessage(message); + scriptManager->scriptPrintedMessage(message, context->currentFileName(), context->currentLineNumber()); } } else { logGroupMessage(message, engine); @@ -53,28 +53,28 @@ ScriptValue ConsoleScriptingInterface::log(ScriptContext* context, ScriptEngine* ScriptValue ConsoleScriptingInterface::debug(ScriptContext* context, ScriptEngine* engine) { if (ScriptManager* scriptManager = engine->manager()) { - scriptManager->scriptPrintedMessage(appendArguments(context)); + scriptManager->scriptPrintedMessage(appendArguments(context), context->currentFileName(), context->currentLineNumber()); } return engine->nullValue(); } ScriptValue ConsoleScriptingInterface::warn(ScriptContext* context, ScriptEngine* engine) { if (ScriptManager* scriptManager = engine->manager()) { - scriptManager->scriptWarningMessage(appendArguments(context)); + scriptManager->scriptWarningMessage(appendArguments(context), context->currentFileName(), context->currentLineNumber()); } return engine->nullValue(); } ScriptValue ConsoleScriptingInterface::error(ScriptContext* context, ScriptEngine* engine) { if (ScriptManager* scriptManager = engine->manager()) { - scriptManager->scriptErrorMessage(appendArguments(context)); + scriptManager->scriptErrorMessage(appendArguments(context), context->currentFileName(), context->currentLineNumber()); } return engine->nullValue(); } ScriptValue ConsoleScriptingInterface::exception(ScriptContext* context, ScriptEngine* engine) { if (ScriptManager* scriptManager = engine->manager()) { - scriptManager->scriptErrorMessage(appendArguments(context)); + scriptManager->scriptErrorMessage(appendArguments(context), context->currentFileName(), context->currentLineNumber()); } return engine->nullValue(); } @@ -82,23 +82,23 @@ ScriptValue ConsoleScriptingInterface::exception(ScriptContext* context, ScriptE void ConsoleScriptingInterface::time(QString labelName) { _timerDetails.insert(labelName, QDateTime::currentDateTime().toUTC()); QString message = QString("%1: Timer started").arg(labelName); - Q_ASSERT(engine); + Q_ASSERT(engine()); if (ScriptManager* scriptManager = engine()->manager()) { - scriptManager->scriptPrintedMessage(message); + scriptManager->scriptPrintedMessage(message, context()->currentFileName(), context()->currentLineNumber()); } } void ConsoleScriptingInterface::timeEnd(QString labelName) { - Q_ASSERT(engine); + Q_ASSERT(engine()); if (ScriptManager* scriptManager = engine()->manager()) { if (!_timerDetails.contains(labelName)) { - scriptManager->scriptErrorMessage("No such label found " + labelName); + scriptManager->scriptErrorMessage("No such label found " + labelName, context()->currentFileName(), context()->currentLineNumber()); return; } if (_timerDetails.value(labelName).isNull()) { _timerDetails.remove(labelName); - scriptManager->scriptErrorMessage("Invalid start time for " + labelName); + scriptManager->scriptErrorMessage("Invalid start time for " + labelName, context()->currentFileName(), context()->currentLineNumber()); return; } QDateTime _startTime = _timerDetails.value(labelName); @@ -108,7 +108,7 @@ void ConsoleScriptingInterface::timeEnd(QString labelName) { QString message = QString("%1: %2ms").arg(labelName).arg(QString::number(diffInMS)); _timerDetails.remove(labelName); - scriptManager->scriptPrintedMessage(message); + scriptManager->scriptPrintedMessage(message, context()->currentFileName(), context()->currentLineNumber()); } } @@ -131,24 +131,24 @@ ScriptValue ConsoleScriptingInterface::assertion(ScriptContext* context, ScriptE assertionResult = QString("Assertion failed : %1").arg(message); } if (ScriptManager* scriptManager = engine->manager()) { - scriptManager->scriptErrorMessage(assertionResult); + scriptManager->scriptErrorMessage(assertionResult, context->currentFileName(), context->currentLineNumber()); } } return engine->nullValue(); } void ConsoleScriptingInterface::trace() { - Q_ASSERT(engine); + Q_ASSERT(engine()); ScriptEnginePointer scriptEngine = engine(); if (ScriptManager* scriptManager = scriptEngine->manager()) { scriptManager->scriptPrintedMessage (QString(STACK_TRACE_FORMAT).arg(LINE_SEPARATOR, - scriptEngine->currentContext()->backtrace().join(LINE_SEPARATOR))); + scriptEngine->currentContext()->backtrace().join(LINE_SEPARATOR)), context()->currentFileName(), context()->currentLineNumber()); } } void ConsoleScriptingInterface::clear() { - Q_ASSERT(engine); + Q_ASSERT(engine()); if (ScriptManager* scriptManager = engine()->manager()) { scriptManager->clearDebugLogWindow(); } @@ -190,6 +190,6 @@ void ConsoleScriptingInterface::logGroupMessage(QString message, ScriptEngine* e } logMessage.append(message); if (ScriptManager* scriptManager = engine->manager()) { - scriptManager->scriptPrintedMessage(logMessage); + scriptManager->scriptPrintedMessage(logMessage, context()->currentFileName(), context()->currentLineNumber()); } } diff --git a/libraries/script-engine/src/HelperScriptEngine.cpp b/libraries/script-engine/src/HelperScriptEngine.cpp new file mode 100644 index 0000000000..313fac74da --- /dev/null +++ b/libraries/script-engine/src/HelperScriptEngine.cpp @@ -0,0 +1,31 @@ +// +// HelperScriptEngine.h +// libraries/script-engine/src/HelperScriptEngine.h +// +// Created by dr Karol Suprynowicz on 2024/04/28. +// Copyright 2024 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 "HelperScriptEngine.h" + +HelperScriptEngine::HelperScriptEngine() { + std::lock_guard lock(_scriptEngineLock); + _scriptEngine = newScriptEngine(); + _scriptEngineThread.reset(new QThread()); + _scriptEngine->setThread(_scriptEngineThread.get()); + _scriptEngineThread->start(); +} + +HelperScriptEngine::~HelperScriptEngine() { + std::lock_guard lock(_scriptEngineLock); + if (_scriptEngine) { + if (_scriptEngineThread) { + _scriptEngineThread->quit(); + _scriptEngineThread->wait(); + } + _scriptEngine.reset(); + } +} diff --git a/libraries/script-engine/src/HelperScriptEngine.h b/libraries/script-engine/src/HelperScriptEngine.h new file mode 100644 index 0000000000..53d2e89306 --- /dev/null +++ b/libraries/script-engine/src/HelperScriptEngine.h @@ -0,0 +1,65 @@ +// +// HelperScriptEngine.h +// libraries/script-engine/src/HelperScriptEngine.h +// +// Created by dr Karol Suprynowicz on 2024/04/28. +// Copyright 2024 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 overte_HelperScriptEngine_h +#define overte_HelperScriptEngine_h + +#include +#include "QThread" + +#include "ScriptEngine.h" + +/** + * @brief Provides a wrapper around script engine that does not have ScriptManager + * + * HelperScriptEngine is used for performing smaller tasks, like for example conversions between entity + * properties and JSON data. + * For thread safety all accesses to helper script engine need to be done either through HelperScriptEngine::run() + * or HelperScriptEngine::runWithResult(). + * + */ + + +class HelperScriptEngine { +public: + HelperScriptEngine(); + ~HelperScriptEngine(); + + template + inline void run(F&& f) { + std::lock_guard guard(_scriptEngineLock); + f(); + } + + template + inline T runWithResult(F&& f) { + T result; + { + std::lock_guard guard(_scriptEngineLock); + result = f(); + } + return result; + } + + /** + * @brief Returns pointer to the script engine + * + * This function should be used only inside HelperScriptEngine::run() or HelperScriptEngine::runWithResult() + */ + ScriptEngine* get() { return _scriptEngine.get(); }; + ScriptEnginePointer getShared() { return _scriptEngine; }; +private: + std::mutex _scriptEngineLock; + ScriptEnginePointer _scriptEngine { nullptr }; + std::shared_ptr _scriptEngineThread { nullptr }; +}; + +#endif //overte_HelperScriptEngine_h diff --git a/libraries/script-engine/src/Mat4.cpp b/libraries/script-engine/src/Mat4.cpp index fefcf6a0f6..a333d28ef8 100644 --- a/libraries/script-engine/src/Mat4.cpp +++ b/libraries/script-engine/src/Mat4.cpp @@ -90,7 +90,7 @@ void Mat4::print(const QString& label, const glm::mat4& m, bool transpose) const QString message = QString("%1 %2").arg(qPrintable(label)); message = message.arg(glm::to_string(out).c_str()); qCDebug(scriptengine) << message; - Q_ASSERT(engine); + Q_ASSERT(engine()); if (ScriptManager* scriptManager = engine()->manager()) { scriptManager->print(message); } diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 492534f021..3c882d7292 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -126,7 +126,7 @@ void Quat::print(const QString& label, const glm::quat& q, bool asDegrees) { message = message.arg(glm::to_string(glm::dquat(q)).c_str()); } qCDebug(scriptengine) << message; - Q_ASSERT(engine); + Q_ASSERT(engine()); if (ScriptManager* scriptManager = engine()->manager()) { scriptManager->print(message); } diff --git a/libraries/script-engine/src/ScriptContext.h b/libraries/script-engine/src/ScriptContext.h index 7bc70e1080..8eb67c4247 100644 --- a/libraries/script-engine/src/ScriptContext.h +++ b/libraries/script-engine/src/ScriptContext.h @@ -57,6 +57,13 @@ public: virtual int argumentCount() const = 0; virtual ScriptValue argument(int index) const = 0; virtual QStringList backtrace() const = 0; + + // Name of the file in which message was generated. Empty string when no file name is available. + virtual int currentLineNumber() const = 0; + + // Number of the line on which message was generated. -1 if there line number is not available. + virtual QString currentFileName() const = 0; + virtual ScriptValue callee() const = 0; virtual ScriptEnginePointer engine() const = 0; virtual ScriptFunctionContextPointer functionContext() const = 0; diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index b47255ed24..52d3b2753b 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -158,8 +158,69 @@ void ScriptEngines::removeScriptEngine(ScriptManagerPointer manager) { QMutexLocker locker(&_allScriptsMutex); _allKnownScriptManagers.remove(manager); } + std::lock_guard lock(_subscriptionsToEntityScriptMessagesMutex); + _managersSubscribedToEntityScriptMessages.remove(manager.get()); + _entitiesSubscribedToEntityScriptMessages.remove(manager.get()); } +void ScriptEngines::requestServerEntityScriptMessages(ScriptManager *manager) { + std::lock_guard lock(_subscriptionsToEntityScriptMessagesMutex); + if (!_managersSubscribedToEntityScriptMessages.contains(manager)) { + _managersSubscribedToEntityScriptMessages.insert(manager); + // Emit a signal to inform EntityScriptServerLogClient about subscription request + emit requestingEntityScriptServerLog(true); + qDebug() << "ScriptEngines::requestServerEntityScriptMessages"; + } +} + +void ScriptEngines::requestServerEntityScriptMessages(ScriptManager *manager, const QUuid& entityID) { + std::lock_guard lock(_subscriptionsToEntityScriptMessagesMutex); + if (!_entitiesSubscribedToEntityScriptMessages.contains(manager)) { + _entitiesSubscribedToEntityScriptMessages.insert(manager,QSet()); + } + if (!_entitiesSubscribedToEntityScriptMessages[manager].contains(entityID)) { + _entitiesSubscribedToEntityScriptMessages[manager].insert(entityID); + // Emit a signal to inform EntityScriptServerLogClient about subscription request + emit requestingEntityScriptServerLog(true); + qDebug() << "ScriptEngines::requestServerEntityScriptMessages uuid"; + } +} + +void ScriptEngines::removeServerEntityScriptMessagesRequest(ScriptManager *manager) { + std::lock_guard lock(_subscriptionsToEntityScriptMessagesMutex); + if (_managersSubscribedToEntityScriptMessages.contains(manager)) { + _managersSubscribedToEntityScriptMessages.remove(manager); + } + if (_entitiesSubscribedToEntityScriptMessages.isEmpty() + && _managersSubscribedToEntityScriptMessages.isEmpty()) { + // No managers requiring entity script server messages remain, so we inform EntityScriptServerLogClient about this + // Emit a signal to inform EntityScriptServerLogClient about subscription request + emit requestingEntityScriptServerLog(false); + qDebug() << "ScriptEngines::removeServerEntityScriptMessagesRequest"; + } +} + +void ScriptEngines::removeServerEntityScriptMessagesRequest(ScriptManager *manager, const QUuid& entityID) { + std::lock_guard lock(_subscriptionsToEntityScriptMessagesMutex); + if (!_entitiesSubscribedToEntityScriptMessages.contains(manager)) { + return; + } + if (_entitiesSubscribedToEntityScriptMessages[manager].contains(entityID)) { + _entitiesSubscribedToEntityScriptMessages[manager].remove(entityID); + } + if (_entitiesSubscribedToEntityScriptMessages[manager].isEmpty()) { + _entitiesSubscribedToEntityScriptMessages.remove(manager); + } + if (_entitiesSubscribedToEntityScriptMessages.isEmpty() + && _managersSubscribedToEntityScriptMessages.isEmpty()) { + // No managers requiring entity script server messages remain, so we inform EntityScriptServerLogClient about this + // Emit a signal to inform EntityScriptServerLogClient about subscription request + emit requestingEntityScriptServerLog(false); + qDebug() << "ScriptEngines::removeServerEntityScriptMessagesRequest uuid"; + } +} + + void ScriptEngines::shutdownScripting() { _isStopped = true; QMutexLocker locker(&_allScriptsMutex); diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 671789bd8e..bafaa1322c 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -186,6 +186,13 @@ public: void removeScriptEngine(ScriptManagerPointer); + // Called by ScriptManagerScriptingInterface + void requestServerEntityScriptMessages(ScriptManager *manager); + void requestServerEntityScriptMessages(ScriptManager *manager, const QUuid& entityID); + + void removeServerEntityScriptMessagesRequest(ScriptManager *manager); + void removeServerEntityScriptMessagesRequest(ScriptManager *manager, const QUuid& entityID); + ScriptGatekeeper scriptGatekeeper; signals: @@ -251,11 +258,62 @@ signals: * Triggered when any script generates an information message or {@link console.info} is called. * @function ScriptDiscoveryService.infoMessage * @param {string} message - The information message. - * @param {string} scriptName - The name of the script that generated the informaton message. + * @param {string} scriptName - The name of the script that generated the information message. * @returns {Signal} */ void infoMessage(const QString& message, const QString& engineName); + /*@jsdoc + * Triggered when a client side entity script prints a message to the program log via {@link print}, {@link Script.print}, + * {@link console.log}, {@link console.debug}, {@link console.group}, {@link console.groupEnd}, {@link console.time}, or + * {@link console.timeEnd}. + * @function Script.printedMessage + * @param {string} message - The message. + * @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available. + * @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available. + * @param {Uuid} entityID - Entity ID. + * @param {boolean} isServerScript - true if entity script is server-side, false if it is client-side. + * @returns {Signal} + */ + void printedEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript); + + /*@jsdoc + * Triggered when a client side entity script generates an error, {@link console.error} or {@link console.exception} is called, or + * {@link console.assert} is called and fails. + * @function Script.errorMessage + * @param {string} message - The error message. + * @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available. + * @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available. + * @param {Uuid} entityID - Entity ID. + * @param {boolean} isServerScript - true if entity script is server-side, false if it is client-side. + * @returns {Signal} + */ + void errorEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript); + + /*@jsdoc + * Triggered when a client side entity script generates a warning or {@link console.warn} is called. + * @function Script.warningMessage + * @param {string} message - The warning message. + * @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available. + * @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available. + * @param {Uuid} entityID - Entity ID. + * @param {boolean} isServerScript - true if entity script is server-side, false if it is client-side. + * @returns {Signal} + */ + void warningEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript); + + /*@jsdoc + * Triggered when a client side entity script generates an information message or {@link console.info} is called. + * @function Script.infoMessage + * @param {string} message - The information message. + * @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available. + * @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available. + * @param {Uuid} entityID - Entity ID. + * @param {boolean} isServerScript - true if entity script is server-side, false if it is client-side. + * @returns {Signal} + */ + void infoEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript); + /*@jsdoc * @function ScriptDiscoveryService.errorLoadingScript * @param {string} url - URL. @@ -272,6 +330,12 @@ signals: */ void clearDebugWindow(); + /** + * @brief Fires when script engines need entity server script messages (areMessagesRequested == true) + * and when messages are not needed anymore (areMessagesRequested == false). + */ + void requestingEntityScriptServerLog(bool areMessagesRequested); + public slots: /*@jsdoc @@ -355,6 +419,12 @@ protected: bool _defaultScriptsLocationOverridden { false }; QString _debugScriptUrl; + // For subscriptions to server entity script messages + std::mutex _subscriptionsToEntityScriptMessagesMutex; + QSet _managersSubscribedToEntityScriptMessages; + // Since multiple entity scripts run in the same script engine, there's a need to track subscriptions per entity + QHash> _entitiesSubscribedToEntityScriptMessages; + // If this is set, defaultScripts.js will not be run if it is in the settings, // and this will be run instead. This script will not be persisted to settings. const QUrl _defaultScriptsOverride { }; diff --git a/libraries/script-engine/src/ScriptManager.cpp b/libraries/script-engine/src/ScriptManager.cpp index f74bb01b71..f527fbb9e2 100644 --- a/libraries/script-engine/src/ScriptManager.cpp +++ b/libraries/script-engine/src/ScriptManager.cpp @@ -225,7 +225,12 @@ QString encodeEntityIdIntoEntityUrl(const QString& url, const QString& entityID) QString ScriptManager::logException(const ScriptValue& exception) { auto message = formatException(exception, _enableExtendedJSExceptions.get()); - scriptErrorMessage(message); + auto context = _engine->currentContext(); + if (context) { + scriptErrorMessage(message, context->currentFileName(), context->currentLineNumber()); + } else { + scriptErrorMessage(message, "", -1); + } return message; } @@ -330,6 +335,11 @@ ScriptManager::ScriptManager(Context context, const QString& scriptContents, con }); } + //Gather entity script messages for transmission when running server side. + if (_type == Type::ENTITY_SERVER) { + ; + } + if (!_areMetaTypesInitialized) { initMetaTypes(); } @@ -468,10 +478,10 @@ void ScriptManager::waitTillDoneRunning(bool shutdown) { } } #else - auto startedWaiting = usecTimestampNow(); + //auto startedWaiting = usecTimestampNow(); while (!_isDoneRunning) { // If the final evaluation takes too long, then tell the script engine to stop running - auto elapsedUsecs = usecTimestampNow() - startedWaiting; + //auto elapsedUsecs = usecTimestampNow() - startedWaiting; // TODO: This part was very unsafe and was causing crashes all the time. // I disabled it for now until we find a safer solution. // With it disabled now we get clean shutdowns and restarts. @@ -514,7 +524,7 @@ void ScriptManager::waitTillDoneRunning(bool shutdown) { } #endif - scriptInfoMessage("Script Engine has stopped:" + getFilename()); + scriptInfoMessage("Script Engine has stopped:" + getFilename(), "", -1); } } @@ -532,6 +542,10 @@ QString ScriptManager::getFilename() const { return lastPart; } +QString ScriptManager::getAbsoluteFilename() const { + return _fileNameString; +} + bool ScriptManager::hasValidScriptSuffix(const QString& scriptFileName) { QFileInfo fileInfo(scriptFileName); QString scriptSuffixToLower = fileInfo.completeSuffix().toLower(); @@ -549,7 +563,7 @@ void ScriptManager::loadURL(const QUrl& scriptURL, bool reload) { // Check that script has a supported file extension if (!hasValidScriptSuffix(_fileNameString)) { - scriptErrorMessage("File extension of file: " + _fileNameString + " is not a currently supported script type"); + scriptErrorMessage("File extension of file: " + _fileNameString + " is not a currently supported script type", _fileNameString, -1); emit errorLoadingScript(_fileNameString); return; } @@ -559,7 +573,7 @@ void ScriptManager::loadURL(const QUrl& scriptURL, bool reload) { scriptCache->getScriptContents(url.toString(), [this](const QString& url, const QString& scriptContents, bool isURL, bool success, const QString&status) { qCDebug(scriptengine) << "loadURL" << url << status << QThread::currentThread(); if (!success) { - scriptErrorMessage("ERROR Loading file (" + status + "):" + url); + scriptErrorMessage("ERROR Loading file (" + status + "):" + url, url, -1); emit errorLoadingScript(_fileNameString); return; } @@ -570,24 +584,54 @@ void ScriptManager::loadURL(const QUrl& scriptURL, bool reload) { }, reload, maxRetries); } -void ScriptManager::scriptErrorMessage(const QString& message) { +void ScriptManager::scriptErrorMessage(const QString& message, const QString& fileName, int lineNumber) { qCCritical(scriptengine, "[%s] %s", qUtf8Printable(getFilename()), qUtf8Printable(message)); emit errorMessage(message, getFilename()); + if (!currentEntityIdentifier.isInvalidID()) { + emit errorEntityMessage(message, fileName, lineNumber, currentEntityIdentifier, isEntityServerScript()); + } else { + if (isEntityServerScript()) { + emit errorEntityMessage(message, fileName, lineNumber, EntityItemID(), isEntityServerScript()); + } + } } -void ScriptManager::scriptWarningMessage(const QString& message) { +void ScriptManager::scriptWarningMessage(const QString& message, const QString& fileName, int lineNumber) { qCWarning(scriptengine, "[%s] %s", qUtf8Printable(getFilename()), qUtf8Printable(message)); emit warningMessage(message, getFilename()); + if (!currentEntityIdentifier.isInvalidID()) { + emit warningEntityMessage(message, fileName, lineNumber, currentEntityIdentifier, isEntityServerScript()); + } else { + if (isEntityServerScript()) { + emit warningEntityMessage(message, fileName, lineNumber, EntityItemID(), isEntityServerScript()); + } + } } -void ScriptManager::scriptInfoMessage(const QString& message) { +void ScriptManager::scriptInfoMessage(const QString& message, const QString& fileName, int lineNumber) { qCInfo(scriptengine, "[%s] %s", qUtf8Printable(getFilename()), qUtf8Printable(message)); emit infoMessage(message, getFilename()); + if (!currentEntityIdentifier.isInvalidID()) { + emit infoEntityMessage(message, fileName, lineNumber, currentEntityIdentifier, isEntityServerScript()); + } else { + if (isEntityServerScript()) { + emit infoEntityMessage(message, fileName, lineNumber, EntityItemID(), isEntityServerScript()); + } + } } -void ScriptManager::scriptPrintedMessage(const QString& message) { +void ScriptManager::scriptPrintedMessage(const QString& message, const QString& fileName, int lineNumber) { qCDebug(scriptengine, "[%s] %s", qUtf8Printable(getFilename()), qUtf8Printable(message)); emit printedMessage(message, getFilename()); + if (!currentEntityIdentifier.isInvalidID()) { + emit printedEntityMessage(message, fileName, lineNumber, currentEntityIdentifier, isEntityServerScript()); + } else { + if (isEntityServerScript()) { + // TODO: Some callbacks like for example websockets one right now + // don't set currentEntityIdentifier and there doesn't seem to be easy way to add it currently + emit printedEntityMessage(message, fileName, lineNumber, EntityItemID(), isEntityServerScript()); + } + } } void ScriptManager::clearDebugLogWindow() { @@ -912,7 +956,7 @@ void ScriptManager::run() { return; // bail early - avoid setting state in init(), as evaluate() will bail too } - scriptInfoMessage("Script Engine starting:" + getFilename()); + scriptInfoMessage("Script Engine starting:" + getFilename(), getFilename(), -1); if (!_isInitialized) { init(); @@ -1064,7 +1108,7 @@ void ScriptManager::run() { _engine->clearExceptions(); } } - scriptInfoMessage("Script Engine stopping:" + getFilename()); + scriptInfoMessage("Script Engine stopping:" + getFilename(), getFilename(), -1); stopAllTimers(); // make sure all our timers are stopped if the script is ending emit scriptEnding(); @@ -1139,7 +1183,7 @@ void ScriptManager::updateMemoryCost(const qint64& deltaSize) { void ScriptManager::timerFired() { if (isStopped()) { - scriptWarningMessage("Script.timerFired() while shutting down is ignored... parent script:" + getFilename()); + scriptWarningMessage("Script.timerFired() while shutting down is ignored... parent script:" + getFilename(), getFilename(), -1); return; // bail early } @@ -1206,7 +1250,14 @@ QTimer* ScriptManager::setupTimerWithInterval(const ScriptValue& function, int i QTimer* ScriptManager::setInterval(const ScriptValue& function, int intervalMS) { if (isStopped()) { - scriptWarningMessage("Script.setInterval() while shutting down is ignored... parent script:" + getFilename()); + int lineNumber = -1; + QString fileName = getFilename(); + auto context = _engine->currentContext(); + if (context) { + lineNumber = context->currentLineNumber(); + fileName = context->currentFileName(); + } + scriptWarningMessage("Script.setInterval() while shutting down is ignored... parent script:" + getFilename(), fileName, lineNumber); return NULL; // bail early } @@ -1215,7 +1266,14 @@ QTimer* ScriptManager::setInterval(const ScriptValue& function, int intervalMS) QTimer* ScriptManager::setTimeout(const ScriptValue& function, int timeoutMS) { if (isStopped()) { - scriptWarningMessage("Script.setTimeout() while shutting down is ignored... parent script:" + getFilename()); + int lineNumber = -1; + QString fileName = getFilename(); + auto context = _engine->currentContext(); + if (context) { + lineNumber = context->currentLineNumber(); + fileName = context->currentFileName(); + } + scriptWarningMessage("Script.setTimeout() while shutting down is ignored... parent script:" + getFilename(), fileName, lineNumber); return NULL; // bail early } @@ -1281,7 +1339,7 @@ QUrl ScriptManager::resourcesPath() const { } void ScriptManager::print(const QString& message) { - emit printedMessage(message, getFilename()); + emit scriptPrintedMessage(message, getFilename(), engine()->currentContext()->currentLineNumber()); } @@ -1651,8 +1709,15 @@ void ScriptManager::include(const QStringList& includeFiles, const ScriptValue& return; } if (isStopped()) { + int lineNumber = -1; + QString fileName = getFilename(); + auto context = _engine->currentContext(); + if (context) { + lineNumber = context->currentLineNumber(); + fileName = context->currentFileName(); + } scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:" - + includeFiles.join(",") + "parent script:" + getFilename()); + + includeFiles.join(",") + "parent script:" + getFilename(), fileName, lineNumber); return; // bail early } QList urls; @@ -1665,8 +1730,15 @@ void ScriptManager::include(const QStringList& includeFiles, const ScriptValue& thisURL = expandScriptUrl(QUrl::fromLocalFile(expandScriptPath(file))); QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); if (!defaultScriptsLoc.isParentOf(thisURL)) { + int lineNumber = -1; + QString fileName = getFilename(); + auto context = _engine->currentContext(); + if (context) { + lineNumber = context->currentLineNumber(); + fileName = context->currentFileName(); + } //V8TODO this probably needs to be done per context, otherwise file cannot be included again in a module - scriptWarningMessage("Script.include() -- skipping" + file + "-- outside of standard libraries"); + scriptWarningMessage("Script.include() -- skipping" + file + "-- outside of standard libraries", fileName, lineNumber); continue; } isStandardLibrary = true; @@ -1676,8 +1748,15 @@ void ScriptManager::include(const QStringList& includeFiles, const ScriptValue& bool disallowOutsideFiles = thisURL.isLocalFile() && !isStandardLibrary && !currentSandboxURL.isLocalFile(); if (disallowOutsideFiles && !PathUtils::isDescendantOf(thisURL, currentSandboxURL)) { + int lineNumber = -1; + QString fileName = currentSandboxURL.toString(); + auto context = _engine->currentContext(); + if (context) { + lineNumber = context->currentLineNumber(); + fileName = context->currentFileName(); + } scriptWarningMessage("Script.include() ignoring file path" + thisURL.toString() - + "outside of original entity script" + currentSandboxURL.toString()); + + "outside of original entity script" + currentSandboxURL.toString(), fileName, lineNumber); } else { // We could also check here for CORS, but we don't yet. // It turns out that QUrl.resolve will not change hosts and copy authority, so we don't need to check that here. @@ -1699,7 +1778,14 @@ void ScriptManager::include(const QStringList& includeFiles, const ScriptValue& for (QUrl url : urls) { QString contents = data[url]; if (contents.isNull()) { - scriptErrorMessage("Error loading file (" + status[url] +"): " + url.toString()); + int lineNumber = -1; + QString fileName = url.toString(); + auto context = _engine->currentContext(); + if (context) { + lineNumber = context->currentLineNumber(); + fileName = context->currentFileName(); + } + scriptErrorMessage("Error loading file (" + status[url] +"): " + url.toString(), fileName, lineNumber); } else { std::lock_guard lock(_lock); if (!_includedURLs.contains(url)) { @@ -1719,7 +1805,14 @@ void ScriptManager::include(const QStringList& includeFiles, const ScriptValue& _engine->clearExceptions(); } } else { - scriptPrintedMessage("Script.include() skipping evaluation of previously included url:" + url.toString()); + int lineNumber = -1; + QString fileName = url.toString(); + auto context = _engine->currentContext(); + if (context) { + lineNumber = context->currentLineNumber(); + fileName = context->currentFileName(); + } + scriptPrintedMessage("Script.include() skipping evaluation of previously included url:" + url.toString(), fileName, lineNumber); } } } @@ -1748,8 +1841,15 @@ void ScriptManager::include(const QStringList& includeFiles, const ScriptValue& void ScriptManager::include(const QString& includeFile, const ScriptValue& callback) { if (isStopped()) { + int lineNumber = -1; + QString fileName = currentSandboxURL.toString(); + auto context = _engine->currentContext(); + if (context) { + lineNumber = context->currentLineNumber(); + fileName = context->currentFileName(); + } scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:" - + includeFile + "parent script:" + getFilename()); + + includeFile + "parent script:" + getFilename(), fileName, lineNumber); return; // bail early } @@ -1765,14 +1865,21 @@ void ScriptManager::load(const QString& loadFile) { if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) { return; } + int lineNumber = -1; + QString fileName = getFilename(); + auto context = _engine->currentContext(); + if (context) { + lineNumber = context->currentLineNumber(); + fileName = context->currentFileName(); + } if (isStopped()) { scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:" - + loadFile + "parent script:" + getFilename()); + + loadFile + "parent script:" + getFilename(), fileName, lineNumber); return; // bail early } if (!currentEntityIdentifier.isInvalidID()) { scriptWarningMessage("Script.load() from entity script is ignored... loadFile:" - + loadFile + "parent script:" + getFilename() + "entity: " + currentEntityIdentifier.toString()); + + loadFile + "parent script:" + getFilename() + "entity: " + currentEntityIdentifier.toString(), fileName, lineNumber); return; // bail early } @@ -2440,7 +2547,7 @@ void ScriptManager::refreshFileScript(const EntityItemID& entityID) { QString filePath = QUrl(details.scriptText).toLocalFile(); auto lastModified = QFileInfo(filePath).lastModified().toMSecsSinceEpoch(); if (lastModified > details.lastModified) { - scriptInfoMessage("Reloading modified script " + details.scriptText); + scriptInfoMessage("Reloading modified script " + details.scriptText, filePath, -1); loadEntityScript(entityID, details.scriptText, true); } } diff --git a/libraries/script-engine/src/ScriptManager.h b/libraries/script-engine/src/ScriptManager.h index 01d0a1dbf0..8197f26285 100644 --- a/libraries/script-engine/src/ScriptManager.h +++ b/libraries/script-engine/src/ScriptManager.h @@ -430,6 +430,13 @@ public: */ QString getFilename() const; + /** + * @brief Get the filename of the running script, with absolute path. + * + * @return QString Filename + */ + QString getAbsoluteFilename() const; + /** * @brief Underlying scripting engine * @@ -1074,8 +1081,10 @@ public: * Emits errorMessage() * * @param message Message to send to the log + * @param fileName Name of the file in which message was generated. Empty string when no file name is available. + * @param lineNumber Number of the line on which message was generated. -1 if there line number is not available. */ - void scriptErrorMessage(const QString& message); + void scriptErrorMessage(const QString& message, const QString& fileName, int lineNumber); /** * @brief Logs a script warning message and emits an warningMessage event @@ -1083,8 +1092,10 @@ public: * Emits warningMessage() * * @param message Message to send to the log + * @param fileName Name of the file in which message was generated. Empty string when no file name is available. + * @param lineNumber Number of the line on which message was generated. -1 if there line number is not available. */ - void scriptWarningMessage(const QString& message); + void scriptWarningMessage(const QString& message, const QString& fileName, int lineNumber); /** * @brief Logs a script info message and emits an infoMessage event @@ -1092,8 +1103,10 @@ public: * Emits infoMessage() * * @param message Message to send to the log + * @param fileName Name of the file in which message was generated. Empty string when no file name is available. + * @param lineNumber Number of the line on which message was generated. -1 if there line number is not available. */ - void scriptInfoMessage(const QString& message); + void scriptInfoMessage(const QString& message, const QString& fileName, int lineNumber); /** * @brief Logs a script printed message and emits an printedMessage event @@ -1102,9 +1115,11 @@ public: * Emits printedMessage() * * @param message Message to send to the log + * @param fileName Name of the file in which message was generated. Empty string when no file name is available. + * @param lineNumber Number of the line on which message was generated. -1 if there line number is not available. */ - void scriptPrintedMessage(const QString& message); + void scriptPrintedMessage(const QString& message, const QString& fileName, int lineNumber); /** * @brief Clears the debug log window @@ -1321,6 +1336,54 @@ signals: */ void infoMessage(const QString& message, const QString& scriptName); + /** + * @brief Triggered when a client side entity script prints a message to the program log + * + * @param message + * @param fileName Name of the file in which message was generated. + * @param lineNumber Number of the line on which message was generated. + * @param entityID + * @param isServerScript true if entity script is server-side, false if it is client-side. + */ + void printedEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript); + + + /** + * @brief Triggered when a client side entity script generates an error + * + * @param message + * @param fileName Name of the file in which message was generated. + * @param lineNumber Number of the line on which message was generated. + * @param entityID + * @param isServerScript true if entity script is server-side, false if it is client-side. + */ + void errorEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript); + + + + /** + * @brief Triggered when a client side entity script generates a warning + * + * @param message + * @param fileName Name of the file in which message was generated. + * @param lineNumber Number of the line on which message was generated. + * @param entityID + * @param isServerScript true if entity script is server-side, false if it is client-side. + */ + void warningEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript); + + + /** + * @brief Triggered when a client side entity script generates an information message + * + * @param message + * @param fileName Name of the file in which message was generated. + * @param lineNumber Number of the line on which message was generated. + * @param entityID + * @param isServerScript true if entity script is server-side, false if it is client-side. + */ + void infoEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript); + /** * @brief Triggered when the running state of the script changes, e.g., from running to stopping. diff --git a/libraries/script-engine/src/ScriptManagerScriptingInterface.cpp b/libraries/script-engine/src/ScriptManagerScriptingInterface.cpp index 7c9f264327..36da99b751 100644 --- a/libraries/script-engine/src/ScriptManagerScriptingInterface.cpp +++ b/libraries/script-engine/src/ScriptManagerScriptingInterface.cpp @@ -12,6 +12,7 @@ #include "ScriptManager.h" #include "ScriptManagerScriptingInterface.h" +#include "ScriptEngines.h" #include "ScriptEngine.h" #include @@ -88,3 +89,47 @@ void ScriptManagerScriptingInterface::startProfiling() { void ScriptManagerScriptingInterface::stopProfilingAndSave() { _manager->engine()->stopProfilingAndSave(); } + +void ScriptManagerScriptingInterface::requestServerEntityScriptMessages() { + if (_manager->isEntityServerScript() || _manager->isEntityServerScript()) { + _manager->engine()->raiseException("Uuid needs to be specified when requestServerEntityScriptMessages is invoked from entity script"); + } else { + auto scriptEngines = DependencyManager::get().data(); + scriptEngines->requestServerEntityScriptMessages(_manager); + } +} + +void ScriptManagerScriptingInterface::requestServerEntityScriptMessages(const QUuid& entityID) { + if (_manager->isEntityServerScript() || _manager->isEntityServerScript()) { + auto scriptEngines = DependencyManager::get().data(); + scriptEngines->requestServerEntityScriptMessages(_manager, entityID); + } else { + _manager->engine()->raiseException("Uuid must not be specified when requestServerEntityScriptMessages is invoked from entity script"); + } +} + +void ScriptManagerScriptingInterface::removeServerEntityScriptMessagesRequest() { + if (_manager->isEntityServerScript() || _manager->isEntityServerScript()) { + _manager->engine()->raiseException("Uuid needs to be specified when removeServerEntityScriptMessagesRequest is invoked from entity script"); + } else { + auto scriptEngines = DependencyManager::get().data(); + scriptEngines->removeServerEntityScriptMessagesRequest(_manager); + } +} + +void ScriptManagerScriptingInterface::removeServerEntityScriptMessagesRequest(const QUuid& entityID) { + if (_manager->isEntityServerScript() || _manager->isEntityServerScript()) { + auto scriptEngines = DependencyManager::get().data(); + scriptEngines->removeServerEntityScriptMessagesRequest(_manager, entityID); + } else { + _manager->engine()->raiseException("Uuid must not be specified when removeServerEntityScriptMessagesRequest is invoked from entity script"); + } +} + +QString ScriptManagerScriptingInterface::btoa(const QByteArray &binary) { + return binary.toBase64(); +} + +QByteArray ScriptManagerScriptingInterface::atob(const QString &base64) { + return QByteArray::fromBase64(base64.toUtf8()); +} diff --git a/libraries/script-engine/src/ScriptManagerScriptingInterface.h b/libraries/script-engine/src/ScriptManagerScriptingInterface.h index 119cbadaa6..d319cd30ae 100644 --- a/libraries/script-engine/src/ScriptManagerScriptingInterface.h +++ b/libraries/script-engine/src/ScriptManagerScriptingInterface.h @@ -512,7 +512,7 @@ public: /*@jsdoc * Start collecting object statistics that can later be reported with Script.dumpHeapObjectStatistics(). - * @function Script.dumpHeapObjectStatistics + * @function Script.startCollectingObjectStatistics */ Q_INVOKABLE void startCollectingObjectStatistics(); @@ -557,7 +557,47 @@ public: */ Q_INVOKABLE void stopProfilingAndSave(); -signals: + /*@jsdoc + * After calling this function current script engine will start receiving server-side entity script messages + * through signals such as errorEntityMessage. This function can be invoked both from client-side entity scripts + * and from interface scripts. + * @function Script.subscribeToServerEntityScriptMessages + * @param {Uuid=} entityID - The ID of the entity that requests entity server script messages. Only needs to be specified + * for entity scripts, and must not be specified for other types of scripts. + */ + + Q_INVOKABLE void requestServerEntityScriptMessages(); + Q_INVOKABLE void requestServerEntityScriptMessages(const QUuid& entityID); + + /*@jsdoc + * Calling this function signalizes that current script doesn't require stop receiving server-side entity script messages + * through signals such as errorEntityMessage. This function can be invoked both from client-side entity scripts + * and from interface scripts. + * @function Script.unsubscribeFromServerEntityScriptMessages + * @param {Uuid=} entityID - The ID of the entity that requests entity server script messages. Only needs to be specified + * for entity scripts, and must not be specified for other types of scripts. + */ + + Q_INVOKABLE void removeServerEntityScriptMessagesRequest(); + Q_INVOKABLE void removeServerEntityScriptMessagesRequest(const QUuid& entityID); + + /*@jsdoc + * This decodes Base64 string and returns contents as ArrayBuffer. + * @function Script.atob + * @param {String} base64 - String with Base64-encoded binary data. + * @returns {ArrayBuffer} Decoded binary data. + */ + Q_INVOKABLE QByteArray atob(const QString &base64); + + /*@jsdoc + * This encodes ArrayBuffer and returns Base64-encoded string. + * @function Script.btoa + * @param {ArrayBuffer} binary - Data to be encoded. + * @returns {String} String with Base64-encoded binary data. + */ + Q_INVOKABLE QString btoa(const QByteArray &binary); + + signals: /*@jsdoc * @function Script.scriptLoaded diff --git a/libraries/script-engine/src/ScriptMessage.cpp b/libraries/script-engine/src/ScriptMessage.cpp new file mode 100644 index 0000000000..0b3ea1abc8 --- /dev/null +++ b/libraries/script-engine/src/ScriptMessage.cpp @@ -0,0 +1,48 @@ +// +// ScriptMessage.h +// libraries/script-engine/src/v8/FastScriptValueUtils.cpp +// +// Created by dr Karol Suprynowicz on 2023/09/24. +// Copyright 2023 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 "ScriptMessage.h" + +#include + +QJsonObject ScriptMessage::toJson() { + QJsonObject object; + object["message"] = _messageContent; + object["lineNumber"] = _lineNumber; + object["fileName"] = _fileName; + object["entityID"] = _entityID.toString(); + object["type"] = static_cast(_scriptType); + object["severity"] = static_cast(_severity); + return object; +} + +bool ScriptMessage::fromJson(const QJsonObject &object) { + if (object.isEmpty()) { + qDebug() << "ScriptMessage::fromJson object is empty"; + return false; + } + if (!object["message"].isString() + || !object["lineNumber"].isDouble() + || !object["fileName"].isString() + || !object["entityID"].isString() + || !object["type"].isDouble() + || !object["severity"].isDouble()) { + qDebug() << "ScriptMessage::fromJson failed to find required fields in JSON file"; + return false; + } + _messageContent = object["message"].toString(); + _lineNumber = object["lineNumber"].toInt(); + _fileName = object["fileName"].toInt(); + _entityID = QUuid::fromString(object["entityID"].toString()); + _scriptType = static_cast(object["type"].toInt()); + _severity = static_cast(object["severity"].toInt()); + return true; +} \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptMessage.h b/libraries/script-engine/src/ScriptMessage.h new file mode 100644 index 0000000000..fb9ae25f40 --- /dev/null +++ b/libraries/script-engine/src/ScriptMessage.h @@ -0,0 +1,64 @@ +// +// ScriptMessage.h +// libraries/script-engine/src/v8/FastScriptValueUtils.cpp +// +// Created by dr Karol Suprynowicz on 2023/09/24. +// Copyright 2023 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 OVERTE_SCRIPTMESSAGE_H +#define OVERTE_SCRIPTMESSAGE_H + +// Used to store script messages on entity script server before transmitting them to clients who subscribed to them. +// EntityServerScriptLog packet type is used. +// In the future will also be used for storing assignment client script messages before transmission + +#include +#include +#include "EntityItemID.h" + +// SEVERITY_ERROR is defined as a macro in winerror.h +#undef SEVERITY_ERROR + +class ScriptMessage { +public: + enum class ScriptType { + TYPE_NONE, + TYPE_ENTITY_SCRIPT + }; + enum class Severity { + SEVERITY_NONE, + SEVERITY_PRINT, + SEVERITY_INFO, + SEVERITY_DEBUG, + SEVERITY_WARNING, + SEVERITY_ERROR + }; + + ScriptMessage() {}; + ScriptMessage(const QString &messageContent, const QString &fileName, int lineNumber, const EntityItemID& entityID, ScriptType scriptType, Severity severity) + : _messageContent(messageContent), _fileName(fileName), _lineNumber(lineNumber), _entityID(entityID), _scriptType(scriptType), _severity(severity) {} + + QJsonObject toJson(); + bool fromJson(const QJsonObject &object); + + QString getMessage() { return _messageContent; } + QString getFileName() { return _fileName; } + int getLineNumber() { return _lineNumber; } + ScriptType getScriptType() { return _scriptType; } + Severity getSeverity() { return _severity; } + EntityItemID getEntityID() { return _entityID; } + +private: + QString _messageContent; + QString _fileName; + int _lineNumber {-1}; + EntityItemID _entityID; + ScriptType _scriptType {ScriptType::TYPE_NONE}; + Severity _severity {Severity::SEVERITY_NONE}; +}; + +#endif //OVERTE_SCRIPTMESSAGE_H diff --git a/libraries/script-engine/src/ScriptPermissions.cpp b/libraries/script-engine/src/ScriptPermissions.cpp new file mode 100644 index 0000000000..a817f00056 --- /dev/null +++ b/libraries/script-engine/src/ScriptPermissions.cpp @@ -0,0 +1,124 @@ +// +// ScriptPermissions.cpp +// libraries/script-engine/src/ScriptPermissions.cpp +// +// Created by dr Karol Suprynowicz on 2024/03/24. +// Copyright 2024 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 "ScriptPermissions.h" + +#include +#include + +#include "ScriptEngine.h" +#include "ScriptManager.h" +#include "Scriptable.h" + +static const bool PERMISSIONS_DEBUG_ENABLED = false; + +extern const std::array(ScriptPermissions::Permission::SCRIPT_PERMISSIONS_SIZE)> scriptPermissionNames { + "Permission to get user's avatar URL" //SCRIPT_PERMISSION_GET_AVATAR_URL +}; + +extern const std::array(ScriptPermissions::Permission::SCRIPT_PERMISSIONS_SIZE)> scriptPermissionSettingKeyNames { + "private/scriptPermissionGetAvatarURLSafeURLs" //SCRIPT_PERMISSION_GET_AVATAR_URL +}; + +extern const std::array(ScriptPermissions::Permission::SCRIPT_PERMISSIONS_SIZE)> scriptPermissionSettingEnableKeyNames { + "private/scriptPermissionGetAvatarURLEnable" //SCRIPT_PERMISSION_GET_AVATAR_URL +}; + +extern const std::array(ScriptPermissions::Permission::SCRIPT_PERMISSIONS_SIZE)> scriptPermissionSettingEnableDefaultValues { + true //SCRIPT_PERMISSION_GET_AVATAR_URL +}; + +bool ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission permission) { + if (permission >= ScriptPermissions::Permission::SCRIPT_PERMISSIONS_SIZE) { + return false; + } + int permissionIndex = static_cast(permission); + // Check if the permission checking is active + Setting::Handle isCheckingEnabled(scriptPermissionSettingEnableKeyNames[permissionIndex], scriptPermissionSettingEnableDefaultValues[permissionIndex]); + if (!isCheckingEnabled.get()) { + return true; + } + // Get the script manager: + auto engine = Scriptable::engine(); + if (!engine) { + // When this happens it means that function was called from QML or C++ and should always be allowed + if (PERMISSIONS_DEBUG_ENABLED) { + qDebug() << "ScriptPermissions::isCurrentScriptAllowed called outside script engine for permission: " + << scriptPermissionNames[permissionIndex]; + } + return true; + } + auto manager = engine->manager(); + if (!manager) { + qDebug() << "ScriptPermissions::isCurrentScriptAllowed called from script engine with no script manager for permission: " << scriptPermissionNames[permissionIndex]; + return false; + } + std::vector urlsToCheck; + QString scriptURL = manager->getAbsoluteFilename(); + + // If this is an entity script manager, we need to find the file name of the current script instead + if (!scriptURL.startsWith("about:Entities")) { + urlsToCheck.push_back(scriptURL); + } + + auto currentURL = Scriptable::context()->currentFileName(); + if (!currentURL.isEmpty() && currentURL != scriptURL) { + urlsToCheck.push_back(currentURL); + } + + if (PERMISSIONS_DEBUG_ENABLED) { + qDebug() << "ScriptPermissions::isCurrentScriptAllowed: filename: " << scriptURL; + } + auto parentContext = Scriptable::context()->parentContext(); + while (parentContext) { + QString parentFilename = parentContext->currentFileName(); + if (!parentFilename.isEmpty()) { + urlsToCheck.push_back(parentContext->currentFileName()); + if (PERMISSIONS_DEBUG_ENABLED) { + qDebug() << "ScriptPermissions::isCurrentScriptAllowed: parent filename: " << parentContext->currentFileName(); + } + } + parentContext = parentContext->parentContext(); + } + + // Check if the script is allowed: + QList safeURLPrefixes = { "file:///", "qrc:/", NetworkingConstants::OVERTE_COMMUNITY_APPLICATIONS, + NetworkingConstants::OVERTE_TUTORIAL_SCRIPTS, "about:console"}; + Setting::Handle allowedURLsSetting(scriptPermissionSettingKeyNames[permissionIndex]); + QList allowedURLs = allowedURLsSetting.get().split("\n"); + + for (auto entry : allowedURLs) { + safeURLPrefixes.push_back(entry); + } + + for (auto urlToCheck : urlsToCheck) { + bool urlIsAllowed = false; + for (const auto& str : safeURLPrefixes) { + if (!str.isEmpty() && urlToCheck.startsWith(str)) { + urlIsAllowed = true; + if (PERMISSIONS_DEBUG_ENABLED) { + qDebug() << "ScriptPermissions::isCurrentScriptAllowed: " << scriptPermissionNames[permissionIndex] + << " for script " << urlToCheck << " accepted with rule: " << str; + } + } + } + + if (!urlIsAllowed) { + if (PERMISSIONS_DEBUG_ENABLED) { + qDebug() << "ScriptPermissions::isCurrentScriptAllowed: " << scriptPermissionNames[permissionIndex] + << " for script " << urlToCheck << " rejected."; + } + return false; + } + } + + return true; +} \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptPermissions.h b/libraries/script-engine/src/ScriptPermissions.h new file mode 100644 index 0000000000..f4b06253c5 --- /dev/null +++ b/libraries/script-engine/src/ScriptPermissions.h @@ -0,0 +1,31 @@ +// +// ScriptPermissions.h +// libraries/script-engine/src/ScriptPermissions.h +// +// Created by dr Karol Suprynowicz on 2024/03/24. +// Copyright 2024 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 +// + +#pragma once + +#include + +#include "SettingHandle.h" +#include "DependencyManager.h" + +class ScriptPermissions { +public: + enum class Permission { + SCRIPT_PERMISSION_GET_AVATAR_URL, + SCRIPT_PERMISSIONS_SIZE + }; + + static bool isCurrentScriptAllowed(Permission permission); + //TODO: add a function to request permission through a popup +}; + +// TODO: add ScriptPermissionsScriptingInterface, where script can check if they have permissions +// and request permissions through a tablet popup. diff --git a/libraries/script-engine/src/ScriptUUID.cpp b/libraries/script-engine/src/ScriptUUID.cpp index a6c054d2f6..376796c0dd 100644 --- a/libraries/script-engine/src/ScriptUUID.cpp +++ b/libraries/script-engine/src/ScriptUUID.cpp @@ -45,7 +45,7 @@ void ScriptUUID::print(const QString& label, const QUuid& id) { QString message = QString("%1 %2").arg(qPrintable(label)); message = message.arg(id.toString()); qCDebug(scriptengine) << message; - Q_ASSERT(engine); + Q_ASSERT(engine()); if (ScriptManager* scriptManager = engine()->manager()) { scriptManager->print(message); } diff --git a/libraries/script-engine/src/ScriptValueUtils.cpp b/libraries/script-engine/src/ScriptValueUtils.cpp index f5808080ba..77ee38de8c 100644 --- a/libraries/script-engine/src/ScriptValueUtils.cpp +++ b/libraries/script-engine/src/ScriptValueUtils.cpp @@ -79,6 +79,7 @@ void registerMetaTypes(ScriptEngine* engine) { scriptRegisterMetaType(engine); scriptRegisterMetaType(engine); scriptRegisterMetaType(engine, "QTimer*"); + scriptRegisterMetaType(engine, "QByteArray"); scriptRegisterMetaType(engine); scriptRegisterMetaType(engine); diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index 6b689d4c4c..95897dc276 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -39,7 +39,7 @@ void Vec3::print(const QString& label, const glm::vec3& v) { QString message = QString("%1 %2").arg(qPrintable(label)); message = message.arg(glm::to_string(glm::dvec3(v)).c_str()); qCDebug(scriptengine) << message; - Q_ASSERT(engine); + Q_ASSERT(engine()); if (ScriptManager* scriptManager = engine()->manager()) { scriptManager->print(message); } diff --git a/libraries/script-engine/src/v8/FastScriptValueUtils.cpp b/libraries/script-engine/src/v8/FastScriptValueUtils.cpp index 66c5fce2e4..d45b01b303 100644 --- a/libraries/script-engine/src/v8/FastScriptValueUtils.cpp +++ b/libraries/script-engine/src/v8/FastScriptValueUtils.cpp @@ -19,6 +19,48 @@ #ifdef CONVERSIONS_OPTIMIZED_FOR_V8 +ScriptValue qBytearrayToScriptValue(ScriptEngine* engine, const QByteArray &qByteArray) { + auto engineV8 = dynamic_cast(engine); + Q_ASSERT(engineV8); + auto isolate = engineV8->getIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + auto context = engineV8->getContext(); + v8::Context::Scope contextScope(context); + v8::Local arrayBuffer = v8::ArrayBuffer::New(isolate, qByteArray.size()); + memcpy(arrayBuffer->GetBackingStore()->Data(), qByteArray.data(), qByteArray.size()); + v8::Local arrayBufferValue = v8::Local::Cast(arrayBuffer); + + return {new ScriptValueV8Wrapper(engineV8, V8ScriptValue(engineV8, arrayBufferValue))}; +} + +bool qBytearrayFromScriptValue(const ScriptValue& object, QByteArray &qByteArray) { + ScriptValueV8Wrapper *proxy = ScriptValueV8Wrapper::unwrap(object); + if (!proxy) { + return false; + } + + auto engineV8 = proxy->getV8Engine(); + + auto isolate = engineV8->getIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + auto context = engineV8->getContext(); + v8::Context::Scope contextScope(context); + V8ScriptValue v8ScriptValue = proxy->toV8Value(); + + v8::Local v8Value = v8ScriptValue.get(); + if(!v8Value->IsArrayBuffer()) { + return false; + } + v8::Local arrayBuffer = v8::Local::Cast(v8Value); + qByteArray.resize((int)arrayBuffer->ByteLength()); + memcpy(qByteArray.data(), arrayBuffer->Data(), arrayBuffer->ByteLength()); + return true; +} + ScriptValue vec3ToScriptValue(ScriptEngine* engine, const glm::vec3& vec3) { ScriptValue value = engine->newObject(); @@ -31,7 +73,7 @@ ScriptValue vec3ToScriptValue(ScriptEngine* engine, const glm::vec3& vec3) { v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); auto context = engineV8->getContext(); - v8::Context::Scope contextScope(engineV8->getContext()); + v8::Context::Scope contextScope(context); V8ScriptValue v8ScriptValue = proxy->toV8Value(); v8::Local v8Object = v8::Local::Cast(v8ScriptValue.get()); @@ -98,6 +140,9 @@ ScriptValue vec3ToScriptValue(ScriptEngine* engine, const glm::vec3& vec3) { bool vec3FromScriptValue(const ScriptValue& object, glm::vec3& vec3) { ScriptValueV8Wrapper *proxy = ScriptValueV8Wrapper::unwrap(object); + if (!proxy) { + return false; + } auto engineV8 = proxy->getV8Engine(); @@ -106,7 +151,7 @@ bool vec3FromScriptValue(const ScriptValue& object, glm::vec3& vec3) { v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); auto context = engineV8->getContext(); - v8::Context::Scope contextScope(engineV8->getContext()); + v8::Context::Scope contextScope(context); V8ScriptValue v8ScriptValue = proxy->toV8Value(); v8::Local v8Value = v8ScriptValue.get(); diff --git a/libraries/script-engine/src/v8/FastScriptValueUtils.h b/libraries/script-engine/src/v8/FastScriptValueUtils.h index 80d97023a5..00deb04d9c 100644 --- a/libraries/script-engine/src/v8/FastScriptValueUtils.h +++ b/libraries/script-engine/src/v8/FastScriptValueUtils.h @@ -24,6 +24,10 @@ #define CONVERSIONS_OPTIMIZED_FOR_V8 #ifdef CONVERSIONS_OPTIMIZED_FOR_V8 +ScriptValue qBytearrayToScriptValue(ScriptEngine* engine, const QByteArray &qByteArray); + +bool qBytearrayFromScriptValue(const ScriptValue& object, QByteArray &qByteArray); + ScriptValue vec3ToScriptValue(ScriptEngine* engine, const glm::vec3& vec3); bool vec3FromScriptValue(const ScriptValue& object, glm::vec3& vec3); diff --git a/libraries/script-engine/src/v8/ScriptContextV8Wrapper.cpp b/libraries/script-engine/src/v8/ScriptContextV8Wrapper.cpp index 2b92a9ae8a..11e90b3a5a 100644 --- a/libraries/script-engine/src/v8/ScriptContextV8Wrapper.cpp +++ b/libraries/script-engine/src/v8/ScriptContextV8Wrapper.cpp @@ -111,6 +111,37 @@ QStringList ScriptContextV8Wrapper::backtrace() const { return backTrace; } +int ScriptContextV8Wrapper::currentLineNumber() const { + auto isolate = _engine->getIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Context::Scope contextScope(_context.Get(isolate)); + v8::Local stackTrace = v8::StackTrace::CurrentStackTrace(isolate, 1); + if (stackTrace->GetFrameCount() > 0) { + v8::Local stackFrame = stackTrace->GetFrame(isolate, 0); + return stackFrame->GetLineNumber(); + } else { + return -1; + } +} + +QString ScriptContextV8Wrapper::currentFileName() const { + auto isolate = _engine->getIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Context::Scope contextScope(_context.Get(isolate)); + v8::Local stackTrace = v8::StackTrace::CurrentStackTrace(isolate, 1); + QStringList backTrace; + if (stackTrace->GetFrameCount() > 0) { + v8::Local stackFrame = stackTrace->GetFrame(isolate, 0); + return *v8::String::Utf8Value(isolate, stackFrame->GetScriptNameOrSourceURL()); + } else { + return ""; + } +} + ScriptValue ScriptContextV8Wrapper::callee() const { Q_ASSERT(false); //V8TODO diff --git a/libraries/script-engine/src/v8/ScriptContextV8Wrapper.h b/libraries/script-engine/src/v8/ScriptContextV8Wrapper.h index c410587c45..4512e72818 100644 --- a/libraries/script-engine/src/v8/ScriptContextV8Wrapper.h +++ b/libraries/script-engine/src/v8/ScriptContextV8Wrapper.h @@ -45,6 +45,13 @@ public: // ScriptContext implementation virtual int argumentCount() const override; virtual ScriptValue argument(int index) const override; virtual QStringList backtrace() const override; + + // Name of the file in which message was generated. Empty string when no file name is available. + virtual int currentLineNumber() const override; + + // Number of the line on which message was generated. -1 if there line number is not available. + virtual QString currentFileName() const override; + virtual ScriptValue callee() const override; virtual ScriptEnginePointer engine() const override; virtual ScriptFunctionContextPointer functionContext() const override; diff --git a/libraries/script-engine/src/v8/ScriptEngineV8.cpp b/libraries/script-engine/src/v8/ScriptEngineV8.cpp index e4f6ad66f2..5a9dead93f 100644 --- a/libraries/script-engine/src/v8/ScriptEngineV8.cpp +++ b/libraries/script-engine/src/v8/ScriptEngineV8.cpp @@ -77,6 +77,17 @@ bool ScriptEngineV8::IS_THREADSAFE_INVOCATION(const QThread* thread, const QStri return false; } +QString getFileNameFromTryCatch(v8::TryCatch &tryCatch, v8::Isolate *isolate, v8::Local &context ) { + v8::Local exceptionMessage = tryCatch.Message(); + QString errorFileName; + auto resource = exceptionMessage->GetScriptResourceName(); + v8::Local v8resourceString; + if (resource->ToString(context).ToLocal(&v8resourceString)) { + errorFileName = QString(*v8::String::Utf8Value(isolate, v8resourceString)); + } + return errorFileName; +} + ScriptValue ScriptEngineV8::makeError(const ScriptValue& _other, const QString& type) { if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { return nullValue(); @@ -307,7 +318,7 @@ void ScriptEngineV8::registerValue(const QString& valueName, V8ScriptValue value v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); v8::Local context = getContext(); - v8::Context::Scope contextScope(getContext()); + v8::Context::Scope contextScope(context); QStringList pathToValue = valueName.split("."); int partsToGo = pathToValue.length(); v8::Local partObject = context->Global(); @@ -370,17 +381,17 @@ void ScriptEngineV8::registerGlobalObject(const QString& name, QObject* object, Q_ASSERT(_v8Isolate->IsCurrent()); v8::Local context = getContext(); v8::Context::Scope contextScope(context); - v8::Local v8GlobalObject = getContext()->Global(); + v8::Local v8GlobalObject = context->Global(); v8::Local v8Name = v8::String::NewFromUtf8(_v8Isolate, name.toStdString().c_str()).ToLocalChecked(); - if (!v8GlobalObject->Get(getContext(), v8Name).IsEmpty()) { + if (!v8GlobalObject->Get(context, v8Name).IsEmpty()) { if (object) { V8ScriptValue value = ScriptObjectV8Proxy::newQObject(this, object, ScriptEngine::QtOwnership); - if(!v8GlobalObject->Set(getContext(), v8Name, value.get()).FromMaybe(false)) { + if(!v8GlobalObject->Set(context, v8Name, value.get()).FromMaybe(false)) { Q_ASSERT(false); } } else { - if(!v8GlobalObject->Set(getContext(), v8Name, v8::Null(_v8Isolate)).FromMaybe(false)) { + if(!v8GlobalObject->Set(context, v8Name, v8::Null(_v8Isolate)).FromMaybe(false)) { Q_ASSERT(false); } } @@ -458,7 +469,8 @@ void ScriptEngineV8::registerGetterSetter(const QString& name, ScriptEngine::Fun v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(getContext()); + auto context = getContext(); + v8::Context::Scope contextScope(context); ScriptValue setterFunction = newFunction(setter, 1); ScriptValue getterFunction = newFunction(getter); @@ -482,7 +494,7 @@ void ScriptEngineV8::registerGetterSetter(const QString& name, ScriptEngine::Fun } else { v8ObjectToSetProperty = v8ParentObject; } - if (!v8ObjectToSetProperty->DefineProperty(getContext(), v8propertyName, propertyDescriptor).FromMaybe(false)) { + if (!v8ObjectToSetProperty->DefineProperty(context, v8propertyName, propertyDescriptor).FromMaybe(false)) { qCDebug(scriptengine_v8) << "DefineProperty failed for registerGetterSetter \"" << name << "\" for parent: \"" << parent << "\""; } @@ -493,7 +505,7 @@ void ScriptEngineV8::registerGetterSetter(const QString& name, ScriptEngine::Fun } else { v8::Local v8propertyName = v8::String::NewFromUtf8(_v8Isolate, name.toStdString().c_str()).ToLocalChecked(); - if (!getContext()->Global()->DefineProperty(getContext(), v8propertyName, propertyDescriptor).FromMaybe(false)) { + if (!context->Global()->DefineProperty(context, v8propertyName, propertyDescriptor).FromMaybe(false)) { qCDebug(scriptengine_v8) << "DefineProperty failed for registerGetterSetter \"" << name << "\" for global object"; } } @@ -527,7 +539,7 @@ void ScriptEngineV8::storeGlobalObjectContents() { v8::Local globalMemberObjects = v8::Object::New(_v8Isolate); auto globalMemberNames = context->Global()->GetPropertyNames(context).ToLocalChecked(); - for (size_t i = 0; i < globalMemberNames->Length(); i++) { + for (uint32_t i = 0; i < globalMemberNames->Length(); i++) { auto name = globalMemberNames->Get(context, i).ToLocalChecked(); if(!globalMemberObjects->Set(context, name, context->Global()->Get(context, name).ToLocalChecked()).FromMaybe(false)) { Q_ASSERT(false); @@ -557,7 +569,8 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, ScriptProgramV8Wrapper* unwrappedProgram; { - v8::Context::Scope contextScope(getContext()); + auto context = getContext(); + v8::Context::Scope contextScope(context); unwrappedProgram = ScriptProgramV8Wrapper::unwrap(_program); if (unwrappedProgram == nullptr) { _evaluatingCounter--; @@ -588,7 +601,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, closureObject = v8::Local::Cast(closure.constGet()); qCDebug(scriptengine_v8) << "Closure object members:" << scriptValueDebugListMembersV8(closure); v8::Local testObject = v8::Object::New(_v8Isolate); - if(!testObject->Set(getContext(), v8::String::NewFromUtf8(_v8Isolate, "test_value").ToLocalChecked(), closureObject).FromMaybe(false)) { + if(!testObject->Set(context, v8::String::NewFromUtf8(_v8Isolate, "test_value").ToLocalChecked(), closureObject).FromMaybe(false)) { Q_ASSERT(false); } qCDebug(scriptengine_v8) << "Test object members:" << scriptValueDebugListMembersV8(V8ScriptValue(this, testObject)); @@ -632,7 +645,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, // Since V8 cannot use arbitrary object as global object, objects from main global need to be copied to closure's global object auto globalObjectContents = _globalObjectContents.Get(_v8Isolate); auto globalMemberNames = globalObjectContents->GetPropertyNames(globalObjectContents->CreationContext()).ToLocalChecked(); - for (size_t i = 0; i < globalMemberNames->Length(); i++) { + for (uint32_t i = 0; i < globalMemberNames->Length(); i++) { auto name = globalMemberNames->Get(closureContext, i).ToLocalChecked(); if(!closureContext->Global()->Set(closureContext, name, globalObjectContents->Get(globalObjectContents->CreationContext(), name).ToLocalChecked()).FromMaybe(false)) { Q_ASSERT(false); @@ -643,7 +656,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, // Objects from closure need to be copied to global object too // V8TODO: I'm not sure which context to use with Get auto closureMemberNames = closureObject->GetPropertyNames(closureContext).ToLocalChecked(); - for (size_t i = 0; i < closureMemberNames->Length(); i++) { + for (uint32_t i = 0; i < closureMemberNames->Length(); i++) { auto name = closureMemberNames->Get(closureContext, i).ToLocalChecked(); if(!closureContext->Global()->Set(closureContext, name, closureObject->Get(closureContext, name).ToLocalChecked()).FromMaybe(false)) { Q_ASSERT(false); @@ -651,6 +664,11 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, } // "Script" API is context-dependent, so it needs to be recreated for each new context registerGlobalObject("Script", new ScriptManagerScriptingInterface(_manager), ScriptEngine::ScriptOwnership); + auto Script = globalObject().property("Script"); + auto require = Script.property("require"); + auto resolve = Script.property("_requireResolve"); + require.setProperty("resolve", resolve, ScriptValue::ReadOnly | ScriptValue::Undeletable); + globalObject().setProperty("require", require, ScriptValue::ReadOnly | ScriptValue::Undeletable); // Script.require properties need to be copied, since that's where the Script.require cache is // Get source and destination Script.require objects @@ -699,7 +717,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, auto requireMemberNames = oldRequireObject->GetPropertyNames(oldRequireObject->CreationContext()).ToLocalChecked(); - for (size_t i = 0; i < requireMemberNames->Length(); i++) { + for (uint32_t i = 0; i < requireMemberNames->Length(); i++) { auto name = requireMemberNames->Get(closureContext, i).ToLocalChecked(); v8::Local oldObject; if (!oldRequireObject->Get(oldRequireObject->CreationContext(), name).ToLocal(&oldObject)) { @@ -724,7 +742,13 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, + "tryCatch details:" + formatErrorMessageFromTryCatch(tryCatch); v8Result = v8::Null(_v8Isolate); if (_manager) { - _manager->scriptErrorMessage(errorMessage); + v8::Local exceptionMessage = tryCatch.Message(); + int errorLineNumber = -1; + if (!exceptionMessage.IsEmpty()) { + errorLineNumber = exceptionMessage->GetLineNumber(closureContext).FromJust(); + } + _manager->scriptErrorMessage(errorMessage, getFileNameFromTryCatch(tryCatch, _v8Isolate, closureContext), + errorLineNumber); } else { qWarning(scriptengine_v8) << errorMessage; } @@ -770,15 +794,22 @@ ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& f v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(getContext()); + auto context = getContext(); + v8::Context::Scope contextScope(context); v8::ScriptOrigin scriptOrigin(getIsolate(), v8::String::NewFromUtf8(getIsolate(), fileName.toStdString().c_str()).ToLocalChecked()); v8::Local script; { v8::TryCatch tryCatch(getIsolate()); - if (!v8::Script::Compile(getContext(), v8::String::NewFromUtf8(getIsolate(), sourceCode.toStdString().c_str()).ToLocalChecked(), &scriptOrigin).ToLocal(&script)) { + if (!v8::Script::Compile(context, v8::String::NewFromUtf8(getIsolate(), sourceCode.toStdString().c_str()).ToLocalChecked(), &scriptOrigin).ToLocal(&script)) { QString errorMessage(QString("Error while compiling script: \"") + fileName + QString("\" ") + formatErrorMessageFromTryCatch(tryCatch)); if (_manager) { - _manager->scriptErrorMessage(errorMessage); + v8::Local exceptionMessage = tryCatch.Message(); + int errorLineNumber = -1; + if (!exceptionMessage.IsEmpty()) { + errorLineNumber = exceptionMessage->GetLineNumber(context).FromJust(); + } + _manager->scriptErrorMessage(errorMessage, getFileNameFromTryCatch(tryCatch, _v8Isolate, context), + errorLineNumber); } else { qDebug(scriptengine_v8) << errorMessage; } @@ -790,13 +821,19 @@ ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& f v8::Local result; v8::TryCatch tryCatchRun(getIsolate()); - if (!script->Run(getContext()).ToLocal(&result)) { + if (!script->Run(context).ToLocal(&result)) { Q_ASSERT(tryCatchRun.HasCaught()); auto runError = tryCatchRun.Message(); ScriptValue errorValue(new ScriptValueV8Wrapper(this, V8ScriptValue(this, runError->Get()))); QString errorMessage(QString("Running script: \"") + fileName + QString("\" ") + formatErrorMessageFromTryCatch(tryCatchRun)); if (_manager) { - _manager->scriptErrorMessage(errorMessage); + v8::Local exceptionMessage = tryCatchRun.Message(); + int errorLineNumber = -1; + if (!exceptionMessage.IsEmpty()) { + errorLineNumber = exceptionMessage->GetLineNumber(context).FromJust(); + } + _manager->scriptErrorMessage(errorMessage, getFileNameFromTryCatch(tryCatchRun, _v8Isolate, context), + errorLineNumber); } else { qDebug(scriptengine_v8) << errorMessage; } @@ -829,7 +866,8 @@ void ScriptEngineV8::setUncaughtException(const v8::TryCatch &tryCatch, const QS v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(getContext()); + v8::Local context = getContext(); + v8::Context::Scope contextScope(context); QString result(""); QString errorMessage = ""; @@ -844,10 +882,10 @@ void ScriptEngineV8::setUncaughtException(const v8::TryCatch &tryCatch, const QS v8::Local exceptionMessage = tryCatch.Message(); if (!exceptionMessage.IsEmpty()) { - ex->errorLine = exceptionMessage->GetLineNumber(getContext()).FromJust(); - ex->errorColumn = exceptionMessage->GetStartColumn(getContext()).FromJust(); + ex->errorLine = exceptionMessage->GetLineNumber(context).FromJust(); + ex->errorColumn = exceptionMessage->GetStartColumn(context).FromJust(); v8::Local backtraceV8String; - if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String)) { + if (tryCatch.StackTrace(context).ToLocal(&backtraceV8String)) { if (backtraceV8String->IsString()) { if (v8::Local::Cast(backtraceV8String)->Length() > 0) { v8::String::Utf8Value backtraceUtf8Value(getIsolate(), backtraceV8String); @@ -875,7 +913,8 @@ QString ScriptEngineV8::formatErrorMessageFromTryCatch(v8::TryCatch &tryCatch) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(getContext()); + auto context = getContext(); + v8::Context::Scope contextScope(context); QString result(""); int errorColumnNumber = 0; int errorLineNumber = 0; @@ -885,10 +924,10 @@ QString ScriptEngineV8::formatErrorMessageFromTryCatch(v8::TryCatch &tryCatch) { errorMessage = QString(*utf8Value); v8::Local exceptionMessage = tryCatch.Message(); if (!exceptionMessage.IsEmpty()) { - errorLineNumber = exceptionMessage->GetLineNumber(getContext()).FromJust(); - errorColumnNumber = exceptionMessage->GetStartColumn(getContext()).FromJust(); + errorLineNumber = exceptionMessage->GetLineNumber(context).FromJust(); + errorColumnNumber = exceptionMessage->GetStartColumn(context).FromJust(); v8::Local backtraceV8String; - if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String)) { + if (tryCatch.StackTrace(context).ToLocal(&backtraceV8String)) { if (backtraceV8String->IsString()) { if (v8::Local::Cast(backtraceV8String)->Length() > 0) { v8::String::Utf8Value backtraceUtf8Value(getIsolate(), backtraceV8String); @@ -1000,7 +1039,8 @@ Q_INVOKABLE ScriptValue ScriptEngineV8::evaluate(const ScriptProgramPointer& pro v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(getContext()); + auto context = getContext(); + v8::Context::Scope contextScope(context); ScriptProgramV8Wrapper* unwrapped = ScriptProgramV8Wrapper::unwrap(program); if (!unwrapped) { setUncaughtEngineException("Could not unwrap program", "Compile error"); @@ -1020,7 +1060,7 @@ Q_INVOKABLE ScriptValue ScriptEngineV8::evaluate(const ScriptProgramPointer& pro const V8ScriptProgram& v8Program = unwrapped->toV8Value(); v8::TryCatch tryCatchRun(getIsolate()); - if (!v8Program.constGet()->Run(getContext()).ToLocal(&result)) { + if (!v8Program.constGet()->Run(context).ToLocal(&result)) { Q_ASSERT(tryCatchRun.HasCaught()); auto runError = tryCatchRun.Message(); errorValue = ScriptValue(new ScriptValueV8Wrapper(this, V8ScriptValue(this, runError->Get()))); @@ -1252,7 +1292,8 @@ ScriptValue ScriptEngineV8::newFunction(ScriptEngine::FunctionSignature fun, int v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(getContext()); + auto context = getContext(); + v8::Context::Scope contextScope(context); auto v8FunctionCallback = [](const v8::FunctionCallbackInfo& info) { //V8TODO: is using GetCurrentContext ok, or context wrapper needs to be added? @@ -1276,10 +1317,10 @@ ScriptValue ScriptEngineV8::newFunction(ScriptEngine::FunctionSignature fun, int } }; auto functionDataTemplate = getFunctionDataTemplate(); - auto functionData = functionDataTemplate->NewInstance(getContext()).ToLocalChecked(); + auto functionData = functionDataTemplate->NewInstance(context).ToLocalChecked(); functionData->SetAlignedPointerInInternalField(0, reinterpret_cast(fun)); functionData->SetAlignedPointerInInternalField(1, reinterpret_cast(this)); - auto v8Function = v8::Function::New(getContext(), v8FunctionCallback, functionData, length).ToLocalChecked(); + auto v8Function = v8::Function::New(context, v8FunctionCallback, functionData, length).ToLocalChecked(); V8ScriptValue result(this, v8Function); return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result))); } @@ -1293,11 +1334,12 @@ bool ScriptEngineV8::setProperty(const char* name, const QVariant& value) { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(getContext()); - v8::Local global = getContext()->Global(); + v8::Local context = getContext(); + v8::Context::Scope contextScope(context); + v8::Local global = context->Global(); auto v8Name = v8::String::NewFromUtf8(getIsolate(), name).ToLocalChecked(); V8ScriptValue v8Value = castVariantToValue(value); - return global->Set(getContext(), v8Name, v8Value.get()).FromMaybe(false); + return global->Set(context, v8Name, v8Value.get()).FromMaybe(false); } void ScriptEngineV8::setProcessEventsInterval(int interval) { @@ -1411,10 +1453,11 @@ void ScriptEngineV8::compileTest() { v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(getContext()); + auto context = getContext(); + v8::Context::Scope contextScope(context); v8::Local script; v8::ScriptOrigin scriptOrigin(getIsolate(), v8::String::NewFromUtf8(getIsolate(),"test").ToLocalChecked()); - if (v8::Script::Compile(getContext(), v8::String::NewFromUtf8(getIsolate(), "print(\"hello world\");").ToLocalChecked(), &scriptOrigin).ToLocal(&script)) { + if (v8::Script::Compile(context, v8::String::NewFromUtf8(getIsolate(), "print(\"hello world\");").ToLocalChecked(), &scriptOrigin).ToLocal(&script)) { qCDebug(scriptengine_v8) << "Compile test successful"; } else { qCDebug(scriptengine_v8) << "Compile test failed"; @@ -1436,14 +1479,15 @@ QString ScriptEngineV8::scriptValueDebugListMembersV8(const V8ScriptValue &v8Val v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(getContext()); + v8::Local context = getContext(); + v8::Context::Scope contextScope(context); QString membersString(""); if (v8Value.constGet()->IsObject()) { v8::Local membersStringV8; v8::Local object = v8::Local::Cast(v8Value.constGet()); - auto names = object->GetPropertyNames(getContext()).ToLocalChecked(); - if (v8::JSON::Stringify(getContext(), names).ToLocal(&membersStringV8)) { + auto names = object->GetPropertyNames(context).ToLocalChecked(); + if (v8::JSON::Stringify(context, names).ToLocal(&membersStringV8)) { membersString = QString(*v8::String::Utf8Value(_v8Isolate, membersStringV8)); } membersString = QString(*v8::String::Utf8Value(_v8Isolate, membersStringV8)); @@ -1457,16 +1501,17 @@ QString ScriptEngineV8::scriptValueDebugDetailsV8(const V8ScriptValue &v8Value) v8::Locker locker(_v8Isolate); v8::Isolate::Scope isolateScope(_v8Isolate); v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(getContext()); + v8::Local context = getContext(); + v8::Context::Scope contextScope(context); QString parentValueQString(""); v8::Local parentValueString; - if (v8Value.constGet()->ToDetailString(getContext()).ToLocal(&parentValueString)) { + if (v8Value.constGet()->ToDetailString(context).ToLocal(&parentValueString)) { parentValueQString = QString(*v8::String::Utf8Value(_v8Isolate, parentValueString)); } QString JSONQString; v8::Local JSONString; - if (v8::JSON::Stringify(getContext(), v8Value.constGet()).ToLocal(&JSONString)) { + if (v8::JSON::Stringify(context, v8Value.constGet()).ToLocal(&JSONString)) { JSONQString = QString(*v8::String::Utf8Value(_v8Isolate, JSONString)); } return parentValueQString + QString(" JSON: ") + JSONQString; diff --git a/libraries/script-engine/src/v8/ScriptEngineV8.h b/libraries/script-engine/src/v8/ScriptEngineV8.h index 5badba271e..02352c9ed7 100644 --- a/libraries/script-engine/src/v8/ScriptEngineV8.h +++ b/libraries/script-engine/src/v8/ScriptEngineV8.h @@ -309,6 +309,8 @@ private: ScriptEngineV8* _engine; }; +QString getFileNameFromTryCatch(v8::TryCatch &tryCatch, v8::Isolate *isolate, v8::Local &context ); + #include "V8Types.h" #endif // hifi_ScriptEngineV8_h diff --git a/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp b/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp index 8dead23ddb..211da7582f 100644 --- a/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp +++ b/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp @@ -725,12 +725,12 @@ V8ScriptValue ScriptEngineV8::castVariantToValue(const QVariant& val) { case QMetaType::QDateTime: { double timeMs = val.value().currentMSecsSinceEpoch(); - return V8ScriptValue(this, v8::Date::New(getContext(), timeMs).ToLocalChecked()); + return V8ScriptValue(this, v8::Date::New(context, timeMs).ToLocalChecked()); } case QMetaType::QDate: { double timeMs = val.value().startOfDay().currentMSecsSinceEpoch(); - return V8ScriptValue(this, v8::Date::New(getContext(), timeMs).ToLocalChecked()); + return V8ScriptValue(this, v8::Date::New(context, timeMs).ToLocalChecked()); } default: // check to see if this is a pointer to a QObject-derived object diff --git a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp index 177eeb259f..7c3fd07e9b 100644 --- a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp +++ b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp @@ -56,6 +56,13 @@ public: // ScriptContext implementation virtual int argumentCount() const override { return _parent->argumentCount(); } virtual ScriptValue argument(int index) const override { return _parent->argument(index); } virtual QStringList backtrace() const override { return _parent->backtrace(); } + + // Name of the file in which message was generated. Empty string when no file name is available. + virtual int currentLineNumber() const override { return _parent->currentLineNumber(); } + + // Number of the line on which message was generated. -1 if there line number is not available. + virtual QString currentFileName() const override { return _parent->currentFileName(); } + virtual ScriptValue callee() const override { return _parent->callee(); } virtual ScriptEnginePointer engine() const override { return _parent->engine(); } virtual ScriptFunctionContextPointer functionContext() const override { return _parent->functionContext(); } @@ -127,6 +134,23 @@ V8ScriptValue ScriptObjectV8Proxy::newQObject(ScriptEngineV8* engine, QObject* o QPointer enginePtr = engine; object->connect(object, &QObject::destroyed, engine, [enginePtr, object]() { if (!enginePtr) return; + // Lookup needs to be done twice, because _qobjectWrapperMapProtect must not be locked when disconnecting signals + QSharedPointer proxy; + { + QMutexLocker guard(&enginePtr->_qobjectWrapperMapProtect); + auto lookupV8 = enginePtr->_qobjectWrapperMapV8.find(object); + if (lookupV8 != enginePtr->_qobjectWrapperMapV8.end()) { + proxy = lookupV8.value(); + } + } + if (proxy) { + for (auto signal : proxy->_signalInstances) { + if (signal) { + signal->disconnectAllScriptSignalProxies(); + } + } + } + QMutexLocker guard(&enginePtr->_qobjectWrapperMapProtect); ScriptEngineV8::ObjectWrapperMap::iterator lookup = enginePtr->_qobjectWrapperMap.find(object); if (lookup != enginePtr->_qobjectWrapperMap.end()) { @@ -188,7 +212,13 @@ ScriptObjectV8Proxy* ScriptObjectV8Proxy::unwrapProxy(v8::Isolate* isolate, v8:: qCDebug(scriptengine_v8) << "Cannot unwrap proxy - internal fields don't point to object proxy"; return nullptr; } - return reinterpret_cast(v8Object->GetAlignedPointerFromInternalField(1)); + + ScriptObjectV8Proxy* proxy = reinterpret_cast(v8Object->GetAlignedPointerFromInternalField(1)); + if (proxy) { + Q_ASSERT(!proxy->_wasDestroyed); + } + + return proxy; } QObject* ScriptObjectV8Proxy::unwrap(const V8ScriptValue& val) { @@ -197,6 +227,12 @@ QObject* ScriptObjectV8Proxy::unwrap(const V8ScriptValue& val) { } ScriptObjectV8Proxy::~ScriptObjectV8Proxy() { + for (auto signal : _signalInstances) { + if (signal) { + signal->disconnectAllScriptSignalProxies(); + } + } + _wasDestroyed = true; if (_ownsObject) { auto isolate = _engine->getIsolate(); v8::Locker locker(isolate); @@ -231,7 +267,8 @@ void ScriptObjectV8Proxy::investigate() { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(_engine->getIsolate()); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); const QMetaObject* metaObject = qobject->metaObject(); @@ -348,7 +385,7 @@ void ScriptObjectV8Proxy::investigate() { } } - v8::Local v8Object = objectTemplate->NewInstance(_engine->getContext()).ToLocalChecked(); + v8::Local v8Object = objectTemplate->NewInstance(context).ToLocalChecked(); v8Object->SetAlignedPointerInInternalField(0, const_cast(internalPointsToQObjectProxy)); v8Object->SetAlignedPointerInInternalField(1, reinterpret_cast(this)); @@ -366,7 +403,7 @@ void ScriptObjectV8Proxy::investigate() { for (auto i = _methods.begin(); i != _methods.end(); i++) { V8ScriptValue method = ScriptMethodV8Proxy::newMethod(_engine, qobject, V8ScriptValue(_engine, v8Object), i.value().methods, i.value().numMaxParams); - if(!propertiesObject->Set(_engine->getContext(), v8::String::NewFromUtf8(isolate, i.value().name.toStdString().c_str()).ToLocalChecked(), method.get()).FromMaybe(false)) { + if(!propertiesObject->Set(context, v8::String::NewFromUtf8(isolate, i.value().name.toStdString().c_str()).ToLocalChecked(), method.get()).FromMaybe(false)) { Q_ASSERT(false); } } @@ -555,7 +592,7 @@ v8::Local ScriptObjectV8Proxy::getPropertyNames() { v8::Isolate::Scope isolateScope(isolate); v8::EscapableHandleScope handleScope(_engine->getIsolate()); auto context = _engine->getContext(); - v8::Context::Scope contextScope(_engine->getContext()); + v8::Context::Scope contextScope(context); //V8TODO: this is really slow. It could be cached if this is called often. v8::Local properties = v8::Array::New(isolate, _props.size() + _methods.size() + _signals.size()); @@ -587,7 +624,8 @@ V8ScriptValue ScriptObjectV8Proxy::property(const V8ScriptValue& object, const V v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); QObject* qobject = _object; if (!qobject) { _engine->getIsolate()->ThrowError("Referencing deleted native object"); @@ -621,7 +659,7 @@ V8ScriptValue ScriptObjectV8Proxy::property(const V8ScriptValue& object, const V } } //V8TODO: is new method created during every call? It needs to be cached instead v8::Local property; - if(_v8Object.Get(isolate)->GetInternalField(2).As()->Get(_engine->getContext(), name.constGet()).ToLocal(&property)) { + if(_v8Object.Get(isolate)->GetInternalField(2).As()->Get(context, name.constGet()).ToLocal(&property)) { if (!property->IsUndefined()) { return V8ScriptValue(_engine, property); } @@ -698,9 +736,10 @@ ScriptVariantV8Proxy::ScriptVariantV8Proxy(ScriptEngineV8* engine, const QVarian v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(engine->getContext()); + auto context = engine->getContext(); + v8::Context::Scope contextScope(context); auto variantDataTemplate = _engine->getVariantDataTemplate(); - auto variantData = variantDataTemplate->NewInstance(engine->getContext()).ToLocalChecked(); + auto variantData = variantDataTemplate->NewInstance(context).ToLocalChecked(); variantData->SetAlignedPointerInInternalField(0, const_cast(internalPointsToQVariantInProxy)); // Internal field doesn't point directly to QVariant, because then alignment would need to be guaranteed in all compilers variantData->SetAlignedPointerInInternalField(1, reinterpret_cast(this)); @@ -723,7 +762,8 @@ V8ScriptValue ScriptVariantV8Proxy::newVariant(ScriptEngineV8* engine, const QVa v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(engine->getContext()); + auto context = engine->getContext(); + v8::Context::Scope contextScope(context); ScriptObjectV8Proxy* protoProxy = ScriptObjectV8Proxy::unwrapProxy(proto); if (!protoProxy) { Q_ASSERT(protoProxy); @@ -734,7 +774,7 @@ V8ScriptValue ScriptVariantV8Proxy::newVariant(ScriptEngineV8* engine, const QVa auto proxy = new ScriptVariantV8Proxy(engine, variant, proto, protoProxy); auto variantProxyTemplate = engine->getVariantProxyTemplate(); - auto variantProxy = variantProxyTemplate->NewInstance(engine->getContext()).ToLocalChecked(); + auto variantProxy = variantProxyTemplate->NewInstance(context).ToLocalChecked(); variantProxy->SetAlignedPointerInInternalField(0, const_cast(internalPointsToQVariantProxy)); variantProxy->SetAlignedPointerInInternalField(1, reinterpret_cast(proxy)); return V8ScriptValue(engine, variantProxy); @@ -912,12 +952,13 @@ V8ScriptValue ScriptMethodV8Proxy::newMethod(ScriptEngineV8* engine, QObject* ob v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(engine->getContext()); + auto context = engine->getContext(); + v8::Context::Scope contextScope(context); auto methodDataTemplate = engine->getMethodDataTemplate(); - auto methodData = methodDataTemplate->NewInstance(engine->getContext()).ToLocalChecked(); + auto methodData = methodDataTemplate->NewInstance(context).ToLocalChecked(); methodData->SetAlignedPointerInInternalField(0, const_cast(internalPointsToMethodProxy)); methodData->SetAlignedPointerInInternalField(1, reinterpret_cast(new ScriptMethodV8Proxy(engine, object, lifetime, metas, numMaxParams))); - auto v8Function = v8::Function::New(engine->getContext(), callback, methodData, numMaxParams).ToLocalChecked(); + auto v8Function = v8::Function::New(context, callback, methodData, numMaxParams).ToLocalChecked(); return V8ScriptValue(engine, v8Function); } @@ -964,7 +1005,8 @@ void ScriptMethodV8Proxy::call(const v8::FunctionCallbackInfo& argume v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); ContextScopeV8 contextScopeV8(_engine); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); QObject* qobject = _object; if (!qobject) { isolate->ThrowError("Referencing deleted native object"); @@ -1027,7 +1069,7 @@ void ScriptMethodV8Proxy::call(const v8::FunctionCallbackInfo& argume } else { qVarArgLists[i].append(varArgVal); const QVariant& converted = qVarArgLists[i].back(); - conversionPenaltyScore = _engine->computeCastPenalty(V8ScriptValue(_engine, argVal), methodArgTypeId); + conversionPenaltyScore += _engine->computeCastPenalty(V8ScriptValue(_engine, argVal), methodArgTypeId); // a lot of type conversion assistance thanks to https://stackoverflow.com/questions/28457819/qt-invoke-method-with-qvariant // A const_cast is needed because calling data() would detach the QVariant. @@ -1057,7 +1099,7 @@ void ScriptMethodV8Proxy::call(const v8::FunctionCallbackInfo& argume if (isValidMetaSelected) { // V8TODO: is this the correct wrapper? - ScriptContextV8Wrapper ourContext(_engine, &arguments, _engine->getContext(), + ScriptContextV8Wrapper ourContext(_engine, &arguments, context, _engine->currentContext()->parentContext()); ScriptContextGuard guard(&ourContext); const QMetaMethod& meta = _metas[bestMeta]; @@ -1153,16 +1195,21 @@ ScriptSignalV8Proxy::ScriptSignalV8Proxy(ScriptEngineV8* engine, QObject* object v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); _objectLifetime.Reset(isolate, lifetime.get()); _objectLifetime.SetWeak(this, weakHandleCallback, v8::WeakCallbackType::kParameter); - _v8Context.Reset(isolate, _engine->getContext()); + _v8Context.Reset(isolate, context); _engine->_signalProxySetLock.lockForWrite(); _engine->_signalProxySet.insert(this); _engine->_signalProxySetLock.unlock(); } ScriptSignalV8Proxy::~ScriptSignalV8Proxy() { + if (!_cleanup) { + disconnectAllScriptSignalProxies(); + } + auto isolate = _engine->getIsolate(); v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); @@ -1262,13 +1309,20 @@ int ScriptSignalV8Proxy::qt_metacall(QMetaObject::Call call, int id, void** argu } v8::TryCatch tryCatch(isolate); - callback->Call(functionContext, v8This, numArgs, args); + auto maybeResult = callback->Call(functionContext, v8This, numArgs, args); + Q_UNUSED(maybeResult); // Signals don't have return values if (tryCatch.HasCaught()) { QString errorMessage(QString("Signal proxy ") + fullName() + " connection call failed: \"" + _engine->formatErrorMessageFromTryCatch(tryCatch) + "\nThis provided: " + QString::number(conn.thisValue.get()->IsObject())); + v8::Local exceptionMessage = tryCatch.Message(); + int errorLineNumber = -1; + if (!exceptionMessage.IsEmpty()) { + errorLineNumber = exceptionMessage->GetLineNumber(context).FromJust(); + } if (_engine->_manager) { - _engine->_manager->scriptErrorMessage(errorMessage); + _engine->_manager->scriptErrorMessage(errorMessage, getFileNameFromTryCatch(tryCatch, isolate, context), + errorLineNumber); } else { qDebug(scriptengine_v8) << errorMessage; } @@ -1313,7 +1367,8 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); QObject* qobject = _object; if (!qobject) { isolate->ThrowError("Referencing deleted native object"); @@ -1362,7 +1417,7 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { v8::Local destData; // V8TODO: I'm not sure which context to use here //auto destFunctionContext = destFunction->CreationContext(); - auto destFunctionContext = _engine->getContext(); + auto destFunctionContext = context; Q_ASSERT(thisObject().isObject()); V8ScriptValue v8ThisObject = ScriptValueV8Wrapper::fullUnwrap(_engine, thisObject()); Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject)); @@ -1387,18 +1442,23 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)); // For debugging ScriptSignalV8Proxy* entryProxy = dynamic_cast(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)->toQObject()); - Q_ASSERT(thisProxy); + Q_ASSERT(entryProxy); qCDebug(scriptengine_v8) << "ScriptSignalV8Proxy::connect: entry proxy: " << entryProxy->fullName(); } if (!newArray->Set(destFunctionContext, idx, entry).FromMaybe(false)) { Q_ASSERT(false); } + if (entry->StrictEquals(v8ThisObject.get())) { + foundIt = true; + } } - if (!newArray->Set(destFunctionContext, length, v8ThisObject.get()).FromMaybe(false)) { - Q_ASSERT(false); - } - if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) { - Q_ASSERT(false); + if (!foundIt) { + if (!newArray->Set(destFunctionContext, length, v8ThisObject.get()).FromMaybe(false)) { + Q_ASSERT(false); + } + if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) { + Q_ASSERT(false); + } } } else { v8::Local newArray = v8::Array::New(isolate, 1); @@ -1428,14 +1488,17 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { QObject* qobject = _object; v8::Isolate *isolate = _engine->getIsolate(); - if (!qobject) { - isolate->ThrowError("Referencing deleted native object"); - return; + if (!_cleanup) { + if (!qobject) { + isolate->ThrowError("Referencing deleted native object"); + return; + } } v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); // untangle the arguments V8ScriptValue callback(_engine, v8::Null(isolate)); @@ -1480,15 +1543,11 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { v8::Local destDataName = v8::String::NewFromUtf8(isolate, "__data__").ToLocalChecked(); v8::Local destData; - //auto destFunctionContext = destFunction->CreationContext(); - auto destFunctionContext = _engine->getContext(); - Q_ASSERT(thisObject().isObject()); - V8ScriptValue v8ThisObject = ScriptValueV8Wrapper::fullUnwrap(_engine, thisObject()); - Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject)); - // For debugging - ScriptSignalV8Proxy* thisProxy = dynamic_cast(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject)->toQObject()); - Q_ASSERT(thisProxy); - //qCDebug(scriptengine_v8) << "ScriptSignalV8Proxy::disconnect: " << thisProxy->fullName() << " fullName: " << fullName(); + auto destFunctionContext = context; + ScriptEngine::QObjectWrapOptions options = ScriptEngine::ExcludeSuperClassContents | + ScriptEngine::PreferExistingWrapperObject; + // It's not necessarily new, newQObject looks for it first in object wrapper map, and for already existing signal it should be found there + V8ScriptValue v8ThisObject = ScriptObjectV8Proxy::newQObject(_engine, this, ScriptEngine::ScriptOwnership, options); if (!destFunction->Get(destFunctionContext, destDataName).ToLocal(&destData)) { Q_ASSERT(false); } @@ -1496,24 +1555,18 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { v8::Local destArray = v8::Local::Cast(destData); int length = destArray->Length(); v8::Local newArray = v8::Array::New(isolate, length - 1); - bool foundIt = false; + int findCounter = 0; int newIndex = 0; for (int idx = 0; idx < length; ++idx) { v8::Local entry = destArray->Get(destFunctionContext, idx).ToLocalChecked(); // For debugging: { - //qCDebug(scriptengine_v8) << "ScriptSignalV8Proxy::disconnect: entry details: " << _engine->scriptValueDebugDetailsV8(V8ScriptValue(_engine, entry)) - // << " Array: " << _engine->scriptValueDebugDetailsV8(V8ScriptValue(_engine, destArray)); Q_ASSERT(entry->IsObject()); V8ScriptValue v8EntryObject(_engine, entry); Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)); - // For debugging - //ScriptSignalV8Proxy* entryProxy = dynamic_cast(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)->toQObject()); - Q_ASSERT(thisProxy); - //qCDebug(scriptengine_v8) << "ScriptSignalV8Proxy::disconnect: entry proxy: " << entryProxy->fullName(); } if (entry->StrictEquals(v8ThisObject.get())) { - foundIt = true; + findCounter++; } else { if (!newArray->Set(destFunctionContext, newIndex, entry).FromMaybe(false)) { Q_ASSERT(false); @@ -1521,7 +1574,7 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { newIndex++; } } - Q_ASSERT(foundIt); + Q_ASSERT(findCounter == 1); if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) { Q_ASSERT(false); } @@ -1532,8 +1585,30 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { // inform Qt that we're no longer connected to this signal if (_connections.empty()) { Q_ASSERT(_isConnected); - bool result = QMetaObject::disconnect(qobject, _meta.methodIndex(), this, _metaCallId); - Q_ASSERT(result); + // During cleanup qobject might be null since it might have been already deleted + if (!_cleanup || (_cleanup && qobject)) { + Q_ASSERT(qobject); + bool result = QMetaObject::disconnect(qobject, _meta.methodIndex(), this, _metaCallId); + Q_ASSERT(result); + } _isConnected = false; } } + +void ScriptSignalV8Proxy::disconnectAllScriptSignalProxies() { + _cleanup = true; + QList connections; + withReadLock([&]{ + connections = _connections; + }); + + for (auto &connection : connections) { + ScriptValue thisValue(new ScriptValueV8Wrapper(_engine, connection.thisValue)); + ScriptValue callback(new ScriptValueV8Wrapper(_engine, connection.callback)); + disconnect(thisValue, callback); + } +} + +void ScriptSignalV8Proxy::disconnectAll() { + QObject::disconnect(this, nullptr, nullptr, nullptr); +} diff --git a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h index 8c7011daf1..9e3445ca98 100644 --- a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h +++ b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h @@ -133,6 +133,7 @@ private: // storage QPointer _object; // Handle for its own object v8::Persistent _v8Object; + bool _wasDestroyed{false}; Q_DISABLE_COPY(ScriptObjectV8Proxy) }; @@ -271,7 +272,10 @@ public: // API QString fullName() const; // Disconnects all signals from the proxy - void disconnectAll() { QObject::disconnect(this, nullptr, nullptr, nullptr); }; + void disconnectAll(); + + // This should be called only just before destruction of ScriptSignalV8Proxy + void disconnectAllScriptSignalProxies(); private: // storage @@ -283,6 +287,9 @@ private: // storage const int _metaCallId; ConnectionList _connections; bool _isConnected{ false }; + + // This allows skipping qobject check during disconnect, which is needed during cleanup because qobject is already deleted + bool _cleanup{ false }; // Context in which it was created v8::UniquePersistent _v8Context; // Call counter for debugging purposes. It can be used to determine which signals are overwhelming script engine. diff --git a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp index fc329c9e19..7f9faf21f2 100644 --- a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp +++ b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp @@ -95,16 +95,22 @@ ScriptValue ScriptValueV8Wrapper::call(const ScriptValue& thisObject, const Scri if (v8This.get()->IsObject()) { recv = v8This.get(); }else{ - recv = _engine->getContext()->Global(); + recv = context->Global(); } lock.lockForRead(); - auto maybeResult = v8Function->Call(_engine->getContext(), recv, args.length(), v8Args); + auto maybeResult = v8Function->Call(context, recv, args.length(), v8Args); lock.unlock(); if (tryCatch.HasCaught()) { QString errorMessage(QString("Function call failed: \"") + _engine->formatErrorMessageFromTryCatch(tryCatch)); if (_engine->_manager) { - _engine->_manager->scriptErrorMessage(errorMessage); + v8::Local exceptionMessage = tryCatch.Message(); + int errorLineNumber = -1; + if (!exceptionMessage.IsEmpty()) { + errorLineNumber = exceptionMessage->GetLineNumber(context).FromJust(); + } + _engine->_manager->scriptErrorMessage(errorMessage, getFileNameFromTryCatch(tryCatch, isolate, context), + errorLineNumber); } else { qDebug(scriptengine_v8) << errorMessage; } @@ -114,9 +120,10 @@ ScriptValue ScriptValueV8Wrapper::call(const ScriptValue& thisObject, const Scri if (maybeResult.ToLocal(&result)) { return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine, result))); } else { - QString errorMessage("JS function call failed: " + _engine->currentContext()->backtrace().join("\n")); + auto currentContext = _engine->currentContext(); + QString errorMessage("JS function call failed: " + currentContext->backtrace().join("\n")); if (_engine->_manager) { - _engine->_manager->scriptErrorMessage(errorMessage); + _engine->_manager->scriptErrorMessage(errorMessage, currentContext->currentFileName(), currentContext->currentLineNumber()); } else { qDebug(scriptengine_v8) << errorMessage; } @@ -156,7 +163,8 @@ ScriptValue ScriptValueV8Wrapper::construct(const ScriptValueList& args) { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); Q_ASSERT(args.length() <= Q_METAMETHOD_INVOKE_MAX_ARGS); v8::Local v8Args[Q_METAMETHOD_INVOKE_MAX_ARGS]; int argIndex = 0; @@ -174,7 +182,7 @@ ScriptValue ScriptValueV8Wrapper::construct(const ScriptValueList& args) { // V8TODO: I'm not sure if this is correct, maybe use CallAsConstructor instead? // Maybe it's CallAsConstructor for function and NewInstance for class? lock.lockForRead(); - auto maybeResult = v8Function->NewInstance(_engine->getContext(), args.length(), v8Args); + auto maybeResult = v8Function->NewInstance(context, args.length(), v8Args); lock.unlock(); v8::Local result; if (maybeResult.ToLocal(&result)) { @@ -207,13 +215,14 @@ ScriptValue ScriptValueV8Wrapper::data() const { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); // Private properties are an experimental feature for now on V8, so we are using regular value for now if (_value.constGet()->IsObject()) { auto v8Object = v8::Local::Cast(_value.constGet()); v8::Local data; //bool createData = false; - if (!v8Object->Get(_engine->getContext(), v8::String::NewFromUtf8(isolate, "__data").ToLocalChecked()).ToLocal(&data)) { + if (!v8Object->Get(context, v8::String::NewFromUtf8(isolate, "__data").ToLocalChecked()).ToLocal(&data)) { data = v8::Undefined(isolate); Q_ASSERT(false); //createData = true; @@ -268,7 +277,8 @@ bool ScriptValueV8Wrapper::hasProperty(const QString& name) const { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); //V8TODO: does function return true on IsObject too? if (_value.constGet()->IsObject()) { //V8TODO: what about flags? @@ -276,7 +286,7 @@ bool ScriptValueV8Wrapper::hasProperty(const QString& name) const { v8::Local key = v8::String::NewFromUtf8(isolate, name.toStdString().c_str(),v8::NewStringType::kNormal).ToLocalChecked(); const v8::Local object = v8::Local::Cast(_value.constGet()); //V8TODO: Which context? - if (object->Get(_engine->getContext(), key).ToLocal(&resultLocal)) { + if (object->Get(context, key).ToLocal(&resultLocal)) { return true; } else { return false; @@ -292,7 +302,8 @@ ScriptValue ScriptValueV8Wrapper::property(const QString& name, const ScriptValu v8::Locker locker(_engine->getIsolate()); v8::Isolate::Scope isolateScope(_engine->getIsolate()); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); if (_value.constGet()->IsNullOrUndefined()) { return _engine->undefinedValue(); } @@ -303,14 +314,14 @@ ScriptValue ScriptValueV8Wrapper::property(const QString& name, const ScriptValu const v8::Local object = v8::Local::Cast(_value.constGet()); //V8TODO: Which context? lock.lockForRead(); - if (object->Get(_engine->getContext(), key).ToLocal(&resultLocal)) { + if (object->Get(context, key).ToLocal(&resultLocal)) { V8ScriptValue result(_engine, resultLocal); lock.unlock(); return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result))); } else { QString parentValueQString(""); v8::Local parentValueString; - if (_value.constGet()->ToDetailString(_engine->getContext()).ToLocal(&parentValueString)) { + if (_value.constGet()->ToDetailString(context).ToLocal(&parentValueString)) { QString(*v8::String::Utf8Value(isolate, parentValueString)); } qCDebug(scriptengine_v8) << "Failed to get property, parent of value: " << name << ", parent type: " << QString(*v8::String::Utf8Value(isolate, _value.constGet()->TypeOf(isolate))) << " parent value: " << parentValueQString; @@ -371,7 +382,8 @@ void ScriptValueV8Wrapper::setData(const ScriptValue& value) { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); V8ScriptValue unwrapped = fullUnwrap(value); // Private properties are an experimental feature for now on V8, so we are using regular value for now if (_value.constGet()->IsNullOrUndefined()) { @@ -380,7 +392,7 @@ void ScriptValueV8Wrapper::setData(const ScriptValue& value) { } if (_value.constGet()->IsObject()) { auto v8Object = v8::Local::Cast(_value.constGet()); - if( !v8Object->Set(_engine->getContext(), v8::String::NewFromUtf8(isolate, "__data").ToLocalChecked(), unwrapped.constGet()).FromMaybe(false)) { + if( !v8Object->Set(context, v8::String::NewFromUtf8(isolate, "__data").ToLocalChecked(), unwrapped.constGet()).FromMaybe(false)) { qCDebug(scriptengine_v8) << "ScriptValueV8Wrapper::data(): Data object couldn't be created"; Q_ASSERT(false); } @@ -396,7 +408,8 @@ void ScriptValueV8Wrapper::setProperty(const QString& name, const ScriptValue& v v8::Locker locker(_engine->getIsolate()); v8::Isolate::Scope isolateScope(_engine->getIsolate()); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); V8ScriptValue unwrapped = fullUnwrap(value); if (_value.constGet()->IsNullOrUndefined()) { qCDebug(scriptengine_v8) << "ScriptValueV8Wrapper::setProperty() was called on a value that is null or undefined"; @@ -415,7 +428,7 @@ void ScriptValueV8Wrapper::setProperty(const QString& name, const ScriptValue& v } else { v8::Local details; QString detailsString(""); - if(_value.get()->ToDetailString(_engine->getContext()).ToLocal(&details)) { + if(_value.get()->ToDetailString(context).ToLocal(&details)) { v8::String::Utf8Value utf8Value(isolate,details); detailsString = *utf8Value; } @@ -431,7 +444,8 @@ void ScriptValueV8Wrapper::setProperty(quint32 arrayIndex, const ScriptValue& va v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); V8ScriptValue unwrapped = fullUnwrap(value); if (_value.constGet()->IsNullOrUndefined()) { qCDebug(scriptengine_v8) << "ScriptValueV8Wrapper::setProperty() was called on a value that is null or undefined"; @@ -441,7 +455,7 @@ void ScriptValueV8Wrapper::setProperty(quint32 arrayIndex, const ScriptValue& va auto object = v8::Local::Cast(_value.get()); //V8TODO: I don't know which context to use here lock.lockForRead(); - v8::Maybe retVal(object->Set(_engine->getContext(), arrayIndex, unwrapped.constGet())); + v8::Maybe retVal(object->Set(context, arrayIndex, unwrapped.constGet())); lock.unlock(); if (retVal.IsJust() ? !retVal.FromJust() : true){ qCDebug(scriptengine_v8) << "Failed to set property"; @@ -531,9 +545,10 @@ qint32 ScriptValueV8Wrapper::toInt32() const { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); v8::Local integer; - if (!_value.constGet()->ToInteger(_engine->getContext()).ToLocal(&integer)) { + if (!_value.constGet()->ToInteger(context).ToLocal(&integer)) { Q_ASSERT(false); } return static_cast((integer)->Value()); @@ -544,9 +559,10 @@ double ScriptValueV8Wrapper::toInteger() const { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); v8::Local integer; - if (!_value.constGet()->ToInteger(_engine->getContext()).ToLocal(&integer)) { + if (!_value.constGet()->ToInteger(context).ToLocal(&integer)) { Q_ASSERT(false); } return (integer)->Value(); @@ -557,9 +573,10 @@ double ScriptValueV8Wrapper::toNumber() const { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); v8::Local number; - if (!_value.constGet()->ToNumber(_engine->getContext()).ToLocal(&number)) { + if (!_value.constGet()->ToNumber(context).ToLocal(&number)) { Q_ASSERT(false); } return number->Value(); @@ -581,9 +598,10 @@ quint16 ScriptValueV8Wrapper::toUInt16() const { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); v8::Local integer; - if (!_value.constGet()->ToUint32(_engine->getContext()).ToLocal(&integer)) { + if (!_value.constGet()->ToUint32(context).ToLocal(&integer)) { Q_ASSERT(false); } return static_cast(integer->Value()); @@ -594,9 +612,10 @@ quint32 ScriptValueV8Wrapper::toUInt32() const { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); v8::Local integer; - if (!_value.constGet()->ToUint32(_engine->getContext()).ToLocal(&integer)) { + if (!_value.constGet()->ToUint32(context).ToLocal(&integer)) { Q_ASSERT(false); } return integer->Value(); @@ -632,16 +651,17 @@ bool ScriptValueV8Wrapper::equals(const ScriptValue& other) const { v8::Locker locker(isolate); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(_engine->getContext()); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(context); ScriptValueV8Wrapper* unwrappedOther = unwrap(other); Q_ASSERT(_engine->getIsolate() == unwrappedOther->_engine->getIsolate()); if (!unwrappedOther) { return false; }else{ - if (_value.constGet()->Equals(_engine->getContext(), unwrappedOther->toV8Value().constGet()).IsNothing()) { + if (_value.constGet()->Equals(context, unwrappedOther->toV8Value().constGet()).IsNothing()) { return false; } else { - return _value.constGet()->Equals(_engine->getContext(), unwrappedOther->toV8Value().constGet()).FromJust(); + return _value.constGet()->Equals(context, unwrappedOther->toV8Value().constGet()).FromJust(); } } } @@ -670,7 +690,7 @@ bool ScriptValueV8Wrapper::isError() const { v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); auto context = _engine->getContext(); - v8::Context::Scope contextScope(_engine->getContext()); + v8::Context::Scope contextScope(context); v8::Local error; if (!context->Global()->Get(context, v8::String::NewFromUtf8(isolate, "Error").ToLocalChecked()).ToLocal(&error)) { Q_ASSERT(false); diff --git a/libraries/shared/src/BlendshapeConstants.h b/libraries/shared/src/BlendshapeConstants.h index 5dd1f0abbb..596e7df4ee 100644 --- a/libraries/shared/src/BlendshapeConstants.h +++ b/libraries/shared/src/BlendshapeConstants.h @@ -119,9 +119,9 @@ struct BlendshapeOffsetPacked { }; struct BlendshapeOffsetUnpacked { - glm::vec3 positionOffset; - glm::vec3 normalOffset; - glm::vec3 tangentOffset; + float positionOffsetX, positionOffsetY, positionOffsetZ; + float normalOffsetX, normalOffsetY, normalOffsetZ; + float tangentOffsetX, tangentOffsetY, tangentOffsetZ; }; using BlendshapeOffset = BlendshapeOffsetPacked; diff --git a/libraries/shared/src/PortableHighResolutionClock.h b/libraries/shared/src/PortableHighResolutionClock.h index 5650a27f3c..a13f23bf16 100644 --- a/libraries/shared/src/PortableHighResolutionClock.h +++ b/libraries/shared/src/PortableHighResolutionClock.h @@ -25,7 +25,9 @@ #if defined(_MSC_VER) && _MSC_VER < 1900 +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif #include // The following struct is not compliant with the HF coding standard, but uses underscores to match the classes diff --git a/libraries/shared/src/SettingHandle.cpp b/libraries/shared/src/SettingHandle.cpp index 88785e5700..2353f30933 100644 --- a/libraries/shared/src/SettingHandle.cpp +++ b/libraries/shared/src/SettingHandle.cpp @@ -18,6 +18,8 @@ Q_LOGGING_CATEGORY(settings_handle, "settings.handle") +const QString SETTINGS_FULL_PRIVATE_GROUP_NAME = "fullPrivate"; + const QString Settings::firstRun { "firstRun" }; diff --git a/libraries/shared/src/SettingHandle.h b/libraries/shared/src/SettingHandle.h index 2390063555..f8ba5f66ed 100644 --- a/libraries/shared/src/SettingHandle.h +++ b/libraries/shared/src/SettingHandle.h @@ -31,6 +31,15 @@ Q_DECLARE_LOGGING_CATEGORY(settings_handle) +/** + * @brief Name of the fully private settings group + * + * Settings in this group will be protected from reading and writing from script engines. + * + */ + +extern const QString SETTINGS_FULL_PRIVATE_GROUP_NAME; + /** * @brief QSettings analog * diff --git a/libraries/shared/src/SimpleMovingAverage.h b/libraries/shared/src/SimpleMovingAverage.h index 3855375f4c..53a71ba54f 100644 --- a/libraries/shared/src/SimpleMovingAverage.h +++ b/libraries/shared/src/SimpleMovingAverage.h @@ -48,8 +48,8 @@ private: template class MovingAverage { public: - MovingAverage() {} - MovingAverage(const MovingAverage& other) { + MovingAverage() {} + MovingAverage(const MovingAverage& other) { *this = other; } MovingAverage& operator=(const MovingAverage& other) { diff --git a/libraries/shared/src/shared/WebRTC.h b/libraries/shared/src/shared/WebRTC.h index 9f3c954b60..1ddccd8428 100644 --- a/libraries/shared/src/shared/WebRTC.h +++ b/libraries/shared/src/shared/WebRTC.h @@ -30,7 +30,9 @@ # define WEBRTC_DATA_CHANNELS 1 # define WEBRTC_WIN 1 # define NOMINMAX 1 +#ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN 1 +#endif #elif defined(Q_OS_ANDROID) // I don't yet have a working libwebrtc for android // # define WEBRTC_AUDIO 1 diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index 1af3ca0ba8..b9d41e3551 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -10,3 +10,7 @@ include_hifi_library_headers(controllers) # Required for some low level GL interaction in the OffscreenQMLSurface set(OpenGL_GL_PREFERENCE "GLVND") target_opengl() + +if (WIN32) + add_compile_definitions(_USE_MATH_DEFINES) +endif() \ No newline at end of file diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 474a8f467d..74734fdc43 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -279,6 +279,7 @@ bool QmlWindowClass::isVisible() { return quickItem->isVisible(); } else { qDebug() << "QmlWindowClass::isVisible: asQuickItem() returned NULL"; + return false; } } diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index b79a99b150..88c7329a4f 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -5,6 +5,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + #include "OffscreenQmlSurface.h" #include @@ -41,6 +42,7 @@ #include #include #include + #include #include #include diff --git a/pkg-scripts/make-rpm-server b/pkg-scripts/make-rpm-server index 146788329f..cf84bb97f2 100755 --- a/pkg-scripts/make-rpm-server +++ b/pkg-scripts/make-rpm-server @@ -1,7 +1,7 @@ #!/bin/sh # Copyright 2020-2021 Vircadia contributors. -# Copyright 2022-2023 Overte e.V. +# Copyright 2022-2024 Overte e.V. # SPDX-License-Identifier: Apache-2.0 if [ "$OVERTE" = "" ]; then @@ -55,7 +55,7 @@ DEPENDS=mesa-libGL,`ls \ | xargs -I {} sh -c 'objdump -p {} | grep NEEDED' \ | awk '{print $2}' \ | sort | uniq \ - | egrep -v "^($SOFILES)$" \ + | grep -E -v "^($SOFILES)$" \ | grep -v ^libGL \ | xargs -I {} sh -c "ldconfig -p | grep {} | tr ' ' '\n' | grep /" \ | xargs rpm -qf --queryformat "%{NAME}\n" \ @@ -73,7 +73,7 @@ DEPENDS=mesa-libGL,`ls \ | xargs -I {} sh -c 'objdump -p {} | grep NEEDED' \ | awk '{print $2}' \ | sort | uniq \ - | egrep -v "^($SOFILES)$" \ + | grep -E -v "^($SOFILES)$" \ | grep -v ^libGL \ | xargs -I {} sh -c "ldconfig -p | grep {} | tr ' ' '\n' | grep /" \ | xargs rpm -qf --queryformat "%{NAME}\n" \ diff --git a/pkg-scripts/overte-server.spec b/pkg-scripts/overte-server.spec index 2857526295..cd542533b9 100644 --- a/pkg-scripts/overte-server.spec +++ b/pkg-scripts/overte-server.spec @@ -1,5 +1,5 @@ # Copyright 2020-2021 Vircadia contributors. -# Copyright 2022 Overte e.V. +# Copyright 2022-2024 Overte e.V. # SPDX-License-Identifier: Apache-2.0 #OVERTE=~/Overte rpmbuild --target x86_64 -bb overte-server.spec @@ -75,11 +75,13 @@ chrpath -d $RPM_BUILD_ROOT/opt/overte/plugins/*.so chrpath -d $RPM_BUILD_ROOT/opt/overte/plugins/*/*.so strip --strip-all $RPM_BUILD_ROOT/opt/overte/plugins/*.so strip --strip-all $RPM_BUILD_ROOT/opt/overte/plugins/*/*.so +install -d $RPM_BUILD_ROOT/usr/share/licenses/overte-server +cp $OVERTE/LICENSE $RPM_BUILD_ROOT/usr/share/licenses/overte-server/LICENSE find $RPM_BUILD_ROOT/opt/overte/resources -name ".gitignore" -delete %files -%license $OVERTE/LICENSE +%license /usr/share/licenses/overte-server/LICENSE /opt/overte /usr/lib/systemd/system diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 6f2bd56212..c68abefa77 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,9 +1,10 @@ # # Created by Bradley Austin Davis on 2015/10/25 # Copyright 2015 High Fidelity, Inc. +# Copyright 2023-2024 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 +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # # add the plugin directories @@ -12,13 +13,17 @@ list(REMOVE_ITEM PLUGIN_SUBDIRS "CMakeFiles") # client-side plugins if (NOT SERVER_ONLY AND NOT ANDROID) + if (WIN32 AND (NOT USE_GLES)) + set(DIR "oculus") + add_subdirectory(${DIR}) + set(DIR "oculusLegacy") + add_subdirectory(${DIR}) + endif() + if (NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") - set(DIR "oculus") - add_subdirectory(${DIR}) + # Note: OpenVR is a Steam thing, which is different from OVR, which is an Oculus SDK component. set(DIR "openvr") add_subdirectory(${DIR}) - set(DIR "oculusLegacy") - add_subdirectory(${DIR}) endif() set(DIR "hifiSdl2") @@ -31,8 +36,12 @@ if (NOT SERVER_ONLY AND NOT ANDROID) set(DIR "hifiSpacemouse") add_subdirectory(${DIR}) - set(DIR "hifiNeuron") - add_subdirectory(${DIR}) + + if (USE_NEURON) + set(DIR "hifiNeuron") + add_subdirectory(${DIR}) + endif() + set(DIR "hifiKinect") add_subdirectory(${DIR}) diff --git a/plugins/JSAPIExample/src/JSAPIExample.cpp b/plugins/JSAPIExample/src/JSAPIExample.cpp index ad23838e04..02b59ba412 100644 --- a/plugins/JSAPIExample/src/JSAPIExample.cpp +++ b/plugins/JSAPIExample/src/JSAPIExample.cpp @@ -165,7 +165,7 @@ namespace REPLACE_ME_WITH_UNIQUE_NAME { * settings = null; // optional best pratice; allows the object to be reclaimed ASAP by the JS garbage collector */ ScriptValue getScopedSettings(const QString& scope) { - Q_ASSERT(engine); + Q_ASSERT(engine()); auto engine = Scriptable::engine(); if (!engine) { return ScriptValue(); diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index 941109c205..84167882ef 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -8,7 +8,7 @@ # SPDX-License-Identifier: Apache-2.0 # -if (WIN32 AND (NOT USE_GLES) AND (MSVC_VERSION LESS 1930) ) +if (WIN32 AND (NOT USE_GLES)) # if we were passed an Oculus App ID for entitlement checks, send that along if (DEFINED ENV{OCULUS_APP_ID}) diff --git a/plugins/steamClient/src/SteamAPIPlugin.cpp b/plugins/steamClient/src/SteamAPIPlugin.cpp index 853a53e881..b6e5b2ea77 100644 --- a/plugins/steamClient/src/SteamAPIPlugin.cpp +++ b/plugins/steamClient/src/SteamAPIPlugin.cpp @@ -73,7 +73,7 @@ HAuthTicket SteamTicketRequests::startRequest(TicketRequestCallback callback) { uint32 ticketSize { 0 }; char ticket[MAX_TICKET_SIZE]; - auto authTicket = SteamUser()->GetAuthSessionTicket(ticket, MAX_TICKET_SIZE, &ticketSize); + auto authTicket = SteamUser()->GetAuthSessionTicket(ticket, MAX_TICKET_SIZE, &ticketSize, NULL); qDebug() << "Got Steam auth session ticket:" << authTicket; if (authTicket == k_HAuthTicketInvalid) { @@ -282,7 +282,8 @@ void SteamAPIPlugin::runCallbacks() { return; } - Steam_RunCallbacks(steamPipe, false); + //Steam_RunCallbacks(steamPipe, false); + SteamAPI_RunCallbacks(); } void SteamAPIPlugin::requestTicket(TicketRequestCallback callback) { diff --git a/prebuild.py b/prebuild.py index 79a29cc198..4a567989b5 100644 --- a/prebuild.py +++ b/prebuild.py @@ -1,19 +1,19 @@ #!python -# The prebuild script is intended to simplify life for developers and dev-ops. It's repsonsible for acquiring -# tools required by the build as well as dependencies on which we rely. -# +# The prebuild script is intended to simplify life for developers and dev-ops. It's repsonsible for acquiring +# tools required by the build as well as dependencies on which we rely. +# # By using this script, we can reduce the requirements for a developer getting started to: # # * A working C++ dev environment like visual studio, xcode, gcc, or clang -# * Qt +# * Qt # * CMake # * Python 3.x # # The function of the build script is to acquire, if not already present, all the other build requirements -# The build script should be idempotent. If you run it with the same arguments multiple times, that should -# have no negative impact on the subsequent build times (i.e. re-running the prebuild script should not -# trigger a header change that causes files to be rebuilt). Subsequent runs after the first run should +# The build script should be idempotent. If you run it with the same arguments multiple times, that should +# have no negative impact on the subsequent build times (i.e. re-running the prebuild script should not +# trigger a header change that causes files to be rebuilt). Subsequent runs after the first run should # execute quickly, determining that no work is to be done import hifi_singleton @@ -83,6 +83,10 @@ def parse_args(): parser.add_argument('--build-root', required=True, type=str, help='The location of the cmake build') parser.add_argument('--ports-path', type=str, default=defaultPortsPath) parser.add_argument('--ci-build', action='store_true', default=os.getenv('CI_BUILD') is not None) + parser.add_argument('--get-vcpkg-id', action='store_true', help='Get the VCPKG ID, the hash path of the full VCPKG path') + parser.add_argument('--get-vcpkg-path', action='store_true', help='Get the full VCPKG path, ID included.') + parser.add_argument('--quiet', action='store_true', default=False, help='Quiet mode with less output') + if True: args = parser.parse_args() else: @@ -91,7 +95,7 @@ def parse_args(): def main(): # Fixup env variables. Leaving `USE_CCACHE` on will cause scribe to fail to build - # VCPKG_ROOT seems to cause confusion on Windows systems that previously used it for + # VCPKG_ROOT seems to cause confusion on Windows systems that previously used it for # building OpenSSL removeEnvVars = ['VCPKG_ROOT', 'USE_CCACHE'] for var in removeEnvVars: @@ -99,13 +103,31 @@ def main(): del os.environ[var] args = parse_args() - assets_url = hifi_utils.readEnviromentVariableFromFile(args.build_root, 'EXTERNAL_BUILD_ASSETS') + + if args.get_vcpkg_id or args.get_vcpkg_path: + # These arguments need quiet mode to avoid confusing scripts that use them. + args.quiet = True + + if not args.quiet: + print(sys.argv) if args.ci_build: logging.basicConfig(datefmt='%H:%M:%S', format='%(asctime)s %(guid)s %(message)s', level=logging.INFO) logger.info('start') + pm = hifi_vcpkg.VcpkgRepo(args) + + if args.get_vcpkg_id: + print(pm.id) + exit(0) + + if args.get_vcpkg_path: + print(pm.path) + exit(0) + + assets_url = hifi_utils.readEnviromentVariableFromFile(args.build_root, 'EXTERNAL_BUILD_ASSETS') + # OS dependent information system = platform.system() if 'Windows' == system and 'CI_BUILD' in os.environ and os.environ["CI_BUILD"] == "Github": @@ -129,11 +151,12 @@ def main(): qt.writeConfig() else: if (os.environ["OVERTE_USE_SYSTEM_QT"]): - print("System Qt selected") + if not args.quiet: + print("System Qt selected") + else: raise Exception("Internal error: System Qt not selected, but hifi_qt.py failed to return a cmake path") - pm = hifi_vcpkg.VcpkgRepo(args) if qtInstallPath is not None: pm.writeVar('QT_CMAKE_PREFIX_PATH', qtInstallPath) @@ -149,7 +172,7 @@ def main(): if not pm.upToDate(): pm.bootstrap() - # Always write the tag, even if we changed nothing. This + # Always write the tag, even if we changed nothing. This # allows vcpkg to reclaim disk space by identifying directories with # tags that haven't been touched in a long time pm.writeTag() @@ -190,7 +213,7 @@ def main(): logger.info('end') -print(sys.argv) + try: main() except hifi_utils.SilentFatalError as fatal_ex: diff --git a/scripts/communityScripts/chat/FloofChat.html b/scripts/communityScripts/chat/FloofChat.html index b2576ceba6..48be283f93 100644 --- a/scripts/communityScripts/chat/FloofChat.html +++ b/scripts/communityScripts/chat/FloofChat.html @@ -103,7 +103,7 @@ } for (var i = 0; i < messageParts.length; i++) { - messageFormatted.push(replaceFormatting(messageParts[i])); + messageFormatted.push(messageParts[i]); } for (var i = 0; i < messageFormatted.length; i++) { diff --git a/scripts/communityScripts/chat/FloofChat.js b/scripts/communityScripts/chat/FloofChat.js index 84b18860b4..28abeed7b9 100644 --- a/scripts/communityScripts/chat/FloofChat.js +++ b/scripts/communityScripts/chat/FloofChat.js @@ -127,7 +127,7 @@ function connectWebSocket(timeout) { if (!muted["Grid"]) { Messages.sendLocalMessage(FLOOF_NOTIFICATION_CHANNEL, JSON.stringify({ sender: "(G) " + cmd.displayName, - text: replaceFormatting(cmd.message), + text: cmd.message, colour: {text: cmd.colour} })); } @@ -462,7 +462,7 @@ function messageReceived(channel, message, sender) { if (!muted["Local"]) { Messages.sendLocalMessage(FLOOF_NOTIFICATION_CHANNEL, JSON.stringify({ sender: "(L) " + cmd.displayName, - text: replaceFormatting(cmd.message), + text: cmd.message, colour: {text: cmd.colour} })); } @@ -477,7 +477,7 @@ function messageReceived(channel, message, sender) { if (!muted["Domain"]) { Messages.sendLocalMessage(FLOOF_NOTIFICATION_CHANNEL, JSON.stringify({ sender: "(D) " + cmd.displayName, - text: replaceFormatting(cmd.message), + text: cmd.message, colour: {text: cmd.colour} })); } @@ -491,7 +491,7 @@ function messageReceived(channel, message, sender) { if (!muted["Grid"]) { Messages.sendLocalMessage(FLOOF_NOTIFICATION_CHANNEL, JSON.stringify({ sender: "(G) " + cmd.displayName, - text: replaceFormatting(cmd.message), + text: cmd.message, colour: {text: cmd.colour} })); } @@ -504,7 +504,7 @@ function messageReceived(channel, message, sender) { Messages.sendLocalMessage(FLOOF_NOTIFICATION_CHANNEL, JSON.stringify({ sender: cmd.displayName, - text: replaceFormatting(cmd.message), + text: cmd.message, colour: {text: cmd.colour} })); } @@ -528,7 +528,7 @@ function messageReceived(channel, message, sender) { Messages.sendLocalMessage(FLOOF_NOTIFICATION_CHANNEL, JSON.stringify({ sender: "(" + cmd.category + ")", - text: replaceFormatting(cmd.message), + text: cmd.message, colour: {text: cmd.colour} })); } diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index c3b432cea4..1cbadadb67 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -47,7 +47,7 @@ var DEFAULT_SCRIPTS_SEPARATE = [ "communityScripts/notificationCore/notificationCore.js", "simplifiedUI/ui/simplifiedNametag/simplifiedNametag.js", {"stable": "system/more/app-more.js", "beta": "https://more.overte.org/more/app-more.js"}, - {"stable": "communityScripts/chat/FloofChat.js", "beta": "https://content.fluffy.ws/scripts/chat/FloofChat.js"} + "communityScripts/chat/FloofChat.js", //"system/chat.js" ]; diff --git a/scripts/simplifiedUI/ui/simplifiedNametag/resources/modules/pickRayController.js b/scripts/simplifiedUI/ui/simplifiedNametag/resources/modules/pickRayController.js index 87d05fa838..fa9479b426 100644 --- a/scripts/simplifiedUI/ui/simplifiedNametag/resources/modules/pickRayController.js +++ b/scripts/simplifiedUI/ui/simplifiedNametag/resources/modules/pickRayController.js @@ -12,6 +12,9 @@ var _this; + +var controllerStandard = Controller.Standard; + function PickRayController(){ _this = this; @@ -36,9 +39,9 @@ function PickRayController(){ // Returns the right UUID based on hand triggered function getUUIDFromLaser(hand) { - hand = hand === Controller.Standard.LeftHand - ? Controller.Standard.LeftHand - : Controller.Standard.RightHand; + hand = hand === controllerStandard.LeftHand + ? controllerStandard.LeftHand + : controllerStandard.RightHand; var pose = getControllerWorldLocation(hand); var start = pose.position; @@ -61,7 +64,7 @@ function getGrabPointSphereOffset(handController) { // x = upward, y = forward, z = lateral var GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 }; var offset = GRAB_POINT_SPHERE_OFFSET; - if (handController === Controller.Standard.LeftHand) { + if (handController === controllerStandard.LeftHand) { offset = { x: -GRAB_POINT_SPHERE_OFFSET.x, y: GRAB_POINT_SPHERE_OFFSET.y, @@ -84,7 +87,7 @@ function getControllerWorldLocation(handController, doOffset) { valid = pose.valid; var controllerJointIndex; if (pose.valid) { - if (handController === Controller.Standard.RightHand) { + if (handController === controllerStandard.RightHand) { controllerJointIndex = MyAvatar.getJointIndex("_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND"); } else { controllerJointIndex = MyAvatar.getJointIndex("_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); @@ -192,21 +195,21 @@ function doublePressHandler(event) { function create(){ _this.mapping = Controller.newMapping(_this.mappingName); - _this.mapping.from(Controller.Standard.LTClick).to(function (value) { + _this.mapping.from(controllerStandard.LTClick).to(function (value) { if (value === 0) { return; } - getUUIDFromLaser(Controller.Standard.LeftHand); + getUUIDFromLaser(controllerStandard.LeftHand); }); - _this.mapping.from(Controller.Standard.RTClick).to(function (value) { + _this.mapping.from(controllerStandard.RTClick).to(function (value) { if (value === 0) { return; } - getUUIDFromLaser(Controller.Standard.RightHand); + getUUIDFromLaser(controllerStandard.RightHand); }); return _this; diff --git a/scripts/simplifiedUI/ui/simplifiedUI.js b/scripts/simplifiedUI/ui/simplifiedUI.js index 2ce6a3e073..8023ce3318 100644 --- a/scripts/simplifiedUI/ui/simplifiedUI.js +++ b/scripts/simplifiedUI/ui/simplifiedUI.js @@ -7,6 +7,7 @@ // Authors: Wayne Chen & Zach Fox // Created: 2019-05-01 // Copyright 2019 High Fidelity, Inc. +// Copyright 2024 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 @@ -20,7 +21,7 @@ var DEFAULT_SCRIPTS_PATH_PREFIX = ScriptDiscoveryService.defaultScriptsPath + "/ var MENU_NAMES = ["File", "Edit", "Display", "View", "Navigate", "Settings", "Developer", "Help"]; -var keepMenusSetting = Settings.getValue("simplifiedUI/keepMenus", false); +var keepMenusSetting = Settings.getValue("simplifiedUI/keepMenus", true); function maybeRemoveDesktopMenu() { if (!keepMenusSetting) { MENU_NAMES.forEach(function(menu) { diff --git a/scripts/system/away.js b/scripts/system/away.js index 87273b2727..3f41020c9e 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -17,6 +17,8 @@ (function() { // BEGIN LOCAL_SCOPE +var controllerStandard = Controller.Standard; + var BASIC_TIMER_INTERVAL = 50; // 50ms = 20hz var OVERLAY_WIDTH = 1920; var OVERLAY_HEIGHT = 1080; @@ -344,20 +346,20 @@ var maybeIntervalTimer = Script.setInterval(function() { Controller.mousePressEvent.connect(goActive); // Note peek() so as to not interfere with other mappings. -eventMapping.from(Controller.Standard.LeftPrimaryThumb).peek().to(goActive); -eventMapping.from(Controller.Standard.RightPrimaryThumb).peek().to(goActive); -eventMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(goActive); -eventMapping.from(Controller.Standard.RightSecondaryThumb).peek().to(goActive); -eventMapping.from(Controller.Standard.LT).peek().to(goActive); -eventMapping.from(Controller.Standard.LB).peek().to(goActive); -eventMapping.from(Controller.Standard.LS).peek().to(goActive); -eventMapping.from(Controller.Standard.LeftGrip).peek().to(goActive); -eventMapping.from(Controller.Standard.RT).peek().to(goActive); -eventMapping.from(Controller.Standard.RB).peek().to(goActive); -eventMapping.from(Controller.Standard.RS).peek().to(goActive); -eventMapping.from(Controller.Standard.RightGrip).peek().to(goActive); -eventMapping.from(Controller.Standard.Back).peek().to(goActive); -eventMapping.from(Controller.Standard.Start).peek().to(goActive); +eventMapping.from(controllerStandard.LeftPrimaryThumb).peek().to(goActive); +eventMapping.from(controllerStandard.RightPrimaryThumb).peek().to(goActive); +eventMapping.from(controllerStandard.LeftSecondaryThumb).peek().to(goActive); +eventMapping.from(controllerStandard.RightSecondaryThumb).peek().to(goActive); +eventMapping.from(controllerStandard.LT).peek().to(goActive); +eventMapping.from(controllerStandard.LB).peek().to(goActive); +eventMapping.from(controllerStandard.LS).peek().to(goActive); +eventMapping.from(controllerStandard.LeftGrip).peek().to(goActive); +eventMapping.from(controllerStandard.RT).peek().to(goActive); +eventMapping.from(controllerStandard.RB).peek().to(goActive); +eventMapping.from(controllerStandard.RS).peek().to(goActive); +eventMapping.from(controllerStandard.RightGrip).peek().to(goActive); +eventMapping.from(controllerStandard.Back).peek().to(goActive); +eventMapping.from(controllerStandard.Start).peek().to(goActive); Controller.enableMapping(eventMappingName); function awayStateWhenFocusLostInVRChanged(enabled) { diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 16390a73bf..beee66221e 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -31,6 +31,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); (function() { Script.include("/~/system/libraries/pointersUtils.js"); + var controllerStandard = Controller.Standard; + var NEAR_MAX_RADIUS = 0.1; var NEAR_TABLET_MAX_RADIUS = 0.05; @@ -136,10 +138,10 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.dataGatherers = {}; this.dataGatherers.leftControllerLocation = function () { - return getControllerWorldLocation(Controller.Standard.LeftHand, true); + return getControllerWorldLocation(controllerStandard.LeftHand, true); }; this.dataGatherers.rightControllerLocation = function () { - return getControllerWorldLocation(Controller.Standard.RightHand, true); + return getControllerWorldLocation(controllerStandard.RightHand, true); }; this.updateTimings = function () { @@ -178,8 +180,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var pinchOnBelowDistance = 0.016; var pinchOffAboveDistance = 0.035; - var leftIndexPose = Controller.getPoseValue(Controller.Standard.LeftHandIndex4); - var leftThumbPose = Controller.getPoseValue(Controller.Standard.LeftHandThumb4); + var leftIndexPose = Controller.getPoseValue(controllerStandard.LeftHandIndex4); + var leftThumbPose = Controller.getPoseValue(controllerStandard.LeftHandThumb4); var leftThumbToIndexDistance = Vec3.distance(leftIndexPose.translation, leftThumbPose.translation); if (leftIndexPose.valid && leftThumbPose.valid && leftThumbToIndexDistance < pinchOnBelowDistance) { _this.leftTriggerClicked = 1; @@ -191,8 +193,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); _this.leftTrackerClicked = false; } - var rightIndexPose = Controller.getPoseValue(Controller.Standard.RightHandIndex4); - var rightThumbPose = Controller.getPoseValue(Controller.Standard.RightHandThumb4); + var rightIndexPose = Controller.getPoseValue(controllerStandard.RightHandIndex4); + var rightThumbPose = Controller.getPoseValue(controllerStandard.RightHandThumb4); var rightThumbToIndexDistance = Vec3.distance(rightIndexPose.translation, rightThumbPose.translation); if (rightIndexPose.valid && rightThumbPose.valid && rightThumbToIndexDistance < pinchOnBelowDistance) { _this.rightTriggerClicked = 1; @@ -563,23 +565,23 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var MAPPING_NAME = "com.highfidelity.controllerDispatcher"; var mapping = Controller.newMapping(MAPPING_NAME); - mapping.from([Controller.Standard.RT]).peek().to(_this.rightTriggerPress); - mapping.from([Controller.Standard.RTClick]).peek().to(_this.rightTriggerClick); - mapping.from([Controller.Standard.LT]).peek().to(_this.leftTriggerPress); - mapping.from([Controller.Standard.LTClick]).peek().to(_this.leftTriggerClick); + mapping.from([controllerStandard.RT]).peek().to(_this.rightTriggerPress); + mapping.from([controllerStandard.RTClick]).peek().to(_this.rightTriggerClick); + mapping.from([controllerStandard.LT]).peek().to(_this.leftTriggerPress); + mapping.from([controllerStandard.LTClick]).peek().to(_this.leftTriggerClick); - mapping.from([Controller.Standard.RB]).peek().to(_this.rightSecondaryPress); - mapping.from([Controller.Standard.LB]).peek().to(_this.leftSecondaryPress); - mapping.from([Controller.Standard.LeftGrip]).peek().to(_this.leftSecondaryPress); - mapping.from([Controller.Standard.RightGrip]).peek().to(_this.rightSecondaryPress); + mapping.from([controllerStandard.RB]).peek().to(_this.rightSecondaryPress); + mapping.from([controllerStandard.LB]).peek().to(_this.leftSecondaryPress); + mapping.from([controllerStandard.LeftGrip]).peek().to(_this.leftSecondaryPress); + mapping.from([controllerStandard.RightGrip]).peek().to(_this.rightSecondaryPress); Controller.enableMapping(MAPPING_NAME); this.leftPointer = this.pointerManager.createPointer(false, PickType.Ray, { joint: "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE, - triggers: [{action: Controller.Standard.LTClick, button: "Focus"}, {action: Controller.Standard.LTClick, button: "Primary"}], - posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand, true), + triggers: [{action: controllerStandard.LTClick, button: "Focus"}, {action: controllerStandard.LTClick, button: "Primary"}], + posOffset: getGrabPointSphereOffset(controllerStandard.LeftHand, true), hover: true, scaleWithParent: true, distanceScaleEnd: true, @@ -589,8 +591,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.rightPointer = this.pointerManager.createPointer(false, PickType.Ray, { joint: "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE, - triggers: [{action: Controller.Standard.RTClick, button: "Focus"}, {action: Controller.Standard.RTClick, button: "Primary"}], - posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand, true), + triggers: [{action: controllerStandard.RTClick, button: "Focus"}, {action: controllerStandard.RTClick, button: "Primary"}], + posOffset: getGrabPointSphereOffset(controllerStandard.RightHand, true), hover: true, scaleWithParent: true, distanceScaleEnd: true, @@ -601,8 +603,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); joint: "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", filter: Picks.PICK_HUD, maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, - posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand, true), - triggers: [{action: Controller.Standard.LTClick, button: "Focus"}, {action: Controller.Standard.LTClick, button: "Primary"}], + posOffset: getGrabPointSphereOffset(controllerStandard.LeftHand, true), + triggers: [{action: controllerStandard.LTClick, button: "Focus"}, {action: controllerStandard.LTClick, button: "Primary"}], hover: true, scaleWithParent: true, distanceScaleEnd: true, @@ -612,8 +614,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); joint: "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", filter: Picks.PICK_HUD, maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, - posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand, true), - triggers: [{action: Controller.Standard.RTClick, button: "Focus"}, {action: Controller.Standard.RTClick, button: "Primary"}], + posOffset: getGrabPointSphereOffset(controllerStandard.RightHand, true), + triggers: [{action: controllerStandard.RTClick, button: "Focus"}, {action: controllerStandard.RTClick, button: "Primary"}], hover: true, scaleWithParent: true, distanceScaleEnd: true, diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 773d2852b7..4b24ef17b0 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -24,6 +24,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); Script.include("/~/system/libraries/utils.js"); +var controllerStandard = Controller.Standard; var DEFAULT_SPHERE_MODEL_URL = Script.resolvePath("../../assets/models/equip-Fresnel-3.fbx"); var EQUIP_SPHERE_SCALE_FACTOR = 0.65; @@ -351,7 +352,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa }; this.handToController = function() { - return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + return (this.hand === RIGHT_HAND) ? controllerStandard.RightHand : controllerStandard.LeftHand; }; this.updateSmoothedTrigger = function(controllerData) { diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 540668748a..4c61e22764 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -22,6 +22,8 @@ Script.include("/~/system/libraries/controllers.js"); (function() { + var controllerStandard = Controller.Standard; + var MARGIN = 25; function TargetObject(entityID, entityProps) { @@ -104,7 +106,7 @@ Script.include("/~/system/libraries/controllers.js"); this.handToController = function() { - return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + return (this.hand === RIGHT_HAND) ? controllerStandard.RightHand : controllerStandard.LeftHand; }; this.distanceGrabTimescale = function(mass, distance) { @@ -188,7 +190,7 @@ Script.include("/~/system/libraries/controllers.js"); var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); - var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, DISPATCHER_PROPERTIES); + var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, "position"); var now = Date.now(); var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds this.currentObjectTime = now; @@ -304,7 +306,7 @@ Script.include("/~/system/libraries/controllers.js"); this.notPointingAtEntity = function(controllerData) { var intersection = controllerData.rayPicks[this.hand]; - var entityProperty = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); + var entityProperty = Entities.getEntityProperties(intersection.objectID, "type"); var entityType = entityProperty.type; var hudRayPick = controllerData.hudRayPicks[this.hand]; var point2d = this.calculateNewReticlePosition(hudRayPick.intersection); @@ -339,7 +341,7 @@ Script.include("/~/system/libraries/controllers.js"); var worldControllerPosition = controllerLocation.position; var worldControllerRotation = controllerLocation.orientation; - var grabbedProperties = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); + var grabbedProperties = Entities.getEntityProperties(intersection.objectID, "position"); this.currentObjectPosition = grabbedProperties.position; this.grabRadius = intersection.distance; @@ -353,7 +355,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.targetIsNull = function() { - var properties = Entities.getEntityProperties(this.grabbedThingID, DISPATCHER_PROPERTIES); + var properties = Entities.getEntityProperties(this.grabbedThingID, "type"); if (Object.keys(properties).length === 0 && this.distanceHolding) { return true; } diff --git a/scripts/system/controllers/controllerModules/farGrabEntity.js b/scripts/system/controllers/controllerModules/farGrabEntity.js index 5849c60553..43109198fe 100644 --- a/scripts/system/controllers/controllerModules/farGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farGrabEntity.js @@ -19,6 +19,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function () { + var controllerStandard = Controller.Standard; + var MARGIN = 25; function TargetObject(entityID, entityProps) { @@ -120,7 +122,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.handToController = function () { - return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + return (this.hand === RIGHT_HAND) ? controllerStandard.RightHand : controllerStandard.LeftHand; }; this.distanceGrabTimescale = function (mass, distance) { @@ -222,7 +224,7 @@ Script.include("/~/system/libraries/controllers.js"); var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); - var targetProps = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); + var targetProps = Entities.getEntityProperties(this.targetEntityID, "position"); var now = Date.now(); var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds this.currentObjectTime = now; @@ -276,7 +278,7 @@ Script.include("/~/system/libraries/controllers.js"); // This block handles the user's ability to rotate the object they're FarGrabbing if (this.shouldManipulateTarget(controllerData)) { // Get the pose of the controller that is not grabbing. - var pose = Controller.getPoseValue((this.getOffhand() ? Controller.Standard.RightHand : Controller.Standard.LeftHand)); + var pose = Controller.getPoseValue((this.getOffhand() ? controllerStandard.RightHand : controllerStandard.LeftHand)); if (pose.valid) { // If we weren't manipulating the object yet, initialize the entity's original position. if (!this.manipulating) { @@ -356,7 +358,7 @@ Script.include("/~/system/libraries/controllers.js"); this.notPointingAtEntity = function (controllerData) { var intersection = controllerData.rayPicks[this.hand]; - var entityProperty = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); + var entityProperty = Entities.getEntityProperties(intersection.objectID, "type"); var entityType = entityProperty.type; var hudRayPick = controllerData.hudRayPicks[this.hand]; var point2d = this.calculateNewReticlePosition(hudRayPick.intersection); @@ -368,7 +370,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.targetIsNull = function () { - var properties = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); + var properties = Entities.getEntityProperties(this.targetEntityID, "type"); if (Object.keys(properties).length === 0 && this.distanceHolding) { return true; } diff --git a/scripts/system/controllers/controllerModules/hudOverlayPointer.js b/scripts/system/controllers/controllerModules/hudOverlayPointer.js index f7d5b5a2dd..4c818647cd 100644 --- a/scripts/system/controllers/controllerModules/hudOverlayPointer.js +++ b/scripts/system/controllers/controllerModules/hudOverlayPointer.js @@ -12,6 +12,7 @@ /* global Script, Controller, RIGHT_HAND, LEFT_HAND, HMD, makeLaserParams */ (function() { + var controllerStandard = Controller.Standard; Script.include("/~/system/libraries/controllers.js"); var ControllerDispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); var MARGIN = 25; @@ -45,11 +46,11 @@ }; this.getOtherHandController = function() { - return (this.hand === RIGHT_HAND) ? Controller.Standard.LeftHand : Controller.Standard.RightHand; + return (this.hand === RIGHT_HAND) ? controllerStandard.LeftHand : controllerStandard.RightHand; }; this.handToController = function() { - return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + return (this.hand === RIGHT_HAND) ? controllerStandard.RightHand : controllerStandard.LeftHand; }; this.updateRecommendedArea = function() { diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index d553bf5714..22ce55c703 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -20,6 +20,7 @@ Script.include("/~/system/libraries/controllers.js"); Script.include("/~/system/libraries/utils.js"); (function () { + var controllerStandard = Controller.Standard; var MARGIN = 25; function InEditMode(hand) { this.hand = hand; @@ -48,7 +49,7 @@ Script.include("/~/system/libraries/utils.js"); }; this.handToController = function() { - return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + return (this.hand === RIGHT_HAND) ? controllerStandard.RightHand : controllerStandard.LeftHand; }; this.pointingAtTablet = function(objectID) { @@ -70,7 +71,7 @@ Script.include("/~/system/libraries/utils.js"); this.sendPickData = function(controllerData) { if (controllerData.triggerClicks[this.hand]) { - var hand = this.hand === RIGHT_HAND ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var hand = this.hand === RIGHT_HAND ? controllerStandard.RightHand : controllerStandard.LeftHand; if (!this.triggerClicked) { print("inEditMode click"); this.selectedTarget = controllerData.rayPicks[this.hand]; diff --git a/scripts/system/controllers/controllerModules/nearGrabEntity.js b/scripts/system/controllers/controllerModules/nearGrabEntity.js index 1a1b55c020..92fcc7ceb6 100644 --- a/scripts/system/controllers/controllerModules/nearGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearGrabEntity.js @@ -161,7 +161,7 @@ Script.include("/~/system/libraries/controllers.js"); var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID]; if (!props) { - props = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); + props = Entities.getEntityProperties(this.targetEntityID, "type"); if (!props) { // entity was deleted this.grabbing = false; diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index e0660df1b7..cb071c2dbd 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -203,7 +203,7 @@ Script.include("/~/system/libraries/utils.js"); var candidateOverlays = controllerData.nearbyOverlayIDs[this.hand]; var grabbableOverlays = candidateOverlays.filter(function(overlayID) { // V8TODO: check if this works - return Entities.getEntityProperties(overlayID, ["grab"]).grab.grabbable; + return Entities.getEntityProperties(overlayID, ["grab.grabbable"]).grab.grabbable; }); var targetID = this.getTargetID(grabbableOverlays, controllerData); diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js index 63106b6241..be45d3b70e 100644 --- a/scripts/system/controllers/controllerModules/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -21,6 +21,8 @@ Script.include("/~/system/libraries/controllers.js"); (function() { // BEGIN LOCAL_SCOPE + var controllerStandard = Controller.Standard; + var TARGET_MODEL_URL = Script.resolvePath("../../assets/models/teleportationSpotBasev8.fbx"); var SEAT_MODEL_URL = Script.resolvePath("../../assets/models/teleport-seat.fbx"); @@ -46,10 +48,10 @@ Script.include("/~/system/libraries/controllers.js"); var handInfo = { right: { - controllerInput: Controller.Standard.RightHand + controllerInput: controllerStandard.RightHand }, left: { - controllerInput: Controller.Standard.LeftHand + controllerInput: controllerStandard.LeftHand } }; @@ -1065,10 +1067,10 @@ Script.include("/~/system/libraries/controllers.js"); registerGamePadMapping(); // Teleport actions. - teleportMapping.from(Controller.Standard.LY).peek().to(leftTeleporter.getStandardLY); - teleportMapping.from(Controller.Standard.RY).peek().to(leftTeleporter.getStandardRY); - teleportMapping.from(Controller.Standard.LY).peek().to(rightTeleporter.getStandardLY); - teleportMapping.from(Controller.Standard.RY).peek().to(rightTeleporter.getStandardRY); + teleportMapping.from(controllerStandard.LY).peek().to(leftTeleporter.getStandardLY); + teleportMapping.from(controllerStandard.RY).peek().to(leftTeleporter.getStandardRY); + teleportMapping.from(controllerStandard.LY).peek().to(rightTeleporter.getStandardLY); + teleportMapping.from(controllerStandard.RY).peek().to(rightTeleporter.getStandardRY); } var leftTeleporter = new Teleporter(LEFT_HAND); diff --git a/scripts/system/controllers/controllerModules/trackedHandTablet.js b/scripts/system/controllers/controllerModules/trackedHandTablet.js index 66cf408af8..6aa5e88b34 100644 --- a/scripts/system/controllers/controllerModules/trackedHandTablet.js +++ b/scripts/system/controllers/controllerModules/trackedHandTablet.js @@ -12,6 +12,8 @@ Script.include("/~/system/libraries/controllers.js"); (function() { + var controllerStandard = Controller.Standard; + function TrackedHandTablet() { this.mappingName = 'hand-track-tablet-' + Math.random(); this.inputMapping = Controller.newMapping(this.mappingName); @@ -103,16 +105,16 @@ Script.include("/~/system/libraries/controllers.js"); this.setup = function () { var _this = this; - this.inputMapping.from(Controller.Standard.LeftHandIndex4).peek().to(function (pose) { + this.inputMapping.from(controllerStandard.LeftHandIndex4).peek().to(function (pose) { _this.leftIndexChanged(pose); }); - this.inputMapping.from(Controller.Standard.LeftHandThumb4).peek().to(function (pose) { + this.inputMapping.from(controllerStandard.LeftHandThumb4).peek().to(function (pose) { _this.leftThumbChanged(pose); }); - this.inputMapping.from(Controller.Standard.RightHandIndex4).peek().to(function (pose) { + this.inputMapping.from(controllerStandard.RightHandIndex4).peek().to(function (pose) { _this.rightIndexChanged(pose); }); - this.inputMapping.from(Controller.Standard.RightHandThumb4).peek().to(function (pose) { + this.inputMapping.from(controllerStandard.RightHandThumb4).peek().to(function (pose) { _this.rightThumbChanged(pose); }); diff --git a/scripts/system/controllers/controllerModules/trackedHandWalk.js b/scripts/system/controllers/controllerModules/trackedHandWalk.js index 9ecc53a1fa..dc8f3f16c4 100644 --- a/scripts/system/controllers/controllerModules/trackedHandWalk.js +++ b/scripts/system/controllers/controllerModules/trackedHandWalk.js @@ -12,6 +12,8 @@ Script.include("/~/system/libraries/controllers.js"); (function() { + var controllerStandard = Controller.Standard; + function TrackedHandWalk() { this.gestureMappingName = 'hand-track-walk-gesture-' + Math.random(); this.inputGestureMapping = Controller.newMapping(this.gestureMappingName); @@ -114,16 +116,16 @@ Script.include("/~/system/libraries/controllers.js"); this.setup = function () { var _this = this; - this.inputGestureMapping.from(Controller.Standard.LeftHandIndex4).peek().to(function (pose) { + this.inputGestureMapping.from(controllerStandard.LeftHandIndex4).peek().to(function (pose) { _this.leftIndexChanged(pose); }); - this.inputGestureMapping.from(Controller.Standard.LeftHandThumb4).peek().to(function (pose) { + this.inputGestureMapping.from(controllerStandard.LeftHandThumb4).peek().to(function (pose) { _this.leftThumbChanged(pose); }); - this.inputGestureMapping.from(Controller.Standard.RightHandIndex4).peek().to(function (pose) { + this.inputGestureMapping.from(controllerStandard.RightHandIndex4).peek().to(function (pose) { _this.rightIndexChanged(pose); }); - this.inputGestureMapping.from(Controller.Standard.RightHandThumb4).peek().to(function (pose) { + this.inputGestureMapping.from(controllerStandard.RightHandThumb4).peek().to(function (pose) { _this.rightThumbChanged(pose); }); @@ -137,7 +139,7 @@ Script.include("/~/system/libraries/controllers.js"); // return currentPoint.z - _this.controlPoint.z; return 0.5; } else { - // return Controller.getActionValue(Controller.Standard.TranslateZ); + // return Controller.getActionValue(controllerStandard.TranslateZ); return 0.0; } }).to(Controller.Actions.TranslateZ); @@ -147,7 +149,7 @@ Script.include("/~/system/libraries/controllers.js"); // var currentPoint = _this.getControlPoint(); // return currentPoint.x - _this.controlPoint.x; // } else { - // return Controller.getActionValue(Controller.Standard.Yaw); + // return Controller.getActionValue(controllerStandard.Yaw); // } // }).to(Controller.Actions.Yaw); diff --git a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js index 77a01883b5..e5cccaf047 100644 --- a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js +++ b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js @@ -7,7 +7,7 @@ /* global Script, Entities, enableDispatcherModule, disableDispatcherModule, makeRunningValues, makeDispatcherModuleParameters, Overlays, HMD, TRIGGER_ON_VALUE, TRIGGER_OFF_VALUE, getEnabledModuleByName, - Picks, makeLaserParams, Settings, MyAvatar, RIGHT_HAND, LEFT_HAND, DISPATCHER_PROPERTIES + Picks, makeLaserParams, Settings, MyAvatar, RIGHT_HAND, LEFT_HAND */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -59,7 +59,7 @@ Script.include("/~/system/libraries/controllers.js"); var candidateOverlays = controllerData.nearbyOverlayIDs[this.hand]; var grabbableOverlays = candidateOverlays.filter(function(overlayID) { //V8TODO: this needs to be checked if it works - return Entities.getEntityProperties(overlayID, ["grab"]).grab.grabbable; + return Entities.getEntityProperties(overlayID, ["grab.grabbable"]).grab.grabbable; }); var target = nearGrabModule.getTargetID(grabbableOverlays, controllerData); if (target) { @@ -164,7 +164,7 @@ Script.include("/~/system/libraries/controllers.js"); return type; } } else if (intersection.type === Picks.INTERSECTED_ENTITY) { - var entityProperties = Entities.getEntityProperties(objectID, DISPATCHER_PROPERTIES); + var entityProperties = Entities.getEntityProperties(objectID, ["type","locked"]); var entityType = entityProperties.type; var isLocked = entityProperties.locked; if (entityType === "Web" && (!isLocked || triggerPressed)) { diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index 459fad3425..a9e003be45 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -311,7 +311,7 @@ Grabber.prototype.pressEvent = function(event) { mouse.startDrag(event); var clickedEntity = pickResults.objectID; - var entityProperties = Entities.getEntityProperties(clickedEntity, DISPATCHER_PROPERTIES); + var entityProperties = Entities.getEntityProperties(clickedEntity, ["position", "rotation", "dimensions"]); this.startPosition = entityProperties.position; this.lastRotation = entityProperties.rotation; var cameraPosition = Camera.getPosition(); @@ -422,7 +422,7 @@ Grabber.prototype.moveEvent = function(event) { Grabber.prototype.moveEventProcess = function() { this.moveEventTimer = null; - var entityProperties = Entities.getEntityProperties(this.entityID, DISPATCHER_PROPERTIES); + var entityProperties = Entities.getEntityProperties(this.entityID, "position"); if (!entityProperties || HMD.active) { return; } diff --git a/scripts/system/controllers/handTouch.js b/scripts/system/controllers/handTouch.js index 5939c6e3d2..6d5d403512 100644 --- a/scripts/system/controllers/handTouch.js +++ b/scripts/system/controllers/handTouch.js @@ -16,6 +16,7 @@ (function () { + var controllerStandard = Controller.Standard; var LEAP_MOTION_NAME = "LeapMotion"; // Hand touch is disabled due to twitchy finger bug when walking near walls or tables. see BUGZ-154. var handTouchEnabled = false; @@ -792,15 +793,15 @@ var MAPPING_NAME = "com.highfidelity.handTouch"; var mapping = Controller.newMapping(MAPPING_NAME); - mapping.from([Controller.Standard.RT]).peek().to(rightTriggerPress); - mapping.from([Controller.Standard.RTClick]).peek().to(rightTriggerClick); - mapping.from([Controller.Standard.LT]).peek().to(leftTriggerPress); - mapping.from([Controller.Standard.LTClick]).peek().to(leftTriggerClick); + mapping.from([controllerStandard.RT]).peek().to(rightTriggerPress); + mapping.from([controllerStandard.RTClick]).peek().to(rightTriggerClick); + mapping.from([controllerStandard.LT]).peek().to(leftTriggerPress); + mapping.from([controllerStandard.LTClick]).peek().to(leftTriggerClick); - mapping.from([Controller.Standard.RB]).peek().to(rightSecondaryPress); - mapping.from([Controller.Standard.LB]).peek().to(leftSecondaryPress); - mapping.from([Controller.Standard.LeftGrip]).peek().to(leftSecondaryPress); - mapping.from([Controller.Standard.RightGrip]).peek().to(rightSecondaryPress); + mapping.from([controllerStandard.RB]).peek().to(rightSecondaryPress); + mapping.from([controllerStandard.LB]).peek().to(leftSecondaryPress); + mapping.from([controllerStandard.LeftGrip]).peek().to(leftSecondaryPress); + mapping.from([controllerStandard.RightGrip]).peek().to(rightSecondaryPress); Controller.enableMapping(MAPPING_NAME); diff --git a/scripts/system/controllers/squeezeHands.js b/scripts/system/controllers/squeezeHands.js index 69f44f46a9..3f6df92b12 100644 --- a/scripts/system/controllers/squeezeHands.js +++ b/scripts/system/controllers/squeezeHands.js @@ -16,6 +16,8 @@ (function() { // BEGIN LOCAL_SCOPE +var controllerStandard = Controller.Standard; + var lastLeftTrigger = 0; var lastRightTrigger = 0; var leftHandOverlayAlpha = 0; @@ -86,8 +88,8 @@ function animStateHandler(props) { } function update(dt) { - var leftTrigger = clamp(Controller.getValue(Controller.Standard.LT) + Controller.getValue(Controller.Standard.LeftGrip), 0, 1); - var rightTrigger = clamp(Controller.getValue(Controller.Standard.RT) + Controller.getValue(Controller.Standard.RightGrip), 0, 1); + var leftTrigger = clamp(Controller.getValue(controllerStandard.LT) + Controller.getValue(controllerStandard.LeftGrip), 0, 1); + var rightTrigger = clamp(Controller.getValue(controllerStandard.RT) + Controller.getValue(controllerStandard.RightGrip), 0, 1); // Average last few trigger values together for a bit of smoothing var tau = clamp(dt / TRIGGER_SMOOTH_TIMESCALE, 0, 1); @@ -95,7 +97,7 @@ function update(dt) { lastRightTrigger = lerp(rightTrigger, lastRightTrigger, tau); // ramp on/off left hand overlay - var leftHandPose = Controller.getPoseValue(Controller.Standard.LeftHand); + var leftHandPose = Controller.getPoseValue(controllerStandard.LeftHand); if (leftHandPose.valid) { leftHandOverlayAlpha = clamp(leftHandOverlayAlpha + OVERLAY_RAMP_RATE * dt, 0, 1); } else { @@ -103,7 +105,7 @@ function update(dt) { } // ramp on/off right hand overlay - var rightHandPose = Controller.getPoseValue(Controller.Standard.RightHand); + var rightHandPose = Controller.getPoseValue(controllerStandard.RightHand); if (rightHandPose.valid) { rightHandOverlayAlpha = clamp(rightHandOverlayAlpha + OVERLAY_RAMP_RATE * dt, 0, 1); } else { @@ -111,10 +113,10 @@ function update(dt) { } // Pointing index fingers and raising thumbs - isLeftIndexPointing = (leftIndexPointingOverride > 0) || (leftHandPose.valid && Controller.getValue(Controller.Standard.LeftIndexPoint) === 1); - isRightIndexPointing = (rightIndexPointingOverride > 0) || (rightHandPose.valid && Controller.getValue(Controller.Standard.RightIndexPoint) === 1); - isLeftThumbRaised = (leftThumbRaisedOverride > 0) || (leftHandPose.valid && Controller.getValue(Controller.Standard.LeftThumbUp) === 1); - isRightThumbRaised = (rightThumbRaisedOverride > 0) || (rightHandPose.valid && Controller.getValue(Controller.Standard.RightThumbUp) === 1); + isLeftIndexPointing = (leftIndexPointingOverride > 0) || (leftHandPose.valid && Controller.getValue(controllerStandard.LeftIndexPoint) === 1); + isRightIndexPointing = (rightIndexPointingOverride > 0) || (rightHandPose.valid && Controller.getValue(controllerStandard.RightIndexPoint) === 1); + isLeftThumbRaised = (leftThumbRaisedOverride > 0) || (leftHandPose.valid && Controller.getValue(controllerStandard.LeftThumbUp) === 1); + isRightThumbRaised = (rightThumbRaisedOverride > 0) || (rightHandPose.valid && Controller.getValue(controllerStandard.RightThumbUp) === 1); } function handleMessages(channel, message, sender) { diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 92f72f8724..b0b498fc29 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -17,6 +17,8 @@ (function() { // BEGIN LOCAL_SCOPE + var controllerStandard = Controller.Standard; + var TWO_SECONDS_INTERVAL = 2000; var FLYING_MAPPING_NAME = 'Hifi-Flying-Dev-' + Math.random(); var DRIVING_MAPPING_NAME = 'Hifi-Driving-Dev-' + Math.random(); @@ -44,7 +46,7 @@ function registerBasicMapping() { drivingMapping = Controller.newMapping(DRIVING_MAPPING_NAME); - drivingMapping.from(Controller.Standard.LY).to(function(value) { + drivingMapping.from(controllerStandard.LY).to(function(value) { if (isDisabled) { return; } @@ -64,7 +66,7 @@ }); flyingMapping = Controller.newMapping(FLYING_MAPPING_NAME); - flyingMapping.from(Controller.Standard.RY).to(function(value) { + flyingMapping.from(controllerStandard.RY).to(function(value) { if (isDisabled) { return; } diff --git a/scripts/system/controllers/touchControllerConfiguration.js b/scripts/system/controllers/touchControllerConfiguration.js index 991b77b8af..1944d54537 100644 --- a/scripts/system/controllers/touchControllerConfiguration.js +++ b/scripts/system/controllers/touchControllerConfiguration.js @@ -12,6 +12,8 @@ Quat, Vec3, Script, MyAvatar, Controller */ /* eslint camelcase: ["error", { "properties": "never" }] */ +var controllerStandard = Controller.Standard; + var leftBaseRotation = Quat.multiply( Quat.fromPitchYawRollDegrees(-90, 0, 0), Quat.fromPitchYawRollDegrees(0, 0, 90) @@ -89,7 +91,7 @@ TOUCH_CONTROLLER_CONFIGURATION_LEFT = { naturalDimensions: { x: 0.027509, y: 0.025211, z: 0.018443 }, // rotational - input: Controller.Standard.LT, + input: controllerStandard.LT, origin: { x: 0, y: -0.015, z: -0.00 }, minValue: 0.0, maxValue: 1.0, diff --git a/scripts/system/controllers/viveControllerConfiguration.js b/scripts/system/controllers/viveControllerConfiguration.js index 09fd8adacc..e0ae43c16c 100644 --- a/scripts/system/controllers/viveControllerConfiguration.js +++ b/scripts/system/controllers/viveControllerConfiguration.js @@ -15,6 +15,8 @@ // var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"); // var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"); +var controllerStandard = Controller.Standard; + var leftBaseRotation = Quat.multiply( Quat.fromPitchYawRollDegrees(0, 0, 45), Quat.multiply( @@ -141,7 +143,7 @@ VIVE_CONTROLLER_CONFIGURATION_LEFT = { trigger: { type: "rotational", modelURL: BASE_URL + "meshes/controller/vive_trigger.fbx", - input: Controller.Standard.LT, + input: controllerStandard.LT, naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, naturalDimensions: {x: 0.019105, y: 0.022189, z: 0.01909}, origin: { x: 0, y: -0.015, z: -0.00 }, @@ -283,7 +285,7 @@ VIVE_CONTROLLER_CONFIGURATION_RIGHT = { trigger: { type: "rotational", modelURL: BASE_URL + "meshes/controller/vive_trigger.fbx", - input: Controller.Standard.RT, + input: controllerStandard.RT, naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, naturalDimensions: {x: 0.019105, y: 0.022189, z: 0.01909}, origin: { x: 0, y: -0.015, z: -0.00 }, diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index b67969b598..076f47f5dd 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -27,7 +27,7 @@ "tooltip": "The height of each line of text. This determines the size of the text." }, "font": { - "tooltip": "The font to render the text. Supported values: \"Courier\", \"Inconsolata\", \"Roboto\", \"Timeless\", or a URL to a .sdff file." + "tooltip": "The font to render the text. Supported values: \"Courier\", \"Inconsolata\", \"Roboto\", \"Timeless\", or a URL to a PNG MTSDF .arfont file." }, "textEffect": { "tooltip": "The effect that is applied to the text." diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index 1ddc2fa0d6..bfacd300e8 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -1,10 +1,10 @@ // edit.js // -// Created by Brad Hefta-Gaub on 10/2/14. -// Persist toolbar by HRS 6/11/15. +// Created by Brad Hefta-Gaub on October 2nd, 2014. +// Persist toolbar by HRS on June 2nd, 2015. // Copyright 2014 High Fidelity, Inc. // Copyright 2020 Vircadia contributors. -// Copyright 2022-2023 Overte e.V. +// Copyright 2022-2024 Overte e.V. // // This script allows you to edit entities with a new UI/UX for mouse and trackpad based editing // @@ -38,6 +38,7 @@ "entitySelectionTool/entitySelectionTool.js", "audioFeedback/audioFeedback.js", "modules/brokenURLReport.js", + "modules/renderWithZonesManager.js", "editModes/editModes.js", "editModes/editVoxels.js" ]); @@ -120,6 +121,18 @@ var copiedPosition; var copiedRotation; + var copiedDimensions; + + var importUiPersistedData = { + "elJsonUrl": "", + "elImportAtAvatar": true, + "elImportAtSpecificPosition": false, + "elPositionX": 0, + "elPositionY": 0, + "elPositionZ": 0, + "elEntityHostTypeDomain": true, + "elEntityHostTypeAvatar": false + }; var cameraManager = new CameraManager(); @@ -2045,7 +2058,8 @@ return position; } - function importSVO(importURL) { + function importSVO(importURL, importEntityHostType) { + importEntityHostType = importEntityHostType || "domain"; if (!Entities.canRez() && !Entities.canRezTmp()) { Window.notifyEditError(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); return; @@ -2068,7 +2082,7 @@ position = createApp.getPositionToCreateEntity(Clipboard.getClipboardContentsLargestDimension() / 2); } if (position !== null && position !== undefined) { - var pastedEntityIDs = Clipboard.pasteEntities(position); + var pastedEntityIDs = Clipboard.pasteEntities(position, importEntityHostType); if (!isLargeImport) { // The first entity in Clipboard gets the specified position with the rest being relative to it. Therefore, move // entities after they're imported so that they're all the correct distance in front of and with geometric mean @@ -2743,6 +2757,13 @@ copiedRotation = properties.rotation; Window.copyToClipboard(JSON.stringify(copiedRotation)); } + } else if (data.action === "copyDimensions") { + if (selectionManager.selections.length === 1) { + selectionManager.saveProperties(); + properties = selectionManager.savedProperties[selectionManager.selections[0]]; + copiedDimensions = properties.dimensions; + Window.copyToClipboard(JSON.stringify(copiedDimensions)); + } } else if (data.action === "pastePosition") { if (copiedPosition !== undefined && selectionManager.selections.length > 0 && SelectionManager.hasUnlockedSelection()) { selectionManager.saveProperties(); @@ -2756,6 +2777,19 @@ } else { audioFeedback.rejection(); } + } else if (data.action === "pasteDimensions") { + if (copiedDimensions !== undefined && selectionManager.selections.length > 0 && SelectionManager.hasUnlockedSelection()) { + selectionManager.saveProperties(); + for (i = 0; i < selectionManager.selections.length; i++) { + Entities.editEntity(selectionManager.selections[i], { + dimensions: copiedDimensions + }); + } + createApp.pushCommandForSelections(); + selectionManager._update(false, this); + } else { + audioFeedback.rejection(); + } } else if (data.action === "pasteRotation") { if (copiedRotation !== undefined && selectionManager.selections.length > 0 && SelectionManager.hasUnlockedSelection()) { selectionManager.saveProperties(); @@ -2789,6 +2823,10 @@ } } } else if (data.type === "propertiesPageReady") { + emitScriptEvent({ + type: 'urlPermissionChanged', + canViewAssetURLs: Entities.canViewAssetURLs(), + }); updateSelections(true); } else if (data.type === "tooltipsRequest") { emitScriptEvent({ @@ -2828,6 +2866,72 @@ type: 'zoneListRequest', zones: getExistingZoneList() }); + } else if (data.type === "importUiBrowse") { + let fileToImport = Window.browse("Select .json to Import", "", "*.json"); + if (fileToImport !== null) { + emitScriptEvent({ + type: 'importUi_SELECTED_FILE', + file: fileToImport + }); + } else { + audioFeedback.rejection(); + } + } else if (data.type === "importUiImport") { + if ((data.entityHostType === "domain" && Entities.canAdjustLocks() && Entities.canRez()) || + (data.entityHostType === "avatar" && Entities.canRezAvatarEntities())) { + if (data.positioningMode === "avatar") { + importSVO(data.jsonURL, data.entityHostType); + } else { + if (Clipboard.importEntities(data.jsonURL)) { + let importedPastedEntities = Clipboard.pasteEntities(data.position, data.entityHostType); + if (importedPastedEntities.length === 0) { + emitScriptEvent({ + type: 'importUi_IMPORT_ERROR', + reason: "No Entity has been imported." + }); + } else { + if (isActive) { + selectionManager.setSelections(importedPastedEntities, this); + } + emitScriptEvent({type: 'importUi_IMPORT_CONFIRMATION'}); + } + } else { + emitScriptEvent({ + type: 'importUi_IMPORT_ERROR', + reason: "Import Entities has failed." + }); + } + } + } else { + emitScriptEvent({ + type: 'importUi_IMPORT_ERROR', + reason: "You don't have permission to create in this domain." + }); + } + } else if (data.type === "importUiGoBack") { + if (location.canGoBack()) { + location.goBack(); + } else { + audioFeedback.rejection(); + } + } else if (data.type === "importUiGoTutorial") { + Window.location = "file:///~/serverless/tutorial.json"; + } else if (data.type === "importUiGetCopiedPosition") { + if (copiedPosition !== undefined) { + emitScriptEvent({ + type: 'importUi_POSITION_TO_PASTE', + position: copiedPosition + }); + } else { + audioFeedback.rejection(); + } + } else if (data.type === "importUiPersistData") { + importUiPersistedData = data.importUiPersistedData; + } else if (data.type === "importUiGetPersistData") { + emitScriptEvent({ + type: 'importUi_LOAD_DATA', + importUiPersistedData: importUiPersistedData + }); } }; @@ -2838,6 +2942,13 @@ }); }); + Entities.canViewAssetURLsChanged.connect((value) => { + emitScriptEvent({ + type: 'urlPermissionChanged', + canViewAssetURLs: value, + }); + }); + createToolsWindow.webEventReceived.addListener(this, onWebEventReceived); webView.webEventReceived.connect(this, onWebEventReceived); diff --git a/scripts/system/create/editModes/editVoxels.js b/scripts/system/create/editModes/editVoxels.js index 13138e55e1..bd0f4bc15a 100644 --- a/scripts/system/create/editModes/editVoxels.js +++ b/scripts/system/create/editModes/editVoxels.js @@ -34,6 +34,7 @@ EditVoxels = function() { var that = {}; const NO_HAND = -1; + var controllerStandard = Controller.Standard; var controlHeld = false; var shiftHeld = false; @@ -325,10 +326,10 @@ EditVoxels = function() { } }else{ inverseOperation = false; - if(that.triggeredHand === Controller.Standard.RightHand && Controller.getValue(Controller.Standard.RightGrip) > 0.5){ + if(that.triggeredHand === controllerStandard.RightHand && Controller.getValue(controllerStandard.RightGrip) > 0.5){ inverseOperation = true; } - if(that.triggeredHand === Controller.Standard.LeftHand && Controller.getValue(Controller.Standard.LeftGrip) > 0.5){ + if(that.triggeredHand === controllerStandard.LeftHand && Controller.getValue(controllerStandard.LeftGrip) > 0.5){ inverseOperation = true; } } @@ -458,13 +459,13 @@ EditVoxels = function() { } function getDistanceBetweenControllers(){ - var poseLeft = getControllerWorldLocation(Controller.Standard.LeftHand, true); - var poseRight = getControllerWorldLocation(Controller.Standard.RightHand, true); + var poseLeft = getControllerWorldLocation(controllerStandard.LeftHand, true); + var poseRight = getControllerWorldLocation(controllerStandard.RightHand, true); return Vec3.distance(poseLeft.translation, poseRight.translation); } function getEditSpherePosition( radius ){ - var poseLeft = getControllerWorldLocation(Controller.Standard.LeftHand, true); - var poseRight = getControllerWorldLocation(Controller.Standard.RightHand, true); + var poseLeft = getControllerWorldLocation(controllerStandard.LeftHand, true); + var poseRight = getControllerWorldLocation(controllerStandard.RightHand, true); var handsPosition = Vec3.multiply(Vec3.sum(poseLeft.translation, poseRight.translation), 0.5); return Vec3.sum(handsPosition, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: radius * -2.0 })); } @@ -531,15 +532,15 @@ EditVoxels = function() { return; } if (value > 0.5) { - if (hand === Controller.Standard.LeftHand) { + if (hand === controllerStandard.LeftHand) { isLeftGripPressed = true; - } else if (hand === Controller.Standard.RightHand) { + } else if (hand === controllerStandard.RightHand) { isRightGripPressed = true; } } else if (value < 0.4){ - if (hand === Controller.Standard.LeftHand) { + if (hand === controllerStandard.LeftHand) { isLeftGripPressed = false; - } else if (hand === Controller.Standard.RightHand) { + } else if (hand === controllerStandard.RightHand) { isRightGripPressed = false; } } @@ -664,12 +665,12 @@ EditVoxels = function() { Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); - that.triggerClickMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); - that.triggerClickMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); - that.triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand)); - that.triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); - that.gripPressMapping.from(Controller.Standard.LeftGrip).peek().to(makeGripPressHandler(Controller.Standard.LeftHand)); - that.gripPressMapping.from(Controller.Standard.RightGrip).peek().to(makeGripPressHandler(Controller.Standard.RightHand)); + that.triggerClickMapping.from(controllerStandard.RTClick).peek().to(makeClickHandler(controllerStandard.RightHand)); + that.triggerClickMapping.from(controllerStandard.LTClick).peek().to(makeClickHandler(controllerStandard.LeftHand)); + that.triggerPressMapping.from(controllerStandard.RT).peek().to(makePressHandler(controllerStandard.RightHand)); + that.triggerPressMapping.from(controllerStandard.LT).peek().to(makePressHandler(controllerStandard.LeftHand)); + that.gripPressMapping.from(controllerStandard.LeftGrip).peek().to(makeGripPressHandler(controllerStandard.LeftHand)); + that.gripPressMapping.from(controllerStandard.RightGrip).peek().to(makeGripPressHandler(controllerStandard.RightHand)); that.enableTriggerMapping = function() { that.triggerClickMapping.enable(); that.triggerPressMapping.enable(); diff --git a/scripts/system/create/entityList/entityList.js b/scripts/system/create/entityList/entityList.js index 257f967852..b8cb3bba33 100644 --- a/scripts/system/create/entityList/entityList.js +++ b/scripts/system/create/entityList/entityList.js @@ -4,7 +4,7 @@ // // Copyright 2014 High Fidelity, Inc. // Copyright 2020 Vircadia contributors. -// Copyright 2023 Overte e.V. +// Copyright 2023-2024 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 @@ -351,6 +351,8 @@ var EntityListTool = function(shouldUseEditTabletApp, selectionManager) { that.selectionManager.cutSelectedEntities(); } else if (data.type === "copy") { that.selectionManager.copySelectedEntities(); + } else if (data.type === "copyID") { + that.selectionManager.copyIdsFromSelectedEntities(); } else if (data.type === "paste") { that.selectionManager.pasteEntities(); } else if (data.type === "duplicate") { @@ -422,6 +424,8 @@ var EntityListTool = function(shouldUseEditTabletApp, selectionManager) { that.createApp.alignGridToAvatar(); } else if (data.type === 'brokenURLReport') { brokenURLReport(that.selectionManager.selections); + } else if (data.type === 'renderWithZonesManager') { + renderWithZonesManager(that.selectionManager.selections); } else if (data.type === 'toggleGridVisibility') { that.createApp.toggleGridVisibility(); } else if (data.type === 'toggleSnapToGrid') { diff --git a/scripts/system/create/entityList/html/entityList.html b/scripts/system/create/entityList/html/entityList.html index 75b172e201..2c024f256d 100644 --- a/scripts/system/create/entityList/html/entityList.html +++ b/scripts/system/create/entityList/html/entityList.html @@ -4,6 +4,7 @@ // Created by Ryan Huffman on 19 Nov 2014 // Copyright 2014 High Fidelity, Inc. // Copyright 2020 Vircadia contributors. +// Copyright 2024 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 @@ -121,6 +122,12 @@

+ + diff --git a/scripts/system/create/entityList/html/js/entityList.js b/scripts/system/create/entityList/html/js/entityList.js index f83c334073..f544689e0b 100644 --- a/scripts/system/create/entityList/html/js/entityList.js +++ b/scripts/system/create/entityList/html/js/entityList.js @@ -1,8 +1,9 @@ // entityList.js // -// Created by Ryan Huffman on 19 Nov 2014 +// Created by Ryan Huffman on November 19th, 2014 // Copyright 2014 High Fidelity, Inc. // Copyright 2020 Vircadia contributors. +// Copyright 2024 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 @@ -274,6 +275,7 @@ let elEntityTable, elAlignGridToSelection, elAlignGridToAvatar, elBrokenURLReport, + elRenderWithZonesManager, elFilterTypeMultiselectBox, elFilterTypeText, elFilterTypeOptions, @@ -327,6 +329,7 @@ function loaded() { elToolsMenu = document.getElementById("tools"); elMenuBackgroundOverlay = document.getElementById("menuBackgroundOverlay"); elHmdCopy = document.getElementById("hmdcopy"); + elHmdCopyID = document.getElementById("hmdcopyid"); elHmdCut = document.getElementById("hmdcut"); elHmdPaste = document.getElementById("hmdpaste"); elHmdDuplicate = document.getElementById("hmdduplicate"); @@ -362,6 +365,7 @@ function loaded() { elAlignGridToSelection = document.getElementById("alignGridToSelection"); elAlignGridToAvatar = document.getElementById("alignGridToAvatar"); elBrokenURLReport = document.getElementById("brokenURLReport"); + elRenderWithZonesManager = document.getElementById("renderWithZonesManager"); elFilterTypeMultiselectBox = document.getElementById("filter-type-multiselect-box"); elFilterTypeText = document.getElementById("filter-type-text"); elFilterTypeOptions = document.getElementById("filter-type-options"); @@ -422,6 +426,10 @@ function loaded() { EventBridge.emitWebEvent(JSON.stringify({ type: "copy" })); closeAllEntityListMenu(); }; + elHmdCopyID.onclick = function() { + EventBridge.emitWebEvent(JSON.stringify({ type: "copyID" })); + closeAllEntityListMenu(); + }; elHmdCut.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: "cut" })); closeAllEntityListMenu(); @@ -604,6 +612,10 @@ function loaded() { EventBridge.emitWebEvent(JSON.stringify({ type: "brokenURLReport" })); closeAllEntityListMenu(); }; + elRenderWithZonesManager.onclick = function () { + EventBridge.emitWebEvent(JSON.stringify({ type: "renderWithZonesManager" })); + closeAllEntityListMenu(); + }; elToggleSpaceMode.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: "toggleSpaceMode" })); }; @@ -816,6 +828,9 @@ function loaded() { case "Copy": EventBridge.emitWebEvent(JSON.stringify({ type: "copy" })); break; + case "Copy ID(s)": + EventBridge.emitWebEvent(JSON.stringify({ type: "copyID" })); + break; case "Paste": EventBridge.emitWebEvent(JSON.stringify({ type: "paste" })); break; @@ -856,6 +871,10 @@ function loaded() { enabledContextMenuItems.push("Rename"); enabledContextMenuItems.push("Delete"); } + + if (selectedEntities.length !== 0) { + enabledContextMenuItems.push("Copy ID(s)"); + } entityListContextMenu.open(clickEvent, entityID, enabledContextMenuItems); } diff --git a/scripts/system/create/entityList/html/js/entityListContextMenu.js b/scripts/system/create/entityList/html/js/entityListContextMenu.js index d71719f252..b1176a7dee 100644 --- a/scripts/system/create/entityList/html/js/entityListContextMenu.js +++ b/scripts/system/create/entityList/html/js/entityListContextMenu.js @@ -1,9 +1,10 @@ // // entityListContextMenu.js // -// exampleContextMenus.js was originally created by David Rowe on 22 Aug 2018. -// Modified to entityListContextMenu.js by Thijs Wenker on 10 Oct 2018 +// exampleContextMenus.js was originally created by David Rowe on August 22nd, 2018. +// Modified to entityListContextMenu.js by Thijs Wenker on October 10th, 2018 // Copyright 2018 High Fidelity, Inc. +// Copyright 2024 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 @@ -137,6 +138,7 @@ EntityListContextMenu.prototype = { this._addListItem("Cut"); this._addListItem("Copy"); + this._addListItem("Copy ID(s)"); this._addListItem("Paste"); this._addListSeparator(); this._addListItem("Rename"); diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index ff55abbde9..1eade2703d 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -1,9 +1,9 @@ // entityProperties.js // -// Created by Ryan Huffman on 13 Nov 2014 +// Created by Ryan Huffman on November 13th, 2014 // Copyright 2014 High Fidelity, Inc. // Copyright 2020 Vircadia contributors. -// Copyright 2022 Overte e.V. +// Copyright 2022-2024 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 @@ -152,7 +152,7 @@ const GROUPS = [ }, { id: "shape", - label: "SHAPE", + label: "SHAPE", properties: [ { label: "Shape", @@ -177,7 +177,7 @@ const GROUPS = [ decimals: 2, propertyID: "shapeAlpha", propertyName: "alpha", - }, + }, ] }, { @@ -319,6 +319,7 @@ const GROUPS = [ type: "string", propertyID: "zoneCompoundShapeURL", propertyName: "compoundShapeURL", // actual entity property name + placeholder: "URL", }, { label: "Flying Allowed", @@ -334,6 +335,7 @@ const GROUPS = [ label: "Filter", type: "string", propertyID: "filterURL", + placeholder: "URL", } ] }, @@ -409,7 +411,7 @@ const GROUPS = [ decimals: 2, propertyID: "keyLight.shadowMaxDistance", showPropertyRule: { "keyLightMode": "enabled" }, - } + } ] }, { @@ -433,6 +435,7 @@ const GROUPS = [ type: "string", propertyID: "skybox.url", showPropertyRule: { "skyboxMode": "enabled" }, + placeholder: "URL", } ] }, @@ -461,6 +464,7 @@ const GROUPS = [ type: "string", propertyID: "ambientLight.ambientURL", showPropertyRule: { "ambientLightMode": "enabled" }, + placeholder: "URL", }, { type: "buttons", @@ -678,6 +682,7 @@ const GROUPS = [ label: "Compound Shape", type: "string", propertyID: "compoundShapeURL", + placeholder: "URL", }, { label: "Use Original Pivot", @@ -688,6 +693,7 @@ const GROUPS = [ label: "Animation", type: "string", propertyID: "animation.url", + placeholder: "URL", }, { label: "Play Automatically", @@ -806,6 +812,7 @@ const GROUPS = [ label: "Source", type: "string", propertyID: "sourceUrl", + placeholder: "URL", }, { label: "Source Resolution", @@ -932,6 +939,7 @@ const GROUPS = [ label: "Material URL", type: "string", propertyID: "materialURL", + placeholder: "URL", }, { label: "Material Data", @@ -1100,6 +1108,7 @@ const GROUPS = [ type: "string", propertyID: "particleCompoundShapeURL", propertyName: "compoundShapeURL", + placeholder: "URL", }, { label: "Emit Dimensions", @@ -1470,18 +1479,21 @@ const GROUPS = [ type: "string", propertyID: "xTextureURL", propertyName: "xTextureURL", + placeholder: "URL", }, { label: "Y Texture URL", type: "string", propertyID: "yTextureURL", propertyName: "yTextureURL", + placeholder: "URL", }, { label: "Z Texture URL", type: "string", propertyID: "zTextureURL", propertyName: "zTextureURL", + placeholder: "URL", }, ] }, @@ -1568,6 +1580,12 @@ const GROUPS = [ propertyID: "localDimensions", spaceMode: PROPERTY_SPACE_MODE.LOCAL, }, + { + type: "buttons", + buttons: [ { id: "copyDimensions", label: "Copy Dimensions", className: "secondary", onClick: copyDimensionsProperty }, + { id: "pasteDimensions", label: "Paste Dimensions", className: "secondary", onClick: pasteDimensionsProperty } ], + propertyID: "copyPasteDimensions" + }, { label: "Scale", type: "number-draggable", @@ -1933,6 +1951,7 @@ let currentSelections = []; let createAppTooltip = new CreateAppTooltip(); let currentSpaceMode = PROPERTY_SPACE_MODE.LOCAL; let zonesList = []; +let canViewAssetURLs = false; function createElementFromHTML(htmlString) { let elTemplate = document.createElement('template'); @@ -2026,14 +2045,17 @@ function setCopyPastePositionAndRotationAvailability (selectionLength, islocked) if (selectionLength === 1) { $('#property-copyPastePosition-button-copyPosition').attr('disabled', false); $('#property-copyPasteRotation-button-copyRotation').attr('disabled', false); + $('#property-copyPasteDimensions-button-copyDimensions').attr('disabled', false); } else { $('#property-copyPastePosition-button-copyPosition').attr('disabled', true); - $('#property-copyPasteRotation-button-copyRotation').attr('disabled', true); + $('#property-copyPasteRotation-button-copyRotation').attr('disabled', true); + $('#property-copyPasteDimensions-button-copyDimensions').attr('disabled', true); } if (selectionLength > 0 && !islocked) { $('#property-copyPastePosition-button-pastePosition').attr('disabled', false); $('#property-copyPasteRotation-button-pasteRotation').attr('disabled', false); + $('#property-copyPasteDimensions-button-pasteDimensions').attr('disabled', false); if (selectionLength === 1) { $('#property-copyPasteRotation-button-setRotationToZero').attr('disabled', false); } else { @@ -2043,6 +2065,7 @@ function setCopyPastePositionAndRotationAvailability (selectionLength, islocked) $('#property-copyPastePosition-button-pastePosition').attr('disabled', true); $('#property-copyPasteRotation-button-pasteRotation').attr('disabled', true); $('#property-copyPasteRotation-button-setRotationToZero').attr('disabled', true); + $('#property-copyPasteDimensions-button-pasteDimensions').attr('disabled', true); } } @@ -2598,7 +2621,7 @@ function createStringProperty(property, elProperty) { let elInput = createElementFromHTML(` `); @@ -3479,27 +3502,41 @@ function pastePositionProperty() { EventBridge.emitWebEvent(JSON.stringify({ type: "action", action: "pastePosition" - })); + })); } function copyRotationProperty() { EventBridge.emitWebEvent(JSON.stringify({ type: "action", action: "copyRotation" - })); + })); } function pasteRotationProperty() { EventBridge.emitWebEvent(JSON.stringify({ type: "action", action: "pasteRotation" - })); + })); } function setRotationToZeroProperty() { EventBridge.emitWebEvent(JSON.stringify({ type: "action", action: "setRotationToZero" - })); + })); +} + +function copyDimensionsProperty() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "copyDimensions" + })); +} + +function pasteDimensionsProperty() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "pasteDimensions" + })); } /** * USER DATA FUNCTIONS @@ -5218,7 +5255,7 @@ function loaded() { break; case 'vec3rgb': updateVectorMinMax(properties[property]); - break; + break; case 'rect': updateRectMinMax(properties[property]); break; @@ -5231,6 +5268,16 @@ function loaded() { } } else if (data.type === 'zoneListRequest') { zonesList = data.zones; + } else if (data.type === 'urlPermissionChanged') { + canViewAssetURLs = data.canViewAssetURLs; + Object.entries(properties).forEach(function ([propertyID, property]) { + if (property.data.placeholder && property.data.placeholder === "URL") { + if (!canViewAssetURLs) { + property.elInput.value = ""; + } + property.elInput.placeholder = canViewAssetURLs ? property.data.placeholder : "You don't have permission to view this URL"; + } + }); } }); diff --git a/scripts/system/create/entitySelectionTool/entitySelectionTool.js b/scripts/system/create/entitySelectionTool/entitySelectionTool.js index a95dd94360..222ee6e11e 100644 --- a/scripts/system/create/entitySelectionTool/entitySelectionTool.js +++ b/scripts/system/create/entitySelectionTool/entitySelectionTool.js @@ -1,12 +1,12 @@ // // entitySelectionTool.js // -// Created by Brad hefta-Gaub on 10/1/14. -// Modified by Daniela Fontes * @DanielaFifo and Tiago Andrade @TagoWill on 4/7/2017 -// Modified by David Back on 1/9/2018 +// Created by Brad hefta-Gaub on October 1st, 2014. +// Modified by Daniela Fontes * @DanielaFifo and Tiago Andrade @TagoWill on April 7th, 2017 +// Modified by David Back on January 9th, 2018 // Copyright 2014 High Fidelity, Inc. // Copyright 2020 Vircadia contributors -// Copyright 2022-2023 Overte e.V. +// Copyright 2022-2024 Overte e.V. // // This script implements a class useful for building tools for editing entities. // @@ -23,6 +23,8 @@ const SPACE_WORLD = "world"; const HIGHLIGHT_LIST_NAME = "editHandleHighlightList"; const MIN_DISTANCE_TO_REZ_FROM_AVATAR = 3; +var controllerStandard = Controller.Standard; + Script.include([ "../../libraries/controllers.js", "../../libraries/controllerDispatcherUtils.js", @@ -149,7 +151,7 @@ SelectionManager = (function() { that.clearSelections(); } } else if (messageParsed.method === "pointingAt") { - if (messageParsed.hand === Controller.Standard.RightHand) { + if (messageParsed.hand === controllerStandard.RightHand) { that.pointingAtDesktopWindowRight = messageParsed.desktopWindow; that.pointingAtTabletRight = messageParsed.tablet; } else { @@ -494,6 +496,18 @@ SelectionManager = (function() { that.createApp.deleteSelectedEntities(); }; + that.copyIdsFromSelectedEntities = function() { + if (that.selections.length === 0) { + audioFeedback.rejection(); + } else if (that.selections.length === 1) { + Window.copyToClipboard(that.selections[0]); + audioFeedback.confirmation(); + } else { + Window.copyToClipboard(JSON.stringify(that.selections)); + audioFeedback.confirmation(); + } + }; + that.copySelectedEntities = function() { var entityProperties = Entities.getMultipleEntityProperties(that.selections); var entityHostTypes = Entities.getMultipleEntityProperties(that.selections, 'entityHostType'); @@ -940,8 +954,8 @@ SelectionDisplay = (function() { var toolEntityNames = []; var lastControllerPoses = [ - getControllerWorldLocation(Controller.Standard.LeftHand, true), - getControllerWorldLocation(Controller.Standard.RightHand, true) + getControllerWorldLocation(controllerStandard.LeftHand, true), + getControllerWorldLocation(controllerStandard.RightHand, true) ]; var worldRotationX; @@ -1323,12 +1337,12 @@ SelectionDisplay = (function() { return that.triggeredHand !== NO_HAND; }; function pointingAtDesktopWindowOrTablet(hand) { - var pointingAtDesktopWindow = (hand === Controller.Standard.RightHand && + var pointingAtDesktopWindow = (hand === controllerStandard.RightHand && SelectionManager.pointingAtDesktopWindowRight) || - (hand === Controller.Standard.LeftHand && + (hand === controllerStandard.LeftHand && SelectionManager.pointingAtDesktopWindowLeft); - var pointingAtTablet = (hand === Controller.Standard.RightHand && SelectionManager.pointingAtTabletRight) || - (hand === Controller.Standard.LeftHand && SelectionManager.pointingAtTabletLeft); + var pointingAtTablet = (hand === controllerStandard.RightHand && SelectionManager.pointingAtTabletRight) || + (hand === controllerStandard.LeftHand && SelectionManager.pointingAtTabletLeft); return pointingAtDesktopWindow || pointingAtTablet; } function makeClickHandler(hand) { @@ -1363,10 +1377,10 @@ SelectionDisplay = (function() { } } } - that.triggerClickMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); - that.triggerClickMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); - that.triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand)); - that.triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); + that.triggerClickMapping.from(controllerStandard.RTClick).peek().to(makeClickHandler(controllerStandard.RightHand)); + that.triggerClickMapping.from(controllerStandard.LTClick).peek().to(makeClickHandler(controllerStandard.LeftHand)); + that.triggerPressMapping.from(controllerStandard.RT).peek().to(makePressHandler(controllerStandard.RightHand)); + that.triggerPressMapping.from(controllerStandard.LT).peek().to(makePressHandler(controllerStandard.LeftHand)); that.enableTriggerMapping = function() { that.triggerClickMapping.enable(); that.triggerPressMapping.enable(); @@ -1494,7 +1508,7 @@ SelectionDisplay = (function() { that.editingHand = that.triggeredHand; Messages.sendLocalMessage(INEDIT_STATUS_CHANNEL, JSON.stringify({ method: "editing", - hand: that.editingHand === Controller.Standard.LeftHand ? LEFT_HAND : RIGHT_HAND, + hand: that.editingHand === controllerStandard.LeftHand ? LEFT_HAND : RIGHT_HAND, editing: true })); activeTool.onBegin(event, pickRay, results); @@ -1705,7 +1719,7 @@ SelectionDisplay = (function() { } Messages.sendLocalMessage(INEDIT_STATUS_CHANNEL, JSON.stringify({ method: "editing", - hand: that.editingHand === Controller.Standard.LeftHand ? LEFT_HAND : RIGHT_HAND, + hand: that.editingHand === controllerStandard.LeftHand ? LEFT_HAND : RIGHT_HAND, editing: false })); that.editingHand = NO_HAND; @@ -1775,7 +1789,7 @@ SelectionDisplay = (function() { that.checkControllerMove = function() { if (SelectionManager.hasSelection()) { var controllerPose = getControllerWorldLocation(that.triggeredHand, true); - var hand = (that.triggeredHand === Controller.Standard.LeftHand) ? 0 : 1; + var hand = (that.triggeredHand === controllerStandard.LeftHand) ? 0 : 1; if (controllerPose.valid && lastControllerPoses[hand].valid && that.triggered()) { if (!Vec3.equal(controllerPose.position, lastControllerPoses[hand].position) || !Vec3.equal(controllerPose.rotation, lastControllerPoses[hand].rotation)) { diff --git a/scripts/system/create/importEntities/html/css/importEntities.css b/scripts/system/create/importEntities/html/css/importEntities.css new file mode 100644 index 0000000000..61c75dabb3 --- /dev/null +++ b/scripts/system/create/importEntities/html/css/importEntities.css @@ -0,0 +1,160 @@ +/* +// importEntities.css +// +// Created by Alezia Kurdis on March 13th, 2024 +// Copyright 2024 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 +*/ + +@font-face { + font-family: FiraSans-SemiBold; + src: url(../../../../../../resources/fonts/FiraSans-SemiBold.ttf), /* Windows production */ + url(../../../../../../fonts/FiraSans-SemiBold.ttf); /* OSX production */ +} + +@font-face { + font-family: FiraSans-Regular; + src: url(../../../../../../resources/fonts/FiraSans-Regular.ttf), /* Windows production */ + url(../../../../../../fonts/FiraSans-Regular.ttf); /* OSX production */ +} + +@font-face { + font-family: Raleway-Bold; + src: url(../../../../../../resources/fonts/Raleway-Bold.ttf), /* Windows production */ + url(../../../../../../fonts/Raleway-Bold.ttf); /* OSX production */ +} + +html { + width: 100%; + height: 100%; +} +input[type="text"] { + font-family: FiraSans-SemiBold; + color: #BBBBBB; + background-color: #222222; + border: 0; + padding: 4px; + margin: 1px; +} + +input[type="number"] { + font-family: FiraSans-SemiBold; + color: #BBBBBB; + background-color: #222222; + border: 0; + padding: 4px; + margin: 1px; + width: 90px; +} + +h2 { + font-size: 18px; + color: #FFFFFF; +} +body { + background: #404040; + font-family: FiraSans-Regular; + font-size: 14px; + color: #BBBBBB; + text-decoration: none; + font-style: normal; + font-variant: normal; + text-transform: none; +} + +#importAtSpecificPositionContainer { + display: none; + width: 100%; +} + +#jsonUrl { + width:90%; +} +#browseBtn { + font-family: FiraSans-SemiBold; +} +#browseBtn:hover { + +} + +label { + font-family: FiraSans-SemiBold; + color: #DDDDDD; +} +font.red { + font-family: FiraSans-SemiBold; + color: #e83333; +} +font.green { + font-family: FiraSans-SemiBold; + color: #0db518; +} +font.blue { + font-family: FiraSans-SemiBold; + color: #447ef2; +} +#importBtn { + color: #ffffff; + background-color: #1080b8; + background: linear-gradient(#00b4ef 20%, #1080b8 100%); + font-family: Raleway-Bold; + font-size: 13px; + text-transform: uppercase; + vertical-align: top; + height: 28px; + min-width: 70px; + padding: 0 18px; + margin: 3px 3px 12px 3px; + border-radius: 5px; + border: 0; + cursor: pointer; +} +#importBtn:hover { + background: linear-gradient(#00b4ef, #00b4ef); + border: none; +} +input:focus { + outline: none; + color: #FFFFFF; +} +button:focus { + outline: none; +} +div.explicative { + width: 96%; + padding: 7px; + font-family: FiraSans-SemiBold; + font-size: 12px; + text-decoration: none; + color: #BBBBBB; +} +button.black { + font-family: Raleway-Bold; + font-size: 10px; + text-transform: uppercase; + vertical-align: top; + height: 18px; + min-width: 60px; + padding: 0 14px; + margin: 5px; + border-radius: 4px; + border: none; + color: #fff; + background-color: #000; + background: linear-gradient(#343434 20%, #000 100%); + cursor: pointer; +} +button.black:hover { + background: linear-gradient(#000, #000); + border: none; +} +#messageContainer { + font-family: FiraSans-SemiBold; + width: 100%; +} +#testContainer { + border: 1px solid #AAAAAA; + padding: 0px; +} diff --git a/scripts/system/create/importEntities/html/importEntities.html b/scripts/system/create/importEntities/html/importEntities.html new file mode 100644 index 0000000000..a1550a642e --- /dev/null +++ b/scripts/system/create/importEntities/html/importEntities.html @@ -0,0 +1,77 @@ + + + + Import Entities + + + + + + +

Import Entities (.json)

+ * URL/File (.json):
+  
+
+

xXSwB_}+HY$RX36d|?}5wjv1<9F33~sITC><_4s9NX!!nCF zVoYtedybBdE#Zq`I0UaOb2@)FJG}bH<+m`tB53HAJ9VHxB;(QR%j?Mm4s9VtTM7Q- z)4<8~#d+{zZ9ehztl6LwD%5&Q9VTO={aUXF{suf0z7@*`1QU2@gC8YTa7Xd+$;sK^ z+xeH!buv@w%~>k-aM-F2C$LWnw_L90ws;Oc6a_F8hv55uESu|plnbb zpZNp)P$(FJGG;8~OO%?ycrbuXR5lOWo3Yp|fr&Mr%{KXNBbi)<39^w;WX*!KffN66 zC-glOB5g9*`ep(yQo7xJ`ZG2MUEODO2AWcU0UQNR0?s+S0=^t+3}Au?!~&gkIvfmE zYZWMX-cF$y-pXhj%arO?htsN1y6z)9-?7(QjZbv-My1&6AYzpo!&mFhIe>vx$PelOJ(BcstujZk|0UYE-O8=tA=jMatqJk|y`o+NQ{dg`~U zq+%||{;O5i*i6}Sz($x8wN-+dtah+)d4T>e@P&(DPN&x_8eCmogzz;*0qiv+S`@yA zFD@?Qg)Smkz#1D}n!4#ZIST|z!Sm3?W(Vu^0ibTf7)+K1A4qNW?t*{AL|mqub50wPQ6%yZFPl7EBbKE|l}>d{qV#7^;|3S+9j7k=0UW z1^mbEPWd8qv67>J0gT{+s@(EE?Ky+Zni2Qml4l48UlOyQe9>K<5Ycj}dLp$IORPNc4**H>k`b&CNo zFooQJw7Y}q#viBfANhTpaO845LI}g}51hmDKMJ5S;Fzou0UKG6`}O8uk04_}3*-}W?%jjm zq&JmW>=vt>$KFf$=G%15?{#_7#@1ydkI!No|NFK4rR=(fz#vT(f4nm zAcc82PVd|8Em+Z5|BwNA0RzZ|!v+u3Fw*0STqs}hjCB3=`=70+EMA^f_T)PNH9pHdeTXIVpRjJX|j6Q zI*Y~5r7{yG(p^u_M1fUPt|GcCK&}13+`L9F3UTe7StA)Sa`gLS*g7nRA1xpXRa zeR;XPlc|K%YnNm*<+6a2dr9r5T-JHKb30qGbN-imWcS4>3qon7K~7#b9;R!!d_>s z)BY3#Py!1;c^QuEBz26?0-T)U&PFb(Y$TJl)YV23L5vHX7O{xqvFPPxgdk(Cya?Ay zVU>J7UvMDLPj(AE3GB?7U)SHSGrRCavb|~1Iu%aAfm(E}QgY=5_>2b%f* z?eD`{-KR^QffR{qZCZRYJeF8*DARcqq--ehZGo{y%$Kp=(VUn6-pIkPE>sqK7}b<^%1YyVAG-X22t1+ z2c=g3dOvdl`-}@^8cz-TpdL}G|xeJ>HO7#Y7s(9;y1$00c;NCo- z*`cw|okLP+Y-V2r)K=G#_rw>N1Vh1fp>P0((WJEw`#inX zKR=YLeOLonsWmjFKU(75w~IO0<{Qe|4+-1^NR?Y-geTTRcOQNsD1g)ySqyJu@1!Rl zujd#Tg_mm?!?m40uq#q&IxEzkoSmL&F1}q{#*(>uXW@k1bK6j@{tq<(sl(EZe7tIR zTa8Q-=(@C69q`hvT&NYB+QoVW`S1^_hI)GP~Uo~c&Jw45P> zufQaHU;}_h1b{c`Gg<5<*UOiv^;Wyx<@FvZrjvFRJ_rX${GZVP*uEqG(bZ-6k}l67 zxO@%(W9=Ix+U!c&NUbzqj2L)_U3ydAQLE1y3`R31XVsLKgowjfU!Qhj1IFkFCpP_k zRciR1*V{g>lfme+>1_Kr1uQeHR!)T&Ct$QmG#uqaHDYtGKA`ZRSpA$Ws|iT9r~x6Ld7FJO(~C{genrXmKlA?RNAFVzf9t z`p@T~6y818K4E!XwVdu2!3sY@ukdf_vUQay>SgRvwZh$r#aIygOePi=OzE;n=Op$A z%SN?~YYkGXWqiIXDt90;9Q(Z0L`M9u)2LFKl-A-B&eZZ-W5$rNzFeEkqrPk@RTXhOUk8oJCUS0pVK{O3qB(lk?QV$5`z1@Lt0+|O? z*&Y+{w!h#>%>6H#ql+LN@LC-}x&M{}*eaQOs{g%5UR#-2;D& zUKmB@ea8cRsWn(2Y>`qcNyo23;Y-@~O+Kw=cND7Gpr({dX>OXI0vM>-uv3E`ryGzp zlM4otzz2Gc?FX~~_T&Kphz)n)KcA!I zDk76fwt5WT+?02P@pw?L7qhUq_JN#~Qt1aL*8P3SyN{l4ALn8Z!GCkP?c-E%F#M$` zc#B8eP#FNYzG%{|3JMWcH!nr!!+LQ)q1_H(pr-v!F>_5!>>;#z{ef5j48J{N zO`v>{?ROj1T$1vbQi*_{Q^mPvUi^OvJ=Fc`_EmPnE44h;K{-4-wpNLVTEaRC6x&S=gFJeh>v zV|ag4-Vdv$yerJ7<6g6xPsl>J`L!n}g@{tU;RGd&72r%p#7}P>7u_SBSlhp+0KOCj zq|0tq4*&v1(WA#t{&;j2*jL^ch?-}(u$f+8MIv3Z&1?oY{sp$dczU`OzK4U}JrITo zZ!00UM(yk8cYnQo{p#IiEcWfyt2b}ozWelrvfYRy^7-AX7tde3c=gwpZ4>_|fTGyR z)5nh=2HfO<{lHsX;_0aevAlW#o|N2MAt04VrMV2F3X%hx+6SuXkX1P9D)C z69O+6w82vmH7!DSpGdu5gVHhznT~|dR66H_vxg3y<{7`g&o)GOPM7oHGgJ3GNH@{c zK!1Du+oRupz5n31#}6e+@9pAkVU>uNZ_~Wr9zJ+*=G!E9#ooPn4U3w+c}wq@FaP@so144}o_~P_J0`(T z5QputUEnUjN~OG4bOB=7G9!(fH!NZ1>Yyecw7RTFuV(? zmzWB(0BKm?rr8~=oPZ^Xn6kGF)!r`LYb|Fosnk_8a(Q`~-BtHm1(*dCk0xnzuu1r~4a)Th)zJGPiXcf5A*D+1ffzg%+G+eR)q_8=zB9K_)P$MD!zE6HFThNYCnzw1DfE61ehcDF!YD8>YprI7V^k}x`B3m8 z6pH-#Jx9Gfc!YGWRO?O=t$I4@;!ZsRN`uLySIc-aDhqI*o}31bJa)Umpv5G7bhC_l zr83|E#d4+58CkW+Y(Nb-T#7wMJ{gcMX++H0UG6_-0NNw}k?=BnNz0le0sgv|sthO$ zKmy*X)x;Tq8GI}aQyFBy6bg7|Mz7n=BVPaMvFQqo(&cI~Bwt1WCrqdFrRD^&i2IFJ zVMgpU0RT4MOMU)Iegn4tffHkRssBL+fF^_Q$!i1D@ctN71~4xS>K(bs)Xi`FNNZ3T z0McMHv&kZRetZ_tU3_7>1%Lw#w%n;~pVxm>xx9p#+ya#f)7I%>!h3<=rBgTC?fo3h zmQiYC?0LU+9gZbxEJOfkoK0P@>IXg`9q|ygv-?0e>j$*()D>>Q09vhPlc6_a`E+Le zqwR%{AS&;X+g-Yf5Dja7NEjs4Zm%{J4)0-3BC8k10otLAmtb@TaQ!w8DSHJ#;lu^R z`YJh&3bP$VleO6Op4>SN_zm;ff}?gR1U%-v z4jxjkQ2;Potx^_nfmWY^TKK&>C6F2LQk&Bu&15P9i%(0-{J`;J2H*e+Ks|D~Z!QqA zsSn1n+)0z#1xtVvXf4}8X3`S;SSkP&xy|PZ`S$*x+Y^8>Jgx=-mjo}i8l*Qdpa5j^ zwLT({WBq!m*o5jU1o#1<^W-@x>Wkw;C-6)CPci^Nz8{Ed0}z|hR389x`t;djn@lhq z{)kSI&|kou%bfmlru=sC`8=pU`Q`L%tLETsy9e$9c81)F2;!aL2JSOI`0OXTZ*EFw z18}5I|NQe*!~-uRasoqHw$CYXD=2?`_T)q*>W!wfJq8+hrhvnwAEpYoZJI+uEbG2W z6Unf2xE?+J>rbNQ`&OO8#Tc&vw?>{^mswaca~CfuuD;&KcW+<*8#+c_{Pq6x zz5q$5(<_R6+f(BZ5vncjQ=jtd=bpyn^I&_FhZ|7+7ER@vdX0wc7%Tkc;ctID1@HH- zXHTC#da$ZmXY-w%-Z`n^o&MgO^Y5JnJWfDDzu*&m3jTE7On!X#Et0KcN+qpS#B4h1 z3sv^}&whVwzTK*#0rmrGm&v^SZIER*&sVvz^|$!4H1hoU`L~@Khtktue|_}ZL(ApgpMyiM%X6qr z${`F3rFxU4kV$aone@(`Uw?c2vCDu z2+F$)CfEq56E&EMK-Rm&Ckou_|3JKF6F@?)qpve1i2bx(pCM5NII7_BQ) z;5~*1a#)aAKgNj%KU|4jClafsyudO|>~%u+C$QG?f3Pq0e-<dsDR9N_ld1L>H7B_5&ZYHJ)nd$#s;K6^&r(m=ZHR#l~U zo;`hX&uP?D>UbR8KLq+z6MOqHyz5GI;)3bflc&%A`0cLY%eNrh4*nUGp8r4p&nGVK z$|H~9@CWWaeDqjb|AM#9d-3AU?sgr%>PT!YT4mt(-vep{F+Y0p$0>P?3Y5bWiDW7b zNltXkWAKJwcIcn`%fm;Hem!ZufA{V@e8q9ut>(QQqSlb&4eKbsX?A-+`4vLJhX@0K zd-opr#+UEjel05=-M@S12=C`-wRt?&tI(GZZ(hE5{q9RJDzIBj24p#(9X>{=#rfpfsZ1%Sih?z0F3oKWDo~e79`H%Ii2}z&c*F{@$Ii27cr5n{ ziNtk0PB!ID#NvBb14k$2bC7Otuy+gQKf=K+C}2-Q*vRV5H)4wcAie5~*B^r$b6)@W zcYsH`qFbcve+07QJeWWt!3*&5+d@xWBe(A!NX4KEb=>(r20(7V;GldY&@ZPG8P74S z%zxM0Ph^UFb-P<))cf}G-Pf(kDTvGi6%T;_`qz^uzfIqN`uL3!;&W>R@U``b(sI$} zIXS&|lz#u$tAFEd1)n!|Y}y-Ox(^JdKOWuDDU?s1IJf7C?*f_yT)9u4{2th_lOXUvapUq65T z=FPkKQNZJJ02@tdvRWOL~&fVxk_vDxbRY628&uI8h6@s zCuKbH<>MQm+`Kw(kv&J~xOlPa$J(I!|Nbw4|ChAjlvbK~fp8RXo=^ES0^{l7K@+9= zn8VkPCy1uJig-L$u~7Oo0HfOp;Qi(IyKaN3kSzi`;2%Ro?(nRh2iO)Pgva9^sDx(4 z=K^l5{&9-WIm>sp7MO&Nxq06$CK8DtqYqTQR{tx!OKU)P$pd8m#wR;p1XoDT@jsbU zqgP=N0PG&P+D<3C2i`O*0E72D#Oo!$Y=IJ)u^W<-Qi>Rk6H6cAsY{8_HNHFd4*?@B zQs+Ai+p1cb!E7#4%X}$k4hfK(fbu+$l3s3n6ilPVaik3f&*S;l7)rT=jcoTp6$l(_ zky4~C>V_7TZjO&jalAqy_}CD8m+;^Eo4(M zd~wavhGNyGRw(4TNtPiuQwn{3c)goUgF`7jGUX&-&*}Sdm z>s0Pm53?7d?Jyy{S++9%10}J6%S#T*zikhMj7`Am@Lhu5+H%pplE%-0`f} zDi_i;ojD~I*V3sbB4uXPdUMgJ)u^!>`Q7{}I(*3&tQi1aw2|o1XR+B_f?(u46z?La zp~seJECs+%5GAJ8jH;y~=@qFqTAis>DiI_36ei|*5s9hNp0W5!gX^deOVM|Q5M&a{ zDS^s4Z0*T0%j0Q#2*rro5nwC7rFka+0RQw!L_t&-?Pjf>*}=9{Mv-jKUuaQkS_gyW z0HlUWcz3~q(SwzKzR`aK2BLpA$`L7bkwW5fliL5YK&2ESA{-%|cBe7MG&=hbXDf8$ z&bty+vr)HNNJ%-tB?E;a#A+b}drX{qzIjp>wVF-23U>$KC(I5HD*FqE$NEf|jHjx! zZNN1+fkz&@-QL~trkIa>M@mFbX~bg!)V;qf$QF#?POuCIb;75z3fz9lL)Bh1h98qv zdU98{)o)gd>0|Da9D-aN$5@xd^?6x51?4<=sY4GYer^7ip*BCtdvS+ z$x*oBHcW8J2ZK!3$t*h( z_S0WZPfjGAa;4Hi4K@*%GsAOgNoY<2{=kvPZMT~AYD^@UQF*geG#T;1>=`x6vP6mL zX0>v)CNbgfnOr4`Zr+n^G#Y)wN~N*BzS-?@$#cdO9J^B&-@c!N;aRj1u}p61 zG-_4UedMt=t|L)3?my6K=X#TgUCb7XLxjzmFDx3l8W;&K_mMpn4h1jqIT09OA{znz zax$4LxqL8C+i7r&1?+<{BzPKTwWsiMOncNq?@1sK@QtX2e!-Mj?XuQrYuNSRWtrgE zCd|reKRdG${%W4yY=uR1z(;(}lo+w-VSB0M`-@~cyJ4?z3BcXH%@QE7a{|AVNI_f* zhuOvgOXIsprjTf7^QAzgH5wf*y^yjEn7x8gC15H#Oq~lX(&e-Ut^*4(F%YdDzY4g; znz&H&J-nS)3{PZUb3NBD&}}(HN@BhU=?@ReWwlE83xZs#)O67U^8hsJeuSoxa*tq@ zaawBJt+D(l1l&d<;G7||$rpU3;-xMrXf%=v5sJ))t$(1I3;ejhWFD;{BpLAhxP`Td zZ?3kg2f!9-?Y$E}f7YrM@Q0Y;owhjKz0Rl~fx$vK$gtO5jlD*^Zn*bja0SwA^lQ^p zPAHd^I6)&TC_TR8W5;Ez*qk9exxwugvxePGVx2zV_1X-WWHLi}omyTe7oltW40T#f z8X$$4NWWFe6|0S@LN1ZQ?Wrk(qFnBdf3)B2w&!Y%x!5GlK)v0nQO#(0RXI)GADD@HhR|U%5miOk@!GeLMd2 z*1Iy9Xt$F#_kZKx<^#Mc>vSLep9S1@U7WS;oVux`It4K9-U4?tid%IB%kkCCP%(CD z0@@;L&+hL(Mk$xZM4lN}50ki2Fpjc~yRZC_phYgKFuGhq#t2_ofb5h3B(kMN0ia9{ zO!7&k=!=dehq*(~SwN4MA?-G`dMynqjmoNR*OaO)Wf<=scy&&*QFLPG!LlGf#ByeY zp`t+BnT>j_T4~>K4m&fTA4$dD}8k=sObCf$f(PGe3Ww<^aCU@#y^57+0&G;l|FXX)aEgj$9*mcm=W5PW^$;Ao&9W1yZ==ZuEL?V@A1O+PHwx0-2nL?@(OI6l*AK@$Y zcBjuNCr|wt{r7SS!|Q_#CtRV^KL;}y?UmTYd9*kHbk=2xwdRZtV-r)tb{_>!{MoBy zzBh&CtYJ#IdZnSJk&|wj#!ni+MhOOz?HHwd|s6h--E7DJcg*v;qgg72G66b zL<|D40nXX*YW0WFx-KK={Zn22)8GWNLcvRUfaQBqSny=VHu4L5+9_|hi|xUm@izUcX>7VrvV$#NUm4g#6x3>C3~MhOg+cD>nDstwj|9w-$`i|5F`qQ+7Rld!!3 zRBCCo5mYKSSUu&)RRpkU9#0501|MYuoyEW#J3&;#2bNa59ao6?K(n0)lt%<0JfsfWc_9S%ay|AW;r?gWDBjxkQd0xSWp9@s{MFqtW0i29rpb}Mm!kwxAu`k{7m=29W3!qIMi14XdvKo z+s(pqwZBBzV)aomjY)XyO~yWYmt5mG*fNp&vU#}VmoUBSE^o9rb2ZPjX} zV&33eO6z?@C{vo2Ul$G_2PwV#5#H85&@!@Ku?(N@#OI5KT|PFOjZ&KmE|EGM$5jRG zN(+Y50l|xZdlIvZ(+O|93v4m7=TexytLS3&#j{!e4t7Q88s72*-vYbAb3iItqn@y_uiG zVY9<)frxYyE%*#=k^zh-;DK2y51LkIGAW~}=rztsX4j}hr9U?3bebk2Fxwqo>6hU7 zWui7@aV08KsXtc|Pg5$SbV}QptLyZ+z_>MEUFlZu*$dA5t#U>s)o>ZJaX$vuXEK7P z{^PU`b+Zjp&??8n-BDI-n*-3LO@Q$F^@(JEu$sAx<~dabKt?z2u!Di7m-5(vF9&*R zzFw_tySS|nam_CJgNQ7S!{KxYL#w8|m92hUD~zU-8Nz}5^M4ejl2k6_fx(%LHmiva zT!{Q2r{D$#AiQH&PJ_wo3wlvI3N%3U_p`5mwL+7h!T^4zdm!n)r~@reKv;?W?ZQ>(`aud22-Z>44$hYMDz|=ikjFpX|eEaq0epOh|Rb?Cr+Mi9NxY}I7qm!WUqQnPUFGMfzAm2x(l~<5v8`y35AHm=5fcP(O0TO+Afi{1>?qxW88Efh7gKHQl!^ivB(Wrd^lU%j`1DX z?QX}WDX+cWY)~7wuOSh@A)#ut|3hf6M&Ur@98)Ee2$8{PC(nzDo8ECg4!?W%?&JAE z;{}@ZRjAryfCH>07IA!k_dq`E9!P%2@uM_90I9?2_G*IPKfinT`qhh9uim`-`~CT~ z^~mkQbAu4X<2wo5(S7~+?)A&(&tJZL_4@ti?%A==11sX~f#%^Rt6+mFED#M$0OIMC zN~H<`Rzdk@_PIv-GWhM|+YHBGRxPhTeGbm17u)(H5nul7*;79ve?WeIhSOCz`0?Fe zuVK#4>$iV@_}ptJUc6{aNPVvtIouswdU|?xsy+Yw?L2Z0{wq4Wdv-?DswBZMoIS-O zfdQzF9*#^Y#~QFF6M0}zs#fbFB_@>svrcCKJSK6yVkMe8LcQE(x`SX9jc=_h-yOmWqpCQ9K9^_0xayT3e`v_koyvbdn0y_4y zr;5^fkWjB-KY;OQI)+(KE9_0dS)+Eoc5u(m=@Dc+U=bms@n|cL9WVeUm@VUv^6}J8 zx8066xt$Fhjs+&?t~jf4?pHe*{G6u=%drrp0Qt`itm%WAG^W<5q}SC-9oY6-m1<*i19qUERjXto&fN*8-gJ*Sj@I|G8PT38Ao)^mC7+&C7Xl23d~lU zOM4am{yhwTFdH=sU4$#WZ!czadl&UM zmP!F7(<0+2n4C9D8X0)j5qM9(&+YgY4pTa1jPW9yCKe(VugZ+WkUU6I>>WwnYSt@8 zl}=||+W-s?3pyp&a5`NM?ziyy+D*@9^EmA((G>m`=w|T=oPmkxkwdxRc@Qg~2o0E4 zB^LFEn^t{$;jlZMTTOWzjRtAHvIHw5k0(}JZKbP3W-mkiKY(@UFh{Dh%P+33H?fQR zaI=|{|44La+6z>p)~KOa2g<%0(EM7!DAmZ@7cUY!?Kx_-%NH*`5Osv9xGBvKXzUuD zfuB#u@S?dS60vwDFE<#qYH&A$#bRqFBUft%F$zaxxw2ZTRnRMIfHRG^&!1lrJxI1u zhRv8s8$gLEG3E^}KtO|YARka*bd%qKMEm>(rt<<5B{C?zhFP|6H%z~sBsJgX&&RMq z`4#!)`E!Cj4>MI6hG`x59&5p<<3%AMC0G9U|3%{Er%#{Wlb`>u{|`wIS^PHd3taHG zvLRlp;^k&*#CLlC;iJbVX#V2k-w+=+6v$?y9HQI)3L|LEL-GRu6Tl7sgL-3{DDL_C z^`$Ft=T7|d=P!S4=}F$lxXtn82>%AI)dA+Vc$eu`D#Ze#Gg=+1xCg~Acki!uMw~)v`^H7Y14{Pt`?rn!M^GM@%Yr@tLquJ=sbrY4Fm#Sd*M1&xXJ!wI-E9J?K+(xl-FhoHZM{Er2x8GdXXFx zR*Z(26yCl5>)ofVP=m;hjiWSlDV+z`WSHBx^#20} zpawMGtOEVNR;@Kum}(vA1@v_s^hf1cHKkZA`s)9;9f|;`VC6l9wV1YEkSA5jrOjrU z8o>nvcgppqQjW=3h(d{}$K`CE${^~Eb~nctlAGQX%v*;>9`L7XrNYGb|M}oQ7@+^^ zVfP8HKg$%Vjat1I9-JMYLCb`|`t8W?6l)d(zueJ|2$;?SfTYgO0y`=z|M>CmPyY23 zoc%8#YwlD2-e`6EY|-Gmw|~9F6@?9d+y}h7b-fV?PicFjoCIWrlMr`q&0TR zjMwYs5?iY=ZXBRxAKO=7L9_-mWUW&>uJp`0C-yNbII)Qls6`1%nEx zp7@?h$AJb`G}%mm2;p#V@aEO)_a8pfloqhg_pih46W~(xorO0$1cIaW;sriRv|GP= z`TDPqU)Gj+Zm;lT?N2x}gQGAVYQqF~vH|%=e(zBs5{@*uN)=|RH>*Wb*C4wGl1<=O z2s{LnYcXFDL)yA3{EsInJ$!6#l z9}YEm5gvFox)}~IQAja8t5&TSwO*fBn~H>2vJH$8l9?2!V>DOEWTm!fIN4u)bOrm# zL%Us)+m!e@m9a2)WSQ#Ub+r zj*pw)zJL1~%mXd0HUl?aB^e@OB7HHv=>f&IIqk+yBAJ?7%w}07lh1EhlhNsRi2zfl z$GI3iN4{Y!67DgTBkC5{PMPtUOeRr@`=jZ+jX%8H?(?Yz1@JdrHp)iCR-RM^JN>!c z-XrU5(5tUBmEjV0=}lAWBMF^un>LwB`;UBXFpg@ajD-)^p|m-oUl?Zn<;)7psnf@9 zZ>}ZlIB<3v=zR};3(;lvRyN?F=D$6%DeoHb#C7!QlDg;@$^ksl%BU!nK5OaaGlB~mOrZoI>2B7yH9ciyFuP(K*ED(r5TvJj0%X&L~+ zF&GS3JD<&?Zns-qibk$!3_=S#yeC_DYV#HjGJu&-i79lodZlRc9vyKn0RtdkKn7+$ zm73%!V>ILNC#h6sHYMClhhVh_?g`S#+c5xRXOY>*0IVm+XQz#i10Tv>BI_RM5ZKA- zFUQ9|1%ts7=$%TjfW;V)udMQTkCf?)ujf(X52#tjh`?+%Vhu2QYpX!$aNG3tR4g$v z8uhAfxma0CBBs~lbxosJTUM+Fu#9mwH^4l8F2y~wTV{OB6N!0N<_zV^WOB^taC-xO zUcV0+XM?!}WNxsi3}C@I-MaSWRXnrdO0@z2bv$GayHg&&wg>!fhux^xU?LtH4hX)8 zCscxB0M@W!0QNKIZhLUzuX2faiee-mo!kio7Qx`>P;l$lbOz8Lmf^!)uW6IjhTqd4 z8vR%pAYLe!z}M)va5PoKBeFIalz~ATzuJ}@9O(#LGZWjKATAZoOXW(rOvGmn()f2O z{fm{DOf(FiUjP)y-vhwgmx`70<{pg*ete=(LgQ(+P!vjMVm4hG`7NMIDkHXB0UC&Si~11!`^Vz?X+9KtWs-IE3t0SYLtRZ zeLEp+Iv(_tx?|Z~BJOXxj*6WQr}I19x7+y8Q!2TT=biHu#m)qn|4Jb5ugaA&7|mI5 zF{+idu+twh>6aRnR5rd=^#qtqz+o;XgLZ9oc^C)8L@gGpVS%7Lq*>G`c#LzBQun3J z-3UI94Q@-@?K8TMyhr>mk+qFDZvgE7sH)W0Ds7Nvq7^`B?R5;A(u+rcyibxGf@i`r0pcYDlgoOY zHk~=`$E1yKQ}t)<^ zMagw+^Z<>S_z2MJ^;&a@Os-rD6NN%1L0aH=A29W*N@udxNP|&m1J(snBFZ~c&~8+7 zcYHi2B1p;Q>o>&9j9-2aQp=3?u7w^wtAkhJ$mI_G&t~&EgMo4oR5vY=Sq8P*ihfYOAG$T3!x$Ee#i&+Ecx={k z-0v2P=>)~Bx7p1awV2D84!Y%nF1Au{3^pTRg95OWCxc$4l1r^T(Xp1hL-oO`tUD#awjSZxu7~b=McA!=h8k0HM!D?OHjLT&cSI@9~cr0IV2L2g1Zt@+)8AYY3)+Rlv8XbJ$@d7GlBl>jFeC2!ej6!W=!0V=r^4=|oL zM=3$~W9$?#BT*?;DlSY7yKM~sH=vtKyZx0ZZ91Xrit4T5{R#wM9Cetqs$!+OtJUu! zv%oJ;CcSLHbspd0#IonXFqFWkgT<(?ZE>x^;+$NE&ZE^KBGNYJ{0Fw8PDL;Pyl|vY zBGbdKa)ZNl?A$PbS)M&)yQEPUQN->{NnM;6JakWQuqzkqE{DrQrkP~5Jznx;`jSt_95w3eWd1#AbBDop znDr`gvPuo$H<=v{TPAvW71@men2d%)iA14&%+d7U!-s#iRW{J8#mm<5y-vGXhe-jY z&By&_C8v>JuFPJ>vDalc0IAIy55XC=_<~qKF2BR7$E17?ii`l&7E>x|80TObANain z$ZZsXZI;S;jVuIL6IgGxy29tRYUMn1JZR(%QZ`xU&vfLr8`WS)Srd4vghD_#Q4ieX zA2R@80H|OL5EQk&S{a4{?34w$5tFn&t$Wa`T61{{-%wg?Mla{@OeRA$!Ps)ARV`R_ zYNc!@U#@i!vCcyE^ZpE$GZ>S$&N-t3W(2vE3b`D^a+L}-l@VaKzcMUEv4YjO6=r%b zmqH#Je0F9sTio6c-HV(Y<_n?L<`x%S_Fe47bhDpds}7YSa#vC7ZE zplFmzxfs`e`rU2|Q5!uIO`pM&>#R;wQ3h*q6WczuO1Y@j8cenp!8pN^)p3(0DCKk6 zgi3CA4;DOiZO9bsoPLk98BOf^OrnBC9TOtsZf&}rig^q^{b+lyDmClZ%PIwo5(MkG zSvR*P@O*D!^;(oh;l_535NlRh{NG=yWwx4e0wtID^2bB z9gU+}&aOvnPukpma6AH`&TBVyQuz{vv1trO%On|%Uhmle=mS+wV{k5Ns3u5M5HxP- zL_|Q7|MbXVF`%u%2x0KG$EBoFh%QNK76=494$BDe98~JAC#CG=7-|Rhz#Wg>h?QEM z8N!#?`*{t}Y#=?W0|CF?qU$z*>B|+lxW$Zuk9>wYuvPI2JvsH-2GbE+>X@Y!ocRbJ zQbQo%b6OOYZVzEg-R-PW#GFEjap)fZlmRGJT9rIptyb`j7#Qa7-7O1pGctOePH!|7 z%v1(oG#bskR3??7NkSK2P`es4M2bg7Q?U$ealXdnS+$p;LawK7XU$W)j2y|m$K&GG zzDNqXm14OflgYG7cD1@21hCa#`H;;{g2lTlRM{loFQcg~>v=y&D-wxOD~2M-_zEf_ zb7);e7f@rLu15w%*-+_i5W6kIoW0rB09#&wGk|8JT&k+PQ*mX$;3`!{`PZS=0L&$V z0Tc@vgVtQEcjhqDQ)KhFtmamxkOvEOmQX5$Z10k*Ff~U^p~~v^FGE*XiJbwff9>WK zTo}JxTD!uKt!j!Z zmDu+Z&3ZMj*DqB)bW+4_*gfZC_WO%Ox!D^pS$u`wabL_?O!~NM*6lkB_!QTv>Hvr| zgZ&IVerSo0!e26i(_CIrl~dW}Nt>MsPk_867vJU%z?Ya)iXi_D98tohI_)>KdgB=?RM|VnXUM4Eg7ID|x8Lhm zd<#d4Z7{BmKH}}cj>(AL=yW^E=ix-9H$eqTb9L-zj%vlsLT_;%1zO*);-&To<;o0= z$sKRElEoc2C`aRtBq~6j3o&c=Oy92Nbzt^PHt$g&_We3pA0qJ7(wIi&>v|BTrjlk;S$@__P{PH41r&+dy z5B>IaI>YDMj9QrpSOueYy+qgH05{G;r#Gz8#nR9Ub3qtfe%LCdMP&U-3^tp$5jhX# z+H+KC?JarUPBovZBk?ht$Ke!6N5LRtD%Vspu~_^wt8N9S5&0Cljz%aRS~iErhdU&z zLvGw1-5`yhgajStr08r=uQ3lSEQqvPBaEbTwa#e9mgs5|mdMj-mh)gaS*>=D@oV^V zIMrARbuEOivXzX60-{i9O-MZQ`6}NTqatmohe}K(9VV|UReB2=3xA1bnj;nuvk!R+ zi%yLZk<%__@Ov!Ro&s~Y&J-&Q8Z~wehE`{GG^63K(eeNh>D!1NHwN>#)L_E z$grN{8uu^&z{NLxH4duiMB3r!T@_*qGlIMd8Mj*AVF1OCJ9^)c?k1$8oBs^W-A%9F zTfm6WsfMW7x{cYYtUHV&L{&O5pS@=qXP4-#na4z2#;9A#$71s?+?f@vVwbOl9Ol?@ zUsoH#e`_}3MLPN(Z0`#O9c#5tQ>q0;eU>B!e>prZ+! z3!(DVpurS%prL`uW0UlcAk{__EO8-k(~jgPaGo*h@5%U#FSO7*C>d6bsWhi}1G5mh z7`xxcHI)FJjoO8Dk+va3M^ZDnis5_2qi%l45v%W5i$;9~Q5yjVC?q41b724U%7{dh zbLw>}$v_|yi|ggo^%rp8*{Ie=HO4$55%LkCSdvH-Vwc|l_nnSfeWAH#k%|O-TqkTs zgWtl?lB(qyn6W9B%O!#VU#JvcAjMb+?u_)x47t8+P$;G1{=s|v6a!d~JW&g*E2HXO zeIbkt_$dZZN$d4Gx6!m#0Zb-~3C#cnfGSz$@O>vaFbM5-E^ka?Vj;Q&X*2H~2ZA(FWmh1Lo$TvvK#jn2KJ8*8D8m zDrYqcDGxe~Yt?*)4DcYfpI*%zW{pb7olk&R)<)NiQUe`#Mzv^yu#jfipcXK93Q5Ra zUYH>?KHpk)E~ncm2(6+G6pK!Rx8lVrYBaz!5vI-${IqCrEdIM^~L~cFpQKimg zHR!Zz{ty-*V-<60+)oOmpRrtXs+4ls0G3Rej(e*F>a3%`o?*DH(O$))RJ)@o!U4uV z&Hx7WBpInXDc71UxmYL^3|LHjb&xsX%VMq7MF4~3_Yd9!$P%2uwy>8k#MZIITww02 zOX~y|9B->zWkGI%OeUS~namawQPNOrv>45;Od><^01#O!C|P}P5PAFd)ywD4U%ZCt zEO@Fp9ZPT$;>`iUtAQOTz`sVwyTHdS6}w(8RRHs;R_LXG?6B9OcPs8+;(-S}`?Gg& zj50q*|9@{senZHH-_L2Fa0$)*Umrht_OCym{sB`Co;{@JQufaOdh-&D$G=~_c^4YS z@r-~?HeBQDm%rb>e*N;ri~SDtA2d<<~2+rdimdP-n`occv+u4lS%H} zfBY10^!vx}kM0q*HUSjJfxCAfs*_R+A}`}E>Pgtf=<2BUb^WzK#E*$*#Y{q-eW_qiSB&F<)MIO-!3sS;0A zKp1M7_}(+wCVc~@FXc<+X7@(Z50G|vGwqW?sa8WVOfF|AF|~S7E0)OUv}Uv3n@Azo9+fXhaD!|___TKQf&z2OKtNKvGnhkx;WX;rC<3PuUJ zoyIT}^t8wbnbYZT0{RcFqKC$#VIL7Gv`0BS^`Z71A85dm{V>0KnO(0jN};`-w7Hv+ z0I$MS=uMWqW>cjW7HD3owbuHwG+>F19=l%EffX7^pcbtX2JsEMrFCn)AEMT5cMzFG z3{-HLj8|^ArmHfl^U-J`5D58;R=J$DU?%&#GQj76$l3!R6S(og=i)9$uGnbPC?kK+mK3{-^-?c`w*<4L0sR{rx><_wlZF!1y44zvm6^oH> zzc=8)t~?yT!0rvyW6xmsL@0Yk8OkHOD! zIhqV|*!@uWz2B=W?SQPRxd9<%02!{C%4dQ?N#;$(`Ia>^Bh(Y=QQ*wuSj4kca{pT{ zrg^8g`{nE)n^nl*7Ca0xC3VAfCG9V3g3>+2*&pdMl}L=o_H?Z`V~aHwr;NK8^%{^N z*&H5^zx4g$GTTH1YR42k1%`)_MwM!(-Q@~G(BIMN^zcI$;PC1>+l^|$V6gQ?rFyFi z?7kx8>R@4O{BY}c_IkC(m~1`=#jBm(Tsdh!Qrc3jqgE<5b}W?7S+lc`$m=79SZ((? zSeYUbeG4G>+DsZT51I9PZ*R{4di~xI5ekHW)P*8sI+4+qCN8!M*@O(Wwdbk^scj@y zY8w11y-z$KN2qeSw4rFCP`g3tgj#_Vs4m-Xp@a88`@LQpk;)a6Kz=~rKxIMNU1~!h zmiZ3&FAPdSU_TI*x=^iEDB38B#{y|pyp>*ZD|FL3lkU`%Ga3N{Fq=&#Mh1X-iGHWv z8p!1ejJi992vjPCtl4UICU{pKz`!Ior@O8JtbYIu;h#bUmA>Z=087^}_tXOWmA6&9 zQf{-`A24a*e>Xt8ZOX4*mDzQvEFMtsW~*EtI?m~ml!V#g^Pe=nhhyc|6iR2=mfEx3 zFJ})L4J}}8w44bxY>0w{vTNE^>L0;>Ug2L5x%R&5BM|cwuGCl^oOt`K8Vmyvn5=g9 zGp0=Da~iO$p~XU0%mEl>c=4=PE>@5K_Ddt$*9dCosO}n5(JQI-Pc_ zRxJ}c4}%HYalTyhQXr-T$Y3}cla-U&KehIdW~Vm*D!72V_9_&zX{E@v6dGDnM4&V{ zweeVDHSf|BpUcoK@esLv#rPpG5qM=5Qi+LJBiot8 z+8X0*cvDvlf{#7L|3&#qgXlvnbWKmn+=1L|&?sa)9>D;}e-7UR9m1j#n8swOY@#{z zRH|7jp}>b>b=xgEg+$0Dwknyc?TSP9U?SCW zF^`z6F5h}*!{qna&Ae=(QlZo^Rv-D%qwig*w_+)W4Y#EW2e$sAe; zn?V2RRrvFl5ATXXhu>>N!tXwO{Jh!(G}%0!Q!M!Q)$148-c7pz3=b?nLWOywjTheF z%WngGFyvCP2yieiPY4vyEt?bqg@>VP%Hj$oH}{RRpWXZQ(Qkhk=9Mg-Tc26c=%IU{ zNIsj=Domd7f~PFCM@+6%V|M$zj%p%JD+~)qiJT+3Mypb00GUvi-Kbis)il5c*rbNu z3R#2BpfegR`LfAoF=l}Ru%$xO3K@gWX0LSyp%sVM_&qke^fHr%+buP~A=Czwx%`7R z4mf%Ve1&?w$YOE@WqOyc%^3|E4Zm6@rQKl&91gpE&s5!j6|Z4TV5AKBg@h*Hby-wW zE^FMa=2Q}a(}Ki)7(dA6i$q}Mmy+ar)4dG5qqQnZaG7Ol_oWYz z4QW$hN);K@rBnc3K*^2llw+5K+L9HjbVi>>tyGqHGEAv#HmlWDhH9*?ij?U=pGBQ1csdl=YW@)-utnO&! zd@hFrTM&|8Z;#RUNw=U^i23MzI%pM(sn|*#)#{CQLHPSceu!}A6esl_IE_e~!p<1d zO^jA!M}c215OE;jcNl~ptKFSVEdscqit=Te$3+9iMre)|I#k}{J054{YpzV1Ll!L_ z%W(J}C^CWR+8p1KqtsV>W7t>2WOe&$;j8Ne#R5R25&A=MX^MH^F;JCyY5|9P! z)Jm0_s;d7OGw21-TMF3{Pyl9WpaGC4*2wC0nAF*x%z4QYd@HHm<~i{?3$a)l@(Lh! zz$J>6R{!D$Vp}ZX%+BF9GIaj~z@9V{n%bzanM|g7X)Wyz!eMvVejr@8u&#E@ynnxP zP7gMf`2!~&hZ@GzD$F_=rz7&We@OojOrRByNO8n>7!{O`3fhxZloqLFj7ira1+IYa zP9Pm}1rQbsS&86!rC6!MR1YHKg@vh@;tl7s2&{hlSfNRKj*{DXL{Y!Dco!W4~C*Uh53p=YY&`xN}r>}HsPRK6@4Z!>EJDWS%3jpJpmPOaZ~u2 z+3xjuv%zq**qT6UFj|P?hwlNjleAR8S-AZYwYkxoAzYzcXEtZ9Bf(2rZl6plm0qT^ zkUZ6D71pYiwj5)ufLD1z=|zp_Gt|1SbK6BiB{CGejWK zSZ&@wBYYK$LrFBsWptRt*%>X^3DP`LTD0sP70{^1Bs_FBX;;^Gpm8vhbhcDGoEGV~ z(huxr1kphQU|3hK_9k(s!cFKG){dQrb7V37w!SrdWTz)^a?FZktAiO=q&Zo977F+5 z1?+$bTM^3eb*^=IJyxpu$?$6tfgDlYAqaa$xNoTN`tBV2)4}r*a>fEI0M97|fWc5o z*e4O6UsxW2Ux7^%@RKz3s?~ZElgPvZpj8T}t_=V#3mCnWeG{b;$W>%E>9yCE<^CjU z@=L82Q_5u$nNnk_1@3Yt?Mf6WWYlt=3lV6l{TZs%+m4V(G)gI31z_^eMu^#}Q*|gs z`hNzwoQz0M9&{U$33ySCmjxsH#l)cW3RqZpVmPL98)aT)nH{`DF$d-m;aD zI(!dko`hNRVW(zzRA~;tiW17O%Exp(5)RQ8wL~iGWU@IRg4HU`8-M}uq$-`&S%`#IEz6RaOd<&Q=4_F= z(i#DSNvbkBkIYD!s%P^(4wF_cKxe}?P&UYeR0#ae*<9*!I|`}fkHUA^cDvOf`Ve!H zP7$ye<_LpH-+HB?`gWzDlkWeE)dT=eY_hvtN6hnZI@?*YWk!dW4J#A>5W_*gizwCR zmKY}d>a8B1ctqQ4@XFX+#DWnFU}wJr@dnO(C#ldkxceReDgf&n!2w_>gd3JcWy0@D z0HkuDMM}HV?P5ZqfRC~P$!PCy2nH+|{sOwaNa)QPOf297;|Df(Pm*fr-Xv-;Ah;&e zD&=yBScd7WL-svqEv;790v4v(EJiMy$43Ue?#crPrhE#BlFOw@Yk=u!3bo`_u}}m#^74>wzdslZ zdObua1e($oWC~0t$`(?=)r$!4!o{-87VxkKq`nfw;<4P^HKj4dK?Wd^DO9XtHX7QF z!908qI772`5XgyYd*h}_AXMTI=p!h0rEn9DuL z07RI<>{Nv#q10NF=b!n!Hl0$yp7k4r)SetcL?HBNXF|Pa7)>NrRcQ{vZU+`XED>5& zI0DRlLKLewh?Um7rU;63GMTJ)ekk5VinlPz=&6QB`qDi z2hISHMW|WH=~NOSUU{hBEtm7#xm+;t<}#OqOd<0Le}ewDom~8iYs5cqc%^j)gYL|# z=mHCaz6D649fT@MR;zI`mIyj*C(7AG^!jqep=y9ZO#Ai+no^?&*fLur2U_Dtpu^y; zf^hhJXMU$?RBcR9%%l+PSs6fyJrnU{Rj{IBKQLmHHjBgFO03Ii0a=W9xm(8~m=wGl z(#5u+(VC39Fk1BHevDQTU#2`ZW+jkbOMda*=*b&;HVpb2ql;{6cJ4}F_FvJ zX9T(HlD5L6iM-PeWm)}iz=wjtNeD1u@O^#03^;(1v;h>bQ9`a-tnTHBW7wpJ2tfl# z{(V3+!S2m=lSaXuG1y4En$=0TFd^m;SRB|cE?43aOQ}(4_6Msj*NI81s?*AZcnji2 zDVwAhg9Y=*VLxPyJAbD_k@!JLNnkb|Ln+SYu71anJmw*-F3c?w!F(~;R{Q`4{azOl z1Bpw}8sBBr5<4EfZm%_y$mC>JjMSi3V}PLQ4Wv=4R?I4c*0fd*xL%aVvH}c-!C0G1 zLO$$d{+z*NS-`|!@WiXJ2a9Tt$+3-1vonRutl5M)?Y+lJA{B~ywL&5UGdY{~yY(t8 z+DwC3I(<hu9)!>vnw>&y_2On0hR z0B!mser7Kgvx&>l?8?=o6B=b9Oc?Hpf63K~T1+C~Ad5+_UCnPB)J&9Le~C z(|-WN!Jv=*ol+C82hr-uVb$g~$$lUt3D?&1F7y;ZJh3=4HyWVUn)dp)fKSBgb zi|5ErO!>54u2nFN$&3#f-)(mo1fhJfGzY_4%oz0~li2C@~CXGRQ!9w<@5X}`Y}@J+!2IvRNbDF!E+ZFGA4`^MkLj{ zLD`U^v-CM&0A(j(1E`dks$VVN&?F7VbPP;l788B5_3Su5H#+?OGxq0bt}#Y<8e=PP zy6DwQhdt@tZl~s9>+B6i9$46@-lkEAN1auxzR`6|0c+ka$HU#m-pFEfndbq6u~E$Y za4B{>oz@&m@ztaB+O4Wp=NRbbQkO|nueXRrW_-8~XTY8TQVGIb=32E}l$$GkL3PaJ z$qde;1WE+?gX1~9x!jm%+jD{5<#pJKi3HqHujDd1mCN3qaI(!2gDX^M&F*fcQtUMA z6}!RQm`N(#86v{WZnuS9Za|}h~A`u%pjlA|mzz0qpZ=0jgE;*~K| zYN(GHv?kg(D&Jz@kB80T;cnan#CCvbJhj?>hbWf{8Lb_ReSL)Tq0jw&jIz}x-nOY2MI!wqz#@%u;l~DEGfrC)*GzJm|C{eN~?7C=R!*d;i~MOqv2&Nfp2xDH7c3VGhr#r-7&%y zNc9G5s#vXb%DI&C$hQ>g>Og@7^26%0mXp~+WmYO?R3|<}pew@2M5)f^^|TY2V)>x2 zsnx7j+FI}il|GXv(HU*uTVf1qXb0~>WdI28jMzqs{JkogLpVfk18@^e%FJVo{trDb zdjL##Ab3XWBKCXH>5GLxb)S#-24bbt8if>Y-^{0zey2g5f2LMvFfG%`tMAZkZxs3n zPqtm)6D)d`5r1XQAwnc!w>#~+U@DrROhYM~z{LJDiLpIpaU|IIyie(y{{!&(A|Ws$ zx(AKoB8;xzk4&?m0WXcrdcYhW&c5I#-VP1k^;k6u0W2O{(23dSYg*=6VZm88UU|D* zNN<>Lvs@@waT@>wCIH+QJfHw@xjZhOHQMU|(IbWxwRRy;sCDc@D)Q|Ty3ln)K4#Ra z)$$uQI&vvY%yS`9U9~-9iGX>Xdq=FT0V#|Mq$(YI;w&Xf zX|;qqbKF&$Tm2=%m8#9UFpMQEz-G=^f z!j@T(jGVU|toA@OZoA0{EA&-?vh!l#ihe0J%W3dN42wkM$dl#*=ZY(X7iA z8jWnDpDLKdRwswY2c})47{|BJiXwfbDVK^wH!y%!RK#EQt!i^)0K}D4E4z+{LJ7(< z2pkrjMj_&`kl7d*d|x302*B9s{(E3VnjgY#L@367fP5OT5laQA>bU}LV06NaVMQzD zA`h5-q!yPZ2Gb3gB;PE+Hd03VYw}V-ON>PPWsZj0%O%??3W+ zU3Rkop` zK8<^P95|L=go42`^@RqL`DECs4;IsIE)u>7Y0ZVoAeq{=)|gg%DV6F~HyyxacBey@ zk0n=0C+j9&7wa3pi{NqHBuaRpoD~+sPOCNH$`vx1fH|f4ZpnGDoHin{L^@yX@N^pS3k3u(cw2A!Ei zXSJJD>}DkLJshUA>QkMboCN$Hx5I8V8};fe%$rAD_s{J1N*=HY{n1mqow-U&0ERob zuwYMXFkl4mSxnT%;t3T@!UNN9_S@A$CK-=KE-zOXkXeSwWRj_sr9XZHALc}``m!+} z_n|i`9lyQ~g;Qo8-aQVCVQo1X4w|jmk~u71MM40i`vR%VNBv%-K0%nsxRHeC!Z7f; zHJtRj%}RARolUyAXyghU4#P?A_JFi&)yjQvqE~_^E-piTI1jzlpt7C^uwu|ut(Q!7 zrXyrNY}MMMaVvXy847oF+Ej^CD&-E|gQ@{Q7?Jh38L4~K`+<3I4WSxdB77@X&gaZw zTH5~sSUnCK?6<^aA&}6@#hB`P+cC`#_|BBbSjzfNi=;vjp+qc2nvMDo99dwd&sGZF zK|lk*a-E1#Hz6Rh6F}6MfP;*BrCe$}gh{?c$Q2#48lL_;-o({G6Ak>c@;L9s{&Zjx z41-1iYjh$b*jZy+R*UTNFR4;@iU=|1;8bm|WOTSs8S@8Dz0O58)SaOs%wSRpIm>lg z@UWb(RG{<+SVDDxJWfJD@+zZ2uabd*ZI2LOu9|e_Vy&}GLaOI0bynA;Re?VoTx8%# z^hm91Hrq2S+lWrOK=Q$eyybY%sbw>9if>61@cSGV{jlBwf|RGK;H}qYv%bM~F2Qnr ziAtsnM6PZc&lRd+bTPEsB>Fe2%eTI!6e2bg5zy3Zg4v<#L6B}U{{a0R>` zyGb)`4VDNSa}SCcU;*OWSR07oW|MI;U?I#+OLQDUo=Dj5x4YChwf&IsZb-C%P&u4- zdn1wBrhhQ<^*mlFbJDLDDx!~YTvwgLNDvM3$^2gdZpY!d4gVqZRwJ~BImi+$#7ZHZ zqMJ9`!uMbz-yS1kLmSx128{}zGPe6DaN^HiCG)*0D%M%OXFTxeoz>q~s^zjtsZy#` zSOr)B)JsY<&Ta^Q35CmQG9u+FG!OMzB5a4()fuC-XJ)e3HI;7I@+nSF2V z(gR4>^Eynz=Q5D#pwlR=s>VV{{ed&TYZ1xSI#81q35ni|?M}_5u{1?vp4p~d)SL8S zIDiyj#g&|^R&-h;~Yl(0Y3fC#00 z4&I!rNMqi)BJ@>5dd3?et+-U@BBg>=JOFV{0G+SZLu3+7s}*A0^quxQ4T38b&+%xx z`f5S9Be+^Ug6pl;%&Vm$z+ifScOc8ZaJLXH`u5*rYf30C<{#6)&L22&B3*HZhp>VO zay(Bq6S0lmv@bs${0jWP{J)9Mw}mTk$LDpnL*?dRj&Mavy;%!vSJs?rO^5;j*q44F z0Q4&s^$de81!b!jPW8&3+X55V|2Fxj#C38@$pDz8*i4-JwJJz&&)`Y# z?rc5a{suCtobeN(LsS6HXf>#yLu@_*rhHC^T~S{iNnL;Yj&E$X%1oufhRGEY33yDa z-Q_(h27_1G<_wh@8)Jrd)Nj^SA*7XZsnV9qrIeVu_fOC5Wfb=49g@gwd`fS*3u2g6oF1J4r zsE4lOi;5fV?Oa!}Y#t_AGn(EiX^>qEILRic?T0>upg*J(2vWKuqliOVDO=9Kmo zra%b+>uS9dO~Pn%d%XS7w_rTm95Fcpkqp5#tz2A+PRy#-f+e4QzPe1Toe?;888(SV zBdb%%iW@>fMX&r23QaP3;^gO5F=#ykw_<<0TxKq zBSZ!Zu(0t@^X+{tlmGb4*AE8Eb}xLBKr&0TzQrBLav`823I9TfGu4DuNse zL7wlGRj3`1O+TJU5S@9p0c$29$lr&fHMi=55&nh|j4I~~gyJ8%DE7dAe8=Z8C4!aK z5J3fUo#layhc0Jy+ha8mo$g=&L?%?kwK|hY8BIm6@kf|lqY{<=*qqa8R;vflf5jFjR*#l&>;6(a_aZj9_@R;Bwlo>M$p488YlIiSDaj{ccWt4XI6aT()btDMh}<-Q@7L&bGux* z(ASI0I8MW9NwvR31>Rn}R#|B~#^W=6t9Ty79sL^V8LPj|0Dg#jfB~=(H*O&xsPt#I zy6oz2iP&zRJQA|AgPn&V=t6=79#t-t$`$*xiXXu2@%zpspQF)ob2Nh+#TD-v2bqjk z4mUc05;P}$ju@uVEs8}v_YNYZ%UB&cjY_qeoOY#Du9|*VNn_hV0L$sc!f`ZQFa-)h zDH9D|MoG=IJnhzUTB6nWu-7ORvPpbH1@#329=jo%to8|`bwXOis5;$l$7zoh_*}F7{%j1AK)tIl)4FEe#ov?3I}*Dy5D=X1-{Q~ zbLy00E^|C+ma`gZXk~TqVSN{1rE<{epjFALB^Ts^In*8aU`yY_m*NDxUci2EiXOBp z1p!{tPwFU_isf=~M-4y&9;aQW1R@S<68QusBulWOh|C6iH6SAjL3YPmv|oYdvw60rPuFogi~2uklA^#gWBxCtZh$d=!ElF@`3DY+j^cuLA zcpJ0XDj|p?Q_8Vgy;NTD=|QPlts9k?6#55nl*`phz>f6x^ho;JtLYU&Zm-s<=kZ0URWQmg>1A!8iYTYwfY zSEN7|@I7{xOXXs*QYw{cS}I<0#yD!WW~j);%4rlRLd3hENHMN>Xax#XdF=^>MF`jR zer+}jU=ZoQlXSJx642FN%<)z($>kir`Pr;U&J(5j#Wv7Csk1=CUhxQ^(Haf%SSB9Z z#I7$RBDq>;GMdsF@~JZK;2gs)ZjE3Ik`KiN1QyyD;d0TN@{~t4ASATf`Fs+`Ut+k| zJ#Vwy8Oc;4anPN3nDGG>Ikw}LTH+j`Uayv=dXu$I>9j&B#5;fv$ZiB+%JpWaSMfD?9?duLP2yGW{+pAiQE1e;&U1kP!eTHs zn=WFYEWyOOy*|Fd#sy7)XmT)yhgC$iGNLP?NTv173P5UKZf~338eZR7;D{N{0%pUL z8=Uuq?8WFa&UifFjGqn8^p|Y8-g#HRnh#rWK^^E0e6XeO>Qa}PolC*H`-Rt;3RsIg zv65A*6LVR>P8>i3sOC$+6xd9!)e|`N$s?I+Z^o8sY+fnQ6T5hLB8v3m3QT@qFsW9m zD@Kc+ty!+c=CqxRKgAL;`l@e)VK#m?$N^?;EjWYTtaVStP4$aypnEq4a2!;fF_l__ zl=5qB6=IUq<%=e*Mum|Kpa=b;G6@Vzn05Odx^x)zheN(VwC#eUqV$dp=pX+`1VZwh zv*C7?X%%4jkwkQF^Js`=9alAXln)Ynr7}RJRLmQ!mG(qX=+5~Hi^t<68YTS*0;9oV zuO_drLn~rSF@y^BBWuxUxS0X)V55~GFh0odU;z950V06iR!EG?fb=aeEnxRj?~E!K zcmN}aiPq#elz7*U45V?D5EwPfA})jEm`;xs5*NWBaiO((ePa?eC*VIjcAa0OkN_-K z#8@j`e1SkX7_>XIxgiT6u1jYiN4QV7-yoYDe7c6&YVhMSjQfq0DrMJd)++ey^Fuzf zAHd{1@&`ttt5~^*2-Nm|;CMFZG@(mH@AP^D%=2)()Ln2fXD<+#_8TRjSsv;Re6Xc2 zaVb{tBDQ@g9X!DYs$Z$IVDY31Lk+f2nUB7e)hbNwC;<%sSOP!|AdD{5Y;zrHKYx!D zdvjE7XpK*R$g9HYK2TlG$rWC z>z&K2fE(C*FdGozF?(!6woIwO8ujvOLy17KSgv(3rDEex78uk@xG4hE&q1>Rhjop9 zx7X1~lnTX$BklkQf|b~9nYBtG519^{)f`PeIPLZ-p#MV#zzDY52RNk496~#n(kM=S zbB?+)nDL}WyQkIM@{@0C1Vwy=)moEYoXBM;wHuR|&QvgE4CJ0LfG3n1quG#*2EQj;Gq%b(Wl5}-dy;56m z1FvDT+U@#G{5nCey99u1`FuUx+M56WiTls4#t_T(x0I8eIFgZa3EtR-R+n*1cLrCX}C4)w#Qr^k{w%}w% zfI000J847|_fcpzD$Vhl5h(<^E02oB_Zooq#~256e*YbC?qiafY_Zs|i34@#206@H z1h}n;TIbt$^a7B;!J8)MF1McnH-yHk5RE~$k>d&hPIrS15V2$GsRFU}2&f}SmjgN{ z<E2td8p0X!fL)PQ_?B$mjOSUDd%hYz#b<;e9sm0W_fBe7BeX{d9?Nv!aSI#$<80h%<6rKDU|ZRDEj^^%U;jbRYzsx=?7N72(10Dq9?k$s>Zb z1c>QwUhO>j%5Hbqcc`{}#<@evh|TzUJ6y$txXxM1q<&TZy%qwYi1n?TPh6vj+Y}A~ zwwMxXO}MYpX!Te&m5dTB@sUqA?4<$Qfc%?ES*Z+ z5U0Y%G+Muc(|K?mu2SJnr~zd17e85So&bX_l+f=abtO1Lj~J?qGBZ*@#gB;XcAIV1 zG6;bI4FE|bvSI*GP^(q?eTVw5Hv$DPJci>h4)zy)K-;h2!I#7YKL{2e9SkU|OZIrb z-y6U-QAiw!_V*t>efrFUrO%I#BTeP=r;i`s+n*6rJDW_#c{aE2{!_2jb^qbM-+Rmc z*n9BM&1qD-692PD_xHRrtyJ-Fryc6U@yj=AG8_r_OSDjPkcN~VQO8NCUnVr-2`VfCFrP@c-WNaez$79>Cdcuo;b# z%ab{t+`qTuBKu$a;MG%QcwR)E`T?hA90Ts#4Sa?#F9-p^J2-gd z#l-vMNCPx~fy-KU~=9r)+C}(%WwfP;9e)G%c)|wOZIODR0bons zgd=kY`~oC!Mj$3e|Nb_poy?ZNEpdm`2PtrMXrs)0QB1%PZ}NVcCX79E==TD#Sjt%hevb3!@)CH ze>AUR*6Z|IC5!=(?|%d5r^hF!PO$xV+9$`yr=*|0*{D`9z14UB#miT(o(?jasTfWNZq{qVM^6Y)(}6MU`yr&b?+lR)R$t9F1UeP?i<#CqJC#H!=T<9A zgE{6HWlxTd-n{`Q{aASlt;tRcSYRrh{ow{lhp?m%_#YGSG-7RWuy3#ClG>SxoxH^Z zh>ZdmgTdHyQ~=0BbmRe)xDuyE48qYntXv^a`du?<90eN#Hff>x5^N5{-~iSXCsTm# zFd~*l$8ZP^a0vBoBYNEq^!Za`d;r8Ez7XreIr9V=9XW0G$@lbA-Ss1DH4zYCq+>)GZ5oiiNGHz$}tZYcM* zxZZZ9n4xYoEaG52oBmO=J!0^(XQ6EDE=&MUC$ti#>5;MC$G9R+I2iB_FgA}3U4*w3Q46dFwOqtP?TQMi zIAQ^*&m3yj&8PWw^%s>gnayUkm4@i~mXFXnOeSEr{v?)!cKzLMweB*RrfVCri(W$0mBV~nFn&hceK;?j@)rKr49PWM{ z*qdrmC7#cBr&uqck+V8GGGnpYXqBtwLN-HD4Mix|FcM%ZmW!pC&qQx?*zLdopk?4J z0?v<;AIJ2MR2p2hm?yLWWunQ-twi*fVY1p?%JYlBF_F~K>15j&Pp-12wOWe4M`v4u zW=uYDN>qw$t8;rB<2@(RJuG2#?{z?F43H904*{f~`(L zrO+~ZwL&~{k+8Ny7*B+>kBvptS`QFDN)gOlh3$$oA#}7o*LLPKw5S0g1z`HMa~CNA zXqRet&=O{rhx)^TT}%!URrt3-jP3>4GNK0Q5R{K)0qBGE(}6dP^hJN6a8nJyYP8u- zTTB?Yp*$vbGyJ!Q^5Esr2dIW&097X!%%_3i0ekL7i1jKt{obGoG=OS3lTF4*xkh>Y z+ECLU0i&P#I;a5H>{EDn6X}63Q8zN5vt`I^$YaZxi6WH+nq?tmw*A#=fLoz9Sj<+K z4l_&Ynp}XLnp0{uidlcm?vHQ4D0%}dpSN${ef`nM0|Sm2l)i|`u$(04x>d6IYK=?H zx6FO}77ecy0Uj?HD+3s}I^nn->->NP@FXy3;-PnN^cDk7+$5;ickswes^e_0&8)#; zmkr{*k?@rs_-|M0{~}6-d=}FhOg2Ptkc4zPZZ=nd8kt0M@aI3C@B7r@NHk8ZDZ+I0 z9jse=U855U5$L`97@+Nc3=0OneK>jt?cDcAA3y(S*Dnqab0Q+rIf{Mx|NiUax6{i+ z8EYLw;(&vSF#Vl-PaZ#i@oL|@|LVoF$B*~-VBALg^x%M$pu5DXKs;Hf(9DSK7ObBRym6#2fZ`$PGN1-YuYvwKyulp}CbT_z z_U!qK7cZYb-<_Tw9ld+^@#yFily#>+K7ROcbaXQ{K7RT9j~CA#JtXTfW{U!i0L7hR z5zzpIz~;1BZZiNFK%S}d;15bRl@8}E>xvK-tr+W;GJt~!?`kTQ!X{nf??9!|=&?*9 zHtzzCO<*)!CzXoqS+#f=iqBJEigi6`pmE_ z4!h?}Cg2gmb&CP>Gn;789wbfFZhtm$lc9NBDO4Km-iXDM8$3=6md;X%*=#-xabvY{ znG9k7N07}V3|e!x+!(`q64^X1tGSlQq@kPz`sL#hU#kpVg_ioBz*z$Tw2^tMSku{k zdRos#<;`6D#9GzcSErZb*70&@pJc`WB;-Ugj{s~pG30+3E16}az%;nDh5EEeV z=bbP*3oeuo+if)pd9o5!EE4V%tF>-_#Nxv4Jci^=jB2hL6Jz`S9qg=DuKX2$+%GUn z>NN_nt346u>V1|>=kd9DKO@&@7%pG{GJMqfdUJkxwIIpitN{Y+TUP+^afyHnJI+)V zygTuvN{V`y!o%STQe_EKQ|kh@zL?GIwaRH-Y6t3{Z$g;GzO!>D9*ksLW42OL^zAdj zpF;N?93QI2#QU^K35!Dy?KYQPd=W`34Og?`zituw?_kuOC3piDrR^SOooaOHAL zg)5{|iCSf_Jyh~0{R*VMIRij70gr6OEXl&bO7hQcF#s_5UDaZ~R49~im71_^7vT5r zTa}D@E*;6%gnHcc^>S{+{1t4})ZhnVv0Tc}R!T*>DG0U_WXy#9A=Iz*07v+~lAO8- zu>RyXFAPOMt%t#8NQURV-ey?W3W!7jC`(Rg1Av86Dsh}SAGjwHi+qz>vojj8SYq6O z$KlLd(jzbpU`!^{Z!Z_Cl|^eGwGu*h;tBvElCPHCE!6v{QX=B+?C})2+K44pSvfi)gcjJFh{G85nx!ngJ+z65!3ZDO0gXnMCZEq`(nvXs zL8FjVi^YB=+|Uw(&E~Cyj?Yu|F-vJ}O?lo%F^wiYe!{lA_ebGP%A3mWu-c-t3B&(U zwCMI8XAfptOykGYS9v^KVhC`**TXol)`Efpn%Se;#_7NtNBW{uF<2$)dq+v~R6PKg zAxF)S9`*-AE+3}B^6x8hXJi5(-G7Ueq-ym_B9lmQjjcYhPuls!A_Kq@3Sb3n1Kk-C z_?CxnwOw6@HyQvK7m586)Bs8@l}5FaeQt5=`F*Xi*_O~1Yix(3)%bS?wgW03!Km>P znS%pxiqUAq=Zi$EZS`B{kZ)447}+}e=@;&-jo95mc60CBoRtf9h(zD;OziP?h(zC; zdC^+6kjWQF8vwQ22MmDZKq8^Is@dUoM0mtzK^lzsFur%rOJaL zqK~DOK^&piBmG0mH#r}CekJ5E`i_UXQfI^vDh<6Kp}@r@BAHR;5XkpVHNCch^XAqS zn89tgs^nreYt*R&Yc_mA>B5eOhybT&D1(^>6;$mq{-b}!B zpwdykfWn_F0ieLSk38A=pI8H^bV_|`BGg)w8dP#dIRPPVuEX)J>+!)u9j5jjJaP7- z;hSZ**BrnCQ1bu{7fa1AaaoOXL?lTCmS`ZKCK;8L!}R9!V+-pzE%`4~5I=JUCDlk7w;k02&o3TwoyF$pe^D z@c3Y=A#x7vw*f+31`@8G_Bd-(R{H$|LfhmZ?30=^}EHOw?yeyTi*^3Lrg%b=~xV? zJv|nX2gqHHKy&5T0hf$KZz3Db)&;=f1AsZ*Hr1Vg!D^LWAOHAY|LcFfmlDe%0CGw4 zT#PHppoH9L)TqcIz~y3IsM6|4KORFSvK}0Wh9fi`SOBL36CVI@@7$+Sw%3mIMW@2G zdeSDCpQ>0cX8CG^kz}Z7vEZol>GyXZj*h-0Rs^jGNF$Meic_=){UJtHf6(+tfzxKU z^TKCi=B}&oy$${ahG(U+VKIdojY@T00$MDz0(`heHESkAfX(5hYgb>ZxG9^)MWb5$ z^51H{&}`P~eW_fAFX+E4B`%k?0mB6xndqmx+4YK=eXK$2-(Ud@r_t2+TP8rD|m1M^G^6Ok!@itjj`hAP6y30asLD8$6)yFl}hCYIzWJB zG+S-VtKj#`SZ&Nz+1um0gpLCgx;7?KXtz_a${j16LNPTaR;%4@wbUZ1l$z(a0GrdX z6}gW5W_=$x6VRkZQpJida}nf{EOZqJG-}n6T4ye{;dSs`Hj{xDNoVt8tdPrPOnRfa zKv<=2iw^gmG@9*=(}6dR^d(MZ0Hh~UI9b*w+^6@u7OBOn6( zZfo=;t!}?50}uJngYzo&0BmP-Uo;E2Ry6EMN?f2@L>X z%a%$Ln0>Iw0Q&WBVp%;?%Vx4MIe*g3M>c1*-hyehS}qxIy+%iO+8cCQ6+cR~LAf2d z{|*10C-ltlZ+Ik}F1JH`8axi1EqEq@G%uhr8YBaNrao+@voLZKr2?3s4S;KLya*Hk z+NWi}9JPP}%w}^epcm)^>yJrJtEEr|BE>0@E+}a-sYo&^6w77ZV?vBv3lPbU18aO9 z$)rLC=dBJp^;y3xHc>*qj1$7p*x0%Pv)bKWUo;TB$Tr42wWG(l+pTBQBm*2kKL*oZ ztyIc1_Z{r@q{_v^mp=jJw93^XM`}RoZoN{>7!0-AI?3abfmh0S`kqLwRF znl%i7Z@1dcV$o>r_tN{TwQ_Nw)EMlY+a(jZBnw>ydgXjJY0KAo>|}i^(z`qkTOk&M zLx78!m`>%gRXVIxeI(G@eNJ-;mI`g04!m)s@8(lglTi)scQl4nfLTWE`wjToP-)3t%hf3_623Pi6K4 zNbDA)ewxkCk`^dvhykA@P>`DdYtKf$n&Wygg7h0uJH0LgRLBF13I{On^G>kqRRUs9 z10A34An@C6W=GGNLlgZ|2U59?0 z!E4jCFoD9FHK@g;{@6uXk}#Bk${z`U1=C{bWFkTw$|oIUpbIvw6xnqmp2u9u4Q zDLq&TymM-F2HRqw5y9?oWPt+kyB>f}HknXwTZ(;gwZGV+dj+dq1=7iwQtTZv<(Ae& zsIq&!gUfIf4P}XIG%}I9&yZW&Bc9UY@d-{Mu{0pw4by=)j`Y3dR56uYBJ}hbGGndJ z6zZKOZX{PME9~_A2ny+PIg^Yj#GVmXU2G1Re5KXv8b^~^M4dARz_r`$4hgJxAyvSr zN~JP^M^;`tw_2{&s+Bq(bEE4|0@cT8$ zWs)hqDO;$vX1h-4b-AqSSR#>GCIagpbQeo&FlPWnJ`EkI6pNfd>g<+5_VcOclx#r0@pG6Dv*jb)k~x&tRs>ty1opKGF#x4fsRAZnA-}|6 z;Is{w5?27JodXI0Eqn!t*T1{x?@6Q#j5%L#Chyrg57ldu%cW8=%_D;)Tb%jpttOK$ z9gio$-Utl_y@79MJUITzVD39>_3Ba(j^%e56s&qNok%srl6!;lW^22)n$i~(c z7_Qc6M?fnky$qRLu^QtFXwmT2ITN<^k}GFiS0t87IHhU@+4+m8o=sZiv|er4Y1iwO zQXWX^Ftr0y2?H*5G>1%a8tfP@Pb6aybjH&w?a}D<(nOHkP`>10&74nNXx}o-0fAO| zJ0dyQC7&_r*x?Xhj3Z;^qTyihg1D1QK6Bi*>#f!9lqKP(W7omJ1-j`C(}6dR^u6U& zaSm)oZhTTjJ9>B$xwwop0hQv*Vs3<%Hfn{>PW)a$K#H3h(Spm||T4@H>H2YxH`2j8OP;-Lsw*XP`#kbhMUpuznk4Kc7Gv z&0uRFCEbf!;8AmcpmyH?%D58Q04iLmz>2HgwCYgo%Op~blDljlxct7m{`k4ej{#LV zNHZ{qK~)Qw-B&B7!e!=sp*bMw)9P_q@gG-FBrhX_-f-C48Bcx&m>j>eR;w>Az^;Ga zinHqZq#;w~8*uxN>)9@tj@~Ri1R~+Fo_-CFQ`W+)bp^(gfm<$?inI11&2}De33`q~ zBhUtXi&dDNSilzvVFt(CCYU4NspRzrm0T{7>V3tm*{Bpy^~J@ALc-hiV?1>hXwod8 zl%$DPfIc~|6S*O0!2cei>`|+w*)=Y8tPsxnO~Dr;wO+^t?CpNfRr z&Bh@nl0r><^Qpp9@XLB!!e>pZsd6q74x*>KVWLShFlyz~YMGG3n$*&O=|`^537R3F z5FC1Emlq?~vAG8|0##MD9a#9~1z825R%=v}M7dBz-ii{y4r~iTP<$$`Gd#kkAC1NZ z8aJCvkFfE_g{jlci0d8a>0mlWTVI&I}kJRAW@i;FougT7XsorqV9qbIp z-+=|bQ>)afbZ3{*;rIKxBcok!$k$m`=b(WK5GlN0o+pG@_pY2ss|s@2yO8M`)g;bsAhLl}i=4{*DnBF-KoX9}rNav61_M z!iY3!o?ucYN-r3O!9^?*S$nz-aNXH+TC{=uqH}c_8@dW~zc-3Wl~TZg?U9R_I63|t zV06+brBn)doxO4)6T6}I&vgBC;LRg_H=hc-Zj{m*1w2)+S1P1nuj$>->>ga1vVkw~;Q+kkas1UM?C@@gH5N~O{a3#Ad+s$fto zcxY`w5;~Li2_tDvQ(j;Qy~%2{bU!B+Vo@nndb1qysV+$2CtT$2#V>wUawDod>japSz2Ic7hI$b02_ZW zR$PD(g+ey(RM=E_a`98If8VzkJ3oPyaDJcyFi&X9lKr|q63f^T0^N?o*!Hs|no{M) zE7#av{+nPRrc`2ecwMDqpacvs-{?nxJz~3SwF-^h>P$AzS$Zhcnk~g9OT+t~rDeGQ ztTzI{(KW3rV5XG->-Oe-#;-e2&m;6t0t}T*#0F&Cs*oZdnYn}EXtr+_Q7T<4L6exsD7f3ki$@aB=ex16d_NC3_N$FQ{P zr3`#rmD6=O(8QqIVJM}A(^j{zQKwmRjebDn(-eMx)WZl}N&0w7 zuOe&!uxhZ>Z%sF?K8yr zjce@H1Er;M-taq=MtkwNriXDqI4B8R)p36Strq+XBL#M;LNJ%eT)9RY`gz%vm`ql! zby7Y(C&xkl0&81WU~CQ`#9@z?jcrKtRU9Xl+3f27}IEN?DTWnFugiY$jtP zkz6hpJzF~U+R-7ILA#PoXk;QT3w4;zCliq?;z)v$LM)txh^0+X`}sMH zgpSZyrqtg6f7`d~@rEwWf)_L4oGTUzKw$zl04ntZJDuKLjY1y$mQ{|GiunTl(`oU4 z0Ko?W1E9gd6EBvj98?9-TcvMeb23Hx@D%Q8I0DvvN);1?Wka!#xy%HAvbnC^GW?dcC9C9Mt z=B5rFShJaoH@GS*u8_|Yl)A^|TwEnX44~g?H^}9J4gk_o@LLdT3}$;K5vI+wy9KuU zh|gSoLGNbR^`k8`_(81r`VqVf*wX1V>d+@Ly(oG_GC=D5>80ZomVaM)4>_hvKi6k& zmENt6qX$SHDarGY1#al-ZFZ;K4iuvg*wOehqu z)L6_w#?pr07J?G;J%W8L4*FcL_b!3q^ZGo#XfO~A&be1HpN2iZfDNDoCIGWIEA?AI zF`vm-<)|`{JOWWi18|3MQup}~^6lQ*bf8sO3C7>z63kwXwbZCHOKhg^Ovnn}7-N5CuBnA zTp?TXCIB!wcW|IA=XeCiu4Jn14ylxBY0^L>a;qY4yffsdQmP%`Mjy~FCV9{ zeB9=E0(6hw>-Cvi;P~n|dax=b7Ihxt0Ac`Ahtuxhq@$4)4{B7U#8pOnrBGSOY8OC< zUy2xnMnUvIMQaO^&}j@hbK0Ctku1SvwwUzgWNb~ph~L5}9M>p-Da3$QWQ`V;X*FB* zkyNh0SNs$dhJ7bYF%Kmd5@|KMCs5J+zOdzgO*9bO-W{*6d=Uzvpa6*;*wc9vv;lwt zfDa1*h)UfTlDULR-FkjJO9%08R*C1%swj_=~~sAiQ`M-=x1D! zejeyQJD$&nlwE$TK909GD6~{zg5Kj3>5CCNo+2 zK)g)E6N%sAw(BrRoERR@&E)&qtP53 z`0BK#cauK6kIsr$av65-uFp3N0tH}!UqYjw0c`*!umlRlTw&4TAOYh5Q`l{LWgR$% zoyX6gKiPM#Yb(AE)A2c4d4!YEWOzir2Dkyb_zvnMb%#jGLXi5O9~``VPB>XwvD0@y z(9JfJ*VozK+5f0@#ZDpc{r%fFsI&Aet~eZyh8P=GXs(0X>%X%<{PN-5>(_7Ie+s(% z+fFCyYT3p30^y3BYBn1sky>Z>dZAj!GTVT;-)lgF0Mn2S(r9=AZl@4LWlonAL$2MR z_rGAK4CsX%4QT_?NL+6i4=-A(X2dY3!{cIy;6McE{n+t8c>LshG~kb#Zu_g+kzx?2A$rNPNxW6 z-E1@)bveKQ{)&@!8BF&c`gxkAo0`RZE~_;d&G{-l=0ry}olW0o>U3IKJPj~nE#5oH z(ilJ-aYJHa?=3L+w!I$Vap-(W{K71sDzpJ`1IwIvzg4TH4y@q&8U%~U{AfA zORVMp%gCS5E$LqJGhIh+Vz?J|#Yxp?eb_ZSPLrHD%A-6!0s=V9ciQjLy#oXY8X5K4 zjrn44Y+V6cVRSG)Pbj4BON6=&BC7_oIepu(?SCj$1InYV&+;|Oc~iLE20~mSRVbEe zVwg{wB0fhGljWOzmH@Z;EYfhU2xFLR(W_;P;X7bDov(nzz|*s~;rBC*Tj0!{^$%bT zmRUZqCVmqRm)D_$r6EYtZyv!5Aq5ghW6%Nvfc@K7wi$N2gFfCW_JPL01xz3MxtIuJ zkZQ8YGU{imbN0gor?eRs}PH%QH<)qSC6>2|&cOh2m#RB`vKjNGD-1 z=#|j$hYWxTEdZbZ#N%@}@n6RlLjyA%&lbn$=d#I!R&OiU+QFI3rAa-2OfHozD>XW; zcCNC+HjY>}o6OAh&-zl=SXh6$3LF>!+xlbZ03eT0fLT5+U;_Xq&;i_FyiQZg`3pnrS*Qq*UxE&Rxh*tT zv1?J4M!m(eS4x$c4^6F7tAH^}wXH;eN`?Wp1))Sq!T^jNqa~ApW{t^cF=|tZf7=5v z1HQX5HDr{~Ym`!Fvy1T}1dTAX7PFAHd&lWaCaZ+Lml=`EwK|Pvn#twn3cw23u)F@U zTU+D>``<$k0L}m_f?P_4T#n14|4Rr7INKuZVS z2+}Q_qz}meYN!wHqrtbUZZThdt~$aM=ff{y%jI%$U}*?Zq_TMgFLeBA43Gyf z^B;?aY`!>KLI5E$O>ZH@Rj`2mZx>JK5^6+MwoxPad!MNE2(UO0c3m#<_sff`FxJi> z`A>yQSX#SN0d$^8gjPgF*MVR%0JvJIt5Tq@5n2GvQocmjWJ*A)^iG=gTBBYI zReq#SWRe*xSboi{ zh+L!5YPSG$%F=V=mLL@g`55fJMgz;U?e}dj1^_8E!ILe>WHPOiUEDNM%cM7H1uOP} z%|EM0}U$T}siR1iju$?A0NU?)i` z^-kpXc&s{@OA1@~<>I*Ld=^7k6z~$mf8PWiS(ZkwUGM zJj}fxK=WFq*66T&CPjMa)45^=Q^=&!QX!j887zf*pOm(yuQP!kY%WC8* zE^9n&mxJl6>mZ>NP0?f-I>?7y2r~D5yvcY{_V4?h!9ZZ%pY5+hZPG~C(|$EWb!J=( zfr1BkTI&3axP(n3cqzx9PFuVIG6Lv{`2sy!>AeFs^5kTb0lN-1o71lO)ZeBz;~$ZP zI#a?%GltIbySF`-a5!{z1;_%;@~4_IV=Oj5j%Y1Ql$e4`86b~pkCc7msETls%)&5A zU;)UZOPTJ&B2WR0^tK=gok6cNnXGAJ(quH7)lr}TEcsCXI%a&1j@00=spjyZ*s7Eu%tR|ze3Vv@v!DP6-{ykql6v?(Qp3+$HGlz}(LMwp< z=TmRap4ZM+GdhXV)nYMQ>M0cz1JD_aR?hKps7{RO<@x=7ufvopwMHxeP0EeZqru|N%luX^`ee#>G077)}fHuAqGdw?KKgwWfe zB`z)l!(fBrW&NW+*wWig?0BI>XVxyS&+BUiFM`2!u0mK5FS_RDwb4AIZ<6D zQ|tBm+|BvVv=BW8kH^37+xihJG`d`|#_YbwXO24GNz*j?kys}dTdXu#baEkkG6au* zbL$GMlhjA?dyZyTU}}u`YOBv}zmBJJ;2Da=5-HYd)(x|afzfEp=ff73akW&;_1=>i z%Uz()Xkm)nZYQ92%DW4|9O`0U5QYYh~4>Q;+zLb;D+k#|ZHjvF|P3KKkiz*BhfYn{G30^Oj`@hK7 zGmKJ%TZ77=o}qL)Y*&j(jeQ5$j7&DWRXxhYZ?0!iA*`NBS~FH)184{?M!Enq*$uuJ zp6FR`OKEo&ZxZuVCh(xV9DpYG)|@uzU?dMgyx2>#_Ij;zat%tr)-Wnn3MJ$>a-|XnNUFvNDo99_0I z0&0w7I}^G_Ev0!}fdJcL(OXrp7##FRjVl3X)U@2S)qvtAj-Q-9o~PuuLN_pV9X$1J zvpVzc-iAtGJRbY^8oJZ7pQOQXJ6J3>d(`VMQ3roF;J-V7BbHH_iHz>W@-bc|0zx`X#yO98A$a zM0}WM-D}tKaV^aP12EZ~Hrvl=#y+n?uG zpjK-thgg3(kTn7<0mx@9tv6^w4|D)a#+Wx6jjj0aD*!D1&JNb1e3&X?W>ybuL|WjD zOb9_?A3*Bg8>Nh1O(cg7d(C<|os7|2LuP=sk5m9e{>TN`&cePK>%kG~ESN-aa}&Cx zd`|%Qfo9JqUkw1x04IRjBrqeOxFRGz;DXm$a+LH5Y zG`NCVUcUOAV8?8={CvsL`+V^(4)jUxOxC~<3^uDld2Ducg-&D%CjNonP*N;=%ez!>ey z$HW!2c3WVtPnmOf*lwwZ0Mr9OIU~4o+TQzhV;`JnVO$jxkm=zG)=CgQb+oTd~{S1SByKEIjo4kja(KxMSxLguhFzY3epWfKLXF~~uG z18$Gk<<5n#uP#@72@vos>*{ajg2riFoW2BSGy`;&w{!j*dd76Vl+eYsI&(T}G*=Um zH3|SdAz_XX#&$AkMiJ_1hkd0thGSW6_u$Wqxd_kYq)Lg9!^9@Te!Btm)kOexKcm+H z%^xfzasiGr^dz7~Fq!o?Tx?V?W^d+W&%NR)wq?M88C+iu0L}oDfs{jF04%9T0Du7u zuy#BjI}0vyVy+BV;I(R@z$O*JR=wMihy~=ZP;luR%7K3kINsl?R4WvKqbOuDW)Xn+ zM5Wg1uxu($jyoURs7-0BUaL{flj%v+101Q^Xw>OcitL)!6`jGDXk7t##i(6P>J%al zV+8fq80uX`;^y-PUYjeXh(cKKCsg$s!p5xn+vpt*Jg#=?qtS>hG+B53%zk4oZzXn@&3+TPj=0GKx5MQ2dVty) zzPMVi?m`AYom*zbQft_qkuG`xNNa$sgPb`Fu~ip;HDS;hd{&N0ev(MWSIBt1HVgD? zDSMve(7sWqADXdLxsaa|r`2Zn=&;1s2-VgAqKS1N3mOeN^MLjYX`t|zv^tYE(!sPq z6A&kJ>GUR*qP7 zf$jpQk!qBoC8bhyM1gnVg!)o#;p3=>WaYgS@ouEg4Jo)Ym@2Z!VDb%5lm-l z)XF)2qtlZLZkZsX5A%F}|Fm4~w87N_oc0=-luoUX8^yhHF`tg8#35`Dh@ko(fZYM~ z4&}f<`P>N0rQ*2AGvewYk+EKh#IjkP1J=)fXmy`uGt5XK`?nMm9yai!A30_#?wcDux8v07DEfD6u)=`A3k#IVIRpuTRiQlhB~ z2-9>fll5!VT1^zwXw@3160B7Kh`E>XUVC{DCt;sFJImhdx4Tql_hkSv&t|OY1qM(K z#(6BPTg@&fLxR5%VoWZ5ej$g!X0hmXHS5cPe+_skDy32x1{#1u4rLFqjl>d#jGL!- z2r8GWb&X6WSyNP5sx<1DNJtElp|`FO-@WT~o&*EelwsQmMS&K^#>RbG>}oU|jxZ); zN}sO?F&8yULtP+t{CggUu~)5)SRxlQrBSdj!Wp-|t_Zuct{Aj(OC(UMvu?ZWgK%^u zG|hpdR$z_k07&3179^<+U~q*F?MZdOVo}7hegtp(Is!O=MrQ#30X3jd1QO!v2Ud*8 z`CD$E+*;SNjDPY3A|?GMy6=%L&2wWC-L77PE2%}YJM6~K(cf%*z;L;IZu`v!2OtEC z>~{pgdy&O>OD4&U z5{M?P4X{n>+H6**O;wJ>A`xnvn@k3^Tq)oHo;2*Y>Xl*|24U&QrqgLFumN;YsYVC1 zyKE}Eq}c#$88mV@_Y%3D3+V;_Fzl1Z>+yIFpaJxHD~>4yc>XqcylWT$U^mEdT!jx3 z_3|f20H9tPy8Cnom_#sPA}8731=RVS3S1$^tbM234Y5kC$KrI9@Z84 zs)1;s4pS!Fe%7d72b#JQnTbm{PHZG$-DADkxg!h&LpRF|068^B{Sh%i?N%uLqR-KC zm&NK+U$kKa?CW*QyTISR1`7RiQXm%A{h7 zSzGOP*t#=0OZd4BZnxLvE?fa@+N6x~5h|QIJR}#QM7}-zyc`}`LgeJQra$nz97~g! z7m!Fq;zc;Ue~kyAU4vNumQ}w%IdXXufm*+n%zMyT9ab9_iCje}dftEZ^!dw!7q4J- z*vl6$U%h%u{-8BH zL3kfJzX(;fy*s{bnpboVf!3tcLJmK3_0nNNm29X!%nx5ZrUK(ku%8x|p%J>;EO-72 zszBSUKn+SRmnxd`=p>5`G}~UkKWECsz*#PG=r3cQ`$u3H-~SIexB-fTy_i8#mTy&A0OWw9=?75IdEZjxQr&q zx#4WKMJ%}2=lTW$ljnc(VEFCFabQywIhqU)9?W87Vi7iJ;LFFiZx0WT5OV^$9FJGM z`tbJM+jno?eLjxZx1DCnw6BJa0Nx!PeT+(;6RkSNf$yJ>KD>K-c#Iu`+j+M*(2qo~ zR&KXN0z&@+FgtBqksDg;&f8#cd);pA>MC@#E^h^af%RGh$O9Ppc1bd?qJA9s9=OuF zysp(@Bs269=}H?bBogG{secQR$o0*Q*k-j8Rx;COwb=yGo0$P1-M|0j>C0v+HfYD&h&tY*1-Qav*}d^;C=Y`*^3u14geo{`Rdt=XOABGm+IR? zmuJCHf7|2p67^O~Fd8%(K7HiZ0=No5l^!_6Ss>a0FC|C=A`tmVvRw)r0*!wag<`3w zR6quxkU#aXY!3isZ<&VK7IUh9N6HT1mJ_)?)0b(+jWRXgMqJ?iP&`naD2{F z$kh+-0{)#iJ9_`|Yw)IH)es|i5&H2@M<3t4d-w6^19@b0{t0lhe;t)ZrO$`|x}2=l zIeLTMfXSWZ;kxYhn@A+G)_uMViQDaVsjn_k3h)N}Zc@n_bWa1vCjq^~?pzznBtT?# zOuN4u0$hh7>`G9F3;=NeU;tRH^_$2|gg80Zcjx~7#}6l8-@S!$0kG0z?BS#P_x-+w zn`@?1nXE>wQ)_e@aTYLuWy^DC|G|@|&mJlIg;W zFM<89P{?F*$PCECtDr7s4a8!RfQ3wi1+Y0hF`&v358u5*i6_UNfB(VmA9wcsw9c;O zVj;`Y=}hy{O!_~bE%>~bG0Uot)mqi7z-1(zLs|7iT~QL&6~TR*x=y6Ca#&Xc-dvNM z6X9bE8_qZG7%gs`96N;2E9QQ_fA{(8_s#pCEs=_Z&1Ul!OSqnoU(Z(Mwh_D(3MU^v ze0cZ%XePgNU*EkW#wDI?=s|#u>5_PhZM9`$U_X%WZwJf(a7Lg5V6!y;_ZfiIX{?_d z2M!B1m(2-q>m&Vp$lZja5e5?c&;w{i2Ea90`-eH)JN|p>4Xm<t12D$*D0>* z?fyp^9j;|$SCfEDfRC6$!Jik2Vtd3At25OBW6#qlJ{CgsmS(ZA*Y+UJjVD4|VQiuU;9BvR($%I_rl#drG zLyXH;s0`MNd?B}D<4lG}=*On4`djLXXxTuH`#|p1E-$aHN-LdoiO}^lxHJjKv(H>T zYPRULv2;4OWch4FbHfN;-jXkhfU!2y#***A9-1b9(@C0&Mt;u&D0aJ?E^Qzj>Mab~ zh*X5IsnV>u;lHf`j7Q^VPFy9Z7Ft?`^sx_4iTclAI)VCJe1YClz~YZctw8$+`oB$Z za~+OQVLpjWp&n;q;lO;eCqj{A7Q^MT#Z8sTCbI?oo>QO8E{rUJV5v0*OEVS<9-rk~ zy;k<*+i@_QvRW;M<>t!Ki{NFbh8ly;6$~K3_YYp&?*@YB*Bc*CE_nR)LSbH@&_PUx*1!joOQmAy|6Rp0DadZCt<5jy~$%$Z86Am|E1p~)t=YiwE$}{&{fl;@N}tP%2wZIZA#;0{jYP)u$|;M}Itj{_5pDJ3n#_5E;+vijc95;GpV? zW>CA`F1PS19J&I}fh~c*w8N)MrUk_ifU8+O%5hlC{RG+3?zfOh#;)6*y+@B< zygGRG^65iAF@jfSh2Oah_gr7jeE#(5)92s(o_;iPv(mbAEtIxxw+rhJ_Q-D}a0gP^ z^Mm<7zJHqmG^*vC*6e=t^5u(tw^5PL*Zw13D}X$q&n^1FZz9(>8R!SJ*{#+LeFij- z&kKI67SeRrdoo|F4#gtTQdBz)bO7_qr@}Q!KvJm+7XWb|r5;4)@;Z^ibQeEs^(n|GhT&+-&1#X^>;r8~(?PoGnD z_5^Uq_-NRiowfovuume9$_g|K0D9&tRfSSJn;pT_sFaFcrM#5Wq4vLg@#w+bMDQvS zS>f-!;PJ<|Zw}uby?u{(@3iDA8TXD<&9{`vC7i$^9R8bj6G(c4Sa8?M4h|NVmH@tT)VJd@-^3!x-?acREFKA798c~&d3o>;z~){) z-WQM~c#(y?h(!m|UqCc^^IQGfA^2(xbEDAs4CL@&+62*mn*kKEv2bcT44Y@zCeVvoAtS&n@nfQO@)k1bf;l@0d)ZW_BJgPz~d#~9=>_^ z?(^~aauos{g)i^kLgx1F=XYgQW*vVYO`dpW=*jFR2TRe2+T{X=cs)B^bWAI z*Y7^Se&lu3nfEI+0M38Fm&fzibMpAx$D{Y}-h4Vf{`B_k`*%lQe#}x%xyXTUq=^6*zNMT-Kooq%S*}-TiV(4 zMy3l`DahUTdE?htiwSqNCChXPm%-!XuQB~In2+-6xwrb|`!O+s7e{V48aC{I>in}F zBP8a>LlIzO5^o?v1Xo`E{rU0Gk^xJgG#kSC``fQyMTRJ|w2~Z~OZ)0N} zGD0Xxnr~DFX$)5?mD{!=)2a>u1z`SdxsQ-+h-q0fFfaylyy93H01Y551&Tm6vn>(w!XhazmP4qpG_)-JiW(IKATPI4VHYZhsep~v0Kf9R3;0> zGOa|T1k?vCpT53YuCHfHfLc9hFI3{tTQLj@gW>ZvfjqS7<)k+bI zHR{z0>BKd;JZrP+32dw@g0Q)LZddc_I()S`;<*X|kJYFT>C~cquJ%?#S6A0`jp$dE z*c(Ui()QbJpw2(5ge5^gK8i)>5p}XiZ~6B(9w)KTMe5&X09Y*}$*i?s%5Q`BX(?V)DN~Hp~c3Ae}A{UIUsmu{brF?Foy)t+fRscFDYg~XZs5Qhm zJb`HL*c%PcfGC_S#Stcj_DLT; z%@_`7#)t9oVtIsdMH02%8q5@P)Rn+8#07+Yqub3KMPLLNv0{0lLa~{OsPVgwMwhLw z_SrI{d)uM9h(=Sz#S)D%uuekxY~qGkWAP5OJ#rzbNi7qwuwJ{8OVBtb0B2UIzf|Pf zyNcpc5uEEXXg5l;KjQnnE{j$n=CFpHS|O>HoRONw`mFlf>xzJ=b-SEy$@i;J_;&*3 z7Gxo|lTxWnnoP!l9i!nhIq0C*%1fUI1D7<~>Jo#2Z%GSSKZ19>wu@1BCGUbde~0)v zYe>3uJWbtMuZm=2ZTa_?jxVD@=KfREFppF2AN+a1JA8e(FkXfQ4nE0&qwAC(g~gy0 zL*CseCZih+03$eo^%$IBW#O#g6Y}K>Vu!C|@MG}J|8MdThPfU-e)NceO}>2l^mTJ) z69i-T{?k8pjz7J7w^R_I`s4YNKjKI4KCbk&R=hlTc{lt1=*#hpkx9Hr4Nt|;r5YD* z!hZL`-F?mZ@%Q7On{u*D50MJf_&nzTdb3U`;$cIuagwFSP_9@1Kk&HvuHWyq@zO;& z?v^iAiV`yNL%;uRvk)gn|R^;3DHoTucq^#Ep8yp1}{8kLYc9yE&SXqd+O zelgqtwssjWJQ2>mf8S*{w5r`PN35;It^$FR*`LYhS@qV)`N`K$1tZX{ouR|SmAWEQ zpX1(t@Zi2L^!@91GQkHe(KoOf64+{`=4i-Q;EA3l8?>mExPT%pYW zN`_(K%TOS2Ot4;pmD7>aE}fZzXmtcH!Dp5*x>={2|KEVQeA5E!niY%279wX1aM0KS zN7r>N=;rc;Od=7v318+mC6Y<3c5MmhEwSaiJ4|M^^N6HlfHtlbC4tUO0{lS@esd-k zopYX=Kp^BRvB+8AjO@d@9z2c5=ilDW1CIXHB1Sx!NH|k1qJ#|R2d95TdtdohhvKRmcZQI{ljjzb29U07T&!*zt8J*7!4|k z0JHrQe4Iu5wB4PKy6u6~pN|IY%S~ED>0|#gO zaw=5f%~au>);j@UIOJ?GCJwnoWwhE|o^40?EEqV4zcK`dO%KhK2WYob;macR1v2Zs zeQPbN-`n?ZyB%huMk(R4yR-X~-`U;W^=&&GW`kBG5pFG6JNkXU)vBM;kNWQ1^WTwN zo?V3F$zomRW-&$sIJBJy1Lm!AGFQmui{pQH*>>kj6RSEpnqtoX>oAyCjjoJs$q~*?A4ay$24L%VjoUrL$o0 zBydig*znkc{84jQCcULIQ&2G4l|uIVYv?9bCV4}h$m@XM4x;GCbTlC{87TDVr!mXH z5fR<^(Ba_7v`_tHaab(I7-Oq|zO_Y@ykdn?F5;8fw{KFd;F(4=06?H*4# z7z#(1xQ@YMHkh(&n}LskQLOX3`%Dp@$rYE_I>2%lunQiK50=op zyqqZjzCFLws*!OS{YD|760ii1W*K0aVXKtZ0__0U$lX>YmrfCL?(5MNF$yuIu78cg zVb!Z!D#D9j&g02&w#My>etWw;*%GiypQEA6S*JrdWQR0f#uFis+i?CK0qgKa?Amb) zy1kJ|%PVWd>~WvGnqLezF!_CVfCkHC5?%Bm^iHqW&o~a=vnanuCC}>$*tIzL3>yxl_e4>q?)h}*a8tC>C5kt zn^^+=s~{52bfl34CJ)l#J zE_h~Khz;xcIC-}wgF?(^jRwtXmI~RjELx?QjZHhX9MxFVWWa%rHtp7mgema%7=T2x zs|8waSGB(U1)z(1qhhtyEww=0e@=lFeoi|8Oq6Axi`wC|n*i0oL1k;6u+m3#5#b3X zDuW{x2?v7I67o%hM!U~ZdtEj&E0xKtcmM!WFPMUG12J z_toA~EjTS77Qb#eFh)4`@PA>L_m{d|OPjGwH0JG}nbwzWlN5G^s5T)DY zcFVtqLsz#u>*+v`Kq^ny6G`&vZk3}#2R1Bzr+UgGEE=nuoiJ?CGaj&?Ua!~;5FCpg zNbu9@?F>cl+J->_S{FX0+p~*dKW0CrwjIDpD{i}9#O&3Qsvz`K2(}$2Eu8Yws~6Mi z3$#EAkHer51Cy*%Nos;nzVdzGwe2kW$@LA**Sx1%9AE-Af&-|`^6c1XFO|xbCCv+9i$QeHW$`7-{O45UW-hcH zj|a`pfF-y1-L_~fnq07QNVLL$Rd$_^G_W^LsZuQ!<~d-wQ}lqUbFu^bFxV%|CmNmb zeNMPpZ_c|w0kByrm*RG86C6o+&AU_+yNnJ(G4 z_rW@ij{|^E(ZqME4I(yxh4y^DNJKZ!#3F$JgF#U8Jwyx@YguPA#7HEY#gs+~C$9JE z^+F~#a}shCXahnjCTH=%`FkYf+J~yIk!)+gl^TnkJ#V{K%0UAB#c%_w-?#5C1&n(b zlZl-ZSEn^tES6;KGV}u|cCF%&uh9Fo8iis4=j3AF1QK$+=UMfzuE-1Pie^3>y^lp| zjvTjp+ikwQ+}!(O6M83LF-YIU_hGwf!68$p?|%$4RwxefD?H(C^1fRv4v%~khn|7W z3`OX2x$kr7UHCX|kMBg~G{FYA6V~*P8gn}~y67ugc0F_FulJ|i1%wBW`FJb2n zBB0Un%rgZsuSc2Xwb1m0r2!^j7sDB*sc*vv_DZE(Ui*+ywIC$u)6;A^Ie&s;mAla$ zu|zt@Zt3cpumD$(U`>XNs@Y`Dmx|R|C7;hEsU{{pN%Kh)(X*Xx0+~NAN_vRO1TVcw%T-FwL>@(mu84T6DxtsgxoD`&<1uYc@P0^K#Gc= zvlhZwj0fKW$Khms$Whwr7~f?pq>M1<=l0&hXwrqDOe_|bx~S0}x&R{LoM^Sh$TS}eHHE9xa*3k5e%+`(5>9eZ-ZSZAwnR3NFIt+1qus z^9hWjeS&j_^#W%1g5Rpe8zZh}SIrw%bA!Lf0F-L2T8{BlI+c1M*tG#wE4yS0g zwm-!#FK2qUz+g4o#@C^rXR#{ARM}gTd&?OqLJOMP{f58D07gU6gJ?pIm$(|4=y~qV ztZ*0X+Dcue(i>?ejl!(csYXD9p`Ak@L!F&3mLej6vj96EkOzQ{D-vP#d4L%c$>v&; zkGYUC=|G=QOe}V z=h3UQT5m8eo3kmao`_y5?>d`ffxbqiOq7ZRXuFs_+irJ1e0?2W6y58gosZ5(v-F5j zEm=UJgXt?+D8*$HeGX@!kKe?mw59rOSj<+<=LFTIXahsBxx6mtn7&c0?ro3pOE}WJ zPy8g(Eu>0aF;M4f%KPq0CMvH2t9Lj5GgKM!HPyYHP9_l^3-va)KNU#SMnX$xuTxB_ z>jtxHH**>)jfBqe<6a@IPW?RwfMx^%se`Ljw=e*q)9HG^z6u@({;F<9hr{l+tFEGv zz6$r-%RmPpkIykB($!f)i9%;{#FZk}n6ONQLJ?o6;$p+j*G%Mea~4gq4rG==iz{U^ zldGIf#c-Kmp6RmXcN>+gW-*;e*2R)LlhRFn!SK-n`R_9ZE(D8nt8uPA;Y!u>glrN7C%=Tk3mreUjnS66h zxkPZU+hr2Rv$+x>R1riX@xH9~F-`gvX4u&UTJ_)?cqoLWKP#C5pvAvyOHR&4{`!LO z&L)K_ozbUJE0ww1hZ}I6yLUO)$>Tr}ZXA|_xm5;aj2aUbsJ?(o zh^sC(2TZ=w>Ls%3!G{~`isaA%pd`j${swsXGw`p7g|WoY9&f$oLKSuZEeq!m=o(xe zkH5(G04JtBn%25pJ|cih04!*0gz;$Gi)*%8647{8M7ZtOGZcl(<#c`F?vrl;a1YP` zBGG8XNB$)A8*At5@lBm;8%yr0l?rK-FYUj>Lf=wDzI7tE;7YLw5UEUfojSfKj%0O_ zPOnnPCNhn#7z-Sy`ij27s!_@S7yLU6fT;#1K(j1WsWd8T1nn9?rABZLhbV9UopnH$ zM$x3r;k4VUk$7~VQE43IN`)F?GeapvFd;5qf}0AyhZ4@XLu5xV8O3*EJ_j3h>$&*V z*=AL)ags4;H8?JlD^E!y$L6l1rnbj)mU79x1$k>r|A4x1RxD2X7yhb_;8HqR~V!g%D^tj<<9foxRffiUz$hlGP8=^5`d)|vG^A$?IVH%68Wc;n| zdvbFbY$Co~XXBDt^;qXh$iaH{M+Pco0*}MGq9)7Z_PVU(_{-k{?+KtVE9QWLe!*z( zwA)QgBo@n-834A_9B4TKS9N#p)Oim6%YT;d(^CMuRA;&|tGZU|8Yi8q?Za9SusA|2qr-Y~=j|EG#iCe+%Vlz0XE56Di2>8-kb6n%@cVZL1HDae$kiAY=cKXFBFDZ1OjKM|ylES(!0L`eBKe9e44>2kCG90{_d)8ZVH7?@p zOju%Fu?aL{39ff}eWOq~JkJ6EfD9m+OsAJk<6iQZ zgO*uut9HiVw%EDEO_0c{$Fi*Hpjp)6mMy>l#?5j#83|wJi4p)f{<2P2t2Vl(A;40z zRl;U@Ssv8u)hZ^(HTsnabclcDvJ|OGa;Ehykb^j!LOav{G6{r<1LWMYzG9k826}eRVAr0tPVY zH`nDeEuz_s2xJPiR&THRT-cyiA#U3?upb!bFs-(iN9x{QA)va$G0ahU!R_z zg$I+#SgwJserX&)C=^PVjavQo?_V?O7a+`;G%~=T!3yU6n2 z0^gy~neC0sP~dVwj{qhBD;--=0I){2)Yq8Hye`LJ)3M5x2K9iFW#j>a^jZIa)Y^Hn z?IP-pW)laZg%9-k+lCv^dcw~a1t$AoV;IX^!7Qs@;ksGKss|T0YL#ax6U}Na4X6Us zxxF4YHxVv77tE^LI@ZJWP)nWJAIp!VT`J^qT7#|1Dh)Qb16oeNMUVm2oo&Ia6L3q! z99x_$zeCXV;m+RF`U^k@*qG(pB~A6PyLa5KU?dW`PyD=Dx}g5BKNP~f4U^j+TDHN~ zR$H~#(NwXCjeaQ28lk~HS5(`dp++*SV^XU|At~3JfcO3l2EbG6RH|sHR4(nRv?}r3 zU%vpS6Q)@O0<+-nZ-ZtLn_&c?9|aarAB(7g!eK9$Dy7YjH!0QI$|e<#g4jeRC#172 zm_jld^~gNeP3Uwo9G$t;#`vyAA+1MB7gM3pM2)3TD7$knlj=HpsMAFV-HQRx9Wa8Bvm3``?!^Yx$CM|3 zYsLFqy$1Uq2Zqmd@*Qe7CE9}lJSC!Vw3zDJ=z7H?L!t2hg8vHCkl{^clmKaL%mhr0 zd=I{ZayBv~-98tP-x?i{g23f;I8%`D())nkS#nD? zejbLGWcAt&N)c)&${{YJO{}7HciU~5M$@p{(cfSIQ0{Y!u-0UxR^h7k41lW;ou6ab zv-8{c+*%loB)1eeY<4?86uXIn$5w~}9l#GbK)H-=X9sndEid|hO7&(N6AO`1wv>1^ zf`X%%UDCnXs2#+N3j20gG{HY3zTyt=olCB)OfC}v=T7G3FpXq^tarMylpGXsG*>@`Eh zY%F$lNs0+Gv_!FD9GZv8=a35y-_RI9=|;jCTK=evwq(p7S9IF~K`aw;7}I{YwowW~ z0o3bbV4t&CzZ!0!H*0V)&=mT;RyCK2lGLz15(Y60rL+pkquoX|SSV0g^#s<}6|qN{ zW3hc3<-BBbxHRt^f%q0k8@CGK?ioevRC~P>8|r z_nKAAS`Vx;o9>KIZv%7Hd>Xonfh$^N0JLTSG&r$wZ0T_UNvtXqbERCds8Xrb>o2Y! zhVTsj-W1;Cn$(mZ?^wvZICJd*@~xrLhl z57)`lrBV|S!K{g@89S9LFojZ?qbEAAj;RfVwZAutfMBIUz~@K3HjtH>_`G!o7jS^m zYjmfgtykbT7VE=8>`^r(7qTX~O8)xtf;>tigjx|CO*CzeP>h!u4->$A{%&wUYiA6d$9L-GpT#bD#0N6E5k!}x{@yGEki~EjW zbsi}L9*r54^Psb7d)x1God$^BH7g8YZpG07m}Qr_=%E(D@c%`rQ~*2CuhQc3*@ZJb zXho)JsP52ODrkrvQN4|iY6J8z0su}FCrY7P5tUZ0wwEep@U~7Q0G_Zp3k-nE2HNMm z$t^wc3Mh{CN;akv(;+mk$8!|4q8qhQ0{BFMy+($RWr}=+JuLg|m8%LJTWVM}tnIT+Gm|4AyfflwkVR z6X=;bI(ylqUQu;e`?VzZ zG1_8db-=MC+~Ybn?1OF0;n9W>DWJ|inr&98loyC=d{JJvND&c{47mW5M^EHxFN1jg zVzEp~aC@fDSu%&;C?3~yVy(&M`FMPu>W(li zO*gXd_A|dff9;%*O_U^q#?BewOOka3sE(E zHJ@IIHY~A(88-AqID)jpi2jpdR)SXrQYRiTqzE$H(im94T9{BEnhgoD?Lpe_{h(~CcK(nrvtzr>h>}`Ow(#-tzJz%lWUlryh?F3u z+F+^2r`VRt>9QN<2KHv;hxNQhCSGnGxE99Ql51!?7m5vOc0$D@cZnPzl5MXQu?(lv z?MjC)!+A~I-K?`z8dIS`3;O7-z~mfO zXrtegaI_}^OC;A>?kfe@pfSI^VZXC==Sreb)7F>EVKFe+KO0zQh?9?FbLQ; zzqTr~w;CJ(^4|}vN2e~8ZjD$%{C-I-;coq;DW-F9xjZfghiFd7;Rc6G>NMgTM#s+1 zKKpw(Rq10qjj`sxGijA`$!4Qk>&oN`e2bJ5LKEy?dos(^tbPy(RG5V!g42{{nCI{z zpgejaR~zma1j80N5&&&Aj=9w4nmbIM!sOk)Wr`IIl}b`2<8v?~$@o^xjC>u9HKFmA zlT#}5T}k8sEf|faq-MfpVK0Y=CbpvhNC@+S3IH8T<+VkZnc!1J>O=dQ68wY z25WWpCp?WpA|AG21+w`8>I~Xt&T1J(w*_xbEbje~oD#v3hbHZ;2ECMv7(}|lHjkY2x zwVKIuA_0?4$JJ7Ie~S+j$9foxCsyh#+dg;sIM2N;DRXy0DCxoejAuP)(@Zz%9Eu78z18()x;Z2q3^MPE`-F5WvhdOdlXPq;!|B zL{gEjoUc$nOY0Qq>3H;ldu40tM_iRdBhNOwgJz>vv^lz(7E7#^0HAk3^|%~1ok}j?u#gilpVTQ)7?5ClzHidF$i$~o)0fnRV1T@J*V+~3Ttt&u6XVB@?MoVVy3o{vQCS@)WSu-d6R`jZbXDPq1 zY%%3P_9ww_CRYOHplS1}Or;u35NC;`Dg!qZ2?s7VmHmX(rcy_a#!XTvM&v@)_8w1W zNLOGLhe%_v?+DMs*9j;|3i*7&jpIs+0pK{Ul9lrLJjIj@X1ho8_4piVxoV*0`kcjt zZq5M4)mpiv)fkLcB9y{$7-dVwJ)xk?FrzAnZ%Z6tjvfDw({4}~nt=FtDsK}!btyjp zw%Qg8riFw;#6#_OU`P-rERbCR*KlcdRvrGq2B- z>T;L$sy6p(j$5%bF5pZ%wQRW4U*`!~!sd$p6)fm&D3Nn94*=!{0`SZ*Igr3Y&&?Ue z!<)H--=k+;&pp&(u}E~^UM_vTB7Uw51$NV6@znar)WjH4T`6>VnQUVb8M~j8^EmCx zSjbd6#H7$ip^%Mi3{p_%AjhGZ0Go6kx|J{kjQsbCIn-#XSj?At&?h)!0G))vpvO(= zREo081xAz6j3wf!IGwP61BDL+2`^>u{V2~oXO+`&tC~+}&E7_L1Y3gf8tGj6IvNUY z`l@lc(d^)SF%W*_5e&QJGe>QwO`R%M!CANhh0fgmetAk{)q<^FaKT+q_C0_NiZR-# z4ZcFF7p9}ZpC~2U*x@M*dbLJQtB@)c^I5IYnr~t$Kr~8+&tr#H1sw~-^qbi6@9%h> z(GR&Q_-j7kd~UF%nB$*_{P$(^;CLZde5TKqwHoRvOQEOFB}@gIBbHF*G^=4ZU&zjJ zwN{6vlL@lT>*z*hG~4uAjfx~tYq>QRz-V5L&_-6loLuQ4)L(Tmx|rgG-T^g&FPTbMhx(tF(wb@$#CFH8ZdgZ}$NuG+w;(yHp0I;4f(F2U7Z?#ein6nZgPoo;tj^?VbjKK5D5_qjW0#>bWZ%^bxqLyR(yDM|S#ohLt{mkHc}g|q@_E@T z&~j1pEi5I_7s^tZNGzN)fLt~e*XW&HOl)n9_;RDi=l(4v^LAJ~U>oaBE+ffu8)E_W z#Z=zC#~F89M9vA#UtRzY%MHvn6&0&-!%=?KFR^@m$dYI*o_oBlVY|`8mp98=N~lr4_IZiQ2injorjzDX^h#8*Y$+0-PhW^P$vV0N}dgwq0+n zcBd=}KOMUc1}^50q}O}{{;OnH+tIBnzAN~pqstAntO~P-k~uD z&0;H#jB+}2_zRFtSL_cal1}K7VH&_f9>BD1CFH$U9-@=$T8!r2eJfGVkehAt>Xit6 zG4&Ih!bWq%DXEu(({@3p1lxT)tW+}bF#I&z$l!ow^y;}bN``JPdr%->{x=zbTCG*{ z!T#@|i|v(5MdLzSkWpM~G-!?HWIBn&u)t(8o5!%p?k|_(-$45$n*esONXT;!m}k*; zqf*KxHz_m_$UvNqP*p00!OupynARwzFnhX@ZdFUEMD&WJuzbE)(7@rUhyf^Zg^Ha9 z`VVCm4|+F{)TzqWO0(U?_zN>dz=CMxLNCTQlz_S`)!O<;d+P_}{soAgF0apYdJ+i7 zimeGtpfK1w)4SewwUn7z%_QojVyP;})wqJlS+qGk9$zDHcA2a}PhY8r?eyEV@&zUm zE2Zp0qfvbTtS^+cU#`~c8ktlgCKy2cz2%Xi*d4L>Dx;HietAjM4&FZ8K&e;D!~)JT zwM_$)ZaJ?3{1A&qs=4%47^VnPXnf<1`|TAqPHCd&TOb!Fo5R0MT>czuOqqm|*Du9_ zp@l!IQXaw1?GC8bihdeo2a~f|YeWJT*a8ll665-z)St0jL_oOX%}YYP=eYoh>* zSW*TPLzt3KrG5^BP^wOlZ90273}rM5pv7UmRxL-;VSCn*lKlRwMGiIvy2O$*Y8_B( z@J=imqcKj$_O_3Cc|IZAB=fdZMnw$$V5)u*daX1>g8+Gm@bPCNJJig<7{R7=fD zxtPr-60zt_=;|VLF`M(%SITgmu#~T)pju(|ZhIt=i;D|NmmKzUkytE7^jU=wPn%U; z0pCb7-RoQ?8xxr23XR$7aC@4;ngRaG*}BV{;kM5J+`D)7JU4-#$EV?Df6!|dDx)cn zXJIt!wH&oRRNrbou2Cua#e5Ob0t;vke(`DG`^iZplgf7|9G*tj>DCGZxy$9kOE*^z zuT`&-b9zIJp)(k??Q}X-BhDGgFic*9#p?3tix^pLzPvgG+OO;z+O!G zLbH-a3&{2QclW&8F1H<4b=%b@3AH$re79XK7Df#5kFTMNVDOyWpkh62md))Hdt)Z* zk}aF{=KxC2-nQ4}bXZL~VDE6D$Bic|X4%1$dZyVZ=i-se&;`M`mlt6HBJiQ21U{TG z1=S;$)yN`_8Ua~LiNRXvEXBIjK3bmq?s~cd?nhaiIeQk*+2|x=Nxk2Be*S@I=#G%w zy6vjq*Xd)iL7Lu)zf}VO6ixgbW}qYmRz4uS`Ae){Nk^}*uVb0YdTIeEr4|66&TldR z8Eox8X8;24j?c@;3E<;IP~#RssX_~^V-tx)WZ*QKO;}bf+S%UoZJnQ=t)HCt_Za}V z{c0|r8cf+7|FBoTp??kf&4pbg_V`@x%2hZ_2?i4VLatoMCzD|RU*B9^UCc8X$3kfK zH!DS0Hpbp|`@DltFmy39G8!-;y21qJvtFy!%4GHx27omf({Z;a(AiB^+2H0pph3U# zcNl=kcW2i>I18MfQkqwIpGMIz>)nyPoQ}W9< z4mZF=`+Il%+uLrtR0_AUOlDxt7pIo1mRf*sv0CwyAG5rXdk>Ic5debPeqx6&@hW4$G!fRQ^#Uo=*35S~X6T_V85uPaMt{&z^x>I(T~D zZH9+uq|*8F{>|HWhlhu6-+lc0W0p>t`T6w|oB(?G>C@Y{@7}+A|K&S!WwU(#`wyQ! z6GY#l!KjCa9~Km^Ui9m6AP~}dT(e2AU=ujdENfu7lOw%fLG2a*oNNElAN=^Iw}&5N zyho4j-S@$kr={!fhha=-G#Uk?Y;>KU{P!RO5ATr$vpxYoQ7t8jR;cb+?%_yae_f1h z4zt-J1{&qeIrbaK{qO$+;QujWD*s*l1_PK*@~>aNJ|Z&82&iBkz;y~LBqS0MR31!z z4gqzOp3!JB3ox}vA0>;;j#?pIoA6D{*miC|`s417(-I0_ zCzkj*0#E)5(3@u7-HAXkpUGEoX#0Z`DV0i?|484%Ebr52FJArgAAkM>lIx$&H5oCj zAD`hk?bolt&iVL-Xxj9*fa5ClIG?3wTVcg&wT_8|L?JXC(|WVnVv3~b6Se}dvPvmG zOOf^^FI2HKKoA*bbXp2z%^63cd zaq!9O|2cg7;nPRr!ffuH{rjf#&qr_n=U?!%*DLK9VrSua#$hqg3w^A!S=Mk@Qkzlh z^zGg=oql`=&UAS6>D+kN>m}MA0N9=(?o02&a~nng7=y*-W2`y7z=LDdri6Zxt;0hy zbM`NSr!jF8f+2s!cRlcWI!S-f*S3Qc*4E^Yo}ztJUj$%B`De+-kMjm;g2= z!7!m%1RE4p=c2MwsWmW}SS-R|Sx+JG?cHm@raqT7dwcT0mjp)ikD_q>5ef6R-;xW5 zM>j|BfriH`jv?|IC_qtN5m5HkSSg3tIfZ-? z9L8xj08jzq@utaSGD{&Bs3u~6H3#TcWRKx90(u_>{k>-|4_@x-mF+Hl$Tta|7D?tm z|NO^4UcGp#I{Etj1dU2ri#7WnrUK;0$OcdE7(g?9@bsA?^MTZIkJh^` zdda}2_wPTQCc09wK!CMjsWu5M!JkMxTK`2*uFL7{ zwo&yhVhubVPy{+%T3uta)oo*ZA&)ogkA_$!0q6LCcz<;C=H1()Z<8%-I2bH!+-|K@ zs;vseNg-b;WP;yLuV)3(a$Kj!Gm*2v*ONpZn7~W>U;6yH*Jy?PSa=+I10n2q02M&$ zzuueS8usjf;3;%Ec{XzXuxS@4f(H^5Edr%a{H+9SZLv>U#iFse!0h*9*lALF@>HT(J$4gNR>7fLlHx zHCh1=Fj+*vAc+4e0pphtAT1uL*Mq<}h*^H1Sf>2PKmYv4qa97MSY7p>cweB)2YB`B znKJnC-B~hm&M_LgB^^;VKoYguRv=D3jNz6hmCdUzq}b7|8u~FpYKq0 z@Bl)QNQ#wn$qf_xH6HL6ul5W$*6q<1qHlnx$d}jPCBXrBd|vtRb2)PjJ(-huB$F>O zWD@aQgBLA(UuuU-?yEV*#X>rU`5!<3^Myo&F1QSMCncFbfiL|^I)Uq}&Y}$%4trfp zBGb6{|2VKqG~HpbHMMfVtt36{}fiD z0EXS2ogJ89ZZjDGa}|$CPqxqnXQ}Gb$rgrtceWj=%U~$gnSrlT;krt-NS~|Om53y` zqEYRDQ`XJ`!HsRfeme@V|Ih|jgp*mLj%hufyDnhjW1&#+l9o+Eo~e><5|dxI87{pdg_Ie~Ne*`b?oE4|Zz~30mg9gcGmWg9XtY=i6C8R=XVR-1=>(uj2pn9VAO+4m zBM-JH1^zTySg(2xMO?;+qqXbW*=!EQ4Mu~(%#UBAk5|)!qZl~25(ThZ8Q}2jy2m=b z${_1t61_uHNt0uRxoPJU2l&Z`rhxLX4<3^g=R)Q#qH3$AD_j{v3?ZA<+5b1NqyeyY z9zK5d96Gas4)E&Li)T+C&2_5%gD2=G%=$$|z~~(TpxYHug&_GJq7{$F6JnzkP@VuN z0I^#|2pTr?3rIEFPy}jcd6hKqc*S=gk53QZe*S)bx%y4X4^U4*c=PVq{l}vRe*fCK zFqu?VPo|Tr%pq6I7241SP~r;yq6-jq5!d7*fxy@Ak#sIctH`B+C$YYG_7bk_!K>#F z?|K}AL>ea1B2?03Xcl2n;N;KGXrMcXx0q1pq&p9Q0EGUldA98wM1Q2MxifzOQ|d2z z^-{T_P-@jkthO_;oC;USx|MSMH^Ndj&;BDV&+zb`cOl7Xj4@dp=%WQH!j8{vruC;0 z$dq!hCNG?c08w^9AV54SD1u{zRfVea+85|`fV`9;ywTfysl{_-z0T}JrUh*&(nDIk z41E9m?#F(^8IH zuDj^hI85UAm+)+zLHeJ=A~|sG+<*M^>GONCey!GG7{UI3@^F7|w#0-lh6z-CO{JJG z(Or6_LcY|5Z9&NQ2w>C43ud#+XvzR{VD3a*1E6vJnggg+>cgZF$L}Jh2`_>tkxNps#F`k)h_V52Ts17fsNf7Du|$fM zvdQ@*ur&~gMbBOx`~$`qUcG#F{|+}4j?74&#-AV-I6(F*d6dBNr5Q=n2I0xN03-nx ziaS6TANm&w@%ht7(LdB<7CgxuTdOfrXR;a%T3#++DE-NT&OcmSpS=C>}@+N`Zbz_ z6leg+B&ky_0_^`h7uU#f6rk2Lm`=)FO|TM+MCz9>2v-DP0D~VV zXCbmsVu3#)#OaC5)6e8l0t>_{FcWfuglNSBKI7h8HW0$Fdk=UGehe@+hc{nGRGtSc z*a4Q$B~mq$)#-7eKef8u7DF!y6dUw=E6;=ufmn*C&JQm$xKuQA%rzU;3ec5(_g}nx z_3HW4C-*pCfLXUiMrz6zZ{NQ8pVzPd_3r5BQk|>X5B>ly){BQ0+HHy4&tATGiI(x; zR~%p_g30$pPJwB5+sThdFrne?(O3G@{5vRK{`~&={rjKalROd9p{Prw%W?W;vt2H) zYZ3^24+NkR)?2{P@Jk-|cd@_#w*CA5d;D*oj}D~gIF0BE(4EtF+4nMxs8Q~)3* z7=y(o)HJb6*Ab~Necbah`p^(o7OVEd=c(2#)V0?jaPL(KipwsE~v34>Uxe>Xx@7{m56N&5y8&JWu#y7~8vEZYh32q#5N7*amci$07)w=mre)zFdril zu}DN`8RR09*{mofQnNlD7Ps3ih=i|UOOlCBr!`M<`QprGfuBmE;j3^TYYDg}#d zJLn0)uXk^|2j^$!WucG<9p32t93F2p!Swz+yXLdNPt?_zNCKuoXNK<5je*Sn1ZWIE z><5c0IX(S(dYWku2E9hUI@sd!y!~FiS;5A+H1saV+xqS8axB&kg_dk~J}!#Is{GPI z@peNblFB5}^FVN2N2wX~)_eQB#e|90FbTgeHD{wNug|yLPEC~MN-?`Gw^Fsc`_OK; zvkHd$utq~%&LAaabr%ifYJ*OcNv2jZG_Xc99!qZEy92-goTY|!yATS77I=hCr!sNd z6PD6ob-1^8cla@`q7q9iq;q%E)k-0kO@n8A6AoWqUIYm#f7>R%?K9%T%l9{MnMl;C zGX}shf}PXMW^?3Y;(2ylp5E1E7;@CH0;p=rQng$lebIcc*XQkDUWCD88V%T6jFAPG zs*F~<+wC)6o&^JlSy9h~uDC3KE8^(QMhA8kyqY_RnoQVr5R++ivaND37>Jp4y+JLT zSzzLK9@2R>rrW!7*YEc+27>`Z=~IdYTU$MNBYS&y_x5(!`I|6t+i9-yR}6q2wg3#k z$^mcZ48V5)C`Ffqbp!;~Gk^&oi&!j~STa&q7y!>GlkrE*;&Cif9nHSJ>hXAz=fR+C zrPzq;-`VA*11ABrYg%QrzB3q!c*%d80k9Yh)FZ^XySL{I1&&WnC@*%m$>B>Bibl1b zo?v67L60!59EMh+g{!4KI1y&S;Y58gyuPI|nPrLKFIoYEh1^dqGJt6@2SKxiHQPJe zrXOF2re3LBUgI%j`~7zwy4&GX_MN>4n)>zW*%gvhYj%;sWHbrWsii)x0s|01t;(>zgaJ4(y{C7(E0iKR6CBvTKy7TgS7eY`ytORX3$O)?Css#yVC)hXe5>{)DkN24Hi0pIBRG7r_}ZuVVlPCA-}!hhX)bQ338( zNEv7=3}6<~L-a{~eb?>P0v?)QEjHr&l-X$V;+)J3?jqkO1s?eG|2_lYJ#d37mgg|x z&Yc~96Hf7)+nD_=iA-*7)k?J4Y3*yEy#|9(xMXYr2azVQ{QJEKI=;C7F$uT{86e@s zVD&O=Hk&4zT3`UHZ~zh>1ivX%l2VBxYdSoB|E}@u{CIlj&TcJme0GKgAFSO)3JV|= zpo%Xt03yp^1@k!UmQsnk_3~=yY*uJZU`x#B@N_Dq<+^Oy^HV9%a#P6_Q8{du+mlwS z6Q6YwxP6n~_SuZQ-oi>`3bSW7f@xXGO1%z;!2<2w2iT|J>hj_eZPuPlB9-Iq-f-Fk zdxPcl`F%U1;6*Tmcz>fkXt%0`LcLK=gs(2nf(w(5X`q7IRIEGx&}-8=iKpJyi*;b^+loeIs9Zlh6T z?E3HA(VYdppPpStV7>*ukZryy zV^oT1v>Jmbn@Ps$dTTnDE%jBn61vM^q-ep(%=NuEIC$pZVAA_9Up!kEoc~RTrAlck z6<=?8Q{cn=LsV6XtJSL2X*xO=!1?)U*YERXe||qXKK~8R&H}h51AXJO*~ zQXLRLDjYodcGA$@d2&bB{_^9;b+3Kh6$s}UYBNah92^|H+WU{hc40^lJQ5D+H-G@Q z3{w?wDGbZx(wNEW_PeDlzH!g(v?#NwWR6(mUMdj@Gaa5g51!rM-GA}$uGcBOjzyBB z;-2~T`Q01noO}1L!#78tzb?9a=AeSw;3YvF?TW3V_HSHUkZv-<7FALlm{i$RBP!F&r{0p?qLxr(P!a4hKG zgIFXKw^A|GvS>5dl1Jb!clWHA*S`;5-51kq7HC+E=*=OOl~V~~b{%|3^kSnBOlMu6 zH;%CAY5y;P0Vw9+K*C~UxLTtf7J!aQeu6M4)oKl(^+>U;`^e8sn^VEN^XQL1VPMCF zD_*|Z{~H;LQkg^+k1vi9Spgge#4HsHmEr)tB#+L(cRHNTi}OJ3uFs!3{&9SK%c^d~ zLQI_iJq|n07xQ2P74pjr01F1beMDi%H}8)=e*WIBhYt@+V)cJlOX+2hl{5l2y^0%gb|(|K1-5 zkct0sZ_geI0ezeN6l(_7JzCZf8(dGvrg4)vOXtDNz(g>Tx17jV%e@+!e zPV!gB-@e^6MOGuugJT^RY66@tw?ljyI1Zc;2L)n<;wqfEIy=tRYN?+WkyKu!P)H>r zkyMTqv+)q!>GpRZ5{g9W6wGeD4dtujrs?sX&ky#$|GB<=e6irZm|-cRH%I^S^$}4C zyIgD}hIPjW2UKFh3eFDa6%qd(Fh;|kP$Lw}1aeqgR?Qa|BchFFvn3EqMO-N%91rU*>GaBg94`2L zyb#>~w{w$CFdBQv%$;Mw*?_iwp^z`owIqnq&#$mR?)B?8hwndqKCZ@L!2Y^B(?v<( zeqig~eQ;;rzrUtl_%EUhV|hZpcu^S=!(_=n4I-=qWPC8cbNAWHfBbpy^4UE<`~2!^ zru>esLZ^YB;)hTF`18+y9Q^T6b9@{KUebb(&FIhg#nJ!u_IvmjC7dX#CU+w0Fj9Pt zr$_Ida*D>EDKXQd7cj2>>iPcC!&5uGE(6*(%=}AWeT+hCgKbDTTB)Q z*vYfhe2ti@i}p)O3_z2eQ`1p2nf(()qGrvt(&1ODY3_{(TDoAn8JYV$T408B1-_uv3U$HSMWfghKKr?A-O<Ff8?rSeGovlq|r8Wv*uw*V?nbTVdv_lP$TH15~j zfAZ`f2M2%t6R0DPx9MkPxm-3z0Boz-zNu&7_IRBkNvho&z%jz;Z>HE5i%EvAT-$CF zRSNtUq4q(pbYPtTYPpf4*KYEoHE11!%L5z$D3_ODf76^~bm(0kmm2c1c}H&&Ltw5z z-ny>X2v&No-ToL>{fVgP2l@8D14g$}D0B@@uMeh$0B+EPRi<;G{<%CJ+A5+abURWWey-B0^2jtW!plj5CEc>VXa&#$#IQ_p3fu^iuhhuAJrMd+JsLZ z!kF4>l{7wn{Pz8Xo_Ul%{*D^>d^!I1_U+NzkKcbTxALWtrTowLF2MdiSx=}ur^7xy zg-JBzfi>t+ZDRpitrlW<*5~teBgkd;Jd;frc-w*#HUDd1Voa$-;F~aoBezQ!k48=C z8fGvv>{fLx5lf7$OBHS^CqDZ5Db{Q?@`;;^;Klkl9~A{y>&GU+vrMCfo3mwoc@ZA3 zZ@Y1Eewh-fRZ2y5;trli@Y*R~6>O55rhLjtPfA5Z;b#`)E znlg-X=9Jk)OGaK^5iR!|mMx%Wke@ZlV-zeC#BDj?4mHDiy+z}_W{=mUh=x&Dym7wY ztd>f-bRrfDUkA^F!3DjjlW*0^g={Vl*+v zdjImz?)9$coVC|E&)M&O&%1NqmKYNg6BKC{I?{Vb&~sfg0}RcW-(8-4J}WCS0}L}) z`S#-JyTUOD%+@z@0iVsJ)usxeJ(Ia-bc_l4o!~8pE*|Eu+O3q%;}d5=36t>JgI{Ys zCBP_0hHB)rA38M2!V=h1@Ocq@>D8oCk&;^JQUr1sZ(*~s;&Z$~yu~kg0l*ut?BK?o z_vtjaSNp3@&)7b%)~bcMXgFR$2=RfP9MUDtv@=(iu1mr?C3*ok!yY||(VvGH_A(5A z0$(olp3QYUOzE!&87VX7W@k)HjzG9bO{BEC5ol=|Q8^Nhw85*?F#bGQOwxlwQS1s@HhICyt^?e}^|hPa!*&!+C< z;ZWe_#y=KXr7>B}c4&ECp$C*NEWK%klX&czZeITOtyG#<)js^yeX-BLLWPVMlw{7!OnW z>jBiM`s`Du&e1E?Tx6aLH`6fGT51v8;V1#1ywcGSDyNW7-_K~-Qmwo(@h)FN0FZJv zJ2y{=xS0UB9O=F*zpvXA@x}6|985ic;OqC6bUJEexZ{x4x(S9Z5rN7&SIY+s=8<>{ zZ6N%ILOEI!ixtGtBLLHsnR$!ZW_biO-aCob0T#DzUl4IwB8Aabx(h-@5zOztmWCHK zP$*KtVB*S^sx**?U@+B<4hR63hovv{fuO4jeNV{4&-Kv69nYOvCX22jw^~n+I0cYf zEXx+N7k<@)O5()%v0;Tc0eynC&t#Y~cK&lGSkRI++kK4~!ESjf|OwFdx zQz*?T2A-D^0B)>exx4WA1Ht=jt;-R2YlT7%Q3${fvdYMeOsmstm0~8+SFTldyM5Vg zTv`NQ2GD3{(WN)TqEqQIAy=x_EgDQ|&m~(auSR20Otbkl0#FQ9vpoXP3}vf00T|Bc zM^B-6%rs-8a>@_^Ad6-mA0MeSg3W03=OLaW0N`j}%M1m31YkT&>8}USGz*vCzZ{<& zdBIbZdsQ_fP}kAI+c4>kG2T%Cfd1?agu4?Oo_(BA)N;3%aM)1pBwzE7l56GP!O zi#9_(!`ax`-&v_8^8Izt&pio18{we~h~f8_&{A>S*SU*V*L9%mJ?iyyQ0arf-$9V% zw{Hp5pfCXb4uXV3*Ec?u$+5D&X^;vPhBt2tY4pfhM@pQ zr{}&qL?ICifq)Bta7!oK+}_^be!suBv$cxr`&!5P#^%<};%pg1FIS11pM{6WJb=}~#8=utSjDGAKimuP;bWV=gL7~Ko zWSf8eW`;mWRU$DtOhq{!jZW6C{~EwelKqxdin;XnOck_g#%VBF9a;qOl4nNZ2>TGhf}^+D9OU}hTsor^f+f`73BpOgAna|y%n3S= z)yFm1jql+SYFO3JsKcr|O|~7y+$%-rk!#W|6vFZNNJa>Td67bU;ckg^YW-8yb?}b0THF-k~r6XI!+T+}AgEge4=C z2sw0UXKFU9rEF?sG3giI%mE?LDYL*Dyq&9o#q%O2kt&kuym1xKjZT9oDv`Ockzl>_ zatBr6#&zL=0(CY%BUBc6JRX}q%Z5GeztF9Ejg&*9v>Ju`5dR9ADFjBF!>BKLqiMWm znnHwI`#t1`Y)rtIn`u@u(a;U06aXuRJunl#6tV9pF?=w?|1Z!ymEOSIM`Vny(zf() z!(=gW$w~rOP&a`vrJQ-Bk)IeBg>_@Sy?v?Mxav#cz!}1$S~~f2951zu}*lXNVmDjku8*( z9SV&tRLBC?UMJSI8U;_Ql8N|?IWp3HsF(AZWOPsl3}A85WYDEtu7J&9;{?4f?;w?{ zR*MMKDWI>|tlf5_I;e6;S4#wdqCC_JLmfDuMLie9n|`%Z*+P-W!U~t~BTkChuGh)A zlv$*fjfv-HI^fyVizbWp-sQPT7Ta((U=U30LV5VyJ()17vy{fQVW!(L`FHdxBpJ~*)+T? zOzBSo7n2)N0ah!9d@xbI1q&Si+ecV9-_{tF6@{I2r zvD%P1(#%95Yfd5*iPGsb8a~#|0$o51%YwX|1L1IRea|>)o&k+U+kK=-5tq+@H(5$) z7EZViX<^GBU?7zkzW?#f-CHkOjZDa7AdPx18BMLhD)`y|#03=9y3H=SIQMw__B{B_ z!XcuD?!X#4xadnjG6)EeA0NtD#gd`adZf&;W-_0{uw4)SF8IYIomL^?vF2be%|@w^ zippt(4%>Xaeo>!wUwT$-7M{oF^58ACaO85?Qu6^>G1tq5Trc}Uv7}Xq*t6tjt?)}_ zo_i8vx!PzVb9mu$;VjK=GZfqplv%D!Cu5Nx$~lB3RcV&$ewWi1$w7&fO9(Q3ypD=E z4Dd97{5&AO3d(Q$V>S`9)VV34yQEpTvwJPnd$@V3CqxWNrIW8sq<>J!qn@o0^Uyl#J0OfRl%R$TZ_aL6*=MTUZHv#*V&3 z%Q6pebAJBi^yK&|-$t70%j1(zpT0Q<``_Sr^>%Y>TRl?*YIK*ovi;7+C2R(g-eA-h zTrQW*Zeu&0=T7IS7#9V4ug5=Y&}%e!=?%@w+S@I(Z1m3NyXU*&Ohy|9!_VT00&{Z| zBkkn);1CvSbI)7#dW{@mbNJ%f`^dHXd%w1LA1tkeR7u9XpIxy>jaCb;(~ZTRcOcEv z>C-pn`u_NmxIM+HA)kEhYlxKneF@F->hRrg0~ERVrQ5T(P&k$N{>#zTzIC5z4dr&!Lj!K z{rk05`$}(@!OdMPVnUVG@#gIo*g5NOcQ&`)*y#*Ho`F!JT*xLO*JJ7)l?Uj=dz(!@ zW3$29k#=GOTZrh{XPAV51|c3D9-e$T8)#9Bwzb#m8*>-1jRq`+dURcn6Z=~l40=7u zCZvE@Rl7l1({aA+%>oc(1LAJla@4rb)Dc>3CyX zb|{I9g`%3HS#7I=TDo4V_gMf8PkiX$c|Ct2el1MtPoYl$YL!X_VSvHHk6(b+4OWHk z)A84-b_&DFGPwU`y8%BQgT&-8Jzlt+9v7eho*CPkahSzHFhmlGh{}Y!3&SdFP9&Cy zUQVnaI9xU-8tZlN9mX6NUUrd&Q074hsRxCDG^w6qi5&BRBAc7>l&eo#c0hJ_&ZBLZ^aXlJ_ zTy~v&gq^a`eq2MDJ94vS*3fBSa)_3Ad#k_aVy zOe=ltwDEn$(W}M%%!#Q@1E)hER%otS2A$$1M*0~FSOi9__kR!MIfh>CJ3Kfl z01XBQi_N0b8V#T&{pHZZ?o`36D_AUB`P15me**pGRxTF{k362>*Fw}6_qMlp_8r5E zmH>n8)#}Fb#@ecFqG%8C1Y>WVE>{Oo0I*Dx6GREH*TrPYI!f>{tRZBPaHtK*&%t4H z=3?P#A7r10T4vO8ewzhJ2`NQc_I$k+?$v*w8Sl*)E zWKo8{d^-Mc@W=7zN0W9a5E(|e&(arPz3Z2(#gKIZ-KYjOKlo>IIh_~8Kr|esoA2M9 z&TmfVm7AEkL(?0q7M;)a4T=arJ~)I8JL3d514~l+jFzhF>!+jN4}L%T@@;WrdKuyuArbJq&;X^!P&l5+Q8-K{ z;|Z25ottN)oPeZ$WwR_0+>n!-20jC-+OYh3XMJOLYu#a9#83VV{RV7(@Xg9EN088B z_AKLu)nwC11W2Jg(wgP30Z|zHJFS5It=r7QEE}Oz`YVDpTLSs}>#5%{IaZe)frEg3 zd2Jd6NL@ey@Z9x;bX;id%d2sB0`&TK{kND)7{&E9%ntlD9e@K^xLlJ1vpTx?`50Rhw6~~(0banat0*KN)$C|?d_Y0mS3!UpqwLveByMBBE z;`-6ir}OUK62|IKw41f6P-O?Y^|E-FqvbCzoz5~elNc{yMEV39t=zE-`whT2`*+4c zmZ5rKX=y3uySz9*3m2QsQtJBi$tPl#LO0{-j=Gyh+^YIZ7H%`B~8h=tYed<7$Y92TwXTf6%^JG+~!tuN!N>xD3gU1z7@v9adHO#RNz-ljvZ=_E6`=Z%p6 z7Kp`HeFuMIE71Y(&ScUFL?&1MO6q}FB!0KQ5Br2ngw^I)vER79xo#X_1y9izuVL$k ztaW*L*;Ifp#~pz@u~Z}oo}ufshi{?DWkIHc0SG`W5}lC9hoT6t>PPBs^ztKlF%ScD z9L0`mpKFASUZw-=P_^^?-05~YzdNar0)S8AS+d|2OQBI|+jlgyH|+h6tm0V-su{aK-)oy`9Z`p@8s!0tH1eg+g#2y!JQ)xnj!m zm<}~m3_g>o>*P-zJUrhj}hbjH_7!ZJD zJQ~fa)H>Zur6d56NG#Gyg~Ac8g~JNXdjSv3koH3s){in9#}dlHEW?U-4}jH+ku2I# zv4`K&S+qh3{)v}>%i+c&lM8+W9UqMrOm%_kd58YM-Pl5*@?lR8+p>gWv%LbZtaQRd zE0!Je!B6LqeBEC-zh7KDm>j6s3bSP4!WBs*a#}QU=Xb+^lfs4D7a(pqX!HKoSz5QU zN78!%N-!NgT)4dYJ|WR;#{6buYiA2^YFI#jdv|wh<27a#lgLy`9{9y6Zc7vtnQ8sq z_SU*ZG35Nwti8qz{otCrJ3DVmwHloTd#($GA_*E?!f zOC}@;BG%4WI!6wk44t~B;8yxS*KgJH{ZT_1!WI(vr3WVeQf&joHcCi zK+3TLNXM3GKzvm;`|CHGtF(M5bl(tcZfpp_2ArqS5E6&W7t}I|emV8N)et64!Db%> zt>#Z467add=Z!!J+23_^&(E)#jSiJcgND@{)+7b??JfGzm+_zR^ltSFG=dx*o_zgU zY90Jso~E<8oJe>)fUQH&45<`qIeB|>a`x5L%e#0PM%sr?MAri(VQ-S8g(gcVA*YIM zpmPck8B{$7e>vzMR1JdpB9L>4!C!hWtr$xpd0tH-P@ytoZhlsW%skCf9{Pq(j>cjp z!CeDOln!MB0%$B2Gx*h~Y&JK+v_{uZVBuDmtD8oiQo&%Q9G>Q|T`*f;o29wz#Crc? zv(==F`aQmTJWd{9UMLpI3i)gXYl1nJZ3Odrn&2cs;4jysp>S}T$L0ZCw3iGN0$7b(^o z&R)UCTB)8-g@b_`zwd5L%HE|GTKiDnpdh-qGBW#$FDJx3`ER`L7yC^myxEuQEs=9POFr!J#?!HugC2v zDCD6u`91}k$rxlC9N$maU7)StsE8Td#0TcA|ZoM%o zr9!F7_;zb+(;=HInImLarK=JuhyaV8kP4K$f~@qLtAjkb&f&|8X=Qp~w?Q!%US^V$ z2Gao286xw}!eqb+WkH@pz~>9}CPy{xnVvxwhAK3HE@RO_(>y^P$5s7Nm8 zDVImRLR#5Zf@>oDS=p)~c29 z+`xLHRzv9EC-GyAkl5h-mvMjb?d0g_;0TPz@#)#OY8i0)V+y)BP#f)xSgDk9o9OEy zQk|X5-k}%d;OOHgFWP4D@F(&#hsy=(@KkFtii8t&y&ix1^7BadAxup+8p8amjm_Qn z@Aq~#Uh#pb#)R7E%KYeG8VnbB&!GZRCSF53pz*|e?^SbHN3@msd28?e=^zjQB??QH`UP$-y;=5c-jGA_^`jy`@l zyS%JgAWuhVFs2LLmy^L?wym#kER>+S`8BVEwssr(%L27dXDInSXRvWDAV{B2zc}%2 zg?1BcHtQn^MF7=GvDo7Ag;F%OfwE*ajx}ccB_zL`0;LRwUYwkMNylLQtb+rb0>b8i z&}6i?Ya5;Z?06YA6a~NY+qcVL6{!QxK}gD)3=TKx4#6(!&!Fu({2%}MpPy1)saE@I z6?;b8hfV?sOwPNVo&BB7Hx3KRTeorF=g40@p7YA%^3ZL?=kYL!ANQ4-`Z=-FKqLT2 z!1d$&{A_MxYkPP9H)sU@ZEt7WF*W2)BoT{psmpX$BoY#h0C-~L9@bs54~h}B^zA!F z!fYWWL4N}5=Lx?g7D+|(Ss(%6#~5Hf01Fxi@Z+CD@@8}A{r>jWriy|vg}ZxOlbQoR zk30-kij`gGKU|}nM)~&bXj1|F9TaFh`a=t-f6oB#WjhqwWY{n(% zK#)om>iKBM@4LvhI*r`z>DP-(Kc<=i9Uok?)fo?bdCKRrX_kD!xNgzuZCkI`Ec7V} zGcN;6-c9(fc~F_dz28?RxgU6 z=&`8k-7E%`atWRmpa<21JAZ8yPoSa`o zEGCl<7Gw|zC6Y!W66x6vx;LKN1W|Rtvc^V`PQ=4)on?I!%8py_9P=MW6PtetQzL$< ztFJe9c6Ro5QS0TqH$*uu`v3Zm|M-vp`!u6f3VycipN0Sb^PkR$0MhWE>4{h;iAi-9 zp25WSp+lHeY7ENDF9*MW{K3*|m4vM|1#F%W-S|A55c~p0c0u6V*{74^FQH7L)Ea|jWT@6Tmmm5$D^Hl7KGGI}6vt5n3RQ`IWv#=a&Ih>W^TMPi|te0|_= zOmghOejtS_An^|yd-IuO_Nwv=I)KjPcxB_sp~-w}_l-qcs!S7(pFhv==e&_kgH|MvLuTI}h)v%ybaS%3}&!y*cndE>>zl5cxqJcXP z&GB|;|NU?K`T706`ejFShgOz^#2eiWAvKLtX_+JcAVo!s8n1qG; zhr)6r;XtG-RjSoQJy506YDBGY2d18L(QFE;3o$JfE5ad zNBnloWfV5RTX(iL*H=*=9|}I8zz@k(ocM)ozEVlj8S@i)mmDS*y|;idlgd;oQ6zkQ zeF^t@)9bqlN7)*MT#C?{Ocqipm8J)`Or~Xl>#NK2sugJGdwaU9%hhT983HgZ;@50( zth`p-p1`L5fIY6SIxHr@e|x3J-@M(~dk?jPcbgl(uHp3|)T7q5P5KKG9r~>hUJfp+ zdGB|gehpyi*|?Gh=73X3+`9d@$sCOZOY(zH6>=!K==~i6*!Sqh=X3i6pj=3yN?1Oh ze>P5OlmL*-wpXtV&huly!43{T93Oo=arG_`8gijBq5B^%)Xx`5F&P9yDH07w5RFEO zTHG+u)G*;T5{*PgH2_%gUjzgRSnpptR`0)=N4aSX)c}|a%T_feaHT`&`Iq`&xqPA6 z{rUHU<1b$xbPid>a})aI7&fMH8;`l&+ugBDU@^(tAp{4cx6|o%p$a|K>mLG z9#Njrumpf3SHrmZ`jySp(~{_HE{|0xq|(Fztu>m#gVWi`@NCzUZM25gd zrT9C+Na(&PgYo{fxj7orY&1q^2?D4vE${*Aq?GZz$NBZk$+0(ExCd+do2&Uiu*Z$A z@hOIhAxw(+RqCzFcG8bC==3-^K6BP>MzaCck@o$ zWA*+54&VPX%NL;pU>;}!cpNB_&!G;WdOn+f)*3TP02T}utIp?g!kz>$c;ZK|TJKXA zhRC@G@&xn&K4sHj|K~FgfQJ)#cO()DXT?g5dSD<`DHl|NWF#p)m@K`8=?{r#(3s5L{ z5-plCKF=VgF%EF={PyYi7@8CgKc0RoH4Z;~IL9k0kF0Sd_LAWCNfEzt^}^D;=i=D|Qjh&zbp=2F&~DV3Jh@b;3HsOegPAp*WFD7x-K*nnMW`fj%3~<F|u39ih13pi*2sm<) zzy?Ylbi)mN`A`GMq*K{?HknNWVn8&<)gu6iO081$HVIv+QmTP&5F4)E3n7+30U!Zs z$s{8{2~DYogi&c*2V=LrhPBjapy!`8s}s;j9UNBjvGCd1DbT+Tj=o+_pIoU_I&RjX zuC8xv?d)Lg(cPWht*zIuUM~Hi{x~M<+3IyP8s#YzB6Ot%z*7?d4v&vMIo%!Of=Y_V zT%f$V0j`W4;=rcq%S0nKbeogd3>FIp36KEk7_5zap;$&ZY#{#QASjM=vs5b5Y;|A= zKn0fT7*E)S$bR|s@c@>GI6gUbN6_wf-(%xz{OJ*rDEDB^PsXH(UzSiVS4Bf!*SAoi z82tvLaQO==+}2E`P;9m=w8S1BU(A^8#>E97e->ZPBYKb-mJ9}%cjf%){?C4c)Ydmm z#wNz}W8m9YH(`|Z{;nC7u@bOr*ulXKMsBg8ia(YMxs=ji-`R5*l$m@9hFy?=C=X`~ z{R;^}3IH6ZQ}@Z-JPV5-r?W6q07O2Qhr&m$fC~)MFhT%0Jc*oMO2x<5k3b6xl=f=n)(fz5KW9Fb(mARmqd!%4A9t?aQYrCOun#lj(=8jOH1l8E`>5(9tQB+K14nfE2yL;rjCF+!MK{i}^4s2Ldbu zz^fnH!=8mAs(nm0+tzA;TCf1{AOS*HLZt>47NOC0!9=XfN7Qdj!-wj+ zk_-n)EARIA-#K`JZ~_&dfGCeLJNz#x8At2I0y2P#7`=Ju&j(=r=%2##8o($4n5E7G zw)XVMRj%8H-(q3SL3KS16bk(hnQSJDpv}63!y-T@3`GE?TCK!*DP5&ftGiGx7$f|E z>x&xM6eIz;`g6g39DzdUIr`*RD+Gl74Rj^gIBr-krOq;D2c=qIxi95x>gypUU1x*0 zp#n0C(lf=#^CRRr7(Hbj$CEoXG(e$6WnTj5C(q1F6)HSB zfT`CAN1edc0N~trQ|rwkrBJ3+N9go^g*yNkKtYY}MzaCr5Ect?ODgiv zB*^(dMR@>BbdK6=HtWNMVMqc#msKy9z<$W3Q%RCiwXlRT9J0w|Q1^ot1Zcg6O&E`Y zR)N~b11+#M{w>Hb!;|YuPYbT89zbh6={a7K@g3;*UIH3(QM3CH!Fzsr8&x{Y%EfTuDy6C*y z+uvfiTzBZIZDImI|8F~)O2Uax#1j+aYm0#O!@v1V?s;SE$Od@ob{^(gQuF@yJL_mf znh@ONz%T=Q^`(K}-b5ZU`D_*_1Y+W~B0vO)ic}gE#;w(W6mTMuP-Ki)&VVn9VW4j( z3zPuxT0Bj)o*~b+3ZX=8wx^P!Xf_+oF};+{rZeDjN1q)n_qD%2BCuO9Yn}x^>|q}E z@1GE>s5hBxJgDXV@d0QMho|R1Xm*ozX?iDv5o9yT1ftX$EEdiTLX|r#rp2-EdOm*q zG%Z>hf(o7-5WJrW&lu34gBjA0HSnofYqwYoCIe5~A;?b9xdG_t3}#f|3&fKC7%QX_ z0X>^506{mEh({|Ly~V!a(Cf^bYYwBKfBjh|?49qh84#TvR*BeT4_iEE$eQ=>H)cmE z?i8R=z74iisg%+>@HM1Wt{f`-0A%yU62j%;B2mP5d2x1~%%yIex3T*N*iRfR zJZ$nIG2&ni?)L^%8zpuep?Yg~^G!c72kkNo2*68~%bx&?XuxBIq;j>M>b*qGSBU|E znS+fr-pd9&tl)8d`*ISDd_O&NU7;L44|B2!e*>5XPz8TbDqh8{DoOlr45$F$PZs<0 z3`XXoUM=Sz7Hqrk_uh@Ar6NMdVE>DOu+B!Kalo%+GZ}^eX!6)wD4rtGNVuy6G5{pF zOqD{TYDK~m%E+QYL{R)k(wS^_UL=_+0z9;8Oy!zYT(7kWf?FMirgON0p>Iird@fQh zV{x!pxvzaR%4L9gRHBiOh~igQ=~?}R0)`5uRxiHxd_OxmJotEUbb9vHLTkST3eR*|5TXA~EgnZ>_5z*eV?ZZb9*2T76)Ly>3^IC)5 zaUI<`hkmST6HwHu)jGmr&SR$4ZZqx0zFtVB3Z*$F3&>bh4xHxqTp)Rs9UdN?5R(W1a~ zdJk6G!S8<@oSb1TTdUsz1s?o;a({>=a>!HZg9mFIQN`O1C(&d_XiG@tn@^9a57)Wp zO#r>J;5{^8wSWCPJDH{zF~kjqqV!9Ng`E-ctQMujWYMgx`} zhp*w}_w0b{%%Lf-XAXW!JeGt)eD(e$3up)$J)D}S;4>u`z=F~ucu#Otv&yC7 zLGTkJR9r}bv=DP~m%&J9GGX5QOtX}L^HfiLKpw$H?)^<(`jN*V(g^|v2PJ)Y9-vf< z!X5flNjT?_#}LxG0q^Aon#M$oQm?RRzMbNbJGHEU!yLIu!t~6-;GIl5n}HT6tXUT< z3qrIH)T z`Ai~=?tg%SpC?loA2r9~pm2hI|eD?~hm) zG$QsKsZ}rB%LP8d&?~m#LY|xDNHo%~H?EtBeQ6L1A6}-1LrS%hFFtU2f+34NMk9b6 z03G_Xw7CH@oTJUnB5fGG6N?5!Jl#_LaZa3VkXSN<#i%db1VZTC)%^36hl*OKD>a`e zkD;53R=NL_?aN1`+qDCg)*AIV-B%;4hr^toi)ZTt;jOo9d$Zjv9Kt^pl=sTbhDD`_ z%a|p%o26_*#-|gbuW7w{$}=ix{U2k&z0%|80Yy4+LXJ#%Jiw-u`SX-!UMLi#5SDe+ zHv1IDy|T@T&OXs99NtpxSptA4dyZPvZ&L^C13w;xas%~fhW~H)KcRb6`u`mM+r*m$ zz_v&bz4J(KY2ad6aPm?a!l=2tS$nAD(xJeO&l?@uu1rZ1X7|^yMvOAE)vUbrd*ghF z4o4(VRSMam7^OOm!C@&|)lxa9w_2@=+pDWy>NEjz)3Ql<CZu~?!5eO|D7K5eiB2Qp&yB*TJV9{c=SuA;v*W*Rk5}IX=a2t(!o#2jEg0rw? zFhMFvCqp6F1)UZF=kaqG_y&WXe}Cik_XWcmTOg6rGWW2DFZK(mm{LbXGgK3!>;{k7EhwN_d$bV!=>~D;5idg?ynzn3RP@gRT?23#OHFrL=Z`pXd=ZM5s3y zM2VZ*yUJr1C<(MhGL^xVC}DjVtCe)+ap9E=D$Q=YUd$Gn&0^f|zw*q=OZjpTYYOq0 z?LLuatenNz)ekosVn~F_Xj<~Q+=DFY+O5Y^P25+xTuuW%Z71x3xPxOXv#+e$Y!*_x z-6jbv0v3H{rrD2oplWq`CDfY$1Y|ggReYR#iT7wr+H@%BgDL;CWH}!UWf8hoCrzg7 zcxP1<)hDD;OvQp=xyNHgM83X;PhJm_EH=OLP$=0Pts4MVulAiuE5xoiJRN<2v+7=D z3hshl$BqSV?nbWD>oHnif-_Bz{BnrOpTL;Etd=QfV!`m;wSSnbm8(%$ej$Ppl|{4J zLiW2c_k~tVn-M8Vqh&$bEH+cjE|)5~WxK^%18c}VZjf9wSMji$-YR#S~40Iinq6S37K4?eoCe?{ioM}?a2<@ z++4Sj3PK`vXAEq%NUmPcn=F=w+kN42!Y(pTI%4+h5sN>Mpl05Q?Wud;^vA(9HB! z4#{XoK#+{{+Se0P>KF1p=-GT0)mVCHQdw-4s#z;lt;@@@YtNN?T)C&4 zEf!P4>+z#f5nU)2%5(WllAuZ`@aiVSOUqwb(fJ{vQ(2rPlOAW6=xm+@jO3ER1f-Ls z+jDsZz0d&Yup`L)W365vI3Xqx0077%k>FiuSP^8XHA-RWK9L@%-yB5_EQ%HyF@gTV zJc&&JY(hOu0FZ97TF#~J!@N+uNL63?Om^?p-8CSq~n3L2M5`JRN<2v%f+Bv~qBX|5^et49f;=(5`6F zuzjd#p`)j;Sj-Z#Kn_pgppxpal^psd91S*;E##LA^<}HIcm)K2k>>_bn5`xW*bZ$u z#2iH|P}OqTTwmG)YJu@RSR(9bWH7FLn;mxl*A^1kHq8Ukzts>L1QLZ>yI@!{Jzl#3 z|92BN3Q~R&)SIhgRi=~^p+hLYOR>&oa$3mf5#;*w^r52Qk!Vzd= zL4V=;EQoGE46)7_0f0vITpBL7+Z#V)LBAkK2JbxRXSikhqwB(b8;m9jZ4!^)DG?&L z<&qFsw~=Tv+g2~osZS3HjHR>GS~WKn3rEmROsT-t4X}VOxThHwV_w8+b)P6zZ0r^+ zRqC#IzMpEfa zI$JC~AO%>Yy??9VtF8M`DC!*!PJ$3EnG|lfd)R3VfW_28Fu|g8A46Ya zkF4ZiH0P;a%;#}C1U8T7gL!Pn;oio15M29=H#YcEGlMOgzn%ba(_m5l??eD}_6@Mt z%JBzuAL{a$1>OKOfcd*38U}%(ROTVMOn!);wkb?DTivQw>{hAI?Y<&t;5`6&-Tdgi zLX)on1a*mWHlHDy>%pMyYnjAxrQ2;Qc8fT3=|U^)0@j-CHmkKlX@&*(8jDoE^Lss4 zSNNOwu!p{8#Eg2qk?V=3;{@RKszSM7Fd2DKzyEqjQbk%~0bd|$JUq_MRB?g2i*>F? zZLH`oL|tF-Er=po4p;j#juQYtHgd`P`(&>6q+VLmv%@#H*Vt#;@gEl#SN=#iRiBw> z$;g>Zs()jxJgj{ei6^toM~UIFUB#pS8EUOk(67T+L<{7FLf~rnf-BK!G|IvpGD~GM z=roRW$!uD-$ZoxEZx|X*I9zeH-JYp|aitl|Hp_C$?e*Y|5A%guA(u+UBH`PB*9#Q$ zp+(JGG@JB`3u>i|PZ~0GLgc}qjJNayyU4_iM%*JEBs+PaZp^7?TJGX#(N?sn@ zZ2=e)Hv6H2ENp1#jXpKUflfv6{0BaT0L>Vf28;)xYoB4*f+EX94AU^-KyZbqv6yV0 zu<~HCradl?XM9<3fFxn0RwQtIQD`M9dM=;Ef*Vl4K?B~daR^;C+- zRJC5ngS{1tgo1as*Io}$D*A(r4twZ(jj`Bi)EYh3K1Kj&OK%Mtozb#X^a4&eqQX)+ zY`TPp%+52p^G~DerZ@MlZ}?cCh=41@H%M8W07wk8_1+K&jZ0?Z?irpTUx2O5k?+e*3u?KLiHr^sl^O{qZ%64xr-VF_$);v=NC}`X zECfQK#*G_g%#SldnM_!Y$K%Mt>bg!B?v++Xv__MW6T0!=%)&98KmaNj@1Bd}zs24NH1!C2$kyf_qfL4FexU4G4hX#&e8ONelv^yqOzmlF}hD zIm`u$SS@gSuG|EnrS%U8Yzj zW2WQLzSPh|JIfGBJ5St&|;_W zz>-SC+P?r~rA93-M!>#NDa{+%Y`TD5nqywFm|8y1wI6QyNdzE+@@{%Gf}fXKcEB+; z7UEcz-_l5=<}d}ory}_MK8*vlk-*dV+J}{k^=$BRA^|`m?fXAN0Ius#RhwgjnjSZabCdf-BgE#?%ED}^tpgRRV2-yFYme=P#$H^oBohOpb<+Ax< z;#FZY*<6*J6LGt)Mq1CIFafQ!>x#gq#u3Z#sM&08a2a&a2n1l!=CCc#T)8i=hLW`( zOZSOnJQfZGg10x35>eQ|ui#?RRn&T|p2kLhi~z6~!7#8MB0kTR2d!jEM7KqGdTKKl z_&h$$T@VYIPXx|4dXIf@R3)&GD=Fwup)V~P_!6l?rPeJj8JEqLrq}23)y7r@L#b47 zGm-EFIZ&+8sprGJ)z{^VB|?6_P$&>%J;2ra3$EJeJ~XRckx`;l1KK7o0oBNjI`vw+)=unO0GmJBFB2@sAJmC|UyJH~Mj$0HPeqS0WhF!)T+ zV-l)JWMWmfP-*4iKvT^Yn^_(JdP{0_40{A133)eda9*f2z0*>_vCN_uYGB1)v{;ibBcfGG?3MY&KuyvjC5oAN(AC zh3hVpzSwHk>XpvA{9%xTzY%jJq}QPKz*k% zX!_zAj9X%vT%pzKbxUTmwd(~U_ucraV5%3i+-xK~9Hayx(wi3at-Bj6Dh@&B1OlG; zv0NcI`i2)AQYp6T6`(WJJCB`m{KoIQ^0@JUnr3CD-L5`xH9Cz}OiBW>cjfMn1%%eN zp;AgY%*P?2&a(2#w!-#bd2hneOo_WhA$8g*?9PP_+bYlwj0)L;Qp{(~lb;6ehn3el zT`>;ydq1C|WXs*s+d!8|KR^t`qoIyA|`l711IptT520E#?5pQUcJyHql1USu>a zTdgI}h1c5}9YX>E&?*V%g@6poVU$Og+L;w9Nu6$qNZ&>;gM$k+$WW+QKnZ{vyi7JG z5P&Wz3MJvhFbn)80#HNa0NBxg)CK@<%A_A#yntc6p@xESGnmkr-|Y|1qUuR79|db! zYqpqpK+N_NH@iqA03<#$5_()_GC5p!(CzX}D5D83WHxjC-lJQ1X$(n`P}v3|K>sr6 zp!M%hxV*e#)41K2Zr9Kts^2bwg~DqCHN(0N^*rH#E%vUEn`82X^=7SzzB5Ju$h_=5 z>>hFLMZ49GqEc?vtCeDzEYiv)B0itPV$AdumEn6N;3?CXX*QcrkN`-43~rUklxkRZ z+o(#xKm`v_`$tzrZ}AQYzzmbkVlf43800UB`>${Djn(3@IRZ(w(drVIC;0Y&03eO~ zS~;Idp~exg|Gi#M|9R`hhkCh?xsTt6f!5^qcm{8w(CQouS|N`*(|~u6LvDB29psCP z3%5TI%eFf6OAilKEV7NeVt-|~a}w9L;b@I*rBjf0z4uMI%JQa}%obX7o!+p81Og#~ zTG6aj!qCV$q$08n7K>?VX$foyfO!pcnTD!QYzB=hLS`8PnObjLwpyq_TJyxGjv*uf z#PewD5J&*p1u}^$6+O1wgovxL%iti>(XE}0mXQtz4TnqKV^csR0PUv=EKtz~+=opV zG4P2O6M!;|17QBcUVzDq7w@bi0%MGLfRI(LCsAGopMz{RSuGVHz7n<6xeZ+T*I4G zWZE$5bfl+788#%<={OlMTR3`GxMeWti?D)50;-l{1fZ3HF1p~&HJWBQj(W0IDdmB1 zl54ZHavqyCMSt1lUk-B5CG(<5YK0lsBz@(=Gqq|Fudk{ zrI<}8!r{=(t>5qUj@W2)hWq=9SWj+M2l6^%wb|`5*TwmT%OAWiH%U|hzfvm3zz0Y+ zyJN*xxCE+MFrF=UmS~+uGllE*Y%x#}D;<_{L1#AcZ~Qm?>4j({?(gqoLqD&w+wIF{ zOG79{}HIqEDTP|R~gN~pJ(saM`hx94%{7)~VsNV`U*&KoH0CL!W#>@qkS zm@6>^Ch_q;j=scYFd_lKorMtSeB%J0_!R=6RfBc(zYPJXo6Q!s*L~sfBjgnW%%A5M z+K9xmY*I%7HyM%*VGxhBn9uaPMPfjw0`9)?y@5!~R`D{Oj zfp~wEWxK<+e0O!}at&F&!NicB1P1-nBSN(~Y;Rh@3s0cGYI|33QRf-+3pJnwz%P#x z0HocZJ+!-ICJglGfksdU9`mVEF`G$c(%C$sQqbqAPYC9`nmmRO6#Z)l?mT_y6!R&t z7o%VS2Cku|;Xxl_bX5!!0IJ1iH5&~&y>8K9G+PQli}&_JG%NJExmoQTGD~B!Xf!5Q zvS74Wmkr$LeYi&ez-?Dj@pdMeOy2}@L;4x=RE%X)@pw2CxViCVMiwP9n++$56cwwO z9uysvTJ6i$=+%$Q@3~gHo}8gFxtc0?Dll1#HizAwySQ+=eWCkYokTHq>ZLNi5uSJ~ zns$yRgmIc?ug~j)ERycfsR5Y%9cVJaV@dG=(XOo6!E-ZPl?s{{TxYUS0)Uh$PmgVi zMj-*yx1`K^JTCVYxVvd%IGq3>jmLQg-P~$7V4VXTqnGKS(}oao*+8MK)!}RW4I%-+ zorMs%vm6EZ#4iy5xLN*50x&uC6a{y2XY=*Uxzptb;yX(Yvl`#S`fC!ANQ7k4<@m;xGQpft+9kh4(-p2XCXO(t!25Gx{*NaaW(7R3dLyI4H^z-7U- zTbc^&NaV(Uaem@-emZk`gBi6-A;Z6#z4N-hGvDp)@4S?PaU>rIaL(=n_~3*hc?)CQ0~zI{HqZP92C;g6rrV4HRPtzShb5D4cMbqfp6 zYylz`w$R(N;fIree&FAz6aHU`=bqlSGVlHT_59L}O4Bs(XmEGKX24ZnI-MDIp}=Nc zzSe}Aep3M}{);o2)R4?jN++}VDP=(nCX3lp_W_k;)Jq4x=X5zQvaoTV&E;}lx+Vx5 zNHTC62#6MeT8W<+0allRh^^7?ZI3C>$!FZ`s*R(IzGb6+Z-K! z{5oPRnhc%AvA(&jqX8yViJW{qIX!a{m7lc7<@|9z`+5^BPH?;X``eovbKlO-e*nFB z0G`_T{{3&9NmIQ92*=*e+E6Y!4*9#?U5k)eYYy_7NwCLpIeXvh4&2OFtB1F9vs46; zDKr{UG;(u|#tbFyBKJv-L?Y~!sPkQ&pPu~4R!hF)vu~HULudvT-zHL6n7ePB0rKaf z$zm^Fe*1XzJ1kC5EFoI`{`KVe5N)i179Twy5e0t%p-?2I(CIYOdi0K94((51e6Fl3 zbYngJ8+F2mP;U)6w^QBz(0&^CUS5PkW8)U zH3nD#bQabX$B!|2!;_ zX>$&X9ud$>Ivqwicy{{b%;ia{78jI*D*Y3|n4cwQDtK{RAWP=L^HB|7~lzavjU|B^4H%&z&n3ihbGdq5b*8hYMKrQYezhkW?aud$8_P zKuzNDIKA5GUFWyc!-L;H{Bdw}{PlbP5%l}RRZ^K~Lq(QUrCF-`UFT;X4^KXS{ygLl z>G)1hj{ks-IS)U6{6whRF|6JUVLV?LA{PzEqCjKE>&*Wg%;A2IV?g^tJf9i-b3h#v zfBBE#83Itb|Kk8is$WNBtY!g~%5XdA0GS9pef)BX4EobM8K6<)#~6T0kbzY~#v}=$ z*=#opyfCB?KQoM~%k{$*thg?nuFEMjv>prw0#8s&8?DFJ16onDk3N&;Ng)Q@K68HQ zx{Dxksdz|5aBL4@ONDSbTuv+sg933p!*VBp%a-8@5T+4VgO}&!;m_sIMN( z)8`pTy*8*NdXIM7y@(@oTp?drETq%3LKLiOsZnRM=!0>-J^ct9m>zsQ8ICKS05*p& zp1qG;yT6ae6w|d@UNi1F{BQs5KmIdRXG4n{_Qq)GaTJ|StBKHPbCZj=#$xe%zDA=Q z>s2`o%%OgdBPa#oB$fUQ`-UaifN1@{3jqM*clPmgOxjNWpZ@{z`#=&^p{7sdf1&69 z7k=`mfc-!M-+C04bFY+{M&4b_NX$Xbmtd2st zIy&#Zv0E1=^f^Hq<*!F)&VhfPGE1W&?e-x0x9}A(=+Eb9#d9`NtyC~I9zfOUJkHZ- zgkNmN+~?oEUG@4*JcC&poe4YJ^s;Yhgc6WgJI?)8Kv^Iy1=s-lcdWcVjs~Dsu$s#d zneL;Ih$WM9x$Ncbn4d%81kAvYc=m6AOaUBmSS0?N@hkxVViNxMIT$hPh<&1*H5~#m z0){Lwu1aLR2M1@AQ3CMJXt9{RZovPC2?hae7i37iUe*&42;2qII-OoONp!!*dW{fq zjk1+F9#sDvT%y9CFO{nZoluQR@(O;4fIYBoIc#<`8X>6r&$8SJAXAWsS~&>xqGD}~ z9ir&oeS*F8!3F>lHtggS?Cb0fxLbA| z`V)ZzpmoE#SUIs_&}cLULY`a0A05C*CCySO#9T9her0{#VNyP)MGx@UY4=>4dbPMu zp0IYDYjiN~kd^|#+M~ne^#>@I%K4U1BGYNm8yPj&B+{vjNTzskf4S$;X-;d!{3`&r z^m7vV7vd)b-~~_`VIl23A>|h@Rls`Q1c+&15Xl?#{7^-X8Oq*!o5d6a{NFoO#~|_L zu+|+}D;%-h5l{>OFn|OwNd&k*`8bLn_*#G5*6OFU_+Dt=d?y&Q zPes39T>Cpa8Q&@@=RdqZQ|(4j`uM*fzV=rb8LOxx`i!0*>gS?@ zO%g23_@&1)O+I2SZDW1=VBa3wRGJP&fIF>DKkC2Jp~ZAZY~R`%=^+BZ+1TG*5wj`;Q-2TlQ13Vz4d9%jexahuuw+p7T@OTe#;c8Y zJA3bcd;c4_&CNGc+%P!mjcNrEO4Z9Yo!+|jc5Tgu5WSj&rh&}C%OOBz8r)6?Nyfsr zDWh#;8%n7=d%wNq4$2^c@yX@$1))edSwr|Q!A0VqH39r-0zfq`S(mPz=ih$}3|;>w zma#_g+v8RF5xzt!70)0dlU_fixz^&_t#@D-06w?B|J!d{o2yf{%;Q}k5G*X{u+|oR zs4y*md6?B2QHt_TS1IOlb+KH#IABB^nUKeBR4S-C{Mg1C*^HpveW)FNaHE+3Jv_s5 zC!m~)M($L4o8$G;G*h+c_0~2RIJDc`{`U5}K{rOgPEz5T+VpyFfA@`5(+=T-hbCP8f5MCyejx?Jc zBopv?E<%=#9U!BErLnv7dZ>Ryu~;h6d3^439ozmvJd}aV)vsjx&*1q`;6XeY%!3pc zVYCu{0*7VUp-aNhyeWPywpK@(kR&+)YTN1x5Y0K6#1IKY@6UG@2!$#fRp;@co1Gw( zQtkHT6`R>)&}%hHxkMmf)92g0I~sn%I(eSKSbVJ2sl-S?zJ6~>r=v!OI~QuLu<}qM zl49_AOvPiXOP$4=>BwLdvm)m6`Qgo$(CR?r2xkh>P?TI+?;!&(ay})?~(Du-WWZn4P<*R?8(K9)mtx=ri-?JklXA3S`=cUED^Lz=-Qy!cbG(DcbI=($uT&{S#dv(+#N;#RbUL+(a5ZW* zH+lB~2FeqVNumlKV+G8uO;v~lqv@{5K%b?w0goSDc@%IgFN5R!JeN(ANai?FH7w8~ zzrLJPODoZE6x@PZTLuIB7!EZNu@f zn3pKyAHD+qKh-ja!~iRJfpL^$&>MF5r8%c-LfTLh>Z3ky!(z4=Cs@yDj+OO|jd!|- zu+!-Zx8&Pfn{QWF2znzC!QgEmvY;FD=)RrgB*LB^!`S z9hSvXD25ixN01sCu$sf$`1Rl}AgNevC|iF5%Y7DUzvt>l;+0*2OkNgN$E&xS+uKS~ zIa_G)*0(lc1M$Aot9(wg4pbt8T zE}%l&wj6nQ0P;3@hJv~)1~|MxKV~%ARve_SC&wQ?eEfKNXSNv)i_baVBLScLd)~OU zyT8Bd=$@ZnH5(l&74317ccQ{o3aqHoE&4J#%z6~tyZbw9CJp624U1EBUFTm;FgM8| z&}fc5LuTztwN!g#vzY(8#O}pIr5U;QCnl1=7JrTaNZu$~NGFlYPgM;4f>A$qXfQ1bE?rL7#l-y&*0SI0_Iddxv&}pj zjpF#*p`j@$qyHUc0k$EZ^|Kq+4H#dIGx|VGB-DOpqb&f z{q4>59G)eC!UQ+M0@}jorx<0q2=ein9`iTfzkN9k1Ok&ZL#4@Pb7-AkPLJRU|KsTN z>|E%ut(dV!bPzSqM>P8Q@aW^jp!r4?>&t*~!kyM6<&XBlF?fyRV0X7Z? zGChmkkNhKoqFGol&~JRs^OMh?kG{Cvfr3VQlE5lB&KY)b^C%+yaAHtC0!$Z77&j8IvvpG*Ex_ABp zPu{-;%z48Xuf4ZU4qN)0@TUlXYSFH|J-kuc7m22)9`Hbo-O>J=lOWLR7Z+ec&v2SQ z5hb7J$`_EEOlI?N936jAXEd`suJg0g16W0D_5!) zv5mf@kqJ$$a;1hei@AJ(%NI)sfl+K+j*!XK8hIpi?Y{)VdMA7nj@=8SQqi-f_vDw; zlY;{wxg4IHeo4o$01aFSV(K?`_ffaU?(4CT&e>VoY%+0qe0uKel|BJa8G8rx0iMB#CHzqBm>JpK#;APH4!|JmW0UnLeyxDIt5jSI08 zZ0FN3V=x$~F4&WK>i$QNl;7v|-Rn#ilX*x)RcV$MWw$<;^E<3L`BaOXIWPQoWUXcz zmnq+c0)dBL;QtW5uYkUs9D}C~h1?$xkI^iN9#g&c`TSY6MyY|CEd`Amo-(lGEH?)C zAdrzJ?@)k>>*R2v1f@{xbh?1wdGXB|EmkY3t8Y%H??z(K^}1052rN5Tht1tp<+yyp z(dqOilm69`TEob0YZ2_lTMJOGS$Gns@mMfR3b4< zAxtHsL5#`8li57NAEXIPM*HscfBrxIkN^3}m+p$_*)te$_!+gP&Y9kA?fh&1Hz|oSk``rr? zn*m_$fCTJu45Yi=O+W$Y{y+$plT6-+Vg;3U$!J3jYO~VSb%${QJ|2}T6o|_CRH_A( zZ2(fGTx)Vz%%3g_j%5c8aM;~>t?JV*{`$tw&Mtb_Kmyp^+IX`%WWlT6eguNU?O=)hnB7{r8PT0r5m_*2wk=JnqGTf0snL9&H%8DaB;|EL8J;7_>$RZ301#F6S1 z;QaVFOey+42@g-nP$(3cl;uObzbu73J1Y`(AKRUQ*-T?zwutZCuB$=3%@e{GiN&y- zHnb2=c)}K)Md#6x4mgRq0beo-kHu&2#tQ za*1#)^;j;y}kv$did33}j zHv7u5bqU;#N+A~V=yP3y<&k8z+w5kOVR2Ebl8JdtS{c3H(UIso=TNUjjYOr+Fm>~= zZQ!EOWTv~_E{_|q_BNHpm{-nrD05t)NUAj~nQfN2Y8^ZtXUS-kWp8~?=o16bIa)RK zJ{qfQb!yJkUm`8_s*fvVgX-fv`-)@PYLz~=nr$*yFXwaSNrOI$G$ohY4P5zr*P+zJ zc-k>n9$ll;@-v|bmZ02C-vgw}crc}c(;!X=WHuFGJ;tK?lhjeEUy2iW!X zb#TO_7WcXRpPE{=+F@U{?>qKa)*KFkG)JPMz3E4=M|;8W&oZ~bN73l_VbJMJHc~F; ziLygD7EM;9VwnJbT5Nl@@#fvOcJ8S|Rqt-SedDkVbOXxD>c-~V?Y-SM>wu$fZN7cw zc!@L2?{PZMZ{;xRa&O00`ufA^@}kQ*bm#NBWsbG2-*y0R-rwDR^JYN<AasZT|iH zoj10;_cju5AUraGP;WsDL-*II&H@=g zMSFPcG;lxqywPGbAcBVVOS9Bxhl%=a)+W$c)lp zH0l?%TD3yTpB>4~$m=)dT8}KY+6@d z0STZKz7B?BHI|7xH`{5$h9jdRG0c|%4Sa0YtNCJAMJuMReg4Z{x35lf7I}IQX}JQW zR;ODu8da%uB_FRLf<@gT&~XX9AG9j*Q#caIs+1~WF&2&CqeT2AiXB!TUuHBal}ENw z$e@wY3f@YaW5w}!ae3+X-$Y}nN^3^TY?iPl`UrDn`ISW{CE*RmCjoGAZoUeB0zZa> ztL0}ycza{OO;&AIvs%JulNw!_>77{E>gS}4NB~AXSD-Z*mqhos*EfB^YO(}S4`XMr z%DU0?AkT%PGuX@$O4R3Zc|F}(lXb}m#{Qp_0LV<(VJ;bp4rQ8>7xg-IHHg*##W0Zo z)LReLbTr`id8RnBCl-C50Fl|guG7tBbK|b6;RV@j0SYj$qI4d*cvd(xit+>c#4185z^!Y>d2xy@>|nDs!QRx1<&J_oJ!J3t=Rwk6pd zcCO2P@q{cgXJ^Rm-e|$=iwoWatQpe>Y*qae0wCZp;S$oq`gTx(W)LJ>rd9xh;%);( zs!5heWs-U_ktiZM`>U0e)P?KPdlQUh>yIq;W3ybwe^3qt6$kI)^2!I^%$K`5Ugx0( zw9uatfX7s$R>-6ay!+1qpV#B*cVMeEo;r+@c@d_izQ*b^IR%)wSu zI1nJ91w(PUN+z!*?o+t!M!l%j$nak>udLcu1UFaS>qs*V z8lVud1wyUfrptu-9~~wD%vWnN&7#?4A^EO-*ZsXSMgUAoxpw9%lts_f?|j8?=O9*<>btjl(L_u}&0>A4LhOKtMJf)4ZrydJMsJ|weR900n6zF;07H@6=xna zZYCBE0+kZ>YePgvi;443696)7%iBt&GJT>2pja|32@ zcz;16ofz8fHinSMq&~WGjauD_g(GRE(QGLfZqI*Ql z;qvf0YExMvB{`>V2kFv1tQz1C#uD43N1l?bL02>uu`5} ztwzng_YU=u11KcpT(L+Xs??fuvviKsU^3g4G%of_*5wtu;tJMw^#v29*32wNTCL=h z4;oz+(2h{A&wNd$QYl;DVY3L|W^*j_E}i!zsgyK>)cOmO(9fGCpGksM;r%)oL!MTz z-ae9DMbLm7lba(4RYte(TUx2v+|0n@yOC%B*BUdUAYDUkSEhZ!V z+V8*V?}y0*Kxr}W=(NRbZaB((Nd}v%i#qU%gre_49Xu^&kJWLMfc>vRW*WK7p#EGB zV_P#v1{6Rf>^^oLps5>`Fvqs;6maaX02dvy&tv#00no#)7t4t&_sv}}dY{hETNo5d zr#tlA8Y=>La`Mt0M6w;E4Vd#26|bDuyZ-0f2obUI{SOI1ER<}b1c0ku+SY2-T7<_& zbESbEGg!P}C!%&V**aP~9|uXM)wZJHNRqBwh=f8-AJ!$kJ0p@xg_Zj_(8>Xr20bYh z%ekDWx1dbsibT9}B5_|I8;J=70B}kJIYC;IQXeWq;>Tkl1_%J`&lb47_M`L}{FF+q z=I0}UW0d6e5e{`pd>%_q@658q3f-c?YT=%;wCFEMt$^0vyDl!AsaCs|e4M4TwC!9b zQ=+3{TP}nv`5c{YL6N%)1oM2G9q@nGM5n=Q-kuNUELr8=)rF;YIa*Vngh4-)|Xf`QY>1+V?Cp@u5M1Yl;_=2%Hw070f_ zrV;D(`zo+P`XV!brqyhAD%jnkWB*SR0E8(JaK-IPy`Ark%beL_wdj&QkADz=KA8X* z&1SP=K9|kI))0N*J}*scIDCz~x&bYHTJ{jzfLH_shYiLnm(7kvyp3$v(EqvxMJk(%| z?(i*?vh!`AJ_7cPv-n@Q%Xvu(Eb>GiI`_cWq>qmXZ>E#)!mbH@qa_jAH!zE`52{Mr z2?s;THa819Y-aHI8?r(TJ8XuqtPGN32E3Wd;O!b_#|%g=oDe98r}@rML<>Z=^hgE>dAL<|%3poiEiaE*gzYcUq%{7P{i1 z#cE~y+*g+XDnQl081>9EIU*T3nYhnp6DaL?qC&8wdV|Tpy}rJ_sV%*B$Qm)H%N?J2 zB0bJ?kt$A^i2c0vYp=jdb~G}W1c`1*$G8pMV7pqdxKs`P_!=sdtHn$#aP4)w2lG2j z0Jz%6WF!)Ry_SegPKF79&}v_nxGyg+(IW_{ZfOxVXzg7V?^@OD=xvc?7OUNArMQ8BJlO3M2>>5(ZUZt~$!D`fr}&)M@&=4F1SwW+JVU zMS3EaMt)}URR+^(Sa#SK{g*#}TzPH+kz}FTM#zhlX0=N6J=Ei<;!$O>*i2d}0AJtZ z`{NhxvQ|PR`v{aPQTt^m!{XhXqB?ZnXi0=v`(_!gq8^l7u!cg(r{sMQey2;J(!h2h z!3o5=YXCg7k^%4@>Lmf7suy~1j^61+0+39`(j<|XnTW=S6AyqZx$MkffaP+j>LHm( zjR%9YX=LPqw6$iq zfJUB}^<0$hE9G1=9Ps@WckFPSXlJHIba63<}2sn8qf zZXiaY2LM<%5jx%q8lCR@_CHDhNJuUU>F_|1Yo3GdGR*v`PmRQSfBJkq9 zqcwcf;b6(K{{~Qg`~(UxyQ+8lhFmB@7*!OoJ~(oFa8sXD3>fvDe#Bp5$oo9*gM)(r z+w_vy`#yyB6^D)b^_&AeOuyzVridj9O(o>>IImLGW;Jo~&FQ&`0U<{r!9UT5d}R;o zFS5HlIU8{jL~uc44HLlQJR~AW}`EM%slq%o%ex7Eo?9bQ494_u2|;s zBw|9w4PK@xCiKLf zr_pFAW2tnzVW?aaebGATu&Jft9X7FVK|7Y&WH4a;ce(tY1i(~lRlH<4 z(sK(>lmM(Niw3>HaDQ_%+VTyJS`dPd57rJ{DBz15`Esp4RFYTPI(i$$TC-Bf-bcfC zH$IQ2EC*~nQwfvwCWGMq#_#W0@|urOD2m~c5KP$P8$h>cB%J{%9hwANDd<*Za@cG( zC3Fv7_r40&ZNHy4c#?b)0r%FwXPUvJ*CuanZt#bz=My1p(>IFw;;^7P;zAU>S_JnX zEisH%lU^fdVa#$^0$?bVGF9<`tzI-2(&z*0wT8P3_kgj&bphKKW0a*1DC;_0& z^?1uq2!LVDuG6uex*3(t_Ex7;m!i15+^W}6S9LlR4A9jE6O2KS31)^=E1nSgw)$u& zwKxHg+w2xtCJFDbsn${n1b_lVZOvxG5-gIg(+O1^>`nSJ;W>R+_l!$MQ{LzG4>2Xt`1oLSJ6tYX#DX1$N~mnX(^)K{`aR0eD=8ps$D!Nc+I!t@!zP^fAr6qsx$)cU zK5^+l<9%P90za|DXEK>=E;k!26ibC(7>*USF=?KNx(2bIw^o;hONKBBe8bX`BzWz= zCN4TMX2T|r2wNjAM9l`Yyil@MyUnts;iWM@!Y~1N6badU(Y$0~5lN+>JVVhbgE^0T zz%0>q(I)^BpsfJV2d1~bAFy^rt=4TetHn8)K?xsVzT)?Kt~^&mOM4OlSTLI{UC$T3 zkde#ehqiSCsKEkJwNk0{#To6g-LjZ=U%EX+Sph-?a7Db704%RN1l6$9b(KM$NRLd;Q<-?jKOq2|1wHgN-UbJnEmJNp z$0>kQ;>QrW6`R9mHIh0HGjyen$CxKSV!;YOxyvT)k*R(&^YrwzWOJ;r?=P-y2m7Z- z07%QrcGHqxtCI0q^Cd!LWwDG?LljN$af|?{RsgJKLATLB2dm9t3<$uy!@jy~1>%87 zfOhf_sz}(IQSoX#!lHP`S8dvRP@h8n$ga z`RHvto=E1(9a7~U?(<{^43>@!t2T_T6b#@e;6@Hb;u)rr$EMD-aSEWeZGb)b5GO8N zVn1(I6}}9n2SiI@G#Y5vUjH>wm08$Cx8YM{SidRx&@xYCKJ+Sf+d^o0p+#mLt9FZ? ze0Pg&+F=6lK%1wwDI&R$+D@h6T|JVsp-7OR0uK`a-qLSCSYt>3(eA+TzgoUoEfuoK z`)odYe|LKg#4-0s`P~TwfNBPU@x9+G5NdMy>`;5jHigAzNy*h>1!`0fG>dh4nGUY& z9^K%G5fFfq&+Fq0h2m^B7abn=KEztbD=1rzXsBQahf+$WL7_+mgNZ9w3dsHs!Cel7)sNAu#BBkBl;9aDK2>?n5-84ofi-ikx*O84H|=wK&;i-A!`|#7N%OG;%B1a=+F(*6_8H% zBQ612dB7*bcVMb3b;v-%L%xWg5P(^^LMH2E3)R8C>pVi$1T9qAg9UJ0ljrjC!WRf7 za!o`kX*J44bi?(okJSpm!5wV#6yW~IGKplc72VFup?!rDfaWW^mE3N%=_)k`#_al7wGTrBiUx7l!gqnR(Z0H}Qgk_;9#0Uu1FB#G`vgF@^2*_mc`saU-(3K1C$SoT z^J#i`&Hw@$nh;6w5}{DptQHwx!_S6ELhf9x#}e8PvolW}m4qt>DyD7;T(gvK<`eKn z*e09BVlf!>I;~1B#=rCv0#L7EZjxvsS0l|WBVdd%>w;XXTZTy!1Og!8v7q6oicM>~ z&0y1LY&I1aPdG-PAl-RJ*4aP4<~UAX&b>#q{M7 z0?^GgOX+Cv?$+zO1M8D)R=|0u4Tf@z0BF@%O)j&|4j#<3!*@mhx5Wg*#fU_dJ z{mf4YKwAo=6G5ffoqg!7l~x=L7_MYz(0J3#?Wku||Jim1N?;v%M4&Iy^o`d}WihMQ@xDDio zQ$S~cI&51uYZjzWE#Be$ii4PPm90oPqK+PZ&b#m!=j>P24xP??2vmB4r=tX32(G0Blo*c?s zLmj4k+zqEOTg)V{d!*SMAOKn+*a;H}fDndeK|>-W0POp^DRJe$j^$Ig7|k9b0DuA{ z(tupuU84pJ^;^_OIx~=##|Z%UA0_~UeAH5~3bRuw^9WJTpANbR?5lJ(lL30WKqL_k zS9pm;0+E~=9 zU(j#A+j!%!^;26z&hyhRCr3w4pYO-f(Z|!1uit-w?Ii|ZdGl^tjbvl7BEMM~W7a+3;DJ z_#W@T9zPTcmDo21q0WMp^MGcp1J%fP!?&-!-F~;Vy*jzA7lqt7I5@84v1iQRXA9*y zCS!hZNi7y2fMXdy+k?;bpOQ_|Lwaz|y~`=bpQcLLw0YIyAe#zfB=F z-~{Z8%V8tsaFAi26r8G7y0qkL$z%#*-M!V8>#FlW z5w|CFv@_pID%O!B%$g-4Nt(iAD=I2K_L#bYGF}F zh=@=rRPt^lGE%txSJ1k4I!}*3f@v8GZ%4rpDs`rn*W3HM`+GY(MsUWlKN5*Bie@1< zCVu~&7#=-X#&c>}2;ad_QB}aW#hFYhSIefd8KG!|03fk&C=wBAG%6?psDYf$3rB(x zqGu5UxQwKdO#(?dmrZ9YfRId%pW(1Mj97@!XSauZKAU9-rBY&BFAj@IS2h~W-pXCy zpU~Kuxxe3M=FMiKiS^o6UDSu({37Tivic0a1#hVH{ap za;!Jokfx9ZNfnYjOv@Xe+3t0!43#=raSVo5Te$6>dd50A?u z_e=w1Sn&;&B@DNbAgQLUak5{OWBiHE7w#JXI zCr1Y#4iAn_&s}WOqE=2Xmy5*)moE^HZtaJ5N40MfDhzlzF`>?wZ8u!q&Ax!b5lciu!2Qz*)BF|dqS5gSAlXzZ0V~gH2|P{)WN)NcEca>Y#NJQL zfiPdMR?6Tf1Vh9}D&%tkH$^kcBA1uvU%v(wn_JskU{1HUHkU?KfdZ_54lAHjXfSnu zd`+ajLkiKy9sY@4xxi&66gC#|1GHRVADvp#iz(qxacFO4G8ps*Y@x^D@`w_XMyZm~ zlc{vJ`_%0`AXFL)6T=m+mQC8B;;evkQf4vHm~1jHsR{HmSBjDsQ!vJ3s&p|B$yoUM&Wr_$u=CWp@p`kW=I8updEUwh*^iaaWpwemO zl3a2^Zs9c4i@9u0C;=J^egZ(ER>{Q8Oab$61b%!w`+RzGOeNIqycCeB9N%9W{uXk2|H}g4MfX_W}|_ycw!xgOkrs@)>bVBqP%^71~gmPY2fhW z^m`l%CI^>2*{xK}b|o6q*8Y}hK{}JmRxkn}P-wKeWXSJ1_r#OIZ&%(>R3wv5Y`ZDI z9(WB(`GL>(x8Cze<)K-;b%pM;2!{z&|8z1D>nPL<`jI8oPDi76H1o=vjcr&fYt2_qW$qej@qzK!Wy7 z{PjPAf#e64BFsf7=O;U>%V?*U!HzPUE#{KfNUhoJ7x9r>tX7LOuO!%$TB}wy zBjHGP4&1X8X|%dsd|Ff%>#{|88qQPplMs9iHGWZ($d@`9x zHCKD22o&1ICG)a%J~YSAhfp82LcrxRA7d`h)s;6fL*~xc%WfE+gZ=_op!GTV?SC>W5S!If0vTmY|6rqd#bVZld|n@5D-@wnpaxrG z#Dm|MW3gGPX0=?kT9*|!?o0Q$wS@`8fa5-oAC(Q}g(8uBVp$NR+e(E(k%_j}@;sGB z(a?}t8i&K-iq%Ud^NPiM?S@s>;QPJJ_bdTua99kMvWq;;(SYVmniniutTu}!@9}zi z(Qd6&zLd{pvgu5=!dMV6=4p?eVG5wP*;Xv3rA0j&daG6HkWm6KfwqFTo-G$RN&p_! zT8+96-*0djbcVWvkmtGd`FWODZ(d%uEJv?g9!~@5GT3aIL^Q{fsI>-@Y2nsMRZGj! zFzUE$^LRX#h)!mURa(8#WFcQw6_Ro!6fN}=R8jXW=1ow9 z9zS+^$z-)Wc-uZib54H$~Xe>Kxn#vhk{1tr9(Jd5a{$ec{*?#EFc{^bB>{Y zoShc{)lMr&_ZDQcJJV@aNph`Nrq${UCQ2CQta$K|d7c31tTx+{Gf1bS1b}L_u2||PN5m`s7A|z zK#s)al2hq)dLYBX0?=G8r-AP9`W%^rXgE|BU#L{;7A+==#d>{p>G5EfnY{T%2ta3M zoASR+yl58e5fXaO71XJQmIO- z)0D3+JQAg{0kkFbvNJ3|HDw|mcyrOPkZ@meRPtIZ8q4<+R3q@Vbt|vIK|V?h%_txb z)l#|BsVDqbV8_Fcftxg0j0+jR*Vp@`K-~<5@}&ti577sL=>nbtn&pwB(jjN#jJ#pC3ybpB;9q3jAc|ux(MYceu zT+kWxCJWQ!zP!4^cP=`}KO+E0ah6PaoL!=`c@nUYV7QvqoOF9GuP`wJN4pSDq%-+K zq28it$q&t1FT?~uz4FRox7YnwzUv!w-e)8|?la{cP@r!;Q!a2i0YHirgw#b0Jg!)x z)aW29*9F`zmkZ^}tp}vjDixbgGtCSvI-8d(BH5Yy6gsx$Su)aU0u4YVm@NP<8c@kP zox=S+nq9fCrQx@&);eBkvxB%6h;FJ`&ZXkP(5?U4>%pIgr#CK%5;xb^Jq3#V6ar6? z?A7ie0(~Gz01#N?g!KlEyvMTpbwH-@!_AN*Jw3G^q>CbjRtxTBV5cu6@r0G8*_!iQdfddFgn?(anwF?_DJol`kSgfIWkE2Gq^i}2JcwE}@1q*Swr{QW z)N0jQyaf$dFM0)%@OdviSG`h==pg@;03dnjG;0VA0*OMcT`(+}9)k2zaQZ z?7V4U$f8s&PsoX_0HLw7eGpT|v=C4#{iC4&u14ry0Q=$p4bK${;WGoetu zkqM!<0~1tn0-&?QK_U_WuTS_SC9Y^Tj8v9EN`rF4In@@|i>wu-3Z(%ppt-n9g$qKxqZS z8BhyV7y%%OReIm$)#aqTyU7F~@bv;C%@B?I@E8H0J9KI_oE$lKw;$Yr!JnHOes%=$b{lz3~kvm}uc>Hs4={(;P5v$QiJ zwFu0tdxCLO4Fmu_B86>)amaw|n$P40p9Mp-uhk;WTBX0$0F*HAquSrTwPLGQEM-!; zaw!|Xxw*cAML@B)`aA4r1fZMkHmkKlX@(^f3t6NxAa9VLmmN_WWf zOc}YD%k~YHGONvQPCGBpFWml6Bocuocz%HZOf47q905R554CEgkS>7BcI(L-zt8Qt z!n)i5RMO>q@;)34++6#-rKbkEC1C|!fX8G5@Nw{F@EUySngMuzFqqHA;Oz{$*MOCq z&6h_A0MM7p8Q4LGkdnyP=@;4ef!jf8)+fl`@z=u`CIC#Weiu$W5MY|0!Oi#bpwhKk zDVr_vg~J3uAmP#lGY743X*>0l-X!$PfX*@lpZ+>j5M>T=E35>2Jemti>?qW+|rq{h4luLZKMm?Cx%^ z+E5?eyhth*lPH7GR^uEQ`UOW_<7n)U?yWW!4o!05BQWo}eD)D-{fOb?*Sc6B9&rRJ zl?z&(Zsg)pqDQ81cKZ4F=-|WA!NKo`hexNMzw|fui}7%Ic6NLWebTVi?$I&qOzlMH zg1L!0t{mvcP&B=PNTMR-2?yP9**u{HiHCp)PRP{z+u=Vu{Rr;$-~c@N5!y_fVAZZ) zAHyKKgQHJiW2QcRgy_G0_vs`0s7J@2qR2J)I)D?tG1R^0+X10Qt=@zWrV_nXH94i>FGuzVYz}_-;lm#Xe;gg3oc9GtG*Vy7r%{ln z2}2L?5chujp2$`@@J?Yx9W*DPb`Kx_B#+7)*>CUw^QHnw?dV+Vd%ecXvgQL%W zo>qpwC5Bb}LK)Q7jUg35M56I9{zxPkjw6w1C^Cs7!y153ncN9nWjMa%vIw}&SS%1w z5_OZ41bTQ%`OaY9KHpcY$ojv7PUI_)BOo^fROB0K2*<$O-`}Cl5jkC}-FbweTlgW0 zna6IY*XLiNlk;D~oBlisH)kLI^S}Sc|MUNS9&NIey_iQVQXj?M-mY~@0E-dK_}-3# zFx(lmy57M#df-D}AN6grGgh!bC4=DR838!9f@8EkIca1#UW9fT5HCn@4-USBjq5hM zep2ud0n7XSxNhZ3x#faRyHF1X2+G0J?M+XmqG|pl0qX;ZOAz?czlH6I0c3w8dIW%C zc(=2=^DjtKf7{;PdX0_=PazQtTlsRSjJVwH%S5%sKtsRTB8f;G{ER&=jxSLWOzVMP zBO;coJs_pz1Y!;d9v~y4OeUGdF9DHGX7WJ17z+dELOap(`U--Wps2pC!H<3k7{3H- zubv8B`+T>dG!XvL%r~tXd1%#V94t1UNP*_46cWr7!VjU++j*c+8EjN3pe#BpM(uc` zN2z`oe;tXSEW(DJfPCYPzGu;7CltT!ZEw9>ofr*0Cwc$d{@%L{1AYupD?;F83?=SF zY05Mb(#pk3T6P|qO@um2=*CJ#rF#4Tf-KPJ^;)77g(823&imov@#nLv+Xa(W`D{N< zfM;raP6Le02ms)RLr}cgUH=Kqc@ZAlUmzuez`WD{9$xrFe;M#SC4957xwE^!yR*N) zyZhVT&gO=Vh6v62MI|GdOk^Gq!}i{ep3kVGU3GxwE!V7D*t{#o!| zZ@$|F^m~8*-NrgDqT@dT1c3b=aF&=Y)dPhhb?k0!ZP~`X~P&Xs@5lywK?PV*5-!8Hi5NLDBq4QW?wIxv;)!y@40u=Wa7aysFd@?HiygW zZ(g1WK3`v#Q~>_6_kJIy#cbN~b0QNu4}IfqB~z+5X-vjY7SuSvV(Vq4!qkeFPym%) zM-AKrC;9wLj@KKS(KMES54CC^f5wdeC!~je2PgrM+g8@zyfIeKpjr6v_?ou4vHoUd zcYDKOCHZaxVU$Hn_h!DGe)j<4CJ;$Qyi6vY!B-eWCYza17UWsbfyyAtq*Ft6fXkUw zHaAulq!PRhh=3gTj2&>s=5W|-uEUhgVYln?lLJUzp*xNU0 z!TJi2NvlSx&^b2N97b&`98V6Umvk~34$^I}clY1_>$km4g~#1~KsONnK7uTvT#3VRflB>o=l>Y`QY&2`1o4z2A&Yj-5kDWDS@F39TFHF_%DGo>gT&^Fkd&lasouJcVkgdQ5 z7(*C%voK85zLcmqoeB7SuB?3xh)Fv;`#Z28fWyx4+}uV8mgLCY^_72a4QS%Kd)C^; zk9(*VQ~T8`VxK<`V#zSz?_hu?*uYnDAMU<0+A^+I*fm7 z6;i?ae5sCUeo`Quz1dvlHuFWY;q@By;Q_SUEy4bxSY^a%k6b0>Fdl0CQi3S5ABW;% zp1xvK%>`b2u$NzcCIifn7Zx(Xz$5`cW47CD_m|fbNW(9KV07XBM2L9~{}uHB^hT@2 zmT`VN{dfe;-yc7oeseyp?eDJ6yT5!pcj1l9Vh2U!@-yIgXc4ah;MBxn%Vv_9+IU%z z=K#sY1C$)_V+??hq{nptBoqiB3%W(!6ssA@W?QvcTpp*(>5AIcR_&uZ=nivq6Y(8? z>m~4eoX&4I3!9sJ`(T3qwZFCb`sw`Q5)E7JA?9px;CO*MnYV?0^z{rvQmV9uS>KP7-w#eM{9K(zDMt#JH6P&cqka`CC5m20Z9Q#=$>Ir7@V+lq{NH*=l7N;#WtsunEvH#W6Ys#^n!h-yAb)T+R00QXN=9}*=j z_DaT+)~nSSa3oibb!Qre@|{(17YGE#Gf@OaquH7SQ#-IK{1SS8gdWiOAe5OI!xO=1 zJOMyl{KxA7fSK0l3y%~ELt?meK;@3uEDD0je3=0WMc4J%%;yp^m9gCDBC`x;!5i(mlD;?ZcAjFzhF>MmJ+n&qi7W)^Cu?IaTsk==sOWr=bJ zp>bpjCSxfONeseC^8s}8mwTK>wt>oH@lSZ9IN5>9G}SN9dh{g{#1*;?jDLn?ShAQ} z*B+mHN+If>V5mSA3T1XuxJw_Xvvig^&z_ac2PRiB`N!)4kdPTzs&)R(cjfwl?cK-N z_e;0`2B~L19-n;qik+Of@%elqlyZwv4In*|`-kcP<7GjngC~9fnMrt(>OJ)%+EEhNi+OFXG(g>VEL<^H?K4(}_4WuRd}+)>x!DDDHdp$Q7WqRyZf-QC>u zTafh;`}yj4>O!wyX=I&+VNlalA3(DSSSUxRe7m+T{%$}9wuSgbSiuM2$Tc-_@~JEpoaS4b~HyY zfuF|@dw_0S2Fx0nXgH6fwRXO{!A^CdYNBVHz=qJyEhoQ(-Pg)1!d_UJ2M_yIKZ_GBaiY6&&6^-F2uc&1$7Qx_IvyBh?>KIXvj$pf4% z2^I%44-0_SDv(o_Dzyeerw_#}fypVBs|bTOKS?-kRL+k+|M9kP zH~a0o)9pi-vRI-}2=0T|9%mp|OnH7>d2X>WsrX#yUqAb}+b{raZ+Ct6+n4W9S?K{U z9kfd@)=)-6f0jqwIg{`Z31ADUygsiFqoeKsbI+mP-2(d4$TkVv#I( zaF7}4iU}53CVp@b8x`HBqk;tzeP_U68_WQwgfFR>KXsG_Cj)nZyM={mo&cZIc^PS& zmY0`xwV2!K92NNg0+g>#J{}#HsO6&n+wsxK$=xJ}O{b8IA@gW@^JGpvPYb^_PI|IE!>zEgw*VD6a7cNY=6M_{67FgsCdxC^NoWr9+?wwyxPfm`H0*`wAgAYhm zpH7{Z19psc8h$S!ZWTg}PYl|h?tdg042)#$qR?s=b**qHG)nu5cW??0)6Z*&4L*z} zc{L>)t!diT4H0K7l5sv{#0KuYeVM#aHXTw}V zBjEt{c6V$qmP39C_0S!Q%VB8twydno)b(M&Qit}YJqUAu+)FvRTp)%R8k#pSVI|OX zlq-UuIP)T3{Uo3bsRMZ9oIi=``Pb9qqvNk3BzksyeDd`ilkG@AKM~Y2iT(m3{8;Mj z^Xc*7??*q}%>WdfKA)Wrub;QS>MI?Ky=vax210j)P2LkN1*!$j!hI}4>;Q(wgEBAw zC&Z-+9uWE-3}c2QdH;Toip9|kVK{Vh2=Soz@@s%9U^a!5U60l8+1x)&0GKQ;d#+f> z4U4m#Y&KtF^8{=H^~GaJlmGeOApRHX8=wK^Ex?=}x&I42|9_zeL?l0tT1$zb&|wl4 z54xxsFtQxY^O%Hm;5HBt=roHHMS#V(Td)y6xWNAYZ@+DAuKq$m!Y_c$A%FikyS=`$ zI9jo`51}5-3frVl&9v_2(hS3iY798e3FS|E@rd~0WIShk0y+X6=*=})r+frySjqU1 zQ1$!IpzS$5IXY`}k;jLNlhbotWMKfBIa@3idgaoG(7rkOczpcn`^BAD-Ef_pj7cKO zU)cXNDuvu`4IA2<;Q4|#gnpbr?!;ceg4LCHG(J>(1|{8CJg5$HvWzNg1PJ>b240G+ z&-MA_2sTbQOHR=AAvwMt(<}d8km&P3f2_kW)*)1?)hfcI(|;1IZieCKJ1frxQowkW zXDC3YQ&a47*+K@w7I6jpQYgnxMj&__h`^M9LACiFsH?BvZ0+r!!SH*#+nYO^uh;)f zUglI(?hg+R&+jZM$+UpgS%bj{gr&L@tIP8jpcjvbA5K80(|JH<=0;kd5FizDIY_=( z8dZcz5~)V7Us%>ounzt^DD*Y>XkQ8jYlwl=VA=JFXvZ#3oUTj1$YjvYkCCzF)u=6K z3c#Kf^LebM4~Zm@4quGNFQ^Aet9Ek1_F0?4?3MzH@aMDj+OO|jd!|- zu+!-Zx8&Pfn{QWFrt;`NfzqdgqtD^&6O+*JXBc|DK`#!u&Rwo?GbBP$ui?tctHoY` z&|D=o=o1jLXR)NONm4(f6OG( zBkDTQ1w-4PsH|b&u;%Atnd*3O!SjfJIzBu&I6nF`FyRcg#(zmY01Aanh5-sAjJMNy zd_t(RKhrHK6y>X(cUx=rX(4*8r4N!CcF#Jy+kBi9SIjs5p~Z!Plc(EX@aV1O93 z=E`(>LcQY4X8C*8^b?g#CUvLufTTO^Z5AEfe>>7anf-d> z-S*!5-`@YW4}EZB!IKm5ysCj=cqvgR6eEyx$OTY7=PlNu8>H*A5RaPfijfX**`+Ve@f4yp)XpH48@9pocqRY~_ zv%BM%$f06)Ue`AfY`3$?J$HEnQ!Ja28&QA~j_yFXq!{aaK zg%#VfQ9o5A{n%9UEfkRXP8(f0SYw%)D?f4DriL#A;4>gro?KfAkIKxumS_SFl*kI_ID zY|B1_(7}YDr>QY;ysfQet)4bFgUnGzbQs)EbolcsnR9c@-`?-9+bX`hyFm%zo=k7; z=XlN1VRz0gKu+0wiGWuu5NgjokFmQ4`@aVE|1)yU1iY^&nC^+sJ<~>2 zl1f!O+$-$0*9xDV9(@W_+ZEr@@#!?9-5fNpVu?7-G?N@iY%8e*1pv8>jF^!FRb|t#6ncKlk>JlkCmcuU759 zY_8#L{{~?&X-@^-+`~n*C*$GBkXyF__pj+>!0IHGAqME((Yag}^{Jo3K&3JDyKj~8 z6R$5iv#Pg?B`lc0v-=kQ5ekJ8q+V}4)8!uCn_1dl6mt2g=m*5{HfarP9mue9=lT4viqnTs_=$5P|)n(Y{{c#D*zx&ShH}6^GYSFNy`BiF(Kaaa1 zciY_m>h)dBS5(B3=*N$M0Oq)Omu+s(`itRrbic*voon6gu{}@i{x#Cq(P(Pf{qpVB zHc2|a1>)wz&WBg6AD_PbI42n;R=a!6bNlTx%%b+`OVHw8F`Iwo@apyYU4+Hrju*d` zeA=v%Sj4vIgl89t7amg^M1r?Fzwd6ZniGX`gG2y48_-XH3i)AseO@X9BA5*7#ohsU zjuW4M*5N!mqp)MXP$1~H8?9$a@Ov1FRUwg@Kp=s|s-Iflnga$^yKAl_RZtCh4Nu%P z&^6w_7K;2&!9Pd<>USSLIHcSTaWe^yMU&05ZWC&^;70ZBS1ZcqW34?5A3@Ium=~Wf z=B*VG5W7mmD$7P<(Kvx-Nxxi6M5E!cIyVel#!@-CTq$`NrtuE~=)i{ufT(_8DQzDh z21ozty!p>Gm~0Mx^yC=E3LhMQPpO<1GcX9BhS6@H2r%#xV6GNbySfPa5|&qQ-|y}c zX||rv=d?rAeP{JwRJY5 zp1`q-z`!-=BGAhg>wuTI3uXw*i!&MI%Ig!bg%TkY6S~XuC@F0m#k#q*z5Dy_@4x?T zXKVA-Q<)&3cmMOpkAL6WKROvNKK=?k9-rR<&x0R6i9A*pGe(^4d;xOsrxgGOox{Al zjw4VF*g;p-#|w|>+y4>#g9N}<{{GvRt96lnScg-oHk!Mi_Yb}=Zoc37eRDODEj_x# z*=-Hm?e^X5Qp7`4Vi&QvXqjjOC=@C+J(Ehtr$Xm0od>@q7R-x%|Hsg;|NZ0N50Acv z9?@}AFp}3dPm57$mQA{J;QQypLonmMocl{Eqe1-(9YyyBj1Jr=#A}Yeoa@?*2E5)M zUiQ1~4?9~^im0Olckk2Z%GTT0uiOs&GyolzKA=S--qWM+-w(d}g0aeyR-^jGXc*Q? z<&sFbWPY)vP#Ryoa=UZ{-d}+W3TWJ}U>)8mp04)>E>X-@UY~%)>YN?^^nX1#I`Kvb z1OOLy-+w?;oNNOn^7YFnxBTUfVPfR{Pe;V?sHZ^Z@HnhqxBo2B+sD=tU=I9ABgJxc~zeLl=ZM}P=B*wxsmEisqaCMU-0Jl9( z*V}2gT0mfm=dW&80!IgXfB*RLkHfD&30f7^?S8$v`QF0lH-?g}&DU>Mv3&H8p>b0w zms?f10*EDYd|G2bm`pYsO?ZeOKLbt}81!=7gq4r~^FRMr4*e-Sj{O+`^PiDG6O$2E zfM)i7RZ7n+cKlDmKTH5fuX%Ft&HFHzoW{IMUitS9KErVDy-z1Ue2;r`#d4U2!u>EA z@B<8DvDj6|Xq;G-EP+JCuhj}AqJ7cpcDj3EDh>Y>+>8IPtH0xU@c$IZ!vKc_&42a# z%jrl^uV7~;l(5Jb3&pikCW&rRxC_S;*@{3c6-)xH9!nPpxy6GpUt?z<*A)ZbzkL1# z%K#jVef`~IOaa*cbaEPuiY*4+beT{lSE!IeHjVF*%j8Q{L=4Pg?C?=!(uu3EbZuk% zx4->ucjxU=v_kOP4R5)-OCpgJVJL*$UHi=t(+;|gdaWsusWjv8Y#E0*=q`@X{ZogZ zj))8XHmAqEuDygA1on@P7hkP;oVKSzQeZ$DXdOUc+%@#&IPcgafGJC{Os3Do!YY<< z(oO7oH~Le%n-3$?9ssp9fFYDlUC(-t$ox?%7XG~i@W|2*6$%>g*1QcbB-HoP$OR#+ zVS;XEZ?9d+$G?94yayTn`#j4Shg_z3xw|EM&R*gyj0l$7eqX|Gn9bqxkXExnHbF*k zSJoMpk*WX=K=HqJi3-A*Sft_!Jo)p_HTK+L0OU`Z2FAe$xnvUDBbLGx`m*9=m5D)T zauzzR7VdSv%ayA&ga?DGjvohX;FmA0v~N7(o%&@7J$> zh9b#|8t5D22~HPEB=SbJRJa$SV740F9-lwHn@4dUj|P0_%(qYh*x5vUf#Gm!?lkL_ zmO^KHySwc&sW|0o6L0WJv5==|b<0*%DlgjY7Clj)ALPP?a=C%!n!uqn+w7|>@5$%= zKmOP|{Qk4#Ua>u%4GMsv0!{Yb?GSL?Zgkt)FZ`KI1`JNXhW>K^G!mF_a&_IkHrEQ530gUylBj`8#^u+v!Ifi6XAeCg#2#*6AKrCa~9Xrqgm9vpvr%Sfx zuAx-f^P+Ly_+I>pv=^Xy+b94}e*9D0;N}hkfLbA6vc8u}Cj`a^FW+wr2wNu6ocY7s z+i`m~o1N|@gE@}0d^BAkn#JH6sb9gr00aiWse5ru-TkZ2z!HX*(5xr5n#dH&K*CS5 z1n_u#0n%DQz0H7b3SJ3c-24g23Fg1eMUT+$t{c7Ffu zo!b(QXY#l$TgYWnttGQ#b;Dyeu57M*Y!(ctNqL!i-o`Q{6E zOiMcT;=_2gG4khr%BmZM$Nice`PKGj&>K`_^lSp4!hCaf?_tgDS#^))z07Ph?e2*4 zUf-{_o{m~j*xZcZ5{dr_0)U&kfCBJuT@#@6d%YgQV$J$!xEwYI>2!N@bR;XRv*vQ5 ze4d2~5Dw6=W+{5wHSNb-r_gFui}h6UscBaQ!-1yW7aELK z1T04O$D~lV&CJ#^3~W3-@N+WVkui4|pePgzMMMf106&&OWu{Qdl?bGFHw43OGFxwM zhu`-0M%eMA=>oBw3FxC?QE?wXUQUz>AbsJgJdwKV$r_ExKnAlb>cv{Eg>cxDd;m-a zFo74SXgUoHCMtyvC&jN2`}yto5KhbegQL&iOTE*NA0zlgZ^a|NpOm-TyC1feBfj9( zm|Gj^MC_8{aKG7tdc*s-9>pOkt&?TV1#W+fC`5hj&^!%s)Y)+&-32<2wz1(v^UvqobZ0CNjuM9@O8s>qfSuA$T z$OnVIf918iLdDThvU~FB)9KSzNA2N0`+qGcop!r95(tGeUK0tB7dNfOoOKdBH^>gw zU-fbsi!fibV+PSn!kVqvzJ(VX+J3$KM1UiZ|8O?qe}08z>?%$mczlKV*{)NIc@rJ+ zv>EruoCQeoC*}?V48=mZ(3eW(Ac#L^H2C8yAcb~2dUX8F8=O#b9MvDil&)xl)@GDcNtM_NsM>?7YxwQBRnnIq7%7+Pm%T z-M_(VL%;p@e(Rm(k<9=ILm^ejivElo{CUpGeJmD@|K(O%GFvRW8|=hSAJLvhg#nlV zBpsMWXQ5&OqlWpz07Ji!DHLI06ifwx^@ZUUc!D)I=+v7nkwRy*W2`dz38&89aenNg zn8nF2EkTsJd3-W;3j}=LeXHFehg>|6b&xgh7h&<9F>Ws7&vOWOB3RJI;g=Lr`vUha zpNxMj@csAzR#yCD|LEJ#>Cov!Lf#+WE5_}eo!yEIYL#<&d|{_st+YQSfz{sav|9*xVw^?kW`lgP(3|z0dC%ycmm;6{-1iFu;7>y3 z@L2jjpEn5p^Ig(kyo5ckw%9CkDF5+d?DH_Y72#L7Z326`NY-Wl)u15dQn7$2)LNod zOEu^;DgbIA(kr7FgCzFeX)p~_cx%*LE!CU}DJagwQa^J|OJ1hy1ZA&vwgXHJK zg9Bg_9)9u>t*@-l`~BRa}84Ow9xz|^$f+>0Sgqi)9aG&`2@$X(= zxQ8a3JcKcUOtZ;&e}3kDYW_diitsCBzJj*E$reJF;x(2wum&to$lpVaBMqfbXb zao7iczWr$WK7Nd#DqE#e$kB90=bI1j z-hbG7v+hn4hDPDcclz<;cc1rJJM=&UkgB#k0W4tO8wRG74@}X$y$~?9q>HB?kAC`o zl}lxIE5g$Ny5@rV-~x$1IJ1HoWmyrd*F7!CY|S%R1KxB0jC{R?sg6aJ zMsvJ&1t4CrJ8g@Ra5##p&+Xe(B9Ta4fKguUUW1ou;{0Zcq!LN5RH}d$p9CT~0O{0-RK^|)UdhmpO@PH5A8HjlznV{9}b;t8f z?{cj;2f;`%Jfi`COX$0amcgK{!cODr^m=hMnb=1ijuRvkBiNqxM7tbMz=u|Y?@_={ z6N9W!ux)SLMx#ETp1CQGO1q@jDy19>*$L52H_N$fG9HV9_UsKo=Or@0Mv$zpI~R%} zuit-vPZ{0I>4P%hAfd@>66LR=m*b((O$IFjeijvDT=pt=rdr?$zc31!y=G= z90rrIyhx!j_zXIeE!EqdZkJ0H4EO?**ep`h^mshJMAmM#S`)X1!L{lz6$9siAoi?E zxBKPVO6J_>4@R!yseG;36&V*P!%_92g`-_@IK-hyP$XB~Rm!u%i^fgI{pP02OC|V`+N_Ym?;tqK)0YY z2`Yr%?Ot1zTWOU0#RsXg9wmTVx`4;gFWlVT57CaG8=WgImm(7M2Z>Sj5C-GY67xo5 z)MJyGuGMN3?Mx<{LQC0A0DfAaYk-z*6_QXwKS}_6lliT^eHNH{`XD4Wr`=*P8T1;J zT(mG|C5;4z8noiD>heWA56u7CctWw4sIVV>zPloFUjRSbOoHYY8_gzuHg*+z(4_+! zlfJ+*ip0vLWs^l03-|Sa*Tw`u5%s#=E~PhZz}ZPTApoXkNiVHrlGh|(327`%2>=D} zr5R364$k_*<<}mgu}YZRv%du}=M=!>1R%2hW;Po6P{{n-00>}s%cM~0W*VI%gyxkA z^rF+H3I)yr)16hMrWsIqe4cW#U2kRZG0b#0T`o4z7y_8xMt8YitlDB{=U#v4B9_dS zDy_kgXTH0;=|8N`V0AcFTA@HhB$Y`30n8|H-8>Y$i3Avrk_{W^0!a}tK&gP91mm1^ zXTO5n!Mu*=e1V5X;y}pd2{pIKJ)L?B1~o-!0Q2AB>;wVEl)znf*sWlEqXV2hAdUSS z0Z;+tzHcl`c43j=_S49Rl-43GE$-#q)`FzgJWb_$UU7oe-N!LN8-6PeR z;HxnKG=a#WQWpsP(B-mOtmP3xWr=tKiO#lSUv*g%0bejQ+ZY;^Ucaa^Xu)ORWWHCa zmHHu^hS=K#V2Vy6jgr@+1Rz?rn(6_6fJ8Jlw|jNPZZVq-zz`8~8QA7N4cco4MrM}S z0D5;>@LQ=6_+W96hCd4}$S1nAzDOC}^f)S`Ucsn^&U{=0eV85-fOBIR{Ls?<)NY-f zffE9t>U3IV^E#a&*(#(To*@ABt96)^zJZH;@X?p6U>5&V34m`felT*!NBo|Q2n1XfV~97O z(dB;WD*L>Hdk3AefK0^xZtB+d%?YNX{Vuso81_od*MiJ6%{^ zedz`bWL;X)YZOuem$q1e17BkM9rPMIbxoQDII8fsN@zQl;Y(EgBM&wbM)kWvyI#tr zlUEm!KtXn5%{81myw z&0cxlnR%xGo+JQ+esc8h0UE_v9gc1X9p9_2>DV;O@rJ~xB{+* z+H5s3T4y?e(ZB@qANLv1U9MF()#vwmBlx5DiwK3qTB3ED%~=w5#Z2NFYY^-DT&DK(SsiVbVX3ArfQEUsvj08DUicf~9KdRA&tfs=~c zv8`HTP#5nwCZpA(tJRmiv{8mpVaCo@YT70GXa zVK$)(^-Du{x;TeJZ(KIq#jj$s76qa-n}Cc65Y08`+Dq5!YCm%oPh|TdJC8vh-a-Wf z44qvAb9NR0;7T=batlr;JXSX7dV@|>zMkn)48vTvRmo?Q@n|$04Bc_Bfdx^&#vC_l z!|E%;kVsdvGjKuxcPfQeJIZ9pJ`DJAIuz?;0-%5)KOq34$5otMYWB+W&dfUn@H7Dk z{}Tj&RC|L?w_fc~7);t-GZBdZawpl5;~T|Exy9oM_^Mv3(Zoiu%k5mc4*CLkIv+LA zo|hB;^Yf{Xc2s9FxdKhQS*^|Z0qrY}l^QUZb6lxRDyx(#X`*|b2+%&}A-D$;0kE`s zt*o9&W0JIvFjK`(no`UlI z@Mh3q8D6nNCKe1|q5OI)6!WBl&duPy4exS6YH~WH-5gP+ z0pOXfW>fPj8h@}Dq&1lk=W5wXA##ylHg%nhT}Hul z3peua;(t z0m@gLcH>nf9BMBB0$6GS2AH!_fI<$YY%P@}697e{R%>pu`8+9P!MOGsjHPl0o)j6k z97dzG*UJ)_MQ4G-Et8zk+@LnR>9%WqmJCt~`z8|(`GJtbW$nt^%hgrYr9TjjMKjq_ zb!3!M7l%WFN8?^Bk!qj~pmnU+3n4OrPS6G#Z?#(M^ls%!m?r$05YPnQScR3xrht2dE9z00oAQtE!X?~ZaH}o2>~l?-a_)=c9hl|xq*-m z%aA)@aJa0c#ak*vD3-|e!0K~57@ZD5A-0)?5`)ENm1Qq4q7M>hX^o{)#7|^YyW6v> zKlgflq02BV$8`)B`(?=E+f_AG~7|7jQVzHR*{SH1~9l!;$H1v2* z0STb<%?I89xXxBNcK#yerjI{Nwce>!3VCqP@RmWN&*dGYd1tj*>j7U7x9d7=8g0?c zqVr{Py}@K!Sy^%2wYx>Ud%-Auxvp9=t=Juu@J0CIp)TG@CQ`(WR7|_uu~a+z@#8F* z&8FJ-RK7w2+j98GEC2v+cwAsMP6@zhehVjX5twEg8lg*d9!Tf6c?RZBBtmmDHA5aH zfD-;;gMnhe2>?;Z<(fvmP$c#e<|0aDP=)8F54DR#8W)5!kMZ!S<~-+|QvgpBzz_^O zg6;h;$29!2bh@9Khl#OoXe`>2=-I^&|#}!{rJVn~nO6 z+^EzVbtYfLBgL*$<#M^KTr8IFkWm@T9%u$Uk`{y}glX7PDKa%7)oQuSkpdRLk0As+ zE?e8{pZ2A-uUzC>3EWkW?b=6u<#~8UqXZB!oEK)_mW+zG#-Alu2Mzt8f=4 zsziV43I>f&(%4UN+lARI zmQW-jr9c_@(Aun++-iH#gFm%h^dAJQ-UxW_Q772H0>t=l+6o%pwL2T$H4pjg#C1}# zw5-)3TCG7RxK1V}34d5@78~hyIx{|{cDvn$C2C2#g5)xVj9rAK#eqRvDixV>mF$sl zU|@CdQ;8JGY6B=23OPij)*A>zK_!!jkb0dEa*oJMkJS|=pnY+8aJYA{2lEpi99>Lk zkSSGZma3`Cus06X-qz)*FBna#;BpuC0f=I$P#8)?LO=lp@FXOj5@0!l*6~9m6G^6X zh>(xP9t)q73K}gQ2yXAIHTKYOYX_1`o{Pg7O zwFuqX^-9@1{`&1(cxvm@=ydn)hu8OBVAIde0h-+C@OV5fvvRuY{&tWA>=YRHd8OIw zA}l8TvF#sEqgW_p?^POLv!I6-y-Mo3Z=oqA(Po_DS2-0S%*3tM@5W;Q2%UmNDwU2^ z;QtORQo+c1OauOxfJguo>JlVL#k1N3#%6W3TAh}sH|lha>vVFW+Eb|vCeWZ}cId*D zyBmbb9H;5QAd|^u?Q$Mvy<)jg?w~tq=RzP5-lWs1Sr=g;mr7@#6MV{BI-^0YsN_qe zM1@A9+4xjW2^=*j*bU|t4bg|s6$r&tU?NS7^I9aH${-5JGZVB6xk3?$d9joV1{aye zdFTeJE%9W=WQyhHh|OaDi5ZdxNXCBpF0XTlTr3=i6ECRO;3gY5xBc2dgw{rKq9;r1XP`w48J1Rhi#ax7gk~0 zUK`U;Cj|Lv&xg0#?QnSijIt7spqNP}V%lZ9W8EwjYSukYlMdE3f=16J8QABaLOYB) zl^4x+GaT2W819=b7V|lVOrdx(*WiBu|Mq+U_+K2_XIEpbKovlwGw5|Xjh=U%ytcc=fpGtKS;0Mr&1((e$m$QN^kYJ)2v>9}x5kWe6$ zN~$l%vS=7J%zWd?B z_U25!G7P{die^(|gBC^Z1Qx194W7U69~>QjA+8=SEL`pH9UdNhI{8V8nxfb@-+{Rg zv-JYP`LMO=#_uEH^#jy2W>})#_3Six^Kl)IT^6m5^|vr#@aCIUhaeKW&h(M{2)tVY zPG`lzEZQh9?d=`MkvN_r4clK`%cXN!xoTo&pwOe)7_t2*3(M#M$wv6svpsK<<`c_b%*I9dYL51ySrFcW1Oid@)%r`q^(md!i~YioB_>F_i%48 zozVcm7AcJ+c7+sl;*1)1JGh~`Sg;Q37!2y-I33%c!yTlKF>$awZ#Lh5*xkkyfNh`x zY;A75AQ@R0?ry$<)+2kD`WHX}*oOws!T#Z=Fv;(e^B(>ICfMQUuO!B$uB~sr-`w5- zTJ_Ej__3|cSJQe=TNcvqAL?63$#g12e&5~Rcqu*$hAv~{ zq60+yco@q_09ySVMfVQ5>*B9JqAE=$bDdPEbdywt09tJOE|#R%1c`1YW9Ppf|NeXc z_+K2uFJJIi4=|fqw|SjPUJJB<0+zVI97vKD=bj6NOeX7;3(II*@9r)YD*Z>(f?VwH z?VZl^abHFQexJ;l8HHePxjn#KjVjqP;SMR1!7e?JK)^VLpoB1*9&798LH z|M@@v^S{1ckvrYyUVrs=OW%!t|8x|nNVhg#z4kb8r>$jkYkTK!zy1Cj)S!MhJjm>@ zvv$rr)9R)hiV-RY=3aJUiI=H7_!y>NI~jis)B*Hn4lU2%Ka&rDh#_d5Px$K@Jj|>M zCVjHBv}Dw3L0kUW8VMNqGKE~umcEBgjbcm2d^9acGZL;Y7*r2?q!ILi;X>Kid=E6? z-+lvPNq+j{CwWKApc1>{KqKHeMt6GF&ggdAO+=(n8K(+i`a2v-`y?wAx&he+QK^_O z&Nx6@&3dgXSLsc5Oms_OyaV8No$vhH*W<%bhss2< zM_*39{q#-OaF@^uDRV8ISv1Nh@FN7l4zbRGI|un%1G#|Qd&2fz>h3Nn_2XX!mNVM= z>s`M8iF^P=hu@H0gnP4+*@vupPfNkK^2BL=yU6473=1;7EEw#cwCZVVS!9P8T=Mwcsoajenq0=neElqSz(f{f%5AmdJXyBiaJoF$Zyn#mti& zHa&qvDrF;ya40kz2Fwvl_;SiEg)U%m`SRtJ6{lmR7m9=;xAPZ^34n0`{X{&khR~aB zjuksK6p2K7hz&;H34K1lFLV)&C)3$ty*o0Akzv1A6>PZB_GA?B>%MpeLmv$?;?fAR zwCY}Q+090+T3E(d@5#`8qjPOdoA93h^j}_GXG_gnWJ%C#G+@xk>gtQtRVS@*6}^JX z=ZOqM0@wK4lf`hyi#3PcV$`VRBF^GM@&3Uup@L&|6C6X=?OpDsQ!ABnnPlt=G-n_% zy=xivWZdFxbi$)smjLq_4N@56OH@<4R2GM&?R0wO@!n~8N9yq%NexD$oVXW&)J+e2 z?ctg6sn$EY0fs2!W8(2R0Hjr~H)dA3|J5LA0Ovn1FM}r^=Oj63_%1I)r+<+1=qG{T z&l}$w(tp(hfUW`b&Q7tE*C@XG29MbQiWeInc7Oly_T|Gg?1dlSzkE76IQSV$M1Slb z9vy!E{$o7<$B4z^HKWmL{1p-3p!GD)dZY=E%cSCWcK7<5jd%7ZE&w!u0&-I*KTPco zM{+^G`tt2NAZ&NGw_bZz7A_LkS+e28l}ZU~@gy)&!3<>emd7E!h$S->gr_%K9Xy}+ z>(S>=(3A%c4s-DdY?dW0P|LMY?rcIzg;+9RIjl(?ZdiS}xdV%He$cn4RQ7lh3!zx1 zx{Y7_{PyYS`*s?%{tnykW0lPQi0$>OHD0gYV6JR! ztiN7agO;OuQFT@|pZ>CYrwr z&-;JSEBdbkX*@7Ja4BiqI>%aZqiUjqq?-?uXzq!4e>mE9IgTRv>i@Z(1x< z$Yw%zAQml-mz#X1>y^Xu&_bwvJN|Nbu=f#6gTL?Z9~^)A`p{W$8H-7l4W?iD0Rw0X z*F7&uzFfy6;lSeB`o`AQ*86RjX1V}QLZMJBj*6ws1L1_!V&RZ4WqiE}tl-~wciwHj zTs#YhN&6B-4lolz2fVXBG~sz_IC}whw%@%GdVRrY9MGpitruTK&b=omiCVYcF2}x| zczqXHnNFvWH7;Wn22=d|MP$RAODXp+2I?=X>vUO6yMyG3))s3{^JK@GOUgRc!^aXg5I&%8fcZs_OQ*?#|iYks9F4x7cI%#Y=|2ztHW zy~sLTLEriCUU~iP$7zsgDJ;L;dcOl#*S5Fb&fa4G7E8gkySu$6On*5!^#y??!xxI> zciBYLH`RHs!?9Ejk&1)@=z`mj52W;)TAyB-qfK69;_hzFbuW_UCb3fgR|<2vn~EF6OS6|^bZ*YM!$uy7i(QDEpOC0Z(|1qMR=_V3`p7BLr}G!gB1#muPV&n0So)LipSM z|N37C$x@%GGt6-j#bZ}jw?>1(^jOJI-2e3+sD#83h@#;0o_zo2r>||j2h;S!?$+ju zIV;3Ul}gzv)?I7 zaRBy#q7QX%xWdU~F@wjoA}QP?X~57!EqEE|bldj8#Om81mkE4&ibIBBYc{h9BDBD3rqUug9N& zZgBARG#EoPil>V}(b!PS|6K>y7nmmaa_!lfH6KA@Z%(SvXwc{Lxtt!n)MP1u=I_qw zA@@@OV|%?nPO-P13B_x8uQL%)H;_rr{=GFnpA8ZLP^tC(gKORTnwhXRf?|={-#fUJ zzV_JW`9()$=`pxTN|&| z9fW4S@4*3t1g3wC zI15B#sgjLN*~(8f>pu3|feLG({P20ma& z5l|XDA9dcE$MfJ`<0wsHWvc$`K6H zuY<6;+?g)sbGaO(-I*GjV4llh`3$1M4CtT)m_ho`4cKl~CFa-fY>HariCD>HGg|Iu zb)JhT9=nX?Oh(i4;~p8%9UdJXeofKcTkkeEU#+jskdaoaQZkE$ zc?tkT^7!-NAAdB4cQe65^!fXLR^T}OBuSlsvI6R&J%;|I_Qz;~#6@HRVUW7v? z$D4O(;1SS}2!s6|S)eZBt!`Pd>PR~Nu${widXO#FY6y?Rfe-kU$D5FLo}7XHFQ9@& z5J9_%jC-H=^dvnVfq!kY(G=*)@SJt@Fw`Y}l|T4D1kyww{RsGdh0YC!L!BW2#1{yh z=h`=%2RrEIpw+6@Zv{f~PAgOSvdx=#cbZ)m6hh!Yiq_5u^7!{G8!N8;Q?r;4!GH2H;f*C{(Nw-2gLa2 zobK)GH|t~D5r=fysMD1S`Dsbe84MENKVhcm%>(j>Or=e-JL?(Xx*Ya~ zz+j-4i|D>I3}+X)aimBBWricth1dcCi_z$yg+Ydwm+@E?XaGw~hGoOT)m1D;+#CUG z?6B^;jpk#w`w)$b;OY0k@&=gy==7MBd%7L;dk9Y;GXU$1rG2yRa+qfwVPomrY<0Wn zmJnbJ-p#QTaSXlFB`3nmKMA}v4-!V(E{v#9#f9MHz7Kpq`Amqu#fWW!hwj)%F&dz? zo&fgI1a?p28JigXO(0Kn=ZD?*CLz1q!K>j+VR^r^Z5DI7ebVaKe2i-KYFQvvDoA2n ztyIa_C0G;}2bA-Sa(N~}NEz4!Y?)$GW^aPgh417jRrvbNADxM^nSx?A+bpnFzRx%B zKI32t#Pm-X0Z7?9(UFNG5Jxxq>C@ri{{H^wX1xySkG zQCOYV-#+0svhNbnPPRn4;V^u@9!fE571D)V32Wt2?BeoDwPad?S^&@oE@xR0DhtCu z6M2^Ko*sV=TTyQXF-)e_tXC10&hZk9Mt-{?48lYjlTwHT4B)(nTS5S74CYvh7+^@+ zxc#AE#b5qO%tkdW!{Y9{yCmL@e>;FRY;oY5jK;vvJ_pyB{D=55$K5~zQB8PI_2S

4gAkn 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 _ANHbNBZ=n<9SMR2 zfYE5x+g_etr`ttR94>o3^}AbzXqhKcn9Z}gRDkfrS`T}b5%Ca-+a2_sorS~YTPGi) zSu0hndTVV@A7Q-kY5w)LL)!DRQM+VNiusT|qgyTIueNS8sb?38j{B|pzWh+_N3W!j zD>&;}?{kWvB!IZ8mvjn=fC-5rTeT8pMsIEI35g=b4{TsE_J|r@(?pzW!G5UlW&y{b z-=$PV+yf$&P9qLi8-xV_Xl+&C#)nk+d_nP%K-ZcurAD7k_qhpfU2^0*@*(RbezY7c z)ex7#V4Ndd&Q=5194>PzncXhGLA_dkqSa_N0>GVexm@JYdRmJi1IWUGw#KssvRsT? zivDtw!|Z=@f_JZu;SD%`)@?e~{nnt}td{fHWD9wv(B-|DTn!R4%?DFz* zCjd903HTg-;0db?HiysaPhLi29YlBhC>U7MmMcj5ZVUyELuz#1&LDRCft{8wl=6r^ z5D4<47m*TnZ~bg0%~UGYj9wq#P@_Nau@I*qTc2oLXt!OH^XT<#GM4Od#QaG)9ZMty zfy2=3$B%H$F1nj8ekc+Ml(HqtWKOVLjLfFA-~s@?`HeCWjqkF(H!!JJ>Ns6|B2K0Q z-vi!J$Ze8Q7$s))DkcD#R1E6^y@gg>YE{eSN|SeepHd~6Pa0b&xfmA!_*kvp@uIsuK5row*|EK|W&91e#4nmM z*2}rxs??jE2Oeee^86Bb`%5;R!SXC=t94@yruspE8~%O~?=T&1yO~}t74yoYK+tw^ zei2!31z;{$%dDdjiV+9e4^OzXRw0w`3!Q^vv9v-4rA+)Xfpl3j*dZa-Re5}YdL)c` zf{MH8;#W%^muZr)xIC#;EY&&P-hjnOBLo1_XHrb?4WK|vSF3Ho`VKL{5IjB!=y^zw zvZ5Egda*Z2R|s2(gXuoB<2-M{hl(lny6w?;)Qen@b=ej}HjA51*Y=!XxBLBm;G&W| z=z9u)`KcGn7|Eqj0I-KvE4oq33a{U%0t#SPLi#vFyvJ%a&T*#(Z7-*z;gqLP3XyvJ zG8~13F7qKmr?cHF+H#Eq4vzwY^GNtd8sXU;4n0yWupCf4EGcOnd2oSwy@ik231FkJ+hCt z9U3h@zh@A;jBQOH*F(G=V76≫u7+PODzZXHtpFNc0jeU}Q3)(HLAO&;i2W;i32< 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 R>>vOD002ovPDHLkV1jI0*3|$2 diff --git a/libraries/render-utils/res/fonts/Roboto-LICENSE.txt b/libraries/render-utils/res/fonts/Roboto-LICENSE.txt new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/libraries/render-utils/res/fonts/Roboto-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/libraries/render-utils/res/fonts/Roboto.arfont b/libraries/render-utils/res/fonts/Roboto.arfont new file mode 100644 index 0000000000000000000000000000000000000000..b93b738e1aebbfe8748ea1e49b90123994c15d9b GIT binary patch literal 59804 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

+ + + + +
+ Position:
+    
+    
+
+
+ X     + Y     + Z
+
+
+ Note: If you import a "serverless" json file, such data include positions. + It this case, the "Position" will act as an offset. +
+
+
+
+ + + + + +
+ Entity Host Type:
+    
+    
+
+
+
+
+
+ + + + + +
+
+ For large import, it can be wise to test it in a serverless environment before doing it in your real domain. +
+
+
+ +     + +
+
+
+ + diff --git a/scripts/system/create/importEntities/html/js/importEntitiesUi.js b/scripts/system/create/importEntities/html/js/importEntitiesUi.js new file mode 100644 index 0000000000..6e80c7f173 --- /dev/null +++ b/scripts/system/create/importEntities/html/js/importEntitiesUi.js @@ -0,0 +1,217 @@ +// importEntitiesUi.js +// +// Created by Alezia Kurdis on March 13th, 2024 +// Copyright 2024 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 + +let elJsonUrl; +let elBrowseBtn; +let elImportAtAvatar; +let elImportAtSpecificPosition; +let elImportAtSpecificPositionContainer; +let elPositionX; +let elPositionY; +let elPositionZ; +let elEntityHostTypeDomain; +let elEntityHostTypeAvatar; +let elMessageContainer; +let elImportBtn; +let elBackBtn; +let elTpTutorialBtn; +let elPastePositionBtn; + +let lockUntil; + +const LOCK_BTN_DELAY = 2000; //2 sec + +function loaded() { + lockUntil = 0; + + elJsonUrl = document.getElementById("jsonUrl"); + elBrowseBtn = document.getElementById("browseBtn"); + elImportAtAvatar = document.getElementById("importAtAvatar"); + elImportAtSpecificPosition = document.getElementById("importAtSpecificPosition"); + elImportAtSpecificPositionContainer = document.getElementById("importAtSpecificPositionContainer"); + elPositionX = document.getElementById("positionX"); + elPositionY = document.getElementById("positionY"); + elPositionZ = document.getElementById("positionZ"); + elEntityHostTypeDomain = document.getElementById("entityHostTypeDomain"); + elEntityHostTypeAvatar = document.getElementById("entityHostTypeAvatar"); + elMessageContainer = document.getElementById("messageContainer"); + elImportBtn = document.getElementById("importBtn"); + elBackBtn = document.getElementById("backBtn"); + elTpTutorialBtn = document.getElementById("tpTutorialBtn"); + elPastePositionBtn = document.getElementById("pastePositionBtn"); + + elJsonUrl.oninput = function() { + persistData(); + } + + elPositionX.oninput = function() { + persistData(); + } + + elPositionY.oninput = function() { + persistData(); + } + + elPositionZ.oninput = function() { + persistData(); + } + + elEntityHostTypeDomain.onclick = function() { + persistData(); + } + + elEntityHostTypeAvatar.onclick = function() { + persistData(); + } + + elBrowseBtn.onclick = function() { + const d = new Date(); + let time = d.getTime(); + if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) { + EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiBrowse" })); + lockUntil = d.getTime() + LOCK_BTN_DELAY; + } + }; + + elImportAtAvatar.onclick = function() { + elImportAtSpecificPositionContainer.style.display = "None"; + persistData(); + }; + + elImportAtSpecificPosition.onclick = function() { + elImportAtSpecificPositionContainer.style.display = "Block"; + persistData(); + }; + + elImportBtn.onclick = function() { + const d = new Date(); + let time = d.getTime(); + if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) { + importJsonToWorld(); + lockUntil = d.getTime() + LOCK_BTN_DELAY; + } + }; + + elBackBtn.onclick = function() { + const d = new Date(); + let time = d.getTime(); + if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) { + EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGoBack" })); + lockUntil = d.getTime() + LOCK_BTN_DELAY; + } + }; + + elTpTutorialBtn.onclick = function() { + const d = new Date(); + let time = d.getTime(); + if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) { + EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGoTutorial" })); + lockUntil = d.getTime() + LOCK_BTN_DELAY; + } + }; + + elPastePositionBtn.onclick = function() { + const d = new Date(); + let time = d.getTime(); + if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) { + EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGetCopiedPosition" })); + lockUntil = d.getTime() + LOCK_BTN_DELAY; + } + }; + + EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGetPersistData" })); +} + +function persistData() { + let message = { + "type": "importUiPersistData", + "importUiPersistedData": { + "elJsonUrl": elJsonUrl.value, + "elImportAtAvatar": elImportAtAvatar.checked, + "elImportAtSpecificPosition": elImportAtSpecificPosition.checked, + "elPositionX": elPositionX.value, + "elPositionY": elPositionY.value, + "elPositionZ": elPositionZ.value, + "elEntityHostTypeDomain": elEntityHostTypeDomain.checked, + "elEntityHostTypeAvatar": elEntityHostTypeAvatar.checked + } + }; + EventBridge.emitWebEvent(JSON.stringify(message)); +} + +function loadDataInUi(importUiPersistedData) { + elJsonUrl.value = importUiPersistedData.elJsonUrl; + elImportAtAvatar.checked = importUiPersistedData.elImportAtAvatar; + elImportAtSpecificPosition.checked = importUiPersistedData.elImportAtSpecificPosition; + elPositionX.value = importUiPersistedData.elPositionX; + elPositionY.value = importUiPersistedData.elPositionY; + elPositionZ.value = importUiPersistedData.elPositionZ; + elEntityHostTypeDomain.checked = importUiPersistedData.elEntityHostTypeDomain; + elEntityHostTypeAvatar.checked = importUiPersistedData.elEntityHostTypeAvatar; + if (elImportAtSpecificPosition.checked) { + elImportAtSpecificPositionContainer.style.display = "Block"; + } +} + +function importJsonToWorld() { + elMessageContainer.innerHTML = ""; + + if (elJsonUrl.value === "") { + elMessageContainer.innerHTML = "
ERROR: 'URL/File (.json)' is required.
"; + return; + } + + let positioningMode = getRadioValue("importAtPosition"); + let entityHostType = getRadioValue("entityHostType"); + + if (positioningMode === "position" && (elPositionX.value === "" || elPositionY.value === "" || elPositionZ.value === "")) { + elMessageContainer.innerHTML = "
ERROR: 'Position' is required.
"; + return; + } + let position = {"x": parseFloat(elPositionX.value), "y": parseFloat(elPositionY.value), "z": parseFloat(elPositionZ.value)}; + let message = { + "type": "importUiImport", + "jsonURL": elJsonUrl.value, + "positioningMode": positioningMode, + "position": position, + "entityHostType": entityHostType + }; + EventBridge.emitWebEvent(JSON.stringify(message)); +} + +function getRadioValue(objectName) { + let radios = document.getElementsByName(objectName); + let i; + let selectedValue = ""; + for (i = 0; i < radios.length; i++) { + if (radios[i].checked) { + selectedValue = radios[i].value; + break; + } + } + return selectedValue; +} + +EventBridge.scriptEventReceived.connect(function(message){ + let messageObj = JSON.parse(message); + if (messageObj.type === "importUi_IMPORT_CONFIRMATION") { + elMessageContainer.innerHTML = "
IMPORT SUCCESSFUL.
"; + } else if (messageObj.type === "importUi_IMPORT_ERROR") { + elMessageContainer.innerHTML = "
IMPORT ERROR: " + messageObj.reason + "
"; + } else if (messageObj.type === "importUi_SELECTED_FILE") { + elJsonUrl.value = messageObj.file; + persistData(); + } else if (messageObj.type === "importUi_POSITION_TO_PASTE") { + elPositionX.value = messageObj.position.x; + elPositionY.value = messageObj.position.y; + elPositionZ.value = messageObj.position.z; + persistData(); + } else if (messageObj.type === "importUi_LOAD_DATA") { + loadDataInUi(messageObj.importUiPersistedData); + } +}); diff --git a/scripts/system/create/modules/renderWithZonesManager.html b/scripts/system/create/modules/renderWithZonesManager.html new file mode 100644 index 0000000000..03dd460159 --- /dev/null +++ b/scripts/system/create/modules/renderWithZonesManager.html @@ -0,0 +1,410 @@ + + + + + + RenderWithZones Manager + + + +
+
+

+
+
+ Analysis in progress... +

+
+ +
+
+ + + diff --git a/scripts/system/create/modules/renderWithZonesManager.js b/scripts/system/create/modules/renderWithZonesManager.js new file mode 100644 index 0000000000..49dec2bdf3 --- /dev/null +++ b/scripts/system/create/modules/renderWithZonesManager.js @@ -0,0 +1,453 @@ +// +// renderWithZonesManager.js +// +// Created by Alezia Kurdis on January 28th, 2024. +// Copyright 2024 Overte e.V. +// +// This script is to manage the zone in the property renderWithZones more efficiently in the Create Application. +// It allows a global view over a specific selection with possibility to +// REPLACE, REMOVE or ADD zones on those properties more efficiently. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +let rwzmSelectedId = []; +let rwzmAllZonesList = []; +let rwzmUsedZonesList = []; +let rwzmSelectedEntitiesData = []; +let rwzmUndo = []; + +let enforceLocked = false; + +const RWZ_ZONE_SCAN_RADIUS = 27713; //maximal radius to cover the entire domain. + +let rwzmOverlayWebWindow = null; + +function renderWithZonesManager(entityIDs, highlightedID = "") { + if (rwzmGetCheckSum(entityIDs) !== rwzmGetCheckSum(rwzmSelectedId)) { + rwzmUndo = []; + } + rwzmSelectedId = entityIDs; + if (entityIDs.length === 0) { + audioFeedback.rejection(); + Window.alert("You have nothing selected."); + return; + } else { + rwzmAllZonesList = []; + rwzmUsedZonesList = []; + rwzmSelectedEntitiesData = []; + rwzmAllZonesList = rwzmGetExistingZoneList(); + + let properties; + let i = 0; + let j = 0; + let rwzmData = {}; + for (i = 0; i < entityIDs.length; i++ ){ + properties = Entities.getEntityProperties(entityIDs[i], ["renderWithZones", "locked", "name", "type"]); + //Identify the unique zone used in renderWithZones properties of the entities and make the list of this in rwzmUsedZonesList + if (properties.renderWithZones.length > 0) { + for (j = 0; j < properties.renderWithZones.length; j++ ){ + if (rwzmUsedZonesList.indexOf(properties.renderWithZones[j]) === -1) { + rwzmUsedZonesList.push(properties.renderWithZones[j]); + } + } + } + //Make the list of entities, with their id, renderWithZones, locked, in rwzmSelectedEntitiesData + rwzmData = { + "id": entityIDs[i], + "name": properties.name, + "type": properties.type, + "renderWithZones": properties.renderWithZones, + "locked": properties.locked + }; + rwzmSelectedEntitiesData.push(rwzmData); + } + } + + if (rwzmOverlayWebWindow === null) { + rwzmOverlayWebWindow = new OverlayWebWindow({ + title: "RenderWithZones Manager", + source: Script.resolvePath("renderWithZonesManager.html"), + width: 1100, + height: 600 + }); + + rwzmOverlayWebWindow.closed.connect(uiHasClosed); + rwzmOverlayWebWindow.webEventReceived.connect(webEventReceiver); + } + + rwzmGenerateUI(highlightedID); +} + +function rwzmGetCheckSum(array) { + let i = 0; + let sum = 0; + let strForm = JSON.stringify(array); + for (i = 0; i < strForm.length; i++) { + sum = sum + strForm.charCodeAt(i); + } + return sum; +} +function uiHasClosed() { + rwzmOverlayWebWindow.closed.disconnect(uiHasClosed); + rwzmOverlayWebWindow.webEventReceived.disconnect(webEventReceiver); + rwzmOverlayWebWindow = null; +} + +function rwzmGenerateUI(highlightedID) { + let canUnlock = Entities.canAdjustLocks(); + let uiContent = ""; + let i = 0; + let k = 0; + let zones = ""; + let name = ""; + let elementClass = ""; + let firstClass = ""; + let isLocked = ""; + let selectionBox = ""; + let setCheck = ""; + let addAction = ""; + let toHighlight = ""; + let viewBtnCaption = ""; + let warning = false; + uiContent = uiContent + '

RenderWithZones Manager


\n'; + if (canUnlock) { + if (enforceLocked) { + setCheck = " checked"; + } else { + setCheck = ""; + } + } + uiContent = uiContent + ' \n'; + uiContent = uiContent + ' \n'; + uiContent = uiContent + ' \n'; + uiContent = uiContent + ' \n'; + uiContent = uiContent + ' \n'; + uiContent = uiContent + ' \n'; + uiContent = uiContent + '
\n'; + if (rwzmUndo.length === 0) { + undoBtnCode = ""; + } else { + undoBtnCode = ''; + } + uiContent = uiContent + '

Visibility Zones:

' + undoBtnCode + '
\n'; + uiContent = uiContent + '
\n'; + uiContent = uiContent + ' \n'; + for (i = 0; i < rwzmUsedZonesList.length; i++ ) { + name = rwzmGetZoneName(rwzmUsedZonesList[i]); + elementClass = "line"; + firstClass = "cells"; + if (name === "") { + name = rwzmGenerateUnidentifiedZoneName(rwzmUsedZonesList[i]); + elementClass = "errorline"; + warning = true; + } + toHighlight = rwzmUsedZonesList[i]; + viewBtnCaption = "View"; + if ( rwzmUsedZonesList[i] === highlightedID) { + toHighlight = ""; + viewBtnCaption = "Hide"; + firstClass = "highlightedCells"; + if (elementClass === "line") { + elementClass = "lineInverted"; + } + } + uiContent = uiContent + ' \n'; + } + uiContent = uiContent + '
'; + uiContent = uiContent + 'ZONESACTIONS (On listed entities)
' + rwzmGetTruncatedString(name, 30) + '
'; + uiContent = uiContent + ''; + uiContent = uiContent + ''; + uiContent = uiContent + '
\n'; + uiContent = uiContent + '
 \n'; + uiContent = uiContent + ' '; + uiContent = uiContent + '

Entities:

Modify locked entities for me.
\n'; + uiContent = uiContent + '
\n'; + uiContent = uiContent + ' \n'; + for (i = 0; i < rwzmSelectedEntitiesData.length; i++ ) { + elementClass = "line"; + firstClass = "cells"; + if (rwzmSelectedEntitiesData[i].renderWithZones.indexOf(highlightedID) !== -1) { + firstClass = "highlightedCells"; + elementClass = "lineInverted"; + } + zones = " "; + if (rwzmSelectedEntitiesData[i].renderWithZones.length > 0) { + for (k = 0; k < rwzmSelectedEntitiesData[i].renderWithZones.length; k++ ) { + name = rwzmGetTruncatedString(rwzmGetZoneName(rwzmSelectedEntitiesData[i].renderWithZones[k]),30); + if (name === "") { + name = rwzmGetTruncatedString(rwzmGenerateUnidentifiedZoneName(rwzmSelectedEntitiesData[i].renderWithZones[k]),30); + } + if ((canUnlock && enforceLocked && rwzmSelectedEntitiesData[i].locked) || !rwzmSelectedEntitiesData[i].locked) { + name = name + " "; + } + + if (k === 0) { + zones = zones + name; + } else { + zones = zones + "
" + name; + } + } + } + isLocked = " "; + selectionBox = " "; + addAction = " "; + if ((canUnlock && enforceLocked && rwzmSelectedEntitiesData[i].locked) || !rwzmSelectedEntitiesData[i].locked) { + addAction = ""; + selectionBox = ''; + } + if (rwzmSelectedEntitiesData[i].locked) { + if (canUnlock) { + isLocked = ""; //Locked + } else { + isLocked = "🛇"; //Forbidden + } + } + uiContent = uiContent + ' \n'; + } + uiContent = uiContent + '
'; + uiContent = uiContent + ''; + uiContent = uiContent + 'ENTITIES'; + uiContent = uiContent + 'RENDER WITH ZONES 
' + selectionBox; + uiContent = uiContent + '' + isLocked + '' + rwzmSelectedEntitiesData[i].type + ' - ' + rwzmGetTruncatedString(rwzmSelectedEntitiesData[i].name, 30) + '' + zones + '' + addAction + '
\n'; + uiContent = uiContent + '
\n'; + uiContent = uiContent + ' \n'; + if (warning) { + uiContent = uiContent + '
WARNING: The "ZONE NOT FOUND" visibility zones might simply not be loaded if too far and small. Please, verify before.
\n'; + } + //Zone selector Add + uiContent = uiContent + '
\n'; + uiContent = uiContent + '

Select the zone to add:

\n'; + for (i = 0; i < rwzmAllZonesList.length; i++ ) { + uiContent = uiContent + "
\n"; + } + uiContent = uiContent + '
\n'; + uiContent = uiContent + '
\n'; + //Zone selector Replace + uiContent = uiContent + '
\n'; + uiContent = uiContent + '

Select the replacement zone:

\n'; + for (i = 0; i < rwzmAllZonesList.length; i++ ) { + uiContent = uiContent + "
\n"; + } + uiContent = uiContent + '
\n'; + uiContent = uiContent + '
\n'; + + Script.setTimeout(function () { + rwzmOverlayWebWindow.emitScriptEvent(uiContent); + }, 300); +} + +function rwzmGetZoneName(id) { + let k = 0; + let name = ""; + for (k = 0; k < rwzmAllZonesList.length; k++) { + if (rwzmAllZonesList[k].id === id) { + name = rwzmAllZonesList[k].name; + break; + } + } + return name; +} + +function rwzmGenerateUnidentifiedZoneName(id) { + let partialID = id.substr(1,8); + return "ZONE NOT FOUND (" + partialID +")"; +} + +function rwzmGetExistingZoneList() { + var center = { "x": 0, "y": 0, "z": 0 }; + var existingZoneIDs = Entities.findEntitiesByType("Zone", center, RWZ_ZONE_SCAN_RADIUS); + var listExistingZones = []; + var thisZone = {}; + var properties; + for (var k = 0; k < existingZoneIDs.length; k++) { + properties = Entities.getEntityProperties(existingZoneIDs[k], ["name"]); + thisZone = { + "id": existingZoneIDs[k], + "name": properties.name + }; + listExistingZones.push(thisZone); + } + listExistingZones.sort(rwzmZoneSortOrder); + return listExistingZones; +} + +function rwzmZoneSortOrder(a, b) { + var nameA = a.name.toUpperCase(); + var nameB = b.name.toUpperCase(); + if (nameA > nameB) { + return 1; + } else if (nameA < nameB) { + return -1; + } + if (a.name > b.name) { + return 1; + } else if (a.name < b.name) { + return -1; + } + return 0; +} + +function rwzmRemoveZoneFromEntity(id, zoneID, forceLocked, highlightedID) { + rwzmUndo = []; + let properties = Entities.getEntityProperties(id, ["renderWithZones", "locked"]); + + let newRenderWithZones = []; + let i = 0; + for (i = 0; i < properties.renderWithZones.length; i++) { + if (properties.renderWithZones[i] !== zoneID) { + newRenderWithZones.push(properties.renderWithZones[i]); + } + } + + if (forceLocked && properties.locked) { + Entities.editEntity(id, {"locked": false}); + Entities.editEntity(id, {"renderWithZones": newRenderWithZones, "locked": properties.locked}); + } else { + Entities.editEntity(id, {"renderWithZones": newRenderWithZones}); + } + rwzmUndo.push({"id": id, "renderWithZones": properties.renderWithZones}); + renderWithZonesManager(rwzmSelectedId, highlightedID); +} + +function rwzmAddZonesToEntities(ids, zoneID, forceLocked, highlightedID) { + rwzmUndo = []; + let k = 0; + let j = 0; + let properties; + let newRenderWithZones = []; + for (k = 0; k < ids.length; k++) { + properties = Entities.getEntityProperties(ids[k], ["renderWithZones", "locked"]); + newRenderWithZones = []; + + for (j = 0; j < properties.renderWithZones.length; j++) { + if (properties.renderWithZones[j] !== zoneID) { + newRenderWithZones.push(properties.renderWithZones[j]); + } + } + newRenderWithZones.push(zoneID); + if (forceLocked && properties.locked) { + Entities.editEntity(ids[k], {"locked": false}); + Entities.editEntity(ids[k], {"renderWithZones": newRenderWithZones, "locked": properties.locked}); + } else { + Entities.editEntity(ids[k], {"renderWithZones": newRenderWithZones}); + } + rwzmUndo.push({"id": ids[k], "renderWithZones": properties.renderWithZones}); + } + renderWithZonesManager(rwzmSelectedId, highlightedID); +} + +function rwzmRemoveZoneFromAllEntities(zoneID, forceLocked, highlightedID) { + rwzmUndo = []; + let k = 0; + let j = 0; + let properties; + let newRenderWithZones = []; + for (k = 0; k < rwzmSelectedId.length; k++) { + properties = Entities.getEntityProperties(rwzmSelectedId[k], ["renderWithZones", "locked"]); + newRenderWithZones = []; + + for (j = 0; j < properties.renderWithZones.length; j++) { + if (properties.renderWithZones[j] !== zoneID) { + newRenderWithZones.push(properties.renderWithZones[j]); + } + } + if (forceLocked && properties.locked) { + Entities.editEntity(rwzmSelectedId[k], {"locked": false}); + Entities.editEntity(rwzmSelectedId[k], {"renderWithZones": newRenderWithZones, "locked": properties.locked}); + } else { + Entities.editEntity(rwzmSelectedId[k], {"renderWithZones": newRenderWithZones}); + } + rwzmUndo.push({"id": rwzmSelectedId[k], "renderWithZones": properties.renderWithZones}); + } + renderWithZonesManager(rwzmSelectedId, highlightedID); +} + +function rwzmReplaceZoneOnAllEntities(targetZoneID, replacementZoneID, forceLocked, highlightedID) { + rwzmUndo = []; + let k = 0; + let j = 0; + let properties; + let newRenderWithZones = []; + for (k = 0; k < rwzmSelectedId.length; k++) { + properties = Entities.getEntityProperties(rwzmSelectedId[k], ["renderWithZones", "locked"]); + newRenderWithZones = []; + + for (j = 0; j < properties.renderWithZones.length; j++) { + if (properties.renderWithZones[j] !== targetZoneID) { + newRenderWithZones.push(properties.renderWithZones[j]); + } else { + newRenderWithZones.push(replacementZoneID); + } + } + if (forceLocked && properties.locked) { + Entities.editEntity(rwzmSelectedId[k], {"locked": false}); + Entities.editEntity(rwzmSelectedId[k], {"renderWithZones": newRenderWithZones, "locked": properties.locked}); + } else { + Entities.editEntity(rwzmSelectedId[k], {"renderWithZones": newRenderWithZones}); + } + rwzmUndo.push({"id": rwzmSelectedId[k], "renderWithZones": properties.renderWithZones}); + } + renderWithZonesManager(rwzmSelectedId, highlightedID); +} + +function rwzmGetTruncatedString(str, max) { + if (str.length > max) { + return str.substr(0, max-1) + "…"; + } else { + return str; + } +} + +function rwzmUndoLastAction(highlightedID) { + let k = 0; + let properties; + let locked; + for (k = 0; k < rwzmUndo.length; k++) { + locked = Entities.getEntityProperties(rwzmUndo[k].id, ["locked"]).locked; + if (locked) { + Entities.editEntity(rwzmUndo[k].id, {"locked": false}); + Entities.editEntity(rwzmUndo[k].id, {"renderWithZones": rwzmUndo[k].renderWithZones, "locked": locked}); + } else { + Entities.editEntity(rwzmUndo[k].id, {"renderWithZones": rwzmUndo[k].renderWithZones}); + } + } + rwzmUndo = []; + renderWithZonesManager(rwzmSelectedId, highlightedID); +} + +function webEventReceiver (message) { + try { + var data = JSON.parse(message); + } catch(e) { + print("renderWithZonesManager.js: Error parsing JSON"); + return; + } + if (data.action === "highlight") { + enforceLocked = data.enforceLocked; + renderWithZonesManager(rwzmSelectedId, data.id); + } else if (data.action === "removeZoneFromEntity") { + enforceLocked = data.enforceLocked; + rwzmRemoveZoneFromEntity(data.id, data.zoneID, data.enforceLocked, data.highlightedID); + } else if (data.action === "addZoneToEntities") { + enforceLocked = data.enforceLocked; + rwzmAddZonesToEntities(data.ids, data.zoneID, data.enforceLocked, data.highlightedID); + } else if (data.action === "refresh") { + enforceLocked = data.enforceLocked; + renderWithZonesManager(rwzmSelectedId, data.highlightedID); + } else if (data.action === "removeZoneOnAllEntities") { + enforceLocked = data.enforceLocked; + rwzmRemoveZoneFromAllEntities(data.zoneID, data.enforceLocked, data.highlightedID); + } else if (data.action === "replaceZoneOnAllEntities") { + enforceLocked = data.enforceLocked; + rwzmReplaceZoneOnAllEntities(data.targetZoneID, data.replacementZoneID, data.enforceLocked, data.highlightedID); + } else if (data.action === "undo") { + enforceLocked = data.enforceLocked; + rwzmUndoLastAction(data.highlightedID); + } +} + diff --git a/scripts/system/create/qml/EditTabView.qml b/scripts/system/create/qml/EditTabView.qml index 96e66c109e..2db23ec659 100644 --- a/scripts/system/create/qml/EditTabView.qml +++ b/scripts/system/create/qml/EditTabView.qml @@ -301,6 +301,22 @@ TabBar { } } + EditTabButton { + title: "IMPORT" + active: true + enabled: true + property string originalUrl: "" + + property Component visualItem: Component { + WebView { + id: advancedImportWebView + url: Qt.resolvedUrl("../importEntities/html/importEntities.html") + enabled: true + blurOnCtrlShift: false + } + } + } + function fromScript(message) { switch (message.method) { case 'selectTab': @@ -333,6 +349,9 @@ TabBar { case 'grid': editTabView.currentIndex = 3; break; + case 'import': + editTabView.currentIndex = 4; + break; default: console.warn('Attempt to switch to invalid tab:', id); } diff --git a/scripts/system/create/qml/EditToolsTabView.qml b/scripts/system/create/qml/EditToolsTabView.qml index 998c3a3aac..1000724458 100644 --- a/scripts/system/create/qml/EditToolsTabView.qml +++ b/scripts/system/create/qml/EditToolsTabView.qml @@ -291,6 +291,22 @@ TabBar { } } + EditTabButton { + title: "IMPORT" + active: true + enabled: true + property string originalUrl: "" + + property Component visualItem: Component { + WebView { + id: advancedImportWebView + url: Qt.resolvedUrl("../importEntities/html/importEntities.html") + enabled: true + blurOnCtrlShift: false + } + } + } + function fromScript(message) { switch (message.method) { case 'selectTab': @@ -304,7 +320,7 @@ TabBar { // Changes the current tab based on tab index or title as input function selectTab(id) { if (typeof id === 'number') { - if (id >= tabIndex.create && id <= tabIndex.grid) { + if (id >= tabIndex.create && id <= tabIndex.import) { editTabView.currentIndex = id; } else { console.warn('Attempt to switch to invalid tab:', id); @@ -320,6 +336,9 @@ TabBar { case 'grid': editTabView.currentIndex = tabIndex.grid; break; + case 'import': + editTabView.currentIndex = tabIndex.import; + break; default: console.warn('Attempt to switch to invalid tab:', id); } diff --git a/scripts/system/emote.js b/scripts/system/emote.js index 6dfd1ae1ef..0d56932b4b 100644 --- a/scripts/system/emote.js +++ b/scripts/system/emote.js @@ -15,6 +15,7 @@ (function() { // BEGIN LOCAL_SCOPE +var controllerStandard = Controller.Standard; var EMOTE_ANIMATIONS = ['Crying', 'Surprised', 'Dancing', 'Cheering', 'Waving', 'Fall', 'Pointing', 'Clapping', 'Sit1', 'Sit2', 'Sit3', 'Love']; @@ -138,22 +139,22 @@ function restoreAnimation() { } // Note peek() so as to not interfere with other mappings. -eventMapping.from(Controller.Standard.LeftPrimaryThumb).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.RightPrimaryThumb).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.RightSecondaryThumb).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.LB).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.LS).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.RY).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.RX).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.LY).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.LX).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.LeftGrip).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.RB).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.RS).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.RightGrip).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.Back).peek().to(restoreAnimation); -eventMapping.from(Controller.Standard.Start).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.LeftPrimaryThumb).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.RightPrimaryThumb).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.LeftSecondaryThumb).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.RightSecondaryThumb).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.LB).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.LS).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.RY).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.RX).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.LY).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.LX).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.LeftGrip).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.RB).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.RS).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.RightGrip).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.Back).peek().to(restoreAnimation); +eventMapping.from(controllerStandard.Start).peek().to(restoreAnimation); button.clicked.connect(onClicked); diff --git a/scripts/system/fingerPaint.js b/scripts/system/fingerPaint.js index 88245503e8..376a60e85d 100644 --- a/scripts/system/fingerPaint.js +++ b/scripts/system/fingerPaint.js @@ -9,6 +9,8 @@ // (function () { + var controllerStandard = Controller.Standard; + var tablet, button, BUTTON_NAME = "PAINT", @@ -334,11 +336,11 @@ leftHand = handController("left"); rightHand = handController("right"); var controllerMapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); - controllerMapping.from(Controller.Standard.LT).to(leftHand.onTriggerPress); - controllerMapping.from(Controller.Standard.LeftGrip).to(leftHand.onGripPress); - controllerMapping.from(Controller.Standard.RT).to(rightHand.onTriggerPress); - controllerMapping.from(Controller.Standard.RightGrip).to(rightHand.onGripPress); - controllerMapping.from(Controller.Standard.B).to(onButtonClicked); + controllerMapping.from(controllerStandard.LT).to(leftHand.onTriggerPress); + controllerMapping.from(controllerStandard.LeftGrip).to(leftHand.onGripPress); + controllerMapping.from(controllerStandard.RT).to(rightHand.onTriggerPress); + controllerMapping.from(controllerStandard.RightGrip).to(rightHand.onGripPress); + controllerMapping.from(controllerStandard.B).to(onButtonClicked); Controller.enableMapping(CONTROLLER_MAPPING_NAME); if (!Settings.getValue("FingerPaintTutorialComplete")) { diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 4a87df5659..27f1ce23f3 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1,10 +1,10 @@ /* // edit-style.css // -// Created by Ryan Huffman on 13 Nov 2014 +// Created by Ryan Huffman on November 13th, 2014 // Copyright 2014 High Fidelity, Inc. // Copyright 2020 Vircadia contributors. -// Copyright 2022 Overte e.V. +// Copyright 2022-2024 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 @@ -701,6 +701,17 @@ div.section-header, hr { align-items: center; } +div.simpleSeparator { + width: 97%; + height: 2px; + border: 0px; + margin-top: 4px; + margin-right: 8px; + margin-left: 8px; + margin-bottom: 4px; + background-color: #777777; +} + .section.minor { margin: 0 21px; box-shadow: 1px -1px 0 rgb(37,37,37); @@ -1304,6 +1315,27 @@ div#grid-section, body#entity-list-body { margin: 0px 8px 8px 8px; } +#voxels-section { + padding-bottom: 0px; + margin: 8px 8px 8px 8px; +} + +#mode-section { + padding: 3px; + height: 28px; + margin: 8px 8px 8px 8px; + color: #000000; + background-color: #999999; + font-family: Raleway-Bold; + font-size: 16px; + border-radius: 5px; +} +#creationModeLabel { + padding-top: 4px; + width: 156px; +} + + #entity-list-header { margin-bottom: 6px; } @@ -1433,6 +1465,11 @@ input[type=button].entity-list-menutitle { background: linear-gradient(#343434 30%, #000 100%); cursor: pointer; } + +#voxel-edit-mode { + width: 120px; +} + input[type=button].entity-list-menutitle:enabled:hover { background: linear-gradient(#000, #000); border: none; @@ -1743,6 +1780,7 @@ input#property-scale-button-reset { display: none; position: fixed; color: #000000; + font-family: FiraSans-SemiBold; background-color: #afafaf; padding: 5px 0 5px 0; cursor: default; @@ -1762,7 +1800,7 @@ input#property-scale-button-reset { padding: 0 0; } .context-menu li.disabled { - color: #333333; + color: #777777; } .context-menu li.separator:hover, .context-menu li.disabled:hover { background-color: #afafaf; @@ -2080,10 +2118,10 @@ div.entity-list-menu { div.tools-select-menu { position: relative; display: none; - width: 370px; + width: 200px; height: 0px; - top: 0px; - left: 8px; + top: -16px; + left: 124px; right: 0; bottom: 0; border-style: solid; @@ -2095,20 +2133,19 @@ div.tools-select-menu { } div.tools-help-popup { - font-family: FiraSans-SemiBold; - font-size: 15px; + font-family: Raleway-SemiBold; + font-size: 14px; position: relative; display: none; - width: 690px; + width: 96%; + padding: 5px; height: auto; - top: 0px; + top: -16px; left: 8px; right: 0; bottom: 0; - border-style: solid; - border-color: #505050; - border-width: 1px; - background-color: #404040; + border: 2px solid #c0c0c0; + background-color: #333333; z-index: 2; cursor: pointer; } diff --git a/scripts/system/html/gridControls.html b/scripts/system/html/gridControls.html index f752ec4f2a..e8cb643e46 100644 --- a/scripts/system/html/gridControls.html +++ b/scripts/system/html/gridControls.html @@ -1,9 +1,9 @@ 4x=8A9!;fMk{NmmgA?Lro97MYy6noDf~G&{}=Zc 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

s0S;kMbc9Y?(IW*=c!raS4zA(VRl4x}@ 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≈@8J6zmeuj=h8G{#UC7KXRqiEZ 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 z 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�FQ6?*L7Phb80 z{$Rp;NbA^rWuOtXA5Zx~2S|I+9L5DE!wKSPC*!jDMTG%k!;{Ylw^gE^;5B%l$1~HN zpi})NI318$UAn;*lCNdMQ6HzOUY&8tJpC13b?dK)Q2idN!*ek7`&eP5;m?{2feD=7 zTNo$eG7@ZDzvy!SLnSExHU5qw7=5^ycf=oIIa{Xy&6nL zcq8DJ>z&9iY?n*Z4O{Elp2P`0;d;%wFLQ6fFzhOH7nvDqR2vjgHnXnqcG|EuxU@0u z0pL6blv$%zR@f&G^wnY_nF-L!D>qOWpv+BN1D4S--6L6-iL%a{wDQJSOHyt1;C16lt`&FAo z4^{8)*fw$JdY5k_=m3d_D6dZw*9PvWAACZ^1_alaH)p(ee$tlvS{xF(jDb+Q#}916 zLJr}wm!qmo`j$;v@9Xg14^8NPjkE_*m}xJU$D|ZJluq@)Q!0r(wJ%Se8bSHwhkxkW z1GlumjGZeLICxQ373S_+Etq-C95(Z8qUn$fiC9-quJ+=r)jezW^`r_{O9J^)tdDrx9mhRH>9Fu0Ku9XL78PqWNi%#qibvI4q$o zOM=jnKE#9u#Ohhy53Bfh9NAwj&p(6?Be zNP3gHEJp`K{Pbn1RrR$HbnvM_4nJDA0H@m{-Jg@r4vVya6D)rVrP)*Fzwk%96kzms z#(@z|bO30iKMR&H_G4I4c-+c72E}ygs+0A#sPBTX29E__Sno-11|1;n50|B&q+Kti zIynm#jA4NBC4$8QSBg_{mxb=%4?RmpbykrS)}qRYI*B{ff6wYzp43!a=W1r%=p9I$ z=dBm}$@C9rc46|M`ldm?J^A8D#VaqCiFEs{-O_SAXKd`b#XMOYT#ODFHVZ*XNzlxO zh;RWtv4Jq8=PhSLTV>s<=NJ4hyye)y!Ji`ZJy?QP!}Jb?Z*Df#-8TeXH-DwkEv@=Z zsKBwxX@CaDl8_ma2V3ZtvajtKvzp`b%U~^C^S{eMm{C+`D0Q7W(?VJ!LgNWsY?5y( z+)3R|!pVlRk_P2}D<>^a&1yg=HY6R=S1l4O!P|dCLP_IQgk<@6EAU2_Y!Wn8xBC3P zH4?q@|9Xw#s2poQR?c&_ml%uIix4UixMNh_n{8)3e?94?zyVLzfbh6|Gx`8)Lt4z4 z&26mqxhk`<{OEb$GOLhqLLmqtzycu+@2HhH$Sq9{l3!mPFzMq1*FQ23 zuZ8tt$r&fwxpaWy76rNLBaX1MGhoc9H?(+Eop8gs4R+xHcdtp^SOX3YLs)NE?`2rg z0V?6s9^L_c^}!MoFG@azXQmpz==h-DptOv^wD%H20wR=HYzCfXt`;~fR)(G3&p^{B zPc%N;;)r%k+}e1{RfFVVVcE}`p0#vgy zIP^l;xjA46HtU%kv-U*z!o_;i&8 -Au*Mh`YW4VLu$(S?7r$Uu{kG(WN71ez1= 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≷^du6Px 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

?|5G)cjB(hG^%feh9h$d>y$6;OQjY)Q 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

    etqic-m9B_qK zEVQ2q0A2+&AFY3>o--!QIuOX!R5keYbQTAj&(&~l~tu&SRSr)E0^ z{%L;W*s#56TKFcM-FH7CFHk7lz8B;RUE5tf%G?f(r2`({aR1^SM3fSvejS4sN23Q+ zeqK^vy|)s6H_WI5jq`h=qT@gm)eP3bS=i+Zev5lF28m)JVv7E0AT<87|I6nV{8VQ+@>5v+b zw;ZZo%+gwNNiY<>pyFYPf}4MhQ$Vk#()!y+!3qb+`|v5J(Ao+d_uPf^N0LB6f%^6N z7%Vv-XN^p0#}TmoScQ#TTk`t>f2z1GXQoR|L;gY4N{8t43%zop&X}$hGr3#nnfBd6 z-4c>Ce7QS0OmEiBVTiBkQN`DZ=J{?plfv$fTIlz@EC)LuDjq8bKW|mvoaBT5_q4C9 z7|SL&c{la(6%+5#z2|BZ`Q_(3Aczmtxq)q}c>cz$OGlaQpt*UZPJQEhH8p~Lnwr|u zmib~K(4}>Y*`LsIE&gsxlGUxsPm@n1^%-D*pQvSwGMNrYxG$xQF7yCrG>+nl%tFqu zB=uiC@jGwG9$5O#OmZ{;@XA(bK%j4rtnWA;5We2fsqz{pH*4SiL|TOawV(2rukKW@Z{LCCh#` z^Rm2b5QM7GtBtGFIUnGA2x(0=MYS8By`lq*X?>IJ(Z2b^>`m8_lr7n0hDpV(1GFil z1Gc+2rt1K5+@KSAGM@b72>%KfCY$nC?u*h~-zK2y%EN2Q>0cP4%kPyY?%`nKbik0t zqI;+QH1`14b_9#BY_HHb;nUlu>&6>hfZi~Fp_S5UDX731=)4mq8mlVP zt4t__8=v_-B5yyPK2|OFgx`91eVRildd2qyB=8V?v&B&_0OxQ*Enbb6q&D6H%LHT? zWd7uUa{TRM5dFa&-dp(ki)3%)641Wn?71P)SX8^RrFb$nOCeu6`u1g*0XpEmCS%+` z^a_&N3zM9KKO) zw$OW(pTzp*NPs}6sIq6*K!e)>K2mUQ9aY$P6{`4amJao_=;K?3FC@VHcuV;vswUPG zZ@KuYHFwSI&ejbL+eb|O9&k2BuTgiS$!ZWShYrZ@AsY^_PG<(}4)O8ZziPfI+*q{A znFc*!mew07W4Ac(^Nx20eK1EpKWsuh>WyDpkYy6yrx-u1OO=+94!)Oi<_#3Ab*42} z4EHTPnBC;a%x&^Sf3N!GpohMYHfR=monM?`JaKbJ9tTrK4jFhf&LW~Y2~z3dULke| zrSGBJ?3M@56UM3Gf~Q`!7Ic6xK8DJQaChjS<97a za~v7-2j{Rr)m>|gA27UZYqxvq}|y>$l)5F1OGEv}8(kB^{7f^MqRB9QLxu?Qn`{5$7xu z)bFZcAHAB7;-NNzsL(TCZgQtMMn#txKVacT>#LmaIGyLz@5#wDAbBM{s(}bR{26~u zDBKQmY>q~lQ;`}J&G^szKiyh%a_h9UbhF0y6F(b*&OLx&ewECif5%e?!FN5+Y@3>B zW8@z3pfQYBXWNS_#5$|!mUHN4k4#B18w;K|P`{+|FMjvXSbq{p)tbVeXIKA@Q`foS z;>8JXqm6F=jUQrIRd5}ubCk@QQXh{$*$CbFq{7{7woveiGvLjk+S;KJ&Fa4;Awk2E zi#o$7(-{FQ`g&1Dl#8pJ>9Y^?`2)+K8&&RFxot>;As{H|U%t_vaB@d^GS5Ywra>tV?7fT)jT$);9`yZy2^!7g< zHCAW9!K4sQ>x+R{8m^v#O&1Z=O1n~!$quxA9bv}yCb@;lQ_mvv5N9<^TSb1xNQG{$ zrcb}{x)w5r_Z3XmbotaChncApJ6-bX-86t4%FqGX$HJXiKAwbiZquB1!tVmhB5a-t zp}pVa%6GfduNa_O=h%Q&w!64>jlj6_ZqqUd=YD2%CaU zI))J&t60|@TK8~7d(|5B7XqpK`GJ60l#4JE`h2sKFErB%9vi?qSmlGCRhJ3oX!1L% z!w3AVvhqK07V-J;-o!Ez80Jgd2Afhu5}zmc)rl`|=nohNWhZGa$1P?km9JP})DTmk zy(MZ-UcWe4whK=EUh$)r#Hm`KMZO{a*3lwK;I+7!DCgmulVwaH_Q5_G zTQ^5u22TUk&K%Ah*Rk1|Xm~DleE%#x(B~kUteOXv%=u1mVB^u8F=UC z7T7IR0X?kTP%%i=Cm`>x^QG>|HNYjNW?lY-eRfQ`U3{Q>nvhg>zB^?bh1F7Sc-7h+ z?Gq-9ef*8s{q!`6@#cPJ-cP4WbBnfsR(;p=3Aw5uyh-0A_APZB7-~&qQ5>gmpZRIy zgH;ADiHu4RC$4_Iro3WO@$p@#?CQXk%^4?59r(dr;|Gf@$^}*JUOc@kFcZ)tNDq0p zxnu>X10C@FUNKlYhuD#~IJlF906K{HaGY$SI$^YLw8~aW)Mu{ofmdC=fp-%7ilmY6 z@5S1IZk~vD=z={{S)ZUfO`4~RM(BV?^?AK{s%&Sr3|^#H|Q)TWtb?RwU7?&Y?e1Ksu~nPX z2$(}keP@rCAgf!LE?5)-`s}}1E?XViK`5_@SYqIGuB@jIKE7`<;%w(J+Z;4o%+aH` zr)(eWWylh|S0;lqp#vzXO~zD1UjpkeQrIXhor5#TGV`C_F%lZ2TKM%^bsQo5%;ujH zri|7?UHJmv_S)Hh|4_#KY4!U2>x7B-JZ_K0<>t zg>N{j3#O8rdET3H``3vHc7pI&Ibj%{6>lr0L<%a)Oild=-~R2?UoLo@U%~S$Vxo+s zyMn3(Q~N{%SyI1#{r{yfOgDmt3ux2E!+GLVMXPa zes%QDnn|1^`&ET*QzBs?u>2Z#YS8jNk2G`u0jD*L(E$?5yDmh}ABjBv79W1>Oqey@ z#LA_fP;Jt|&4{)&+#G($yOT6s4nVjX<7l-{WDrZ zE?Q?>Nb)q5u>DBlN+~sd8`oT+PxWVMNKcjtQ^NZTO|{xr%9&RRDb|46SzDw{!>LC& zEBnad{9)faTR(2day9&- z{5C-fa`7>Sa$Ooqnm=woUQ5q<+8p?tc+#xx$Kz5v{u`?ie9QBGf*i)A&O z44?xP)oL}JB|jaR5BJW!Z}v9TxGiKc_V(W$Na7}6=2W_(WK)i$LAwofd=OP8o6&bW zVdABMoyTc8s=epz6$U?>w;IfB?>pA#v)UG>ezW;tXAKOM-ZJJ#ZzPD>w@X785b(*> z`ZmsZcV9_#Q?a00(I~N$$-Efswv5SKrTs{P$=WY&inCOQ7uRYYFy4NA>zy|O5@D%l&uBA~ zw8GU`%c)y3;+`{7W>dy7n`}?7nts2#eoi9%2VcXUxuYFKnX;7G0H*eEXV5mTUY&(b znJtnaCTAc0H_*O(HDsU}^7*-t04l!L1A$(cg&s z6s*MiCnX36dp&2Ezb@A#Rov0hktL<-vGSJty3Xu}`b|`)FD-Fw+J-ho;Uiwl<3^ye zjE^%z!OZiTVoBPoN~4L_kC?P((CgdY8x6MpfBjY-^(aQLi)|cX9OsuYACa=)`*Z!+ z(ibm zefPo>?R1bi<3}h)SD{^0XEEhcx)%W}*F#h&d=b6u>C>RIdLU%!`Yc%HxvvPd995Gy zh<#8>*iCOx(KdTD3@GGplzB4{|NXq*Ut)2))(n~uKe%mqqCw$I3oZ8*bf3Le1^O}l z)@*dg!NT1saW>q>NZGmiXXu^8}?}reD4o_qwoWH#<<=V? zriKW%JC}HVd7`in=m15L_$M?nc_@+~wBOm@*iB{*x|9_4>XzIeeQ~ysa~{D?m@$<{ z6yBr$#2MjktzX~Ba=#m#YCen{xYb#jP3Um&4 z```a|SuWQknLrbE@m)Z8&uUa=DCSI=!b!03y_ZwzKK1Hy{oi^?*LJgPtQ-1e$Tf{n z2!#%y5f|o1bkp%D1Ik z^U|(Y)i%gDYx_?&;FPKz<dF!gGJ9f!Jk@wtgdxotd6 z)Md;$%KgMr55>vica3sKL0|QyQLdkE-q1YVGoSYVa~N+2q4LoIj~n1;Y?R?N8DoJ5 z4F{O3b$=1y4e3F)f?jQ%aKCT7J6fPqYg#$wYplO1xuj6ym7QAJXcNOzgxlnOt)BSv zT6~St&{lhDyk;8FUQ)`!5WZ=rCCGh6z`pCXgWAhrzFbgGz-^Zahhy%;IAc9`8^(_j zSH<yjb+|7m_cu8%f#4hs0`|(0)$J z464~u?GG&D78k+~%$_FnNsxjobmq%gjljx~qmIsU;p6#?=e8M#T3=nSZv247scAl8 zztcDu?1Yh|O_9?&*5dEmz-{|mQ_@;fJIhNyOq{QODfof#JcCzhwBQ<}caJlwUHr^1 zUFY;J)w=Y|e$&)OKJ)&8m}}j5(ldh+F}(U+sXd8UwsM)BA;mk8BcqZt=Y!A&N0v%f z4}Pl#C)=Z`b*Bq|OSd@y&^ch}B()fWfWe2MKK-7QJG0wUS-x^tIBe!nJ8p{a?~7a@dUaQpOYo3NjIMPqLV`fxcnzHbPG+wkB6aWHfDMdwlF zJwo61Z|6Hf{rx5CPuw9NQ_}9GvN&jTHG&cm&!fHCKf_&Cca6?Mauh8buiW{i&g;sr z*u?fGo-YFjdrV#4?1NF_VW|k)#_3aNQ{?D`nf`}+Fb5RPc3$ik!#pYw2YZAZ5 zkl-iG-LWsnG$oV|Dt{X(xfPmDWYHMrm}a$|fviPfFFSJ;zLi^61$0MMgD9JPGVWZi zrM_MS_B``RpPq8%w6$rb+ct?}>=xzTg=LIsAnFl?kHS2_OoZKYdo5?|%v)O_oSpeR zD$AJjF1N4vy#IyF{VHGR0>Yz-4q)tY_PO)5`ZKpArhuJqp}AN!iaU7@_~z_A5hLb4 zH9MV+lq!rM&l0yQUxet@;o-N%`H?qx)L?!_AX*je8~Lz!6*kl}f|cuy#|Lkj^u4$X zSrN>SO%qS!N$tDW@T|})4s<$3xfWlj{iQKF!0OkNb5{({TQEHkSr^Eiz*%vo(+Yi9 zN>VwBAUpM%EZhdDvDV|b(X@?b8^6*xP%7dT!ur>lCp>6dX7w%g-8ZuPYfiW45zILe zz4(b#*_Gx^6ovv#ueu+wvE^-Gva~RRySbQ5l-327O09P;L>)+}`>!;{B6TH;}!=*`_Q)(@E8F#7X@V7KMp8XbmW283U%MmU?<@5}S7+0TU6n92XDWKYgrCDt!i* zl_wd^peZlNyFFD@?QN<+M2@Ht=;R3R;1NX; z>92vu3LfOs6iUne+y~FZUXRXFYTPggmq|uT5 zjRy?((mq@+w3z)MbMarIT1n0xbTYFsm2fJq*Fe_Hl$>{Wol4Z*7*DW7N6s^iBO-f% zZ;-362||1+hX~KQ7Xf3{)j9RWPIh;F_pUISJ-PE9`2rZah9tmbW~sLOKFL0O=5C?g z5wqgak1NaX{O~aMD|TC{61@aE7SyE!vf~>&C+g{d{z1zL_u$pTozX95a_DZtdj#7Z z@$*fyr7s~n2tC^9*p47f)C0;sWfl~nqjs+>d)=n~SIW(%aGZlDm`Io}^O9+M9i@4Y zxj3aOIIs07qVw~-Pw&G6@ReKW$^SHXaG&1U3tVpwY!+?%Di4AFvbrv>f~CPs@i$Q= z$JaWQ$IHSTSd=wO@OPB&i>wc9O>iT|-qwQ&PMCPwe;Z_}!k&sYr!7x5WHl>)^QzuP z6j+wuaj5&4QYN#~T@keIiE1yS@>ye?@7olv$=XZ?nAC$g?$mbp|*|cG&rS3Xk#HH*#vbdt5E!A}!k;81^gc*$Q zs}fi_^F8UO12SM)$L|WwM(=&l&ysr=`f$mJRdc7d?rktjRu?dgfeuhGqy|=b@}>Nx zMw++6s^{kyzu&>Ps#iVYj28PFZ`dPTto_p$I!y=sJ~h^BPTU`~{=D-{yXE7~{Lqio ze`>yeb7>z%fN#PneA&veudAr>wso1CcoU(IN$QXBq@Ve17w0qM!_`P#Kt1py?AqP> zUD0)SqHe%Z!(`Dt5!?6(oeq6}Ay=D`v5&7e?Q5ao@ZG+qr3|TpGgs>OS*5m{_lsTA zb_~7~J_4WeHGbj@BsHV=p<1+w@dNvFV-wvDPf?(J)2HfhDEuRPm!f`719g?VFmAaT zUnixOTh~gSyzG7s(l@hyBMu5INasJA?WnaBrZOTui5<+)3Cme=AG4jnq&Lj3Ql3_# zJc6r00G{rD=qnWM%-`foIhC7=1R5eO&t2$HOOEy1>{HI8XBO#Ke`dacc7mw8cx<|Z zKOK{fR3g}^l0pul)Y((wqP$p3;IIbgOta!X$3YXHwy!m%Eu}9Z`|kA=-aKXp zt&!fNk7WYRe+&5~ZhOuriSOD$R`}8oM&rs=zQ{5vYRYx`0Npm1wXrI6Z0x=4U0Ts# z{?_3`GrD0|aE5wYmQQ|tYS&+gEHQ=_b9G6rFjRi}Nrz90fP>vSgCaCZ;B4e)gg0%M z_A{1C-?o`kt6p~h<0l60z&;!EHO9+oe zmR>P#FI1-@9gy8M9tajh*w4VxMsF-1GRNgy$rXDFSsmZXTR;9$`R9Og(Ba@UuP6C-&?lW^%*nI})B}(@yn@W9~ zH`>40v{yY@0_sApi8L64;jP5seut;lTZI$e{HS#kCYx`x=SlVX6<^I;&I2_Z5E^j# zGv6Na36vsNrDUt|U435Xv7N$if}6Y96XjInz#|;&$=MSsB(Q_ekkW|u=t}MA?-K-6 z9RxXU)IneMM6?Bd*+7-%<+Uy}B?W8?4vcPKivnX*ET@KUdh1xoO9;SiQvcnsx(Vw7 zzTr#;OL$=)(gE^LG-`WGNSKmNU6f(d$51w8>3ISMQ1xaQ60<_>-Ih(OnKc# zBvEF)y57g~`GmKW_OXn+kg!GB4y3>7#>_G%y}dmhMu`Gv95y+4cBEUA1gwzJO}jW@H#XA&gVKB(Z%7}%!uoO)(2Oz^123ccW*_JlF%{jEsnl_KYSuo9&0sR`S;slZ4o%V`twmx? z8l+_#hQFxaEAWlg^Vu^m=dmsMhyKH2Y!FG7ADV@((E)jq4w48u0AkOv*uQ|jBlpC> zZQb?{0K)+a_Ez>l@AFYYhSPcVRjnz-pVKz?Cl9J~CH@pjmHm!4RkgSp3Q#~lnrv9# zuHUEJ5!qzw^@#-I0B6J^?G(CbLYk?Ix#K*`9=TORI#tn*2Xa z2HqL;AB-^EXMoq44#-YyoubhJezyH*?didgSnb_EPpw9jLmvKhBnc#%9S8yudG>}c ze4Oeorr3!ctb97hAbDu5`Y0rz(}E@Nm7fmeS2eRuy5XG|&k$2^hrF4FElVE!FA zH=Pd1GS{UdiNDr(pN|xV@8A={JF6FyALXuw+HFkiDWLbc$uRP?KCRu*iJRMzC2ES; z9NjF%KQ1lIz+8x z85P{8$e0Y44@6r~gvq)W+in9d5U&}Nv^r+g8Si4_Lm_gsQQFi9RC zn}3zLPsqK)@srQpXHTe3m|0J{%0!ZS(z+;EN$V?w6=>fGLD4=%a|-+}OxcWKK~>HJv+c|+zm zstY+8C+=CZ#o@V=4?9I9O`G|BP#q7!LL_hsSN1F&K-mA_$z8`jZq<8b`L770m|m!v z1s~vB185M{A#X_QBc~HRm#F3%69QS5UvuyJJNLoSBRXYE1hxhtC(Mu!6_3Q9Y}*ce zJ@X+Q4#Ry2<&}i`aWSjiyGevF7beC2{-O(e!mZ1gu~so8YEjKj_Ty5h10rgj0b%b zkVB71jHKgdK4Xph3kKt$j^4jIqdE}N+W*=@HufZEHHvbs8Q68t;E&bIQBfCmhr(~U zSx{hT3mx!SiCW4@z$C3vUr6*;qa zw!yEa_EKKQ#U=N0Xq$Ba60IT@bmo8|;B1M?-S#@x)Wv&oLr#*h?P?d9e^LMe`7-&` zWlSbk6wDup=|fv@wbxeY%!d6uGS`0DulxSCLV;FSz61lfUk(RjI1BZpz+;9oy?Ths z4`(-T*xP)6!EUr<5w}CUZ^o{&qIRxt%X4BGGZN-$G6B_Z9}UI&3wKog^v%eA5L>+x z_ocw~G*SJ#Qcip*WDA+3s`r(A7V~g}DDA%?k?wZ!LglSv=U{Jn^lOzN(2s*%*)AgA zqgmWzPks3PS-3&eyMo1q$H5hffcU@7JC_yEfH68imF0-&NNXjZ)(yKhoztGsA+Y=P z;-2aud%qmRUP59JZx9X}tsSW95m0Ua1xp*mp z^Su49&EuisfT?61ClqJyuTBiNg5WDUfb9cG$Ee5&Q%*}Hlr_|qQt?AI`n+GAGv~5C zJe4a1N9&OZfvXN^Pb^PUY1!GPDa&SNbb(^#lfC=fGP2f ze4lMbXgw630dzoGAH&r^wI=qOM-9~jV5Ahn3hu5RV8e$(VhTUTh?q)hi zh)8&od9G;O>-fY8Gq^(gZCw+yQ&0^*Y{LW|VGSOM%B z|H=qhycp6rwKeNyb|HlmD%6h1m_a9qZDf*J#xl@{mxjhvZ2R+y9B1|CUN^5@FGyN{ zd|lhw=%55dQMv8hTJy1Xe+?og9 zxlmbIA5;dKgr)Z3MP*^V+pmqu>N=66J!^e|@@om&uS(QQv0O~hLsmP;XXKFlnH3_I z9M+U^(Bd$4w!>=qCNcKv*NVE{5A8Z4rOn6E79|w&5&4ATL4!LRr~Ymu?^f0wt93sQ ziGXuD*8-KBW5cVYq3H-Da!At}Iiy08vy|VHoNSo<)JR+uu++WaRzZV4W0aixc^`CS z+{-G3NuyOlKJ$;%o}xiOSnHc&#E zh-Cl4k*dD%qq`WT@ane(mxakE$N^(^XoW;Oy6Vgh0oy!>RJgS|zWpV8g!!|D;&JJj z^rhcDExZcoi?jmT-~{%*tRc8RbE1H`?`O^M)n|JLlA1g$ubHx%9xD7-e5L~)A~MM1 zXP+AyIgdjVWyl|+e(IO=7b{LQ7=ftgDNAIJU5bXm7;Segrl^)nqXfeM0z^E2-=zWK zB47LQ5G%NXYCj@VhW8Lor>={X$uFPpsqGs1|GJ!d#B+luo_0`ih=VyIY+-J#ymeKJ z9*2pNUpv~K@Sj8FCY3?9f6B{Wc%AqG)Z0#>#?@{(n~b;$3${1LUU=wrptT$?cE0{% z=YwD;j9yyzoaNVgiv6&Zk*6Qe?J^KY_FLZHT>qRfY1lR4%pQEMdKQJzuq9l|OGUP^ zo$cLfx65gl&P5$zLk8+JFigj1n=!45MktdmuRpL#DyreHG0~cfZBnVVWFTxRsRS=h z4_R)K=Inpm4|}EaG4+{~QtVJyyoY5ox)1xHdb_=?Oe%BW$GmV`-}gIA^DV!{9g1G_ zsz^hJ9V}>t&Ht_W5eyw5Lp$}JH7fBDJmnTMx?c`kxF+%~Q}N%yHmWm@(nEAervnr= zsUd-{iFKSC-A%0=L1_Mm22Pi>Kh-WJ#~Cq>NJHr(#5~F;Y*tgIPqrjhBy{_#?uARj ze<++wbkbQ@$KFFPF{<@DsDy#($MUx)UPD*iZEF6ckA0l z5Y*ipzct5`&=4uh0O=KIS&1lnXly=gYSg z%yG~p6)T&6d>+YY6iMEixw8Is>$3Zf@1Ul$2Hkm{| zlzL89vKQ_cN{d#R*X2--7e!)ywNRld+4OOzA``_t>@ z!>T5YNmH(wi;LtP0d`GSA<+{5w#ebRw&x?lsXqJY7^Rt#Xl+Jmqht`x;@>R}Aos)r zoI@dzudfZB*dXdX%c7;32zV=Ib!y4>6%eemTjO}hFb52cr?SuikEP`5{MTRta2)r3 z`n1&68su|DLf3VjML940Ls6jvLhm>*7)x6gGDFzU!l3lILSO&TNi zSR{hq)O7#E-{Ov9jMfI4?_C0xfKKj{4UH)M{v=LgpTHtgf$05n{hXvROyv)!g)5-m zcCDprYoU;R`2^W3?eB}`H&|RA)V1s*s)W?UA1cPjd!W)8Qx*MqZ=rc6>41mrJyKbM zWP4&d(~N{4c&ZB+w@io?R14t^eIoL0xl;Sg&(mfy0Mjq^U^tMkkSOw%4p6VxAb~6N zP_O9##ed6~2wMB$naem*szHk1L8dX+x+u8vyLx*Sh{EvpM42N$vF+>o4OHhM1xl11 zie^mN3Ru5s{z3b=YTWLo)#%a*RrMt&p}%x%xg3)bngh;}Jwe9?Z%3uE-P+}nFTG+T zrjw*4+4KXbfYE}*0N>C7G(uErAd=d9FOs~{TT|e6W5GIR!LCeTc2;LHVVGTW`B)FK8I(_wrM~?8MY&K^_6qt6_Per z4N%5e4~T;mwQ5$(Q15qXk2~^4%oeF%DmF0ZQn4*Bwu$Nl(Z*=Kw5TcUz0mP;gg?U4 z!7s*2%s+&&%=yDZ9z=s6ko4v8gM7^0rclW3!n5br@Ya47*x;+bZLJ5HI@J@s+2^xd zqodg+wB_fP{}?r+*D-zZa{8+xP2`<)k7+Ku2!glQWC?tjLq{kEC^N9W0a&{a{@a)amQ!6U0e>SLP_3@G^ z*dfUZb0ea*vDKS_8P!zE{nsPHHohX{D&#OKDKy5PXe{B*T0K;wFLA^8&0U?@+l#Ww zZ!J$Fg`pGd!k77-Fe%EB#BFFlUxp%vFc@h!Z|6RpL=Nefz}(pqDPr)rbNQS2f`tW& z%L$`KMkacP_MlmZwmC|noSfxlhPMEHERX_CC-~)6ov{W!O;1!g^}P02`$^SC=5W^i z;wW3iIob9)Ezi5*#R+PY<;&mL?wEwi@Q~13QV%_9sj9QPMxD*J8N~>G^OV}c?l-=s zheJodNHMS{S!lcinthRsqN0H?h5|)%`0Qt*EWTDpnryeBB6EAh#K4tN}!$0u$ zZA`!MN0#!g3DaE-6xNsykfQ@sifGN#G|X!3eLe>t=|SOksFC%1?H@%2s0RJ}U3HcP z85R1}KY>KBC^D+Or=*8_-nw`|-g3kOFEDV6PdDfyiJ@F(eikESGjp1^){#TtUrf~34tEBCdipy z_tv0RsNrDh$RP!4)PQ&e95o9w(q|oSO-cIkM9jzgPLVrzCZCbxt)v{z&-2TbNBt(G zGZ+m|;VrG$jFwN6{M~e1m5})4!}4qa(j?h1yjW?=7dklVmWln0RQ^L7qAfI|en}P| z$0tiV31|k24nGJ~iu&zgTg5)!2s(XAxm4@JgM!WW)>i%W4ZkbPn5qVx`@+w)YhMc` zFcK|}7h)G1sMxhI;6AGYT=M>sUeYDD*`;5%*(2-#5ESMf`??Vbhh1YqN=trdBf@0X zrwE~S#4v9qw3VueXUONDj^V`&=Hg$Q-r5}DU{4VNaKGg_-AUzGs*Rq<;XYDVXe#lbNX0K*wZb^K6;f{xeAMBmC zw(33(CT!*QAB*XNEA~WYjOB=^kL=hudNN2}?M3TF>`Q*njHx}iZ54onxu1n@rXlQU z)#-zZEWg~g4CKD*bDeW}ns~nKQi?FM&up6$MvxB3eouLtm!9rmK${^O-z4mUW?M;? zWtX*@(%51x&pQLpmy>kA;9z=BR7!Ik$Au;vx-;>nu^rrQ0O^OLhAL0mpo@7DkQG&P zibz^$D)oEV>h^u7*{ZZW&MMa@`|m{I648d8H>daJfNzf0uGLmhyUzTW`*JPDBut-? z38nwCrem9X7qO!6LbblTsmd}Yvt2IbnMAzzkTW}Tni)v?ELjy>mlZ4`!e8;V`t&e9IM`40hG<&Je}ST|%0y%%ipqv$gXzHLCq)=&mntvOC;s!VoL_%FOMnzth@X zh4J5VT^+ZryjIcOvi(-k9^!_UN(IIbSfqJfXh0u=h4z}J?p)wK z3zQ~BkQW8hoX#=iM6W+GzYh7Eub3wv7qshf#AR_?_qrJ46>-+gKYE#545~2ipWf!B zqbs?}0U7t&r;q#+=C{5rg3HC$EQ;_ND^l05@s75DD3krP*EiHAj7 z^9A_W4Uwcw2W$}YVWX(5-|}B$OUrJ5+k958aZvZMHszV&3R|qjpJ}SOniA#^HIx$_ zMdOJv`gRYEs=>@YRAV;KWGp`41mb+l)L_m%))v|tRJHiY-5#xRW5*wAr%YA7atm&2 zAcpRmrwNgr;=F=f=WgqGmdds*TbwaQAq6NI&(mJCe)fhHAzU+*r(+3j3bFM9%@5QW zNNfMmcGX$hg_{Vr2j-Eazoh=h)RL)^Z@wP}kAFmP1?g4W14gGFRB>1q%I2i^<{FS<`Ubh;II?4h)8tQ&4pabikO#BJG8X zgH2HI%J9VEz=Z=t(?L1kdmjChtmw012sk`!yP^*Kt+}pzWjV}Ma#Re#lG8M2sKHwz~4-W9 zANRqRHkilg0P#e51k!SGNhLVHJY{J>!~U%uRP-Us9+NjLEr@?5b0Z+X`Jp^V>C|4K zR`6Z=%kU$kG*CdRpWJTN-q zZ`HDvJ-BCwkx0ICF>PrmQ{pW|r}~i{R^4bAD?_<3P*Y$#Ro2xLZSg!#F9H(#(myHD zc3n6^$Vs+a5o})p8aTvK`oYq_a^zYHo_BdXU0-TG8|^P^B1m2=x-5+T@8*>lV{eF; z@Me!6wX-E|Kn)&Zt3Nzjk})}Q(ZD^GJ?Vl`Q$s{#xlyk-)WvT-{^e-W7H2@7z{e2F z)tmoyK@EY`Qbn^h227jmi7!=!GOBBM>u77IzU>byoNGo7%S8{0iCEZ&ZQxw0Lfzr59PZ_)HEE0@;=bmP)3v*@o}Ss5D-r8w7ODw{c>R-B5C z7hM0mhioVy13yrg+uBGQ$u0t$EtB5*KWK&`EVRB`QPYi`nW+(@g>{^-*4e3I& zAkBQyuUN9~WBh(jM`P@|H!I)|x)`2|$Igs0lJ&?iw=k~wk6e8|xX<6dq93XPR+8_5 zjxGQV-9rnnWHuR1Z2Cx`_Z*HKevM1b__W4b;A=yMr))mG1nySBA|C28aHOsN?*Cee zjjarYkmlA;3WdnLL{tGavvi^I*RJwcA*qtuuXtk~s6grEB7pw|pcXBZB0h>JL}YU^ z6CCq)JqTGh7q=wh#uQYtB-KNuiY5l+?p7(=VT*jdDE-VJnoS>1E+Ouk)acPP3xdt_ zoG~-{X*UCwyOa%ZQ+XfVF-JBURz~E!ptj6P_O&!fM7uo{d9MCN=w?U84#%Zq!2khZ zPz;p-Ph{T6TZT~^;wKK5yet(ORW9N#qSao$uzGNRZ4qF&<_pAra62)(*=uHD^BeavF$dRbd@@VVQL`uiZz)=DtSkLz8o z{%jhmp)$}%lypKAc?0W0P}Ta%xO6+@p-6jH%VwK%ubTnk)eiB8<_!>u4tRvronN;3 zR781cT&RU!$@7nOGt=bbgg;C>r#eStcmbmYS!CbRm~oZk;Gw+33D}HTvW; zJ7H7X#Hz{sc{ixCy$_*<8dG)4K@e9`YEkl>n=^tru(sy3$j^ZUGf^P;{0tlE|6uhZ zaz!y=U&MAHNLF;eIXh=|_SboRb*42rW)T*nSo>-#RNXU364DGcrffjK32GDSe9Tcb z^PeuV{I>lp#K2=~x#ddqa<#*z;UNtxtR#0c&I z1vfY!oeylT%xD%5dng&EeiRF>#XevF)J)%XS;hSex>-+`%^i98Jek?Adl{+w;Ik^= zyuibw7n`OGe0beG@n?{Om+Qb53>~0;h3F+0R4=caD)MJ}LwT3T0 z%?Tc}o)TB-z2YZV$sfSWAA-m7llzIlLMpyL-J=Qha0Qw6(LeH8OI|T+P0CGq&BqMI zSrKl{F5NOP;>_2`YMKG^rS(6i;oqN4{+ zF381h##OmAYRs~mA6h`;lrBssJULFsXxX+>?64VNq5zo4C+9U72$3?W-(OzZpIqj5 z-Sj&68m0AfEF{0`?O!`=jQp+uj5%75{swRhj^K zF&x&mKUKwn`Pq&ma^?=9vI20qNWE##x;?42d};SJ#9`pxGeL>)_SY(3*IXl(yrFc! zJvuyf_9nyM z?XbgMlwqv6CPjn|T%cy2^gZb7F-??F?6qe}L^$%<0KVQ*} zpZW{72ddZZ>GIx-#<4HFR^3G7gizq}85|fTe@F)mSi|o-E0|i_t{8C!r*b|-#{wTe zS7JEVwmwr2I;*y}_5!&mEJJy04W$bdFGF0$ zm=cyXNcsu0bk#Yg$6~Ffp-js7+CnRG-_AmyY@+GqyQhUJ+B#h`@j_Q_|Y5?nXrvWt<)yTd??t` zAhXRT6f~h5SzaByx>5b@0J{v5f&KF_pjYzQgUd%wy$VWl4X1ri5@rL1B#bb!5IZON zi?3f*>+O%w#z3cQWG)hI{*?BWHg9z3d{3doBCx5R+7>>r1BveM2ZrYU%ZZg*(X@k1 zRt6hu+&c1y)qyK4u%5Q;XQ1i7pcwRQ^8!$}E0U62^Ig51=0tnd&}7}I#P7TxzJ6kG z&obHEQ@H`Msctp}3@fDk^C!~*gni+yoR|&8Oi#zw$)g|qooBaL)?8EGjEoiR56pQS zm=BZBPZ^T)&+_Z*o@ZvvcBHqnFtF*~)=XyNEISPaQh2tXL5RGC1%A_^ou$I%>$z!*3- z`z!+TyUsSeBxvqGf>di~4)dV{Wr|`D(dncis}eMlV@6wR-J;+-M(mr)QnA~zn~~IJ z>otYaN^7|d1rfSlC5=A-ds#w;jN3cj9`Crrf z&maj%AIbpDIyVe{Jfyo@DfM9xAc2QJ3Ke+HG3WE49+Xkm5kTmvzMGP|>6$87)1{x8 z8iB34Iv-tcWN>PS9mY}yd51joN#yZ=tB}0JnLXx5(i54S4-*nLPJZh0`+a}=(?Dkh zzfb`>QzgX^-}l&38xu!6(mS~HZIzdIS1%6Z9B z9QS>#A8bYl#|pmY_yji@r!#$J78DQF}qnOtmE=tBDWy?Yj+Rx#Duoln*j4XRxb(EjnxQ-#*6JjYRgolUXT)c+(<(I9U(Z3Vp*Wxwa)CCy&s<#}~f5$9!&G;@`#~eZn&>gm| zmDgWM`b&L|JcDfiBXbCBnGpn`r+g9LxI@V7EIIiZf#li ztmUddn8OuvB-x62cpv0EfBIj_JYvz>qK3~aTu9}KZEGnApmMDbAwdU>xdl~1rw^Gf zkp2#}lo}U~kR=qMGvepohsu`fG&z6_9$T@>z)D?ElaqaroK>let0wZ8N`tY=lQ_<;p+OAZa^R6I#Qnw7}KJRlSP+MV`@SDCsxt#QwFBK z_FZon2)E7R{dk;M5!ngK@$`mGsQHn4!P#mW(>THUyV|Y|^O;`FPq#L%31D{`VAbH8 zu69_X>E=gg+CvIz2Kh$6JZ$eZ+u!;5C6x_Q{p0+rV&U9t$~M8Wh^&*q zq;J^CYHh@MS!Uq;hgZz=@_+PFJMdWg1euq*9z%c^mrz~~APSQ-Jv?f~vmB+SY0r6OH`yoI+b899`CIHYg>PP9HxBrbc*}NdsR1(ov-9J0UvCQ@KM8UE zep%GS_S5SgfVU~?It1SV%WOgo+v?krZBkATwxJjMt|vz{jNTqweOI-C4u=)r`#*P_ zy2g~4g91@gH&EkD&Q+7vR?~tBd}lw=57T4cx$F3E0RA?>mQblYKBU%-u`)U!j|Pd0 zqr_=MnF_1uD%1xRUs>fzHJ3!?M}r2yLVny<+ruY0t@#2v3Dwohb!FF!IW$r(CfZL# zh>85}g9mxtt|l-&{q61lA;&V~C({qRbmu6e(=XoC<_Zv+yprxg2i%v#)Lko0;Bsm? z?YxZrnmZ7l#aY8CBezoN4q06xzwdFG!pX|*nvvF^SAF9}ObfMIIui00-iwWns@=4> zZ*@em!+uo3o=Ok)xJ}D(nL7{v$Nj=isfNt(R%9myw)@8pQ#rlYbY}QyR4dlQ$I=mE zfQO63O0FcAP!C|k;H;HYHM4KjFD{f;)cM}iAmI&;xu0!0Ki_Kk*$!U>z$*T}hY(lD ziG`U+7qVg+!g{7W+6@yz!8RQyx2y9rR;x0{9e1nFn1`waUWXLW^ig9v&hx0;X0R0U zg@8-PywyQ+x&3BY?7$fUS>G@-j(9s&E&~;(10G)SD5GG_CU(%JX16>GL3`d%={i^T;Jg4Ht0{-L?UFlzvQdDFk-NSGc>0MF<{~wh zWLrp>BXspxwK4i|kM@mZ%JDn8aQ4({ASVm*f*I_v`8dAN8ApmAWd=;pP-JBSTTk_L zY4!#73W%S7{3a)2h%u2V8Z?L_3W1f5z=KOh!v!#E9l!qY@E}-c{@j*obDFoJn19tJ zeRdu>4$L<5fV8M&IUSJQHU22GZF+hBr$o_%G7mRyvrOCh#5r67XdragFYxOl3-_{Y z&K+%ucTIjq?{Z)bC1+RMAs4W4#1G6?u!bKyAJY){iw1v8V(jA4iaP|)ThQRb^Q^g> zI-AqG@KYla?j=GGft6o=nUiSiie!p%HK+rJd) z>qGL%33ke;{+2T)DVQeOMDw?yZKjO z0T{|h2Ply#0bv!Ck0Z+Q`Q1CpOdBP$CFj}1jI?ysJ{*?o>D8LgB|_kBn9aiBvtX2( ztwE(I>sCfVb9C`t_pYCvh61amV!9W0E>B-PtA#LPgEH12WOr0%PKv?UQUiSd}QTkT*3=vfmyB zTz~QikDXw$Hk+zFbn}v5oL!#SJ!4zfWw7eW62m`)<|lqx0EVs->$FXBN&AQnvYg4C zU5|RZwBBNS=WgN3^UlE5KP@NQXq@U%KGJARTJ~2)0SqzGV>?kdXWZQm(!SHQDG=`Q zqcb*^4+Ga*>^^`kDx>o8)V9gJu1__ew|O!v7e+qW9X$*dvs6-Adb6&}!+UcR-R*xu zwjV;ePwh!HAGw`-sf;}8-$Q?nlfL)miB0tBFL+*^R(}!If0}SY6Nt9Wb;juJ4is|HNQ9kc-z)i599{Wu>I*Q4nAGGY=SBw_%%NyZGG7X$-7 zM`RZSzR})5+;^E{zO`V=p?aw-fMwV-s@V7{{KZy8Ve_iZKfZ%1&DF{oX=&g!_Ufu* zfcf+e167n7b=Axu2~buzU$va|&( zE78>s+bSn{;Yq6bl%&e{nt58HV5vc8dfU$NwsHBJHJwjf0W0+&3YauZcA^8w%(U0) zqw}zAhY*B>%e=3D%PakRYokv*(rDjI*Vdbb%j|0HX+96g3}k3K={TKYuKBh2_J<7G zdqg+kdY*AMdr7)9#A(y&xDVb<2Ru{^BcO(7NGsHJ?JWI1RH>H7&Sz{D&L+(p#O%a( zN1Z!{f62fbYCut^Br0ZgMS4(vP`)NxXnKn!|9s3KdaJdJv)f8@4c+5MJ)?HSkiE!0 zIupXNFA^5xIy)!(EG$Jt&M*DVl=!Ogdu(eb)PbtmT)pPb^iX*NU8Ym>qjQm$7Y`2YZ%EVB;>F~8>uy~t2;zK ztwWN~+^C0RX@$xK8T(pVYM<{v)>ZX}O>`s@VrF;yTC=1b@*yv>vnuAZTz zo~vk`9l&DgfH5PaEhUu<8_qF|BnQ>CD%qus~saqcfo7cB((=PYHD=GPucXU8*50uPK zUHh6|GVZgH>yT@dB_1v6cx+HQ17;^U8q`!2~w!Qr0z!EwB<&7#_N;%&Qa zeQMe@5wmhn9}DaMYOgj~~{x z-YeIdD0Y^v$+Rs)57<^U#4D+)4}t4>GV`{>#_H|wK{g-DC%(p5wx}29f<$+&eP_N7 zNpxkOT@G%yoDN1A=Q#fm{9}%*DBDARE3T>sCBvB>j40pj5Q_ScYE<6qXvAizbWXF< zkH6O2{O?ePKfBdMf2bIw!70A24jjUXZVu(}?L$b>-1_S%aMBk9X=$nB$J@4o(`)|; zYi2+K_}@%TX?jE{3IIcI5%(pZQ2Z!Ugz-^k$tK-m7sW#B=?mWC9H*bOK#_Py`^Wy@ z_t7|0ltMEH4y=;PlfNF4z1w^)@_F_&$r5NjQ-sTm2{KIdYFy z8~IXH=8QBPcEs=Y_RpQP-4PvtgtD&^W(36#zAq=~_0N$asgg-Jy3J#qvv1S}J7-(= zj!kNhx$oHYt#5T7e3b$ua3O~G9|ZM}EIT>1&(w>cH*T0jo2#EM)ry~JlLe>*DAj~j zBE1)aW;f^n$hUpwUw`a6BS)I6AgKHAr-u_-U=l~T+0?o*IwnHnQJ-ZAJo6_`QGaWu#jkh_F zVzxWC_Q$R|quP&cJ}uFMOBdk!p#C$uNq}fJ*DdM|%i1c?AkhIE*Ga=0`f_f6&(Fmeo2oL-oYbsL}8c z-*W^xhEeh3guQeMF99|u#~9r{)!hB$=%GkbwNw12Nagnl^D|;~AHvX$6Fj?CKz@(T zjN={xHM5^MTEusT3WkXQ?Sfmn$P43Y8CACYA zo1dF5^4^7HBA*8FgGW`ZEvdtkdr+dm^tI@p4RUDGY;xnJMV8-}C2f29pFxfyEf#1N zzvkxCT$uW5&uyfqI*Q$q7`ifGd>ulhLBGkV%5Fo*b69VcWv`?R)}b&_^RG|eT>ccu z)WMne&ny4ahE9mM_CB>ETQ;tm#$T+&1LizMVg(jE zNvWbq_Uj7f8Z|Y4{1$q7N#5p_qbsE*FB)eA6J{TlYf4nS#es@Nf|I8C+iJ{+VT{kn z+)D-dRUcS4$1hjJZ=D-0`ul9=JLUo|#vSrogwUT_NP8VaJ_V1Prs%JOr8OnHJHelh za=(93zgN6-ty-XteFgX&O#UtXAfI`(K);D35w*`3k|jK~o0AAK{A5ghgodR3xuyQm z4%-11rG7fSw8Nakk6tFeSMXVQy%8@54Oc4V-h@9+r5Uw={z|gc$^B}62rNyi7(nuu z|93AW+y+9FM9Go2I32dkrWp!8{K<6vP(laDRMms9gzb=t`YqN)fJA+S^+K^xT+*Ly zlE4dequbwuN)=J$)#N)C%?3JqC@846*}tu=RT$5fGSjD&FcN$V-y{GGO{SdC0ZKEa zH1BP=t@YoErXKxSqMs+OJ0yrb?cI$}Kr{n$R>dF8WjY`a*Ft6{BxIIuA<{gNA6-31 zd!Lira@gK}lmG>|-Z$ghJ?;occl{lJyCcRB;)66vFm$@B|J8h0UHP}N8*z7aip_Tz zS@?iwr8Gm(-}B(XeEwlQ5*?6v%b49CD(=RG(H+TX3Akv3&+e?Aw;evclI&Qtxmr!`LJ$T#hj*x|YhP;kF za))hV;H*9i^9wA6^dF|YsMU1-<^E7ABU3T>=2J(d@qkBT=fIMFkD{2h9wComqSm*6sXFYGd zvUQ8K&d*P7aklvvS2lz@hGKy^L=7VmYBmueaSj$zRs#H2fS%WTf7??r-^2dY+@J%@ zB_Ha?Ks4X$ObY!|>4t9OhJm5p|87&teXv5)uOlz%;BS;T%_jYAvC?}F?xzhkG;kdx zC%p#l)S%_WMN0`Bp8jIW}* zE|36Ud_*>QbMTtY%6+?S&B>fq&!d;@;?sPu+y3A{rNY}Viz>tGT#^pX7e@X=wXNS0 zQ-=mYW#f(A8cdlJZ6I$gw^7CX)1jjq;#ux=fcA(atFaqg?vEfi(lY>YMrkQ4W`5T*U+ruZNt|MBJtW#vU(GVrZ|iWJa@p zsAG~^Mn(h02buz%wz8_t7;5Gl6|J{+KBdZEsQ~?b^!Q)Ndm7BAlI}vtXE)K03zM4l zdCg`bEc*sA&3)~c`Ck~&00%nE0;_ek`pB7P*NI*C8D^0g81+)*C=fI1)nuy+P8VtD zLm(vBkAsorj#X^QQ9O%Zdq$cBzDpbK$iN2SLF6mB)Gr>DgC(1cY>9G>+TOlRhhH#N zH!7M18NwHU+Cl7Od);f0POJ@O z!K@(yeE*rCOZ^&K(1ka>VBQu@Hm_svf8G47(&>LnT_xX;c$n!aF0G4Fy)=rkeb8DV z#az|0Z7i+<=T`94Dg}mJ`==S)$TuSW=t?DE2j7`(BYkGh9{sSpV7GSiA3*nyDSro$TBWcKA>bDEvjgp) zRH(Og52`3Q?Jbj33wHCF0|o{Vg(LrcO%VgTcbdCpx%Ck((}$h8b!s!us+hX^(c+g> z(r~vghNZLZqCX;JhihF9e&r;zAMGlCG4F{!#al}~d6D#W@LtN3PV<50?}vLT;VD8wEikz&>1; zp$krZW^}-)7nt{+^T#dW`JV?t!jA9RUj>00&n@>9+3(`qAq3uZTprw*O8(G7My7o3 zGu3ZSanUuMd|YzvT>Wa8>KB%kUU$rtNAuDZ?Ra@-bSqG}b-P(J? zf!P=z8ffZ<9}~%$1~?BRUGh^?#yiY=$?5ZoqRN$*j5(%uc|#!*bU@x)>S!BDH7}z_ zvv7JA8UhiuUyo-ylIqW(13KRUm=peB#&ARyl>$5JCOIIxzSKf4Zrek7Yh{`zXW%@i zA$vU?zzsXry?ES*D1rN-=SG9GrUY9ozwt?Ki^@R709^jTGN3QzGO#gJK2KPWWs|hj zCa1=U)%%aZjDk$fg#fl;fg`p=KbmJQzV;(IasX*YR!FP}DkMW$LTQL7zsuoTljom6 zuY#R#HnEj@4Ff~_DeQE>Lv<|D4*4=@f9Un*pj7&~rlyHHV+l9zYZvwY+aGxC==n7l zkFDq`qyv;FWfXxC5i)WW*j)9ZEy4QTyX&9I2z?8!0KEvcFhkJMqb2CvXfSrxUH4^# zY_bG`8<=jS>|v;SjXT7y*7LwGVnpsvSda(Avp{f3S8`$w+uKd`HU|^T=URBId!|(u zpwnR8k-e#J_a9s11&$%%_%}Bq+AyoTJVQocI$#X4X{Oa157rLJ&Azs@TM=X>V>Z+C z>B?dx8fQw~NK^-?9xm;A!=cf zmF1^-9_eQff34EVbt8Ak4k~>mYd(K3R;{Q7W!ai?6)X9^YD~k6xB8lDTEvKfqCo7H zxA}=aDLR>$JFL_-*2-?>n0DOiUjiYpbUfbE7x2^;ust=l!@i{hh+rz2CK0DQNp1R| ztCqUK=+E+XZ7M-Ceh~fC#WXNup=jZ?7U)cc4#*3pe)&kAORaUfFg;Zg!RQva1P->m zYTE119Ya261K}oOLgH+l#wPoo!u5Sb<^(H28V$VfrYgSzI57Qmz)rO91t0U8k0vFyU+nCG-F(*VS7mHtn^+p&g!W?$R_<2!`$vUL)*W0 z)rvVga)l$-VP*p-ztyW2E#+hvzwir43Z3E650|Pgsc>f17*Im$C=Z5C%@OGZu;~}p zseUqE78vEF;Q#JDzp;0x>IwJ;C?|+>26x{haicJAH|LAlgr8fR)Q+xD;19XvegQMr zj$|S2og^73EUeC@z%5_?Dn4QBq1(9*ao8IXR`%~W{?0%<>>wR5nkYpnq$nc(WXqPi)|trDhhxu?af!g~%mFk9e}HV-cx@s4~aSk|Vn-sXoUa}6>53;fxT zv0|*tHKIY=KWiS2$X!kcq#IGfTWm3&86bg`dVlP&33Pzs(Kc>8&eo0eaiC>lylTip{;EUn-+l{+1k0RTt-|rV zQ8)gZ?8IYR=l~Fy|Bwz4r#Lp%*=LkG`aPHXmU+>;U^%LqKR)~}`uDHPpx=d*fx=+} z`yn2!j$BgFRhWK(HR*z>Ry0R%tY;kU)C;Yq`lb(_P6?qDS5vG9bnD#|WJg-Bi=_NO ziiGn31J@pl4W^kGC}33}a#Oxvs$4k}v;LDN&;j=?t!=F%o;b%|ALoq+X(9vvmC8Qj zq+M}o9T~X#po$?Aw)y;(>3SWmg|6D&SJNVBrKa&pE*d^wxY}Y&c1ZgcDo-yi9L)R?YrJiK-!)|`o zezpI#>31LeU)%+_&O&J*h=WI~B?)06!_cS+HV(^uNW1O<@4MbEz)5AK9TtNUk&kDm zy|yQnKA9GY-$6Su4z=E3N6;{l0&ZImU zbou3bqrSc7bu>1WH~i!p&5{iO^M>4f z*>=SsVY|wGpg_niJ0MyoF4#`U^KG*?EGP~V`FL`{B!{(eklP_RA`K$>J9)nD#11=- zkfH;|9u03SAw~X^M^w1V687~AipGQM>lvS25Vh+GG;m7&fc`r<+}4QP0L5- zf?agcpPP!nK6o?5ny31uPt~&))0S*|?_Xz*Sp#;JZ_5HGQ^ke~W-l6OW_N|>eNs{`I4L(3dgfQ!zX-fAge3&tN58|V_`yJ zqm3zA;RU)2MLHSac|jIBE~j``FFccbq6ge+pD>%ZrAo-o{ zy?4b|R=EwdwW~{6rt#JA&I()C6OS@OQ_=GG(J692;L%0=2FVo*DG24R<5S1HVJGF<$ zuWP8AAA0mTKm;6|u6_#DbeRK&-Klowv>nY~Aw*-G`6p%-m~Iek+kH|is1WC?+y^j1 zA;A+Hl=lUBG#`D8G@>2Kqph}A9uk)`g*0+khef}L?G&|iggtL{6 zf9klK_r(oOR~1QCaTzTFuvOc4d#`!R|t}~Z{g7HgOT8O zfriZzIn5(4UrGz4l*TJu^i_3v9DE&ehYk>zsY5-KmiBYI28YkOr&|%`Pk!kzPAxd(yFFM9g0#of{ zRba&Xx!$_StW60s+Z9z}8FXPdN0H#s6psJUoFJy2+R-5SHSJG+mY3mo48D*uf*qXL zSgoYYX}tYX_kAn5&j%!S*_&T>fZN=2(fA^GC{zZ@Ohn0T@jRaJ#R*WFQ?}u=?en$w z&+#|+)@`3Zx_9T!@HusDKl|ULK13MpsnMme^4qPIm=c7`w*0tM7394PKpZ_=9K zkrL(`9Uv>g)O9>%jd>7T>6`>fdGhPSH4*P_Z&(@yr_oI6>q2Fj^^12m=cr&jjN|XR zkD1?FLE1Pgd-EvOxKP5XzHjvy*qX{aL>zCJrwdfc4DyxSSAEEkvV1WRj~yPT3}L0* zf)8g4^VGC0n~WZ~kn}4?Zms6*Df&ZF9@v&@U0LHVwrED)lz~karm5K=My2^|Xf91J zLO-K-8pp0+RE(1_ERt3tpG~s-F1KMOal{*R0P&vh$|IN2nh}eB?qN%KXYoZteR(lc z4dYp03%hv$CE_Z$g~#16P^2pbXYBA1M>_Xw_Cs$aM%-mg4!##nkc>D9mf- z<;KiUajQrgb|*2p56(+GyFh)p-mU($-Ff+VPlh`eZQRJCJN6cq%s6N`@^225NC!w! z4^e!$he9N9CV0|U5`IUz9`fX@Z@5OT|IMD>tTJ=sPj_}861&Ymg#Yfw{<_v%>&sd& zCwI8X_YBnr*wFGTvO=|5xBdFQkhqy?*P4%D5`^c&z-lWhXv0{%RK%I(`27cPKO znWLGrFRRaaY-lTDf(W)&ijAb(l@-IcM!7XcEvCIp z;O4GH-&wnFfDXiPqRo;bWZB^Xq;V^O5L=sKs~7Z!VJA82DIFk-8UK_E47)-}By3Ad z{7}t!O9zyBNi-e3xI3zGf1$Esiw@YX_Ih^$SHg@pVFg?Yo>OaQRB*^G?1mISS5#6` zqXwJA<9HftP0^uKX@C;uC%5!S&kJgInEnthd&MHt!1nc$@7wktdmbf8I<_3sjhdTe z_?q_$5ZR4J4LoxfDk@Sxz za?O%k1C!58fKiv1P75~LFsokj+o$8yH7xOLoOrD@of^NvW%4q}=Z?_xjAhm*X~+Rk zKJqzFA8ffWwTT?n*T1nG^9nw05%GETp4U5#Kv_uOkrUz_9bg050F#rHGjopWo-*^( z5Fl&0mF|>dUlrT5V!yTNQfZ}y*o!__ivERhYN*YVM_!;U6gi%=uWzX^w_AJ{x>~_U z5D!w6JxKqfE?pH7xl_H3?tV0kASWIKp#~M;2=ZJd#x>)ZJilzy633!9 z^CLbm{uGbRLJ_ZZZHnYjr#7Sq5=ZpYQ=~!g(;VM~prL`rhNoqPKLFky4tN-{ zF)B{pCzbpiMCySN9gSTU&m(|k(R+8U8wM7BhiP9<{{wBqY$1f`fHB4LlXXH2fjwuB zQC}!34+fK&CgXuwU&SDmH*e;Dss|NNGD-Wx$I9~WgUd|yLrNDq)B`O_A8`Ch9`Wjn zkRm>R(ud%r1I9FwFpD5{5_yEjg)}Ee)~e=5sIyLVyKw46eB+b`z7{iF@D%|X&;jz& zp@gvYz|fDSo7gHF7is6yl3Pz3e`~DXTS5-GK&)HDw5xq{@&f& zpy?5CDVORq#1SqkId9rI+r;+A5OfG0L{Nr}#A$vm{vCzsDc075!Z!^ZrEh`c9|HY$ zA%A8PtrS4V2KEN6v(k0Deq(C!#R+!t$AU*`iE-;XMM@Zc^819@T`yM-^-m`GT*z}( zfpkDx=UoV%Cxq0$smc?^y1W&lZvYi;&Cxl&uPt*f8|AF3&w0F!?qQ*lNzg(9v#3Q- zAsJ_zYux!wzIy~L1e4e;K2N7;`z^nHV+iU46SUL#CkQsS7x9L=p6&3(bNrA;xo|8s3CbQOuc`<4pv6Edw_BzSMqENJ3@bB{ z7y2vo6*q^SF6AK1qqXD~PT~&C&x-e3K!?&JsG)r6Aq9mKh3q$7!w$?NM+1Vj32JfmL7Agc zUKK6c9Y*hTH!Tha19gr7FuB5KR@}*G^sDJOC24u1<>y+Gh>;z9{ z@LRlWf2?ng$9_W$@{Z{9N+aD|>422m(p-z5tlJ&p^=coaU6=}<%1%votbEKY6$uQp z+^7TnHERufN`kd$rlgbrAZu(@_<%q0+O0PKqv%ZBnegK{zM>RKWGQ!!Fd^m2HSu%j zHe-&JkRwEn=9&nZ-1mK-!;srphq;e1N6y0LO0uD*wf&y{hR^eSpYQYeyx*@EgmORx zZ{CXAgiq6-PR};*LA$ce-^Hz$o)<|X7f!MSv-jr}l>=QhA>^V;qI9^99u}*d)vkm} z$#x@6i~X0Tzt5T%WhU;_iKc8)_Y7z!WN2Yn`KDy?X!mMI5NUlW==e!nD$|(Ck=s}6 znq8@NKj>#1B^j58{PtIn0U+NS_qd8c&0WC!SmpH(bvk?P`101ANzN~k9Q3S$naVQ4 z3{q@O!A`a3wYN`lr$!wpym)RNyq2l2@oL$39q4iuu?Qbaefol!uGrAbWB6jPc!fLV zdg}1svPxrs(%KR*lv(i6XKX{+wOQHy^&`&5GU69m<86RDPB&|fI{M|UlfYvfva3sN z<8O=k3-&Yr6H^1K$8$I6i<*ZJ)-h@n7Ls^e2DXEPr15cOgf$$JrebUOXMTh4IrY`p z2?P%>22&2hxu*Y&bZZc6p#<4mv7zo3?XIa>ZVqh5KXK0>4Nu1f-Dn?LrxhG| zzle4czVaxyT6~b%ftqs!Etm3A8TcKv-n6V7-*UD+!RK1(gL)SD&S_S)jyr}|R1-*MPHo6ZUiZGwGH?X;8bB*V+ zHIxu2-*FugUe~4gtGW;LJD>jisVyO_vc!xy9#}EcCiBADD?#J#>r+2aR+`K&OP*ip zLRb5J9Uvz$^umU{6hk5dprkdRRvoHWG@lmF2}BrJ`6!AjnO;l4h%B?Md0IaLKKc=C-q4btbbI+$sd}WEb=FP&nHiw|5+ZwevGEmK zUn%?kGL*+q`I?8yunSvvbT8jm@t~XCmmuU{|6uS9)R|2i9IbLj_8GmcVp5=YIbmn) zjh*DRjG9LgYo13e!0=LP=;kPV*jL=W&;DIdZm%S0sP#dhhFDQWFmWh)##68y7*BhQsgGZt>qPPjGjmu0?Qx2gl?Ve}^RdETG5iC(~fmHDj_^lgi(lS525(g(W-m zQ$M9!bl0qVSuR-tLwtH3&0A!}XA_>XcQ&wmQ8+b;c!}m};f?IbnmZ8}5gFmMiw+6> zN$sreS639|Gt@=-jDu#Ejz}{Nd?VE`$8)u|03N=K$PtG6>55ONp!Y# z#T#*|oxiYWvRi$E{#h6c`heWJehilPY(J&vW6l?G$az}8bV8pOye?yNMOvq}Z#GF_ zWP8>z5y`sUl*FTMQn~+LMM(NxdwGlq(fX#-hziAWZCn>`-C_%X#K#&nukw zL%+6yn@em#xW1NvgO#7-A#^L5w$YIh)in;(w~Qf>fMod|23i_6NiNBI)BGKL18~pE_mTG z;Q30bvAI>oHB|6e`^m|#2X!X-_Dfkg{%W7=PEnUXpx1ydMFF%C@;q%*w>movLq!IQ zr#04AIl-6SWE7(--iGrewHX=IglnCF7lHm>DuTKZpG}K-aKB3Q8e#yXh_msHztIC> zAG{R0v9P=*`y1=NQqL&0U_>veV|S_(%!bF0Lyj;>=31wSY85OLM-nt}@3 zeo&I~76Xv&%>ZcPRIpu}OE*Zx52sP9dmLweJ3}y+*+1V;QDL?!{<4qi!_z*Km2(uh zKhyB#_jE5OqLZ9T)hk+Z&b=HDax&?ViRUYTtm7ytA-$ja3O9>ITbrqKq?<1|Gri}4JyAXoYjwYIfpa zLn#wNV`Qk`OwC|_=l<%eoVJU9gwOn3ug#`y;7HlOgNI-eso&nRJ8g}k#jHN+m$|jR z>A!M0+O>RUU!FM-Z>1i6R@{*_}mZC)+yePJKxqJlNANml)g%Lvb<=!OjrZH9C$}9W&pIkiQbq0 z=+9gu50L2^w@7ohZcg8D4T=r~axnM#1XhCLb#enj+6H&=`oH|gT2Y@JB`mvYW!gl4 z%I~9Qi|HGOfq@>m_wFnAc`tXOzj?m30y5P$gg9eb+jOUW>Mg&OCy`e2 z^icN%iOQRVAMR2Pki)iaPh6Hzj{o2zu^zI1o(%E)oYlB@nz6|uVf?z0PrFl=fGEhD_$t<6MFcPc+#7W;uJcf?TGfN z%yBr-SC+t;L%${bO7}JcaB+7q4039*Ik=lwL9XucOH1`PRa}+SMWXv;h;ws)HSDH zHT`k)vxXA&NlwvQ=zX2B2!}$4W$me9Zqo}1yC?$eWEi!5e{!+tqJG z3PTDY>)-;-fGJw#W6j#FFXNSk%Uloc*FJZ79wTzoK%_JduVXd{3_~&in#;7N*)SX_ zS$=s}Nw#SyTsrjRh2eU;Gf+hkwYxV^?H>n>K+~UkDz^tIoyr!ns)t^D;;*;zim5il z?Wo3b1j6^Oi^_Y0$4#FgQ-lw@B=EP&MV8T zQaSaTSE5n4vH=VsAZ=OC!k+xmR{8rnhMfT@7$palZWT+&5qVNeQ`(lN z96dS9eFbt0Lf%%SRn{TDdTpTkI%fhjq=#XUxQy2Km;63l;1sSE5wn)jnPLKX{82$~ z9jJNm?r%b~RQXf!=y*}9=lwBR=QoWcc*7oQayaYoI%A4&F#rY7j$X-%$9x7r#&B0biPlw`ruW6Tn3W8B2Dpyi z`u8IM-|Wa~70{Vr0J5OjygrqRU0D}}FJlx^q*c%6PbEkQd}v%%K~s$BemY}`^^1GK zMK9cD6lLxTVb)rw?gYywJuc#TVoSV?%_(jr9Kt|<;~0Pf1f?&YnoTrp^S!}Vsc$$P3Xy@X(PzJ39rDR$1=4dzmJYW|kKyPQ)8-AX+B zYnam`1We*jH>{#g!eYKgc8EuX+77#aE^ZZbIp&`6F;HE{RJNL2gUfCQ*(e3gm{hMW z87TgjrD^oc2RJ*y0C0%Fsxp?3YVl70YGfZ?2>dN5&gKcWNJQ%Nv^VD|`^bZ6ZWpH1 zk{{kF{T+9vR}b`;x#zP&v6y=vCMRr0I9x8lWGAcM>yl@`z%Q{#0Q_<%`s)F8AW_bW z;!DzOrY1Km{54$+O}~}weFJXFcP=6FcSv!4OPuu5Ol>V59ueVAr3=YK{Ft+`&v%FJ z{%nBm@yL2+?Q|^q+-B)o3xjOtUF#Amz`o-`e7te{z{fm4-|nJVh*utE|OF!S|~jEnxF5SK`TRgS+(n&cCrRXga zD*8mrN{H!XX`gon7_L!8ZCn}`G|b!7=eM%A)fZp>4{&2H(bTa}5+CEyiHF~acuM!* z9j*2hd^r=ff)Lr#MR3<~-Ar44-RUVXM1;MXi%d-Pv+-g8RARRH%p^U6A{@z!y0US# zd|eyvx?ECmN>70e2zl5{)sl}Nbg^4Mdu{lNCogfnYR%~-k<~U#+NkRnf3g({yF@uV z#{gJQ`{J}6G}`t)+|B-Gsj2A@d7xWY^TWgsIu56#IfW&VL&^SY589;zF$;sf;@J&{ zfaQ$~@CH^g`(fbPCtxUYQzGL4C(i)fJ`y@Cc-PPKFnP}tiA}9~aDwhtL4^bHPVvy&F<~?J-VDthvB;OMt3t2 zupeI>v(uD6B5Wdsj=R?}W(+`qAZ|vT0ic4*Kaq^x8-2=~e2wmS!O{0kVuBuNIS#3J z1>)hh9(iGl$gF&=B<@!E)A}bI!?v8e{t~s~@qi${OOT(zv<)0>!xXbA(Mw+K@qg#; zCA|z=je=DfE<1f^YM%r;1dk}SP=3)i@dqS+;zYvP zWsj3))0gEBqjOT27PUE;_D8Ege|#y{Yj%j(dH4*IaozhdnbKPcq7Pfko4&Ik`5`}{ zv`sj%je_VIW&n!Q>R={Sr_H<9w(Y;AJ=Hb6YaIrvC*MKDNZmB=m>-sJa{pe&%slwFZNyXGbjPgoF^7*o6JEvz7S;Lw9 z(wPe*%jiCu)?ysc%_$FIHfDoN;knZv5FRmLeY)&DqI~Qu2Li4pi%5Y?#|VEE@o@X? z`KN|t-IG_9j$+;(&!)5*m+c9g?z2m%JHj1t{pUecB;DT%;wUs)WI|baomhh}_q^6{ z?<0CdKj-@WyGf}72EOF>15_W1yjJ7`RtW1M?klly;_F~b11&y2H{L#eTh@CWV7p## zO!sdYF}XVOd6!qSBrgJe?)#Pa_m|SmMw6vHIadjykUWlZm^}*-W z=eNQIRc?yv&bTF!b`J;3eOarkR=ZV7#DM(jFWFgW0XDHHH~rcTnYp(sr5Gzhx<-im1i^nP#fm(x;twB$~`V$77=oz^4nQy z4ltcc@wNiDnXi8dAd#D?L9Ra5rOVBlOu#TC5zWT{PzQa-#;vp|3LG4%ZHOlX?}J2$ zb7!aiW&WgwPRr_&-}BEPCyJOHS=s@~gw2lD?b2s#>-`m*r&RXl`i(0e!5+BaE6yR( zN}z!Q+?K@gxZsL<%+@v3qV6kv0(pneuw=T*d25%QsTZ%}~BFAQLVZ;^A zwf0OxE!xF8l{E81(7ukdDSn_}iM|E&7JSrwW>WKI2W@%gn=8RrwCi=8=v1uIQG)$Q zzlGR3HipIM_c~^#eLCQuE%iY_sI$#{p}_Sb?YOi~iI6<#YR`JT?fB)?>Msi(pf`&z zD!ir6e9=xHHVG+G^PU0fAf7^UAEo|yW1sMQe1syNDE4HhRP&|*^#~6KM!3+Qz_;Or zr@-n=ajspPAg>yS9kQZm+Mfzm{l*ah^0dOY<}p4paCC3UxhBR{D zmI>Giy{G4S(KW(Lxaw;S%QMIkG{=SKwDfdTp~>V@>0XmLU`uDNswC-C5{M$8Z~QKO zlFJHZD`pGj$`~sFxr-b*a{_ zSFCVxE&RUVj|k}+@#b->k+}hoB`;*PDuZ35o3slzleZQd-Q3a zvA%a<3iq()LolP-bE!2I9FN{N{RDk_1_654xBdL9Zet8KCPYsKfzC#KFj#W_faHe| zahaOF*%U7eico%7cN3r5Q`NvsJQ8agA)%gx4~K>6)NtdW5lNV=D z+4rcB^%@T95Kf93Ou0Jze78GcIIv+S{k9FywVe^G=Xg>7vJ&8Ll(KI%P@DQe5jO;u zq6SYC=?CO3868bv9xGaCmf!IZ6Dj=9Yoze~5-_B_l$!X28chwXqE?eXu7)Or2Q3fj zNHGBVuY^83Z#0>;mK^UNS^;0K(TJD3h-P9DW{cawld_e9?<&Kc=FU0$G-|-3Q*K}6 zXDe0wqaNmGZASak7RW=k^6+;AIWDb*+nxjWa5hO4XjgwzFz3)V=9~wH*HYUmSGu(n zV->8fj(|=$a7Vo z>}|U1{EvJ%FTOc#Nc07@&;Ks;Mr=s|Rbqh)0M&1_W%(=KfQvLTbB|ID>l)8#cpok8Kn30_F{nu;=&*nBON-m!@Hy<$j zo*~?VI#R@2aVWS?V(cpW;=p`924Wdk`fpmAn9&S^h#e0hq_s3~Ru%)0b&)h~>h%o~ zOfoTbv@x{PGEgab%PjQnXI(62QSB{+B$QE1NsO>3OBB!sx4Z&9_S?Q_;*P!I*>rt& z2b?T0(Ifj1wM&qlwG?E2cQQ%GOVepyW@ODh``0zj$t&uvZ?16Gaa`&D9-hGu$w9oH zMQ#xrubmlfc2RNst-d6!c^Gb z=FKF2tiR(h?qlf5qhCFNO@6w$^At;A$!}~Zn{k!a`QQpgrWoYKnDtNSo(KI^p!lX7HQ0}c4$&S4 z-T9*T=E2vU4?vsNKN?EaGpJ_F5CfoS(txm8GNz;*&eapWMA=8f9-e%+U@c0tW(~sI zvE<)WJ`F$OeRTFQpIDui!qYQ-T0p$*)6B3$*=Q=$tLk?KUA_52 z#9~E+alO@$N=c8ozTfC?zSL2nQ9h0__*n5xkJIqY5Iyu$5k)NS$8c$q!J;Whxh#^m zBD`97y&g)@x9s~TfFQ+fhMP)#CC9{TCVw)6J^dYTsX2w=ovH4ZhZ5F`BAjMvGerGn zvbLFEN5%3@`K+6BYL&uK&>$<`=FNz4tsn9>@(pwdb;gifl@LfA#aY_X+JXaX=Wgzf zhG1Q+)vO8EOqpqphc;{sfC}*i(Ztl40T@MB^9C2_h`qCu-2B`-efM2n8;6xOpQNHj zGqjBXn7BoCc61)KX{WtUe0Dj%ZjHl$ran4yB)Lg5`WmY-Qw93RNIzHVp_&m;AS*T&D5AWzt>K&NH!yhB!CfhYV zTd;9P{=ZKB6GCg8-eBL}oQvembn^imE#$4&7kQrz^%j}%z6N1qPptRjy3b*f8Zo5t zK#j0oZ~9|`UDQIg3LPe**YZFtTj8zI_pvwCB^)-DnzK^vztLmr>Hl8*6eh|;`->^bYngqXZ8XY!ARBc~h2HK;@b!7}+h?OY z;g^Ly;m0-iJ#o#?>%E88K5g}T3_uR9D0?lN*V^*ShpO0S0|A{@o2K_JcMrF#+fjmy6F*m9wLSFZT;{iPGGAU%rJ{!T*sMoaGpuuex>Seb| z+$BhZd&HjoDyl{#>0&wOe5TFxA%F7 z$Q;?suy5YwOUq6pUZXQ<86Y#GGkOjX7OFQ@16)Y^7ET?c=6zjT^z1NxqN|zKPka<# z)}S31bIqiKXtf3m-^WlR=A-*9>|8U=?sIc-r)m|Qjo!YFW+Ly;65|Pfb5vKRlift z{gd%*?}f6mrEoVRg?Mq*w1j5r?{&;OjH1Vn$`0F&4ep7vO5ajwx(Q65vnr+kh~%IJ z-`$!tH-U)6|Ckk&x*l22#+qpkc8$BGg8rIJodDm$kmhPP4@MH%Ul%=^4fONdt1WLV z_#D#%cGHoGu^Y8RQK8P5ZW@M~xhdT%r%P)? z0h?#t-XSbMOebD?2-&pb@#i#VGJlz+a2>s~qBJlY;3$h{%IP<{d2CO(RgzM_qRD$j zT^^d~l?8)lO>bC7o4y+7^{Bi5;^l_>`)XuNzV9Mf9U@HYBu^yPQT>I+lF`x3I~v&M zXFh~W*&)(;lRpM)u!lO=!zpTHF>24~a}QirhmICDR>B*!&ZTYdV02N8X|Al~H`HeV z-G7`sxu#Axyv-Hy^pwlv$|qo%r*?^9;77$SX3i#ln6AL@Vt&YOo*e0X8zMrqJz;L< zSxhFea`|h^Bp-fwP;>Hu(NRSP-f4-*08IQ5@SHOz*Gec>kw8R{WlWXL&@jK+^~z?WvEwSr>u z*0AzgUN#TMxsu~X*%>#9OiN)jMcCZir~vX~Rh!o>y-c@afzJ+Zo+Le*ZSj*OlPz#} zIo|uwx=KUUn^(Wx)x(8Q=4AQ3mhXLe3`c~C&XF@le2Qo6B(J?q6D{oobNp-LL&#<^C8;=% zH=Fd7_>vM>kt%dImf+pS&GId3U(AIQgGAkl0c>9ejUu=Buo&O+70-y5v->garQ+D2 zj&$S08$KGUOrzaQ=2FY+7<8NQ<4164-2Q{Bx8+$vLZZJ(EMGOt0=<`2{Ddq^2!C8+ z{Of84r#5VJX&A3`0a?rpl!vS-2Dp6cm=GHv^jC=J-cf0vY%cI3_-%@uoA zdag*`z?y>o3ew)u_NH)3>dEC1PhjKh4ZUPBa$Ql{q4+ve=Ou$vd24VEcAPkQ$DJG= zam2Btxqw)bW$6c&kf!~G^;);obFppJtwHIk4ewg#lRPJHqz=jF(P!lP`F8M5 z_C&Y{6-o|u=prAaM>!y3Ppx~pYVR6w-@7ZFYR(x9>b1`Mg}0KUjtKm>TV#ur+O^7O zOAJ}|n49F|u-dz4a2ANMS+|{k@7zePPFF|R$eBUoeG&xU=g$Ao1vC}f^ES285rnV}nyk~(3b8m3zV z;z*8~YK9KL^B924Ze`+NraBH$;y4y?aNFSn*PHoY-#9+rbmMC(1<($mEAy;A-`9H# zS1(q@uN#E;*`2L_x*R2C)vK(61|G?#2JE@}a7uI#6*^E_B2^W@zZI?mYWxr&iSL|b zV9y?#SB_!(m29Rc;R79WDaSW>C$@jm74>4Oe*ki9FTuZiBy;-vEvu85Plp!(e;o2_ zt42F_P!(WGKDdxaQ>1t^gb6&X7YuVNHgm+9BTQ-M`M_&x=E5crG32F8%$NLD@o1;>Oqo2aV0- z5e1Z}xw$|#j)57OO(EWx!{2YJV?mh=zyvpfX9Awy2lHw$b>THF;qsCWx;=JjbHu&+ zy9MkmZgU+|?u%1+)^?+ee7q?Hoelr5tG&a!LjGc}q8m%uJzkESY6pl$+vh-mJ3Lu) zVp-qcT_C00JrrxQeZMW$De(f;CqVm1X{@fMhf+g+*|#HT?@D&ADm4d;L|}Lw9>$*E zTza9;Hd6)q^T+Ws)M5IWV)88qu|qs((PZ@Z598uiruYW{2H*}Ju54E2;Mq+o#B5LC zY&x*+vc0Y%p?;t&_xbQ-xmILK=z1*!kaygLVF2WX$)QX2Foh}YRIH(;ucVo#e%b5u z)xZcJ1ad+ZmxkOD3&U@A@yyMs4}v^Se*D1$YvW%IT@Z=Ua+W?i!NZ$FCK5tx5GLyR zqzhx8cRjlU-%BK4SUg*$LmMH8$N7H(I^!q_bloYO47Nf&ff(=B*)$_wG%k1ykrkSe z8A>(TNXf&rgGK^z5*{YCcGM_3@LP5HK&)BhxwM#ZmXOP$OTfnrK<;8|dy#qfta_j5 z3xT)sfI_r@fNiOp#yKq;jOlOmI-*U$i2I<9pb?{28s@h!?h5I^Ml{}w)+#p^-ruT@LdAP588FHIvvo`Ev zVmu@@YCKRl4WtGJ`^lI3s!DDP*n!>@@2r23b<;BRGhq|Df42^EdF|;SFd{X=3_%(k z6>$t;5-8qg02&k?V%a|NGD8+Fi!m!$P4^t*E$AM!Awpn-HoKW=s_+c^c*76~ThI^9 ze>d-=@I~tLdR=kuA~F-F(z>E=WMZQRiQf=%=%ABV^~@ouvHcghk}}Gz=Yb}Q2jhkM z!>U2A$`Ewq-gAp$N&(D9# zy^j7qt~0ji)KF_yYU`kTs{JLSvw^=FmUl2vbRE5gnM5jj;xg!l1f-a!4c;>apUd%I^Ylek z^gSW>+%J`g9z?d?M`_ZG%vd|a)$?{bMBi!7xPQpuuOpx?^mYzmtlEs6msh$~*i#68QG{*=pY}t_qb-Z-eph`nAHvx4gb-nBxYR9_qdALzhAx!a7F3 zk|)Hfx(n~5lqV`j{8391kP^ib#?I~gYze=s?w=Yku_@-Ac~g3(`vBGCOFh{lu@4r8 zakAy9a(Xj@3W%-*WPpu@8Luad;iQDMbAU zA1m}47PQTvKT+uRA26BiosnZMy5VdY?u2S#UhVbN;cZqyAFL^DfA&IVuey=5Qy0G+ z7do#J*OT1p?i8vdPQHogMTB3kdqNeU!mH@I5w<(XZ1RAsSKzJhtk;sf7h!i;zIjM@ z>?=Qnq%7+YkrN5hCPrjw4V6yXU*7k|y&!Fv5rK=MgeVY;V|H8=sW0ZELK)r&&p2J6B$((R9=w zgFXb`aL5!IMd5U@+0<7YTX7lBjxjc(-Vm=Lv5ga*^De3EUKWvmo>)4`Zo}w}t2J%V7V!2rgS1dwl#*UaY2M_)=%7!j5m(rc-BxIJR$5^nf#Dr2#L4XX zMFG?6#L$kI`T3AG(d04)01Xq$0!0yAQ8W|gH!WrMrvSN+2|u|!)toVz$Xk|slTEwb zW+dHso>j=KkKl({?T|b1Hy%M=oV<#!+b2ZppnADRVKe_$cCE%sOG9ONW9u3? zR8}}-cSkKuzpi6qkMm`8CXkf0CXXyG6JJ;Mw`Ntt!ncGPmr}WB)h;`*0Zm`8V?HNq zBCJN|l$E%=obC4#td*LtHhFpF@+2sNQyL!ZwkhJB5L5~^RgTymCs_T|&0aTWn0@^} zNnU+Z*0Z0pWXzQg8$ncKy8pqrO}3LZnE@a^&6p`zbwLr9mL%Tq@cs8B@Ok++8NEwm z%EHdIw=&B+XdBr@v<9k%3)zoyd&AD)P!r%iy-9Fs+w1$TXWZ@ypI514HxFJ= zT2aP3-6VoXi{%meHiYA-krN~go9peYi8i?s!zw+0QWK*e(j?Ff9WJKCt-7a=I=!S+ zX9lDi3ON?3c}WUHE!L+S?csUqU0+%1KW|%D$IKvA>3%ywb4bWL3;NBMkGF&bz9s3q zc!*W&YS$YFEf&h#+^2ewG@z53mOb+>7B1;_Jf`ep+QwZFD+2kgbl$0#H&v$M>@Gj}n zT4{Up3OglU#z=Yl58+<^2?nIBD?g`pT6jS;c&i@h6bx(WHIA0KuQiFiX?=dl?eNzc z5TTd*_}V*CXingT{U1dWV%T?=O8YQ!+DC^zU_B7^7u_FC?mnQn&CQ#Wdxm{U8aMJn z1;r$4jF{D5@s*|bhuY+zxhcLy0p4ol5e8tm$p7`HEg_kju2yTS5&O*e&+3`gzyQrVkok%eZihjUajyQvc=!02M0L zO+mlmP87XrAbv0fclVkB79E7Mi<_^T5-R|1Mt_$ou~fCG20Cum_|}6J8%X4e-LT%v z@vItCfKi#1@sUCP{s7GazZi^_mC-yfA|8=Gt{Q@PwzvA2i>;QUs!V~)@CFyNbL-FD zRis4bCyVl16jZ6`9apyCuuXswnhXvu`z8>Z}LfNf*FGa6B z@a9)?V?_qVumCh4u_Egi(CcOs#Xo<bE9sP%f^=u8Y5k*}9G5x^H`D?+Tyq zAC)>g;))Vw7UJsTHfR7=i?VYgx zxAmlQM*`CB$6e#b;D_BuIQ*8nF{-o?T9#KLd+Wi?haz1`Uu5>@)?>L7ucD+&nOl@U z4tBaWf~ZB|IuKe^Pb>p4wyWFvk-4piYgA;^Poz9&WBWh1H@%uyQ7s4Ky}C4fg}Z8F zuHUZhaKmu*nzW#lM1$N_NgY_VT;%1_oTB`7%p=+$MYEP_R!x2K-*oxC9q7GV#TPH# z*S`L_E)q2K#>=Vq5L)C8Pg^YDoj`9nIxTt|Q2Kh_t$%pijCr5RIvw-v!jOK*d?(%( zOxuFy$yg2hQ;paxywp58uIe)BiHgqD84Z2%2QJ(Z#CJPmUeb!l6aO$=pyE-C<|(v2 z%imt3DT8y@X{xMS*rDUfs(K zKz>NU`E@lUXyJpHjcf^XY9s3~h`0F5`PYeYw@R0QA+y!#$vH?Rvz$EZv_aFi_jJ1? zWAFU*+krBNAvX`JKs^-Wkzi%3549irDlD9I6;jh)W*$eOJAq<)^^>MsNlVW4YIOhB z2elE7dtBP74FntiftG8RES!@OaY&nDmrIa1hc zmgo&s-@m|pFl})zY`H~;G}I_Uemkwzt(C&<7OQgOzfuOki;xM_)rIKe=Q`0lHgf?O z(P7yJv{njiY)D3Ci1UW8xORc$MOYUHE>iFaas;PsWanvkbGyU(QS<(-4g7Bb ze{Bu1dOvsw5g;XcO5K)Ipni>7v+5+oWEFB|BVHHf-tC0>1@ZR@J%ijJA?mSl3L%Zn ze!L!;X^+O<14-cuS|QK*U!7?9K|eu5++x5qVnbDG!^btdZt_|!@I$58*1f|GTFJSE zUzcY0QN40>{{$O~TncTLh#A*l)m0UUIfBWIcQH$~_tXv1Lf-kCUwL`9_YnGp9@J$2 zSHmqnzT2KV4`{j(73oxH$^cvu6a~JFEk!)`s$JR%Y4{oKbcyZ5jkT#xAE0RIrS~$S zzNm`goZ=jI;-3$jz94G^>W2_)^@h12;0dVb-Wfv7X8ETls6O$}`!swR-OUr34MTro z$yfh9-PIAVf1%9YOkhtS96x-40quw}Fg|?4|DTOmwR7kpoZ0 z)h~s{tnBf8>lhB&^o%{-@9Ici1e$7p)%aFMOIyD$wna8#o!jI7dHaV zpA*V;(jkPcJl4R?IT3Oj%6%=qre?okS>`=tIw)xbCDF>s%Yu5e)|v9DlC0@nw)Fzd*=g`?)N$cju53nA=V-++7N%xbZ zoI`HipnW32N8!pLsR%uiy-l;na+0NkB-_$SPA&0Y7AMqG6P7S>!efpwd~N*g@t@?e5vbUW-D^(g}Y>ZX6|dw0GlSv{2_$;R?hD$C!@=SgL_$#1A; z8J&qdW!gqkLOE5C!}7CNhY}3ZYt9{0VKk)8dbT=VL15_L>#@l}0%smzi+S?ZKXGSY zENfN1(`v!7d`d%!d+0f9ztD={#l)A@f@yz{!`+3r?6wuSS&D2-?XL7~<;q<`{0$B9 z+PZrCz^Y?`X6UM>H|>4vyQ?0&StXBq6TcnZ;OEJaEajInuZpVpGt&!v*ihEpWdhr> zF#!L!tjnBbrA@S*zT)EF%k|n^kbrkmrb;}|+v%YV-&>YVc*y`jT9W+jF1(TVxY`}> zZp<`Wv*e6P0B>#Za4sV(W!Q|n<9$jyuIB&u7}|0Ch`&a23S*XA+PHw8!pStMPf^9G zGNn7K&)c%txj*qYS~ur$iVWB&Gu50+yu8da5C@FNKo}hu6_3~i&-Lf?<)oC?!tOQ^ zPfpXVO!YGHHk$Fk#JAdtZ)>;=#s=yhR+alT@1w9A!*ly(yP8i>C&vdO-QB1ChEl&} zcwCRHg%;|v1Xa+KU-akE6kGSAXSDaUcTqxhT@)Z;dkn!kDU8zcg55c?`_KSz_JeLS z0J#-y-EZ8jB$W+46e8Q~nAm4>g+$)ZuUD$AV^WY(IO#S=u8q-o1|Wal(o@r=8ApKp z=nAR-yvrqf$a-0FufGo2T}Qyf!>A*}7#ZTbHy5Jis&Bv-EVLw7%w|y7o}qQ2(F5s4 z+r17;C!^`?H_ftF?ym!$;I1tBF0a>vDcPf0c?>|dTVHpK;NC;2V7El6vewZvdh zQ0b{2h!#Y9rRhRTos(by3Q~GIc<4^E#gJ%A`69g=WrYRjnIWt(&gwdsAg3=#@G&-y zaobiIWi8{tDfVA;vVBe8imb#un}9GBhee#!WdfXcy&g9TT_a6wmlv4bu@YJpDN&Sa+-QMHExqFA;FQAGG>E* z5y-_YZ=XJ#ELb;Y%|sD(_JgiSQ=5r|Dc;>hm#&^7)zMGYH6yI$dBM!SOTdRXG1Zz`@@sf$`T4oWY4o}fs4Y?mV5u+#7op|%pXHGtI-;Yg94ZV+u zor!vdH=UjVJDcs)ZR)H;=a+zCq2&=yd&oR6C51P(qed*5h>=^W%xoL7Y+!ES_s_Hp zRdK`o5xN99V#$2=J)L4|4_3GTq**2-UTkWxL5k-Petq71woCAtmzC8LFkISqo74YF z->#*REJ-m5#XYkQ5IWIRLDzGxnb<#uTVC(K#19~qy~-=%3)n;9zS*93()#6>_pYM8==J&?cFl34^b>jL433i27dnMhr6o2DUhy(UOZV(wKCA;S zlI}}(fP&C>eE>~(RG_%GF`t_Wbvn|aaK>xq@BAu9&eY-=e59RF@svxVY@d(dV7fe zzHIj5K_7^=PLAkKU;yqzNkXISu2peHbFF-?t%(b-@BRnLj8W74bBu>qSR^n2(jMkJ z;;_SM?lT$YlH3zP*XsfY+gKv5>;Hv~g@fya+h>J0dUU77NP zI6E95IopE9Q|fyN20({{=o3-ZQzes|8ni@#&U32}L)l|?GxJDn2bY$PxyYNxiJeUAXPiCO`ap*`Rk|L+jPk;=eHOdn zcGmqg4ZdW0LEq!EMQM7&V?W(T=fAS!<)Jy?&7ZSKd4<2S(l*J%+N&48u=B1I=N|oH z2~0Et{@ge~VUx*QV@_@q9#UNK(QhDF%OkINJLj=_*T>smi9%;&5Ku&LMBd zi+0d&Fy+a}{1xPcA;Q%0f0EAop9;tO@CvYQ6r`K>o~jqSfLZ(JgZLcCbs<6ti5 z_Q+9niW&Klgsg#pcD%);Ck-&kpGq&ygFuzt+kFtRc6IGmJu*&;8DAT-4pYM?DRJp2(qN z@(uWR4bD?1RjIYGR{u*&Kttp@XZtV7T;%=373lCqM-2Te%-XIf;nAz2$#P`zL+Qvz6OB< z=yuT7HNTHNAVehjjlKG`GT~+QzYF;+)7_T!Yq%b&!m)f*-M#>H&P9%l6OR z#jSbEjhIi9WIlBZD_+6eFWrfIbA*Gb!1GA2hl1m?{lFs;NiHuY>Nj_;sVf+G^xwzW!ze^gvNZpLmy4mlrJRRs+4-ravXJGCgxU*UXVlO+;Bhu z+r!L~x(lg1E;AWfeti2T1{l4!`QGFbP!+|`dR^_E?%U-seS=7|xBulorDlCFo4IJuS1S1BcK~RTGW0McvURll9mT_Mqt6-xURL~g5`vE`7*OKv;tuc2Pq*b zy?;XM2xtAmvwI%dXpQ5$t(Cdi*$BSdFD#ZCe*wezZ-1t?O@y|4{jd@3X`B*Qx$;VV z=wcWut|$TbVMpzs3cU*UcTj)Zbw?<|rss#m4?3ZV@?T2?R6BAwQTJ1~9Bw0NL+$2_ zkztipTEw#eSveIS3oc6dTE~qsRgGy`?f?z%QvK)Rm9;8Y1TKCw4~l{nZsK6dG{7hm zoCYwdC0)d=|L~IKuh?SC)GAb*wP>tc(^m<;QlLhv2Az%WTTeBFsmQH}Ck0gt!aX-9 z-iqtRKf49`y8GNFU9%xZ%1Sp6HC}MUn(NTf2Rgt$h5X=62WP3;Qs0n1G@|)^4on805IId;;UmGo&>V4acA2ULn;XaxOz)y(9wTS^HNcd)tI#H<{Fy3HB~3TcX$WI?r)tvhGs?oREE~$$wLd23_l}Z(j+$#OA-l6E->RPEmseMaydiQ&h=5JfKtR3f)9$5zLGaRqh+)0yX5oSBqH~kX9TiaQwKb@;_a+Rn)+T$^^hDL{Dj6zU0C3Y+NkdmwQ&bU+ z)S)Q}>eoW8Te-ry)2IIUdyz{y?44)4Jo6RqMDbvq{3M$;Chq1ZPby|BG<5;q%K*a1gE^Zq37q%?T+e#`K>-$bbrYC+O|jBOVT6B$Ky7FsW1OhzUeg9a30eQmPQH} zPs)d>_qKEWetz|dw)s$N!Vnr-w zH3l`%Pv>@UH>1JWt1t;ysHxrjg8J{?Ngh+J0($fE-O-|CRDl3#moV~WPFC04&JO1- z^tIdUq&ul!)Vkq<#aWHH+-yzp!&2}3tp|M5k`gcDM&*O^R`{G^)4i&+gpl%2A(TWy z6{Gnaa$TxH!G77gqoZ&zOefq`93M%i&_B=a9U&j-1Klerbp_5wzUW=~C55|#xhSXd zNO1Z3iE(h|zAc0_TjeO9Wk3TY*0UuISvR&cu^6zVkH5V+Sh;cz0l6xA&Ne7^SACA-i<1_nPgU(>}7cDh`h!agC<+7?o+3Sh{e&yTuqJ z)bidgTrYfJ$kRGy6^JNlLi_Cxf`uxrW!>&Y;RFpqGsjX|vQ`MlaU?tfw{SkygJy|}&g%*^iYqogTMs8HS2E)T06;x0kk-&pu-_#Q zh`=~lvc5|McW2hbC^W&Q-L&V0fOgLPK?~Uev7S6PJT`|05b08tq*ms>CXEqOQ|894 zVQx~cQyEKt_v@Mt7rrSqG0d6+!tZ6Z=e!K89$6PvayC%E zq1m{8DWUrv)PKXxwG%{5LXLrlW9f{i!Rwq|4`w~JLPF{SUxslM>N__GfpX#nagPs# z0&uY6)fD^CC_8y0(!Fd+;oLp^rPsQFF2c4B@5X<*5X&bB}X7d;9ZeuGte*pJ}# zCHV(%cdfSerr479=>|x1vzjsIO22k`@?#oPkp>ujf`Hv47M)(r9TBf* zJFQfV(dW9%*WFYyD3AZKe}XUX$~iP(8L_uV4(`h=ag|9h?FKCyzdkIVj$aT)@Nzm} zA5n*>9i;WV&^q}9?<(_of%of>Ps~B6i;B4`;4}{i1w#p;v{WE$&V%jX7JC~o*Q%1r zrg$?^p&EXd!CT?wU!*ozp^c!wAM@;|LMMvsyXDJ;j?4I3u3xMyv5a_wHB~dy`t`lJ z0!E+##BE%8tfLriak=C}lnYR~**^WcpTfJo=m5WW_(0#f4S-X33DGurlW`*hu2@T;1(_lv=~5!M9g`)Grn=-d#=Dxdozr zi#oBOW>aZ^RuVX+I7$45b+b$6fMp+N`z7btKrR41xWSb1Dda4cqP8A;2{PsMP9LHr zIjR$0IIBq*Xw7{J_xy#bFdzs%=k6`_3)1tF@S>CqtMY%zoxb!O)gG`v5?mZr?tRK- zlx%UjwtKUk=L4`!>v8J#ncfWGCWM5{YES%xP&;>P$~vNxPo&^=s#HrvhP?#iVu3mf zw>8ZZ&uZo8@mNeADVX>EvL>;GdvR33Nq`Q>G#AU0Iv;VzXiBXU)C*SKUos)hmJ_!b zq@8CD73>w*Bt*AuZJs!P?4#C*N)bcCpF&9SRQwz=nA_pwjid_lHEyexCqqkB?iN^s z8t5nz-I|?g(b%-;adBuU?KtXqqrX@nJX+-2R4tmouPO)(UGjvNAzqz7quev07Bg!W zZMLHG>tGixSFZe$GtGzMqa$t4SArd|lVJ#UvYwm8VsWkYz)h*4J8RRQeTRSbR$P&W z4zK3&JX3{In_AmzfBB*(D|>anAPqJ6o+R8_R?Ixc!DKfVuU1jUh~ji)a=%68f$MDf zjUEO~s^g?q%G^&qaD`e4)qNG{49CG(6Kw6u$ijDxXGE$%jxyQdcNFhAHz|drPLg1DTx?cXtlba6-LjWYPuTFbkL*? zt_S3sD*5B=jc9;JVa4y2GkCfPLvX^(VMrTX%wqha@gqxu1RcZ@&U>tY+3-|?)RSML zcaHKzpQdF#MH37ru}lKL&(z!A7@p6p0)v`}Mw!*hABs9#kL&by&px5?U5qCDSm)wv zzc2p}uaW1Eu(a7!h0cHntbhvpO7p z4KF$sT|D0#+nO{ycrz(LCtW@t$JfS$H~cFNT`yfTB3fW6PQ-XsgeJVxxeonfd37_8 z9n5*d$ZWS)ce!GQ!{dp2=O4_WK8aVoj+5j(2grhOIsF4N1j3gGQ7`)Qdpffd*5}1LTEF`j09LJk=jPu0cYd}wKpYO$DCwF zjSr`OrKlnC>V z0fyNTJ+)3O$HkArm>~Pb^-RNp%BjM9%)B_bXH6N--k^8`UAvXW+g@Lf#?8`Q z1e>Ca_>_8eS;2K(wvZ3!4r32o*#n!B!Yocn6IFj7QyoouE^ZB9V0FNr;IY@q&=f)P zdU|MsKhi4({F6zWW638<$V`cK4($9C&k)vz~$UHoHkjFifz1c?eL(i%B&hU`z!}YWo$29{>3DWh+hRsG0Spm5& zsN79IO9KeCk8n)M^*6BVG~AEm6!lBtSaTW3e*mh%E)@Eza$x=yQ3PZ|Xn;p~!Uz|* z_##+pb8dFljVU}XBm{RuDEn~%0%)xD{SRh$Uld-!)x7KBor5UmZWoyocBy2P05mQg zm}trVsgQ<_23xzF`%y=-tS5hT1%<&f0YOmgv|FQT;=LlH^2Cq`_-s!zqif1Xsiv~CeTvV2tA2~3d zo3b$eLVX_VBcpQd8_0BozO>MZDy`ZFmN5Yd=2bXWVV!N9-nbWi38*`t2FMN=Z_imT z^QV_AHDEE8-27^6EgDYWbOBbb#%nsm_Xm&71Fwrxo2n5eZoH&=YBcXAH_uMG+ch1C8_oghly*lB6n5mzC_v3x*w+zo|h5V{+GN9&Ot!8G7V*R=EOCgdnE+$ zK23Iih*u0JRI_Q^8{Q1GEVS&N&A}Nk24_RwZgxH;U;OUovn{V5;`rKSS*#CcQY7#* z{e&5FL1QXz#Sm1Mm&#*C?n}cee|Y)o+2LA;x&GstENCWQnCzt;pIhA8ji7@u1?1?% zRZ6#3n)=Mqb6>&C(KExG^V@vQjp(1k7W?HuSMdXg%kx)Sg6gm|^!o`7kUX@G71t&D$QM_CdwP89=!Nc|6oS;%qVauo zB{N`XEDiAJhWk1L-C&IcZNZ0V^((q6bXVzKqdHbcz!|zk-n#}W5lJ>%v&T}uUz~YGp<-_zI523P z|Eb{gd_83E9OC`RPAmDlHCKpVCb0O$VNb=IoD~gDmIa(NG{p}7as-VCnrJ_&u*T?e z)-zRHPqvIC{SJ=uVNm z`~tV7qPiT;JK}YAW3gA{6)IG% zsI8f@ijb1ZiK2nNn)(|iS8k!B_`8jSev1%UGg-}w?*)yPfA6YV?9&pm zD<%?mr1<8=oNN8Jh6gXw07)Z1Q6+iga(~?9HHNA|ebgz+ zeWJJ{SY3Ddip2af%&nt4FLZID1 zMD)3b`{LE&TZI%A949^uv_*LSVx8CUrh&W=OJC!(AP^CX9Oc;Jc}@ddqU4bZEvI=H zW#HDX`&YAvho}04)ODJDS03M-h`gBhvKtt7LgA&P6;PT6TAwJ|dS9tG33gba=#AH2 zyr^S%bW>+5*a73u?JJt|(wuaX|Jhc}oqKXbTvX(P?lKe^m2yyygQX&~TZMSZah;GE z8sI^D@q>72O1xc^&VEezZ?33hx1FvwN3P?NKbRx-f!UgEd$kGWUE;A?!0dhT!(FK# z{RQdh=bCqP_8ZrrKx2|tK^ejcJ_?f>yB0H~`0BJV=T#BQYay}kZ3o<}e^YSwiNshd zk3Dg}NkZ;!K{!Cpi;?dpE*NzYR6U_Q74naKeXxLEpfqtZ9 zjeK{TE|~H2yrhP9t^cd1Q106uiL3Fq(gEChZxyYV{SQc$)|!^mB~GnlB%CBOgR$Ef zl`o&)+%mNQwMRhR^7#>teZb(pe2if@sSC8E!RWKxIOc*K>um<N~d#e&?%j zeM#lS!(pa&b*yBL4|EtwPPVt6K}svf7jNW3rv(g*^rm|;LQWCSE5*GydP)itUVP%r zS=c-i1Zr9#N1trM@?hw|Q(m+QZe>c%F~sd4imhf@$F!gm$Np2c9Ai8U1;R@+X@L8` zsNvM~X*q8JyBwooj7npv@N{;1u)Ae)w&UOBASt>!`5uO=pwp0Z9~yw{Km(9jv?ly$ zfMKPA=S0y6c9?0R>sBAD?XYA1S}7i^9S1sf)S0X|8jO`6<{ZgBtexH~nH5fNk~A#P z043YI<(r%3vvOd1k)Xr*_U*E*uIw9I1213I$tBpKb&@}V)PDP4)fAnUF2lhdb6Y4q z`mPYBXU%LW*)ttL;C97-diQy;0cR;b+%y|pZ!K1$Rny{X@*=J{wTFLh+KaFeloP2b zZH~6HU$lXXdla(RUQ#LZm%Uk581qJ+zxcVu-+Uh^ah>|pE6G976wF`U-88>l?7#<2+5mtg8tUpDIMA(J~H(>rWeI--V(Di{kS%Vk*bo0CQ+sa zf{n87ddd&8!K~|MfIqUC*>dx6TvDtAEdag}rZ&DxIvcdkrn6v4h%^njb5VZx(ZSt@ z(qE{q`$%Q5e0A}(pSSeNV!Y(JTQPZINvWFZS!A5eAir+XTN*B@W_<^8c3}Jv`pyv~! zQrf-o^|I4s5%)VCNFbP(|7;Uh{NVk*M#FNKGaB>#+e486;RhaupucL=A!`3*XrjAk$ScmRg}s8Q4iTo} zgb``_s;>xv{G2mm7F7GMkxUCLC9ZHPU$dn)shL>MfhUV5A@F6V==LGT^OmUR_6 zXU#^$?@T!HQ#e`yMsCF&C(w-TOQ~G^P^l44{HouC_ zWx)5V=@p_RchceT%27Bk^_m@4&tWnBWL8SiB1*bJ}Mtf@C z^saTEVN+YK4JN5Di@ul%?NoKWw%m;dkT{>Oh)$?ixio6;z?;{~UM@J|e9X4j^B-T! z^U>VPm$#e#a|y&5cV&4N*>5ijy3Y>y`L_=Jo#Mwt7sa7i=gE29+Lb=OBebc!(@JwVX&2QQN$yGuomg}6DxsE4XBq29qLF2C zm#$jQIO=dD_u+-p|4g4bV0mh&#~q|8iuTZIPSQZ}w#>p%*(&q3h~X_Zg&fS|gTMJ; zcf11TUt${9H6zDyxGSJrNCRB9wN0+F(>}=4ZGOj@Xf62exX*AN!%YLE#yU}i%PHQZ zI`yM_@z7j_f;(GXrVzK&N?pPC<;E@sE{YD=e54du#=AN&j?<|Lkr@F9;1!Bkc_+7oFJqG|Q)*1UUEN&no;k&>VxJ4_`9NhZ3Aa+bQJwdI`bVPfXR)T##qUq7At-}a+-HjtLTha zP{r19aRQ`{&NK|0{EZi$#lMW*XaM?G%6bXlfmL9r5*8jjM!H9)8#uPN$lGRDL^o`n zz6S3LqxAX&P+|FTJBO^xIgL1&F>+KB;lF@*`rSoQbEHU~Z85hN?HmVwcQ+d(K1t^LG+OQt^xx~BI{b|^1czn0W@`MRxuAf5QNefdMl)?$sw|W zF061s}6sg);WMM$ z<@4OplmE{1M)c<)fylDLGJJ2_=gY>)s9H(Nx7&BA7;0$^IX+Wjh1k)W1jY;;1iRm; zRcc;7-&Cmn`^MTV6tWv!{4p<^21svxa&qzj5kBd)$2q|ie!42^#Wts8EaD7@hL@G! z3oFA&ldx&+Cu~g!zjD0|A3NF5Yd`ikYg`=y9Utqn9`^rD7($gJf)F8Ysyb7}-ck!+ zy}ggj6jv_&{$T!&S7_^Ne0LA$G_(XnIU_614X@_r+K=Pwr&+Ww6l9r9b(;`S&M$+q z88#*o&ipoQAxC={N?@@rA&44V(v#qNJiG7g>ABztsZVp> zTEbU~j&)_yX^ubRF_y>UquzFSeQX*DPSYngYJYqvTMvNdsiIo}~C~1Ic0WRj>Vh z#MdIDfp3{-)bu>qgR>!C9W=lrCc=sghyTJJ0$aM+(a}>3xG(g&m#U_ES<20Sv+)n+ zn=%bhMkyUzO+S7W=6nM=RP=5pFop5EEHU>3R=qpiVn=k@0egc6$hMC$qN2#&S+dn? z1;8l&rrXtow!+~{d3fr zE?N3w)nLb%2W0Ko`f3{mTD6ty8OJt#h#Z;7 znda0$zjl%1Gk;SH$$?p2Vuvl(29J)8+(X=UIY(}<)v(mnH7A)Cz7p|)E_8Z#HKaX7 zs{}Yhq%wpO6{Aq8kl#XY09@ISD2=evzI^$N0>|E;%s+}nuGf1GrM+?~n$7Wn60wQ0 zg*3pUa7uCUc4mZECzP}@Hj{gs<9D0oTNZIes@Y|?_yrEkQQ9yp?HHXVN>uVgi#AUx zpakPYJ!*ragt^n6+aD=lSf{}c8g7iq4^Uih<;cEfP+cVA_dgwE=!l3m@#QCzaIi$x z;5t~piU4?6w9V`joIH}&np!W)U&-?^IjQn*s+Brek!*1$5P`?eNcPhJ3d6a^jHRcd z3SuK}OKj$Ain$;4qV64+f5utK=XgjsjLV(HQ20Wd%22V4IxP>1b(P zdS~=>e@!ofaq+r&8lPzdQ-=?9Lq6lzU*5v`bn95JkDpt_%mJWYwPl4rk5z8{!Hmjo z%j~tTpoSQRbBj{SuBvYVoauZ`EZ%mMqY6jkT|J2(M$C% z2;_`VwS17Da^KzYTak>^l|Zf;Bit`kduqK&@!)S)QL%uYzhS$B2gY`&q-AY4&mTSz zMscg}t8he614n07S@&x(4~y_8_N7+(wDqwfy-Z%d-4Zb#~i8&K~T?OE~i@MB_-v~n$x-2ws-IS^3$|ej<+Z#q%tY}- zn5J-GZcq!R{o}Fj2s^Ub<2j94^f#S^s%M|buGeS)k#HkYFdi#L4ouxEtZkd|<8W}5 z^fz>Vz+V2CU#9})0?iRwn;!Sj0{zBP2JGDCNc&`#!A>7iT)xwc_`ZY7j=(tq{cL<9 zRg?b8N&X*~k}L=8)ULO!>){{t%R?+ftP)E{J{(|`uUN?FdLnYnLr96?<$uQU;`~+k@}D_ugaq<*~_*`TrWl&crT$S5M^lJ7EAMv=)nIG8#eAyE8FNZpoN`44R<)uG+hAt~#fG*Mi zRPw0ir z;*{~L;>`=1h9FW(SI4aXqdlPUD>Ig>eD%KnSy^7{cwDeVsj>KF$!l^Xc~?WB#o3>? zUN6O$?$yIzKT%zh|B9jw#plmyfKgpIdRskKh;&pRXz;4;`sx7SpQoqk* zHn_%QD0v3t(sXLm(4k$as8ZP7ndu~FqZK8K`u{k7fv%~%xQxdZhQ6b;vAx4sBkbL0 zX#mLkm9f$u@>K!#NLb1Xs565xqoD$3UipYnRxNS&p%hm?TXwD z`Nh9q8(ke1aqR>hNe*LnU!MCck=f43NrM4t1J;z<7-MsAOH8}~xk0p7vy=xUGHf{q z3~SB}o>3+sUzWL~)x`46ziuHuX6B@OVzS&y71x_+Jor@JL^!XPW1Xv#$GHr^>^9N@hR)Z)A@{=^OAURFz-AJ;gN?w|FSz@M1u3zHE8hJ7+{$ptYa2wS* zJl&4-CK^bDc@1vft&^5z1v9YYFB{VUU*?v8K~yF?_fy{`N`Q+*LEK`E`P)7EJCxiA zrW%SF5aE6vvLyw7KvsH8ZK9UeN6m73YzCKLUsZq+jlQHOKK6W}7mPGC`hxM;iY{5@ ziPsdRGRjLM(wRZXOZb)EYHiM7pS0^&uH1i2%sSNAM|HN4^5&9$cNY%;hgq})6MAtO ztRikupVb8KP{(nH<2% zXspoygKcLcQdPvP^5A2PcwPG9g^NeoV-|6ycbl{e&(e*!g z>@6}et#p-g0~SZIaF=!u1Wu(^9B2jzu8)&WIR5!261t-v$?DCt2!*KywFinKR={t66AEKFc6^{;y2!DaY#kKG z^{lbGl**3I*y?5dkdnm?7h45*;mtrb3-zL1&_9Xb-+%sxENXi@wTD_3;yu*AO;q8} zg49HZe|%dV#Jc3AY}9@B#Gy=_{oGd(cJCZw<@VjBKF^@JX_CLiN&jHI2?1e_J=_;4mytiV;kN3MdIX%`5U@Z#-%AM7i=Zzrjrv1_N zQFAG;i?)?y{U2MYU8mptYKs`F9cA(pQTVVvlhSKHg=<-dfeaKxQ z9A8YtL__fFKkv)W1PVhTWN7A*of+xdA;J{iH~DU`)o!$yuH42WB$=zvQH4l}&>C+FoN!}I$<6TF z>}g+Ier56YtNu}^_hA(^O391qn04&1x3W7dD_6I6Ryp&=l51vad(-F3<++0hX%!BS zudKeW6hjXzTnTd1)Dt3u^`n#p%f6Swxhcyo*v0hL2F_S z<-;1Iux~C**#7qJ{$Gh_LP<-_f0}pt>m45XxjA5~OD8;#_oz(|-x1P`OhYUuva8rn zOy2*x>?eCiyYXBB^FvbhT#FJ-4%jy#(`Dh~CW=1@>w4-~Nj&BMuXh+Bh22Dn$QqBK zYT3yFr4*J=aPMJQMnbn-x!9x*Jx^Mp-*E-5zedafDwbK+yb96MV?LysZpij2O|iM4pJcPEInI)EIG1(O(| zA(F#hKHn*ox(HH#&wU?`7iJ5(kW19Up>AN$5a z!LYB}148J^Tg?uJ5mwPh6NM`p@)^!GEuN|qcW;Q15j>YjTxcX;hWPndT{2ZJb6WhDc^OMA^yc%-Te}y1I2i*meHD>rFe2AZj`d zklov!N7~mPrvW6C8$7vlBZ)S}Epa*@U=_->{4y8mz7XtnHm!Y1mK?C1V9CQAcasUe z7=40RKe}Axecsb6A1hN-t_iVmf8bHTa8Pr}d1;8QN>|19?voSwmJMK zqVW~IkG})}t&g}qLJs-}^3-2Ws~JdH<*$@HcQJIP%-6vgg@Xrb(vBeSV_RFW)Z6fc z06O3^ReLgS_5-D%{M^tjv24;Ya-=Xx2c@4uCrj5_BV6{-!p#o{`=pq+mNU6gGAo)t zH}y%6pD)Uqg4|w%k#j*(4+yWItz{JSa2 zTA-diYWQmklBf_mLbS7fgbtZ!_?V=9HuNg#YR<}r_BDE@GcwMe`yYswxT%~&DOk^8 zhcsqBY$x+pxo##5kNZA5sDOHY;cNQVjM>JHVg(i`Q+CtB_0an11}=_Vu4!w*R~AMF z3!3tmuakvHdL{$~7rDS9(8vBrdFV@2)L zRI0Q^7{yV%I*anU0$~Wx=-?1$8Ipw-#;h#MA}UkFbjHv9$G8*j6xt2p|5u2$WSZsH8Y*?*-hmB8#bnKo7J+CxN_(yn_0w^>aqKm2Z1 zF^;osn=Ax6f7h9;_mly)w75SfiatMji+jtL_~)J89stZ7f5{f|14&NgC4cHCNh?GFTi#?SEB5iDh3U(k&PNb5?s z22;IKnI;Dt2H#!S{N3aC6}SWSmF-t8E3xj`vMCjYt|@vwTm^Tl5DQH_1Kke%ed>;C_a>c@Z zjJyU7&P6fMHJ5m%+%-_2_3sabUmv;LRlirdtNP&P~bKQiUVFfiQ4P}8?PShnX1 z*H3@P<}(FMe|38#1xw%^WpAv9dsE+4YAC&_z4&>sw_>!gP`ly&JigLq?A-73`oH|V zgOdEf)$^!DLfjvG0xiBAI_VbMOuG1~p}G$kf@q^$@ZlayqyeO3wm)N|PChotV;d5BN4ISd<1Z{Z7xkH~A~|rr=gYzfEmHQuy{% zj^=$-$9o!JRKo>{=0#`TYWF~M8R%WR?O@?_(e>7@N!vvOwJj}BHl=IW-96Bi66ww| zB0nQ}rCvB(GdDYNjklIgCO_QzfkizBW}v-;;=N9wc936YbV;K-oo8fQ7O(<$P?};+ zx%9Jnt{{cR8Pi*!rgI;&r=-BtE}=sjU^wefv+HrIA7e%Rvkqrgb)CohJcbb{i0iH` zA!`mvniU z^TmGSV(o>;8q7Y>@jPCY~z!)w)? z^tTb?6L>Q*vg>13$twQY8!Zm+QrTbTE!j=e%Ut~$9djUV|g{Dj+VZQD06 zMOKCW&nT6UmqkbF9-;wkNmT_r&)%-No_hR;m2ZB2JHVh-$BES9t!aeGB}$Z2JM?5}E3eV<-Vw`;ZpMAX?OmoH%E6@O=B;1lWb{cgcD zK%OlG$Tb>3)vgDfSGc>!7Z@PQ zL0_cao2|{Ah22#s_JLL+{I{>=yP{7cGhBF0+tMur4;0?YtmSrm709VECzH&EIHMD( zF)tSX)*=#`L&|l}OtpAkrt({W-6F%w&pjab-D5VBzQZ$|T1{zyo24J4R*IAh{~+C; z!wP{~iz|Hvn^m$S-0f^r73f6H#EjLqZ@PCMm1Dc2K-7Qi!Koxa^4um306KjoIm-~6 za%{><@ON;8hpBgYWj9|~$U+nX5uJ#qlq}7Odb@k3WMEylPvA|c@zATHfh{{8r+yVi z!MlAs6(|SnC+ktzq!E$cS`F^W+W6Dp;s8r#B8-6TRU&az7*v|fKH779Q2wfdl~n8k z>LT2M#M$+Dz5L7V?@{Ibpms%`&zK<+oSu1bs`DFX*+&>79(HAN=9U+4j?1EhZRafT zFYl!OilARKUg=hN!E@ynXpl++6uM-7PkWv3=A*5e250B$x7m@Adu)HN-0N_+6ZB_w zIM*@y{8iJS;xp7EY-oPGyLZ^7rbo{n=xHeOE<)~f@-DHe96&!ITqvtUt)Dhel_v)r z;Ye0&=}KyafPlBK;&_3ET8B;v==7cesi)OGuN3~y?AL35;HUgwt%?VD4xR>|7P@s* zk>it(T{p}GF2jZ1$h=)n5tTpV=?;aU;8@a`E_JeTI<#rs;Gt+ylHB$Fc2vBMIp(;^ zg)t69-6qEq$I`*;Y?;Wx)dx%R8TIaGGm5_b9Q}>-Wh2*Q3l&+*P1^2P&*1FO2zf(F zuwn4X8Lx6ik)mhQOf{}bgD*i#Me6)=f&Z%K@iG(`7P!tR=yI<0p-Z}ft9;k=i|vqa zzgz9aZN30Cv)|+6A>$6%(O7*lc+B3H+TGSLEm_<^0yfN@^3Iu;bv0j5VRw3LR1)_w z=xqacFdjR}H8zZymwhLP%Q7gNZ|!boI;~B;gMRm{*yvkWq>uR<#Iw!d+yZZmjrD*+ zrb21fo0}6dA}=(Tp-G8PAw+NvGEctKHcAO2F7u?zy$fU2=Xd>dS5W)lBItUm^CjwM zY3LA)oV=*nml1kEaiqjk++T~$C2m`60&^Z)&N^-l@FySZMJi)D*F9@2)c(0b9aXC+ z-IRdEwQt*oos(EE4Swr_GOf$V`ZGz%E&PqB>>LATvlAunI3M z*NiUl@G{Ck+RO>F$mJlSXR#g;AqNr_vz;8=!;9-pk+nPdwN4oX>sE{n+vAZ>;0$<`}6jTw-iO ze>*{S6jLVVQxeS}w*)e1P&lQ-_NR{=O~xb39CZk^-Iz-F*ig&wtE#+1EVYvhk{6l3 zd|7d|A+s_4<)Bnj4*@gWZ8iBKfC9jYREf(X#PqM%+kK(ge+&=>hk0PAQzlUB$S&xm zUn{Y7xFE-r6yuG9R(XGZnQJ|A)^&ADHjt{f?}tekxB%Wk_;~5%b#7ci%3!>yD!#XO zENb>wZ^T7Xn=f+eSH!i?q&0LpW?QB+Rz0c7L6Dd*Ky2=sP5V{yr9Cc-ewjDH1n;kO z0%niE2Rox7^Ed>tHNNFJVMP?ngy;o$Q;^UX#wTb`b)aU;_|z3}}!7P*917iDwC5JB%0`Wy{6{p49+cebb7^4^8qx$KGQDVi2MTZqg&d zz5Royd@GH>7{he1Wi0*I^2xg(z;18SA}}aL21fz#l2&ko?h`2{!R7g^+9n6@KyN~- zC267C|GEWYGGkJt92?}=@Kxp*_QkfkA3i;EjokVP=g0SYook?%{(J-rZDK-NKmiEu zKmTA__0f#9;5WAAQk7@?v~L`#Z|#0{u5l;eaSbFWq2t74+pu384J^rtb@tVoLOuYO zAQ+em<3BArqoXK*?Yqr?O^60i#XV-c)pvUt9bI!yp6^zCk5?qkJq>={-EcJ>QP~myt#iu6C zy$$k~m(KDmC?J8W{Wk>C0;x};&)(A(SM=BRx4wC3tog2u`=-s?r1s4|I=sJ-aY;;FPIzrU|pvuq_q5X{ z_E6$O9Z&M5oJN1{GfeN2^|kL7KlsW-kQh~UWp$A)a>=wi`JC|IJa)4~i?h4PWsm%D zT42N3_JzQyLPO<+!!cwIO8oGwVCQ^eH9fCAgo|a{G&osg4Nx+T4D^qU~IsV}>AoAaT8^p6> zO*Emb6o3%#mv_TrmGcg|JU}lkoghu`V+H393PAE03w0#*^$&CTP88z<^b{+TCA8P? z7#Q2IFg?>&uuKR88`z;A3B*@ql{R{Rkk#iJ-5b*TbMAcw^ey8vq=@P)719q3hHR)| zHsj@q85V?xDfEr+ALF~Ld>DcBW5UUD$rcm<6Ic`d^PkcAYziSM>I)OQRue}%yppwj zbSz$DVzkg<`I_AsEk#};pA!(9sY!;y_xBm}Vb}er&qw>e^LUzD3yF#}D>|d2|INcv z0HU3^ebIVst)4&RcddMc-hI?i+bYzt@M5nM5@oXyC&Qp&0p6xe!0oQwj9EPCm_$j@m z-IMIAx&Q?eA%D4;#pt2NUYu!Fp)9=kx#1C!Y_1AkA+Ht6%?hb^Xy4sy=_IWKg}Mug4Y`aif#M;?jD zVbyF-_X%`CLu3kWvW68*Bo)O7qpfwWRBF>2oE05@_k(02Mit?2>6A!{B@kvKk2o0y z?s^%22AAaShq>0Fcffgba7~;sk?zmT5>9q1siEQR(-$9mGS99fFKfW>_ieS$a?UmP zUjFRvFaWYtnTbBGX8&w@yW$0yv|J4r9OllV0EkIScbiKW2i4P?!ljhwU0;8%!#Adj zSZJQUE;_P3Kz4WL5P#tZR{}B!JtH@sPsx6bbK*#qp<(6lUb9;_54{6EMWkX3mb9I! zye2eT;(Uw1EuI1ag>H<0^!QxaEr2dF8FFm*$HbzMiz9y0!@Y$NSN>!TE?L=Q7Swja zsQQ=(1t1KQiHR9E&c3W*%C43NbluKsBSUYgV&$-A?c3z_{0x#`6)A%V!+o59_g&uS zU5YNLqE_g$jF@K1eRHK_4Q)w&OWtfClLb@<^ zvCWT_vV|x2 zLtrs09do6ks7Hl!Fdfwa7S!qc0SX|iT`;#ZhpV*JY}xa8PIHg!_54|Grm!ufRu^Lr zwjj(*E<*9Y$RibR)Y|yyb#TYr#!Z1-Vj615KrB=Sen{*vAiU4@#R!*tdQE)V^2&?p zr=={zvyDjOLeb~sc|S`C`A5M<_}Ip4rM`}>fX&*y=wBm4Z@_6|Nm4{IHDT*P)ncdK*xupbgFa@A{^&D+dD02Erww`a~){F>{Cp8QtFE|7hKX2-zqS8 zbwi%KUXdy8?%+LoJm|M2ewXR;>NVqrbWcM?MiD-s+Oj018Zr8uL^IruPCbD>ggf^s z;=$9R&!_nrJ!;o12GZ5Nicn{PvI;0Ja^X}BLYx+}fyuNoLwilwJN{Q*_w1D4E!8f` zyuN>c4dQE!TBQJ_$nPrQ7K4M;B%?doO_GDcF*n!h64q>+0|9MRT{l6;1*CpD`JV08 z35@*D*PBUf3tMM;fbT;SgF9r6nc|{+OtaQg@D7{ccCWk`cZRskAD>g2-8{$1rsruO z)--EGfVsk->@~FIbU?T~Ayg9~LyEbMZ5DPks+Bz17rq_t)*yvWKpDHfM#J)gp_)i1Y}r2rJK4F6*|>>HGyYF@m8Y>E_y< z$(DCjf?PG>QbDU_eh?gn7|8|q?oN7p3_9fedEMmw>yf)42BTZ?86lS=25VLQY)m)viMT)Q)VeCQ=~@=ArK>E##BVcN}kO&PyNmc6Ue zptsCBdujz7;O(Gnv5fpC8*=0}JAca4wg#P411+AU(!h+w188(47Miw7{G??>kl%vl zQUDUNhUgP^5n@n9alPP_nLexWI=)2Uajh-~__Sg>daHt@cdmTtpe0v@`Y-7QayVjB zYGrY!ZE<(Bfg?c!v@8k9jCf8u9CkEn@Rp$;3?>W>OgO%IjVrU1`l8BS8?uI;4u#6@U@~nb$<0~AW`oxj9E@1$n~TzwZnJQmPysFQVQHli zR`8qnp-Y=qDbJRMsAS9ee09s&qU(t)R$WHMpp3ojk3O5>ZeMj;B>64+GW3F5dM>bY z_bl1yu62X3m~G}?i=$jxEkqfkD@6Wh+LeaAA$6bWjP8`6=Qu2Xqq{+*93m7Kh{?d@ zL^w<%#9e6BzMutKP`*ucGF-@+2PQxJMEES+HnotAt0Y{`U)ZJJti|}hN9rS&gh&-q z%z0%xUiIE64d{eXcZ158|0DQRFhyPF1S7p`mGAr|ZeOyRJ*s`HQ?s3TJ>@LW)t0;A zzxP1b6%V{~@)Xnra=xSQ8uBXM?86Nep+K&FVDM|D&EaHwyE3p?k4o{I`l$LV_esyI zbj}LK~?LxXXI4Bxam&v$yqeddtIZF(D3^i3~Rh3(I{stOO0-TyMiP*NQQ@c6F? zNuCr#kSi-$)%Wyq2hGUFBSLVvB7^>;KUY1coY8Yeo<(h7{7Hs(dQ!W&X$r2ulb$C>&~3A$Tb@2DN-ciX4C@|O5x zBHtPI;9mwhmmV=*9`0hH#kK{TwFUVI4N~!n=J;-qxBouujDL+w@`f?l%7FMY7}5>; z<3i*kjOr5K?{sNS1gQG&!gs~Z^GhcTIU0)dBY}?t9nt-Dpx@{*TA@xeg7;YUt7;_I>Lf0JDiLJ1V*e?6*GW2?1JVmn1qD8@b zZu19++OZZ_>_V%aJ*l{N0s86PFjE19>u$nwo9RY^-KPR-?`fA^3vll4QV-!1;Cejn z=VH!0=Ul^n=}&X%*McXm;vgZkCvO)&9(Sjx2&wA*r58R3NHR0NmYWlI5os{ech@2h8(9Xt&Q(UltdZdVxR#izm@Gy(ROdHgpY~{dx%pX zUKSi{2iDcQ-bpv|b7h=O>~!X0n6~WuieCqdSuB(Q!xl;5Bw1G!yniI()r^xH!Tz~UeLULiu?%if!N>Dq&T?S|KoUJIL;avHbTsAja+LzU{VQ2 zh=WcJ{FOhP>Vyue-nvcmVi1E;enVgBr2($%sXUjoy0I-Lc~IiOt%r9>Lw5=N;wgMq zUYag4OG>>vhuR!dLX0YTttREWPmr-Bx@Q7-+jGU{jzB+)tM{U>CM#a$%<0dL!9ryb zqpvTW`VdM82|=RzxkUPlk*?LcoC^L#)08tzkajJ?wI1@5Q9YaOVdG{=?a)AS&#hA* zT$nd|*p=DZ+Y32&z+0_sVidp@9NG7ujgQR!?IO>#vvl`X*A##GlR3~^uiFZ!2^H+a zEV1U6xZnnL=DXf4$M+WVSGbKq#9BjeOT26cq#0ht2;qw2yO`%M@k2+5c7>AbA^%QZ zPvPDWqIyaIr!FqpxHUt`K9-@XO4rqtQRr7qNi65_zmMD1{d4C0p9}k=kHX!@&fT(j zrd+?+DnEEuWOKI8V@gx?=OXEt1AH=5R=u*@uD-MEFKo@qcJPORCS>|qoC!dol9SdC zGMsNiZg@A0U%Gtez3^Pn1|J|KzV4OqmL+$)f7ilbO2oP!M25^p4W&Zasn=noWe(ibjEr1@51Ei?{D(@Pf^U$25oofq2#xrYv?ix zKuTSrPF+r=CaifgC+j5KXLyTdT(?J5<(~hs6}9}1hMEFOf!xqF9ahEIZBIn7FPr4j z+tUO+kioxjjGE$oOZS<#!vY8w+&KFb_JcHyzI53aeb_Qt`zfWO0o2UniPJl!a-P)f z?*-^S1)q*KRvWsgl`wu54Kz(S1h$rl58Yf=3X1_9nLP41H!1KviK{gicJ~(Zkto)6 zR%ikoN%S8);4Lya@D0Q|vi{3mC;)}|S0p?sYZIXY_uAisuyKrt{fvjMXx4Gs{n#5? zXmtvw0BKG2`E4(6-Ti z;yZ$BlgS`1y({!fO*xXrp9WC&TufLw+Sbws^8)-^chi2>%F&?JMCu97zx}mT`wdZ( zua1v#XYodAkx#+vtC^593LrzNH9c&(Y>rjTciYapHAW3`^R}ghlX;)gTV3O}YFARE zPesJD9WD<-?MTKsKj?+qMyV)@_btm6qfE8~d^hMwg4FlnHLVummH7nQFHZB(N$<)w zP!i+I!jq^MEHCWKtpCgcO-|L-chzj(4?WtE;{Uuix?BG3%2v5mD?_r~nJIdn3Qcd^ zhT2$Fjj)IJ6GJck<)+2N(%1DT>RgjkbRbi$Pd;XR(lU0!0U0j@3qFDV^5VnFm^X)( zpk5$xgr0-e3%uZb%#6G{Qpxfj@AWdB4MMF|CQ5}UpZimc@BC-=ss&TgUuhkxH35$N zUTTX&1r!b;uF?}!3rAQIt>Qp~p5X}L)-^qdcA|DeGp&b^=S>P%eKcoQ%zRM2zomz-7CgoB8GWm2 zQ%#TJj9dhUn}u%F-p57gO_1w1-EyAdUwd%aM)J66e`L55m7V{vQ~#fXh}=sQOy4YkW79r|y7%zu$t8*Sj-FWhAFfI~E3y z;t6cpPr0M&BAepJ?0)7~gfCP=?pofymX0EEq{Lh_+J;W_0 z3?&TtFymI--YOhQ44=m@Hx#-;>xS?(;!U;Ya@76gBxDzTQ9bbvK3?PY&svs2L906+ z7D%CAR~UbFNm}u2weVTe4RaeG5Ipm4u*1U$~x~J)qZGM;3)GmU&*f5zf+T z)iOBK{wDXO`z`;6v2D3yU{H8DVrrSNz=9%Z5ny9D3P9m6X&DWVV$K*msK%A=4G!G; zq3;4|k=co3y^CD_wmu7t=-NM1vnLNt*6@RAQ*}%mm=SqoD6A>d);?YXkkXC@F zz-Cs9E7er`OD^X44tbd?`WMQ0V3-*Npb!`2LP{z0Zh>aia%6WWpqj+|c&;pIn?BY$ z7Q21j5}(Mp3Vg0k{5X^EPQ+~KW_Cty;ZSiDfEYZ&#KQ1Yi=kc-L~nokcL7NONWNMG zMj+w&s=j9B4&OvSFDR8t+m1i7w9)c&n$LdY&&m7({39Pm?pMzkaT3fC-S`5pW0RMw zPm@w<7#F7c8La_Y7U>2NF|ytZ+gswD2+^<(m{@IAy09amB3JM6>uYC$97D4}`0wq4 z&F!?XoPZ4sPSt;C`IoGNW59V2!%EuX6WY$m$i9;I;{s*$vDC z+_kY%?{-AN{-D+s4tw~+_wRNJ9%fVq z5^;P&_<41<8Dv7ctapO5dk)`w_DSst)7|m}Gsl=h?)h>M;c&!c(1)8SKbT0m zKf-_my-5-Y;59Xj)jSn_V|u@t?kV^PLEdLFkXKKHC znKJY1u{CdYVujvV>HS!T-wgiBAHlLo_YJcN9wmB+3I6j^r#OaT>o?0)4*iiVs5L05 zpKf4(Y#1%#;Po#JJ~OHwyIUWmmLn7OZdVB@#fe0MHIBIEP)y`rq6kTvSl8>3A+>9D ze_@1ANb|)UuTIO>kC?A0hMYgS;GKWy+Z6-_pa@tv+~{ld@@aMeIk|@_B|6<9BM;DVwytUn~-@|f)qLRwp@cZa>Qo8M`_(#u#&R`vwWJM(rv7h;9DbV)+qLZBAb>?7eY*I2q$q$a$U3+lWrOqy!J9#52TX!q4m9=qN zChk>vZHL$${VPHFvW`)m(fUrUcQa9Q>N3AhmVq*hs*ggLGo<)`&fa?J@%d>{y6w+! zGYu+RR`p;wUjXsR!!xIK6B}>z=ddtI6+rQ}vC2{fM^IeKoE3CmtSr#;xZqOOi6FwyFbV_eGg_q3A7Sv3((ZMH2rokRNG776YZ?F zdR39WkKxCV5qTb!)_+qKMev-VNuYd$ZD&J!gK_dVnntVyUE`7Ran*(Pd#*pIxv)l3 zdi!btGvUHisP(?5&W_5nDz~W1Q<3pRlPZD&iitcwSwSAD?;v+{HHLN=**ox;GVnZR zI}v_#LCm?oZpy*}u#i_|`*t^?5qRq;F5SoIJn0K;i@ zX{xQjwm8w5cK*bk`)v7iDq+_7#nrS_3w+(6o`0+UuGQM4BIa#G3M?60+cY-O-dw=6 z9>C$Ad#CqB&6NuOKrRK;vdR}y0(TC1HfYmC#l*MW2-_)WvUE*!0zXqIBU8&l9VVmt z3G{nR);rOYNq%-_yk?S@%2&)JMz-*o&NT3W5T}PEWQ05YygQDB*@}aWAcO~Kqf6z} zVfQpBLXZm*|>W*cf+f>i~xlV|K`v8og+5MRlNdK7J>6eFTZW$_gJu6AF% z$h&+&SFi-`2Qn0mA9VJOz%U&Z{?SG}#{DbF$lO|$ zAbBlgJN)m@h1E^DO1fQDX6w#|tTjZ8(5C;^P40W7mXy7W|5DyZ&7s!%QEgFi}*7Umh~Zn3M~HxrfNRtEHLW_LL`WBF&JinzUb;9{f4Heq zmY1GrkZ=o#fF4uF1rgn6)HfY8eF8E$X_l%1DNQeGj;IUXPl3PRx==tVlB3AKp-MX~ zE2c!wsFr-E=F_6?mNveWN6D!b=TSg4KrZ;_`AYpM?bU4)Ja1@DheoBsNwRQR5vRsB zw({ zZfnm!Rya4}moylp2W#olu-|JUu@(l+iSJIo4IxctMHD~TCcnV|7Q(- zYqm3|u)MhnKDmO8K?^j_OH-}YMSq!!oI?5D1ra&vp3G7Jk0^kL0pSw3g~GO}Q(&Ub zFB>yH2#B{*B{|Zd@!Ewo3ac(oj%FsWZ?zVCXW+$FGs(RSw8pLo^CulS$S6_yA3Ol+ z7a;3HWcR;%$wvZRLFeG+p8z@mnV|NvPaS1miHjNByYw1O$$~CZ*$0=_>>t6KP-60G zZu?3BNg8$OvO7Ll21gln#?SK$1Z} z>_@H5ZVm5CjYuA25HRqHCx$C$4NW(NP+26bU;2A}_a+QvYkC@%dzwRjH|YJ0F)sM* z>L#-jOzn)G>as9&DyyqUJjgyynDMzT>nqj)`YAv6mk$`mPU4{e6uDNjFj-2co8+)2 z>t-I;3VU6aQonCk^fP5aW_h1_{@Ke@SCd+qx%19fw-C7p&n3N09Zaq^8}ydjrhYs; z%AEQPZSJkTRR=n!8{*DUN?mc830Rf%@Y}tFw4~LZ;JVejB^Px6)7|L#abG&-@pN#~ zA~39E)5p-3OeW@4Y>AQg@5WcGHvd8vdk}x&jBflFNGQNg5TM2V z5VE0MMquN-!|3{tbn%EBRo!_@THCxT0@kLLyxtOB1otK^;Beh$No=>o*nlY+@~$XA zUtaJ%z!BinAJiI*)Sr_utd27>LI?yb=5#rF!pgHGm~;|F%~d{@e%S!X$N>L%cs5H~ z?qB*B5F3ZNleu7V!)m_yf#c$}3(8juma}^4_V}V}4Nb2=r;%L{!sWWd#M-;z6nnAd znJ+IItU))xG0Sfxn?u4=bUCLiUt=9zxBrQhvL*6Q#zqq)4PfHp0)}?MA8r)qb_hux z9J6IpzvdPNp9^fll&#@8xB~mhC97psEmi@W5>v(d(3f7JXIMwO%~ch+#mG5-H+<5U zNl*OY+xa}Lh&>yA>vhwvFAVB7i~m1DB~i93XWN=~@avlDud3?#+R`_XyUZ?((I%1c zU!+cD826Fg$HdYU-1WfULJWo-)?K<_(GS7npByxfsLDa)EOLBT%B5ccvB#d=Nqiw~ zOEPrSN6UECdn_1;CRe9iKt0=1=D7Z#&LoD<)JtDb00#4_RvOezZpa@o>!)jIf%*yc zCtebvAvD^8)l||m{1zH29J7H~M;}reDS;{xEpwo22eG4B{2+7o$nx9>G-3E@!^}&U zc!&pv8e`zEoPeb%fNT96qxCI+(z`7>8BxD*T`<$+z?6#HCjqC=>aG8kM9iVSB1FT^SCb2}1EHz; zE=dwK%{e(2%i_Oa%L@FT?cAvv=_2VqW1*$(CYcf{J&eCEohSc_1Z`L<{7FTDEvE`% z^?dq&My?~vpxG;?c$M|y0Sdq>=5kc%`45f{?5^e>@6T*g%alxm*XjrqK<3ZBJlgW4 zJ4uV$p0v#+;jOM7*PZt`ftAJ z{HtRq{J=of$OT-h!Pk32)dLJ3eg80L)8_Csjz+SAAC#ZgX;$MG;spRZbbqb#3Qj>{ zWH8%OhbF0=%NrQAo$1E5Dc{xH98^=_nj+I}T@5y)xEBfmbI#})s1#i53HgL@qZ(c< z+tKjljh+iFmL2(%I>DLt@jvM9bto>FACC|f1y0U8pLaF%rD`x#7DH6EZt!>?U%d$$ z4*RXrsD3MHx+ImaP3#P?1`Yq<5POD&rsfBdF+}+dqy9|HwoqqI=xMl@w_duPPF3sz z#7y{F$M{`!+_5uy3NwfpiRZiY*B!J{LlC~ZdL~W&U~OwRE}*)}>De53^c!o#Rh;ZO zseRvggdxjT>%kwS;Jrw_bKPzb`46E#`|ux83sV=zNO|QuHohIk!e}k_&4+*O*fw$K zc^N3D*@JhuiFvCjj?c*7J0`-qik2VW%&1uYY9;S`n`5%QTJl|&sOGWLVyElC)#G3v5xOZr9{t#1~0j~EuGe^wPp$+KuPqqb|QDfZCU;`bQES= zs`IdBfh2D*^J2@Joi(6KO{B!<@5YBoD#@wvY@jnD$Mq1|Jv@8rLqirTBW0L%ep;B# zHCs}}-mB>K`e^r({GQFC%^tk9aQb&FEq8B1CgWVEf}`d414TwFnb^KX0DHnjH;8m- zN&(~|Ce$CkjvE_gNp??Lu$yX4FZ$?xwst3imsMv)*%{rc^653Lw@y8Ig)mZmJAhX0 zmeVIq*YDqK7USj6{p&}Gl925T^7isdUb%O-!E6_EGL5R~7-ENup<$r1UrJ2QO}oo;8D3+S1$*p2^M7HIJuz=FC#2+BDz8UCE_I5qNx zs7l-0!tuqB@oBg)oh<+>(t9FX4l3I2$P^%|MU&v(?lM88CQtv5|s+7*^0ogef0{*{FEK$GNN!MTYToe6cx51quj z9c3ea-hO;sOaz@?seT+EL(Zs7XK;Z9hmHH68Rv1&$ZQtqo7&iBTE1^JO~RdQ_s!^PjoJp znQSfxE{N>2|CteN$PkKh7P3wa_!0Mu+fAEjvo1LSHgNWc<=_dk;`A?4V*t4N5*Eq)`AFGYfd|6x-IS+4pIw#erC?t>n>f z!K7VG4Ct(2WZ7i+QiIr>k&TfMGp;QHohyxIvATeSB1~~U#trhk$vktF=*P(J)QQ6v z2uYc_fSm8om32lb?_X ze^>|?^gcHR7_LY%tF}dNo<)Y=+2@CZg!vEh7NI(;_9UOlQKeY^VOy(GF`WxXob;aI zWPAOu!FVSNh1Z5_LhdQy(OSqtbxE0$EhC1t6~oxV_Jk~kUPePf2Aeb!gEU4gG^y40 zZU>2*^hTxfr^{5>^eZPn;7^`xMOJHJFV5sA`a&9W6P-6fe{Z%CA6Aj%z18zdni6+2 zBu7-gZJUiOL~d|~YnxKX|9;k04~dEJ^gd6>4KR@)EzE4$S(jM| z{P4vk+LPyz-IAZ^Kr-gw43K^E2R}3tk>JyGmMFibL_19|+@LobJ(9oyMuucJ zcj&u-6R8LngfRo+?3T0N+tCGY2aWUGI-SWc;yPa?so6=^ zk^k#iWEeABNo=X}bf%B*-y5v9TYM94pr{GI7crq4VU3a`R&0w`s5a+~R+Wp5Y!x8< zT>XTeeI$x8QU~49TkO4$>>MKo5hjVlhMNT{mu{mia_5fK$qSj@J7fF@+|yBSemlMv zBt>GOg(D*>p*$uB?eQHe6o@*n9yM=jM+)dfT1NC%1O8R(u%@fTHxVvh$O|1r zx-FgBm*J`g5s&OxMRrBq@uow8cY&G*MCCO!f&$1*C*F9U(OFUH#TGSE^397UUK~kK zYf1sV0sN^P>19y5>IbQZbBtBPAqF$#|Ek2x)BU9H*F(6y-q&+}0(}z;8%o|gr~;VF z2BtmtdNarXG=MzQ_kDh`R%2`;4TYDJ$I1p?M1LIfI3BR9iRTk}&wn{f`(a1X8J&RH z=0yl}R*^=wj3fqN!8}9*L(aw0H~w}W_9CA(0~sDr13My-owEcxBElwzSOF&I&t5Yu zv+-Ol{sq#0a<=vMi6B;}>99W$3$1wsKh$ac(u7A>7|C4{OjDh9V|RfX<{@u|B7&>s z%_oB4?~z7#5{FBr^o*{1-FBnJC)M5s^>yN1WgxSOo=NY51d&cCzTnZp7L@sYjxb_aiAk62LZ*t=Oscc?dqj<$I&cS^=&|S@K z{2;7g6CO4QW9v;qL2*~EGyS>cDoD;d!#aXV+MDxD))Hh~&9~hZZ?&i-$Bz3_e7bvA zcgJtxj?It_dGc1%D!94@JI_WD4|3i^-Rqr%S8BQCImgtujy7k3!6xJls?9uf<^ctO zy|jy7pLwlcJoWmbazL8@^JjCRh;g5>H+zzhFNjg8OXoc{3SbkK-n=T?!Jo$2zp~K(fzd{#s zVF1=_mH2@I$j?@*uOej_SQk&IjJ;et_{kRO;9KDe(Fm;s0FPd-HY2+aUT1)(`1OBy zta;4N!tYMUvs7_S@tr!OWji}s#n=1vhz9VzaJe!$efRqSmldqOjF-ScSo^ zX2?bGOiNGa?=42GBQxo6^OyoqNZ2e8xO5=dejlrGT+(Jjy%civNR&Xa-KbhC+hafT z0AQi$kl$N|B$GkRn)d}9{XnwREY6kj`S)N5Ih%HOO%gJYZ}rcSb<(&K+43=n$j1>WXzx}7An1W=?axz zB4G%IqZw+^Wlho!c3zdha2nm~jEp-+r##&YgmpYHoW0C*u#BW@&_q}OEiNvWeyD%5 zQrrZxO#7R>?bC6H{Om^f`*-x6U#SL9TbjLklYYB z!$E;*S@yvk^G~FZg&DmP>O$D=TZ6aqoF-x5Q>y|YVntFdIcGbr_`KNjXnsXVh3!c) zwP+O5SHs1zfUX-vdQZGR(6A59!(_xWV1r7nHN`{^wmwG7yvN&Ui7%z`c?t!V3C{u} zl1L>Z^qz^|%I!W&gRaLWd0nfjeoHsMijMgQE?OAQA|8d6!JRfX^LuQYQdyoJYQuCi z4z#}a72>zY|5x);h8(qWsMFjBvST#9Rj_KlVjm~7<=obC0UIP%hJ?(;xN)EatR zRf4E)J}xuj+&^O4wE(yJIqx9-=yapW<}uP7b@_9>{)`V83P~s9Hz|NeyU>azROD`_ zzjQwf^#%im#cZ|`kT+%zjwJ;>{xW&mff~`-@Dh0?nauQ>EBB5|H4k6o6HQFv~Lq9>M8`%4UeTbWgQ1jy1H8>uaqfS z7X1knOI~Q7QaL8Yd6L4nV)I_x(TspDY2N6YJxF>d0ND8mD9u?ysMs+&Dn?AaMtIWo*A;1__#ly zTZkH`dS-`)gBV%HbC{=1TE`6lR_y0p3k zndnY>S58VG5RRdGdhhJ#O**|?5))7NpzF^>)(?=~;e_ptfWD4xwCvN*5$`9Oy+d89 zw63Soue{!h{~5n$(Mx@TwT5oGZx8JhWVJ%m@~wmPYd7LdJ{@$k`f@SG+YcyT>Ge~+ z73e#MI>@078i6n`^kr}rlP729ot|9@sr-}tC=kVEAG`XY5^HJ;+kBtZe4%QF{uPzZ zYy@bz->Pf6X8jEo=eP92^AK4E&2W4|l;2D%ooR8b-C%|suiE33V+GlwPTAi_`W=qC6Z|ElZp89wX z6{JR(W(&R6)G2z!lV0=LGityEcyP@1gp-nlyD9S&%0wXNAO6+c~GMZG+(4Lg2QUG`4zV+b{&>ZlHMj2^D_KZI=7 z)D@kEOBi>w=JWXKrTnJ7*jT^N{`xRan22?}Uq%vE-+rL}2uf5;xAURVZRs%7FEbtg zk_MSdLZp50VLr*x5j_Pnp`MwC%c+-?5A2aS30)YQn|SjYH(Gkp|*!XoqLSnBpHLYAL7!fUoH zeQQ$-ALYZx;uR<&Y!&cQi7ogJm@rKNWWqXANF?u#+K7W@0iqZQVYO-a7}(S(6VFgb zb`%<*$3N5HZt{cdSB@8UW~*s)SEX40%t>>kNZ z=c5BSY%{GcfA##1=K%z5R5$--K|ymOtltsVhvd$GZ`z-o2UcvN&As9JS05t9A6jp7 zP8U~CO`TLc1>Y{+fgrNf940Z6VbhF>UP?8$W&^Kwn%c{_mCBu%G*z^kA-e;}-9)3q zRD}G-r0BK}bjZ|tc@JV6bzN5cV5v`RF8*N&>K zA-Lqh;PuZ}`DHoZ6mhH8gY#cb{va$dRRxcTid8(I% zXjxNjXTN{Ixp&__6`486iA=0#p@5Vv@57(XLV@tU%(sby?m;&6JU^w=urnP-!wm2iwfacA} zuq5ZT9#cAE0M=Zc*E>Xh1DY!X8)OwY6+NAoy)9~0C|#MhX@2WC$oKi*Y9Bl>{PuRi zLwoyODI>nhJo~Zhl^(+$4!1ZN`y$@TV;w!WxrfD=2gVn7oqlvqNwB61tvgRH4=_|~ z#*Z#G-T||cUXkc;eEL!u;T_=~9qw+LcDLYK^utMDq4h}M!?>ZY2x(;3*4gFc-l!g* zn7)&%xpg#;HQ?Ia)S_#Pzz82Wg5cSNQ6jApgN9y>aV|EO-ad+cG}bEYdGUlH$}*r1 zbdI_7)4KG3OaW~3%Pbr8TSwNM(%$`UYu`U0f;tcVKbFqJoelTv!|7L5t5!=;YSbuV zmD;nd+OfE#gOyP9k`Q1^CUB!(+QZfK^_gPDr&=F}^F8>dsLj_)?aJ zHJ4BH#(H+Zg<}=Va2ZKA)O&9F`63v$vbd&ru-eSOP8~bI2M7K^+j0@z$pCp4?=uv> zve)2YYrd@kT|Eu#UM{nRvaHDkZyGf{7Hu*daJ@Cjw%OX_&&63`DUa`b-hU3+>WZ!r z(6%THt#k-lj?^t{wolucS)ZsOdBgsw_4dSM1^(3^wD~-awkoRJ%1-adrB;U{T*yq+ zq_u31(tdRl_$qVl;$uzO`;K24&ig-~Zdj-Ws8O_9UY72|x?D+nlVZ~Jyu-v~Y{Pkc zx?@Aq`QkeBoq>1&4t6;<Qr8A>X+x?({SH}5ob;=jpP1li=$`oq3 zv=T_)q^dsck{D@=bc!d(M-HcvPSe1EvW&*vYC*dl?}3q|q2d2{A{+~!e|&bWb=^jd zae{3f5%%bqi)hYi7_VK6VwQA;CNHd7u_-%xnhFZfIu}zcvpFJf#+%fU@C0bSQh~?m z;$}+8&9us!iLYBU%wj99cBMri-5Q|N_+h(|=x8@PA{ZJPW4;6bHvHy(3?RZ$m!}PK zs%1Fyhx0E&jMP8XNPBni@Q|O&EL?$EgIdSvX=3{Rla=73HJ}z5AbxyPRiq*7!ktb{ zCf@h)T2EnBl*pdlneXxsVWSN^+Kzs4tNApHkg5Ph;kLW#OWYo*ZGXpnsY?KePBMki z=n8Nv9Qi5NLH7KR)Xwj2=5J^itrnP5OazGjbG~1BUfH;CWu05AfC*uZP;U_ zM#ZN%U1lwG$Lq=iE$8_}>AObdm7TP1u&&?#w!x(DgzAL^z1Z&+6jDUK9@Z%%yQS~gL&!Q9Ni^If6tK~&&9)Ns&D>>2kuQoVhC zzuK|2VR|Fto`XhgOz`>LAl=ijbio}ZI#B;{R(jYY_0?pAd6A1*IS;5GHWn8>+PB28 zh6IM(!l8yk)$l$9?_Mye{abqAgSQ2nOfT-e-Hg#@@+!<}Ve)V{^e?>vbqx`!b5NNm zLGky&E}}zgE}604tSb@uuQcwbxxNd!V@n1ofr*J^fL!b*f(+n=+8N{rCW;lewRU8( zG8l|hU{;IU*(xI*=;T!1LfawjQoCN%PByGf`&H6T3A5oVza0S#|5Ddd^#0HMBma>o zUl}&0CJS$%75aVKp~|Xpcjk{)F|O*)lcGk`*o$dkX#5)t86e^;!)BITHkIk#@vc7U z)-5)W7quUSj|r^n9vP7Reaf?BM(Oja1xzD+9(-P;quLg?k}j2`RTO3vkQWo0>a+pt zoF25yfwu?aM5$+n-R~RqU-y65m7p8_O2kHG>d}1|eInZK)|C@6_Yhw{d~Sg0$J)%y zM;E&>6L$O!6As1TYE3@k3(e(!jW(D?;sA@w15M zAjqo?u!rpo+P0Z^M$9OKnwxi5?dHpQF;ecSNi^&E72ZX@lhb?{`T2Pd>sz-1Q`d%KbU@ucL?`?S&Y0Ty!32pKFckY{S2-U)Ffg7O`$5xL~L*^g#QFH|QS#a476S zU_72cFfb&bunlU4Q*QSkKXnt(`iXv-9957(X%%nxHTN&#WDUPLnn|^J8H>tdd$t%6 zEk3J9&tmb@FLv5P2EZEveLWSo7g{=hweWzl6%)vzrW9+P|y7#%6d3L+tpUySdr@uC~-FnDKaCuf`6ofHmbZ|E~1kic>u$FUUlfX>GvTh^*|Hi!`>5(MtZl z+I8cd@4?BftO$Mrc*NV~RMK|vV!JB+t~t<1uQv__6r>*p2E4(oVLMSdA$k57WiyZ%gTutXsQ$nS1lL zsW%_=B(kUMD~A_kTO1cVSGT#w^RR^YN-o|yHzp?k8iN^S>c?d{zHnv4G%(~eOcbI) z8pMAmyqZa)cPqRN>3ol{u|6Nee&pZ)&xd&{IwA{D@`qA3%yU8=qrO`|UFt%;bt4>} zyV>55k=pJLil<&j!pQ(}RpC99Jn1*iV1x))w0h3aSdFa*m$iJE{Jay z%Vy;_C-Oh*J&Sw#YnCI3Q@IY3ef7p=F4-B*2_{$@Uh40s@zx#HjsF#73g=MmVqxxH;pY6Z9}-2k8L zNZ>V-TLwL5DKi>KR+}&Z+K>VAzCJS5L?;|Ay_R3g9PjiTU-(L|-h%`XCxAM(k~yy* z_&Sg96+RW{hKCGsS`*$Tc2|_tTUDntJe#5l5~`LJ`f+FZ(?Se*)dla7if}W7TVk^x z>)%&;Y)$F2!$M~p>1udqV7&qwxv3jKBKl%v|JIzayS%na@^h8@Pc=F;B~n#VqZb(R zFZpN`Dz%n{N>?~-SDM3%j(9EKnuK1z+x|E#8Ny`g-t`x8Dm-rak}VdUmgOEUbvpE4 z+Xss~@HncGYp{;eE8fc0{x^4$1Q#8L>Lyavj*_&u4WE(fAU{+IXk^>KHJ25*{|*LIylKkm4E^Iv$tK%Gfu)Iu*8f#h28P zXet`^CMf=$3#_Y@fW$V=V%Prm8pev1Hbw+gJLv#Z2Kip``OM`Di=Vet`fMT}OZOL) z?)V*0qOqOD%tzFqa6i8IpES2v5kEQ>;FiUQL`7V*%foM7+D?h^&w?8Fiwd?Unnl3B zQG`ThS#Ri5%`&Ljv-fcPY0-U`Hy+yXr?2=O0lq=iHXnEOn$WiK|3CJzkpVe&DQDu| z!|pgy9tnmLw&b3VR|+1X8UUodObuvUb(1mVB(Ns%p)Ua?D!Hn?*)a|NVr`YQ*ByQE zR659yQUi2+_%IM0(u-Ts9hRqqc5yoiPw_7^=TRd1>n zWH>K%4IWg6W7(7n%Q-Xq_AsM-TLxv^T;RfOPhXR=1C%P`qm0_@r z=Dr*fpTE^IX+%7+6ri1H>T5sc1qSNJZeCL0Q5KQ-xDz;UrJnMBJ)Zy0LfNBBc=0@R z6BtNO!u01w$%K_HaPAZvNGQg`OV=q9V zwo{9IiR!N133vx>3Z4V!(gau$!cfvz#zdsVEQ9q`z3_d;CgPXO$3%#*eza#Z~V6`plM*-R^y%(BiNTRzNhhpL_iY<3%2v0^vMqe)vfTNLcrD0WZx z*7Z*<)q*Oy3y87CHKFarGq|+4nA^MWr)-(JjNN@7Q@c-|PAdrkI?rmzod>f+-AMRhzsXRQmjWC|JpYcf^`{_0oioZ zq;8C-G!cP64|d5iYdWvq`yBr%xrZzNuM01glK~C*%pVt-q4b3~THw0KT(87)re_4) zyIXsok9LOw-E=KQn1WPn5(17V1IBovj?000Lz98t0b()I$=kjZm-j&DD=t+bx7^{mYa^rgMOzhdJ9+ zPLr|(9Bc8vxJBBSOkYBf%aj0E?{md)WP7P zA@0F<&SIv&gIivZ&MSwov1cx-sd?#lh>%gUBrA0Jc42UE(yWNC$=9XU>T6{$z!IeC zS+k{KwmxK;b$-7u;`cbQCyY;5>?4j07(+c$XRf&K7%cA=P+<4fwlOq3d}27M zztIZ}b->Edu*_~|dz_0QM9CAyKuKawZHL18rD4X9^V9-f%AJEHK}ugmH@t6u=b zd_ID`yCBT*_lpQutE&mnifD(;`xAkz3XB`G%4l%cnX*_9TwvYXRHyEd-KpFuN>FCs zyDWlrphoR7^pLwvc`0lhUwW*oCe@{vpM!cn-~CKib-$C!<7pjaJ}*JVycX}7DvlD_ zq|K;bKU-c$GT`@XkVF%r z4VFtFFpjyFu)7OyIz8-wR^ALRPmNsqi~y;eAh~G3+gw>|iJbOxC|Qp%d8O}lw4nas znx><+$5u}j1a7tj;1jf_#CdtnA(BGie|5B8_Afu=ZG9cp&MTl!_P7aCPyjMK)p5vJW&GZlR z+kV={D*B}bA1ahW^g$QAW2$f$EG@0H=fL%^>LN+YEitneZv z+F;nUnIL^0J1)aI3^=BJaJB%U@hCK{~_naT}xZGL2tcvN2?x`3^ z==*SOCYSBv+Ez8l^d>EmS;@%wxA05uE@$sz;f#EI3o zLuqH(Ree&on8z`qZ0f4bpWmG+nf1RsruQX&*cae4PaM42y=?n#sO9dIm78B`xsXNA zf7yduo&E><3kY1El3{Qhx-SkVd{!?iBi}oJRvbZB8SdVUg0rr#W zfP7;G3KTMpLp=_(J4> ze3=7X*_h353OqDS0CAg0y$F~AI7lxc=#;%6<>tG7i;Mllh&SS;V_W}8l4w|k=!$(Y zAeUoDBs*R+?U-Je{t@(*#nTh^PGk0p12r?*0C!g1w=Ydayfa4soJsg~xt=CV%p`Ak++Je#vsPAf@8| zlRx(>zn=;KLq_sSMD)56UzHn(+$j?@ul-vTxBj6{xm(H;$GK3+HLjY`#?exXhot{x z998H)Sa0&~2z~MchSa!D9RuEL3Zk4VjP~-#mP%Igx!S5ckKINHQ+o`lGgTZ~g9&5+ zR;G*le1B5smeNo}(YT#t9V0twZN1|kWfajC9}*WDETi@&ivD92{PB2uZ_PtfLO z&70M_BYEj;21y}JRy#p2Sl*1>OoaPc6!NgC>LA1)o^r33qa*@*hjI;Dr$S)SB5xp% zmp&vi@+BAIF?JOmR8ciEC5r3FK_qdI+G_^Tofb&!tDftEXgbLo#U+i{eG<~u! zqvi2_=|@}uHmX(gXwodkzkJy^r_NNVXh&vZXPl8GXUZ_qgBB8{wr}ZJdCDjh*m$#g zQgn;oQ_s0a2b_wSiv)(`VAhEJEXjo75xd;9=>{3bXtTOl{X?D~J!YPGjbZ)Io~IF@ z2+}2~lc166Rk{W>maTZ@9)!1Zkwo!#oS6PG>@U!v(2qz*Y!2t~?V^$_{WCfUKC-h) z**r}x%^$d8M!pFp)C#x{l*oV@cVUGnP?)^Cs`T*%8KAOeC8Aq$dpH0%7whja*0DUO z_vvjwJa+9Zct07%R={Y!jJ5SuzQg!ZSFtJq9N(;si z`HSRC^@Qojlj8`IwGCCDmmVJyHNfNL(Rmr7?w05`x3`~ru<|0pa~xbb)iz;W%49%x z$SnVy@9)aMs1{c!7`CB@atyfCjv0$Rx&tw*Y(e%I=O0Kwwxq~_Y)h83lNGL^W|^k7 zja!EfzSl!U-M^owy-TiTvP@9;PXf|qB8{xmQ56 zg;{b<^^fU5PVk#7ZDxXxcUT~WZ=<~+(^rnNwn?^6MS|jIEr?3irV+2Vpih!v1s1f5MPC#1tgN|#SAs=R`pOM@;+E%O4@=O9~W7o`Ji!Dr4;^Ez#cuf_RG7d;sI1I0EDNqFMX>%%DPORN1-rCsl%z|w+QZhd@;o1|cp#4Kw7vn6cQ(%)mgS3x5975P3 zY~2hB6W9 zWC-})=I(HosJv%YLCnvuWmRwGGF~~#eWCbq`7L<8N=NTOmM$|S8yE2FPJ0FNVN4N1 zLbc(R@WBmGG=8Nch$H*_Iu|7x-3>S|8MHHG*lc89D%^*4#E;v)z@Om`vyR(G4Yd|G z(`xv->H;i2T$dzRo?+-fe<>_ULumxzEAP$cQ#FZit3v7}7k}un)k#s7n(=zZ7BA+| zfiCZmmPnY+rU*~5Kxp@&FJBj|N6wMS;XdqEa(8&X6JZIcLk0+_@)hM?t&Ly3%@kgf zdX_ZG4pd3a<6+e?Fi$;P{+P=!R<^4A~7OrEK1S%4|O=@twg*H^U{!QC~ zPZDVUN$QD0lF{U#PFT{)Wt}NY3aR7$l8yf3yH#h;m)lVNN21V4W!H7o0~ksnZ1a|M zI~g$SITk-EF18cBT{5AY_sr5X@i8zF(o&Rj@t*`_`^FdIY)N3ao}2EhC??)`T6`5< z1V?qGfbv#8)`y4*=`}9_LpmL>^dqWrfjw>mViBLPH%nx?!VR@mOXWs|TYkD0-?9BZ z9RjwpB-A7mb#bw>_?_#LyFn8ycl%v`D34UAvhK*1mDfq|quv06#7P^T80tVHrRmK) zNfZKSAhnZZyJFqKWaT~L5K-pvV^3=r)|F!8xN~7c23%8e)HNv`eD5(z&&H+ufw$8N ze!<{n+CHLU-K_hvt212aIX%tLm@rTJM|+3eBOk?(F;K+QYN_&MUGdpW7D!S)O1YA% zwr2HTYH}opSFeS%M`~pI{Af@~_PlB}t3+3O>qC-kJk{@~zfE9W6NHt~Azh#QiN(%C zyZ3@VOjWGhQUoe%4gSqpNT{)d?aR*mR>M8eVT<0Zy z-rZ?Ia>eAr@?nq~P;OwfX>3`vW{Xx?rcRJV#kHRt^pEw6+`G|s|AmbiUb(JX*lv>n z37jTVt9ZAPkB2Yrav43){;!$s?TAG@pQ0oMtn=*3>+s4;)0C8m2k)2WlsB|}`NMP1 z$;f;KxGGEI{|e&xEHp0~`1)Knxh7;O-^zd(!Fb%4^^S1f^h*P|RmWsFyD@`HO3^Mfbu!-Dhll%bYkkO6WfC3>&Q z0}%P*=mvjoF&B*x;%lOblV=B`6eG78l|$V=3}w169h~Eh!_8gIrn>L*_(&Ch809sMZMsge)C&y6r51=M zRi7+X>zo{98PBjCEQAB?Kc*T!FNqtrYz#`$2mNIv_*{A4wjuq(PfmmhIL?TOKbT9J zSssb8+W9kx#sXjsufUgM3y9T@f8QN-WxIY+HN??jE%)m6rUxyFJS}N+uMT;SkrALj zWB_r1OLj+<5M4S?pc;SVwFLQiXueV~cIK{ktruJJ1D3 zk^1e8u$}4Ycaw>NbPRXu3Z%~X{yHL)I}Qto@guS_Y-!0gKP0-#`LTEn)ZufnZcCnE z;B~wzW*r$y=$v+u(WcA!Zz<}o9L=LfdNIe3R;MYkR{;o)IT6cX?J(^k^_?k;*H6uG zl(#7s!?OZnz#9|RE?z`~5fq&5MgL^ewAEB|1wHlft&8{D148dP7P7&A%19Ro{FUdZ z_;8*A(c{66tEzSJ-7&#CAIvt!_y5_rc%OMewwu#YQkqzCc>Yw##%o^LJ@{P3ow9pO z^bwu^ebnzH2CP?|*-Tqb4IksuSQ%})X`JPonV_20in9pY{GwzCa}&98!A7%cYm2WA{ohm?0h`*BhFKBWnp!cE>@mD%U&C~$nKA}6I>(;kNvY++rdw#EIJmeQ451=1TW4Cstzv7-*JA2FP2fR1*sjSLVdB@a|Xly}r{j&cvj; zP+x1cPvz;{g_YP55>-42Vx3;4fd=@1;vln9$MfZ}0%}bjtvyN^u_sKWC&;(plQ)<= zOkWz29)o#=nv)JS;?#UcL2s<~Y}mHClQp*&7(h?@ZI}N3Qq_*sPpW7KpO2`#X=@3W z5||T}zn-AKcjJ5A7Tp43dwe8HoK(jr8ze>RCFL)nCgNq1SNgKR+raSF!`y|+eJ7*o zMs}OWBW8a-N=Q~VZ2Z!s0cOSTOK^X*O-EcKZIhbn2(Lf+mQ+}XZcMK{Z04)YF!y#e z4K1jt6$+S}%R(=$Y!PCN=*Qb8lrS|jotqt6}GZ}i4@fryAJ?{gc2 z=cFw>7tSN`PRrB+D^GxSO!ZF@7k-t8hQ$))E3S=eKxjJgHX&;$*S)D7f7!VIZ8!7% z*XFs=Cp$kx)hIf(-7829J9MCdnhxZR0*_VaDP9RZ(S}-HUIrTQgXkNJ#q{k!s1vbg zUY9gQ+QQGLhBUqu7kf|1qJT-{eCFJH1mnLt;DOZ%EJ!mzH43NN7=Bax@Hp}u*;wNhY%Uok&a#k8$jtZkW^RtO4 zpZgDNL)@HP!U!3_(9+`O9w<`CUBLj|fxE$iY zSt&B$5!CbQookn+T~%+XGhTGG?YqvA2`m47_lqXb%{|u{v{7Cz{idGkNYKl;nc&xs`%h2fLGo0mredIEBEoP|^|5#nUKRH~%zAc@`S+_P@U>U5#Q(yx=wppY z;19gOP>Fw7*(&p9`23*%eBxlGlp|2AcLw`o_UXloWrg($dQvsJo)h5WB zKm)4cO06zLUCfg5ZMCS9diwh>_U#PI?O}3fOhBhYYp}lFf5RiqKDKT~xnAkr7R+CJ zgjfmXf4@?=($+yRVaEZc*?U`(&bXsg z4%QWSluIyp3;tzCh&!FnwI!TZ`{IOK`M=F9%DKElgW03=&AYe%R0SVP)qr|Xn_Sl0 zd4xn;@91$^NgJ2UP}sd!U1rCHiPj=dnf44R+K=yhLF&nXF_S>}xZ&k3FQV7GZ*J$x zm!)Oy*A+F6G$o_7OVP@zWu>6=H)HTgH#Yq&oYn&}KsO8|;&Hac@u1?h(}z@aTOw-*?kLD0+~1H?qDvyAKAb$SBLRfo(3FG zL=xf%gKgoc^!E$Z?gyq;Xzx4+90wC;?+40wBHtO>P&LyHyYGYVwIZnRO%9g1TE6|7 zooe$t@MmH`#Lp;!D=J_Zc`5~Jw99viy)xCl!6J9i)VD*WN2v!5 zcX>HG&MZ&S#-5Ty)WH0zXO?DXN6aqV2))a2%b9ZQSLRy_w3ez8SUPF3NBnV9D&l75 zoxwsHzgO!9+?~`(0g#%_CJ<==uaWFaWF!ORsg+W(12}i~5oTz~{x`r@#P&}|?@zyl z+p4c!2&BCQpSuzF-xNFNTGiuABb0?J5ME4vBZaRsg`7Oq(i|RuV{$*LQ9D)@;k&RW zWPp$@@%H$qoy^3fFf&7)hPS^OJX~ff`2JI1Q1UW;SS}UDz2zmf_&H4>F6rLwj9Y0L zY6G<#x42k_Yd}O{eDdgO$b8Md^vcu2eNpL7GlF5$`C^@4!|{kp%!lKk$X0>zj&@)uUUA}gDKoXbX--Y2(_|6tS#-`h_K1>-CL(jX!9aHPyWQ%yxlt~_9J>v@9@hrgdRaQ z#}W~{wnn7O_Sudicje|Z#$kfZj?niBYpETM61#fzun)ig%jILVosTmaki}_ESlQjR zd|1FV=z5Rqa~9v%r$GULcIuPWbi`z}`@XNYr${N|_u})XVNU6ld9HsQD@^CSUmSYu z|6n>{cW;UPS{CbXhPI{Hv>OR!9E48>s*PD@xO?E&-E^y5jC%Wi>tr`jRsBhOh2d)UH(5r52!*B#h9wUP$nHU>Ojb$SY-x-6 zUXhr5lwkBl-5-g|oDXlo*r2gGcPAf?(^x54h0h00nM{dpJVF&-3!vmqg=#OzASwfu z!SEC694IhoX>LC*qy z`z5BVDBigIx$wm?Rr;@CGOT0#e`xeby|^(OgCSSALgVOu=L}9EteCY}+fX&0>PBhj z5hVJ`5gJHLzVfmfg_^atwVtm=RoUG1Wta6{jTbP+vhJgA@hgFPOFmFJ>Fg^< z0?VAEmH*53jCI%Io#lk)5lW+GYw&Fh?HdgOeMWKpbg3-!nUS@1zf_D1Up}Bf-S7Gh zDGs4SSozkKeGIXW3>au`I-}-Xd3jeSuQeh5D`2I(5p7HRFWT;P;yE_1WVz!tc)i)u zEYa)j2vqC=E+L<{LS)8>)cziAu8AwdJXi52Va_kJA$~HMGd@4pL_q7p6`7AF8jgRO zfee$ELC10k-~<^UC7m~Ts7yZ`>gYtf$ojkKS%==dAll-|g>8m?#YU%>nKrQkVS8n= z;^HGU^)M0F>}JtQ=Ugq_Q!mWC@;pYcN)}iOI#Pi&?CZwf@ ze5tr{zIsO#Cfv4Y2q3ir_lKACan-ziAAT$y+Yb+v`JJNee6HM}Pqv}Z`GzQ^@ttM$ z(thr!L}%pv5S_~0K7{4jvLiB8RnC3}C8l&zvIMoYE{V$tr0p=P8xo?N{hhV`nGE2S zj8MVM14DUnTtiwU(%Z(c5g@p;t)z1}iA~pm!=*c(s8+sJ284QU<=PXPu>!c;R~oL8 zdZA@{o`F90FW-nvO7}#EX~*yL2pRaTb%D;a#xiHP66&|+7zy>9eyztIDBl4@iPk&B zasl}D-mDx#U;_DGcl?ptsuteMio?KU`azP5gZ;Sc*hu0FM5DRlqy2%*Y;ZQwX~cN@ zoNv{hz~nuQ6P3=NK5*1QKK}3X8&QL$pYg9$=|Ffp;(+S#z8WDy8Kg)B*P}4Zy>)$p zy7^}IEnwsx{$b@x%f&kKTa}1NVZ-$M&KFfO6#&t-_0^?H}r41xsboEwEi^rz^N&-{%E_$P<7%#*x=!K)*-R)VwZ?is;g+p zdk!-i(;49CR5hIbWm>H|L(v4fRG%$mD-@q?ZJxsxXE#$Dje{umVO`p!12Q1n6+e$d zry>)pMIw?55ZZq8ev&~ZC0jo!1b-%flH>lnUO@)rk^v8@8e4blisw=ak zFb-A~kppE55B;YQ^1?pFsS^7TqW<~>$?p4%YoeIP^_mu?f(-SZCJ=$&>iE zO>83>kVyuJ*X%cbpgb8x_C9bL`Xz~Yr~LhdmH|3&S_2~T4&=U>I;L0H;E0^)G~1T8u>tGanD*revzepI;z%L`B5;j&m4=>}zTpBYrchE* zEH_F(Fq)Vz@fjo}86YnmTukh;&}encj9oVF3w@r;9G}ynr;;xRDZ zYJ@hj#y%@oZRmE=w;S4A1BS}uPy!3kS6gk^h<2wfbvb4TccoxU)HUA+aai*KLAvfQ z5|GWv4ADJR-eaW57XBYIy>pIv+Hl62r9Fv7B^}eh4p4y2|E6AYM9wM;t50_%W7>5& z*d{%SYU{pQ7Lx4Sz*8MNm{6qyVdKs27mAj%-!s zQ+?dv^0tKZZC;$&PR5y}-l0VXuvf~9nfsOg?u-BtV^&sNQ91rlPnIV{ulMuy&7Ox; z^CGj^to_gJ?*-0#yDB;&Q7CSd#5$>OI&E|jQAKbo) z(e&PdG|#kHrX{~*El^~{bQ-1qw=zC8q-)jAZ|;4A1q6III*Rqk4^#joo|WH+#(*C* zO&l-X&eAj957m2NmCUI3B>O8@hvf_cKuwA|LfeUx0b`Ctw*HaK8#%-}PbM;;ETpW$ zYHd)#D8MSHMlc@rN)+OddWT9-rKFAHPyl}`TOO#Pdi zJvun$0sI}55|G9#(<_fa1A^v@Ofo<#ZKAzt=1^4nrhuiAtjvc8`D?#~HE3rjU8GiAvZA$->)$nG7vd28`3Wg+f8%5CQ=T)u`zKSquF+{2GkDQ z6bKW(QI82#9OX2pR2t0JIBR?L$IGypTiBbcq3W}d{*q&r8-DZ3o6T-${L%IroGa(! z;sHUgX%31K65+He(=w*!+_C27^?2Y0>zX4NWV}ac^PBU#SrkOhU7wCM(+BZ+Lw;mtJ3y>9Ouk{EFDPd<(^9W#kgMX@5drXYIlGY7w>IbO*OvfcWCd=ws+ z-QmPNzr0={kX>CiS0z?_Ufp?G@WxXb+u*5wi%em9 zhx<+$cSA;(2bDBZhaQKv4JC#V+Y7_Sw0HEdot&PCZBC0+%#5=7`k|6iDi5{b%`zF+ zfC)!rXr#@znMQe~M2dRlv@(%$O0B5H+HcM8Ut`)2=vL|=qg;eUsI$RvvO}rGv!vZz z=NWg=>b=e77h_}q>iX6A!9>1_D6I4Gn0UVP*&M+YYr73Ck7~Tj{>C?kNA)$00ea># z_itq=V$po@i9?-*t*%SKmuE@nk-h&U1j_k79a@8N@GEotcyeQMFmjwSJ9(uKTBrC2 zGuFW@8C-|`L8^n`{7IPQ#eX+oDoU+I+f#!G7uTkkrpdgON;$C_bmns)?D*LhBJFdHZ34__umjqG)c?NlP8CQ*54M_b`)M#cA$bLQsEjp zeze_gu+Fv72%-1G!nt z-l9hjyI2u}9b`bITc&5WQbi#V6kWC~N?6O~OJusk+m{{x6SLgq1k|_+g4%C~C9PV_ zha9?z@K;rjOlXcrOErQS;xLMh)F%K)6NofV24tI%YT7zFJ4k1w!KnQcp+@%TrO0Xvt)p{N_n52k_GP&&*h|Fvxrmvg0?wPmg_T~ zxL%b!Y*|_tN{sZW3iV!}AC)=ck0^OrtYqN!ShyT)_nA`lZp&phcw0tx_0=q2p8k2S z)u1QiZMl&T^{g2mU(ud^ah zRUM0$T!Fsb_PO_J+9`8b?}VzPPw3^wj@RK1QwhD1vBEl%pA5+UkT`utfB zUov1gHLxS2vSWJ1swBWlzm9Q$S!pEs5pUVG<8@>{8Ss#_c>|Fj_-U-|bw29%1zv!~ zM;Y*x%_Q3IIxO`P<|S7tfM*2hzW1t z8Zd}C$HBAEg{>(A+mxwmY>cU;ntiZ=WpE76<>u@~hJeG88!2i6Umu!x@Nnn|Zx{I` z1|U9kvsNqKIih!fmBzm*Ty&1{;1ke?sTz)ityEh+M)-4wmu(%P?H+}V(L-NvuX>{9 z867(8t629rW@O`c_dJKk%ycqd@Rsf(rLH{2|XX4L<|Htw5 zjTA|aAtfrJxso&2NbcNb3|mNHj^vzUD?%oB?)$zi<-S&F?jyIHg>50p<{C4Ve7&CtwNt=|@zs1eLK%%^@Wrri=z*DoNz)bqJFowN{RB%l`j`DKqjwUG zABFKa_;ibTAE#KrswF=O^T zsl;|YrtdZ*hE7<_C{da2Gw3Is6W)zHZ(nNbzm3%q^NzftPg}t`odWVhQB?;c;3NT! z1d@J)pvaCqcnLvvbS6z>ov7Qo0{v$cSx` z!!D+)+_mR{8E6SgANWl&%H$6VA@uk>*bFXgg+d6Vmo?=FZdLh9OW)}ipJ*( z3Z@k&H-8t7+b?6N?^uIrmLxmx5o2CYvpFLdndzsEc@j|^14d8GZ1pz^X;Ny|E!D(R zz%WL2^STX{oJucsyLpBb(pOvRbY}PI!)r!&m^g3w3T?~)!%iwuPT;{&2|HH}J1)ET zwqB*k;K>ml`ABG68k19vfJ4Y|;vy<=h|jvpLt1RtdnS`8*pZ~pulKz*bq$;jGyp5p zQ;7LP+(k6-h>}MU*&=+?G$H1aS1lMbgh{SI3kQ^Uf%4GKwBVIZ({V!Fa;1xNKapk2 z_@3TpU}ZCxZPlgar!Cc=Lo?SpLCZrk%UMg0P`i~+n=d1q%wDZ8@b~g0Y0VX8C%>Z4 zR!`!|K>A57`k5Z7B9y4~=l9;`E9VWX=>?vkSL%IN1^kq&;m_&el>7|(ObDqHKA1C2 zQq2(H*%dX_zq~qmbu7K&qz0lu6(cG}liWP&JkK|UkF91+>e>9Fhk>EdaM#F}EEVo6 zxI8>r0k5L=nP%&|wylUVozk#x%3E2szJ@--xjTLrN>8z_AlQQGtA8qqCrIk(FgSx@ zk*vBXWY;|L-{^S@wr&qC^X09|XXrH$qo08AM9pgpWPh%jSJf|jCUpMzG46NXu5$Ir zlZi|btSDzgQgw~2M(;sXO|@~GzlL%Z(*K#W>(0iuayz2S5cBh>QlX9L1HSjhlJ3N_y*s?t zzYHgyD-sZi`59;^RVbz7M?N*}6p(h=p`S?m^SGt;b+zR1=j-#swS zJeYj$!e337r*H)C5)e+bbe-m`JI>a=td;Cs@tU%5BYfWqjz50R=zf?5d1KnbKyY%e z6gp8QarQ@h$`-aukmABO-A9{4QWQ*rdSyRa*<6)Mk~LF7*n1UE7-h{s2(ZoQs_S zhM;RJVK(#bT|OV0cjrh;;?^t5pVItFxmW&RIlic!1NDBm!v3CI zfzk^t&9SB5e!=ZOPXC-Kn3&qjuaY+s6NtBuz8tE*A^~q-!A0WLF_MkH8rwNWyoDF~ z%N^^|Qr!IN1Pw3iuLY0kFSS$VftseZ zZndS-^(17{vpr}UujZ%FGujt;1p5_c&+J2ijvA0Dr^#evjP4|G_kDo~*9|?N-i&kY zsfn`0Mg4yZxxVGZ=ja#cX9$KTs_u@^fKJ}emDUHhe7Nf)f8f5ecE^ETLv5-@8olpr zW+BL_`9vk6{H9N7?)_^9Dp%M`!)!tNn-4)jdquBSvG_+)p|!+iMVr};CO#?uin#_K z!|Jx;*8p(&DS*k10SEPlj(HR3jkTUq#Y#OZzuwBdrf``@BNw?;fExrNE$Lx?9P$(H10c2AwH>P$PUP_GAsMkR3u(m>OgRF%ce4fM0gu7!Ot@AlNODc|m4Hk-vHpOuo|bQ!$@-?A@R10~5OI z)&$~&U;_46yiLR`6q%EZcz(P4Kr0sH*)Snx8*w~7=X?cn_bXU~_BM=tps$f#@Q?Iv zPiP<7@wI6gfqA*)yf;MT$z7_h(6gtTSiHROwAvsovcg>`aiUwQD~8yNRkqL-X?)*i zCh{(#U8S-#1XvylIh}|*1&p{YoB{??DW2qXdZe749b!>tomT-G#Q*oM=I#yc6hw0V zHOO8F!v^7c3P?l4rbA;G2{pLMao11cAG)u}`#VoZ2bj~pM0-HOF(Y6U=@SiaP2tFx zw58R}skn^~eeLz}D8v5gJ()Eb#z)ON)oece*Q-VJ&CO(j?}BB;;wh@r$rO~?hlZyr z&mpYkSkPn@b_cjafCI@A`AR6IQ5cxSmwv+&(?2(zE8N_&f_yTeRQ0N9_TGDoODnnc z(AMvm5p|dw+#N9#W@#3$ju}s$URiYg>GGQTmizeTUDHeR|H}3KP_qA;oVRzFN_nn= zgGIMh{G|T4(&X@LIMM7f#H=c+;cX)|ya=Q#5{L$K28y4{WtHhAU|@U)VBa zMJre7`L2N_Z0GG=edlXGO%i?NhWkFsiSknf*_m0IKsi?&TY2aF1~0bdR)QgkIt0!K ziRT5afNHM66TncrQ^0*pu^ZZkveZg;xUHOG&8fQ3aGtX)UbDVE{c3pmgu6AK_0Xv@ zns{qDSImf#Pg;01L5p~F3ed|m4%1mMr(Z z+`jxzmGfTldDn<7F%{YCw?ZK#e3laIfcP~_CNnfsq&XaD$L{DNGB7Q7kE6Codf`Ws z4p+8})B(W~Gd!$E6&gH%F=?2FOY}~j7)i`WK0w#4Uo`*@T2?7vGYm@Vnyr>HRoiIm zfOVx;+chPHdGZvoeb>KRA9t2nVLl~XU;W%_6NuhJfyB&sdp5)>w06w+sl#bP7T=Tf z;XWg_O$)MS$X08?dgxqU@Oy#b_Fn>flU)5wmyK@vN@|=F)iL>038;tBF=`OIAz1Zi z8m5wF<3MR{kGgRSU}1X`^sxBk>07QlGocVNKK;lZZAQsU@+BjGZHGIQR&@tjID;1W zRs^xk{=WfcKG23=_>9YXq#y+xC^dZUs=ic76D!wk?yhkwzr!fa$-DZZY+^W@D^pE)v>gQ|%cF+?oaPN%r` zZdk|PuWhc75G(0r5Ycww=4>7)lltELu@&XxZY z0Z7M^rOsq|OKej5jRMZ3E%`(A({9y69@p3h{G(;R8EaE@XP}hEgDh+Qo0FbD-@*u- z*GnR&-}3Ag)Lsy@L?2cUodU>gYPo82Kyq#eO%j^-bn>CxeSftAB+AA7vU9!7<|D|T z;8OreG(DaEsSQYH^9Bw{sbyuS?mpW2Q1}u%^#r8NLD4<&fsQbI=owAaX3}UfeX63e zZCu`_k*(Fso}wkM&pWrIcQyIRvR8x$gqlt#}`t!c_*C5wOKea!3_ zETWCV&iHHsI>BTOjL&^xv$^%!?I)9Y3vTXqF$R5D-9tYGl>cf=OZ6pgD`~M$=gNFm z{vlp{=SL~vuvM_@H{?f_DfxP~FS+{^pnQCEP4!n(KwIOxo~WExZvl-s`ML5I5hq-& ze1(kP)}<-`?>q}H(rmM=#diZL3Qm&Gtep9C2ipawQz?(o&nD`;9dSoWQ>2r5=47R_ zStf7054?B;^4<~P;8TF|GVLpdEbO=SjIuebl-yNax+w<}854Rm%o=|7jw*%8$8o%U zmw?b>xH1skWpgD>m$js;9eti;`7gZrV9dvN(d_=x+xb&KZ5Jqs;kx9O7LYc#U+*SJ zo%?FsZqH%GhGl*;f&a?Gy_gL|qZziSTO;Yq8NsIja(cQ)Ny;Xa*UUZ3V+fcxb{OXV z@S-NWN8c6Rh8xj7*se}WFL^q(l2$#0NLRpJA#rH6$)o{Tgn22V$-K#ZQ?B z24iwg0pg*>K?^ z_`oOknc$_0ThpZY@E}Rz177_}E2fLZKUBYktl*?h0Xck>P=?i1M>koJvlu_7uo<+c z>{Y4vlKyDN@^_x`%?iu5teH?q>M39(l$P*FDAi)oHLWg-R9NBm=l;Vm-Mrh^bi0oU z*6JG4Ki)!i%h!5IB|@qAbPfA4jQsZ7<_~(_Uf$&IWr2y3hN`N~cQ37FSxv6skTW=2 zVhhp=`A3ar%r)k&Wcqfi*_2Wb%}vH<1&XsxLJX;V;$5;$KNH z%PRg{XXHuR9yb<&K4uPjX)a}T4=9Mf+&EuBK=7ObMubs+MkzR|>F6mS75(`9k#u`y zBO~|4d`F6ds!~zIG2;AFo|8nIJp!V52|{m<9ccEYB~PW@mw8)I zcKz?KIMn*ksMPN0i;X{s*whOndFLX2sygAa{sp_1NyW4{^|T5cOS+vN+a4+~{wmHq z;to3JFw#rmJL2CM{LgM9Ocf*PM$J8AEsM@dYnETjKs4eMF#1WshTcrh z9~8t`iu%n=NWS|FFMy)ci3hnF8R!a*<{gLLjXc2Xg%GG@P(YlHr zMA^~k2I}u-Zx10&HRAO80%Lt>p>ymVxV?1Ii{WVIoALlU69qVrod|`*G3*=&Gz|F^ zpp16vEin)s`f^|T;uwP>|HSHzCi$^|NtNPZ{9)Dd5XXJmM^pMIQ;lEd^4Co#{MP-E zX-PrVgMY)5?{0JP)hY@x2VaA*kVc1Ng=qcx;`46SY%3d`pfK^2f;A3MXr^+)V6uu1@K=s%h4Na?mh#q5l1}B}Z!9 z;1VtE>Sv#Tu`GjYo;N{Xe}!0`(Qci~cB%sBTz^cJA(_6DZtxylISQz*=-)F!5-vV8 z9f;dFv#)<%kH-E0{f%HLG?GB;QN=5wWThGxjlKP)xTXh?)=A0<7E@`uX?njF22S#_ zCxBsRX-Xtj@Q}>lBG~@ptF5K=ZutBFze2~)b;*$NSNb>fOm2DBL%`sXv|x|-u_-)B zMfx>(?=3IpK=1{H@-EQM35QlI)s!@yN_JfhYKRrQ0h5T7y7KJt+3uTOSAy#O#eNSC zv{cX0H${|c-F(y@=)`Ncznl_tpXl}J-bZ3fV5uil-%bH+s4!=gl+(!!N94_bYsFBp z;&1~sVm2SA2G_7>w7ku>uBfvn)nENK0}PcYM_X(AoC5B1V2%W% z@}-N5wS2vwUl5P=mgF+zIdF4)q4fLf3a*fa7?naStVODblo{(mLylvFSN}H56efO% z*k^?pMlQZwe&GY{I|Wc9@-vfRu4qTfOb#$Aw*X;xbp5^Pw2}V1#i^cS!<@wC?-!x- zKy)!JWklfm$1>Zx6gB9+h1ONZFRCR0#ZsZJ=!^(3uLCpee z-Y&Fw$@<))(>;ERIZpuuKrj!ce~mqscwV_2R(j<2r1I<=2@m(5~SI zBe2~;+qhK`=A-kfTsG<2d$Haj3?b+9RS!TJXyoZst)v%O!1!Zv+raM+|1FR*k)4XF zAS{OmgepTKr|)4DX5D=3z(v!|(-Zx^77I)=16BnKjqCu`>&F)%8y2XgWviBE-WLfq zYW{h%;%9DL9EAB33FsgC^FY+?k2F1E!hjuEkrtts<*5+{muPP-kq*0|FvNQ0olsOq zTH}_5Ec6{(8|_1jrX&m;i6vtcem)Dg%ar`Rr*Q7$WK9$0OzWz+w{b#T*bej?%p|1* zHDNgw`dNNQgjeZIoyECpnWL9QRy)tH@V=|K*-{ng!ka+L56BW&xLsJPgOtiE6f`(r zRMm*XeBT_*AXs9ChLy~s2l-UM=@l}bwHbE4Izg}9t@sD_9M`Lvalwa9xC9Is4@w_h zdO~la500;_?6`Bn7Qd9VL`}SrO)%Y0b7H^u$g`oEE;Q!1j&x zO>yL-95$Sl^A+8toR-5C+$2tp_GQMJ;zHaj85@=A$>e!i&1yKI(+|1REW*Qd?1UTQ zp;%%@#L;#_BctMjB5jj_clDl&o~$fp6C^dH$&*PS23s#MOo}E!o?is_@#Y10!O<@5 zZc^Je^%Zgm`Kkza*_bC1U7ubS0}(hhVgc>OaCC#aQD3`wG?eXmCP!xtd&O<&7kDUF z-p=1>0==H8-981}SL=7KL;-cCqhvhi_L(id06#uvGSQ_^I}YQg2=FZuLfHW&D!(XI z^AH1TGrr@+4|zE9K$Cx8v|mT8^bD`{(9B@TvCGS zmJ5UG_>+t`v4#E#eeLRCt2U~7>cZvqv|x`rZUJwYmSOQ%%)eM|U3+VV!!#Z%SCeIi zIEF&D#oW5|CZ&U}$alhwZ$aC|dzL4IjLt_SGzi-G9YUr8=Ye63Px78omC|LT{gNHy z`Jc92uyDEYi8;I!70UKEVzHS3Pp3K4qTBDiXNnSy;4OdYR1iR$ z0X{oWDMDNpZf1$uvrB#ng5|pkWL=uA?&9EO{tguuXC=qKQ8vLQC-j`qgT3`|H#^sBU zmCIsP|vwM~#dktVy)!o>Z( ziNAU{beqOZ1S`@I<`{`P6W$8edSqtj9|Jum?#w~0BK33cJRr&6zyA^l$R(Zv2Jk~t z55%ar$tQ7MRnW~FPe8X>XakW__7HK?!4q)5io!wqEdyzVD^5!6&$W^=IFIum<-S?7 z9ad=qjetSB$_+w;i>i9lkZZ9(WJA*63$*FRxd7mX1b)jBhzg{ycqL|CuO&M;EIN++ zvt`@Ws(>h~97XP@fO~sP`w9e`X0i znZW4Wx*=F-TB zz3xsM%C0TiR3`IeeNgK`u|>8`JDx#eNF)* z5cliB_4NJ*Do0WsEKJ}lfqm_{YH-t_Na^=ez+}pVvnfTxrh03T-DF|_4b7*@*|#7o zetXPq*v3@L`&ocGV+H$E$Mgw^^OzB7)PTC`kU&0t?OZd8H(xBrztXSU?Y=nP=3*I# zVML(IVRqxCMOo-VmsdSii8K-?c^#2-EBv3~>*asP}FcbE|vrW(t#yZBWOq z4y#BJw#EAmccbkaf~`;%{$8i*K1DCTAgX(49VzF=)$s1!sI}ht0N~rE-h%C!e^&v> zLKx+a?_?Rt&d$Zx5gT||)Xjmh?D%^h9xSgN-GGeJL;?sF?VUcM<-q6+jO>`*h+Jo^ zvB(A_86~<4k@yDsGf-4LdANf6ptepc4lISiUQC2(%U(;e$zy|`A8u}1?FI&UlcLE^ zXxj&KQTVIfyhUSLJU?FCy_;AW%xSnI+GV#=bqdH4$_x%gq-P~$`Ss2H&~n==eG8)a zOZ){~`r7{h@F$Rf;KYoG(d4X52V*hPv?x=p+sO7eTQMgS5&eBan=jT^3NDocgY${y znOh>!=9H0)?MtP|=bq%pFAbh|73qF`H8~c@uvQC|g|^UEM^*4ip5xP>zhoa*i^COw zN#D-J#0Y-*ZLIPN-U~$S4ID222c4e3rL7iGo0eueIE}*b{P9uE73B^ z61ftFjTF9&wKTzdq|Zg+HkW&CMHA)C^~9VFqbe#3(Z#^0%W2@WfMBHz1#dOmn}VPx zat?pE?j}Vho2bUy&!+9cUSszOHc)axmIr-jt(_g&r|BL*U66{N4!r(0r6z3){+t5t($l@#a(3RW zvT|)W1)RxwdEqbM%-`*w*!Q?W%3ch#|#*eHHbk+>7q8(k-!o;F1K>n6As< z?>P{Y&5kzDXgv1T z+%>;s53Zkj+u+IqH~%{H3G=22*aXz47KuZ}T2MLiJKpS^*k(R%{jm2gg~wOVQn&O1 z`#?MWc1BfsvRW!hP@BSMMQ^9Rj#J7aPT+o%(j09CYSyoE|LBf6ARuxvDFQP&U~&9S z`jjI{N@d%4LVi6=eA>jxU~R2vT?gHFu3`0<1#=Cu|AN+Q@DD^9liwD#@9-`gs9#*F zdTH}nsO*(B{!iyhR0 zR*`1a#Sme0y;2O zncqTrpp!@RcT}mx{C8Ul*)|rx?Pu+zzLtEzmt6iJ(fn55)vlFb5q8g8mbN;yvH@+0 z9hj?&3B1_E)vFs$oj&tKq3|MKBHLU`^%@7Q8$3KeD(ueirKeLP=)>gF`_>D`$rzcW}}b_WrJ6`yeOW z#3?|YMj4(P=@~H(WL_Bb+P)&98(%ZDhK+k?kvjs)*_^ZJdFX`_E2-_{x*&?aj2RH# z-66p53#H=thKh$%8eYI2R#hTpNt_IsCCO+%5oY47xB|f(4W$Y;=Sa~%dwTDo{?yhz zjk@YD4=q^Jzdqcm{!13x-#ub|3P@RUZNpD4dPz3MJUa#G7(7bV7p_cs^UGj%t65;b z4z#B#cx-mh62(^nX1ywHZVFtTh_c?5<$9oB)R1C%&mGqa;lt>o zBUwuMZVUf*R8Pkzbm9mY&uNdX2q#I6rruV*jU2dm=5X#BXpgVQuwIY?P?k(RI?&f(hyhA;-gLaGYB4Y4p@!(Kth+9OU z+peKZvA|c&lQ1>uwO}BqdWQgy!;l=5pdZ@9FBBh*l$)6Jg(*G5*3Wb*GtRYC`Bc&* z^rqEng;Km8bGcc5O0+-UY|R7elW5uhJG+8QZK%C~xurI>CL{c7#sVGL`g%QJ#{{Mm z?<#RKI%NyJ!vRo9yXu5{PG6whj;6&_G-X!V-O>Ybo5OVqSjRqCdSTUnDG?A7r+^V} zhO1&U%`SwAGYsbRcOEtuxq4Yo!a8x^jE$Q+^ghi*5IRSqG zL@LcU6jaXLz{Y=ig)Q)FnQF*3e&A+{6V0tH@vAP9_b_bztA>GtQf%FIm*MTmBOMnW z^O21`$sCyN{e-7lJ3<0Zm57W+eb3!&DpD;FTykE~X~DPPnVP*tQI#9wA|w{Ai86xB zs1*hZkqeAEp?chQz4EccUd8bbhGqU!WY^MU4wZ$bVn+C8-*~I-`J!X|iJmG)$adtn zJJ&2^lggU@YiGKes3N;u209QL!VKhs886uSUNk4i&Mn=yYrp@fJb#+UT+{JAych3(#TBV#SmOBZUI_I}!4_yv=BO|O!*YPLNEcVV-jiDFP~W5+%=y3Eb-{!-IU zvG^UZ{HB1vN|oUgHNLVsblrpM`MPoZS&O7+H?KwoYn|q=!t*~0{OUl%9Y2Y+9HaGm zHQJn$^6QDcsy(0RS_f||i~nQSt0!27#F@(@Lribl>D(CKa(eUfyp+Hn5dYry0Gbmn z1*5t&*q5ITH>4U9n`gZCuJ(JZ#!SlH5aN0+e`wGCiMCRePo*s1oZApe=cc8SyvGH! z=N4?J^5VdE_um8@>N6d(*IOG9@k8_5(loAKU3yQNPceM{b3j&hxPTuPc!M21lye>> zajX;V0b#yWSi1?%O3TkeoBMF25htWbALEc;ex~MLF;?|KFT1YbZ0Tg`$$p{h^pbrO zR+SV}oJaccO?M#y=h%Ys8(z_QN#T2Go z{rL|)en#UtGDxy^La!}~zmcs1I?>{0{WwtFh)L)Si?coCbaFkEIW|_YTg}}&m+y4SLMMZlaQ;Zg>+#OFmqr%1esW@=$vaiKRc@Vb*e-dh z5V5CLCWlOYH&Ja|_FQ&m~JaziLNeeah7Ur0Peoki+Q< zNO7CBT4$YX%}xLILzMt+zSH%Yvi9d&2#7o(YQBokM5Cz`t$L8_Y8<@VDl$*P?;J$R zP*UraBwLcr0ZZ*2ODx_2d`E5hX&1bHVlSE#6&_S%m-isb00z8PsrBH?TOEz*ZXfK2 z)-&9hby8setNc81v(40r_v$;=XB0DK*792JarT-dP4}VMe>1+dfhpa9O)g zFC$RF`)r4KO1o1xUj{!)id;^ia-KDLYXUkp9k}ioF`8!@4IfWAkzIY$q5m^aj)0gT zP8TQQ;t#6_z_RrIXKS7EG~AGu=5;w@1LGJ`VF;N`4`ULBt>(Lp0f^sHUvT+-m z&$D}9nL9T7wH96oj>@$sxI5u)Qow7=X@2U0r-1G(cShVK6ZR&K&r0E3?qpUYi#xp@ zI=Y-Alo+TY8lQ_pL*>GddhJn0AurTT+$57E#}XIUAjQqc(izp?P5}}fJ0%Pklu4M% z_|EUpi4v9%c~>~LJp-PMAhb-LG))o zi@kN{B9@ndE=_6s4DYek>9nb=@uS83amXP)^gg8bG2ntJ7ZTes&+ut}5KW;vP?eJ9 zAG(G%^Ca7Uev*23u3u*Rk+{lP-E%Jw2)1dB)wgp|vaSL_wu_zafV@Pw*dGz6O5w%` z%8{VODZqx^9~r`U2d&&40&}v>{Ipt-L1$EtV8|Cj=VkKz5)|QX6Bqo*leg2R!LRjm z)<8STg33UVvv}SE{_5US09geC8YPXqDq+BXY3fwXEvYprygf=Ds2cqHO6!a9JFWmQ zpBA=snHmQ>*GjSxrUd5K7RL_FdBw1I&!==4Y>1qlsw1vxx_>DE&I7~z^0X$mc5OZV z>{i|CY&#ngdAt{somqcaI>oO++O2XRYx$XI>nXL`wfhRL4a8}Gm%z3(!UZprp&dSl z4Op&cZ=J2)DqzVSrejW z=D%w@1&~=X=$UJHNqvNAtfk$SW)IK#!bnuwt=?3ip7n1<0^$YQ0TsVAK7>;F-Iu-- zZ@shdtzS4sPeWk?WPAzKP4C`ZSp4eEIX+dMCPvD+Vg>v$rEsXDvZqi%K!6!=lrCeN z8jKir;NBn&*K`%5E-SbMRNBU&Ki>?`WlVz(@<;3rrGhqsJHLLLom+^p zN;x|ZbQ~;p&GV-Ups=J3>xMXU_^hmAGxI4Rz_v>K2W#!6Pf3p;JKkhO$~KtH@2@UH z#ZkOXnNN_YT${e%iZB4`dU5AD(6P~A@FI>(iu(utdP^?M7$GAANn=NSttX+<*=J6!Et1 zy0fq6@M#l^w^mCbNr6XH90>W@i%+A7c|mDfI^{N*60@%h1p}@hR)w)o^<=vt-H5Af zH5C0XA`c&1H-LC9t^Y-v5H9U&yT2n!(e^x`#>eVmT1W z2Hcj<-@gR-)l+~nD9;ihf^g4bGV(?#mSSE^CtoskPowbnUY_4FPUr$rakK%X7o9!_PzHNPTs);|e`>x&;=TyXhli4ZarA@JPiLjb!%tBDoPi%dh z+;AXlChsT3laYom^4=0lGe|w#1D}Yka9(IcUk>KPew4Yt_$x1NQsu3?r$}>}+*x4| zv5zIO>}Rz3au?_jNjacIXwhS-QCDJ=pN_aV}}33Py= z^_q&hq20zXL|;@A{1`g9F>>WUNvv41e#Xv5t(nz6fE(L^r{F2hFe(R0FCQ!)IuYR9 zlnrSNUs?=^yXks46L9-=T2;^#TOh$KpPGVpp(5z3+nMs)Bc}i{yAw>`K1J_beMu@v zx$_n>brqJp%+2b#?70ISZ#y87O3Qw6kU5mreGN*aIxOHIy^VV%Nin<+z+M_W7g~- zEh~*xU>N%|XHUV}j=+I&+Ze-l4g@J;SY+^jcOIz8TJf~W?&YoYKv0hpP5Q6EYHD=2 z3ig6uYOQ0-n$wyD)Ohz{;UcF1o7>Y$iDRhuqMuo|d`sQ7xxOuYKVSC&k#V8;#!@!8aJ5 zG~a>cR8OgyWKTcaE$N?H$%qiZ<4^8$ELx6iNE}7zaA4YN%j8wu53gu_cOd^n^^Ems-Az ztZ&&oto!pTk>JQM#j{^u9BUt5gV1_u+iN!X8yIP|EwN0NN?%gk6S%E|NJNUg?&rd} zs7Uu=lUmONO7!G6BBO@!r*V)yMrb%$MCX04|z)Nw(S*OZp>1v(CPHyvds!LiX zu&cVVoUPI-OYZ(X%IY`n!4-EW+-NNeRb`Oj(@i~wOenZ5g|Upf@ze_R*#7o%P6^LD z`}plU(FDVpCeX>%dZed`L8Fvt@}Chq>$m;aa*}#43E$We5lS`ofp)9-p&gs@Z70>p zVx@)aDk+1d_VYqZdfpt{uzT&7OpN&tSY@H#1-8?-LTSY3J&vL&-JYT2EUW8z3W?Ua z;>a!#4Z-kL8ii>@(w^1)ntmbrx!HBg%hQS?d$gO7B29cyeXeq!ze#+3iD=jEk$Ct2IrGHbLwbw8$jiQ(wBa}vyV)Ok``GHCEv z$L;=QeB&q2-pWmE2W|+iYs^NOu$iDY-|H^g?3p9_ZSL}YX+z9$;7^TVn>SP`6yg)%O%43y4zhT?GOp!|tjJ8tK%+dZ!~$YuZi zX0is3&F5vspQ`edy~4BIz>w`MdL?n5L&9Ml?K)8&ZJg(37;Ax&eNh^Axu{C>TFlPX z?MDzJirh$^%T#gT)b58i$5y|1H-3n^=&p-@N;NMJk+M&)Ias?e<{{H@3aDT>=zp9= zxJ!6Tb=FtSyZclcWP4F+uk_6}o%ldoOr6>c)#Kz{C#5VOj%p>?xWURe*D=In1T^fXaw(z#x! zGk05E#th89vWJL&dvxU4@eyhHvHbxPr!ptDb=ik{b^pl6nkVLVWRIzCBQ~x` z8gA`=jmPiS#aALtNo}v>E$7;qpQyhIxOxEjt6crXg({Ox@1;4Hq8-sry2`Z2a~pg( ze$I|ao;!Q`r9giFKi5`p6W#;EdXK1jDGkFR19G}^_J4ifDCj>nSX*>wH$K17P`Qb% zgl&Ax?u|bh(@8HEMkap#1ie`l5o`tp2F>K|CP>mI%xy()>=|i5dGAtgy~d*A)vL`d z8K%Mk1o*2`_hv5Ci=GvVekpc&O21#o126nabA4ZL$f;UhHKDHb%hJs+Iji9|lO8rv zi=nC)AtX$OP|7|!n0h^vY_$2&tzw<2F;`<$DVS@uuqu@8NAOCGDZ@G&1s$3WR;(NG zpk=#>wLM5G;AUFk@M&fHp}R|f_tKJ^gP+`8&vW?ktbIy2av}Z!PyVYlKD?-uxhXfc zUWcj}O*5S1AUZ^T?E9cFRnYa8!*F>VVV>r8Y4356jwb5v0Rb*Ui(cytq-sMuDPrl4 zyaC%>Y6qgjqHiv55}H;A?rHwfUp3ybHlS(KLYJvA4D0lV)Y=7O;<~i3t%b0Sx_MHV z*20SwWc_a+XcxnGLu>$TH@P%-FE6q_LkFmzY5VSD0W1$POW(2T?Nn4Ptirzpy z3v2|5J1B}nLVRFRxKIdUD7dpFS@6LNO`h`vs_$?7jhjzhTtvJ^30k)*ZuJ6#!NkCK z^nTh|;!c^vLS6BH8(fdn6@=$&#v3Mln3A2%zcSGd_^T(=soJ_zyz?U}o&#msm9`fO z4SD?ch%4Ea&qVTWeR8VFA{$5O+bU#!IeEd!clL!=fqZoW`gY^L&gKcjU>5BK?4_3}+dJ9ZbwS`YA zL`0vQnaj!20N!ew00!r0cN994LhMHGqZ;J*o4E?3cyXV`41^6JZFIyLg#Q5n{;B{K zwLFw%jLUjz5fdtc9Z!S4fzx11NWWdXsKR5HN7K4eW2y2h=Deprk8n; zhF4N9iXR6(#P93eFnwKo>7D8!R{#N#DU_u0wA!2OeHTR@`eN(YbPDhZcO`IX`^bp7 zjvgP$#7@?Ix*Yr#a@0;rkovXsq+wiv@F8mDthfAapL&b!=5H5DHG--?gXKcgOHujS zQ)-=R5616!{+uX^FRcbPY(5uI_)U8Fpyc>1BnzL+Jp|WN5hIC~c7A^n_2*|CYfR!L zXM&b@oc^qAxibWjr(r@T=2MD?mfYwiS#FKqvYW|<)7I%)Y5W{4j-D3!t`8M}CuN5| z&{+&M9+MtJ9KN^Rr!vpnH0hCN=!2-%@xGTiCwiTe_X*Fj!h9L%C}07j!P+3xtH>FEQN*XhqcW0+nt-BlI$HeFdc&ywPa_uR2U>b_? zmq4vnx(4;Vm(S4wC6)K+z2K`mtm0ri#|*0kq!*9U!bk?I-b!}SlPTg>17|KI+r2;X0Y)P6|#f#g)O7zS>y!+lqgj`IpdHXin=|NL>{nZs*5}y1Y z9wgMS^0bwhKU7gV;op9>)iKBLwcKa%i?8*Km9Mix+&aT{pcQEUZj$%l9cTLJH{22i zWCL3`>;CJ>T~m?I=3m4&i~#^22x-v-)Q`XO?-cO2U1PRkv`6Bco5qv)t*s|+XNv|! z8slzPg7|x_*TQz7h3F85om+m!H`ttmq$yH9jiM*Rut(efiiq?2X7w zPXrL{_iH&xWhV2Vv}AdtGGjfBbJ){Wa*&#}5;Fizjf6z9X3X#FuQpx+Rh|NrF&gEx za)vaLT|Ig1NrFjh23LLWp0(O6d#qZcbnm`F8R#gMB1a_;<~^r!)6SYS>?ufr#^r(& z_&NR#jaAVl2!PUG}#HzjZ zSnP`{kdD9*u8YDtT7S@1jDe?hCr%h!5*?7P*1B%An4meVolyo&Gj>GWj;R*7NCCO) zuyFCs2W_FSi)6(!zl+G>q)`c#J`#i6sKo+8vK(?x!06TdaU#=KPPWLuPTSJZsSr9K z>)!D+bmXyH;2p~-FTw+RCd&&eX0%}ugY@SbA_p;*$5`!d&qs17Eu{dhz@C^_7K8ofgr zCx0~?TvasLp7+otofziomC!60T8fh(v4$C9bDOB{*jXHihQ@dtuV9>#jvt$`Hjg&a zjIKvo6kprLF_5mRnMgwi3dxzPnXB16Ay`snHui(Zl}sMH>9Wq$ugVwV1TL58OWxN1 zhkCow`c10>bW&+C>!lX4@k_o?N>6R=n*c{`7<~5OK5YM?!QwBxgYPLT9odXBi#UiaXbs>MlQGd$8Kvdw_rPA2RfZMnlCfTy}RzXB^GG1lwTm zevQqb{$557@t4Ar856PDE>qBm933!fCh3mLQYp+k{ILzQHuOGKj8B!Qer)~u#Q1H| z-_qAk7;{D=!I9itz&B#m@9AyD)ELf;b~II}gm@jv+~P%&Nf zXA9MBLt7?wgg;%zWm0xDYuywrVLGxI;_tWDOy#`ewCs-@x*DK4bijBQQnt`0Zd5ZJ zRx>PFT(P>G1JfSj7c4LZq%;G7>UzfDqZhf~n@J|+%RQgZi#u(9FF9=$=iGBBsjT!m zcB;L&sZ(h65vf=cWpDU}FN)}Gn8)w!P!a(#AG!ALXc1^jHCktp8bnGhE!m~WC0zC~ zPae9S^W(F$+=Ee7Ub$LE-+yB-Kb^MvU4ylV!K|VhlH|0AUKR^P6Lk(oS^Y`<@1kw3&P(S%J`Ilt zzoC-)jmek{pU^C{twEs3^s}atl$nHAkC)xKie9~8dn9e;vyqfpL4qfjlf2V>?_)b+ zCk%UIZ`@iFdjelr+CMSDd4hk?0b~?UKs$*J$SEb6=-@zjx`O^P0Ql9%I`-_A?lgOi4GOL3d2~X<8 z#rOge1zX~xM5iCzi}C(fqe@0eJ%FEiP@#io;P+a^cBk$pUb#GWrSGHTyx*nWIRBp> z5uURiGtB(jbCDvwD~&n-y$*5117;$U?Z$|^7n5po=yPb)5` zRz6O7fm%=!Og=xN6sTsE>?#Ih<_!h^2zQ&?n{k|M@^F)+5L-~X9}UFr8{B@!zEl#m zPB|^eP<6tjX!225ZO>j()kdj>#90oiJK4SoWvisY?w!T)Al0Id5b@JoV@gqS8V|35 zrfjWvP43dhH<%LbHLbM#T(r!GdXk^IH7pjFyGuh8+~)q?3|=V{}pQ+rQSjZ{@__Pv2@g3rJO_g?lF@ukNrQf*(YVQMrt zWp{;1ifQ{eC0YXFSaBpAmdbs=^^x8555omN(W0j;saBRLPiC{x-{=4VoU#N`KD6#? zUFfJ!?WG@X>iVZL#tq_8z0YsgB7v_&0z*>_dBYKE|rJP}mPhRJ`agi3bvtl+Ds`D>Nc*Et%c?zO2!J|#4A zsLTPMuRNiBA;=}kw5}gz`Jbh^FgE2Gcb8mRr-&LJUU||U3^**8hGV>K$Gv)mtQOY_Ta+tq3 z3M|N1RxrorJb_h29x(Yo`6vK1ljyz1{h^BM=5*jq3fno0o$HDm`htMkX-zdSmAXDY z_|uI_F5hnb{TB|u8SL9*F?bTCeLv{M0`w&ZCt6T?kx_W1T1TSAZhPnwg>Bh?+9}n67_~6Lp2DHV5}a zVC~{3TnTgUZ=riMHwC@ZNFET<-+Y|-1xWadY57q#_bb(SDd&efDg%^76vIqqYZN%_ z^aXKLb+o0y%;TD(?spwQ#+r#vhRH(8tG6Bmppm?~?33n{p-vY4%ZWmlk8)bBh~n(P zQO2+%kAU253%->fV7G1ghs4hT7f{y}@o+{sBIY59X_J_OcIB8Fv-b9)eBw!Nihi7U z+u9;;dH^B1bC)|5Oo7sf$PHpXHEpB0VYb<_GLAd46B1kXNl@^C#noP!X72ivE8y?E zsqmbR#4Kt=^%(?FG!%+utu-GTGp`M`IW=H<$n*FqtLsb!=#=Y&cFV`cOev8v=N-$M zVNl`6SCe@kfr1Q7KTue>{iIWRcw(2S!tBl#W6)E4Hk#|8&fO|-4^>{6&Hl}8#X^V7 z*qgkp#Vp>+(OSXJ*uy?Dh0^+AIlqQ^r@6VL;Wqua+b5TJ!~Wu5XnR}kPFT5L3FG_| ztO=W;6J|2$!LKZIV2QoH3E6feyx{>5Zt_L^#s`$}N0Qi*6q^g|MGz4x6)u2 z)kCGBDe}LI<$G-0r6_@~;>$kDS>EmE5nJoadw6DX6+}Cv0}9`0=8CV8l0F;`mjn!l z>8y}sAhD{grROD(Zq80j@3@Ze@Rx`f@;Myet^~Hf&N^RbKPr#yrP=R=l>cQ2?p~mV9-7@W}fq8T= zZG{V@gU01_-aQDsP{*cCcxl4;a25DUindnKP=UY=9iPr%D|SuLOM<~;0FU>Mcld(l ztxlVH22qVlb#nV727ywNHx-}byOdK+Qh45Q9(Zf-7QBrN##>Wn%l>|8^3Y%7AGMv0 z=hGSpnD=@V1oOUi-59*F`XSQ~P8EQWW{U&6W}?>A`%fB-tG7HE&l`L#;>oLqL4l!L zbbyi@k$Zm#Ik2oaQeop0uoL$oXJN1#Xeg!@b*WnO5@U?d74V@kGM5KJ2joSP^84!6 z(Xo4#vpo+#^?8Wjl3{Uwky-MF;c-gh9BUwPyUhbKx!U@LOBr zn}Qge!#^~UZ3L;Z{L4c!#tk?#(^u;)VTHf#TI_EqUWrU7tz!%ZE;(T?r=9sac&Lkv z6lSl4piCYSiBkm2CKQA5FeO~*UKB1P=mO8z7+_c&)nGl1il)k!@5}~kLdZPDw{n%& zAC)M?W1lxbfAH~$(CQ8#_iE1^s4Z{}b$sJsfZlZyO9hP$|d^q!;7zuAlA^{Z4VO#h)G5=!lKi2CxrqHz-*u{^6T9IN_ZGDft6w=P_-BtdO#yIimWkP&d;m8{Gh9W4UqeHRZhTv=<8NK^kIlLvG%u)NzTf zZckEQ8nM=TE?xPk&LeC&5m~Hp@^7tnoQym3vmgwFJ13EFuvn^l&`Mp#kmb9`b7dB| zQHS$ec=K>eTI5vVzOv$GMf@klx{{W{DwwDkcUem6=kpy043XgkD9})IllAE&wz>vE zAT?UG#qF@K+BtmtmI=yiX}xsqVM&>%K8R64sb-wozvn<6<#8ipHZa-3EXA%WwQpsZ z7T*4n4SBI3!U(7fTFFLl<=~{TxOGSzXm@oF;usi#>5>8)4;5bt2gy3tDe*}wTBTOO_^ z0HbP-pj}*wN4X0PSBTDoYp=$?*~EIA23=wDHwLG=f86fYnra#!Djc>|-Vf+9J);E1 zbr#$3XRe2!a2txkYurb$Y&0M311)zpG!{X0Z98^&mM?&PeI)VqFh_&oGs?dt%JIw; zlziqu2aMTmwVZ{_;Y5Br-nf1%>TB8V=iwkhXK~-OhYUqE@@^2o8m5d6kZ3|{5z!}m z4n*D9y?@^>{)D`rdI(Fu7!}3z6aftd?`XIZN*ky4Fiw{W%vRAP${#KX|2$ z!RwjQ+g#m=fqRD6-H_+2q~F}mv5fQS_kIion&Bj9iyN!Tw(bs=B=KTQ)1XP$mq5YG z%i#5YxB=o#cN*AXzQ&ix9rVtwk--1F|mq(O z{kL??yjpR>hBtrNgcQ4>A}kQ^;9qkFVG{_qGb7@Jwk!Ha$;PcW(grRi2)^19^uxni z1*@+tzI)_h2i_=M+V~ zJgT{k9IuNW55b2;DFPLHr$5$*iXxW|mCzd)A!@4Te+6_cD&bN4$E@vO2*k^EYKFzI zKQ)81Tu^))i|&}}T*H)7#z)~&4pblWXn*-8uDf-sv&LI}j(*c(cSO96OtrmFrJ*s1 z{YPE2Il^A(WL5MjZ{?ty-@vZmHG_*|zkYfz{^e9cABt&{%gc^Z!b~}BbUON4*{fK@ zJwbI7*Z-LBJ?Z<;(&C<*SXjN(f2=^BXd-y4+uXwJHnK%n5uCfv=4&K3hakDnTP3^v zJxBe+Tv*FHv*a!cOW~`u)|GR4-J!tH!ICo@`~N(mp7zu@8;TDdAWln7!N1_{xAn+} zNPpGptIS1)eZOuwZCtglS3eoGa?Co?ws8YZsokwlXEt|mb z?1cq7AgTT-4yx_(;>u0mk<=Td=^_L{DGqFqpjBZ3D1!WArkRD$+up%-smcxgroYY-^ z#6guEN)m(x93A27m3?#Tcb_qTzNYLm+>>w-w6jX>PZG9w?dZlJp9;;$cNz%cQa9IL zxWqGG3o2$$Dw}q~IFUNEi>N{NVpG-|5kSF|&uDxvC+y!;sVA5>tTtQHQm^gjFVmv; zeM5lNj8M15`8`zk6*?eqnY2<_X$vKT$)>T!W9% z7g}}Z;=$#!3Czl(MQu>gs_=dxN;&9gppkSAms|v(*}ti@10S;G1?ohLdN$2}t9NQW zos*uMykfuTZ2H)|FcTCJ{d-|yY|*6S%3;(`f0Y?$nKz&R{T}V>YOw0po%Z~__gik1 zvrm9y)Y>KaelaimLq^^8y~DXsFx7w>f!RTl-@~(m|Em~gazvAKPCp|bd}-H7ef{S5 z#F10YcPpPoV0hWK17aeOm0weNX+R*Nwy%hQy0o$+Qe~G*xy!bUYDi!iSy24I0H?l; zMe5^sY&#jMwwL0Pbt#8)*+;j~8{-=8tt&9v>J)L}^odWkwl{~7#ogzvO}Z2T$L`Nq zq=Kpwrj`zn3e8+_`5Ep-Em6KX0{w{fa^2DP;Y@sNpar|CRVK)`0N9nb>LI_-*YG7J zHdgKn&Oy>ZmOq}SWI0_rk&|1=Z-B-kY)5%qX$dVPy^a2Y%uRa&MfLTUK;+g<4>zYo z#h4iv?ze~%t3a^*areNFAE~&bwSC9+QAoIh}U<-!ql7|HCP_y9qY<3KCM;GU8@xs53$oDmD-Uq zJKiU6-(Y+GRL=a>^u7AkYnr#Wl^OR?-OjYSS^1>l)qx905wpNBe~^u2)*bi0z>cnr z5!&?i$LRBOPt~NME6182T&ncGcOA^<~7HZ!ab4}XQ$u35 zu^JV7Hdms(4G%RNVZrlZx6t@dYMDEaJ8hD5mM>?hhLcP|1bBUBOeL*Zn>VJ>4*UUU zJ*ftG3(PPbFq$v$koFTxD{n|O8WnqFDgO33#imjW&;9}PyHt~n@B`J|U?zy)WceT^ zzayPe@epnvy|D2LS3jb2TX&s<#p=Y|smg=l61I68H}6lhK*4o2YA6NJ8fOj$le`Nuu`e4-Tg%*^+sxGLOp?-e3#jr-PA1^N}xhM{IWBzdl(w$)?p| zHbFPdtPoE|sA)^{hPnN${u3A$m(4Y?R*M|YJu7})=MKrLdN%beH?zCsz*!)wFpeeC0X3*U>n2$<&Fg6yNpkeLhPIn7BT_K2)e zT{a6uW#T>m)=Ey9W8WEoJh8^$$xjr9)K7f=;Wg`AMn$ti?kFmbMoz#Z1XN0WY zSH9dRCrm0*dICB0gCucO>N&Cen6%J0!8|Y;@Wwph!+ZCS;Y-X@F~E>b+GY~*c{$Z` zUAUW67I&lAw`yT)mi*wGL9*-#gHk???LT0P?Q}VhE8^Y1t|9{z2K*Pa}q>gTi4aVh4bp$ zB~hkcKOo+EHHQ*gr#9y)FCW~fz_^aM%pQ{Xb1D&~b%jyC`mW+C5KLl96Mr%3lX+<* z_C=MEFW1eDAqOFsJ5~2@wT4G`=7BXPL-9TvESV%ot7?yTiEZ|GzuOj?(e;TsK;?^f ztfvF_Q6=TEu(esY@2R&_Z3<*Knss5ts~-;zFwr2q8eucrtBKWcXlC~H$uk4Nx2!q2 zL~rRgiVNCc=LzXQx6nT{hsFmRsnjqeu~2z_{Dn~6VD7>;lbEkfK0|8HF4j3!)#|Ni z%M2b~biBW@yzT%k)FqvqLSd(OC7R2J2Yjs3H)B59damtnFq7tzVrrhl;79P6)f%|GJnkWLt`d+z~M9L1lotu}o ztVZiUMoj{*#tk`YfH#6i+5C3B>VCFLvOE+uB!avvEV{xV^;Gwwskm# z1&O3FcTuit!Q^I%?_ItS?oGHjqHq)@r_gPd@uR)XHUD;U^^LDCh;CEM3|6-}V2Blr z0|8t1Z0fhE`^wL*Zo=IDyc|$1RHZq13*Ar4DY6YW+FCeG%Gl$WaBscI6(5sxxLjs& z$2_s_;o|GRcn57dKuMY!SW0c#`7a$Rxv}pdF`Z`p#a)L>HBeBjGNzgr7&6z(wT7v| z(g6xl#FHJ{gck;{tBX@Vw!K$=q#|AJI?X8p44t6^lv&n0ZLoDUhU4k-hFTanwm3!yy1%bCO#;f5S1#&z2YaZOA=K68oJ-6B)3MQsm+y2^Kap`VPWU2Q2iSqWe z$@#Fx^im_d2N)Vg2Pl`m@O4{PBsWPt&ZK>3ui( zyfIj%yrMBH>Ji` z1?+ZsAn(RCEF*r9Uu5FDt4kb;)+d&}_WS_unBSkN^8Sa0lM|5SWMAai!>yx^n#Xc< zfV#HSOyB^xCKcdeB)$8YA6P$M%2=q;VRJ@x+mNMl4|%EhyPCE)AdMy(<>7m7?jt;q zHe5p&Z$Z={-Wp0~l)Ps5T~XS<3j)9Q+C-I`T}vLc5+4M3;d8>->vG`A(1p)@oVIR-kjwnq4mI=GNV zzi5_~&-#>ixLK22>&nckK_@yltMcEV?;hddMd9Aji=ztx1yJm#ltao7#%a$b?zpQD zt2wLqP)VWSh()&oJ5v4o6^mxVl&Wa@;y5?oxzfNpL7JZwj(wqT=zuYua4V5KT6Vyy zknyo;c_nOV(Cb$4fl!Ayc5_m*JN*UhVfO@ ze0!UJ!Ci6TTiXM7An>=el~ktYrqDh%pXg;aEZcjq1@Zm1_%c(bhHEFDA1ruPf9eyd z`k%HR_Z9H&mP?&YMRS?G%)1*ZWt~nI#1#w31SwGjAquriMKokmpK@ zF7*`}uOq7dZO=V7Al_x`T9~GIC)1iJNQ3syrGGv$y*YuDRJSt-$$_b zJz3!CqNi(^Ea5X(5ZO3*NL{Ror=k!jcD9U|3RgCo$iRKXrDD^i^GB zNKjDkv9_R}E~{B}J8_XW@(TD*4DFyXgpTXAdGufY93IGhZpjuw*1uq)4t8@TKIZBoAn>A zFphsLFc*G(%r3gvt(f?;sxGVnIuB#poNZRea_LrAjv|TnlD$pASsdcJ55GsYI~eKe zE;=4~yGla>H}>VX*{K{mBXh$pH*ciA=>vKPJQpldZXV5xeMuMr@9R zyEO(TX-_*A2pNX@$fx(-7UIqgk*Q*}Kl+1*BeuJtEi)86S*J%V%sM3U+`)~yC!LU5 zP*3I8*K4&9qqro+<(3%YrBiO_(FIdu9=!{-k7Tv~G`~LlgLhzb4i2LOl;%p7zm30a zJ+r*o%HiI)^acls`1})8#p|ietY0M(3Z~Dd$T6866}@y#DJ*V%*?YerL4G8`@ue0|t@M4r5glb_g^}<{ zZcAVYE9qAL>3WM*L1S3+-SQilxE#tR(M1`G70?J+sR z{s_OHsLv^kY168dGk9kU=r4SLO<`N(F=_vw%!;zNgmc7;)}%eGzb*>neVPJly1Qj* z49-3aLS#9RgyY;c6ZjdUI9^v#UR}G&|52taGM5ARuh7ct&;O6V$~268{%b4Ro+3MZ zY5U{C;^Xb|QX^aQhbY;J%Yy5qfb~a&a%PPGf7DVM&TKUQ;y!8H>cyuSb)^mIR;Kt= z=1!yvnmN~e4Fe?oO%I);+64NPnO(_Ep60iX|Hy8u^gW7GjCaB2_Kp1MM$s))d9Y&c z_=d@0Va_pn*%lFg@xvV3N4@l$FP@&e@}}w%@>w2uA4yIqup|*n{vva_pZrjR#hv;Y z+$heq(Gb!7^Anb==@AN6BC};;JK6H~I2ZId`)&)aqZn?DeqO6BnvS?<15(_uL^Te~ zN=f3GG7qCrD}D2kiGo#!sSkD6&HXMmQRjf6tJP;F_C}kr*uQ}R6<`0Fo^p#z*WJE- z_qoBjyEgXE)|VeO;Ni+=ka>+}YB`#kHJs&!^awdHx|b8C{4~&pf2}6CRNXFdNQn>% zK8((w1C(z{mfr2p2-J7>a_IXzsnINEW5y{qVLmwcQ2o)3Mc`8;skTn-8@1eR)5x$# ze&nT&5H>oGv3NrcWN`0V%cyBB=OG@hedcX*mfHQXo$y9RTeeZ)IvBxcj=8J9%+X9G zRN)<7Y>W$k+X`Wv4o)>exX5?fhI+Cpivn)AzchF6mR|*if~f*jywG@FU&8CN95)|X zt?Rba{EzASV>Y z`JDvy=mHeS(Y#{6CPz&XDpB+?@XSRCy&`}9YL@pg|9$472gU1Ca-K}Rp&%8uZ;PSe zh^>9it((p1+;P@r#E?2JbM*&eu(wOy*65!9;>V4t-<2d>24z(0@J$-5C7(;>`kfR3 zUaJ(Cu0oZnG~U6U8b{o-JoB9+`{N_`^9&pRGV8SXEZr^I(1WxeP8|t^v&60xzV?Te6L+| zN5<0^@mUqS4VRTC!Uqp-p|=W$$45K&7*0%REw8hVmyVp)CcSsIP2a!m!P>L?AKK&?O7y+b&xN67JFvd1_s4>-r{fJgsmDL-|lByUkuv z&NWc7v|!uY&^YN{^MNdPVr&&$4?5u;>3|_xw{z=b_{Yool*VoWllw~7&UdukuiWRA zhch6@bibUb&e8IzuPIuAKK}KK%}a4l?wELv%G_fpI-dSvf;9!5oRBYTv^oR3+n;{Y zraV_1u1M&{1ljKfpboHTYKRjMFU%s($u<#m znQpJh%U@dN%7Ab#B%r#4<cFV#NcXEr@n)Z4$gAf2nuzU-MOqKQ#k{}2He=Y>Vew$?;UGl~XHr2< z(GKQrVyB?=u;8Dops9XQsM95pl+xExp5PQ}3$-c##u0Hv?t!-By+p$)E@{Bq*9;m0 zzoo%&YCo%n2{-A)tRpsK7gInaajuQ_>>#fJ68@zsD^kU!AwjtrW9U$)8FoQ}i$RH_ zcAq4>^qT8Kc}GJ!Amb+r>u)_1fEhZqpaaOlSN&2rA)}5AyynMw{LylzQMc!%<^H|; zTMwd2jSX(Hu#H%2%F>d(%UbPa`iB`4Xo9EXsmZYSO04|Zu zvsR)#OlO92nXfg?j4~)ud2hW@Px~WOVA01DOo=H}BLw?P{2&YGdIjWzK8=U!eICB! zP?FA{7xi$Z@ROb$`1^_*IXr6<5im|2U;|O#4{xlB} z$Hxq#SUU+o1T<4^Zc~aMu?d}`rIj00AxwYs)@~cM-RM(1EVj-`>NR4LG2i<`*+G>f zlT~hJc&p)wtJDGNNJ@gCpx#Hr-6(z`L*Jit!HD=>VkzM72E~Fs6s7 z)FYAGCY;`q&R*NVW2%L|*{B;>{l0Boz?a|dgu&9@)gy#^#{RBYcC)bMgkqe1)Ffo1 z1EV+A50?}zf(1}VjtkjnIa(KOCT6@Ny0dcYs1b(AqPR6}l^0ySENwk}TVhc2P5^(5 z^Gd!nbbOVZf=gkcZV;u!7j(b)1bq03^h>~}l$eL4Bcv{*?KEld#9fG7cdYnjAVJKSrWfuwf=iW7+66 zws8!#Z<@9r&8x6r*OEs3VC(O zXLg8^xgA{bUVUV6X7j7H=x>ijoN?!GnX8f+-i2j^Cn3zp=hPO0AZJ(B(^l%%pia-CLitS!7-@#G5d2V1m z=%3M$BF0@J5$hc+)#nTBpo11>nx?p8r_rI5mVax9&vTO zwy;Y)=wG`$l9}153N8y@ULa$!+58>c&)&T6XxQsx#ctH;>Ozh-`gU4N$f#H6p&D#8RS4PA$)SPGJs`H@b@ua}n!Pj7#K+Rp44hR(dyjN{p;`YUVg%tf z0*-mobAx9C>|yI}61~Y!ZL%W$c9E}!HY?wh`$8V7?P=!ugm$gt`0uCQHk+f^W)CnZ zIi^ioT3SLRaL1i%)lk~HL3iA+N_51=k2`Z-YAVRNJyko$uxEy{Id1E7Qs!x-5;WM1_LT*~9 zu0Aog2fr*#kn4+(8O)*Elzj7&Zt}nztaAwaoyQrAtvnPwSQ05K5NFjua{rYqu#+U1 zqAmB#K#(1TQ{dyk+H^~8Tv79dTqeRr5lQ1{SLBqq(_39(?iVQz4^zn*2K+_(PJGkQ z*rFX&cLqt1_&nvs-PLqj$vrMe+#T;(g@fapJm~1``}Wl6^K}Li4)%)RU%u2_g5^|B zful>Hpqnqqu1;NMcz|ZhI@j6_qD4-HMkz!|&aa&VuQWKgUbE8hIrloDr0Z&XK|8|&Ex1wBX+OIK} z^5ng_6Vq@DO=wPGSz=LgaS#$rX98F9GXP$LI}`hX9>X1h>DQO-Kd+ScUX*z#kEyAM zyuR?^Zb#A;@ONAu2~i^!hX^g@uWMqa17I=l`CXT7E}65LtEs*TYk+|103|wrdcE|_ znhN@|Klz)2f!R_;-2G&h)5Q4ib249EHl1N;L$AZ7!k-{|NUh{33J$akBpsCp2Ufui zi^MImEMOv9e=sHbub87Y4gk{7d0sM#4k$b}+q{t!eBS=?!fcvqPiSp1Z~U{8p^&#R z1mSK8G9IqwQP4Dd)1#6zPmY7%a_wAhGwToS)+uSL=-bcRrqu81h=xCd-ItR~nD#VX zj+yXhR7d+HpH!{2aRj#%l`LpJIgW4 zBs@HQ%O&1iRr zr%922MTI(*LaN^2K$NAnm9FSRJjBlJh^wm{|2;=1`$Y@K2&*2Wd=Ynk>n${p2a1%l z)~WK)7k5l}M@gDy6Q*HEMLCF$xv&;^e7l_AvN9wAHB$`XiIDCM3lA8JayCK62jL;3Q`QM(nj@p z_-F^vaMv>{#Og>;ZfJPG^_89rH@V`&-&?~*_-WFg*jnt88TN-m!RP4!rSbKX)dw~^ zK`NGVnceoYC8+z$T?&sgXO)Mp1rg%(6HpOJ^|T}i510Ed1@_48r~TX;Pv0bArI&vZw~=+676Q@BuWAKzIrQ&T zu`M_;2IJxSTGZIea7XIUPvdFHvWDdOMbnn3Fl86R;|z18EAZU=tw+*Ut>m7O4*QJw z392vxX)ux$=-mKrv$yrSm3(#M?Np~F(44%#sZgq!5eS#3hA_fq@D-=9~Q$*fH zD~BI~V&L7HWnXB)nMF99+B%dB-&qOE-z49i8p*op262gaI}&yc=M;7NApPH~R8!Db z9G|8XwsQlg;(#2}LR7_ZBjxSvj9k6%-%ujHEZzHkcU0?&2K!LKE%dIY;+Z4dO|hjT z=-o>xx%uaB70u7@iExnws=>fLRD-=Ntb??l^SgQcfmCU^-A5b4``^o9HJ^P39g0$s zu1@vjq-<U+1`J5kzEud-40OD5_hP4#<-uGtOnw0n*q5 zYF53Cer%OTmcoI=<*NctRo8f%!uj0AR)OI;h*t7V9n$Ia+oKZO$SiZ0O1=_6ggLue z?BGAQqeKw-Jslt|Og@XanH$>tZ2wmiR3nYy{N<7N0QZ2O1NkkBK!ecbP4(chi&COg zhOx_Pc=?a-JC*N*WB8kIvCabw_AI2WVus-zz}0Wb<9!IXML^$;x*9$C@*-%VPiVodwHl z1p6}?#cLyZu0gvO&xEtls<RzCsKTmWU($NkR=Syh&Sx-!?owAZau6GAGJ}+|*6CWgt$O9hLydU;w_E&gJdWODLT0nk z>zdR)^j#>~nH=6vzHXOmOAWw)7W=a5nMI`pY6kJu206EKxj63Ys5oJoHPz_=No?ij zqH+m&V-a7p)tc;Kp{&M_G76S_=C^E>6m?X*g~B$T`5KYAct`gncdVuf+s7se16-b# z9CqTNsSkfr-+?|It@U_WE_0QX z#6B`hUVlF527Q;^ z6*Zob8Af8AJzm;BRcn{g7lb87N8QLz629PsQKSR%V6>lIv|f@`i2Uf>7Wbk@T()lM zJ&f2ZqDERfACGtBuBLm&Vkr0nm76qYNzN?3SjLI>b>p0bzl0mA=~C?`li?BOB4Y@Cju*G<>Cruv(;}i4|3^<`Sa_8 zdUmJE-mvN}H$byoajC~QTEl9$RnjfOkQU`%jrUH*WQrmGbQGS5Syb*Zuhrfh3x*bw zQ7gJ#(-MKoJ|zU9o4Ufw>#Z-=oaHOVMlP#9d9w<94kyQIW(UEu7lg)%b036ky*ywY znU}mOL9AG5gDpJf;0a~P040WNgKF+%O2WP!fWLL%rbB*x%8#mpn-WvzyaP8E{0$rjcaFw zQu~Bwa$A$s!{wp1ff5rk6A$F?w%kIK=f;nYTWM?kL`ne~*qe&5wR_@nAXP z`f5~s-RS$pYPnnJ!)|rik&q#Kp}Jl=z^Z`Du>K27QLW2lYZ~oar@|Hr{(!I@YzfyP z`B80+o$pxKU`;qR*=`LtoPNW6HB#maV0iZt55G?b3`I1OlGc&gq3@)i8&{U_$663*il-oUH`H0sm>CLWON}z zD0}GU4@<+V>TJf~`?POUqkl8&ZA}6b)GtFsg)Q~-l7X>{NL1WH8@d-MPFpKXc6j=c zR)iJzqI_M*Z%dB+;SIEKkw3G!4_28x`sEA#c6=1=$SmiC{e5=-o%e_O@Adt~>Icwq zIv}s(O4foHm1E}FtH65{9$Q}RnLEJLPMXGPSi}E-E^cS~?#P@1gNe{s>MJLE4;>t# zoH4ulo>uJ4rLE8H$#g(!(G~FKDmj%7$X6Ms{Yl4 zU9^L4zMI1#9&U!%A8if!n9b2}cd zn2j zE8Zjf2IvTnFRfr!Q{Lyp$Xv_Fgj+D&z9G@2Y>fX(3IA&VS0y#( zKSk`UqLT!}y=v{d<^9tW!|eREs6y9QKJ!O-_#T39Niw_03IrYScY1HJs=)b}O|3VZtuRQg&j3at1Rs9+NUf1n>UF;;piSVrr^a znp5B6#Oe$Wy6TuvmSH^`hwKbPfegB~RZz>> z65`a(Vac@<^JLGO?;L-3Tlqh;<2WPaPmi#s1Nm8KQ!x7fDq=k_>zl=^f(G2>*VlRpC{|z z`g@l#&f&LbkM0ml zO+*E zF~IO=W;DxJy7SwRgU%pAX!d7o*G|WY*Y0!X&8<+xhKhY=7AITJxVIR;p*#bu8T}JJ zHo`OvPqus!>Nw650v;L*1D;ept2s>q1Cq5{j~ydS8JB&|u$PW4bmjA zFvR87S1dxH)fqH)I_JFoqeFUz6L- z(&$QKUVDKYJaKAHF1cs*wrFEJS)b>-mPqeUF&SQj)b6Xga8*(Vrk& zPc0EeLu@XI8hfiF!c}4BEg1d<4&3u=1-v@&LU1Ff49`BYQGvKN!U$!&EON1gtm{3f zGJ|p5Z|qLvkucow4i+g~Ubk{L_`37?oz!qmc$uB!9zFWh5D}K*FbySvhTtcNmT5@0i@;o7O{8aNFX(po57XIcWj%qPZ$YJ4G%ZJuu{#I1%8 z@-=2AJo*o;1AgENQJc(mU;3BaQEu<3K(Guhx^Ll5J}u^^6(?y_s7qSHE21B%qfZ=f zN8>ae{h4a{w}3`%3hLl0mWGk9;xBvUqhBV@YKJ&iUa2{`(zt`{^qW?o0Pt{99|bUs z`F+{`Vv(^u#Ema{E+dRE%$ z(zGL4n}LPRf77(PYpbPfz8|3X=m*z?7f5;pq9%a?NEaUY{N3sHv%8=dGgrd7s~;m9 zsE2@VBgq||xl*;|!&z?gbJK0FAjcO|HGO@+SI^v(q0d_CZDtBf)-WS9>z3K#!6*hbn z(`HL5MnXRCB%92h7g+Gwah5J$igcuBppR*Zf;#aBjx&oq)%7o8ITt@xczH}X%1;e*T3tv+A~H;#R{VcIr!gvo3ocKXTFx9{10iGPC_ zh7M(wORlRCj%3h0sGg|bjy!eGJ#+w+H`I?Nb?0y~2>*B%imRLb!nbRn_dlWXl!0r-RY7(A!Ug4hjH z_B&*%x09vK=yi6|7^|^MRd6i+CWcn|Pvk)If?0j?6A$Cs3rGQk7WTg52)c*Ex7gw7 z?$K$$O7W7>64gCl10h~Z>wtTKA(B{_qC~4ks+enfQ&O&?r=q~_ntig&dySjR`|H|R z7&TsbXhq$Ms7L{1d)NobUwfHeN5wtOCClsGb6WP)f7#F$Q=rV_OGir}YIjbN(;miZ z=gg?{ZxR_sHp}I22y0RR{HHab-TKkxJrCNj+MZ7t9%eA^ZOJ@yG`fj+Ntn zn>stlFYKg#Q4kzKc7|ubQC{szZwT!%6%z7AF!k^E%#+$AqGCAqRa{;`PUmo2_jf^D1?V30 zeU^_F_rGvd-#o*b_ocNL;MH1;*JhfF&sMAy$l^(OylHu>7w@{#6795wKM&76q%?gf1f0shN_9!_1@6GS0 z^A9s3IqJl4wU}4PPHS>Ixt7?`*x*x6`Y~`QvCr(nJH9ZlkgTcqBd@M;_kma9ogDta zaAapOfqDyfiIytdlt(*8SxqD@?ymoQ#6_LoyZP&}QQw`uiXCKaLO;FO5@N|n$zvLK zOkZ`Vqe8>;PKZY|-1g&*JriVSQkb|6DVYMWo;utQ(H1UKhSK&2PDIbV7xX;3aiRY0 zF=X?c33pJmCEqp0<}xnqDeU0X*r?wACL<7a+aGYF{BMD?^uN`3qC(-;m;`CghTPno zR6y8IIP~bOEHuh{JASSYtG&{2;}rchFizXr-+}wyeo5}kZ~aO>R+%tom@_Y;<}O==p_(uLfnSs z-enIHt#veXoIoS@uDg0UT#qbRN|Zs5Q2?C5SX_uFk?D9W)5F7o*oZD_8xl0-je2rJ z`_Hd?4&JPN3uW=3Gg0~NRX72fEx|(1_v4EV3L(OkuEfjivVhNhkbJxDMo}B{UBK1I z4D>GwfGhN+^CCXqZ}UR75P8ZD$u*4h|L-S2{^Gqhi>V0^K>?tqjAqDVmF9gcC5=c6 z-ptfV3#YgkF%JajZ*o}9A+kte3b!ZUL2pEm{BTo!Cvuf@G+D0y{&X3)<@*bhHK6Ig zOK^G(O!#nVj`)}z5~QB2Cy=U$2y|wXqW)IELXYDHx)Du?H@f?w{#mj*ym>p2m!IfA z_P4vRu=PX=Dd^@v2wLRL(CjUGPW?^y*RBExG!|jIn58MmlT`JyL+!)g$%2BA)chGdJeo8@XEN+tQR)#R15kLx-40Q~_IZ`r+r67H`BLkMN4u&u}B z>P=7H-Ae-kfAW<|oso2Z5$?d+&lU5``KKy0>lm|fUw!rgX5NU?5}!Tz{1Su2uhonb z!0oZ#ws%y(5HzwggaXL9lBX=&luJITYIa+Jk1&7Wn(d-_YP>@(C^y}JFW{ztA+`9B zC-?y5`FxeyjmNPGF)Frnw~na0fp##PEZRpk!iC@!vA2UtJ%Z)F?9YtB@u2Pocj$8h zZ%fnc0Y?+cq$^3d;|)`8PnEHx(S^Q>009fnTO)?Ap`<%slQ)T5xsuyFU-R{|&aXX= z$F4YLPq2NSTQ{G5rV^H60@Mrsd5y$=>7BI)o|Cl^&8XhU>wk9cC2;S3QWNo`h9O{E zXAjv0ZU0)e1GBHqvyqRHLQbq$HZXjy`L97~jM*e+4QRXSI{k{U+EhepdeP+H8dNf6 z&*QbhK$RVWy~2{W1q_5?yyFDofsfq>r)D|9_g8`g)_@4n3_HI#KSa^J0hVan^^=zl zW<23j`!vuWQ9uMLC3v5)4Ik7sG$fyztgrNWAN$Bq91;Q!q?vtpwHe*3yhTR=@R`v| zlQxK7;q{C~rqeY-?!xS+?uBKKQjbB0CS8$#&@2={P98B7{qGxIP4Xcs_mB7Z-VpVy zm|sFu*;f2m$Ze{=)7^yadI32x$R$m<6n(F74NOaooL%8`=!_Sa)e=%|!kAHgFIdc| zv>{RBtz_G~Dnp%I115CJ_v*4^IFKr4iTCevX4|@ay>g+as%u-xo|)%4i#d*6;UgF; z!$+i!zFIEpD*HZp`S@O6&9`Z@2rn~fFA{hwj?JB&c;Cd$9X=v8wf}lLewE(?XpE3G z`>Zkl&R^mPYi^T{qQ`G-<$Vn*4KFW^@hp5kg~75m{L>=@bx%7r0*3bPmcV+2z zsjg(Nb_o2ZiZy4)=i}WmxV%iiDw5-Xr>94eIcL+2YFQU*km1}<*q=%*e7>mbVn7CU zo_eBZ{Nb?d#h?+!+^S{4dhh$# zyd{+E0|qm*3a+S-_8Xug{Mg{1liZtYmteJ4$%EoC;ig#~(?1zz>*iyNDetqct!AJX zt34(5!&C_rK!$Hd2X*qInvo}z549@nj_zI zi$_@PZPOi%C|fm}to17S5twcW^o5n^(_v%$s8!II@nllhw_<7D5_-__cF=mojchv+ zG;W`1=nN$;zvG)F%YzX}zspd(`0>Feumr;Yy{ zLpv0J?6oZy3V>JnnL-!o5SQ`Soojb>OJnxk#U0ibt8}hZAMPwM9#Q>T=ynu}XW~wr z*H*QN%K9lVOOsCJJ7C@XPDBL5`SC;ay(w~wyUW|P%+?(K^O1TV3(E+ODvmVq@yi)q z4h%^K&876RIcxqeY*pEU*YJS-eJg-o(hjB?`!4k051MtHoLQvro98ERf6qtj3mm76G`J?Y*FppmG4E|n5>e6d{kAGPGBpwGtk}b^LL6Z$v%K3v0 zKmd6p?mBj;*2;;ifUgc&WEH%Vez{R%B7D zl#{w+q*Bbb1$?!Z*ThUf(2hbo;Zuua!)8(@lI_twfsqn|rX{X}g5Tb*V^Wp3ZYztt zA{R|@6ZtYf4Y+N+eF?6Xwd*yuN)^%^#D?$Qtkwjbve1txKl{ElL;(!O>a1%Y3uYXM z9^#;kL@O;C9iN_xg|#FHy76AK(hT&zFbfep*m{sB{2FJqV0KMFyHku~oyXKfmHA=SEMZ`nZcvs-#q{?l!|}+PO&W3&7S0vwM*)ngyL1{)&-JfDRF&G~n`Da= zHtOK@cz9EG3v_yOR)G!Upn!)yCK*0~c6vJrARbIoY7#s9+8F z7LM=QazxwV(VheD%mXzFSgB8vH$CR_X!p9YtsW&nhZ29~TUhfb@&=f^o?ciqQhw>Y zd$#XWyCK_%Oj^XI%?EO0IoZ%CBZ6jObq$3yaucZt@7|vK539&Ax~r@gxPF%@g?du|I{LXx`d&C;b8l0 z9>+eiBcJ$;sJp{iH+hmBHj(TqedH3otXnCsT{x6`$u-Tn2adL_k|sh;TFG(aCB)GR zR@_Q_+HF|njaL^f@Sm2xoQSJBXKO&)OCR#wKjM)Tbp2ANHvIrD-pAUS?ebo+#b<}C zEqz6&$g?RJf(*3@8x^y5mCoWF!$1CfVNDoKZ}m^$nu}f5#C2Z{5N5dtP&B1EMAqes z5^3i2$g9)J!okZ)EPC!w?q+4h8#Qz<_3{cWfy8y=I!uW#rLk}~L{LAo_)egE?tZ0B z`>D7DwmT|Oyz;4KZeEnvd^*RmOy!8rP?iD8;SVL zm%hoS*0guONMCKs_-(OOaYT_>a@&zw1Ix)W`%h%2138_-KAk)pwHT_K(^8`@)wN&ieo!cqXTddk_s1z$ryjtg^4Ndlr0?w0R8KQvI# zS^D=B|Li&BOkPr$AZg6Xt@cTNdv#Gqb5C7t`D$rot~tXTP)}JrIE6JmTO@@PcJ(r~ zor#OnyX>OLLFb8Oe*NdD-q=HAEtu=lc~{At{G*QCL>@^`c1`!TUa|=vyrB)Tv&UQz zbhIi|FXU~@F(Yd{E^GO%eW>7kne)8xGS^$xy5qjS4XFgA#(X%xqi4n$7M34|qZc&a zRofjTTS_q9Z807kPy~S9(v5^9G=kE~@OMyKdw;n(0_q*=51`PflQsWwyoRyIH z&nWW!>KeGps-vSD{oC}e+KYrC*VI%r(y;j;&=%9?-u{6P56g6NuXvtUCFsdQZ<25> zmoyY(^!upL*92zeiR|dI?wAS_l~8pku;iQm_5D*X5||mM{YDOWiZz6pPWb*+hQh^n zesi`oH{Ie%r8#0O0xE6?tz$UIhg0OCEn&?u!qkxeq)1d?6Kzn?!#hCJt2wc+^~6fF zUO;xw!%~mooX(2@C|?Jq$jnc4u}>rFi%(bxanh7cQLw)n{XLDf%!e$S=1Rxc#u zX(YS`7(Pd;UT~QDVt(k~0p*dP#L0~jl-VT=IV$T>C5oG9c%Y@+_O=KwrRZ8!L$5xV zTML29Upnb*2JyDe2v6Q(e&ytBm^`BJ%Ti{$jm$x18|(#*XYUt=jhu8Tkk$|QC0Pwv zvbUO&4OH#aS-#&=l&QG00@9pW0y@Bnu@pcSn-Q@rh&g=Fd z@@?&ErMsOQz~HriLHe*^3P73UHp+lF|5=)15+C%4(@f#T)6u4@53f)Fei^gjtzX@( zA^De(P-lb{0R^4v5|n5Yt=P3Jj#DtJaS;m&e`v4JIvvIFhsG9Du^*f-(V7$LV|zOL zMO_F4jCNUgP@!|<$q$8qy^KC-uT`to~L5_&g1$Q%mN1EWlPp!#wq__70&GbWA@a{7jqB zhabyqKP>G#v0*ZcA6%dRArsZSP?Gd*-h1cZ4|*zWi&MCixRvY|!cL$7jGZx(+5A5n z&E6#Jq3*m5YQ0K7)CUY+p#Wqe9xFlWr|TcB2MOBn871@M5*bFdIC#E!Ld=`r$uaW% z$UvXK4dYpbKd0zxFpS)Ls+-LGp_)2`0;mBUbv<>3-dnZ&?nnPrgjIy~{`&f{O6}oX z$X_(F0^zY|L;>8_FWqkJ@3j7^RGduXMHN4qTfP*p#0_+4l50p?89$HWeCz?A)}T(b z8$aW?t7E7kjfIQsF4CdMo8T1h+z0VuA475A#W@9UBrvCq74o`bo;pHp0OHE?OugIVfHbT(e$B6JM(6W&= zKsVBxS~xo-{ttSw{ortGuWeu4K;P~}I?{o?Rjgj9frceyJWuPdWdIibENs-^(gu?^ zhC5AixVF68>Y3q70q8_LGF#Nt%v@$)&;Yl4B0I@5hsj&uJVnJ0Sz8O?l^L1z)tvC1 z&XsmAXsT+Bgwe>I`eZo^%V*>pRfCB!tkDlHu8SWXXoB((qPt3VgblYW{pFOMrrN;d z85wyFswj?tKN?j3T#F*)6!N#JE*W20Nl|_xvwP~)($c$vS}T{$a5evR^0l`|9(_PN z*h`{giBlEWz0xY=6<6nTxb8tfI`=B7;QCMlx)>UsMEIEMvFe#OL~JFGJojprT(^EIS+1y3xcpiQ3zK#$ z&`>I`gIPsRDOLMiM-PMr?Ma$FznbQ7{S)LjioD6ge3+&z5nSy>(g_oCulmm1S60R+ z0Q@|$Fxva+Dp|5ZvIca%K?D=MPktRM55?)ckk0DXf32QqlQm;zrXo-m!5OZ2dDjbC zs+7HXD7om8g23~a&el78d{QJ(a^26|Dqr0!q_3ZC9xY8SB~N`Jw(SPmch)neqrUQX zj0~o|9Mr9w&H^qruvQ>z*~Llm$D!`q?5IG=GBoWiNjih0oYYAEeuj5}K91W8cR)Fa zBC9#cuwiXsg$~yN7f1a1PF6wx^dF#>8sGxPT5Bdo`lpHCz<%4X zY)7-Un0A_*)r&q$Fl{;bPJK@UBsaZ|@ok>^hl+QOjrp66xb^np7gj|NKKdC~4UE^W z7lfr8uVa`9p7@g{d_cy@2>pl@EI#RF;*b*p^sWZ$`0_;F^!!yX1#o9|9zB1uCc1Mz ze0NVOJ$;TjaEb5MO?uO6v&eeIr7mj-2^)eNXn>RY*F;I`1L7tIb!`pP4M9|it?$^U zTu|k=uTouWT3iB#mWIBz*lx{7Wj~+$>4t`SUz6_nq1GL1_*RT$=!=DCh7n_*y5^@} z>P{=1Ra-5y&@o<^NPG8}JS_g&e@lDhK_!js{wda(2^gAcg;?(!GYiy2Z|ZF1E#mzW zYDSUyz?+M0rp#w+jUaU8h)a2P_RS>t#vMzYLx14FG5t^*VRrzDHOiO!R2Ba1oL2ZWrab(x3m8ivYD z>-Gz%nO9)CkzHE8`NI|psI63TF)>=b$kpsHqyT=uSBjSV*2|16P=95=advHGcq8c4 zsbTV`OFr0nBPc_uR#2EurL0F=p7DxSC#XAk1*gwFlCDCEd4B4T64>LVW_+b{v#^>< zg(GhRXfIcB^186mdFnWrc1I0ZdByUu^IHU~ycCGMPu`tv|4wSct$+vEQ@YHZhou=- zMy>BvrK%e%4K{J9URq88L#!wOnJrxI{ee#7=`Ml#$d7x0AstnLpWIp^R^=)c<>Yy3 z=@<9(kIZ;WL#?(cfONUiit25>?Pcqc*A7j5XKr3MZ|B_Fu0A?{pG{c%wO++C8fsok zUM1(vv@dN%+fU(I&qp>Etn-XwgfFMWWNJKxi(ogrAR6YZCk#}8pWT>^jIf|8fw!d3N{<}9F9YQ zddgc8!H0afzLbcr^k1tQ(`DgYEoMTs&z>?Q4>eyIkY}!}rgKAn?Fb;!55kORgE3)M zp<8BixkCS?!&gQ`ORfm@m`)3n!P;!gN$$x+ldoT{>-+d!pL^P;{w@0PjiiC{YSZf_ z0z<&yY6?IWijOAGyi(o_Lvcxm+Rg~;R~Q{dt8kf}xYUK;RRCKeJ;Y22X9PPa1(4%S zFqNhN(g}gB<8CIvC=ny_3%c*B4WKLT=RvlZ@-Y6cr$34HMhNKe-M?DHQnTaQN&3R( zObHqDlLgW%4Q|LU`6J*bO6aXa0mR4?sND6*L5{>k-b4W{|Fu(785;eEwKbqTMAOJ= z%1%?B*MyD7ukUg1zkOfIe){RP92NRT!QYgh$S)?ON48n&r1wlG?cE_^o)rx<-OQQy zAdkPzQyou!DC@F@>|OfL;>U$~i8)eD0dwO^OFWII4gN{$1loJkrqhQlOO2pJm>bhv zp1P;pwiy$~biG7Gz4h>7aF^z`Fu)v5o=$ z3xg6)D@e+P+4KsUeyq$(vn}n8Me$PoIf7H!wPqE&7Ev!C$MBT)nbOc;mjE^g!gLHD zYO!u9T_Hp}^>^CaIIO8O7CwV)Um9i4420w08TDOH)*@qdh`j&RPBF{G_MITF*7iS$ z=Sfz4_OliXr;JC3jXEQU5F$L?7f$Ny0)24ssf6peDK=j}PqIorx`D?&AZe~3Z1 z&u|iEo#x|(J2Jlk{61fNrE=Q*bfc0AasnK#>Q=(YpuO+Ygu1d z-8KE7i}c09S;9uuVJ>2%sR0TgeF|;hSFJ19SiWf&tM8YkrhOsF2?-l4>jWMD+h9@v zJd4ifB`ES{$ftce3Lu@cxt-=iOmo5%m3xe0h5+2d@t-Gji~`7RytE_dd?EiDmrZZi z3o0DYxq0@Y!s3A+&BMw1YB6YiSd@E>g#4q&(Fe>xXOlOyKwfm?_sTn<9uabb*SW&( zY~64E6I`44ka0zSzgE#detsp)iOf1Z@|dNx+zK8E^(4SF8AO-=)b!o&^VQ} z%#v#%Kb}2VN|j%)qyRGEX-sCTFS??1^K(h>C?ak*pv~M%EnIdg5Ib9-> zO9`?Fn5dX@mPC3ZqyKAy&~NFQ_u?;-6tw)E(%uT?kIw8MD?{Jz`h>nDc@CmRJlq{? zXX^=6gH8O^h1Z{YOVd9ppb8N3f=-SENcQ1+Iz2nCUi7DNup~sDFbGVk^U>Nx5;TM{W!{`7qN36K9!TAJ{1+<@8o^!B&$4^u#6@;Ff|HH>fHcTEUUpusuom)dmRUQj=o{)$UHT=(4Z zW0S54&`~+BG5y>lLOSzAZ%5d@{vx0=VxxS0guN6&W0YP+$6VC)8iJz$h>=jsnj#WE z-z*7?>8^nH9o0+)8t4vm&ZJR;{-&rly?|tq+>CWj;N@0B+|?^zqYTr;B%Y@^E=Cp( znvhF=W6k$Z67^`Snem9%o1qMk?EX01Og8C4PzmT)AUkC#fcyYN{KWtTkOK=NV9 z^-`1ED+Zk(6}X+sIJv8-&8|aFYoU3UhP(IY z5eKJW30JYwWZw%NMFCn4t28Z`=-A&mF0vls+CRTJKAZGmh$)L5AB7TO{$71z05#Gq z=KaBV{MB-l76)(Ae`JQzX=`d2V1ZzvY>zgY*k4?b+rgR`udwS zpbM6fMaMW?3h%~^rsqiqmM?WH(;u7Xhss|SGZV(y7}c}h2$2~TDMkjQKZ+&XW{-vh z`bq367=Ypk=ef^pW+iEx7|da@X0}7m9T|>X8Cv+#q_GeMtRHPJ4}#fg<3aG-bQM<% z=U@#Lwuk6-PtpK}(q;i;L9$(_6An92(PRSlP=fSPLhVa6=3Q@bo`oLr%=K zU8zz*v>c-jP0!nh7dK9rBihQ>Ls@H~6UrIqtfi%msHWMZ4zjV}Z@=s4zAmml_ZVx) z20W`P>366Lf&v)443Wk-1QhhXO6~1S{i4lpCs~<_5suyl{KmrcaS5dUM`0rW;F1Q+ zfK~q>LUVVL8Pl39|p8lwFlMpKtfl1LwrwVoh-njObI zqoNS6KB@{^C!_gOc7{tC^oA#CSYObHJT&e|t|g=2T)N%2f?J#S;$@%Tt*f}Q*MK-& z@9=`Ql0=7E`N$iXbcI};TQ+ve^O`2AN%VIW%6L-T-tNMnN8n%{y++1T!lBpD-um58*@Fi=Tp3&(#Nj}2|8B4+i84zX5*B# zzD#odmgpd6n6_(}0vMFtY$42LIK(RkX*${N$#DPW5?|r}IKWZ<2a40MAj5MOdn?M_ zq_U13Ui+qd9^neAh8Av0hhZ5D(CnPiAwG;-SR3)j}EN8_utJ8 z>KN`*ZTa;o-jhd?jO}_xJKfIvBN@TB9x7icvphPS9}mF7bKVi3I%)GF-ca*&YU zmu+?z8``s0%Y3y2id&KxRvQ5C5unPspCwcPtoh;ArkQe2wKA)78>845!Tz zw0>9V<-`eC+*zvn$HP2xkLuZ1zX!PPrKk^8o4@gVGlcv~0SxPLZzVjlAgQgdYjY-( zCbfYYY?4=+|!x@mH~`!HDsd==Q2NCEVmHO{ix z$B4Z4O}VNSU5q7K4QW*;UebaZtGF~MSkV7K_wEo1A~ofslbZ`JID^@ z0SX{*(OxNRvP)F?X;Q%q1)$Y|&tXH>-a=n_SI*LQU8rl5Q2G}VB0lEeQ{~!eH|deS zt#j3!_N8tGgYNS3jSKK#0b7*EJo+)=PVwT$Ip#3|o-YlJ&fMOqYLFpGdXi?E(ma~y zOZ)jsG1uwKCMpD%VZ_S@jw_RG6QRE#L_YE+7?*Q?hU+YJa41s^QcNqZ!@oc~uoJ+>u{0UTIF3O-RZ-PhYlcY4U#io5sit zSjgf&(6NI=yJU5w5N6U^%!Ni2sO;=o33Rn9q&#(xG_2w5rF~GBr7^N`O6Rdz4gHg( zOAi0CAA)DL6`^f-8+ARxK`Wu>nw6HfdvWHEOem6kjTAajsO*|;i;??3JWELOaL7h* z7zS?j*F#%q(ID`z04U3@qh`&hpdRk^q;&O@Wxid^PD)?u%61;C=Z zUqg;-+VCv;w0g`9t$|G-jAFOiO;JtbZ+Qjf`;%#{vfFUDujFB(;2JR4hJ?aV08gFsiSbGngwEUwZIpk2zdj;3PD@Y6o?hWy{*p27 zLm0oSEvB-4#1luyG$0Mn2K0W(Wj1F!i4v{0Y^^U@Ogo{Y3(H>Tu*Jl7ZBe!7R$oRC zMXIZt06F z<>gpI6_5%4b!R9>YJMAg500WK8K! zs7=EcuV-ag`KGJGWPA22Wsr)+j#|?YRLXx-n_05L?vtOTsIE|Lev*{EM(&^<0wS8W z!f;IK%Hm~{q6+)B371jr$kVAQ+fv!P9>vVbA8R)i`yxR{;Gu*8_NU!=y@U~rj7?vV z-?N9mHq*s(g!Z|TA3$zj^ZD=ZX*Jm92s)i-R9ibq?r`wJq4TSGB67BrZ*!6BDn~vlTXFkB4*~#Zw;IH0iGV!{6TMkN3+>0E?9wz_Avx=HFwoJ5tzYn*7{}2kTf@Wl@(x?jySRT+5K0!Fh>Ewi|_VD1~zJdXL zu2HuH0~UMU5FKMNNiA<(f@f$3diL<7&E59M^K-OD`GTtON;m1<{`L9k3t=1X8Vn*yct=&3GHm(nkZ4wk_#R9DVa zXbdr8HK)=dM)g^5rFG$@nvL6P0~W4|NdKWXPq%D{nRy|~lPuLEj=a_s=uY^T&>4nj z$cdob0_X019dPkv`TZ$zru>TAI-T44jX#xcn{a78QQQG4Sc%j8nq%<&1|~aL~OLw9w$EZJ1qr~ST4Pl{x;+fi*g}6hn$s~%3Qy!td90;H#J5+ zNdmq0Z$Cvx$) zADx=0m`!X#)lwSc>Gn5~k%23)O$H-mt$f#@ygRq`O#9hpP+P2xlL_7EqKcX>k|lvt zO+P4ax&jMhCvlUATl6xdJ{xi=@x`0iIJVPATt<%!lhj=aPkBym?`e2s?<3nM$X#U= zz^Epou5WDBF-OY>hY)2HR@8x5FzS|Z&P_gsSY{{AJVYN-033Uw#jf|Z)5Cv8LF!^B z4W6VY%kXk}Sbh0C6U6U&-x`7<4Gm3MnosJev58Tul%&L7*#%=mNv4YER_lJe2g z?^hAmuJpO4d+YoOoC)6+gDz75s8Hi3xI9c4^*u-+9l;r=!q5chLS)R7r2JXd)YSmpI(RKk{)XEvA#`F zOpIYjO48BAk?9!$-me5Fw~S8_O|w|&pj>vmwmUAGP|h@#$D z(@}f6)RDsEAR+pv>3>EaplA6tm!rRu=sUGM3~;!uliqnj0=ye0n0DY0iP_ z{QhKIf2DrBj+w@YFE6<)B6p_RB$!6^n?C`NH0hkGhpHWc>zG`>`#yFh(ldO{B`1~= zDRJ~l4kK2Vf@*J1v2dkJx7BXa*trRzXmkWPhmy<{dF1=dHK*ofXS}je@p4Fh3?yU# zYr%v|z$Ik+c5zu^s~C7fn=wrq*XL|05Gk7=z6G19k6lyVi@f`FO#?XDE7MDJbvr3#qJ_co zL?Or)gYgwr+RLKVd8-HZ3q76UZKmoFQ#1Ek=I2)_Tx`G^$REGz#MOD@RzN?!)Fr2w*| zl9#tFbL0Lr{iUU=ym{+dxByEn%jpS&%knX+0q97XbT%7T-HoFi${%(MtI9ml`!e^8 z5N63W1OYA^tK9s(1Plqs$8IyOZVvqrsNpv88VL(x=eo&rvJx0vf$V%iE+DdQWrl6> zv}383jn{Ts88caP8+fXWbnV4ojOI58X#N2c1h8fH+PJh zf+3l1jh}m5T77yVpMQG50~9-YsYm0#tMnHAjRJV4%-5H9tgSGBtLUuQ3gUN&(o8RT z;Vo}CO#z6Raj?G$a{>nMmytdg5y4r<;Gsj==W820$lZfmE4tPG&r@ZASFRNCEtV@( z3LrcGk|)X4laU`TA5VJlST*y;l=$s%_kEAH>k=^t4?iu72mKKoyYwaUkvGCJe4#V; z=c6CO0;K0Qy(UCz)R6D_W7#=|t@Z(TX>2i*YlFgSz2}08LEspXCqah1et#B*Y9FUv zgx4yx(t9BbgMaewBEsX^BOknXUbSTkFkR5iYrC&Y*Y0H+HF9s4iQRfy!im9^v2qms zSe2wGWmtBaoxqJOq5#AcDoRLmY7K)d{DOpX1*~I&5?FRTU1P3QZu;pN)Z#4qD=EIopmO!rHVD+3+1gFVImD!VpLX`(n*jLa>GUsEh~I5PEhsa^h2RE zEZiQdbk$+3A*cAR3Vp=w01CiNSEs)nga~x&$v28paT^b=g=QeI!kBs+t%0m%0iVyi zQkuN+AOOs;e-Lcf{G`L65BQb>Apg|UC=azHMG^1_3lDO2l`FGSu5Y=m%RO1h&o^=( zG;a#FU66J|Ua(0_@uCgX@#ONJFXu!b&H;m>wxsdXW~3Z%@GQWq+KPqueh55ghkz=70{;c~&!?!C#B5x6cbBZ-vO#7)||r@}E$=w)}YQ zpJ2`DQcw&bDlNEclXk>xOC_2%XnsAmDB*D1Ia~}(v!gpwn>9uFEA`l}It4Q(Q;pOHmc|+susV!uu zIR%i@JI6_GA=j!R0?E8@46Xo?VGJ8(B3_ zg6P~{wRC1)iZFeX^<2_l*?}PqAtuQg4?4e`9UE41Wiy{{udlPzPWN7NO_CG;eY|U# zR#+xhyObQ>2#OyMXwoP+2&%SzQW3#H)kFs?}E(Zu=kqNgUT4VmLbNMIR zRl!YflStkROEV{()5(=jeK!Rlk~ky%Ml75+G~|CAo%=r%{ujrWuAfRuO(J)Sxux84 z&Ha8ib6=6mEJV4?Wh>><7eynlGV&pEH> z6OkiGrw-Ti2&GA_$qvCo@7kwiYQNWwUg}BaSD=f1Nyn_ARA}2KDh#tZ8w7{7r&8}D zBX2=Utoe5zv2Y3Z0Hr)%ttU%nztKgi)QB8#{qQM3!n53uEXR>um&P~g(PQ?mN`};8 z)&AuafNcQn8F}3Z7PM;a++r?YM|VlQ<@Y$h%Y?ha69WEq3ZTX>gtU+D(BrT)zyJac z!)gi9P1!CehK{$qZM*evNZq`UFG)VX9Z6OdAZ);PF`>?5N=`90x=$gWm6A=Ju!*XQ zuF(u%QR_O&-z1kCvow^o`@Wjk3l=Q=KAWkU;0U~X1>N3F|1q{*k6~sbYNdCJDfaw5 z``b$+ai5sJ%Cc!y3{*#CJ*k?gyGQ8{h(0TA&Yl9w(_h!(!6N|w_~hQS&fo;v?G-L( zhEJnovq99BbHsLU7;rDIy)-a=kIzl$)+s>XEV@0J{)3{GoP>WWZZ!Crs~_@b2y{Ve z=s$juTM1gckj=3d;M0*k;}53*`I93zsvc^EKV*x);-f#)tjSpJ;@&)xql*P1RZaRe zjhVnH?hU-Ms?VbGP!gdSMAwf&z4Bqp!u}`^z7+qf?6I6D@zZ6_G$<|q84XvYZfH_O z`(hV(zkTffjCE!pO-_E`5?JufyhuAJ2@$w3Ko&PKb03F^@>ylig*Ux^8m^9hAoZpC zV$ljv1L>>b_hoH}>?tCcIC%E{WFqUGocmvz^mFR~_fI^WuyHtfdR76Pm^66`peC~2 zeyCBl7H0FpyVRg}Q@13{>wPWL#JxfjRylUex+?7lba0$LNpGs>C}UPN4gQ%QbJv%( zy!@(0u?z3*ZTH|sY_6)}DL_Idj@Hf6f5Ji?qhxbc)GPLihjh5JeX-26ycd|y_1`i$ z&hrD_ah~@06!2KXo0d@EaeX!Zj9>A>yq2_BU`-9~@6%vbt8j)*QJOZDF!@MJHK|VU497!orB;i4B5Q#U)4LdV zU5}bYtoFaOVD?aU>l=XpYvd|^+Elb}J05NFdU<=*W-KP+ zuJ$8TdL2gj`gGUPO^|kKtt9Jkfh$_%!2KIb^ePb;^6V5KA489~yneF^`{O^4Q$TV6 z9(w5%Flgg3C0QhyrPRD56dO|_zVI3TA5UxSToAEY`I75z$6vlYaA4?Xn%+t_=(ukf z#umOLoM3QHA7uCnTXOA3xQA}(oJBVfLLZ_xkI`wQ0h7KQ7d4@VQVv^tkg!_`&>?m} z`>=8PhCO(R*^h_5xI;Q2*ose9IC~0$z?@65vRXFx-4w^;txo}>=2=cyPl{+X<_`TQ zf~iBOAB3nnmtbD@KIgN}#veSC8~GHezBZYgUd8ZsPp#1_`FvZh!AJfA>6YAM?v_WC zyY6zuhTz}Lw{uYV+$@gJPew;_MmL9EOiF`qa99-#VtEuT;a-R78X!L2!a@c#J!hzoQVTJbJjg6Oq!Ps{uL5X>w!Xzp_}X zy1R1S^UkDo0|SS0P60BmRQU-ymKN|#c_n?X_i6Zozi?eiqMX&vL0H13Ht6U+rI_K< zVNB1aG<-c@KBByn?&7H*JdJnNTaI4*MeM6T6!w?LkWsX59IX%0N+_Y_QY5LspW8}b z5kfHyCHvw&oITEO42<5|a^x%#fd;MU7#eVeS#_}4X)e@m;0WJoo6?$Weh&fI7Y;sW ztbb9{`8OJ;w(mMhAM#{Lm+UmnD}M`(et;D^G_jSkFi8c`@`s4&52k62+7!oZH{}yN zouU-uRUgaF`&9RtZ4Qh1X2LLbO}k(9YJY;GOX{N+1G z)gkn^tJf+sq#GD&L*_KPNWIvXY8#O2igtkLHb)!?2mr9kEj$JjrZ9EDa^4FgDXV{P zF5pVm`^na~%h8tx+CO{0@-FR0vA$~11+ri{u5HrMWsRP@kcH;S>}#c8gg(t(?mc^! zGxh~Io`Y+>yILMIAbs+XS$5%v!xOE$H*5Giw+@Pe#5~$WhdBZlF(H3Rw2C_ImQntD z3|5L7NA_2qTHAdrDjjbrQ2%xGHK(Nbw=+3i=l0)89rL4+s$`*PCv4LG&?A_N1NCmZ zMO!M2L_NQG6s$eL|3pO4YkU>+rj+q z`}GoR2ERod^S0RWZntFq<|3UhCX9C!VGx4I#0e=ZH z5HlD;Bq;`NZ^|5h3!@COB_a*XBxT9J;nO7mo}Z<=ct@ROBB6rqXlVdx|COxtwelh6 zoT8Q>Vf|IqNum3k73PEp3c#D;(zuPQN0cha%>07~%%+uTKIbwMp52HkQ!n_%3S{tW zp!o2P0;d4^Vp@Lb#^}1Z|CoVyaa&II)1*gCH9t5v?z$td+rC#-2Q^_fiTx%6;@XSb z1nXw*+%(U8w!aT`TIAzL=pGoOZnGyomJqTkxhtFYjO3{}kXcig;L(^c3xj*TCFR&m zf6-8TBGSU7>RL|(B2>v2N&YFcrE(iuv8HG-^4Z$;sO{lIWnFH|{YifJxug_Phj;Y~ zpTKDhDAL0t^WsWC28x$UXLTdk;L5ComG=I3?Wn%sTbJ*~?&EDDS5`>xb8{7>18n_{I$uauko0tyOL$WiV6g^pPZ6HXdmuodkJ4j1E|f2ZY%UV^C5uP^8Qbx{}IhQmW2xJ+f7()f-U% z%r^tMWIayjgZC%=^s$7JnjL5XeM4omkD3e5zJlR2KBwv)gFDc%`@Sh2+M-o!=+@vq zo~$Db(#^wGe80|3hs{&^`f$c-c&d%{wX*!MaAmavJX9CXmRekfeY zxAoR2ct^={;!m0dVR(b_B31tS`IpZFFd2_S^Ct`a*T&jGhoS?}jG#!e4^@@4Y=%Tf zz=K<0SZA4-XEFTzW{sVfTP_^7vo>LNuss9!jYjAZxu4zSIMhL9V&JP>rssw0WmsS6 z;55)xJdbI)T6DC7U4QdFLA-tvBcOZVS}LdV-8p@dU-@f+{DT_q?1ehQ#Q z9~G}C+9Ie!qIZ!`$9BFP75I7{VtL#LOnfX#qpQa|krmqBRqIe|<6WO4QG8 zbNk+|?8C#W;3F;aaj5LCQ^1FidR|wQxsE9cC&;Zo-!1u5MILiwg4EnxJ?^!cku(++ zQ#Ob^!1M-+uJZX4qt=#t|R%n;k=T9YwzWpy@PSM)hS%Jpmq`bv%!DdjCtZo{e zU&Cz{uPJayN$+AHz+e=)DEn239(NMWPIY4D6kzgcR-ZFb=jAVLn*-@RZuRjCg&!(G zA1Nylmf27@lLzcfr8c?F_+Oepz$rk+pT`)4-@lF6cP=j1SqG)vG~lQB0qI8TFrr*cs(f(@(CJH^syJVF@aKmO(|k4T-I)WhrLXw$y?5=PKa#Y5 z)&A&cRPX%l-UYoVtt0gTuN+vom7<-NP(s5pxAENoVvvj|y3Lt_)rq49QzCoYP5taV zA5Kq09WK1=D+hL(QF#6{|85|>T3-n|_Sn#=q0E=7IE*Ys2nFU_CnxL{c=j`C?^zIu zz-KDXu|)7aF0auUhLEUmgJ9eJk7?Tf3@RFqHG0?YY54##8^Xl-1{+DEw3h9-px>Otjq6`izIZ zcF(0IC|GQ0P>7HZTYj#M4Vj`y-bWwK%bfy*uZuQn?-+u&n=6*J&oxg}o-Yr0RM`B1 zUEF>f&3uu6P8aVeunY_7CacQyD-GN-BBjO#$Ea-IzM9x22sKRQ>bbwlj*+ByPS`9k z3z#}8z-Q)6N?xAZ{b(*c0mI3<3f>q^$DE^(@M&pc`E*=uGb|!S(6Exi@c4{hP#ruB*&)rI0)*h(tQ-0*Z67LyEOztzjS#Z^?s^vwkRQSOGLa<9 zW@5(c6KCxvo7Og9T`xWcI(7KwVQzuX*`Uc5C#=@kPK8oTVK&>}@QRwRDEp*NMVy6& zzU5;11%}gKIm1NhU8xi9O^ns0^>I*#(^)AXR(}yI{*}TOlLuR{qQt%}yX4*4R(j$) zx?p3V^d}CIY-Fx?_2aLccW>~Hil>0bSzlmmRdz^M^$pqTydhw+aBRW1w5jVYY)o~$ zO!?mfcK%&&l$5GwsAliubVrAZYhjQ(YVLMIFLffhvVlI}g&R?0eRQQ`P z|IsVkC$V2}Njmj0R+BSQE+U`pB5sHvzHtikCBTe37t8g#DXt7?KMT|vc5jS6;3?jj zX31CKC_m2M)zzNP_n*gjl70Z*G3(x&+~jr(^ipr9N20B|mw2Ae87~fLCfxx&rGm3t ztO2yeQWJ7y_TI6(^v;9T$={MPENY%x64yoN9|2Q7^VEW6N>yDNx6p0;l!FC=T9|`g zbacke=#(>UTCNgyRs3eue-T3*HCD=?9MLh@nDesHMycnhPFq#>k@($6 z*Ly}~8nbp1Yz2pZFY1OF@HotOx}ou1vQIZ}x8x*Gb5&d2+~3PJXEl)oRt2sA8bNd+ zqu?7&y!-4Jp@xaK@F*{~N8&ZTEJ8|AUc4hLp>g=VMXc($dEXDKNCWh_DfU)WQ2>2o)#kL59Y zESglAxD4YAX}PdbKiM?M8OY*Zq0WsTr+_}tD*RaU;Ud}LLMUqd97iL0VQP~Gyhnmi6$&@RVGHDCBzEpU#p{*Ja z0^U&ZrH@m#-z~Rf>-4&(-7HG$8a}hzEN%1+QUM=F%NT>`bov)caRyH48@-qQy*YPy zs$wg1KclarwK6B>llfVPd_Ar!kb^Vwn7va#PcuteYb|M--a#@5hK=rIyy`{tFdHyw z&%ITAu!U}YMDcRaAuyb$LS}DCzVfc<^rVf01@)$dE)l~pz*B&rWIJe&c+bR{mi5^l z@pdHB?n>_$_eqZwDXXkQ%ChdJ{6}!iPnbsk!n0<|*x*R0?E+^*9yyIwyU@Bs(dX&7 zz}jcLjU(=Err?2sOtQ9-%*6ubhvlUmOTTVyIUIORV=hp(Kd5A3f6{U-nndsChdv*c zDmlc3_8%s6haE0rKd7$A`PrQ)jP>L^kDYh5Gt&OAf=gE-?iTTm)`j=2PkosGI$?*i z4Ti21ueDo^hm3d&cqHny$WTRuCh^p38=M%E!? zj%(fs>Wkt!?VinJ&{YFaN>48>&Ur;ra+GbfZjBT-A+*Q}PK>>c{Q)z1igE)66{ph& zT$uCfMg(oRimco-qIAqUTi#8*{1xwiHZ26agi8;~F02}(_mb-$eH(;~Y2MYW*%6GM zi%Ky9Y;-kYc?Um-w$~bbHig#0nAcX2B>^kkXw3j zD)F6zL+1zhi@T9Z(5>UL$c^8IU~-Ndy|!^`k3|XrbdiNs%z}n|&c3`v|H>^*j4HHi zG0)z$#8KooI8gD@$HM#HOC4r7OLio`xb9VQ&n4S%;x1h2Y(zF;er>@Aqy1=qLefa= zi=%bNUw;Q5UM{gItkw`Myx6gO4P4ou6&H0CeDr}#?AILge(_qKT`goaVqh{qGi7mz zKRZQ(t`(GWA-&9ARd$c~@H+xgI>VmxGj-utsnZ>OPr;`lPijV*a=tBMGgVjk_Iacb zeX|4()ijNcXWWf1nv?i`ZjRiIhhQ$0Q5d$^SkSR*pSUxL>jJUY(Oh=dZY^Fie?$33 zf!LJ@Rrllz>6l(WhhVc-`Y3K#dO6mJbx6OqsqHHUh_HgiW2C0fJBoEzPM7g8UOLSZ z8Xb+sm39go#1@<;_YMBGZ0PULe?+~s?_A9eMD6wUw!20n=Mgspmu}pvScIp7ZUFVrr38F7hvL`EO zcFVd-ZKUXt3Yx5laS|%nqdZYKXKfT7e*b0sZru2V&mLOt7eFVv^rSQmhs&V?I;ngx zKaM)XOJFaUmQ0?hUA#a$hzf7(~75;hlAS(pSz3|$O zr{4f>5FMFn@<6;b*V1*>B8dVdhUGD3PmfeX-k$;nEjI#C*&Hi$jPiwnbN*=uapkR; zfzdhr3OqRAIhKFrKpo9h4Ot*k<7OOklV|Bb_{+HBw&ZAUP7Yms`XkVeG3aqj%XZLR zup+fGCiq_7ROGmhvB}z2?R6OLiMs;VSMzkt3US~BH4x;L^-3<$Nw)IHusM9IS9ML~ zbwc<@Er(`~>#CXAyHNkMX%J@h@S-NkQ3Z&1Od{{}pYZg|6t=};ZxN0hFQ&3x3YDJ+d}-MO ziru<7Rs^*e^_KrajDBhH%&}O%59CLV8Rc3bEop^d(!X=h zrlWA0s1cawFe%GMEivC}%1<%v{UrsNlB5w}rvUk{&qzCjT*aO(7sSXeb>*D5w#Ll!Oe!Ok;r zoas+g!+PoXV0b(Of*Mpu6jK@)Y5|3a==GU+09~raOv~~`IJ?Rzpn;>U8bU-O zDA|=oQ#bSgnfK9P-iPV4F9UHtU6Dl~8ii_H+cYjnl}VnQO7-834t&S)1?cFQmC(M~ z0{nMSv9yO<)dHkS2t|+PeQ5S{p znfje?WjpoCyOwDb^oCJG;;|a_6RDor+@*0M^|zrD3e#2n0G$QC72&c)K-Ht$zfwud zGhzx_X{nVqzpijCiZC@^8A@X{Qsa*TFzms*L~`6pzlz0pJx|{N8#n7lGajEZ?rL=p z^dv?wr_e)`=boubTs^vdjb1ZahZClG8&Oi<-7kDp?q_wGD^uPA!F*=<)|Lc9kGnB&CJYcif|5 zNHJM4$=G6BfsQ<9JBYW}hm1Pe>)&~kZ}wlY0KTarz#hD(lcp-C^p%#CzivW$kZTo?`n26LG3%xpoxVX{^4T#`kZx7#$A7fpu9~!kkf!mkum+BUf~xQ0 zHt%m@qJGc3%U~I3D1^0x{@Fc2$X6ZYc}RZ_q?XTR7%N_PC?rUAvF;D)<0!d2@?P+y z`6GCrEq}RGsfFxdF?f5tIbz2sO2>Wl2Ky)JaG;1IlSxD|Ee)uL0=4#fuE^JWOL;V_ z!_r>rKaagJ@qufE*;=VY{arf9}n=h_<`+QsV59D<8$eAo|sp048tpvdP3ZYdBQ(f+Q;QF)_7E8|^ z$&7olT2mfWF+V}dldkM`>TOBT->hnb4tJ>6ek^JHGgDFAvTzbYPF@uSBOyKF>m%RK zs`U}4ro-3kO3tMefsOU=a}bE<8pFbJYpel8QB|d# z0tPanr0m$QmDWAR)!xAB#7k>_x29tG_s=;$y)l>Fe7+JC-g*l7m`^Fu58(_ zE4=ro44vEg_@shib(9f;;OKc!~Ds3gpsQG>AzH{pQw><)K z^1G1_WGpv5;fIV_#n9u6BeF~)!W+CPmLi{zuic5`@RPn@1UhscAW~y}7&C`tRmts?;YXG+oSM|46}Ea!W+%V|LclppvHixj?N!;4Sl!=+c7Fr-{sUz`3$k7`=u z!qe(T$@>L=I0OAz1jKc&w{<{X9mIBofT_2ac}Q#2fFx;?9=nn^lZEI|JIh!dzVK>a zLjMg%fyv*S1g$wIY(wh?NnFxmR8m#=wLx0+p!tl}EngkM_g{>(A{QA?0-O{coT;{4 zp~CwWc1XB{&kZ9^4hH>fP%G^ZF&dZ9ABsH^>i@e}yyb+Iq{`(~R^r6{h{}j}`hhYI zkFDHah?m(yEra62od9ATTj z5m>~2d$YUQgxP}7ZIs%MvF6*%1SE}j=W85%DoO{5LpOYp!x&@iAEUzF|jC9Fci7!ZNYcu^s}fpD4RWz~70OKwO(lvk?o zhF|;k;>xwdG6ZSBnwG1T$*jN@%c=g%`~CU&?1r_YyT(S&}vn4=|qpkDaZO~%WD3z$qK;*?8L;i zJ|u@~`!r@xf=W#zwA(h-((|aE z`3%qE?6njDlglf^>bP$S01>yd>#~#;d*x)Cr`^R}8rl;Uw616+-zu(}O;9dHw=0|i za_xjjNRUb|ZFQ0KhaK(RkC?6t@;giGCmpmTwlce~;tM$8=8!m+% zygdqDRn1;n6oH1b7&l=A=&|iz>D%;zxS<7akym9fIXTGc7p#JU06-tj+-N}&=mrM! z(3fJ{ylIW3&?^Hq{b`x*u+I}J{Fh|uL)YIu_j0)tsn1ny1$!m5lB)V3tB;{O+BxE3 zfCvpn@ki{rrC6#LSd}r2gf-v;*vsr_W;Cr576p$S7wXU33Fe}%sT#Efx&t>sncfg5 z-Mlj;a6|BIT0QN2F#^+{>0fcPuqe$vjmf=KP1Fc)3FLvtc5+W+7ENe`YRTnn@>_Cp zcBQA6VCKYfqPk$XE_vvjU!f(?YUf9R6Sf^n$m~wvy8=~MZA|`}Dg3-P5wr_d&&wXV zXSFSdZz|tvx5Uu_w~ zsv(W2K>?)2DWI=-c%MZWktZ{ix$kn+`j?f*rXcI>!|TMlWdFjTMt&d22*Z2CI2on) z(rmRuP)g0#SNH8@_1A*`0Bhc~o|v1|3l1RhV2Us47gapG-y+NWme_&%=Y+2)|4mdH zi3oIpQX_aiDH!KtH-at{CE_w{-QHR4-;S;oS$_A*yzFe4qUjhgc!#{1wAMJ8Hn|(! zCTm^fd>6?o6(+M9j#s%))jWrOYAufXR|tK;pOT%YZL z@s1`dr+~gjdNMtKyp{M&`V>%jzbVOaVr%Qo;)b}5(_wQLKuB#NED{*pNME{5GEO1m zcvn?I>ovnDj9X2gW`+e``ACOFmXPz_Ekpu?bIEC>5N?lQI|E9Avr&4Y2LX4HvFF3n zUl@gV^eaLQD$IEJy8ngL9_2ts*yJK6U$5NpB?u-K`-o}jYHa&JVo`$z40~GI05k(> zN;Qcmdy3gOyhTWhS)_dGN{lEP>`LL?QHPBIopL`tS|?KDh*^`wY^#Hv;3CHv=7+~- zFFAAtm>!mPA807+DyKGKW<$DZy#=&WK;JH{Z}6F?cOYgJ%J&>>2GPdaCf1j1G~umx zvgu1?@tc$u9S(d``*@{Gl+X|FzB^~!pC{)=`dSruHdP*C7XD!pV+tAiG_S3C5vvvR zDK^EHS)vKTtF3m;emKWiBQSLX#a|w9s2^#EE8Om98~8m&@i9!+tMTToT0I z1yj4}M;$}W(nwwEh3HDd516$^!@rk0#YXLpJgz@bjK}uErzQ6A(5JA!Y$$5{wVBsW zp(^6{Ez;hW_;v&+Wk^6g373n>+=GstOiRFMg_w ziJ8nr1$cU2ErX83pO#*|1&A+9`NWkH;$zu-=@hW~x;5t6O0GPN`ctnMTDbN^|AKqc zQaX3c9rbG+2e9!)Z22ibI3(pWqEUMc^++0%U#jCFA%0ge-2MHR_lFntFI(kaxiJL{ zm6bN)4z|HbpECl6g|tYp+;sb(6+^?ZG@NteavD=ORwnJ=^DeAa58jkKFu_9TI0l6BBs>9`W@<0K!?S zSvxT9{R2J^*yD-pfcSa)0DcOS8gT!acVJW z+l0^bq8YmAs6GXM3doSo<}r-W6LUs9PauTc=(LQ8&kZeU^Raqa%XQN%6K~@(lHd4o z7c7)$I;Xa*qVFCw1q|Ul1w3|u9bHtL^fNei&m>rmI)0n2tr`Acvv{FRS?9(c-e!uv z$g|v4O0!tL;&8uztgRBeR+;bpTIXB5F8A{fM!I6<-Ki&ce5}rYVR+FR2iKG%vpL|L zCubczW#>KLUbfOIkn;)x3ip4^+a%y#fdBfPOr`E|p265T3O09xMSmAd1jV0Xm~0OSegmH8JP+ zx9U5GgWkn6O)~+9o(kM;+dx<2R5z;K{R8|}O5oQ8S+1s3II5zxlsVp;zCC`8cf`{W zq<17qg6EaKs(798$Ahk4n%qn*#+@C0#&seC%lMbTEim5NG2x?kxsWmODnKIKxbvEN zn-7ElrKU4{C+OjMtIfG7FDmxgSK6MpUGpHgvL?zqmP1nfkCi-pAW;aD{v5AUz^7NW z6WG>{(u$R&{r{ZZT|ZsO-AKd1OtU90o&Uz;Uf~0oXf{dVq3Uy*s3P*MufqanUZMlD zN7VRwANndr(zL$NAbs6nD522`9q`6J=rY)5R6;4zRY`sPBRC$BIr-oF zYJ2RxOcmaSZx_}yhNlXpAc?lL%2A?Cb=3*a+N|-0-3Rz_uY>%34zJt4U&0T9_wmrn zr+`6w1QIH{o*9s?RPT2$i_B>-a*iYQyYtyZJ5ciH@%)l(uN~rvT8< zP-Y=1wUng0H2Hc7{(3|wbc6rsgD{U5NFlz3Rc{N8(^DECGb>uuhfQ{^8OB|0P0Xtu ze;ChFJqrv(rG*gKs!NnV%9aZCTWF?9wZtdXKDa@L4vr-1?`@#l9q0v=Z7l6K7J+l1 zwtdU(O|{*eWXlXebFeSfU%PbgO=4%MtyMZ^gGFKwle7Zq%R-0%JsF zB$P0$V6t`14N;mBP2scYc^^*!BA>6BjG)`oP63|~6aXc185wsW|0Vf%dB0ERs=$MS zkJlw{Y@WTnVQvxvCPS&Qxj9GV;xa@BK{d;BKY)Ut^AB_LGSey!m+F>RtrPTH?w4i9 zY(>3Zz1npOFg61w-4RTE(wd{s^S&!sRG75qRPL=+=Mx&N#B2C0LMFoI`=OOa?>E)D zpb+J#A`q36C;{WiaCA=XHha{8B1LD3koTtECUS-&&t%~``X6kWp>fKmfE9&(gIsZ> zCrxw^n$liEO)FHuotIch%$*0ka|(PEt}F+{Lp>3>kRig|LvRNU4;c&r8NS% zj_g-%T-{BoikB}f?koVV*@O3G(@1tZqBF2|`>N!kG|$2g*!kA)8f+0M@qg9_Cfar( zSvo5N7j_03b5o}vV$G!N@V3or^wzEN@0XAt%YVHyKE$ldzZzID91WQ-U$rWKJ|Vrj zX!l_;{t@rjavT$KM4xHmSOyP$vWD~>ZJltwJ7TzNC;sWBoyq-gqxKK2^ba}SYW!AL zSj2uf1w8V~Po(zefGJEvK4dP7soJlp>V$Lgno%WeZCo7Y1o~zLy$SP}YC>vELfu0N zYzsy8x`g^XgL}h#W9;6yBYirTH{G|@ac>0|u_32`Plmbu{wjJ^t<>eSdnwbg25&F& zvl^=cp6W68oaxiwm&RMtS3*l^?RtAG64n{e4X1_Q+eks(WhmGxzqy$ng?HR|=9QYM zK79iO7cX~US$^S+2uzQOoiVMM^G;4#Q(Tr zK{Oo0H;rPxG&J)ujs}1ILSmm;V`GY{H23VBu==RTv1WnC&2Nh(Lp?ZE7%qofd!)6i zNHJ7{;0uD*V1Gz#iD%fLA(+v!lkdBD^HYErEo_Y8d%NG3B@@dpYb%=+^j9I|uNW(Q zmh`^cR1tVJ=NQNd0fy$Tun;P4JKCMxTlkNIvA*zd%S%Vl97xE4y8L`w^0znytW5EX z!6oelC)`O&A&n=^WsGZ2E^2Ar=!zb(6gJyiEWKA>I$zTPXa3Rs>inEy5L>nL1Rc{==pW5puU%sOvR z*QM45Jl)?rzIp9rXb9-bfPXJ{Sm(B+o?}gdxe4`?=Mj>{17_?MpjyRxz#qI>E;Tmu z-71y#&UAdmLgH(=SV`hF^8%l|Yc&tIdbrzXov=>(^>)g}!CBo&>nyMNQslj&ZULN? zX%tRWqK{>ZbuC)>Ma3`mh?@Td@|JhcTA3UeUgS8y#)2p)MI?Ncm<%b_rM@mw)-by{ z#BxsTuBNF{v`Njkrt`rk?3f=YNt}dVZePX4AzaoeAj`J3B9DD}T>4k}XwP?R_Dn4m z>AO#qInptssFhnXlf$awOH|8d`P$S2J+H}L^EWzyt-P#q?-RL^eY1;LetP+cpQ;kl z1(7?j;22;cY7pRD)H{x`e==9B8%e2EKC`JkjoCX((dOjxU!d%N(c>OORHXC7{7zjc zG1BhLoLby1-9Xov$h&KUn^Lzgk?tplKLL_2ms|^uReH-qaw?Z2ARR~Y5;nbGR7@IV z7+SFCp7(!#Kjz|3vlA&uziNi#izMpeP(M#HH1&X>WNTc=9aBPu&ZlUti1{Q@eFWH*#9~NK9T^V{z)Cu^{?7y^T7QixQ8M8-wg07F@9cQu=$t)|#mHGN;|U?$Gi}gO z+)#5w>58Y@lgT>k!sNA?WQB-qF%Q+(4OKGb)xFTV+7M4dM+o@fPU}d>)LjKq-LPgO zcV;I$F#72ouCxj@;`B$b%(&-^y~N2V<;Q|r&W$q?-zXR7G9k@Vz>txEg5IL&$1Z+N z@T?5m-%IH7YTq00%|@Q^-O4QnI+-CNuH-7uB228`JZbiH+v^eM)V-1X=OSjj-g5K1 zO~f>2)kF?vZoB?+BS^$mx1cd{JZRGT>+Pqxe_9Tmu-Pb8*S2ASII2P4?$=gIdAy^8 zn%C7N`{}+PoXKjqs3RRQ@HWzIY_mR_+hnBhSMcgDs3jm&_LA}5N&fgd$_^U$_&4xqi>t{At8h%m5S7AD0rJFRn4@zN)w@3xXERPT z&D6_^Q}L&CTgJsb(K4@_6P7RYp<%uh;>d9%Ms=x?V7t8mfuO7o{rfCO%g0^t6d-i| zx_Lcm34nRBj7DI&ORDwMfoX)+{mrdeU}zc*Oe-uvI>}<> z5-JDAjK15toUxg|(OGuU6#Qm6==u`SjbWQ>Hd%=9TXm5QicJ;3Xo(c=&?xyb7|o^(Phw`Bq7-_C zUL0Hgc!>*#5O1k>el`^FR6~jj@|)`d=$O-JQd|jNWDgtTUzoR6$`hWSOVILHJ_E)$ zVTYhYEGiqQR0fj5^6ABnm87T%U&BPDGv8-7ZGJ(2;Ng>7`8TcbHg4qH<&+irvNt(4 zg&fRd#q~%hk@J^hS$|DgrjE%6dENR1mKkD12QcV%?mw#AHcArrNE+K0bBzz-PUmf3 z4%Opu(hxQ?3ir_4yo7iBjG-MZBD|mDYQw)oa7JIJhDbNwS`j!~TM|Sm+WTAt>QEIE znB;#$%Vc$&HOKj-K6rxe(u@-349%S;w-F%iAX)&Wc}?}Ps=VaC|73C!U7%#(dcrB7 zCD){>@p`$(weMC>t#b-vr!i~Z1KuEeq$9%#M$AsVUZ7z4Cq~s=vf3r)M1%co>2s1v z&`0n}0OLhdG?kl3KjUzzFvKEvg-hg4DAlKfN^?e%tleh>9l{yayJG7}V5 zVn5(gT*RPO*y}jcTcb-6js2~1vwomst!yce|gZ2shiq3c^5+Nrjt?0t-UMauX?8v1~Jd_2mIIk$Ct037pjoE8!EV1zc#*wZqJ}M(Fale z!6VD30Fjp2joufv(pS`UKVK*Osh*Df(81wP1O|71!pR9z#PMl~A!Eh%27ahPd2WzT z!iPgXaeDu?1tn%VJd}%JhIDAmLP`5!Wf8qgk(LiSJ3E}Hg3yt zm&E`vDQK^7M;eYfycb~--qV!uydh}c>7vm6rb2%_)COTfabgjmy>S1lIcK`2o$WSj$or9?UsV2F zmvp*V&{#~6Q>hMpk%tVWmm3MtZ?5p&px%oN5Q73rkY@tVVex+~-m!3X+k>e@I*EmJ zgR(=0B!-*<3TNwPx<0@1@s?@tQjE}D7JLBY*V0nAqsb~dVTTDj1cHI|nA@b}N85aC zh4A*6G_>_n@E`sXRIeGj3P?%I3h6CfZ-A!PYTZx$j618nIG1V7BvRfdd+V7kcLlmi znbUU6nLa#%6WbPQwAGxl`g5+lX4k49vSv57uDGMOk<$t5MW3f*MsW{b!*lP!ho!5a z72oa!l&bOXmT;lR|ELHqV#Vk+6hUHgT&c!NOKlm#bLY-plmttebC|HR7TM10Y1g)i zGrCP~fOVD@HZ!??_V%hxj58lXL!H@yXD79ZK=Loup4*IS=6|5f3)S%7aiNZeKp~4 zuSL;CN4Nxmwkg5Mt*haS%dL4&D=j0O^}Xxm!$L0M9RtWLj~0H?VhKI3^0X%T#@*^# zg6fKpm

    qTI$UFT#8Endy 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 ao3D+Q- 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 z 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%0D3h2WUnUl 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%0A84wpr;03d$sGvD5*asymk?`Ig&AyeOzMN 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 BUnT$m diff --git a/unpublishedScripts/marketplace/gameTable/assets/cards/Leather0100_2_L.jpg b/unpublishedScripts/marketplace/gameTable/assets/cards/Leather0100_2_L.jpg deleted file mode 100644 index bf7efd26a3a7f5c94160d828bb0306ea21c8b687..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1456875 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�E7;u)igcKAVOi2OA@n2tWx$w*K`#U~PJDEdXvcf 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 z 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%0Dg3`Gu{;Z9Jh3oKg@7 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
    M}fy4(ygQ{>4q@Czbib)bD-bD^+oky5+gOc z3ac{qWgSI2`+XT~3937WSw#o1{%ihOT0*v-W3rrELwe6V_N)N(KWK_~P&#)xRHzUF zAXulIb=K6hof|>X(A%}GnTR={XDa2ZwL5ep>B-7Q@2xC0JFv^m6{Yf&DI>F8Ez^@G z^kCo(>)>_=z?=kCdy^U2V&_4CSkl>pO5sVBPuXT#@dMhFIT>9-|e)b!YzOH)tYNI~SuL*q`qKKRTU67Ht3cuvnZzR+X3 z@@Q@WFD)mVn~t(6*i`IDsTBDfgo!kMe;Y{Lyu(J(FK?nj6;>B&267ciLTV~|C>1Ac zBYL(*LZ0R6TloQR?yM3!xFxqy9m8(X3K}w~*z#fUpVw@8b0=9ZA_=kzInUX4b<()VYzYjbSqGi)Vq}ID#vls zn`Kpe`JhIX^cy6z(BD(jm6uzJfWsBu(~qXXE}ufI%TAK>mk1f9i~kp5>$FP8OdohHuZz84|4;=mJ5 zBz34AXmYW;IBxQY!m~AYJNN7UeQ?MN_%>BDOW)iJdr)dE;95|fn4eJ7={P|3TLEF4 zSbk+{F-P@T!bum^MY^G1j3917m4@M~UGPF&?T?4mU(MPj&zkjS)BLqtfgPL|Cy@e` zj5Ce8(u8AN&vF5zdj4U><=-XASRQ|(cb#kf96$poid^0a#^zEE2`V4+RKr7 zimM(o(YQavDtm*yap9&^%jQ^?&MPN~M{?~~laVAZZN6I!oQB|e1$+19MeR%KdepKt zo|$JfO7gG~RHo5W#f53_^Tt!!{F+Y2gT(G-e>2(M%`jrN?x)pbLICB(2IaiASz!THq>Ep1;@Z3rKbxiiz(}3g+TPq-sIP| zGBqE^lNm_Ju*)CJJI!6L#;uzpk+kxp+rG9`(zZW4Hrv3oXfO0eHLhMq6^07WUKJNfAG1gltu7UhD-b zGGD?^lPO4o0}1kuioRs?b+?!5&SsT)IttPa#h*Mm;=zUG-Vrg zJJZJ_4(~KbQU>?UHaax(d3#JhRuc01(>j`hy%bZeKmhn0c(0Q1bz66*-uaKp&uyZq zZ>TVp<3UCtzi5u4lp?q!Y*_9K$h`N=Hg>j}JQmiZv6TbL$8sG9#Gg9;7|K?Yxnp`H z=@cAgJM#x~wJYE7!CbxJC6)bRG=qo~_<^r}9XfX=qb0--B-0_YyS36_h$;1Flt?Ic zD@}*vgz})kf=eU(&(Uu+FD_}`Z!9FYiPq7XPUB+qkalX4a=f=6iouWQK6^+!wH|_y z2_*HyNbm7dF;b|bu&rChxhWnXBFM*d%kxu0x$-`pVQ3hlYj){;(7#yAowglM8gK88 zO0qkcN7PxikETDC{{Sd;jZ$CVT53qpvQ~mAgo-1n@c(j~aH zmSuyA6TtN$jSky^N^E-5EXgVa20Y${ZE2@R`DLikhSV=7iMX)%)S{O=b_ai+45Ny@ z{{W$YJHBeurnb{WfFD_c~4J zjBN1#0#hy&+(%&`f_8QI1I=+}13)r~FDiKY<{hs?M8~EV>9$kLL5T-$A=ZpJoOlvnp zTuSzW+!j(njC?f%`KUVA5_dP>vJ76#x<;Sp?JMj00Hxek<(xPKa6`Bi-;q0ydgQox zM-x~aCX42OFzS~&jm@Q<>#&9>)l{B`YIGhQG7$UQ1p%|_U(5>&FD+>)abXf0o4W|7 zd0f?9VxVS2)|J?ICZyrZ=;X#bzDE5ed4GM&){}%y#wBuSE3qTMj{g9LF*bcicEJ$( z1I<*`xXI7Y+~ z@u{J&Vx$5*dS!s{xcN6Uh5WPgsIc>cd9w60?BSzRBLx7Rt4{!?xg2AMiJYQqb_e;B ze?8}#ZwxaTQaOzsNuvtVqPu`bR{_v&JJdvTPW-W~OXMv})wEQYO9vOP#5l@6C2t>p zqb=e*lUzy9mSXbuuX%k2uM?~{l}H$F@VgM9fjvN>sQ1RKQ2bWxPRxVMx+TYu^g-pR z_?V=US6R?+Do95kkfKy%W_Z2<19k_cSsyHUSZeaA7q={KEoMSPs0GunY7xum7-V>3 zT~mIf`fn&|9(3~59(lOANgmz)tg(e0q$Yrx`0gw2D}r$0qu#NW{GLIn^IQJ_sMNbg zDa0MaQ>Ok0!@dyMnMWh~O2jb{mPY_rkz!9?i?>_=aRdr|1OEUx`=aUE)!wi*-}jYt z=Ou~wR6Q5s@dBov`8bQlZLVi<^N%t4lIKP+`FqQ9R`W`{aTMqWHluP=PXW2g#Oz;i zWy8A(y4Ef=*rghjtn6emOF>R-%y}`VL)&iIEzJa&*C)Jry3RcV^7a)l`nr|dZ$Vlo z!^O7fO3?M+9KTn=gaN*94D$4`#Em4JnpGbMO4W|T@2|@TAk2z=P33qsFD!X(_slws zfK4fu-QbgIbNGkNn|cwpN>Sk+{%v<<;F{I;jdiF?rimNsv!W7p?166pIR^5H}pdRBT0(bW=Lu`A?%V{T`R@=^TqS?#pD=X{^ z)U8P!!36El(+(<^KYAoc8z&!{deX@jqoc)eyaceUXedFA2N=SS8XC4hjxK!CIlNxR z#w{z-gISG%l%t~#hA-M6_~8YdNvdbs&XsGgURw_?&_rTGG2%xKJFz3!{=5L2J^Tl!KI7QGL1^yH0kd-h`h3TOE} zr;Rtvk$I}()|M_3G!lvmT!skTyiZ@AOCG?3&!@pMljnV7PMh?;qEuBPoiN7^^M^pcxYpOrOCKw1(6T2FvKfU+FMo!#@W}9p8t-QrD74moR?%AI$LecxN@C_{ z%NPg<;ya$8*J>PP$RLYiep%Bs4>HQuwhGTIL8g>)pbC3|^zVYVWyzT7mReD`(=@ms znJ<~%E2al;Re@9s;-jWc?4a3tu9xMD>kBz0^DV&=Sx@_gLO;4t8HGmtYfZaj7*$_$ z6Wqo;&8*tbp#Fu1Gb7o}CBw;DkQnTg8-fY`~>&IC+6Du9r>I*V40$rxuTTf~rUdYW!6=lhs6D z|I_&+a~yM`N2Er^c?x>0I)Z6HzTd|xBK~gV8XLW<^Pf_&(|n=jzbecO)5{!p)6G&( zE)+5QaW$?Cvbj<*$D8JxjUHA>H9ayC_Hiougl-nJ1M$doY`RBze@-*rL@p4B=+YXR zbK}@o?)S+;qe@Q9tIqy-K6Sf}+8DsKwzfzhiA5GNKGzZ3aZ~fb^-9>rH1q!eE2L)f z6}<(auHEBMeyDCtH+HEY`@vPmM!5 z4T~t=pXBz!Mg57V)|Wbr9!vbIwY;%XvO2O{so&H-qPbf2sjXD_0pW)sq9f!QqAb2k z^Iw*(KSe?2;1AU9E*&By)rBd@e1$*{oL)BL@uOLrQ{ zr%4+^LD`95C{X_ZFxr?ha@(pTEU+s}wGqP=+-l+y0l`MR%Cz35B*1Xb&CuoVcK~_8TVt)~mK9wfbyp3&r`GKxj-f9yROI8a#&t5SD zRORjgKSo&rax(8n)3P`7uT#?X4=`ET=<+LD!l7a+ejYC&j(hjszlKPgDG{{K%hN5K-=sCU35!xu!IN-aGsU?N5!(!V zn9QY~Yy7^|Jf%Ov!bmP*NA;M1ykrHy8~j^;5s-+YQWL*EYQpcxx=xzx1;i7yQI$K$ zy9TND?d^vlvOUO%3|q?ZX}Z0=+_5W}yA1o7_3Je#52d8%*GwkzsQ12n3&{_~0ZBixa;AqUc!espUOs%udR zPr_}jhY&Ulv4N2%a#4&>Vp>RW5u*o$ypZu5P?1o6c!0>JQ^zgU%vMslS^6rD)jJya zQ*4!65&&nub@NR6ji#IADi{>QMwHw>DQmWfP9eKRGlRv@bQ?Y4WNCS`^{Xp#XwD zd*vK7v6ZmfOSXbOaj@gaF;9`EPAsa~8{f8&;mQ8?GReng3d3T3$lnR)Olsc8`8lWB z>0?y$?xf}f(_%kXcUq{Gl#JPc+}_{APv7!p1-2~1b59dm z<6AQWaL+X_%bDzatEYLc+!r?i%agw0s0Q7}^}{j2AUv3fZhVr>Q(Zn^^89{OvvALN z>2oVC+)F7Oeq*Ky0kH03MVn%pC6dfJ$i{$$QBI`vA1@p%?UPyE#RABa&`%KfJBI8J z^u(4mZ=gRfbCPBl zM=PYB-xYU0{qhz_zb4w*lITv51&~&bgca-dl+^wBZ)PPey5lw6aYX0RWgQz}PW$`h zPUVn&1E~3$T@&)cZ7w6}-QC{uMf)Wp3qaKGQQr<)H4@bw&AwyO?k*lF(*Zj-Y{~q=r)kGd)9-X?0G>wl?jk#JZpYko-w!4+k?Nh;>9@X4*0jA-SGQwr zG?C7Mm*A09j$b~QU?|b={Rk&ym-9a2C-aT{-Gh&-Nc_Tr08qfyN3h65@A&AL@>R69 zl2wMQTABzK?GV5Xhkm&u%}R5u`DW`-)va{BIyN!9HrzI+4^>9h?Y{p2J@5_A=n`Z$ zdeZq`>-1aBqnlD_&=pAcAfNJ%=BL1%nU>oyt|y;# z20-&|h@`JfhG`}-y%9kb<**DY2LAvvjDaCb>M>#ZPx8=ecS?e55jw`;Y(sJ983($t z=Ctj|p4lwK>{`0MJkz3&PQOSmMPm+XNAmBw{W%*KO|@Z>+Ub{?g5F<(XsTKGK8$0x92NjqhQY00*;q4u8c zls&RCxXa?OHx(Z`dFt0*xU{p;(E>pnB8gD4A52sc;#$12CRR!FK#t2ak0Q>AsaD;>dkR>|MoNM}u;_o3BtS-y^a5z6{Rk!#p<@@?5lonyR9Toy|_e z`eJ2dv52N!<;(3`L(?y0w}+OQe7{$@Vx+Fr*mx0L{+woLQRRCkKo_KbMMylpx4vh* zWRA!|JT&dQrmzLc5pG(xWo5^p?&2CsEA}`rp`qzD{Kr*0q zqvXqklEggSpxxP|I?E6AE6CO3a6S?epnV|q_sE|?qtBZ-?8bdV7(&D{I8ov#D?ojQ zF{@_DZCV>^SwykJEV3B5RBsqkNh7z#(|nZTf^9tWHI#NQrVVZPaxN@9>)OtY|VhN=->~Y|;VX&QueLWtB3VvRGhSeO$D&Ide z3VBBxbdZC;UxV7YJcp3{a&fq~c-D{RMe?SFd8Aq+E!ESI(gX1DT2WYgP|}_`8JpR* zn17UXcy)KVj_r(V91>iyLGe*Xzib43jHS0WVfk;VU&SrPm1D{6W*nuADWO=KkR+2q zN_E2+ac$j2u4iYUerxGA6WvDMT9PKWiY1NNK%wVS)jg<3Q`ZE&M*PtkY{xWjDhpj( zOKnlT8yPK@SsH{@Xh|PV>Q32cuuXDFm500Ed9o>ObuAyuihiuNYb!{bP{yHGv=(^r`-D%Xwqw3AJmfM3CLg@&LcIj(0WsclXSEV1hyN zXC1;>=FiL7e6b~|vAT-pJJeAVA>W%S)DcrdR{ee%Ga59aY{T;1TSD_H`D@dS;=P9)>?c^dqDZLWLl^1nNIw(s*3TYoZM5E@P~MuL=80)PShqjU7- zFpm%3TOujwzJ2o6j=FZ5-b(%JHLPMl1zDU>w4`_BO#t@8IMF@LP?0>|dF9)u7Oy0V z$I{pke#?_n$bA_zD;E}I+JB(PH9cQV7X%m;(K*m@C7*iy`{c3FkdKlHp!R?H$yvCz zo8+r?lZReIlF;UK;^l}NSgk zfk%_VEQJ!DsX%A~{Ht2zczPafp_zGqQhgo^n<6<|UczCOPl+2J5kph9PEoPKvlq=T zw~}2J?ApOGBo9nHW|dMm0QMb@LJ}u(q%!>%%vz)0MZ$7j7%HlswPD-1$>SiC@L?$J zZ>K!VrrK%0my%puB{h57myKd6K#vJv;COYfUi)Cm40uF&b^BOA+u;lAyOg*VcaF-4 zym$gY9H>vH(Dxo7WI?b1_x+6OjlEv)Lxb}F07p@6 zgL9T+h>yu;Y*0=0-D1Y~UDdqoSDRfIOFipJDmga&JP7fwP(3juAo8cn+WTBjeKp3S z%MxsQ1Ic)jT$h z)8T8Ih~bS%cOgIChr^1m# zBB3TXC;+eFUB3J>JY*r?<8U?i(rU9!b>n{R2^XFZhTj zwRlM>QUi(;PZBovt$N`*b7Lzj{IRRN7dJ8aeR$qofoTy04Vcic$%fU*{aX%ye(VS( z?4Oh#U%c|Sm?qRb!h+7;<{5odZGtSdUY$YTZ)}_g2qVAye?t;MeCPRqYvviQZ3q^Q z?vQlYDWKb}cHiG9hcAceY>g+{KbGD`wY8Mj>k7re6!gOURV4oaH)E3FqMPn}#QOf5 z`E}(Ri9D!8mvcFb*Kh9#3O*L6*#Pa5$+!Wm0!^35y1GfN71ow!mffhNxvNIzr-$_M z82}pw%$Gxs;_k-#PSe#$^o+ARd>}@Kq*u26ut@A_JkIIdnqul+R6>!uy^YKAkrsff z?nQc^iwwm8Sd&s=I(4niqdYo%ahJHS5&$Kdv;$-Ozi`BhmGm6h68b0e62=cLc_z!s zR?|ltNq^4rLGcB3RSrupRZ@Bm`8jT5er-^dn|V8F_7;9$lG(16u9{Q*%a%z%+?F2I z%H}o|+|PSqFVmDUqKI}0(JD}euwtib_6X}s^+J>1S{TX*?(ZwTCc!X|bj|z1@-#nMo z0z8o^Sr09^pG&ri<)&qpWGtx1_K&I559Ovnn`JH2*}pF%jbif*S1NSIaV-Y)P(UW1 zF}_F+4f~Wzk-wQ9X_L)5j3Y;lg|ft2+A=G&U=m5|&>92djODqJV*x?n8l}y}%<{*0 z?a5?g#FOxznF4LLB8X0O*j6&WwIh$=e~43KwSDq@kj_6dHH6kbMqSx5tnX(E$iR<= zNI^yv9V!5A@)97D#vW-mldNAQ&104GK~kv^m5%Y(-D(jcqIbofU_@4W%~aEVYUyLiOf#JBqR)wMzEkvhvN z9+DFLo7$hKWqAENvl}HE-Sqd`g~T(;SpYF zZ)a_qUjCryky>Igm z%-27e`pVzXm{KTXi9sAuVp?!dYT&LbEJvm2znFTZnEbfW>~#5$)}TihFfsd8Vj0*E z4x8^xj!W457{t62^4rVMd9PWIO1_`!u-uZofY}+g752lz4>s8fWND>qT4kJ?8Y<5d zP7%NrqKBwID(rpoT+z*xYcfOmYPNchm1i<)vC5`mDpXV+NJq?c_ZW#0w~=cDi<&m~ z%hm%#{{RwsvWu?HUVaPR7}aV!8n)-K10x~BK|wr%TW@gsENiJT^(t^#=os&+uaNr{+)!XnGM(l)kXL0%wMI6^;gb>Pyfa=72A2D9J5=#`ONpF0`b?e-dHOj_5Gq=Ku z2jH~B!+WwuM{s0XMnIM}RRHxA2XEAJK-#{G{EWAGeA8uTs1|#2hk%fAKAz{`_S>(% zTaRK-P#QAw+2Fo?y?r0edZm?|jS?e~mjyaOA zw_9-=9gP$gKO?cmX7^)TY|FJDCuwlQrQi8#V>YdL6`tR(1po%9{%Tb3wKCYphDg1c zUc9OKrvkl@^(E7+QXT?;ucEC&y{S>elj06lFlJ>YH%}kYHN8Ldd-)#n)lA~)5;-YC z8J$BHC#d2!`Q(JS=oV6m8>ilV&_|NKx23E|w{~;HRShUuj$dbfKrch#m7Z%7@<&CAU`FgO#;vZ`toqElN+#z^I&_+xb>T; zgbM0}1s`cpQ9YOKx33=&HYeOf^y6l%>1IZFJj>T1)G#30_+` zUAanB{epJo;7@!XO%}+>_9x{})iv)Z&28o;tQMwG>7Cubb4ri(Hsj%v%0Z3SJ(xKcmIO18LHkt^{_8nt%u_xWUTO1DglIQ;eVz0Rq766v!6Yb0p%M89Pv zB&x34Y@k!v-lEi z>(G~rP$D@Xn0=(;PcOWUzF8~TCRfetUzMI;i_b{YVORx0sHA(tF8C`pv)niqtuWmFHppT(&_Mx zz8(9MhSE(YwtrgF<7ssHsVG*B*TgaD{C;BxEAB?bJr2`bn%7Lzbgn5S#l(~d-wUtA zd;b6{AL*7;4jOxp)R#i?747D#;V+U$aG+4cUP@7qUx*R~2W*aCOh61{&GJdKv-1_K zN-hGjKc-7;R1@~w{gZ9&h~^tPJ=gOyTfMr_tu%Ne`ivGf_WuA^^ymZdQUT;T)P`Z- zBQnMX<=$G-+g9?XrzOD=%uI5@U5n7LkktH?e5;a}sD@ix7SOb7YkMo%wc^p-tZVgU zQa&d?wO^4RJdC!DvNG;f&?2`9Yx+gY$Ih;+)Q!)6pVuaPl$X<ZBd5Xu+dRC93X_E-v&MrJyijWetBz%cJCYc~E zSJR@$VDQg4CEk-XT6NM9EME}i+_v=|Bf}~jtGSRCvrX3Iw$v9=M=^4ym--?fsHAmdi`n=jJ2_;tlM5#WFSAyf!WA{zCJ;x>;&^UzwzuSuX8aEux(OEA1%phw+k)J%9c=qulv>H0@j-nH#u1ZfkCdz=ZLLl zvfAk!KUL@6qr(s{X8vW7%O^O#tX)`@RM;JXYS4^H1kyNZp84C#$>mKWOu7t$nLNg-IUmwLY9?ICl845+&!lRjN2Vz9P%hvD*!)FaOl}HKqDXu8le}UJqU*B|4OC z>b~Qz&nv^ZcM{}TXZ+-3BGb$|ZGOS^Z&dlfXk4DqA zO)FE=w1)L3lFlZGowxZUU_oYRi5_aS}z^7 zjBN99>cky@9u>e&*2^KIZ0pQg*z+Hk;9~5M-Q9gy+9%nMqSOQXOeg{I#(t>+!UZk! ztHBHw9z>joA=7CTp}V^&EJ*Prf%IUW=FuNZ6xB2beKIXlLXqjQP4#%=Ekz7e8iUx7 zHyF%SBy(ohTD6CmwCf)xTn~*Z5P|z#h#!DahPbv`t=?ni)w|K{lgpNX`Vq+?c*kB_ zDKzU-K~J6oyC7D6U+B7Cp{W~BG0Mp8JQs$%|XntM+V7WzJOVj{C zJN5Xu*W0E*NYi9t)rDIjzrTTQ$pQWV)TI?c@%(TOTc(p6D?hACw^t0!&PoE`uDK#A zl6fccBk9_Ov{J}IF$dv1xPr%j0D78t$wPUuBE#x0K1rl`yHU_I!zcTXHj%?DO2&%O zr@+LHqK~m26vBN|G}j%2!@n~$hxKBzxOG7Uj|wi;S5JiU17J-ma-ewkWfM;**LAry z2DY<~C5kI3)-<4@Aa!DTo_KI)>pAAg`uYbo13~lvC<^KSb zH0>=W^N*CKHwHKnN*V;WYMHO8)}YWKo3R_PpDx)?tNEobFGmQcmFHQX_Cowr_S|jaFk%F6@j*LNI`i(M2j&gU;MH4| z`4Rk4jzIb&c3!{{OcEErxd79~uQX_M%W3RxVlH7EgAMs9H6>fX42*~|zc$Mx(s!gs z>)FQFcFJtYSor})BZ=b2={5JJN{o{UBU%|ky0V40Fl*R zO}OEh;foPFmy_j9I^N?@xnnApl34(hn)P!}*McKv?vH1`@&wvd=clI49pvzcmkyz9qem*<+Z2urZ2P@ZTzwY}x^?PayvQ30^;!2b(QN zsb;q#1_~<6;NL8ag;DQpjJH1KW#!){Lw9|rO{&N~p>RXUdht}I!^XWSmT>TkEM#~? z8}qlCmRp<8E%|Us4uZEX;u23Lr@}eYz*hhzF&4zEV&NA+XaMQGN&Q$$D4pMAc`gaO z-}z~LnwtfWMwmlvzvaveZU<37O?p`YOpax3Fay{nVQTtxRVn-KiXiEO;zd|ZE`-AGEf8wEQWc+hmpG3|HP zqbu20MA6Q>e-(~}vW4EePyj&sNEu+?k_l03S!UHE&>?|6ducrq6mD9e16*P_$|{Au zbMwa9+S12H(XZv>bT~zDAsZ}wLY@2WHu>XsD)K*}gigFE(6$U|ndM70fHn+E9$-R0zjY zUt`o|;0%Nw=At-l=RcT!SAy5fu~|k-&n&8?h}xy-J-#mf8IK*w9n7Q2AEanYp=&eG zeB7hcndSH!Wigt7Tm1JKG0M^ny#f5vn&ydruR#(d_jh1h6;a9+jG8H~^kNUu(;-J@ z8v^;qM$@i!?OimsC+imzGBJ=Jv}@PD%O@CFu1%nT6!lV;`mwQ;v#cY7)KL621bY#c z#B8*i=-O4^m{w_Q5O9qMB#FLUQ za%W`UWL~77VMByK0d0it^?hSj{{Rp9eqSmqk;f#LjN~4vGOrid^$fdL$19YtW*~0x z`z>nz(HljZkb&kBv^5|Ty$JE!YT`*EyO~nmI?!=4%t%%r7TfN21N@_tJD-4OEuTsG z39Y=;)}NL7>}ruaytclr2NLQ4N}t&vgYyS1F%rAz8V!x!5&4tlf#n@q>dwhz7ST5( zE-ENT-A?1jF$VTJw#3^V(k~!LuAfm(O(aDkAQ48Pq5MW!D?2hVXMS+Fn^f|Sfg;n> zht#v2e=Lf5s~`21hx`s4+Yo%1!>B9Z(=5ENplKdukeM|*xo!d=V0|@0zME~()UhDs zzLEez7jH|3_gU2Z#bKuTt}ja4S&=SVh@(GMD^pr2U>oK!#xCM|0)9R1XO*x|%^OP( zD__f{X$mB@iYS>_?!nCqfPZV^Bp(5lac+I=fFB3P3$}BEODV#kB?J&2@|sojObFtA|UKrxj|2kOBtYBob@w z(|jE^=Cbm7n%ioAchca~bch;Tn|om#t`Ee*NlB=u?1ke~lAn`MHO)g;^F@b}G&r;^ zFd20_&rIG(TkN>m(|+7Yr^_Q??bC19_p_^f`6XevOPg^|Ql(gRQ^<<@9}E$e?1FO) zbG^fA@}$s!i+;OgZ7ns3+lA$$hBoAu5}J+w0I!B!u$Id$tUj3Y+;+MqiwB=| zn8mKGG$CwOGAkQNN06FzP}L&8X1QFpJ9BCT)91fCX!m-Kg9f>5rdTET`6tA7Bv66V zaCiA-aunMW(c+QY8+So1RULU$3hX>GO`c4zLD8@L#dNxMlMBrBVn7@GDouPlVtrTL z-n_Sa@8$)hK2!3gkC(ivey}wNSJk*$A5un8?tqQy7$S$h9KNGnKVwV{?q53SYj338 zMJ}gmW<-7xp)@VOO7^B0Z@MFd0Bx2_(Ll zTSQi>{{RC}bOO8m85(W{n&}$KX_Lq17D(kVOsp-R?7cgECw#IJqQLL77?OX|nw*#O zpn^H2l0#3}R(kaC8C*FeZ1k_n-4Fal=35OOGhTNClPLY2$v-NM{03d87i5l1CVeD5 zTl3$|w_4Ykbl=O}8DxeFQyO}Efbu(XcK0Oo@llY4&VMukv(CRie3=%Bu8WN_9t$Tt<##$vq@y)Lw zwNE+8rg>vtePu#U-oMstL}~)OV2y|!KaN!c!fU&lj!pTd%InOQEw1^|ibHma_Chib zCm?eK^c}YO;gmjn*u=cMR{X=hlEN=4X!5YIf<9r~nGooxuK>YWX)tVMZt=cW9MO0VJ9q*W6?c^JGrX ze)5bm>w2!0WRXJ}TiTnyQ9z@ZIU4mJ7UU7~%c$;8b40l$);@E!hgY9Ynj}aje@IM~ zQ^d0|Vt>js`Z6&f>^AH6CxZ84mb#37yQx0>vpn?V|wn6})@Qk3e|0*Biq0O9zc^HB2D<*uRT2sInJTG=Gg$;C#= z!|aL=a6cTDE9cju%W(Ra^u*trn%g)-ZEt3vIZ!};9m)Nb>MPihhO8M5LceQJ96LNh z>dQriWYsk_SGsG-qeyFAm1tX*^$So3bJrjZ#y;eYwoE3wxbrAm?IuMxKUWCB1sE)A z!H5TK=s@q&8Dl*XHefztwbX4bWYO6RUcWW~0zm!0pVRRpk+1t23&{jw<1R~_25AS3KRUP+rM07EPT>7-Q<^cu1~LW z33P4@UZJ|ve2FcV%M!(|G-+Qe*+ob`eJCBx2B)K*03(Tx6B)e}=Hz4Ez+o!kBY>y2ydq$h)NwnK5e=J`j z%Wh(k*b%*5#aI1NN{@V*^|hF@D_3Q>a;;>3Ott<}5Q>3^C0Pwom7~0Y<%N&Rx{sVL<5?ssZLR+R zok_>{&{S&pfZ@T}R1RtE}1prZ*Kn0M}tof|&t5z3ikonNF1Y zh5o+dNt89i!t)+SX5gkm7Q0k|KyZr}Oi+L0XVmYeg6VxrJDC7x2jW=Cpk7DjNx~!z z-+Dw+y>{})N7Q-fP3zk`GPOY5bfr6V>N?~JrK>~_B&FVubLLwvOk#F20)EIN;Xi$A z^kn#W5@kh*=~kDQaWmV(?5;$7!jinZ3iQXBXC_7Yhp5DFl+85rY0*h1AwsQFwL4G%J?UH%`p99Am&NCH zI&H+HOKOGahE9OdR+T?RapAURTRo4=Kb#V3x?Y9l-7eNRMw4t{khl%%$yTdx>}W8^ z@k@)~$HJ}9`iGQlb=zxu4=7NviYX-(r>9?p*T#%D`Qak>Kt1jG>q1)%I@3zg1cpCI zHOVA^?11tIx5)gk$dEwjusf)Er%TZE8)$URH}|(s5_-}SHXw3tJ_na>yL;p$4*vB7 zl=J>^>&mg}*K+F`{Ug(2hnW%(yuqr}SNVFHWw7*uiLlSjIpfv!?N3zDiTZ?+xS`lG zu{0ob>HISsM3HQQFUk5mT1K0xYZFMVdlj-xYry&#kIjOl0*=Z3X-<7Od--?F4uz64;)aR8o%zVr=?9Q?XO)vem*>CEuk z)>*B9_=*5g>EHsjen91bUg@8E5gH}DV^+}=qZ>vtyNXacmQWP`04dtE%BydSF)z7@ z{)fD@5xd)xt?{E1vkvsHU9f>n_}-1?-5GSxE@+qWf$CnYr|nb(^5|52Ks$2 z=pS0&nt%r7hQndMiOR&b*&y57e3zvkEoiqozNbj;=P^fUjr%}23_bY+_+^BF7O$&! zf%=U4?b&tUezhQFVNJOa)BKT<#5SVa)#_e)@`bnM_PEI-E}>u&Iih<10C7z=$Z@- z>Nk-3;(e+V5(pl|^d1Y9?+EUXsq?MunstwudE0~Pt0xv4k5QBXUf&5d$=|B{%VzYW ztog$7<5{~J@Ds}no`7c?@m;s4w%;s-1)F3e#2JctR?A)U4X%@;QaNv9S>lYEQbt{) zC*7;QNK}dPY9z!lZFkE$f_ai{F;Ry%F)L7z3V~5yZN7|vj~A0MvNQ7{<@Eb&Z3p0$ zt;+uKYDoZ6oR3u`ZSC{PNP4f}nC@82_fED;X#_rt8+8mtPc!4b!oR~AY-wJtYh@SZ z=AEbcf+^#gX`9zD?8{V81xND&#C_OgDA#)sY`jm+7n5o_r`9fuJF@zqH(EaAA90m| zO^_QZM{d7YG5}qFo3Nm+;|$LV34Le(065K~4PN$rPgVWqnPCZ;mA=b5QMU)UP)#!+ zfFoz|=G7xb=bFsAexvz;9=RNUT+rl@$5FbmYSfPatZVJ^%5aOl(=R3`=N%d})}k6< zaU@q97?L*%K;q@1G4)xJ zr9ujlwecSg7@OFn!>iNvzcT4oR&r_nPn3u*nS_xh&&BFOeD)PS=OcD0h#g)L=D#m$ ze^ZN4y@lciFjNsvOVFQcAHNeCW*D}f@#;47%YP|CsvQ`L{7MeYJ^Emg*_EdnM!A0$ zkv58w5v9#4O7QGIdc8O6zkHWA96P6yz>frTzR@g7ptsrQ&YSoR$)*s8wSHCkrpwDe zS(?ep>K8WhTd^P&k`-P|qqt`Mnsv!>ZAxQhhehOAe9oR>^Qrd9(9f{+z94ZbC-9;|ofH4^iR2U!F!&Y@|l7 zsX?gQTE!$rD~Qh-=zbRN8|Ep8i-@2?w|j4ve1igBNuxtD+{E@Pa%DhEM^)xU2YO_s zz0+Kz!#^}U%goUO;!zuji)Du|qAJ*X@qgvX^BT-H~;vAy~*w+)x`_JfD4W4;!5u)@FcjhSm z!+Kx_*(R4!$!ju3$BPrlmK%U+L%+Z9z;;19JtN6_6gtkkrdUM&oD-HZFdT?0x!d1t zwgSx-xk`c?^K#bPMDyJCnnB~Ip+Oi7PYxi0DeTqCfi_uyw_}f3qRaK2{jR=NUzVS!VT_y%!GJ$MEQN=j&*=lBq~CGtx6ML z3Sto_|JC_x`Aej>tLB>t7NHhqW3@@*rIZhIO}+9sW5iv~O^3Y?`Mc%Uu=73ef>oY4 z%JB--AqYJy;C~#(hT{5^fk6lPV|A+NSN{N{G{+DlRwFb0l~>r4^HWT^$Q9n4fJx|D zrh%f#XKk-QR_gU$R=fn&NiD<$rr>W;?Tq#lSDTzTfw{{YWAtfJmqc|!1VPpI3IT6dwKW8ZH13$et?>t~^P zgUxm^`C8k{HcJ~@@)r;4C~i_BFkZXv4;uKcN@nZHgh2)Ku8Vnj1<2LOX1ImFYP21N zd@w}OZ9UBUS=Dc3)g#wrDJ0iOlyUY?5x|k}wnisv1lf-Pi zj{g8g-uS>+L7zn9$_sVnZ!p9!VR3UE8HifZMGG2x(ue1hiKE5%*<^TwF7jQCkDt8Z ze|zQ-B8}EreFi%(@^$b#80b+y`ZNyf#2twQl7FT|8wn9d`De?` z<}F&wM1__%x|TQ=L$Z!$m29*^_GQIEZ=3X2@}|EwgK}JbR!5JLgSvufXnaL7h{(&D zjnMY@aVHXl@ia9(X-ZO=Xf{$qCG!@R`aHUQ%#trVsH2*l*W##cyAjjnlH_PG4`T9c zKc+Q!e8=V{TU!k>&z!CW72}KSDE|N{jWNeA7Chb)`HAMKJln6`YH=S*2bJYS{!=3s zHK$X(3ChUXvLcx5)=hH*U%Ll%AfFm^-`IA;TZKH+B%hQqTd5*ykjMFIdwjA*K)1Pm zM!m$IVT!`t#CG$udcYC`M5uW$zT1K0lgG&X*kxn7@0nt_^IUQsUr~x$N}bAsMjU(KjyTs%gQVQjndzec{-b*pQO zB3fAK%_~9Zs}4uxM_jT7fh=XPPd_kXhfUY+;L`ZDoy3OT1zwercH$5E+mCF3JVJ-T zHCy)}{M$G9R@%nNW2Dx~C1xXO71RMy>}yPAA$G9bJluK4_f%hxkW^JrYs;6WNspC$ zDo4u!HaBD;NNqay?&8|&8#t=cbD87-0=>xx+qMcc%^T$3G~ekr%c!fFOYuexxOD=Q zKSo$3#Ub%yAokBG+gN$a%rp6WSXvwM(8j$=NLI8SJ@Dg(#5K#(5^Y8?`Eb|%Qoeup z9M=^=U$n(V4Lk)XJaVYdiZps!@60i+66jx9TWOLjMrWF^x!Z|a)K}kQ^T|u4{8^Js zLi5OXR=Vrx6I{g8%gC7(swzn|uX1{1N$AQmq~}!f#g>m~jp{ttUY#ULY_GT@=TWz| z_+`nAWp7^cy~}xD$QKf@BC%m|DpKjiW$A3Lg?rUjT5%99uHv%Krc^bj!aW>P;lXwZkmXq^rNkF8*48zP0--%a@sNB$S_<#q zBu@sxfIM5gA6vh=zP@|-<7p&bN;w~iijr&eoPgT?)?{qQ?0n02r^S7Jcg4lZdQwE+ zw4@PJ;C=e-jaa1jA^fpM;U zRfGMOWduoD*W*GI9}!A*%Cd_;k0eF&W|`$dC9~?+5~`UdXyXsmdSjJOu{(QUB84+E zqMGYE&AyLo1aQJWw+7DaD^_L<7!JgW50)7lC&^hH$DurhHuoKJ zL=eWAu7R%4<^v|1<~g27?@~04$pxd}!BtduuW{jAcq|dt#^^qgvft9TUz9(lytOG4 zOML*hLYq>ub1XZ7K~KY`K;`0zkDAvSL*?6PJm(d_)rm6Ir4v(=1`2m>;A{ua1#M{} zic)XaLDZwwuFzdtt4VDWP!K{?0;Afvcy7=~7RvV8ZSpvQ;AL(Na#;K5R3vZ&}g(+Cu6ynXBsC#L>&zMplkP34VC zglLwW(ozk1ay(UhR-HUD6C6a3HdSCeZrjTDcm7_vIt+VTpIYU}*#R5|=eABB#2E#a%X|@M?}bdA^;k>DStI)xNZ3 zYv+=&KV?>-J064(0mCD(re-^*eog$pm(PZK-5evV5xdN`@2zdbHqrevEF^mZ$ z4}%(~)ZdrZN%>mu7QD+1>*=$EQfd|Xd~0&Tn!cR|pKS)Ir%bY;JY`TR#PT3*?@Hw6D6CHsv|6&w z#t%#fBjNz{J@MzV@X@wS`IV+c<+ro);t?dP3Q03pWtLSE6$7$`E7u}Tk~@(Sr=fXv z?$Rp_cK#AH!y%Bggm7hyg6m%r4^Mozi-t?Zb`gJ>-Dgd;|P%! z5+ldWmifEPjcKN6R}s;s;?L+2@T!wg!^7eDevER8pTQB0ywx@78=okVgtB_1kyEkh z@*@OK4$YhoVAQPI^G_ZcD81X?S}Hg?x{w$^%>c8JOgaU*r7#07p?EODZYhJ)I_ zI=stlbqh}@Sk3i|TYGpQ0x?gt39D0W>NfWyAbnFl_5wRR%UJxhlgZkJrlYK)zLzvh zF{a8$6)2>2G{QEK1+p`+n0Z3WNb_aA&V_hIoJ*)hDqKHk+N;F3ZHT9bQ-&jm50C6c z(;oAmlw{F0k)?WO-8d`peThx_9^imj9|~kS6YxNrGx86Zw7nBkm2_gub7=$3Z*{o% zin5+#`AU&re7Z8&(g?SoXr6JB^UbjN!^~0{q_>na8g*E}T90}RaLa&+ck^e$oPK8c zvg6Ep(DQ|aj`ngfg##|ZMF8XtKqi}vpbj0&5e77~-RpXsFJiA4;G=?kMS?90Q{I^_ zR#V7s$Kj2x=Qnej8u9l$c0Yf7K)u;ocQwt$C|NUukKs7telV@_pZvFYa`U>NLZ z1-GGjBUYP0)CZtFNGJFiO=xMk>DV2^WtgQvu_LP_{NM6KaoWY^?K^urr6DQ+Zfxa( zljA^X>^<_c9uq}kUKyilHy6=OBxRa7lokj*jcfN{yB`+7Q@tYLG_^^Ja=CZlQGsoT zdVME8(l>ksO`~PHSY{!RteAi_1{eC(1 z68wEZB_5;UJxD$zWIDD;Q`$c)^=n@!`H6KqmJ(QxiA6Rc_8fuh!}w*m&pT_7wCBlj z&8~T?S-T+5r=_LDvZq?09}PTyDs=r>7-RJAgS9*dQq_sPo9c1U@*|kol{MSjAr{UX zPX@hh7MgUwtE;liCP}GB8xMuQU(XDUgz(vGP4hg@swTU5izM+9!-%00Q)WNv_81AH zX_OyBep}kv`IA^4ZMT%cac4C`*W)Nh!zYKr!+&Fz##|nznVLv^(W@t!^{cDPCakI) z#RYd@-D%#2jj`#Kh*WKryh7*9@+8k_kq#=sk(;Ql+y4Nut^iXQyHWFL)ojhYu~I~h zBS!E={y)PBG{{Dou9fBsGXyBn7}l_jj^>*gu9%kove-`FRE<%{EI0T;p!!G{WQEpl+MCqD?PDE&c{&As!?-d@o3yO{M`pnWxrqBHCn89vo~YhIX5 zg4Y=m3(fU7?lo8@*0oBto5UI_1MzlVRqaiV334oit7Zdn=L?NHOkXbPk}}09;+Y^Vgu~bGyuEegd(B77H%hB)c+V7RxQ;+o$aC9Z zM%Vz|kJR~3UZ3l_-QCP)q{_@m?_KMa^lTe44HD)bs@>b%8aYpafaulzei>&mNHHX) zo>;URhO1#4l1#U7##Os=ak)L~hZJr>Nv3J}{{W)gA32*Sl)6bFiWls_y$8yK8s$K4 zL0D`aXDzMPjweZ+!z=-lfN!#`@BKN+$PP^e zo>osEG(=Hh+8%*#=4%_NZzU&Gigx%@;s!Gx+b5_2yW=SEf6wT(b1Mb zq8LP5HztrXa`E_p1GX7R$n2gGc9HqB{&oEx+fBb7rQuf%C_5UkJV&}UYxAZFZ?Ie3 zu#E#*ZhhQ=g?TbZ%dx^_ypc8jp#Ky=zOgS+B03%z&q9f_r{i^zjI?85xRHRk-NIUL-3Sv1Ai#}{a zRJlzXP`iggCPpWir%(!#2<=_yeX=n%vfZ)k{{S&xYX&Vg@yj*5LV$0}m&8FoO%4j) z(^U6=%lEL?Z8b}MV%ztYmUU7nSvV=7sn`N`r}4>1*y8Kl!1D}e@^fF(;qw*c?ygO+ zk|y*fj30tjZ-Kp()N;d*c%OoD14)Lp<>k55)6SNu^{CeSg~3tD0BX5n2L3he(-=d9 zd&y!85B$gTftOH%(@%DnOSPSiyB?~wH5+Z)wgb4Lu^k>=G_9y!i~FV(V6~-r5Tc)s z0vmv)U#vaOp%iO#a!evMQ_pg$4S0_lcF7&(@@oc#G9NeUDS4;qw$U@HStM4;82}Nn zcPmd4elKjS?o=I~i>Llz%jLZeKf=g%O) z<$XTG%y)AZhV~Y6RBRZUgsAG{diVj1_LVFRyh;KtWov@p6?p)6*n9hqxBzWCvmF($ zP0#5IR&T6Yk5x%OV~>IDO5+J|n_7Ec_~suoSwRKld^HOZFR3^+J2gh#OA$~{PT47M zN6YlIkV6{t$CjctT5p+_LiY3eD;q%8c*^DULGtMwfwBzreV zghUDj0D?gA>DQsI1H8U0pgN?>pxk-mMZedqmY<@1H4%A_kI3 zw3}%!B);Bp|(E#>ynIXpH66; z%hB|2JlpwuOT4i1b3dlC8KeY;Ek$LZBew!UKSoAU+uiz5W4c@UMXK9tSCVNr2qUnp z*C-D3kb+f5YFBKOWnpF*4d0oZ-u+s|47VZuDWv{m$WV^Jj+tIGh&|WJ`Wk6oS=Ic@ ztv{=AEy}2f5K4s%T#??CuicZEqz{V@dpsJ#3%iIfyyIw8+1kh;X!??`;y;()At6HB zD|98NoJ3lEmYL}&Fv#udQ{rWEJ`KDLaEV%oy#vTlX&3WqT860Y712kMCGjjkR8ji! zaTOBAVu%ky{NBBLk1pwVm#{Qw%ji21LHoX-SNx)B^2p?mtb4auK6ccO>V)f9DQr~EBCVTh>@HR7=PS6zn7NAk=0D?{8`K<}q{J7T=(@GX)UR#y*$tRjOx06HkSh;>`|pti z(=Hp`4Wuo{neFdaQU+9l-btEK)-WhH_N_6y1f=Z3G_`oy z^(`K2YZB`*bl}m~;#!)Dclc^Z$wP5p6YdC>?0e6)R<@eM-02~f-hMKo5J9N}?kBzg z1q=Ls11N5{SaOj;rof8s;U=#dG2|+1>^fv`dMSrieWH1}e=hAVQAnEp@+izQDXT9If<*-lmeJU?A`JFVMEg|%7 zu7HIc(2eWFp1=@r!Xu0A`663IE%Vgk1(v?K*tB_?=_EdvW zU!x-;I7s|^nE}LJdNfc@V69T4km7nDmLyt0 z7XJYM*7^PAzgNq4N6fd-EsXEAx`09EA}m=?Z-{>kq|1amLO6{)lFD}TQs3&UA$g){ zXOW~EvI1Okr*T6-K8%PSn+?R%&nELd)R0_TS)LHcXiD@dcO&n|p6UV`3GOZ7krqeb z>J`8{c>e%qEUA-QLoSbFZ>njMg}#zP7C6mF6g&G+?eyc^6r1MHzcPHQd28icFEwhd zTi3ZUhM*pzlm&?+xNd{P4ntMu*8(l!wy16+R&qh&K_FJ73=N}ZS)tfVsY_|2+7&Zh z$gsv(@c^-7(ds)4U@|!@swdMZi?vh()ftPqbB8*LXk_qG6aBvx#;cMXs zb|?8`e;ZlqqghhWO%wBDU55ieKgtdioIvzaCO3H9&BI)zi_F>Jlks7 zteiA|E|!uYF7F%SZR#SZr{C8lEtrkUO%R$}J9%biF+|L$dUf2W`5cKAx=O9R%di)s zLmLhGFR=J&-)vD&b^NK;>?P^0;+-pEPJH%JC#o%vf&Bqv9vTui3A90&=bfp)MK) z;ul&a)|)uCj2T?9`y!wq^Yvce>iG-r^I zsu+P*J%;xDDGBrR0CIlrw>doWw$<>6j@5_7n?EK3Wjf)7rvougb z$Lt`HsKoe+R8t&dm|;FhuQTa0!{?!8cv)=WnQj%^W#UfnPQ;9HA5zE)Q~cxC;C*7+ z!WLJD(h)SHx(X`o{!ZlN4M^^oig|(4&Yd@-1zOf>m4D~re-8Lat(jVAHAw92g`^2W zIR;>QsRUE;$zIfelo(}^GlAAX^(;s7f=|y3xA`>Vc(0o^8>RBSwyg^lvc8dliB8JueiPyW z8{}p#k_PV~g63v~TguGHNhL#dKj+gExdFDB`SV7pbz=sdb0PHY-K313s!H({Zloyb zl9L(_LGt(IR;50dAD^t54Ygg}Ut>tv3J-d3lOl*dOe9&o?z0Z4llgzk#LD*YdW_0G z%&E0}cRfB?1Ivj<+}>Awkyy(Hy>h%SppVsMJMMlOpA%D2g!D+=DYNtK&ZDTM)WAt? z86+bL67&HH_EI*+AYG}8AdFl2qs&$?*gHjRhzyaP{x#i~+_CB6NEs*&BE;P<&QT{pS*T4i@X_)|3XD5`2Dm0N%aL zu@=p|mwyhL6H=TOxwj>ubsZmayL%n-M1U=X_u1m~Uwau!ws`*lpJBI6PD%7J=~@|`5YvcSdk(Z8uPT6}w?1rx(|nV2ZRIbi z`KMHwr1hDWSe1Zpfk9GFa66BuEs@{e7G9<&s{Uksd;P^l8vc)am$(Xkz-An3K7cm% z$PQmM5h_|{vu&zdLvAA|BDyUs+=V!?QWOEy)KvaCGq@f|B8rFRub0o4SJk-(o}fg? z)u^XKxHPRn6w2g%RIz!OOM6+Zg}aN0Po~T|8juSqKZZ>RVL>8ISb;-c;2xgYR6vnG z6#!GhN0zS=>}k-(X5vp76cNgVSJ;8lxB)!b@iKws#`?blAQFiAl_U1N2ek$pzG&jI z&wnKJh_3bY{W40&ZEZaMn+?bjngBd^sSM$aZ<(=B}qMz2+OOagd}E$jSjc zxX@CS=|NAwe5MvQWfntms5gGL&>EgJ&u@{dz zpWlTR^`iiPXb!@lVIrur00*FH`mV8Ma{At>@tf;$42EI=XxLB?JAIK%gsjAd1|g7n zi0L+-Ska`mW*Xh3ZR+n*WHJ^6cH%+r(B$Ry562*Z5_U}g05JZy`Khnj>)u(7rY zy-4c$%Ukm9sb{I&Xb;4h9k~E#3WHE<+PMgFA+S*l_eRp}G>s~EB~t!q)6hgUA(e<_ zZ}MMb^Bx&8XfP$@x8nEA8oNb5#XPdmaWn;-fHFAZO$+=M!7Y>j062MlJf(VcI4+Xv zNM&f+L>~ocytnJr{IJCCEfJABy^48?t}Qza>XCw7zW9;fzNFr`2Pzm85e*2Vgku^R7Zr@Q)AUb%}U4l%l$aTZ30I z84z7Zw~1B+17CjmEOIeCOr~Cm`N8JdF1*Zc^w|fbsT?ln;v}kyu^s*@(B)@hYpGa7 z5xQG1lGDuIRc|$q+}}lZmdZxaN}@7Yj`aiz`<%BR8~pHMHh1QU$kP_{ zjq=@VlRfA&Oq@o?r2rg{kIU%D!+(n)C_S&wzGJtW%XXemx{N_2F@_8W!@^*^zHE5p z^40slTTCvk^T*fp;bC)PidN=! zcH(;u^vOu9pf7i^>mFztzvcbg-^i_XZ#A_;?8I_yfgNbXkx#Q+88-lqrLGa)o(-va zVoM0DFKr}SzpsCGk^P{|Dpzjcnqc3Fut*S`dHzY->eo%XK^&I0(FPqVNZm*^+#QBS zQ{pzU6qV?9>hmq#kCe2qZKz|^gb zfDvY&^OE`#+H3aO<(dW4Zy{MA3OoQDzMt2WnD9uCHZV3HKHAJ;CDy#bPzdeV2FM7Bn> zwzl-3)A?>$O#_4a!S=}r@k-A>(AGsf;jM6z0iQS|1PyueR87jlu6&nlxTjp+i28r{7=9 zye{R7(o)@*_RTx;r3ZyDOVZeA@@MJgPbt{`T3Pvy8}+*LRrSLcfgA?6RdT+WL%R~c zA1>Xp+}@E99r~Y-Pt?~5%ZR7GQPTAP05)kFr8*-mKg~ z_}H2XY@_<(4>DN*_5T3LtrA^t%rVPs(X4h#Zs(F(@JOQF>UwTA89ZK_k!#4pe>42e z51D-1ajsjMLh`bx<;!JnhNtoRa+q@a*#MDR#p>K0LPX5mH`^8EJN2)5P6 z_X!|z6OV)^UA%Yva_CEjKzHr%LonB%f6T8tSX@r>3tN^FFYOeJ4!-+=_vLXFB7$ga zmj3|GLD))l`#mWSKdRbWqRUT-z!VJL1d0!$xGv^?A98V*n|XTirnyZiBQGH(C`|_@ z6#f!Q>X^GLD@^#G;c9M=Dh;j$TCt}!WagW{4sGP zuOH?-cKumo#~B|sBhtlu{cjhME|bgFF|1Ch6tY!*7YwxsYrfl`6Ix{wAd-C21I6ub zuC&{zwc~{D4SZ=wpt5aM50MojX>KfY+8bQjUrh}+a_tW1GBp**FY95u{ z>oNxxk*JCgO&f>=DL(!_`4E)O8RoNdZ3BYr0;n`SYAI9Guj7;FutmO^{DaY3O1+i! zMKRY0Dz6To~G29*P_-!YptuY?cgcaeQ**-JV;e>qPDFs)@9+3y%>eB8of_*;@*k9%XPpkKtGh3VeP;dU#VHR2ChkTbGap6;f%q>r9aEXL~YBsO8e7k4=;*4HCIv zYD&Ed{W}~^`@3upLM5vbZ@_jizxvegqX#{r;vGS16DCxQJ+poSc z9a}i9J5Mj_I?M$ZuAGGJ@QjEKzD-HJxHH6-j6uxHW#JB-Xsn#5r`$0B&cR5x#h>@wnkCj!p8c% zU&={nC%C-UB`NiAI7Xn8BNhNJuoWzRStx96ku0muzHYknC5?`nO&-+|5k|s@2nXlm zksIz)5kS3%%lB5-KbP`;lbIF0RfB~-HDetE)2$nz87zY8y0ER^D(boo@0a8@+N@0+ z;0B{?eku@1_XjH-%*5?7(&Q1u=9Oyn>}m)jCJK>Bh3=~+t0k;g%^#y{8$%mXlm)p^ z_|mx;@48XCa=q)Vjk1!`Fbc|Qek0KT01UD*x(sEr)&BtJAZ(!W+`4y_tz-07+TM-E zrAsm|I{TBc`DJo9GKjGs%N-6|xtmgxlvbA3^2qXysn`JBe$@t9Tkp|wNuBw7&AN}B zwTpi!>THZ8XSSB@zSE~pMUQ?XZ}PCf3G-nfA}ld~%pV|F`F~lsyox|9noto5H4&3Q z8{7g8RtC)DCL+_v4+QwSWF#NC(`vhwz;rs~#6uJ+FWnh+1i z#)O(4gjXdKM%a^KcjiZ#bzd_1$*ioS^=SNUqXVk(+ymoYUrDY)1BzD24VadzqdIFM zOZFn&ojqR_jpMSMdGrk- z*qYaJdH@exC%utRzOx>obzxznL}F`n^rvMdgR;=G)9$RoT}C(ntd-! zb!hF4q)8byH42J3{OS#SK^_?y?;;TZ0I>NsOGVS}rn5#82*~2YxTQzajD@xh-lL$# zJj0>QplSBtUE7zsl5%L~B@`+>g>q&f@7%=fQtvK#rvCuS_kvp%q_CO*-6|A{Q@Gy* zWU?GjPx5uy{LQ>yEna*VSMv!yMP1rKvEQ&DZSu)R78poV3iAbt(KY*^lBAMZ!Xj{| zcBk4S-)x25l!HtH#xLKQSaCNJ6Bs)JE3hZqY$I*b^?%EF?=_Dw$)s2}D5izQ#C4*` zk=4jPG&|t~>i1h2^m+Q(+u7^$X);dG**oxg0ofFqnxAZrA;osg@_ixXSw5@fPc!Kj zQmof+EO1+I@VsT{?!7kzf$}*yeH(YRor}i&i{)hVM1ElU>N5>8+3ppW`NL3G!|1^y zP_f?3?DMTX4Dv*dswHA$P&yVH?oVEs8cKYOE(CH()sGWhzy(R^;gbf>$pkWQG0SCh z8eX!P=Zr-e=UxoN03Anxr4Cmus=V05+Ff^i`qrlpm@ZT)JWCH0P!CWBj2}tIkuvrgp22tX|CqBTedFhlodOGEB9qF z*tS>#p7*9|mi|rhZS*oaw7RTO%sB7M<0FY41C3A7mQR&?i&bv$71VC;Wiw92!?6JN zJJg@YD*)`unHt+%X}03U9=r2KQ$oK_fduW7C2rn>GG% z$PbH+hB6frP)_ubc{)k-FD~Etn@f>aaLI1#Pl?g@Bjzc+FoRK9ZdLyPDm42IOHsVl zb<(RELnNSd0nWhv1cQV_h9!*23;gHiJFBbr^7V`Gv|^n7HmW}ary=v+qaZgsq--rq z^9Muun1KMT1gl*R6n$!RQqJJ z0mZjV`!CN*&3R1SXX;o2}*O2&$*i>|;NU4YqP13bpQ(nKe(Am9q4;Y7f?mB(+0F0D* z*yhtAPpQ2B00q6Dl@0Eg8Y`_@ZWmBLSh&=0+$qTAJv_nvY+?iC4>H+X{bJ8n<|BZ@ zuuj8zl{GE%Dr!%TB#>>HLtlB8`_59mo~OoE0evPE990L8uo(#(38V&@X)W9HiAVtY zWS_DPC@cJO(`R6kc6ybPL8AE|Rk(0t)NbaEDFGkuh>2~#)_%V%uO}^-mxbul2>G^-P~{F*Ew{2<*#VEv|eML!%??%)Nfo$B4N98Cwdf{nBm5by#5x>sZh`wvR)y|Q7ZkFSVFD02OR)FqCO~9!+8- zlwPxOrr(4i+=$*YcgEqJCz1CP?Aq6~>)!DJ(wE zq#A{Z{&Gc6vC5~vtH?Vz8hnRspf#0*_qOvia)~Q6VzgB%Se7HNhDNpr-raYz$|kpa zX*n{{UZi*eccwsCOiF!iBy&UyuvpcC(29}VpQENtCfSXXK-IjAgDigxd`A0s`d!5$NZCCY+wDD8rO$z5N_Y%%Z!dEQb9}V6_gYkLs!P zrbJk71?c|(mbOf{M_sy5VYCuDf@w(=IR@YPe%4AmZiz8HR`!20YPWY+^7^Do?ajC3 z0o?od$boyNJ3SdgXx>nnbiwM|$iAb^PAr_<0EhX3BD-aiG#bX`;MRnz(uRcfuVebU zxu4X8Eli^2rM+H;Ymwg$sq@iLB+V&=S+HvObyBFzv7z@DtU6y`El80mNlMY z-)XK1Dp31vT#+q6S+eshe>h&f+Uk)+k-27Ju%Il(xqTaoV1&0e$ieQPlRxvf`GKu9 zJ?-AxkD?m9gi1*up5}vY`7CiH62@WS`sSjVPnj<*Z1~G>@o+Tj%mTf7QoE0Au08F6 z4Szfrj}DJx5$7v_Ks5@a3h&;XckszX@g0f}c1ta><*`A9~3Hj_YM|lzCgUQqAZR8zJeL5s5c=qyo zp}xv`psAtnvHCCxRjE8h;nM1m!nbzXeMF4=6Tfp(556GL48XH*Lj1I`vKDu->wmkB z?FGHNxF2o3{0G`NnlZ1(B7=7G_O-dubrQOQvc)4XRvj1uR-@XYqX5|?CqctRjG)y? zb_>*!2(Qpa8&|9OE6Q?QYPu!UHB8e(Eb#&ZBs_sFUfX4u{;zWhos|Co=OX;%EyPnJ^-WTZy_03J^OjJ?x~tB;7QDso~@_?XaV%bU@3>-N%YCi>S2 zqA{4$m)Z*C5?8=W-eHHh>M+ssi@+c!~ zx!tS~i0#}1z<8d&e%J?6&c!`CD|FKQy{fLFcxrOP4a9tp#K`YT1L8a3mcz}6HlO}g z!hDyc-hN@~q(vaNXze0#LjJJ>)D`fq-z=2R07gyA!TiJKId%K#rj2+*E4S{)+51G+ zpN>W+qh%}Uk==UHv=7AFxRDKh$s5+bG|5Deg7t68=vU63cGL8|XVse4{^?*~8-6X+ zpQL24izFX5IS&X4Kj(gv{t(peysKm>ro($W#Gw2vsM`_r5tIuwkn(LE$E=Geq7(NM zmeV30gLJDMv3S%KSrj?zQ$x^q>0BI`+tGZf=C3ke>9+n!(q5J~S3`pluzGF*?6f4D zkhxXzrUo>P@)hN;l5cgCF1I)8kHV*F(E5b@mb4v?KpTR~cam$`)IMUrfUB6qo|?Jr z0NDM&VJv{8CXwaY#5b4ld{(y9B16)wDn0S#yC!}4VC`$>9VS^LD6exD8CzgmnvwxM zXbnzVfbY8r?h=2`>zMC+>8Ra6y(f?o4Mxqk)0prKqHTg{awC%*wL!qw#M6GCj#vtm zW~5@7QfP@_;-vNlv^cVhJN%^jt-qPH>2{adJxnfFQcYo#>dT=8z#P5w9*?%)*D+Dyb&YW*M3qXd`eGmhQBXkd8=kfAW~B|!K?6&=0uRN2Ws&~)kUt{{TY z10<}ht58ASqpsD&k~UIa_2;WwpErJASb3)T5$-JR<+Gxbuuq6F17J@+_~ai+_q9aP zr_0)XY8p<6S*Utc3LmEk*|q4tPrJL5Qn$0y3#;5u8UR?3%t#16mNoiva3`0VG6$o6 zc)&CrTHj9-q`J~ZuW)wwB~>2*J@}o6j#YOfOhB-fepzc57B{m#s~{GVM<*{*lpMDq zE77_gy5+EqqYL>Q79``ru4JM>>J=Q4Xj*^)Ak*r< z%6%vPRjcbdub9XJ(lm%dei0_@}*jD$TP21k?5Jn80nwXI%XL;J~sg+0R_wD#?l#B8xvz0#LUvx@Ujxsi1O zc%)s_(D4orxD@%}k=~h<`^^4e(>*;pJpur-rKASnsQA5o$rbC@xZ5Wp4D2E!WQy`V zJ{t>tZu)61H9MHyr9ityBFn@pSFu4~o&#hG?)_<`lgqb{r{7y@Q!2QMP~s*BiKA5g zA1p9DB?KNt_ciXMy5ExK^E}JUTus2GC>0ft06YL?1IC2pvqr0n-hV?>HZ=Jt>JL4s z*5Okfxs~If6b~mZTt{v~wfSUu0LzDWo!x6U8izM35{*Uxb@;ac;O5Ll^bNO}EzYFb`j-$dM$S47gFJEEQ%rZ!Qu}hDPQFs zzZ{GrnZ~&@Epy9r`9EAfV$~}8ZKcvl3NFS)S`kkXPTo7@fdWSR&)k4)JlD)HTU*|* znO!-g;7=MU8-^#S?L)RiRDQ&OX^87u+}>-wdy9{TFbIw{J19|5EA(UtXUHLJEd;?L z0P;2Dgnp`b$woa{5}KqDSu3J~&M1PvuukxpSuwM1sbJ%Z^#7{Dum0Kl0m{w71 zK6|~Sq8llvsgb_QD*)63+Pp;ta;##76kgVod9Piy(oUu2T@D70(n6eBjbs#|AfFw` zITCFqB%5FwbVeswD<)CBZ3*W6y@RZc;JQe^ZSZqeJ@b__w!Y^nQg7((LaB0rv+O;@2bn~L zryf|-FGbDHx+W0)UMH2E+J((1KJ&GFPDkm9Ssp?3ALOOO`H#z&`en=@*?PXDvq{!7 z!HFWh>M9S2->y#}^m&W>gIu}3fAeL;dc3n}ngYn}0z8~{E<%%C`vX!~Wn^(vIErK1 zXOZuSI$1%*$SeSEdkP-@ zIV^sV)g#5pDf9ZrtLnGfww(|UE!@&1rAYN7c03I`15TMtVn6|q-QjV@`fb#gw?M3D zL(zYjuWxPe#$C*Y>5LakeSi9IQi!}0Tq~H!G$3^!I$@8d%6-pP@^6tWeAREP>aZz_ z-BM4eh9hT14actJ4uie}{oVCMrUF0mk~h+yS+~~xNLFhpcrtyNSBdyqn^SPd!@gK{ zfd!1s)8>Ri_Z&vw8wE7#E8E{HfUw*K%(m?>w3r5+amYtH36LoVV?a-_rV@qTT%KJ% z>Mcwa2HsfNLHL1XZbH7*8|35Z`Jjpqp+7UPEPpss=ge_d*H}oz(!|xL6&+W^D5Wzs-z$V_D3Jg$ zi~Snk$+y?-rJ`NEt;kfk2Wr-Xik;{>jEqjqiM3n*04{nGA2eMaQpwUK+l%rwQ(l0L z@ST!MS^0Z&BwlE;j@}@L>pYaW{ogg+PacPj4&FU-c!?(af+fk8{(naW-m(_hiKO(^ z(0!t4z;I^nPUL%K6=eJq33t3#Ue;i{v9Z%_RIK*yeLz43x2P@h>GH|U-4dV_E|sU= zN@mu*10|ibk(tVhDCFA!efQrJw?0g~miebvvek7FccEK-Qqe#{oU6DS)8F{u0E1;9 zY^Q7K2KD7H&74!lo&Nw4gdVCx=^rEOcly27hoan)EZd9`0o15Glph|n`f}iSwyuZl zYLKUl{$@u6I<@wP9APC%7zR=RUyE>jdl8gGU3=RHdQETS%|R_%{@g5($2X=;dNn>B z{mx4IvJ(lMT7(w5JU14qv)nVv_XtMeQ;O7|denQ2B=<3-lg!u8u1gf|r5FHAdjVQg zVdLY0AX72?xblnZcVynFO+F!}22UKJ`v2V>UFn*cy1? zPrIk8Bd&m;q(y zep|HE^<5_V#@Xp=InMOxyavXsr#EGlnOGTMWY3wza3f=0)>s`f4)SMKFqIdMJrc zCJcKDfNAayN2ctF;Xejx=D75-B9F_+?;X4X&ftaEFYF(8JCZxoY?R3+$5yc|_k7}) z94_QpixbGHBe%cL#}3rk&GgvTeM$>BR65A8F_Ph*mRB#(Ou@?}5sk(zJU^=M|Xe^!>zudEh_<50m5x`R#k##ClWTzfpr z$(~a(JhSStw-toC#~$U#{hEBYueL-EZ09Rumi}L#S=Q&%bQp}h`6-X&31d^zl%_@- zgn~M=UnJV>x#-Z_PQE%{P-=>kg(xVJNst zm6MtRz<5*j%Td9?eBDL~?u z(Q5cp{;>N!!{IUXFRxxB=QS1+wQ@PHgu$i&6l^yt1Oy~W-~JLq3c#Z zXWF@3PRS8V^yp!Yq_#B?Lu^$&a_ZPUd21O9Q= z#CeZH)I7JS1r}{EWq;a@h_5bPhSaBpa>^2gwM_2$SI@V>Jj-=s6R@^`J+vp6Cg4e- zZ=X^-bj*B4h4;3BY;Q!hn)2cxSjP{l6Pc|CfhVvApMFX_nK|QoE7sHO4<=P8%)J77WtFZd#}D(ka}&Rh%}LYQTcf5 zUSUyS!b@vlUU&N?kT(WjepRMR5v~3Ul+a{4uA}}x^R?HP^)zE)!Q!`&kM0sZ)xiG% zBno~RRClx0PQl^6W%BLkm$f;gAQg#~goAO!*NLzBLwuJ{$8#`tTWfVCyrTBXUNTIS zlZl|AJAyn9af8X3m8LrM5w4*v)wDn_5j_;tq=N`11admgC%*rn9 z7nA_~m7=jGy^dNB2x^&p?dHqFf95?>@E?fG=b9axed+u%n%T57{TIqt{(17evsgko z=xHRp!240Q{7pA4Qb_UoGTerT7r}`jN|t#mRFlZ|T9&l&AFj-eZ5glke1%Jc-)`Rc zlTr8VeavKRyw^)VTfFAPQcvDh{5bR;Bf}#RLYa_h=^kUUy7F|oebtoB=}e%Krl2uu zgYahv$v92vfg;w30@HEx@WB(h8PDer3(ZEtEiN$?x7DBs zs5VlF1C6@X!9PwURI9lWMQN#}$KLPxHQAJw5JK;2GZ+1K7ZFz;S=xsqc zMx>b4fi!6-3wxe5rbr89M*j3K=0}uQ%jVlx)7buqxZ)w?1yPEQRUMAUY=p-S$E~6= zwjpqlMDg1sgpnGSJ%cvjZPtSm%90aD^(}4v3Z614C)$R9f#0ogfqNi}eI=&(l7A)n z4$jSO*?zVQmLjw~>VxK2en+JM?}s7eJebgtwgcy%C|UV#Jv+^sU`JWt<#8JB5@rL` zA0iED;&5>Ak`VDb-DUMTQb46(S1TNK`zubLPwU7ClTDx|ve*?@a|=iHPAwoJp9^#w z&0QHDi?a*{OX#C7z+ui=CXVg+vYFDGiSLF9dNLA7wYgzXc@$n5D~j)4CF zl75U5Hx2vHdAzGr(q_|i3v0-nmX_bwl{zh5*?58c)KR`N9XbW-mgv_EBKp!%Ad!S- zhoI_k-qeeF@8wUHto+fgC7i$>amyl>T7_8SBno((m2*V)c;A_4n^)B0^6j{+aY@D= zRTK7w&Or_}zdGq)k}Ffov&PxC94r_+Wa z!5&BE*SHTSZy;V+>X!D}q8KdP#^sz*gidxOc++u=dyg>BiI@2SwBIlJ#yed`T3VvX zBod$6lZo`nJ-4CBV3y^Kw+xT|au3X!j8of8v6vd}*dtT@+Zr(ei1F$4WDdrb02hdA zx~bIk>pK|Y;_*f46H1+!GPd0T=zMXMxQi-K^1q{DGU_r%&F$pnBK7388v**pSO^<3 zkYra@(n);-=%_;KQu>Puo&qhc*wo{eo)vI(@p`FRD-U5#@>k?q#AsXQuQ};gw%Wzz zzNZsLVtE#ukyTIdKlt0-#Km>#Io%?06ofBPlVV8AZ&CM13x#wGNH(A~m zGb&$O`o9s5FW5%;)X;w}R}f-;&$IS10>jBX^Qr#;k1xMY>Cn4KR%#Y>oGFthuompa#PE)8MUW8M%cgbTk6Ctx4^RE-;t1&dM%9&Uc&Iw}1ixNoeQBNiyEk!$1=5WiHPgGo{E9u~t;xZC7Q0lvL z0jsxxp*X}2Bn1BE>e+RfX0d@S?WW=*0gD9qKomX5JD+@)Uz+rP|IqO-oP6`9`5(#$ zMe?$Mmmw|Xbnz$&J}18_bU990-rjE7BFo4;hhsJ8lXc76wRt3z#uS~Z$XTnt{CL*B zH5kZxek;}HUPV>ZwU{*BT#rI#5wLMZ0grNeZIJ@YGwEa=a`HIxwyANZ>2m#Q)ny?H z?i^QTT6hXq$7~)-C2KsT2k6~Id5cka5c^*@Su9<&7sxpHaPr)m4O&I22_g ztpPiD5rT_gmdwg69(C5>&@}cJ6MDXqaNnGPsrd95VsDZ!XQTdeXe(uJr|D)Wb5aqUP1Dp)h}&@z-ENVM2e&008_7pLD=-mqYx##ER*Q= zc{OW)F6x@K_MK0v6lp9@%Sn`RPa zKg;R#&oTLguhs6P2+$|@sInd;{UU^Av9M@$+ve3R(hoU#P9G=vqT>GU6_`s3+*>bI zV#k*y`UNT5reot22=itQhlJ_=W0ynKZZ%jVQz2OqIj`B~DaxKIHpvZz2327%0!z!U zu1+w~n!^Vrg35uBkt`CVVBQl>*F65oN;R4&@Y3N#Yy!T&Mu~sVr%8rz;_kw_s zenX}t?!=aF`NgOGM&{Y1U@sSstQLoh5H}Rsm92i9g%%OzY)?d)^t<>qE4y;hOb-Nc zvjRAhJb$iCB<}UE%UjvB-B#<(vdK#;DBcj#iZ23$dVa=-YHzj?Dg}uhA3S-NQ2jrt zOR70$jGBUb4}WTmteO_uBTlipK?_?YYELdFt$cr`NbZ6e2)CBtjW!_CO~G0&0;Pos zH6Fu!d7e*XYi)J;~=g8W@iBg}!|DP4!QN@HNk zw|V!OeBFEI1%}b(BG%3^Zwijlp5I5lMDA>)M|L}>#AH62e|Yo><5FpaLv}<{Gnfk? z+FAbcE#~y-Q18;d*&xL1^>50Hn_UCW+IF=Rk1eHx{bo|D@p^GDCZO@jVjLu&BEu%& z1cy_;{{WSXCbrj#&24D2+`}y@>+ysC01aRBrc?&j%0q>M-g!1_NG$K!k!^0mr>NTs zo$5S#Z-mBr%bl- zmZc>iC}r?8?~^-3hBA$pjDC>0p62TL9lcwE^);&naq$E5834@lX%Qn;k{(Ln0Z#@a zt^j%==$cK->GuAVbvE)Rry?DJ1W>ok9+@gb=`SdGcH7K4OdfL7_{21}QEvbgQpANU z-#-31Bef9JJD(=`mMu?Q*01kw)tW>nnJey1M`AawR}NO$6GoJ@9Xb5Nc`li4MxI#8 zsig&D-){oIQn-=1Au=?S$89~dqf3%ES$R=WfvZ&4Zywmikvm)6G%X2youp6ZO=69B zXA#94ngCQ&l{)o15w%84n%S(LCxFK(zI&^Skx#77Sb``<#Qw99W(z8?wuv2Jxo&9e zftVTv=otLH@?dckA#T+nceb*VoCxRRpbE=UYmp+ClOfezMxSkC3}Po{GM6LcB9z>9 z;(L67$(_+2_pW(&P>abPUAomc^3<^^jjBi#!b07nZ4vFkk_j3%0T>Zp zL*Ad~O5x;GKBWA#NHs6Z{{S)hO+7`_+EI1NP1R9$QOBufC%0;3vU;Eh=vw4xzBhYG z?CuSVA?96&76!FuAnomtt7Z<*L(}}bsAyLfg$cL6^;D^Ugrt$RcV4v=+Yb>0QfDAg zUUSqBnEb^it$Q(NxqsitN5rh47BuOJYrTmCB@=Y{IG$mA59HJ`fMzjJA%-O}y^R#K_*Lm8bZc zO0@wiy*=@;YMDrG`V;caHD5pLx^|UuI{jbj;@^n@RyZORQ?|y9LsQkMT)PnBmi;ZR zLWQmT)Ung`T~6ml^3(z~Rs-Sa8BY*U4!qWxK=oNkculUIbzxPIC{|_SSo}eWr%aID zGLRhEuBYXhe78N$ovK`?*I|yOLGEZrA9@=77zjZ&%sio@S*lw3vMGIYX(9`#R0@Jf zA*gFr-=8Je{%8KGnTN$G;D?wFMXc;S(c!12V?p*tFy(0G+SF&mri z*n5`K$n^*kZ%WcuRbfg14O*g=JpkULCMB>(2~T9!TTs!W^39FBPd_ut{`iZc}lfF=0yuHhe~+UBVvGU5$dhxO&T2u>{`jaOKY2PGt=z+UMIw1KAQu% zM6CmFX7Zq7=)jsFW81`d3^I)%^F#_<$qIO2#sLEWHiUngrCe7<4P+g7!WirYC*V-+6=s{u~i zlU$~oSOtUqURdRn)bce@C`Cm+?eb@vWSLHzbp$T2DuCkr4-V3eX38Hq-6o;sF)6%Q)$SmZ zeoQ)*BYG_X-ymQ?TCJM%o{u5ZEcIFAF=_xgo`8;OPr&7Sb?cFwtk~=N%873CXrpOr z05oG-fJ*f6@fjoD*h4AQG_R=HT2Jay=Hbuohfhp_LP-5b35`%6i(_v<{JzqzEHz2e zauVVTb1JmJa7h3q3v%kGr@k7XeoQ29l0Wk2NP||>yus#8cBawNNMkKp`qF)tQ`6z6 z-I0*ti9P(i?B(RdJdvw6knS7JLyY3iSh&PZc@W&!knpIi+gr^RFdE8s0lp8gOMQi z`!plAT`%3tcXDGHTh4su(n+aZYEY1t*DkLLfj|KW-8+&=1 z(vCua78C-FyD{(wf1@pd+$k3HZ8HADUB0!@uggbm0&p`b?;v0SsQsZ`#z%t1urT2k z^oxnEKP|kqd8O(AE#>f?k|1a)Mpa`@^gRyUFl3P;z6b&G4v(u{Y196ewv?phoD*H> z!*G0S(-3QuYHjuVne7(E+=W+nCYzpPrF=cGb3{NK(PH*#;fl`UM`)D&f=?QI43SRH zsWl5Ef6H5aMyn>F_1GhYw&t61V@{=b@!0g=4oeb`+wp3FqF+4u$5V>c;EGrlX(uwf z)}cwQDk<8vr?yvn`@dUcM^v8EQ@^kgY4+iL2tvxLM&z)lE%Y9lK5Cii+U50(QlPsj z4Dxd^C)ov*0zeero$w8Xd(^}izG#!%Yu;7Wp6I%2me9v4hAO|_upE=dqwOzoOrBkb z7o8t2=DMQ!hfCItz1){KFvjeRNJ>#b2Wkwi*_dBeedJhT^Tw*$M!wW*=i;uT>;|Z> z`k;`<+PN%o;=8XQPT=ocj3sp*tOznq^-V7S z08i5r>UBY9bI#f~O{6NFn6Cc-XJ7|W*RDJ~hkAo=Jo2;H>2Tgdc8d1Cw^tPavG%eEOV7;tlG^V_u|A!MF3?85W~|}5clOBI3^%mD%KmuPbeE0Nxn!nYrs{a63 zV1tm@D-b()AG@wyilkV~Y@YsN>NXxq^A*y>QOBfANgHh(j6F9r9SaazzbvPVqJeKO z&~@l_eP+sCc2@e3Bi5*?;teP~NF5JTj0hzrDxPujTSuu}YH+&6Xzp4x8~g;&Ql33< zL|(;^$xlN3zSONey{Bs)U)DlP1eU8Rvb7jEZ--;l0lC96I7stjEzPihG5JyrMFz1C z?iyY%>8Pf}QHZA13^&6eNfM;G!^$@|$u-Oe;I}cCjGu^(R3rnr>tClJ2GqQ7pg%5c ztvsM&^G2s4$k8oeAG5+xR)@iqgIaXU?C-O&k*zOo;=N}+-sJkLRwNC7vkcm z0I#?pR37!pVHQBqdIylT8#p|peWq%^1@m)jaCh|`suf7>#SHnV*tG?G;GTGVCLZrFNKe)gO1y-JUI;2SdH z?t0&pbxliB({3ZtZT|q=HI%%i30`s}jmwJ;>ZA{RH?S;8vh%;p?=C@Z@#_|DT2@+0 zqwJrHW>MO!O8eHh40vzeR~*>)mh>ylX)bLoBlwv?sn{QjrB8fMDji!TC#U&J1oFm_ zajX&>P?X7SG*9+@1SozU^)JSqh^`ae^I%Tz7P`2XSGj1}{{V7iW<{kIg{wd?N2Rie z>iPrnvMHdtm&?}bCA7Lgmng&zNC9HOx-Sw3T#@8ho!$kl+nYT~QU=7)YA6XE{+z1W z#61&2^8LJCT)gvUsVRwpRJDw3Wuq^sY5bv1TPg*NU}H`^ix)#q%uEZ>vcKPDrGTGO@LJNXH`OK-1-bMS*1_=z3<6F@vGk}{|o@B8_+ z&NU~^uVw4%(pqVszDAU+b=#F&Z@;!gHUOI2NhX_oBbYRD$0!65&<*_G9!M&d8Igt;*H^G>U+>T~KhD$ed*9d>t7o6`J9g?RcY6f^+-I~szYbyIejUlloXLk0YUphy@B@2VIHhy8clya zBD&D^R?uK3IDkh3dV{zGb~Rdk6v{_6iFj<*ZDi1!gUg)(@EC2j?`BXTOFKyQ7I4x# z@L0hIU=IG?7|7Ed?vu?IQPQEYyiGzDd;5g}9CTfcYx5`Q!z!vt2`m~!)>ro0exh2{ zW|2U0ytoFX2_6EXzT1o-dZ-?VCWAHn+4E)QwLPDgFU)TymL#__!(UES^$83Yu<#xk z8G4B$Yh#WZTNCn5vnQ1`SoI}9@*?Mzh!@5i786Qj2V$h|NlEnN}9k`Vzl^3o?QMAP()72)h zbu4{ohakS)$I6)?k0ap%=2n+M> za%5as$Z~nkm*t&C_U3DgIM~N*tsy61QBM*9JxM40>5(kS46n=WsOY*q{q>Rbfnc)@ z$1Te70Cg2Vem+cZcd-6e-dO%vXf5XZ>b>3U1j7iSBneFk@F0r#<%IBt6C6gKO1jnN zovJsT&@r&OnYs2Q9Gda=*pIs*Tqy$fW%}2aFLmu2O;$x{OT>6a+g2n5p;CU)-*MZv z43VIKcX>DE_m`8+dfmU5E?uGZn8qB9`2a{1J~YBNB(o=kT6-pyHP@Iw!+J!8u|Fqic0PEzv(WU&*4Fuu z10{YMN6D$bcIYYInKCVzCOp-=>18Y$viWV^PI1Jg6~ASU#Gc;&0IMJ)#8xL~tNCw6 zw9uh1bXNCLWS5G^@s)VW(Ufo3r{9w?qC;GZ$u$d~@jj-WTQQ4&t;q4G@}!UPd|yp{ zh6u-s*^f@??dOv%Jinsf>H3l|)KY#hyKKr7Q;;1BlhpWRC(69}q#H`F1?>7(g?tER zMBx!#m<~h};koh4WCht%uunYor|H%=OQu;!8r&-uVt&wS9!=m$tuhB@0gL%^*?g<4 z$8+YZXWW6^)I;f=`wG`2JvZ(wqdiR|4b&vAm8$CpWcAA8jF%~s-idCSm z`BebW<#Xsetw!hM`P0d|GhW@pCCBd4Ck9S^_?^WMeY$vL#{09D%-wDO0K^_~v6_%a(;7^_GQ9QrbIb+}ESRi$gfUPmsv)Nl^BoRX6K8)f zy^KXgo9g#^cByH5q-awKZJ?2gbMB6ct^w>*x%pFJl943=^PDloZ>U9aWSs4h#LPGN zN}bgG)vf`t49R=E(^>vvd6wqKNtJy&h@Q2ghE-J}mG8@^u*rytU(Jaem^Gh}ZuAX6 ztIh|YjO_6OL5va({`CBE5hM#{X2^B5w!YIwgQLgBXnB{HuM*VUH-&$W2{7uxHGeZl zc?O@OT^bl85jA=R1e*T5l#P}`EL!q7?32^Am^3Aer~-p?U)6%QNF$Tzzxl^GA-mUP zc^`+>tnO|LkG42OO&75yv?C=cR7+gq`RB}SH2U@B>qcO5Lr{)1`f{jk$+ib(SLKan zd#y}*e;l{IowTmhspLQ=r=d4Wkr%NoQ*Liq9o)OO1ZsDKy|yV5^0b-TYf=#zQ4 zDO+1n3N(m5AOeu2k=U`pC{g0?L|!Z9%`y)?>eiMqO0Z1J>PTGtDNX37rB1;|a&W}n z>qKsx{%>j*-gnm^(%vRF%IOe@jyoXXPh+04Vy2nv-{lVZxmc$x}j zqtt9QgV3%U!avH$H2`D%YE#c%61E?%n2wd8;kQbGA(C6JY*8hS-@!iUr0@YIdCl4w48Ar?e6h%QWC9{><-GlH1U%SNhyZl2bM0J>S!EvfOXh6_ z^jmui*q@YlF(dLDaV$3t@}>oeR`TA3ajMBDB2pMYtgNPhv7o16-zURkb}}^o0RPeX zAomg8XrH0=S7@&+rDW-J1-pAMDx$|bmWaU3P-{!P?ogUWZdva!e z=XK-1iNF;}&qHl&`fb`tSnVR-MIjbhAfyB$5!W55i8w9jJSB!a3Pf zyF2rKwfa`t(d?s0?{|Xa#(oEKJ`$tEQ-5=UR8Gud8UFy~Hi~?qu3GB8V*(gj-Tgj) zW!xz|N$gDpJZqN5d?fprPj`%dWO`-$rJ)x4K!RbYHhes4MK`Qkf`h7)><% z=({a;HOPfpZ{JLSRyf*1JNTLb;ZSl0BJGqm3YK16yO&7Pyu)r*-GnnhfSMQK$hjo< z8;bX>22OilxraTHA1K*Hm$Vt+g23EnCwfPqN6{QGo4Eo(CA3 zlOr1n{{Wf3V>+(Ca}|IQ#-F+%Q;QMRm%tNReDV?MQX@!v88nvBWU#$=O+{yq(sW)y zq8l>%9`(uI!``*JS?3_aYkOOf@y8O@hPxl*PT3h9(h}nsk|>Oz{5(;B*s%SIdv*GEF_0s0O_&TugX}xyO*;dC!-8pD4VyG9tB* zmF?z`iYR33Ujv34Q!^?XA-v({^z#>)AeT*udNy)KsI=--1vLl6C_dP6A65B}y=!Jy z`S0cl$C~ZZ!bSq#C``(C2=1YoN3bO2J!S9vnL;}}XU`g1>9-Kc5e(CU&Hh7lE5Ff_ z>RCs*38zuf)hkeU6!05R;aM7GT2zr(U&VEF-?(Os@dC6J1cDARmkDBH#j`2byx6{O z^K@62!Q;_BvX0RLIVu($$BxNRxj2p~l2gONHevp3v9{9u#d&R_Eo{RxL=MCXnpVCe zDH{a!`Q^eQk$nyz|Z<=L$j8O3a5Gb`B zi0ky>j6F%*!~lV_01Pp<1N#djY#UuDN#|^ zZHLbQ^$tu&HW#N|>HbZX@2xnQC1&)^Iu+yyYVImFIDx{*?u@h3t~|`vK4xFu8wL_E zU)n_^_TH4*nIRhAqaUQPk2*`J`KIzUviPmW%A;W%bu26L>s*12?#yiSnJuJCuhB&e z(-PG5{{U#7gna3YMU;bKmg=5d)N~y>-uC)E=L{7`c>d1g*n^4NWQheEJreHT1i!dN zHuAZlV?`>hLqXqhj0JOJDJLFl)$OFZ151d#%knP7)tlm|BftUpVV5$-JEDA^*Lvov zFYl){iW1?7Bod;aH)B)8eK-KCC6uTQo@+UhOUV!SZzHt=nrz3!b`--xk81cFj7JIEbko(m zsRpwijd|veFBzn|w75^Kfx4_kNetEdRU(wj4#S8rAYB$|=39L*>H2q>oBlgd$Ld@p zG$CXzTbb?9fJcrzjrlRALczz!`=O!AkJ ztaZ4rCf25Fc-GEWvAHAnO*$x%Vq?%615y2O*2MMb#7W_H&CXdTuf$7$7r=M=a7T91 zZT1)BW}O5+Tk{8;CXbEgpJZXR4Ac+Io@8&aPP=ucStG#<9L~)o+^OOoM)QpJw~HN> zy32aU>hYE(hdCOE=DmWN0rcf@7aI36kYYY-v%b(Zwzkwy8F?$ngS80&)3>m}b+YjB z1r2^lb*)e9mr}IX4Ad=Mw%C765*E%ft(lIq<=Om%EOkhyC2pWuq>?qLUSG3Q)B-od zB1X(E%gl7xW_I;K4E%v4o$5_%U!#0HJ2SV}{{WJ3%lZ>oU0|QkLI;&unt`rCQh0xr z#-NV*IUhj>;L{;x@zs>l8~LsyCK1S=hi)B)LLCch>dD_p^A?R~YOIG+l~6Y_DkzNX zta}b*GM@Cw<{*}_v%q}OxGa^PnBY}(-TwF7%jxy zWQ|mhfofNaj?J*!-z6ikq}nrD)?3RK!^@gtkFH!8<(@zXB|LgBg46HAJ^6GtfzH`~ z)P_n(loNOgI)=B9@FC!Doi8gdZZ&f#ik zSx(}V>6p0X#k(k$P2~Y6nr!0oRn$=)BH~+jPz1iCr9zti$Sce6@0QLyQ8mmD)`@y| zVi9>p=S#SZ->oefsO}ATfKNm72kyy4SILxeTV7VOi&MIeIp2l5Pezf(%ABjY_Z~PP zFLH?*XWyKf{E=&o<^2uH>FH|}2jZxf6aYq~_Z1cTauPgO@BIv5Hnh{oI)wIqO7k7H zvi|_2K?*N6QSe7AfZRuFjo6H5A5X+G-5~}J@srBvH&@@Xx9b3nJ z#vFoxE5*5Dm)cd{n2}sxMBbIatWwkABmhXK(}|58+3|5Nv`$VV*vO2$ciMye&2j6p zzJk+yyKm)B(Vk+nmcLn9W0pBKA(}D^59I-XKXw|ckCRs&zGN>OPc+U-kku45BvzHF z>FzK?mhGI`u8-zPbkg(bC}F*Dh=es%C{yYO?%ej<3{MGO7pDGQ>n-MM}d3UEQ7j4IN4YMAMdx2h+{4#tsAbrgFhtfZovwoNI9+j_49}A`|lQ7z`gGkc7 z_>~)YZ-*>+Wgm9`06S~OH+h}!n_|*@vnP{mos4&h6!J;a;~hXB$G%2K_=hy~qN~+hOy{zJqP~e>7m}=;6Z? zPu|E!BTn?EOq|DvMgBQ87r`=49%}j&uTzj#v zDS2*BIbEtvGQnYLlxdwn3Wlsl&b0&PG9Y)dB{uUXnzXA8ZrDAGazd)J z07)XW9e2fvWd8t~-emgU=$|a<;Kdf7ZV}WP66bPA@g4p4!864>PAJn5^FzL&qiH&f zs9eHjfuF{BB>En!rl!i37f@`Ak)b0~w*+mEH#EvDclg7M>>?g5EL7mD4YKkWl z@qFqw$kiw`^xw*D4sCYo?&5iaEtQm$lykg-AP8C=h+1#$mc;ObBi-|1H;3vHHl?ZD zyeOer$yHIe%zI@q-*Vd>o{4#Vq4|EpMAkgHaU$!Gq`zB=hJsF2Qacr8+Mj+|Id8Xf z0yw#_9WG>^^{+L8%&yMNY4KEwk7K?@1Vqs8W<%OE&pm5?XpSu{Z2fy{HT6GoD(Moh z#7X{g7Ng}(gCtFWe46+`VE%NH57CjnmPvwXpNL^YLPHAoJAOGE;oGk~fJ z9DqBXgCsX>k*$;I7F7skP^ha=qLn3qtxwPJ$dFNX_0P)vF3#fe_I*wW?(JZ*mHh{9 z2v4SIR`IUjWVm)Ego0h=je6W&>9(3etT**?p)m#Qn-+v&Z!* z=DpNpH%FK6SRUjOcii~pQOD`F*oRajDYs+ITa6Eh_sI`QwRsJXiSS+6|(7 z$>&R_xsS{Ch_Tt&GscSl01+VUYJKQ7!x`~e?~1xUX8t$R9lp6E1yzq@PQ&l} zu>gdS_AfDcrq53D`uSR4k5__rBv!ncfec9hDgxh^%70X|+C`@;rrio@p`NaYznpQ5UZAOHnG`oCfECjm$KiVFT2SR2|dk{g={WmMDQmPWpW104-f<0qi-Vb-4c z8C|vsGY=_h*ZxVc)UNFuN9w25j8b(bQC>qvm#Bl`<0)MRz0ws6nU0p!(;K)(nJD)BvOGm8V+ZJ0>LE zGv6;>d5-f>ZAQfp==fGENm*2JWjm-Ke2q?6LjnLSl06$qxX|u37L3iRrN#vsNUOv=@f9tV-zev1C<9uLrgp-{p=t|SEPAj^H2P&ib?MHNvaUe z8<)cxk?@801mujkg>_*q`8@LCd)+fmyYm!CBev9DNRa-?H3p~V0TuS92@?PUfbot^ zJhCIya(+U+J><03cO%%*Qe^P2#PjQ zE!pRtywR!ohTg|e)FTQZ4-l8HJ|GUK!*8OQJF{#AX-7jg9%8YRPt~frgUb)qxF`Vn zjYb6fZA=Afw@JL~(G0Y={aYxH5sw5^1BmN9SUT|zq;bn!K95`V1lG(1T>Ngq6UkV`%qeLdtz zY`-yd$GpEBH9IwFq@F#bJZi&koQ*5<%P3P!g&KH1r{(E9>-ta3w`?J3W#q=Y26pHV zy$)730iEvirL?=#?q7-$1ymLrZPvd={{T)F!7CQ}%gWa&`B!gktF*}~YS9Sgw+G=@ zCaEJYemjCZF*^!rBdg>MSygq*mO@H2r>5kVHK)^GqahAW;q-TreA%IS8%Dn;jJllY zB3Pta@`^W~Zs90l2cq zk*AnyTFNezZSYe@$p83j%7xy;(uXiQfI{MU*Gy zHmwu+hUZb#*hXW7%&puoTAB{TkGrlIniN@1Cuf@ZmtMN_B=a=vJz+GG8nGmTcCW|` zrr8M7F!Jx2{{TnmH~vr5;c11zN#G#tvT@_cp5mRd+?(viao)ov{I>EXx14NsJ8RBQ ztJ~YN+s3A#@gt|bI$O5X`&etOgfspLydD&cwljh<_1xShR7zzI)c2It2s$+-u= z@KFeW`Qt~_r}Ew08oi+$c62nR>VC;MN2Gw-V`hCfv@sTWK zEd^=uC|?@%KKKFIBa=MyKbc3HHBCQA4vlRyg$^oNBds`-){RfcCC*`}ic|dRirdLr z<(;;W{9y`{`Xgml;s{Y*tKYUmi_}>S-bn@B#;JEauEW&4lCFQ4As==SXndJ)ih3@) z=X;Gt{=i+NbFP+=!x$hQKp-@8`&?;3TxYps!%uqiJ&YgWAFXO~Q&5s9?qiK8cZGWU zj-O6B_OB~p5oO`NS<&S4Mw5B2TCiCxt{z5+8o1)5l7Gpd>*I{0Ko(alYVy zR<^9n#bsxV(uD}Ap*?oocvB(`g3OMkhfMNaO6@u2EojW!li^c{Ec!BwX@pU6d0Bl& zR3II=@!Sexb_xO_IW;X1JD8~r>ii-045XTG;%SNK#_ea7QKb!UWUep0Fkxjo3T)($ zaA-a7fn?GM?|oBDun^F;#5yqjb^(W=FyTk&%zH=H&q`h)a0?m17S(0tA2B8>8AQ9 zKo^n6@x1{LKq<&}Cw=<(^vdNkY(Tx=E6a5*u!wDoAyP*k&b9s>3Az67E$b+cN&-JBUfktjomOV{HMLt;N+=1L0 zz_LrhbZM@%L;6P0H6e6hNZ1uM1pJO5(`QO#y0@LLHH}E?vLc;AX@tz`HzWc!r-|R^ zhshj0A4A8ZY5LBGr}@iKaR#Ak>l9K`<9XVpLobhW<582A(y`Q2Rr7eqob|iEFY4Bp zl0>ULM6^4ABo#aSjd#l8cug{d0b~AZ^PB2=j9;RdrG#Q4RJD0H6(s)vvaT`QrZAw- zzc#JxZSi%Pd3}Pss3K* z%jRD)+UXZE1c8Y)>+qtIEAyo(hZx<2xjl2perCCkSk!!jaVQp{f?Eb6zM8)xeZ@DY zj@TePNY|SQ3U0>Od2i0sOKA%IR%pL>Csm{ID{@Cl6S9um;lyYgMEX8y^JU{ct#fAs zpGZhl(Ck1pH2xmh7!8{MPP9dW)0A>I30)Yj+i$k`fx;6{r~d#Y(RA-5YuDF`7o2mb zYVr8NN*efzRDANu)N%Rj*Dd7u`&rbkb=if@>E$AXjFGaM4Zi`E-G;~qq-wDFg2&3x zX!@t_bc?l1m}rBHQ?}By+s5+Fma)$r z!=5saw4;dHz9b*NB^cT|GjBV2Sb3t>4=&nD0Mf)x$agGSJ}%$v06TZ6#(VGGvH{_z zfWD}7NJfic36AwX>iOgksETJ?P5FybyOvvhDu3?bplEx8-oEuG5I1B4 z)4cuX%Oj;j)3S7>8Pi4gw)coB&oa3>t6i2J{>fQ7&R()jZ#OC+*iH^yV3B9%WE zQ%|RJlsm8<<*9jQ-s8!~TDFj+lesIm9&5s`DcX`zSI4dlo{{fhH!qlMq>c;sc@(s9 zzY;0A18uPfX3A!!_J(O-R8$36#y9wS(SDuqg2kKuTWKk(d8Wc*yplO#xsz(BBX{Lq zzyfkn{Ro#QrTN=Z9(9P_>1Cj_(^Y{yY5Vpy1dsBo@Q%RKBfx&9AXDW1O3Uiszm-95 zL^XT0cLO2?b8H5YmavPfqV{ofjIyk1evZFM+a3GTcINtD%UZsj<-G$`k5}V1q$6u( zb|H*U$qF@n*O8}f^2q>WL8eCK@L->qx;5ll7@p&3okG9~BaBt`K5VsN#=G^%WZ0%A z*v_lxi=A`J+HQ>)F}1ppBdPs`H2{0mvoZN(nTXPEn#Hf0ysdYq>Ywr1fTP>WyYh@L z9tY-WTx1NYn9zw)^Hzr5O3}2dxVJi$uPIyYEejGBy}lm4{`AYX()&hE-m9kV!!ML6+@b&jQVSw9qoU&yH300Rza;i{_LjR_$5(<8;bBnOO*4q>W3t6?hgSM_;y_=e|h=D93l zxARDwdbWqHd5c=%b~#>MvV!ffttbm0alf`iZ6eG~1?FuvthCVh9k>leX6amSPdiJIoY&*S+%AQnxW6S#XwpCp$mMtDeD6z)!8C&_k z%I*yZ)0M;>?O{3$%Rtm2^8SwAS-w96lL@93s8&>>wLAdoa@lMe>dPO}{MDyP9GX}L z7!U~%Ao#;@B9-jCh%MuQO8)>J=2UsB>lSx<9)S()3K?#_R(q5OaLV5WPR5kT)pJBm zygt`Zo##vUJIC>*Hl-|L#+n=~7P8hVh}$I5xAd3s#xLpOR7D0=qE znG!6p00KTQHq-BJps{@-8DHx2IA%rrEKc1%SzjHRWe^^>Y$!enOjg?~pP8DS~t4tzOGc(zUyd zRP(-`GaMWA=U!C#jgCr2p+P|19dmk?f0r-~NlM+zJ56rQxRq5F01veu+mB*S`DDca7#+*!i)|v}OzR~qNZ8_rS zeR~9RBx)+hlJe=}N*qAmmNK?DG<%y%l+)FDPb^g7Pmg7*eib+XGG%Q40P?HLtv;=E zW{q7e;cz+kVx%bh&*74Zp=GZj?rY3Cd+WCcO5(9bCJz%6xvQGc_z&TL$Erd_=Bcgc zo(nA$Qr6u9vN8K+lr%mAug?hG4_SlE_FtEJID<|hu5Q*tB!upO{3HSNjq+0NK=nLZ z{Cjv~_D+gxicD_Mw0;!j9O1vsNK zIUpT@Qffc9Bz;UOnQxdak!@`Jt4~s2PwNR#6e>$sS`NH`Ann%#qeQWJwyov0wzadr z(-vWM##Fd=YL=_~JOwr(55-@j49yl|KyyuW3pUd<>3D-2o|}4$QUw*%kEf}`iVLxM zUG=5Jmu`@ONhGQczZG^LjwD?+2~#1{bvyq6Bi;*<3X8_KmIX3=K=Tx>JZN^v-!Pr0 z&31+4yX`?IxJW{x5be+5Bp%>+cfcdHgc1YQbl*Q){$KfG`^vVglHS}h%@nbUuE6Zy zKAB|A3Jqi#8xKA5JgKQoaeJ=vm0lsnB4`|z1AwPMdz_6}rgDuvGU6o@Y7pEH+)W9R z$%oks@Yip=Q`(r%nI9jui^OcSYkgWvh^#@9Usb6Z>{_6Z0q?Llcj?|#$uHG3i~B3& zveaT^kgAnax#Gi>K8o+)auVH~WB_}8+?RSUmSNE+KU=bq(&AAN;>u-=3avVoU7ERM zhQs33wb*#i=yeOq$UKoE1IVdfLY+s;5gIL^y`NwFz_ec`>Ap(x_Y_3^_2hz!C#d0r z0(=K-a>t0@cj{>z-hcULrB}?lQm(+=!CoVFF5vt>E%C%C{29ozug{olSIyVfb3JB{ zi4^^XRH#b#C#gA1L9EcDM-&YoABTVPt4y+}?w(RX z(MP9V&R)@*nvS6frojEgjDXy3u@|~7*&|YoF(aE61ndbraqt7C`8dgG^d_0-jUwCf z?R=?l%MIYVmXVdOC0*39@nio05y@qhh>s$`gKw2A?GM(ZXAICW=e26cPQ|`;83^nd z+ui>FDlV)v+qv)ccs{coV~!ZNYOy?OI}#S49}$w^8;bSY7^U`{X+){OWS`ZWqmi!IwkxI`{cZdIFV3UwluF(#)g@ZjC!~8`X5wE~W-^XksKAhS?b5XE(=g*e8cZ0^leK&OD(!U*ECG4JqlSyfoF8dujumtKr|j+Y z0f--KV2}?_{JVnk`SpJz6#BNaG%M-FD4}4DR*!!XzT@SN^NI35t%2qX&OE1Phf(^T zyvEiWSxI91@gcs+U*#vSwlJ38j^xZ!Ki2%kq4|xZveZ>#wSqNR(M>=hIF=qH@B`_t zM4r6r{{T%ej7UM>`4nAwZ~S+nz+<(BWnWBgV^!XzyOIYYFw4>L`xlJzU#)qtd9q7w zLy9W>TH<@OsZ?ocQl7=l3Gl-`{?Et7F%`4tgoo7? zqR{kWYDV7TxMC5+idDFRRg%y+^q4(=%BO6Mb_t0ElzwBz_mOEHW4e@0b$JQ(TaO+l zKZlE*zA@k0m}L{K1cg)(uE8Dk?# zpP1fYn?%*_yns<|^j$Tc3wT*}kA*%lPulEHe5g#$ zwuq8R6k;lV9>XUiQXvCo1*cfcJAVkCwEUwYzUF z!*){9$ayXss#Fp~4%9ni&zEw>L?+%w(Dc1t$5xM2xd}C?LvF+E7UV&vw?cc2JRpG~ z_}lfcp6^He;qww|dR~l|umnpui@cH@Sq%p54rDy2>_n)xI~SH73V|xU(7nTqkAazq$WiT-{Rt+ zf3X96uNL64jnYD}Nn;w#DEM5CB#MJY1d9ByiB$rm_cI?V-D&Y$t;VX)9C4|5%xlQ{ zi&CK1x9iDt-H8Pz(fE0TSvPw0q#5OAF7+T)s%!W3#|(F}%4dXZ_PhT8C|c^bpQ3!F zDZJ8U7Yzy%U)~~u$|=@|YWK*CH(!m~?DKr{C5Px+zz?8I-meEX?GHn z?xXp!rAMR)bo-=u@3RMBF*KsBKF|4E>@r7_UoJ}ian$VXt!2|Zu_%r;oW&{vOSnAlnnNaj%H3b*($i)_ko};PSet`x7e=;+?q=2hOJ< z5+ZM<6kXfO=BBNyT+CEQ>dwc7b`?H8+u(qx(=Lp&>u_k7u|ehFWYo|`qH6yDyEX)T ziR&1%=rTLA1>HQad~^>xMkwATY9o0ZXJ;- ziaqL0d@@8_NbcRBo=h*vw-EVjUzpy)g{>gDR1exkR^rt5W4FCB5j!91{q)Lgb4R*( zuFk1)1c!^qAR7VANNu~;xQc{G2-7w`94st?oD|G z6rW7=Z8TkD%va@w#l_#%V@BJQDhN~JN#AkP>BtZ$1JtQ};d`fCLv~d6p6OS~hcC zi2h-=`DS5YQ;$R)-#+aD_it4EPlNaUfI@eT7GMgiW~P4Y)r{$((0megT0 z85}%Emdf9>K=+`)CAxexpIg0)%odi{SF!yT z%TD02vVm4F67#J(g2_;N^~lKFz1uK}x0v}R3&E*d&!@sAxVDlhl~{Z=R|cos#|tS} zC(!z>{2!Wd>XYir%UQ6Dt0J1K(~;%wd2~J)NCaP2qvhmGliV5%3Y}ARlOj z9~C{Y1H{jVcfH!&S^0bNmgh&jl)*jq`twpgB4b{j)TrEe(+thVKNdg5@O-OfWl5y; zg;Z1rUC7_B_TU6IiX7SYpB2utrpTVGaf?PD3m!e`QS58?;~=!kEVW5Aza{E^U(_v0 ziKg`yRX+(~MK$&vN4^>FA9Gpe@{JgGjeWGhm41#3>e+hF2l z9&e;{3Fo%_vhxnPV&i!tv@rdm$UrqG*@>o4EWIP%FYp?~N2|nqD#V??htrL%?yte*w_t&qD1aas z>ckpT=?4If@?|3REe^~;*XtM!IzY|11Q01i3-3@psazluJlGPM{!=~8mENi580PdO zv5C(P=apB8r-et|h?k(!<=lmuP1||1O4l^2XwR-2>nuR{NTKKQ-kac+2n5i#&h0$X zTc)2Wszk(rPK28Ezy}pJM6y9GwXC*xI$F4vB8@jLg~0?<+P!fnb|y9od93O~%zEsa z{BW(HmD+cWPr{^;UAug+$6`;pNa)P1v@4Iyw7S+}a)(Mm_P6D3i%QqI1aPMpx-^FrRl@F!sL!!_-wx-Y7R;VdrY}AkIijjA2(~}=ZTs%hB(qr;4fPEo|ytS zC1bE+mX^tCs9}(L-57UJ!khgV#z`#q$$nL{nk(-%`G7Ig?VHg^zu(O0z%R83sW~!s zwlbG<7}GrSb$zX)>a6qF#KI8}-iV}ZgKr98$1UvhAvDf^BehFyBV5#N=WYvrJqV&b z8lRv>9#&&la-CCJb_uM%qiqR@vrK~A0p1S**5Zrnr!Va zrq?1*LU^PyTY6pU-(;TM=yEZY4(2j}UHPl4!{!;{k|~F$AXa4rw;Gf7y~TGP`4i>U zFdHVmQ}Q*Zn6$>U(j+o!u=$IO}#}^S`QBqh9ncSBQ2RH<}=Hx!EZZ9=+AB{fWsMbwj2VUf#)lGFU)&L2bWaz@pNJZ zK6=xZHN6)1G$DtXx^Rusf2~KgSrF-I)(<{I%Av{LAJIJ`1txtZ=2P#(FipeIq{A zCw#IZ$@qSz6AwE4$I$JqJlm*P=;~fL=47IZilCwVYn0Om_ zERZ%Cy`Rl~X}-{Wt*3cT;ZiSAh2_F7&g#S!t$afdpkbe?f#}r{W#yWpMRH@;^q^1y zx*yq*M&$O`ZIPF!lQeVc`i=INYo^OFHc-hU!3*!fnuVwjN|Te3C!avZF$0r4^A?nr zx>cI#t5JO%QbrhYW+)1<`Be4D&+4?>$S;t+_to51S`cZluYc8yMV3c#Nxhz``gGD< zTE|xAuM--cAax&3AlD@#-rMN58yi)&)?r`9~x`cG4jgfYmIN>A;tGTeafO3>tE zao^277{$!?x~{W4_Db>Tf>!j@o&7LPFapZxKmXJDD{Y~*#-BE@vbxW@{XevmwuhBLNOm8Xn40Wp!RO3++mqKBi_b5-X(LUUcJ`!blHMwCaJub>y z3x6>~E{QWHoY2gYx;Er|O1lpLGEnDb1Z;z|^SnBTnJ2bRUr_qUbTNQAFd+8p#P43& z91#bfdh$yD05`msACxbqu|^LAvAMT&I|$eY*!S4|=OSWaDEG1k-ctsp9i$4nYJ&8u zNKXA~ex8{sWmnBL(=6WMUP&3M&mS?$g`JvluX+;?pA~5HaXk8jprY(`{sK`L6cz zd%N3@tw~u4W(Tju*Y)(l!)DuKvOiCG)@Z2h8G7=BJdbwJ#^={$~!h%`6KW&{VUE7AL@xDm-#oM+~dH z2Dm$|taL^lI9yw|ojZYFknx6Q~7`9zLhP>7E-^-6B$)njfElT)0-pkjFzY?O; zwE?YqZ;npk-qr=1eAR8{JGk`ev}f1m7-AqAC8G+NP^CZR42r)V{TBcskWsp!irG zwH_U6JTQV8@LFMOrpKz!6acj*m;x8QTXH|orU=9mjwW&WvE}PuCF-#Gb4>zR)(B*0 z2WB!x31RJ1hw3COeMz6@CmWB;-z`uD3q9JF8xpky8htAN@-^(N8)}JiBz!^>rlsh zAl~IQMUH5*wyUl-nQDMV=yR2Up`cN{dvyFr$d08+q#u(lZFTQ7S*yl9FRTE{IynST zk>7JnvlMq(4W9M+m#9r^=LmJU-DDQ8=%`wJF%{z&`UOYJE5>&yt)4%t%(fbh&E?De zT*D!Ic~0y;5mE2Uu<*%pEQm@pYwcR_-QVeGeypH+(jlU%?^1ov^v7_uZ$g&cbw4S~ z=QRWCRhzI%Jq9HO`HK<7`Gl4-7HdBsIuss`IPT6kavn}DR^{`PG|+gaUm z{{Ti;Rvo}5r}4lnun_?jVWgQ{GMW%e0o;z?jtXQYVN8f(EjqWRKv7aTRQ~|AlOm8) zD|?weRC?2cAxfIl-kue~wd{g6eJTF{IS}yax^9E&BBk)M^kiPfpf@jio}VlkWm7CM z4WArZ#mZ3ShZ23p*jri0mtNWnlXa;u;RnQ4v=78=wfDw6n8>k~nB2C1*xwdS0RSn?F+7h*nOn*G>eK<>tIvd>}D zZ9LB&uXm=;>e)n&r6Z@=6krF918q-yiRFH1>iN<;Wf!pATo94K=7N+QssXKh4nzu~ z+C{UYt=f6hU5ih!j8KnCHK!t3K>u zS)=PwR#F8AfFvJtlM%gwC%e_P+1JYYTWJx@C3zqU%-i~l%(&t|WY;AjXLbjZ$9$Ep zjUQNpSYTMkD3gsla_D?P6%@eudyJ5k?Jkzc0C_SUD@uaq&cj`a`9W8T zcgiv?sfZbVPe>aP2a{5~B%EX}{aDEo1;lGdS$m5rPn<{z_ktUB%*DQ4j zB&%D_f})0mngTpR_sg-J$HitWHTls5r_Ps`lc+4x6iyu~fm9lG->nZ^qB}B)Pro!g z+V`3^f#fTHP48e;l1a+Y)<6J8Jqm&MWU}u&^5@=&a(GG8CA^hwZk3hM)ld$b)O7Ao zQGgC6Rl*rU+evrmX=Kr0sR=<^gYgqy`x;?|C@*yKXOlENM@d_)UHx0>Zf1o!0#vg^ z3}?crLR}F(6a1G^e#Pn4TfCDtZoy zbz`My7Z6*8~Z7k(7KiQmc1wnf%^IiZ%`yEc;Z~WXVtA(2A5Vs4 z2!LAWA#Y*)x@%9%cGWe_MFjequcdEF7C`$J1IKk9`565)Jjcu;M~J+x?m2wcZnWFE zjGBZ2R^3^)ky(p9JJfYi;kH0WiZ;l~m29<(Ei+3xRs6Rx&Dr)Ia3`RT$p|$Rb-Be4(uCGV6C! zTw5L{XLn^iR53eOzUG-+0FUF`*cs23G&?KH$WE&{w6rM7W?{J?ZUsC}!zLm(V>uVB z>Hc(&NYJ${0@*30!Dd8%@omJdPmB}Pss8|DOcV8ke~p5HXLOPeE!%2dPmOdJL343D zk}|Oz*_lZRPx5Ft?~E_)>SGt96Y^GNQ4GBDxitpk&^AKAo z3lZ5%oSy| zwYil;3IfKo58jpe?@X`{Q9eP0iLIW8FPhJpO}Ce|2-yU3&BAtHh2yB;{{SQ_w~j(B zW;6@TeCOqBEfZB&)GiB0bs2cw4(hckDl68br&=21K;hW7M34nC4Hr$1&fZv$%9dqP zTRZa6NYqwyDw>i{S^xnbuMR=QRmdRAgrMW{?XQyU+TUFsrmZwZt)FokAD41`J7kXH zk;G-m9LTwdC^WqBSzfbt|WbO&nCWmv;x z6g3|$>NgrGn^9JGwGqh{+X&q53wN(>zjjVQbz{Z52DXVFbbG>9-C0&rf}|-sk%M5^ z4RT*?9Ft7ZMOGm3K%%6My*^ks40^I(En3{qtHEj)oYOj$PW7kjJ7xIEGFkF{5&84w z%YQN3OXgiGoR?6@v0NbQ3kZP$A}Z_DU35E}89(ztE9$O{AX8UTH=GB#-- zZt@#QmSG*Q9#Sy`l6vy#g0>(sy^1>7n@*60=C^5HEQW+`M&C|I%DzRJ7tK1;ZuKj- zHg*TrGa%%xxukVGhlMJBj1_-jU_~JG-4Q3YpIEq*2w{;{Mg6KUBCFxkrVcJkeaPP} z>o=N3e&OzD_%!@40;wqv-J8c|Yn}qWV$Wtgi!sR+KsTtKF$3 zD^12ZcaGbBIkj>xmvvjF(yt}cEjb3~@e@P#O*TI&xeLFw3Cl4G*!643 zHVl*VgF?2_HCx-gHmxMytL}H9U_qzykMCV&yxpTi{?Ud@Je$sawNd6!yECMOZSq&|_N&{8sYJ|}utB1MyE2V}=w*0h^w z7ecg)_fa5MmYchBC6CYL;gZO09NGT>l=Uq;$@UUMV>-zj$ziVcF9FrJ~MxjaFSSVgk zleXf%KTbBzo`5&0Vq|Ik^2f12Wt3iPFaXqU`UTN%ky8*n_LXyM$@5~ z#7%x8h-hkmnw`&Djpe4D4N?%$*(c9vM%?2u_kAL{@9^6}7DkUY z*hy*{-RiE)!?*|H@IL*qNPSD?oe_0^%h@gUnEeD+ks_V;5)MI`DteN3`DK|l8?=M! zd0&>aXmknXyVOXyv=@^y+89W$`R^Uku(~nk#5$@Ggs4=Fn$Vqtw#NN*C&^s7kflvL40*!q%qurrh|VD z;AAblJ%2#f4vnNwbZwSMr2WNSodqg9N$H6@d)RfhPx)!B!w!$B`Ab!Dklhn9)DWp6 z#=r{q-jp8MDFG+|#PfOgl{EO^`sDH{Yk1L`HtqI@UzXtbS0YEI{mGF+Sn7;szPY#f zVV#(?XZd*pzthtjt&(<5KD!l`p77Z>sq3t1Fe9mATj{1Jxj?jklk`zGsbZS56|@t^ zDx$js%Zc}<-WfPRERN4!{PEP|)8Ugqv58g-bVm-RoTG9|Ejkhjq4&yWEU<&-n+vOp z_?J(axm)^%MT~<|MSdTa=i4V-Uj}ta{01X*Z0%5nG+rcql0^v=@bScKn%SMnt6TX} z@9S1m3CckPk~2_-kPc2M)YX`Q{;o@kgJ0B@`D@JM>bkQL>D);LP_DqOYDGKvysO_NKoB;W^ADSEjAK%t>hx%N22gjR z^~i>BHq5-OE|(sw4Eo$m`gu~VA4ABk0Uk6v{TM}2V#pe>vuP6TLgwxkdZ^mKsc(N{{R;gPXUrL^iv#z)bQUhMs!c~Bq6S+ zZW1{g?cjaux3)lYrK8Q0>E2D$;l8q6S|bg#)IPIUh}bh9_9G<-+1*azSI$?qT7IJ? zq;zmkCLmIv3Mm8JP~>AI4op?N$$xWZ`fPF)f=5v5dUD*Z-rhJ5Fc}DLq2;EM!s#z9 z=eBckXbL7(j6aJi%`?mW)Xq4}*m zH~O8O?VAS@+ar}xPA+T7Ps?rlWKFZ( zUt74)-Ye}XL~D6!u}F-efhykq;=hJK^}gmXh$WZ#)60DPm!(f+D{(SL(*kN@O z3KBkfIrD3d&mytY?k%j|`rz?D55i9L<57SL0koTaDF&};<^KRD+gsh0glb5yBClms zqPg!wUYR_+#8?QMe5F6Aw=&z??P|m;1?a(d{7zRBy)prk*o3uX70S{{B5zN68u%Z2 z{=6~S6WBdo$H}^elXIr&eqal49o>|&M6tN_JunGYYINmVZ@x&x4VZ~7^8TYAob||b zKQL73mhilBP;_Y0fcTOr_hAC%@JSW2Wd*Y>{8m=ew6?}+eHj(%KrxjGzeO8V;KmPL z;=%KWnI#RY-3aJ`T%qBz&=M*sw_L6^18~`59xkwM+>}?2)gynxm`5$y6KI`E%1DA- z%1MSz(W|$^x6j80$d5Gp%R0U0fjVk7QZ#J4o+gBk?a9QLpCn9iZ1=rGO}zZemecbO zMO2Scitac>F`7{!kC!4l4XAu+j|Tw+a^0(z%vTC46G9J${V|X? zX$4D2)J!D7FA*0YWKuUZ6d$g+5ia%Q`jhgjNRLADj-lqgEG(CR%M%8n3lm1)yr+dg z%Q6p09SdByFOjE)!U-=PM=dO&y(RueEH^(PT!cjmWC=Ww8gxV}%_-b|zMtEKg3ESq zKD~JjseL_*nm#g-y>R!}QL+#2AQDqBEU zLF!vj)8agbfZn@eq}iv^fAf#FX+EQOtgCT*XvFKsukOP=E7%$e<)F|f@wld}mP^Zj zEUiDp-e$Qs&%BK^lDQp0V#mXBdU%|#3`H}Z+>o$n%VDcr`GZ@CEv1zHtJ^uFNW0K{ z{CoDs2ZV8SULZvV9p|=2)HH z(fL)PF1deY=!cW)_h~s4*e8kisUIcZCOg9kY#86?YW|e-X|b_l(Z}iA!4RqEM<`#H zdY$TWk-4^G>2J+YMIEg6QGWXME&U6I2E2Vgh=6~|8+{oml6f(%q2yWymvwezlEF)u z)EO7P+n=M~0UI;7|J3rUcYf8n+Y#?LqI2*``Bgcc@%9mmz&R2L|P#DC{=?y6?uF4K~JGY_Z(hQ{{z{#^Nnf zQBxZN2ToM*IVheQM7P=K{{WgAwa=NfdoMH|Fu;<1O<@9`3Ls)ZJ!*RVv6nDUR6;K_ zi$!azC)X7M!x>1yCZD`Jk@pJVw##H5T(x~x<~XedUSO(v(0=ho)ap%1%Ob*Mntprv zkX&kz`Fls0OJ@bTq{1o?CS910{7)ME$;zd<)5V`q`+Hx|BMn^Mv=mPy*~R#vSy zL>#C-XK;Kr%EurXNhyfl`F;&T8?`Y!*2YL!Y&klL7Wbh(*z*K=tIYhpVIBEYx^++9 zAZI7B+iw%!7{-pw!DYJElL~qoRf7h&p>pB3ET`fO2S7UeTL-mae%e+|P3!<~^B)}@a{sNXJ*{L?bRy#D~Ewv#p8noMpzJChT*`z)Z) znjgd4F~;NKu`EACvX1uV3u#D8cYVp*f9aM1(YK0&Hqv#-ytMkCm!U`ZvOxP|33i0f!Thi{`H} zd0I^!{%bev4Wioox`Q<4$r$>^FMpLct4X=$Z@ zdj~l_0lI)g6JFx)YvJ+1sK(>(+!8aZcYEq4B09+>j$c``&Y+>Q_1-klFEdxrw4>WvD9M{$TXM zb}5k@PiOKDnEpVvU(6jwB!AqW~1!BfySjh52Im$qnrGV3(0(DjTaPJc;+Me(bN1DW{x%l%Cf9d#xf) zFL4=1VbY{;PhdJ@9!$7dhm>09Qr4^@(ZGvTdD1A{ZOz%JtVh$o%PoL6vLrTSpPElE zlziCMR&z7U{p5$2B@N4>k7|CrraQ@uMTK8E%VFnVFWKJdypvRo(Jle|EaVhW4}z%c z;!asPaUL&+?O3F~Q}aZ=TDHE?ql`@zQ-v(3$~|kfTgvzV88w-%6Z9_5iHZcN~ocb}A2!^}}t6GFaW)%{}L) z3e>M6HY`ncI8MmBW%d?rb)-wGu?sqau#(gVxdY#7;Fl&x$&h((%D<$1tEp=j$T*`$ z(YDo6jm4|;4fta?`fJUBl@>AOJqmqt_(NqGRSXDKHTY>=kI-^)kEs%q-2VVQQVo9F zPq(mhBU;9(8lI~d9}=H&P;xUCZDMKQV@uO5uHue4c`JZ7+iG|DU;&YmTW?_|zpM*A zD&O}q%f%6ExUR?W!@_r(%_*GI5QP(<#~` z&Np|OToxae3d%05t|yvDjp`gu7K7*m@xk?xOuX4Q1Y zmFexaP7(l7VtKI7%qeWNk6OkEh)G+Lb0L{YrFwO#CWpV#nDOY!JG_@fxVf^OZ){zW zp`i@FXi~J_u?GyLV3FNThq;yLdL8z$CFLYi(Ob!JGdUuPpAj@Y3810FC;&F@M5G4A z4W#Qc>CiW;#z;AO%8Wh|ZnN*P|jMsvm%oMT0MMriAo5TFsHlM6(GTmt5 znGA%*QAR3$FN}EduTI$;W;T0Ik^F?3JkP25uHB=#jsrEMP4*f1^W63XQiHDfY4kDT zwj*h*AWQ!MIBJS!)D}|Oc$K+Oqkiy_`#`OD_8C>sKzPLWv+Gx{b0;Yr$XOVU$EZEV z-7%{TrYw%i`Qf_Oj9W~Qs<<4E+>YPMz5U3`IRH;o zlHqR^r1sYhH>qIn8@yoANsNzEr%KZp>{jiNd4tNo;w!7)F!?esUe-yR#~9p*`Gin> zC|aK^h>1IdMB2Wy=38I)5u)f;%5^y7iaUmN3K~VG`}i7-^3AyHazBEiJ6a#k5DV$1 z(=De_XTV4#KM3_x?HUe%Z$pr;vvgf1UUL?+tU8AS^-~q9BRHvGs-U@V9>n5K_ImVK zn_Z!5>-l{getWf$$9F5TO=`fEXqAXjTuZCVu0hBnO3f0n z9}Q|i-n}+BVt88wHcjNTxwwkw&Jl6?%q<@kPO9gARPft;jqri*A0&=Qcs1#D9a=4a zQi&&TwIz4uLELv0$nvxh`k(S%(XRZv=1(!O4Se9TK_1Acs9C%XKibPO{RN5GzB<$F zZmuD@)h*$yR`Vw^qhMT`ee}xWcz0!V%k>RR>JeD!mMOyFzjn%}a#;K%SH`>Ht1ND} zDd(PTZS?B}(Cj6yqX9*{jnBo*Y#q#iIq%|a%u{zfJpb; zqx{^J%8Cf2$c>Oo}(Fe%l7GWWU z!Hqb8I}lF94<5BS9*vZrOL#Bj7ZS5K777VHKu`ernq=zJ$?w{Ko4><;Q=d!od`>1J zF=rPMP_4SMj=mt1*wkZ?+(7HQP!>zib-P*5l+T-Osv?ofN{jyMH7bqaxxtb(UTmuN zc>T0~pkk~RpvhCYsUrdIY>5M6_0J=D-fb4j_d>m^Y1eVctVdAcb_4xmKu?AlkYh*S z#!<6j=Q_rddFA_0Fk(Hg^jnva` zmP8-DohoUHw|^fMRRW1o?ONoc&eZHvk22~}>o+i6X|V_)f&_#qV?sqqVe=&7eOE7X zfwSAcFK=b@d{%x@lHCW?%Mm?3F%vBrSKg$Yl!p+o)?-hb_3x}peLbzq)LY0>HEsBU z-`9U^t_O%&ZwF>&dt-a$nXNR4{+#-{--`-+z3M7WeP_(GA1L?Dai1(&`)4we=s=4K!?i76C`=IBYYk zWqw7~HF^BU9mbmen7oxkLZ4uuZPkbJ)4zbrFq40eHWTFa56>Ih4I^EFY^fFP#j7mJ zqv5Gz$EW!qQ`;y8%3>|&x{Z=Q6E zFD2XBYMLZVHkYX(jY%J6m+ki3V^NXG9vM<%IG7^q7TOK8H<}1jaVr&(7;b`_(EIek zBA_0ux5yGEp{`l#CRDY%g^-p9Vk!>x=|PjiYjBCZpUnPdnr}M|8%c;jnw%{iyXAQk<*(JC|euStZem-N^i^QrC2R(5=Xb0w+PI|omh^wtrx>2Jz~#9 z9iCOLd0&OQ!C@GZ7>}zVU)U<#8C%1q$09q~##^yFnc%a(w7b!QxkzK?uS$A^Z%Xg{ zFmW;K`T>}#t7%;!e*8H$>p6s*HXJWTt*8y zhbUOJIPKEC{C4rCR5l0_wnyYIHqGWuX6DaaVd!akbpHTXhz=+fSK_aM;%cY)vU>;_ zc_LKsf2*gMuiiTtgiwZ{W@2a=fGo$juIDR&?_@(i)uXoa9={fYrMZtvU)?K_LQ!_* zPjgy(Wa2JJ7fGj{bj7^5vGR?IqJ#s}Jcqx3^|r3=j}=LCAGep)RlnjXey`TYWCzw+t^TLSs_K77EJ4$gHZQe$L?hfE$yOt7J_xk1EeEmTqTT>!b}5Oj?vBT{;hP#8ca@3!%Sv z+Qgd*arxZ&qs;d4Y0;`fB#Rdp6|W=5;rFRKGt;XaJ(xUIdNJ_wAi<~70J%Ih9V?9$6V4iFDG9{*UaBp znjtEc+M$}XC&zEQB1aCzmOoHRVo5GzYnz?OYL^{@5HJ=@ugm&9-p%FD$< zR0F^gSolzN$pL$*?m&6=@AQVVBx@o};k`K}O@p|igHF_?KOCy;raL57T5ZkE(^;ru z(JQm?6#-CGlT4hq3MELZNgm{)RgU6-b+54b%~OJ0fb(k=sCU&pnnPu|5*XdHiITqdHbx=QeBphk?e!QHn1K&(% z>UlP!sLieF6Ui$VgvfXfr*mA0+h)QMuch9@8nm!7%=;m3y*xYZfSNu?5=?IHuJrif z(InsqU){3yEnWV887W8uvW`~z1IoH)n`NSn56oyJnsRQfq5ZE>0r;ulLF4nu$gsQJ zOK3pA-h zl&?Y!N8Oi6gpT_l{LQA?Yw+7O$Pj8Q-kVfVb5h)x_T1AekljR+Z#H?}<@~Xx`A`A% zD=B0g$sC!KQ1t0QNcw%USx*umH+ft-j;-bGChl9v0JYRvxETQ^wWwsS{rcs6ThlF@ zlg-cm9fwRDP25wGvH`h96l(oFGIF9>2qv0U&WWbz@oQ5W>QPQTTVwWQLOv2tk8FF2 z3`$UJA@jDY^?L##k=*)iO_fM3LI>S|M~MdD_Pt&&&`@aJMbwnEmUA(kBk3bL@p^cJ zu=d|2d{P77`1=@>TfDQ*K0+Ez{jb&E_il*tfGI%D@en%D3Y-LQOyedpezMncc~?(g z+|N2J4Ck>Tg1snB2|n2vM~h^EV&0E+<=H&B<-6@SPzZGyrdC8#@c@Q}>+Vk2%W+=x zaRzPVFE83Jlr;-oR7WN4>_9`n{7P7Y19)zveJhiY^ng3VS(xH@t;UUErdr7>J9>;? zZr~``A9{?5Hd{FDCDW{0bQcVqpi<6Ap(L#V8+YEL0T!FKUb?8#{VRDQ;`Y-dk@EXb zUgzinpMDXsYml(Ye>*PL<5SbMdr(D%)KN+4Fz--G_z)|CLZm@Vc_h}FEIRL-EiH6Z z2rjM>9YFXHQvecNHn$)w2fK0wuU*|eLkkxQ#6_-Nbr=ml~EXCf*7 zxvZy{ru6lTT{7)8g{po3D8i22*q<7ZJE4N`1z4Vx6rjY7?VZ!zJk{s;tu#9yCfyTpAa7A!K<66)L&%P+!~^e>nY!|8 z;kTUmyGORvwD$9UlzmT4xXJ!Qx5hel0=x9CLVFSB$%@nn2Cpda(CC z1Fji;%*dqY$@X_Mt;5+z>JLqs!QP<=Zk_iO`gg<)iCOISZ#j8#$3pVOo|~!wHF#%h znFV*Gv6n>oru7~)!ec~vFpkfb-%^_VB=;m9HLl&y@Ex)hZGbW>0*xeB7mR%~MRV*0 z0LjOi9^2+WGHCu_^6jUW4Z59T2Z&oY4kQ98#Gi5lSKQ#3U=1>lFjz^bXco7TucvXO z+AXE*Wb8xNWh1W>N_XESlfUt>W7YEZr>jkObv~Yp#vsZSK;2Aq+<1Jl1AW^>X_xCd z+*+=aX{KDBGesFz1E3W?n^(5RnJLcAeXKy;6KSI=d0O>~29hZk_tX^FR)F~T#PYOV z79C@$&mmbQUs%UpE;s8=wES>0Fnd;xml{=^-g&=%X>%Lf+oO`li*3ktprW2M!bbq# zn^_mIc{QG?aSS&a)x4_|hDKjW55@5c@*V(>^{z zPoz}S!vtl!1GCC}8y=Sx8q*^`2o0R6g`s)?e@3gM^b)X{qk2#xfIt| zkX)^mgbEUh+|7HB_V@S6OX_13UA4To@^9WKfN442e}lfIc7sq1JCbit1p%2j$5mHxnT{?CLEfz0QCTS&}DKQ8*^-1V?ACg z)KTh2sz3u{PZQz}6o;g1I>FR5NhH)4*15Sc$3ny4U`gahxZe}I2%XY9uOjLiErq_Z zc%h=YMPNzlhi+Xu{{T#7VIbSxtRx;<{Jp-hw~(222Oqps+8XlWf0Thw1CCw59p8UO zJzpCV(n%v;Zy_WAdsNgPy8+#>JvYc2PMNH`>H59GTbDu+h@}lH*XYA9r4k357&i0z z?_fu*SK=57gH5PC$;g1uqCd_0Yx#N$YbhO@)CJuuw<>g}Zn&7;F$U7DD&?*1ymTQ;yn@+YAw;=2M0o~O57{uwxqD3I5aqY3gNx0U?aEDQVL z70}?MZ!CVHZk_h+?SoISrc>NAKh5iFEkjdj1d=2dWu$Pl=AxAd!=NR_f7PZ}!+U%7 zYXj2oN3%;rTe#yfMH;BBYgS{_{{T!p+edTW^p7b)ZKr6L!}p7OJVJFl4lmZD!F&6h zvZ#+e{mmftU>cv7(?;_$Y4*v?G^+weHZRMj-KsXGLl-jHDKVeSgLeL6xzqIqtd{c_ zlvnHkXcoQcm4js=%GSP=(YuJqfmjpXK~IM)Cu%9B&}g^sXm6etcm-Kx>_9tH$FJfs z9M<1dXxhz>kTg48U&~Slit0Gxzjqs?vJJ#%zyd>e@X1eYha%Tj@n1Ghe7UV#>N5}_ zSjeFjAV_%wL)d~T;gI@v@@6+lv@J(Xzlu9~BxoM$a}?~PWZDn-XUZ~pUUq43u1i~_r~A0geMV9}_=+EVvU0i%0iy8$gI&{; zP6Wo5jO!;}dsV;SRP26vT!oKwD&6Dr>F=hI9paT!M?>w~=r}mITR65<7mcV$4as6q zb*ALfpQvqwO4PDz8AQ&OzhS{rIczsQK{$hD1U=^CLQB6dYW`B!hYP(XLnXV4GSIK8 z)IIhTuYtinUgjK&=ZJL~Wzq+i{{UuLKCnHH+4@NvU;@J;S?7^#C-T+Ynw`*S<+YF2 zR3??J4FEl9w&NueLfFrGc~orrzN01FV!v3pasU-IVog3<;D|KYvmv{)X#W7*EsSNZ zU1`u*aSL`d^ea40KE(&72tFg*2{di02!G6IAhzKUPQhNpFi%FJjwRBjD-p%^8x9^^#sk{xSGw$U!5(BIJl6Xz;l8hM!6q~x4HSG!?tg|rotb!h?8iSZw2#wT*s)8s zfp|#%tL;=#Q(qEBFl8;7$L4;%Z?s!$%5l4vM~RsCRid7qg+3o_WxE)S-Z^0tv?Uym zOgAoFDo0`OksC9oto~McQMJuxJIj?O$_bI~2?vT$7T{0$VMzLN*iaYm`K7?gU?sj7LgG>)Iy{W+GQke zdw{zRNmj6>VQ5Y-0!gP$rHs$q@wyef2`_n ze0@mW!oW}MaZ^#1jm0vi1FL;jeJ)yj9nC?lPht%yGE{?-%FSoXl3!h0rkh4cVCKwrBq$YQU%MFXa#1=6eO)5HOK}8L zoP0CLzh`mq^zh!N1Z-{I$V!7g{Ls2x8_L=@mzc6l@kerE8*IXypWO5V!+be?as$bP z1@7`+Dp)~1$}RJl?dn6lHw1zy^yENnwR@o(OlH!gW{%wPTGq7d%fN1FgkLrxG|**z zMma3?CqGqZtbp#vh$MXkk%NO|pCz|kwQIY3LWMr8+yd+>Cs`qA2c`(^8(}_ShwJ`E zH@efCIl0+-tyWqdDyF8Rx9x3$JF|?oV}5MB(I606=`p<`732*|P}7keiJ@wC!bK3q zHlgN!Fi)<>ZET#rst>0M4S)ufA2LO8m#F*~lIbFnYgp_hm83{jf+*QlNvJjPt}-Mq zF>h$|ly)9f^7flHnKH1F-5O$mE2}c`a0gx6r?AOp74bwR#PA4ogJGk`uE;()<&r)C zSBj_T~eS&=IUr|i)6KRle}YmAw6 z*)q}etuxF20G7kdSMZ0`Wu2ymM&FcZLV!GKYER1!NN~_cIaB8^GkK2l%~wr*smRki z5n7Y68vqADpmxf{XtUv@#?r}^QNRSCGEfhqpLxT*?4aA;yr*NQ`LjTmOSzFCzP1uH zYu%`{Vo%a2GINg*yK6fapLvf#z0lIvL^Xa0vJ$(hhwDSo@#uZ>JlO+pRs6cVvHt)9 zd4+GIX<~B_0XAxGp!|dGeXz`2L1Fw)BJwTm#+z$>J(XH%B@u@ov&554ylJQP6U~PX>?YEC`DY662^lpk+|2rtC8Q6!#wq+*+i{(e*XYky^$mek*VOUIvz%y zIF={Iov@w}9lMlSHPI{lqmM>kH0gK#VFL3{f=0Q!wqx)-QAW&Ep5V}9hzRa_2_LZj znr_P?LSL(DwpxwV&E}a~J8Ow%m0(ao5lDUr>)a^XnUBLlnNf==@)UY5zKi6)G0tr* zndFf!xC*R=DMlxHZBJry(duI&Vs>N}K2p|yE##k3fl;jIw^k22o#S@orau!^+sAxk zV$Cs}gq~mK2^Uejp7&6iK=K1JhOI)@iZ|~-E0t9O$&cv}rR@U8M*BzN>rd)3!5UM^ zn%zsy4XKKY8qrB7VJGba&~(FeZHz^)xKXJ^WGzty@P~JC)z_}W+L(j89A>TTeqGeH zzbfeW8g8L#&!*~xv@?B$sZzBkV@6ZI$l=OtDK_$-DA>cJEZ%L^-6TjZ6(NF^GD=Nr z{;2$M`arI!JH3C;n)9uzPSOXJ$FB&Ze(*3_6&vykzTL7pBSfMjXP!l_o=g9E+T@A`c+{W9QfLICx`p3^8*mpw1q+IJW zP38zWai=MO8>j}Ar$fq^)$&m;_E8O$&F+Yn5k?szoXc|60MUr2B0F{I*x``6qyt6o zO{rMvIwqavKOifcyXO*@U++W1#GX&wY)2oTj!ITd4&v5#8@_+i%j*7VxP#2*uQ;2T zS_(L=0PF>6l|XR|Y$Xe?`HCbhq#_8ED~2%xQz6(I_z{x3=72Z1oLWaE&j`aBRH&io zNi`=H?keQ^oBnaW;yZn6L8-fqI@Lb2C=?170y@{IB#pcE%b<8c@^SL+^Gn|}*j)LV zdA%fiIA)v~KmfQT{6P5EahUkc4gUZgS#a!zx$@1*1E3?l=1UCIRN*zgMulXPYhb%`3J#XyUi96HCbRs2E75ET%r@c<&=)`W5 z&GpQefAX%sEbbZ?7P{o)lcRmP72L)UgWHWx*yWj^Joo!rfU@x4%B@~2HoeiVA_et` z0+a(WIr_3^*!qEQ!nc5`2PV-z^ew4(uv~b8jn6MQlzfM*;?3ABP zvTauOQ+pt5%a2j{u>z$-H(mGbmRDZy#_7A+lG$oLxHhmx-(FM2I1Tv?z*=?bzm5QG z62J<2E#{i~{{ZFO{G{bBn8iSCh=H4WkLFWff4>H9Q9lGk(Kh+s!$X%))M4`epGTHP zNP|*>vI^608q*;G#jv*T6RX&~<>ajg^6ybwJIR{r;u~mWl|lGSp$L&wj{Jt%I~+#2 z02;W-c@_4jf9HF~)iqWw?#dN9bQBBVHl;E#7tOLeJ-S<8(m$40I%bw@bzye1UM&qw z9JrtW{{WP6&NzfmcFl1+zGu^it3_VNVeC(iJT%*M$`-{{eOi)2o?U?O`QswIR`)N= zPb7Dn6s2+ClFD}^LYI6E|IA&jn zmEu0g1d>Ovu0}B=Zp@2&c9-T$Z!Y<9w5<>Ws@=%(UCsQ8#HytaN`qgRI45E?zcw|y z=1ZG5wz!%l1UEKQy?S;2xe#fvGJQK#iEQreOf@HMudX zDFg~o*XQ230tMJ*6qelu^(T?J$jo^&4z(29>BGZZnMkrHvVm<%#Dmn~_RsSQe_S<8 z0krGOI)vU{zgTly`t88ePaQd1)Q%CdBN0LX{415jHUN{_Dzit)Nl-p2 zZBL^K-;z@&vtmky0P!Sz&PalgY3y-pL(V!zo%vsMiqCFjmMMnC1AV%l`vLc4r~AY1 z4OPNz;?}-fx|30#LY5a<(neP0@bLvIk zkW2NQ9t)MZ*Sxi8&1VVX%;$9~8LC=39f6=9I^}Ve)XIrsk@=45YqZrZz)9mZRUHoX-?rF6YydPhYnkp+?pbLn z0#u*aAk+`zfD#I25FX7oxi_7n({#J&iMW~LkJdCl8EP6I^+?Mj`@+?z`avXa=c=oMr9thsNX*ez3}l*V zx{!pTxIUJWe@BOV<&20JUA)XSbH;hwoe(*=Pe+(p^t>6c_`CXv$ zE{ivvZlnVK=t~8pK>TU9+J68DC)|KL#MWjJ-d7?RK z0+(^!LQpE#z}J4%>y{mXSw#t4hTL5}IJnEJjCEcaPq#|<@fhObzQ%$$;?~QKDa?Cu< z=EJCj)@-erEo0~;H5Fprf7RFwfGCVlB<8};M8DE)HT_C(H58a9r&{t>0*Ba&Q|Ue! zM5}fLkzqAWejQI!4`ElnM2IUOtrju?Aolrp%Q5q1$RvN8S2r3Ag5KrBUd9YkH*Lu( zSH%1HQz0X}AS?%$exJL%w~E`J2%=*mg=&0F*X51cL`LO1i&(n7OFJ`G5w{R}u?D2? z)Y83it1DEWeqmc$M$t*-`C05-tF&kBBR0%F`u-Ui3Mh$YpPy2hytU;*{6vP#hTtdx zPlOD!QT2~-Mj>wu(<~ymwg9&mNZEiQpJ&G)c{Xt(jBvG_uz~yM=+rbNI@7}innkk@ z%dIZzO+qHIQ|iEyOZ&aqxGg~Xe^yKa0QVL-HxEtnUFFB-cc1PxEkN*S{b`mr6bK08 zy*g0tJ@8^2R(~B>Pm)LZz2*rUR+cNsirmb_14aiHMxYG79Vkc77?F0$BG3(~=&-?Q zYi8iyg@iPe-Zvth>C+gU)Wntz=S^1TJHc$Svrjj*8E~5mFhkc@NVfdY^B%LIUas(4Np)wy09}Ie<4*%!ygrX#C<@+8jac+h? zimg?`gYj3{f6r`c(oJ(>x_#7MTGg&JJIMH@M;hRtULxP93Mt#CW04duxddM|eRIoKx)8P0#AP9vRats$ zDoFOHUwla6BovCI@Z5`wgz=dElap=e^)5e`LKv`;>wx_yz z!&8pKNS98De|VQ~BFfu_QaG<6@~IdmZ)O5C@I61v@axwX8vUyoRyw@J2NTJN9FMWY z3unb!Ag{c&`Zh$K)~yGsR;G+AO~F5i`?3IT%ZsDc{OhPChm|z=?3DxDLafm!0**N) zSa&U!WE_F+YKH#+G@J6tZM?S>u=%RnhLx&DlC0zbK!nzn-h|Zs*bk%-SrKdp%)WDL zh%K!&c?h?*EgC5m;((B%o-{SDz5_GQ$(&+shH3UL&asN}l9bzR@dDY7_tC$Vb}-l* zsrd!eBjmgQuNXvRyLzfNaw-W#IbVsnqZ8bvtD~n$y%)*?{P^_wmZPif73s zj^v{&LH37;+<%zMDV3(0rNO&vXx1-Cz=1u>6UmA9Cj__LDB0+rl=@BHv*t@xwjqA3 z-mfAJJ`>RY0Iil{7r%2E^0eRcXIE`UQquIB58Z`CwdEUp4$ADHfz)!S`?9g?Bh84} zl=;`n_Fhu*^_HEbuljw>qe(oj4ND;cl<^fbJ@U-8bUGq3;(X7e-cYBh`whQ^2;Xe2 zoJU_KWqTBwLAIK9=VxFo$llER;*06R`9%mMF9XI0E7IZ zwaEyH18s5~8>Rkaopj4-{FQ3_(g@Zl9EvidKPdtK04UG^dQ&4WSL3;m51Q_DDXlK4 zI};fy?;sVTk^1m-Y>5DFpWEokt7>v-x&dcSB!f-ZFNc9RL@Zhat9R1>0G0k-kIU2D z!RB;cYO!2IIGwY`R082E1=LiD|EN7PEefWZU4lxH`_ghcCI9F3+kQjo>``(-e|4WY_wz)`lw}%7jF9#<4}JAlbpDifI}m?xl7+8-n6Uf`clUv z9l1#TK=~)#k({_d`Gty0`Nw+(vkipWarGozN)xkJ`Z4h~7wTIQ< zK{BxQkM7SvMpz*_)u0sc*Q7WC-DQ^52s#)_mdh{{SgVtV}&=Er^ZD=+$z75AwL} z@?3!xxkYaB4<>nx-`r0Ip~L+#eE|Im24Pe5iq{blJ-$CCQjZB_Kbk&Ehs)Z8wihxV zs<>CPAnsye$t&SNMSgh-GO~No89e_0NAiQ*-hDvI45NX4`YTsq?@S<5F_5Bfe7`NU zDXnS6rnE*WEk^1=r3Z*31V^YKONp0xUOiV=8iXihlF=Y@DJ)NIs^35q@HsFxDoTft zG_=vRy9z#)#>}7dYT_9D!FeW-^tAK@Xr%HerBC1U$epbr zuh@RP$K8U6NeFA3 z%e`9Q!*g-LZkZ%zZh#U^N$uBsj0Wle3E$=9l?ST+UionO&Mis^Ni$m7y!P;)vcY~MQ}Px1F_74v z>T1ccVFjhXn$tt6;+G6n3HZ3kz#Y6v9WX!=Kr2)k59e~nrs}r7T+@GbXKMgn08)-v zmaTUuea-{ClZ@<<{$J=ChPQfR(-WK9p_PqkMP0uhm`3;H%0`{%yZN=4BGqId#~i+o zq&DHjcRmy(ntZE~uB=AM9jswA$l;BHw9Z&5>Ixl)(hYu$NM!UK3(phzTSnCUyowL$ zNTaN(2xN4x5H+IaPc7M!^zX{6xoowY zDYUf`PV=I=c(7`9_XnqjTaR(G35y%OPxDt!i%gR8G-dkj%%Qy`8>?20Ph}#RO`<%X zHu>J_a|%Sv7H(_IbRa5IkJHrQra^;1!Q=hmAk(k6YGTikWY)Tc`r1iv>M)I{Sbou= z6~ao811HiiO}?XR2B9G!5T%sXsK&JU3W1C^=Eg$9(7!4?sdubiUD;0H>Co2VMj#Ny zIWmutD@u365O_%~axX3X%JTzxpII|9pGHO{LD=!-Ks~A0WCwdVigDCq(e#mEV*db3 zl%7t*ZajZj-*J*E0VPk$x^&uy)=ibXmnx94l~aBo3iI2($MNvVbSol(7W1rfBd`)} zxHKbrQy~@P_PgK9+f>r@Mz^>Le|>E8@eNikNeaF-V_m(nQWY)`#1dCqO%IiH3(YD; z<2}r+6pq8s3O{u_xamMMaU^t^B9G z9%0wyT~1K8ZEG}p9<1PwN9Vr%vdm*@M1?$;%{qL;U%9uADsz&N#y<*sWl;>`v`89Q zqAA5wau0F^3H&z6N19WHog|VdDaJKGuR+&vclXJ02^0ZOa{S)X;LNy zko7UN%&~?a>ikvr!I1ic40}A=%r{orHNA(I?GzCjmzqZ1Ta%sr#}oJAB>b6Ou?Da& zeO)!#)r)#nGy`gp`m!r;UDGtH4L8cO`Jc>V{WADT9f4hn$b@pJ9SZGFo=$$4`I@hT z5O-$U1+r`2QJ>73lN+5%c~U#cq}{|2$H_{Lm8Ey>hau!oc`Qddcun`1u65Om%65U8 z&fTS3c==R^l+-BsekQqH+b}7Q`Jc$XUmb_ScU-{{{HLy||E-E{>-@^+JRr~t1z zG+~L|m!H~irl4e}L5SwTd0csZ$vm-Ujru;~aPzfk>wG{3R_vhcN4`g$1Kh-Kx=p6Z z7nk)|bVyu2n{ji-?qwUSax(Jc;Akj4umq4&dml63H<_eQE{|F^k)pSakJ?gdp!?JU zaN-6+lgMtt?(MGZE~OV$6B5kC^xTksTjW79*fNgnlggG?QOytd#PQpk^a) zh|tvb9rEhkhYfRk_vI`xd3RR3I?SA>vqe<&$YpkEI3520lmIGwWbuLr&8(YwKbV^H z%)Volq2Q6EhAGuZD;f~Nhy$vQ=fast9+U2D27}IY{TXz4?ys&HjB&6)RSj<^6O&vGudO7!ExAFPVIA&-x-uYYeGCPIR0pH6v_@@;EI(=4@O zQ%p836t7*8#R@P+N4^U&Y!hU@aMiD2 z)g#m6xcD?@Fxxuy8-mn5Dc|RS?nzjR2^^Of`b=r-NXrT1>96eZC$G5ZPCQ4d@Q1Db zZ|GLGo>kK|>qSpe;TAGt6hNyQn)|aF{{WcCX4ujoUq0wso&KE}78A5Q;o`=i)ky?{ zUko-!Cdzv4mcOQXcgqt`6A5)InFX|QH~;_?6DjZ9gYGeq=+*_BD6h&%t{7c?VssaR zKUUYDUx?Aw3H_=SWgf>RVlgW>k=|-ArRsMnY5c7j=hU9g58^Vhzb3&OPqjS`-ts7J zG}l+X5Tuf#w)@v_el*|bhG*5@gR)toXicbS_x@qInPL`lBucvi&gZAxF_54N39W*0 zVdjgp^0n5FX#t9UezV-ALveqAs~_bn+sAy7l}F;WzUVI_`Fb68{@X=qgllp@m6&Wk zp5FN(9;+!4$?ad3`mK+Y{Nb)>@J!93K?LBdZpG0G)DDJ(npXwf4g5Z6BFOi*=FJ9S z?ev{jLArGha{yQb zjqoi&Dr;I#m1SF$KU|(j z`AHxf$2yt^Q|vn6y99UB4s45Cw7b&uJB>P20u4fQdKfmup(dn`#1UVfMrV*c>&)!( zn;lv|C&NCuq^NyJI?N<$HwudUBc*r#7(~o!yUQ{xq@Gvu{-^ncGs`T7V+?OGUUZ4|P*K9#Tsi#JlW18;GFwx-Zso?ex~ zB{vlxv>RdfU=pyj_J1g9+K!l4$}j*ij`^)KK*K z)59P+T$yo%^54uFg`SCZ<=b!fj%iV6B>`eomQh~aah=cs66W;p%1L0(|jwp%KMNj~q3T+N-HTY2W&+bC}|9VnPAuVwWKU73(GQGQi9SOkC%b+e{R zr}<#{hfKKhE&PTv07#%v1sl6^`bZfPHe-9dzSe21*xSq~QHcv)g!IWo(=KebO;lc6 zCAzP}8H}+M-h_%Dokkg6){0DYo4-o-2||g6&FPSSAS!qN06v&vcVb1pnAGi?K=O>= z;#oPJO+6SBZ<&O@-m_R)qKcMMXdXA$U8mxvWRf`hc0ABR@efVN{SsBov z^bbDy^3m^Lve8fbkByah^lHWkW*z~%E=QTKdsvId^_ebju9)4(%o$gf^`HZ}?@WxS zTVUDrcadj@&l)r68ov*&+sV#0tr<~(-o2D@74SKH&luwF`Fr73`l-=5)&c|KFRNhiFN%t>{Tx*q4>E4m*Rz&2!2DwyMu=*&NMY4?Mcc`r6o z<&7Nb+O3Y85vxfj78Tp!_=)-e!_G+P!eP2tZf+;*A#$O=lvl6Qkc_BOYSTd=mQ~?E zBO8zA{{Su85r!Kd@K}x(dtKlsmbIv0|WIBauf1C#@0*A z-D6l0{*I0pOQR;^<20ZLr(L{hj5!i~tKVxhi_JFjd7xj%rdNv2UO)!GG2)}!kt6TQ z!0LIUa9%YMG-)If7E%~hE8H4y?}A*LVtR!77n1VXEb7l~5iBDe5}TTjkjW5=dtc?v z=AWr)(0Lx#1Wji1RE|`=Bm1=r#3#s~yKJ^5>bbC)Jj>1h05#tEe8+iXs}s#G-{U5u zz|+Tl^Bx2AvJ8^WAMs|950&)TX*HZpI01eqZ`vUD6cr;UA+`5Xn;g-8TF@3rr@iy( zK81pcLMRK6r+_2*N$Z3T5yV)?@}?)P`HpQ%Sz)6u)om{5h>9oxUVLH+dGMwK%m;8fIIyd3A~0zm!0%OCbiMq8V32X{8Dq;`DW zVPp>nkP@h9bvfs(83ynunYrARzZ*Kec*lk~zT({{K&mi3;(&YaDm5Js* z(Au1JH&+BY z$Gs2jD<6mt15Jm%NDYzA>Hh$fzFyOO&93SiMyP#A=8u#HyN2yw4)ymLILC@564{CV zZcTFfd>UQH*s`o>4^{NotA06M@otEc9($yHSuAEZZs|IJA{6^vt5V0>fPAoX`@Y6? zX!`)tT3s}?V-#hAje^vIY4Y!qI}MeWepc(Rr0P&>k|C1P5YaOzp^i5k6#JT=rwKL7 zxfAYZAC!6=+K-yGUobqT3kc+SWN)&qSLTR(DpVXeM*t+S$iqK3eAi{Id9Kd)OH#1f zI*L=YZ@%5h-|))t?tUzz$&dNh%T6^|wW|oq={k(mjP?ac=00nOgpak9C$%t}lvr+_ z+(=|V#*BNAPamTo4tM8=wKsJFjECByqaX!G=7Y_z%RA_fy&T%3asmWpUayu{>afnzJ31&Hm%lCVl5zdsp$S;)x4p8vpj7UrODNu&)QPL zihL@A{NFMYf%1<||3oJxX|-T_cdegr<_W%^IM1ZHFhQ`E_6iM~~>{ z3-NIiIJUdBk~WPEJ~3WhPT-6;e+E+ADS4hN&0;l*R}Q2WVyCw*sz;CQVnV@(>H3Ao zku{H7O>rthl+5qVf9LU{oYDNVo1-z76b zyV|2td||0Z_S&3*P$M%iJM2w+dt^bOO~YrR`8qu;THnR2#$IV5rJIl0mzL!A=~0rg z)dZW_jA<(X^{z`EAy^|FpX1n2cO&@aag79T~Ti-hie=1!@vZQK=fRD0)h-!P1 zMRNKw7J9YJaPmt(Ff6qH04nPbrRkCJgVJ5obIF(ETKI!da)ChCK+Bsoo=NO{m1$`a zD6%3?EKF%h12Xd-)HNgdsgldRc`=VShk0K`v9y}v?_HDziDZ&0fyPH)vKF5JVj6`$9qI4b ze6m>Mw*APIO(%bwdg0TiI!s@@{pAO5h#(vMkKOx1IT8Uzbgq;z&RXG)6a7 z8!@FteoaG?2Kh2Er?P%tTRrWnXj;@QC6tp8CjF@0kAIoz^7qMAjoQgJ!2bX;tmR)Y z$Dl<wgMk-0uQ;2hQxl_ss^$I``}#ofeV?wj{)tx?48K3@4F zR?CjV+q8czrJ#Va!U0PLuFd_bSWD+MA}htfQxb$f~R2__=g zGP}BO>4MbX+#RXmhB2|aneA-TPS@e`ZJpX_VU5vF5;`97ovHq|)%oG#sFnoV$nCWm zb=z4lFCUfc93Bv(ZI4>^*0jgX~WN6_MIAz+rsDF-C6KugDJJ4R&UWt6SJIK)^hk;jeWF!Ws z(Nl)FsTW(lmrh~%TPw6&Zgl{p7p+%;UR*)7Yjz+Fo0T~~E#f5PC7_vE`(mznu; zG}9Jq#=2;3p(IADK+GvkvN0AdBX_XrpP6tC8&b2^=UMMBqgi9PE4*eRKw1v%-`^&A zP(g_&W-G^4I0e_PS(bJycCO zsSJ$FR-kn3yZD2LAv7=X__hOiK5X)5nO9ELTgO~=a-K4!OUlv5we z&pE!0tgfl4{{GtLsWKhF_H+nOXcW!rUFVfvOKad`5>a>De03nxxTkyo8e}DC%@07A zSGtEvwPh1Z$gnBuOMg;EN+F!K4Chp}xPwu*{HDBg2G-@_cU{PKWexKb2ALZ12nVIL z^4%`{>(VSdv8!5W8WAUiO0$w`eNcBa?0A(K5xl+$iLjDu@EO*^=Fu4-wTJhleU&WK z6#oEQt{SN^fm=15)vc%LnGghU{+K^&i6)dC_QQKJFaOv0!9C5!qa|Qy#Ay(ykgAQbYhcHE1bQ{g@*n z85g)ot=;+J;Q4D@MKVtsI!Mb>p&}p^??N(1aZD%x@+lT=0Ɋ}pZ^1cF0UZ@=I4 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 RcfUR~ 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 z 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

    Ny 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

  1. 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

    rqz)0IDRovIPCc9*bMb_!=m1Ak5S$~MH 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

    *v<-3+(|Y%rEOcMXt4m0L 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

    $YL1GJrl~h!SWyYhT~jyitg^;(X+SX_SJzQ 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 diff --git a/unpublishedScripts/marketplace/gameTable/assets/cards/texture_accreditation.txt b/unpublishedScripts/marketplace/gameTable/assets/cards/texture_accreditation.txt deleted file mode 100644 index da1ed56133..0000000000 --- a/unpublishedScripts/marketplace/gameTable/assets/cards/texture_accreditation.txt +++ /dev/null @@ -1 +0,0 @@ -One or more textures on this 3D model have been created with images from CGTextures.com. These images may not be redistributed by default, please visit www.cgtextures.com for more information. \ No newline at end of file diff --git a/winprepareVS22 b/winprepareVS22 new file mode 100644 index 0000000000..0a5a19cc73 --- /dev/null +++ b/winprepareVS22 @@ -0,0 +1,5 @@ +mkdir build +cd build +cmake .. -G "Visual Studio 17 2022" -A x64 +ECHO CMake has finished. +pause From 041c584e04c3db83ce13b07009e10376995b82ea Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Wed, 19 Jun 2024 13:59:24 -0700 Subject: [PATCH 043/109] add ambient light color --- .../src/RenderableZoneEntityItem.cpp | 1 + .../src/AmbientLightPropertyGroup.cpp | 19 ++++++ .../entities/src/AmbientLightPropertyGroup.h | 5 +- .../entities/src/EntityItemProperties.cpp | 5 +- libraries/entities/src/EntityPropertyFlags.h | 66 ++++++++++--------- libraries/graphics/src/graphics/Light.cpp | 20 ++++++ libraries/graphics/src/graphics/Light.h | 10 ++- libraries/graphics/src/graphics/Light.slh | 28 +++++--- libraries/graphics/src/graphics/skybox.slf | 12 +--- libraries/networking/src/udt/PacketHeaders.h | 1 + libraries/render-utils/src/GlobalLight.slh | 8 --- libraries/render-utils/src/LightAmbient.slh | 11 ++-- .../render-utils/src/zone_drawAmbient.slf | 5 +- .../create/assets/data/createAppTooltips.json | 3 + .../html/js/entityProperties.js | 6 ++ 15 files changed, 130 insertions(+), 70 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index ce2dae8d28..52b27cf211 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -309,6 +309,7 @@ void ZoneEntityRenderer::updateAmbientLightFromEntity(const TypedEntityPointer& ambientLight->setOrientation(_lastRotation); // Set the ambient light + ambientLight->setAmbientColor(ColorUtils::toVec3(_ambientLightProperties.getAmbientColor())); ambientLight->setAmbientIntensity(_ambientLightProperties.getAmbientIntensity()); if (_ambientLightProperties.getAmbientURL().isEmpty()) { diff --git a/libraries/entities/src/AmbientLightPropertyGroup.cpp b/libraries/entities/src/AmbientLightPropertyGroup.cpp index c430688113..ebba47a044 100644 --- a/libraries/entities/src/AmbientLightPropertyGroup.cpp +++ b/libraries/entities/src/AmbientLightPropertyGroup.cpp @@ -20,6 +20,7 @@ #include "EntityItemPropertiesMacros.h" const float AmbientLightPropertyGroup::DEFAULT_AMBIENT_LIGHT_INTENSITY = 0.5f; +const glm::u8vec3 AmbientLightPropertyGroup::DEFAULT_COLOR = { 0, 0, 0 }; void AmbientLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, @@ -28,11 +29,13 @@ void AmbientLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& des auto nodeList = DependencyManager::get(); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_LIGHT_INTENSITY, AmbientLight, ambientLight, AmbientIntensity, ambientIntensity); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_AMBIENT_LIGHT_URL, AmbientLight, ambientLight, AmbientURL, ambientURL); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_LIGHT_COLOR, AmbientLight, ambientLight, AmbientColor, ambientColor); } void AmbientLightPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientLight, ambientIntensity, float, setAmbientIntensity); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientLight, ambientURL, QString, setAmbientURL); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientLight, ambientColor, u8vec3Color, setAmbientColor); // legacy property support COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(ambientLightAmbientIntensity, float, setAmbientIntensity, getAmbientIntensity); @@ -41,11 +44,14 @@ void AmbientLightPropertyGroup::copyFromScriptValue(const ScriptValue& object, c void AmbientLightPropertyGroup::merge(const AmbientLightPropertyGroup& other) { COPY_PROPERTY_IF_CHANGED(ambientIntensity); COPY_PROPERTY_IF_CHANGED(ambientURL); + COPY_PROPERTY_IF_CHANGED(ambientColor); } void AmbientLightPropertyGroup::debugDump() const { qCDebug(entities) << " AmbientLightPropertyGroup: ---------------------------------------------"; qCDebug(entities) << " ambientIntensity:" << getAmbientIntensity(); + qCDebug(entities) << " ambientURL:" << getAmbientURL(); + qCDebug(entities) << " ambientColor:" << getAmbientColor(); } void AmbientLightPropertyGroup::listChangedProperties(QList& out) { @@ -55,6 +61,9 @@ void AmbientLightPropertyGroup::listChangedProperties(QList& out) { if (ambientURLChanged()) { out << "ambientLight-ambientURL"; } + if (ambientColorChanged()) { + out << "ambientLight-ambientColor"; + } } bool AmbientLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData, @@ -68,6 +77,7 @@ bool AmbientLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, getAmbientIntensity()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, getAmbientURL()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_COLOR, getAmbientColor()); return true; } @@ -81,9 +91,11 @@ bool AmbientLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& proper READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, float, setAmbientIntensity); READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, QString, setAmbientURL); + READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_COLOR, u8vec3Color, setAmbientColor); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_LIGHT_INTENSITY, AmbientIntensity); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_LIGHT_URL, AmbientURL); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_LIGHT_COLOR, AmbientColor); processedBytes += bytesRead; @@ -95,6 +107,7 @@ bool AmbientLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& proper void AmbientLightPropertyGroup::markAllChanged() { _ambientIntensityChanged = true; _ambientURLChanged = true; + _ambientColorChanged = true; } EntityPropertyFlags AmbientLightPropertyGroup::getChangedProperties() const { @@ -102,6 +115,7 @@ EntityPropertyFlags AmbientLightPropertyGroup::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_INTENSITY, ambientIntensity); CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_URL, ambientURL); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_COLOR, ambientColor); return changedProperties; } @@ -109,6 +123,7 @@ EntityPropertyFlags AmbientLightPropertyGroup::getChangedProperties() const { void AmbientLightPropertyGroup::getProperties(EntityItemProperties& properties) const { COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientLight, AmbientIntensity, getAmbientIntensity); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientLight, AmbientURL, getAmbientURL); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientLight, AmbientColor, getAmbientColor); } bool AmbientLightPropertyGroup::setProperties(const EntityItemProperties& properties) { @@ -116,6 +131,7 @@ bool AmbientLightPropertyGroup::setProperties(const EntityItemProperties& proper SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientLight, AmbientIntensity, ambientIntensity, setAmbientIntensity); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientLight, AmbientURL, ambientURL, setAmbientURL); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientLight, AmbientColor, ambientColor, setAmbientColor); return somethingChanged; } @@ -125,6 +141,7 @@ EntityPropertyFlags AmbientLightPropertyGroup::getEntityProperties(EncodeBitstre requestedProperties += PROP_AMBIENT_LIGHT_INTENSITY; requestedProperties += PROP_AMBIENT_LIGHT_URL; + requestedProperties += PROP_AMBIENT_LIGHT_COLOR; return requestedProperties; } @@ -141,6 +158,7 @@ void AmbientLightPropertyGroup::appendSubclassData(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, getAmbientIntensity()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, getAmbientURL()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_COLOR, getAmbientColor()); } int AmbientLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, @@ -153,6 +171,7 @@ int AmbientLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned c READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, float, setAmbientIntensity); READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, QString, setAmbientURL); + READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_COLOR, u8vec3Color, setAmbientColor); return bytesRead; } diff --git a/libraries/entities/src/AmbientLightPropertyGroup.h b/libraries/entities/src/AmbientLightPropertyGroup.h index 67597d1713..020af68b11 100644 --- a/libraries/entities/src/AmbientLightPropertyGroup.h +++ b/libraries/entities/src/AmbientLightPropertyGroup.h @@ -37,6 +37,8 @@ class ScriptValue; * @property {string} ambientURL="" - A cube map image that defines the color of the light coming from each direction. If * "" then the entity's {@link Entities.Skybox|Skybox} url property value is used, unless that also is "" in which * case the entity's ambientLightMode property is set to "inherit". + * @property {Color} ambientColor=0,0,0 - Sets the color of the ambient light if ambientURL is "", otherwise modifies the + * color of the cube map image. */ class AmbientLightPropertyGroup : public PropertyGroup { public: @@ -87,9 +89,10 @@ public: bool& somethingChanged) override; static const float DEFAULT_AMBIENT_LIGHT_INTENSITY; - + static const glm::u8vec3 DEFAULT_COLOR; DEFINE_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, AmbientIntensity, ambientIntensity, float, DEFAULT_AMBIENT_LIGHT_INTENSITY); DEFINE_PROPERTY_REF(PROP_AMBIENT_LIGHT_URL, AmbientURL, ambientURL, QString, ""); + DEFINE_PROPERTY_REF(PROP_AMBIENT_LIGHT_COLOR, AmbientColor, ambientColor, glm::u8vec3, DEFAULT_COLOR); }; #endif // hifi_AmbientLightPropertyGroup_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 54d20301ae..f51d15b8b0 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2967,8 +2967,9 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_KEYLIGHT_SHADOW_MAX_DISTANCE, KeyLight, keyLight, ShadowMaxDistance, shadowMaxDistance, 1.0f, 250.0f); } { // Ambient light - ADD_GROUP_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_INTENSITY, AmbientLight, ambientLight, Intensity, intensity); - ADD_GROUP_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_URL, AmbientLight, ambientLight, URL, url); + ADD_GROUP_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_INTENSITY, AmbientLight, ambientLight, AmbientIntensity, ambientIntensity); + ADD_GROUP_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_URL, AmbientLight, ambientLight, AmbientURL, ambientURL); + ADD_GROUP_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_COLOR, AmbientLight, ambientLight, AmbientColor, ambientColor); } { // Skybox ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index ae8928b68e..ff7c4134fa 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -173,6 +173,7 @@ enum EntityPropertyList { PROP_DERIVED_37, PROP_DERIVED_38, PROP_DERIVED_39, + PROP_DERIVED_40, PROP_AFTER_LAST_ITEM, @@ -284,44 +285,45 @@ enum EntityPropertyList { // Ambient light PROP_AMBIENT_LIGHT_INTENSITY = PROP_DERIVED_6, PROP_AMBIENT_LIGHT_URL = PROP_DERIVED_7, + PROP_AMBIENT_LIGHT_COLOR = PROP_DERIVED_8, // Skybox - PROP_SKYBOX_COLOR = PROP_DERIVED_8, - PROP_SKYBOX_URL = PROP_DERIVED_9, + PROP_SKYBOX_COLOR = PROP_DERIVED_9, + PROP_SKYBOX_URL = PROP_DERIVED_10, // Haze - PROP_HAZE_RANGE = PROP_DERIVED_10, - PROP_HAZE_COLOR = PROP_DERIVED_11, - PROP_HAZE_GLARE_COLOR = PROP_DERIVED_12, - PROP_HAZE_ENABLE_GLARE = PROP_DERIVED_13, - PROP_HAZE_GLARE_ANGLE = PROP_DERIVED_14, - PROP_HAZE_ALTITUDE_EFFECT = PROP_DERIVED_15, - PROP_HAZE_CEILING = PROP_DERIVED_16, - PROP_HAZE_BASE_REF = PROP_DERIVED_17, - PROP_HAZE_BACKGROUND_BLEND = PROP_DERIVED_18, - PROP_HAZE_ATTENUATE_KEYLIGHT = PROP_DERIVED_19, - PROP_HAZE_KEYLIGHT_RANGE = PROP_DERIVED_20, - PROP_HAZE_KEYLIGHT_ALTITUDE = PROP_DERIVED_21, + PROP_HAZE_RANGE = PROP_DERIVED_11, + PROP_HAZE_COLOR = PROP_DERIVED_12, + PROP_HAZE_GLARE_COLOR = PROP_DERIVED_13, + PROP_HAZE_ENABLE_GLARE = PROP_DERIVED_14, + PROP_HAZE_GLARE_ANGLE = PROP_DERIVED_15, + PROP_HAZE_ALTITUDE_EFFECT = PROP_DERIVED_16, + PROP_HAZE_CEILING = PROP_DERIVED_17, + PROP_HAZE_BASE_REF = PROP_DERIVED_18, + PROP_HAZE_BACKGROUND_BLEND = PROP_DERIVED_19, + PROP_HAZE_ATTENUATE_KEYLIGHT = PROP_DERIVED_20, + PROP_HAZE_KEYLIGHT_RANGE = PROP_DERIVED_21, + PROP_HAZE_KEYLIGHT_ALTITUDE = PROP_DERIVED_22, // Bloom - PROP_BLOOM_INTENSITY = PROP_DERIVED_22, - PROP_BLOOM_THRESHOLD = PROP_DERIVED_23, - PROP_BLOOM_SIZE = PROP_DERIVED_24, - PROP_FLYING_ALLOWED = PROP_DERIVED_25, - PROP_GHOSTING_ALLOWED = PROP_DERIVED_26, - PROP_FILTER_URL = PROP_DERIVED_27, - PROP_KEY_LIGHT_MODE = PROP_DERIVED_28, - PROP_AMBIENT_LIGHT_MODE = PROP_DERIVED_29, - PROP_SKYBOX_MODE = PROP_DERIVED_30, - PROP_HAZE_MODE = PROP_DERIVED_31, - PROP_BLOOM_MODE = PROP_DERIVED_32, + PROP_BLOOM_INTENSITY = PROP_DERIVED_23, + PROP_BLOOM_THRESHOLD = PROP_DERIVED_24, + PROP_BLOOM_SIZE = PROP_DERIVED_25, + PROP_FLYING_ALLOWED = PROP_DERIVED_26, + PROP_GHOSTING_ALLOWED = PROP_DERIVED_27, + PROP_FILTER_URL = PROP_DERIVED_28, + PROP_KEY_LIGHT_MODE = PROP_DERIVED_29, + PROP_AMBIENT_LIGHT_MODE = PROP_DERIVED_30, + PROP_SKYBOX_MODE = PROP_DERIVED_31, + PROP_HAZE_MODE = PROP_DERIVED_32, + PROP_BLOOM_MODE = PROP_DERIVED_33, // Avatar priority - PROP_AVATAR_PRIORITY = PROP_DERIVED_33, + PROP_AVATAR_PRIORITY = PROP_DERIVED_34, // Screen-sharing - PROP_SCREENSHARE = PROP_DERIVED_34, + PROP_SCREENSHARE = PROP_DERIVED_35, // Audio - PROP_REVERB_ENABLED = PROP_DERIVED_35, - PROP_REVERB_TIME = PROP_DERIVED_36, - PROP_REVERB_WET_LEVEL = PROP_DERIVED_37, - PROP_LISTENER_ZONES = PROP_DERIVED_38, - PROP_LISTENER_ATTENUATION_COEFFICIENTS = PROP_DERIVED_39, + PROP_REVERB_ENABLED = PROP_DERIVED_36, + PROP_REVERB_TIME = PROP_DERIVED_37, + PROP_REVERB_WET_LEVEL = PROP_DERIVED_38, + PROP_LISTENER_ZONES = PROP_DERIVED_39, + PROP_LISTENER_ATTENUATION_COEFFICIENTS = PROP_DERIVED_40, // Polyvox PROP_VOXEL_VOLUME_SIZE = PROP_DERIVED_0, diff --git a/libraries/graphics/src/graphics/Light.cpp b/libraries/graphics/src/graphics/Light.cpp index fb14783b4e..abdfd52617 100644 --- a/libraries/graphics/src/graphics/Light.cpp +++ b/libraries/graphics/src/graphics/Light.cpp @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 1/26/2014. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 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 @@ -156,6 +157,10 @@ void Light::setSpotExponent(float exponent) { _lightSchemaBuffer.edit().irradiance.falloffSpot = exponent; } +void Light::setAmbientColor(vec3 color) { + _ambientSchemaBuffer.edit().color = color; +} + void Light::setAmbientIntensity(float intensity) { _ambientSchemaBuffer.edit().intensity = intensity; } @@ -187,3 +192,18 @@ void Light::setTransform(const glm::mat4& transform) { } } +const Light::AmbientSchemaBuffer& Light::getAmbientSchemaBuffer() { + auto blend = 0.0f; + if (getAmbientMap() && getAmbientMap()->isDefined()) { + blend = 0.5f; + + // If pitch black neutralize the color + if (glm::all(glm::equal(getAmbientColor(), glm::vec3(0.0f)))) { + blend = 1.0f; + } + } + + _ambientSchemaBuffer.edit().blend = blend; + + return _ambientSchemaBuffer; +} diff --git a/libraries/graphics/src/graphics/Light.h b/libraries/graphics/src/graphics/Light.h index 81a6fddbd3..cb09e17786 100644 --- a/libraries/graphics/src/graphics/Light.h +++ b/libraries/graphics/src/graphics/Light.h @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 12/10/2014. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 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 @@ -143,7 +144,9 @@ public: void setSpotExponent(float exponent); float getSpotExponent() const { return _lightSchemaBuffer->irradiance.falloffSpot; } - // If the light has an ambient (Indirect) component, then the Ambientintensity can be used to control its contribution to the lighting + // If the light has an ambient (Indirect) component, then the AmbientColor and AmbientIntensity can be used to control its contribution to the lighting + void setAmbientColor(vec3 color); + vec3 getAmbientColor() const { return _ambientSchemaBuffer->color; } void setAmbientIntensity(float intensity); float getAmbientIntensity() const { return _ambientSchemaBuffer->intensity; } @@ -169,6 +172,9 @@ public: class AmbientSchema { public: + vec3 color { 0.0f }; + float blend { 0.0f }; + float intensity { 0.0f }; float mapNumMips { 0.0f }; float spare1; @@ -182,7 +188,7 @@ public: using AmbientSchemaBuffer = gpu::StructBuffer; const LightSchemaBuffer& getLightSchemaBuffer() const { return _lightSchemaBuffer; } - const AmbientSchemaBuffer& getAmbientSchemaBuffer() const { return _ambientSchemaBuffer; } + const AmbientSchemaBuffer& getAmbientSchemaBuffer(); // This also updates the blend factor to make sure it's current protected: diff --git a/libraries/graphics/src/graphics/Light.slh b/libraries/graphics/src/graphics/Light.slh index c00bfea6a2..d8ce697b98 100644 --- a/libraries/graphics/src/graphics/Light.slh +++ b/libraries/graphics/src/graphics/Light.slh @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 1/25/14. // Copyright 2013 High Fidelity, Inc. +// Copyright 2024 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 @@ -15,7 +16,16 @@ <@include graphics/LightVolume.shared.slh@> <@include graphics/LightIrradiance.shared.slh@> -// NOw lets define Light +// FIXME: For legacy reasons, when blend is 0.5, this is equivalent to: +// color * texel +// It should actually be: +// mix(color, texel, blend) +// and the blend factor should be user controlled +vec3 applySkyboxColorMix(vec3 texel, vec3 color, float blend) { + return mix(vec3(1.0), texel, float(blend > 0.0)) * mix(vec3(1.0), color, float(blend < 1.0)); +} + +// Now let's define Light struct Light { LightVolume volume; LightIrradiance irradiance; @@ -35,17 +45,19 @@ vec3 getLightIrradiance(Light l) { return lightIrradiance_getIrradiance(l.irradi // Light Ambient struct LightAmbient { - vec4 _ambient; + vec4 _ambientColor; + vec4 _ambientInfo; SphericalHarmonics _ambientSphere; mat4 transform; }; SphericalHarmonics getLightAmbientSphere(LightAmbient l) { return l._ambientSphere; } - -float getLightAmbientIntensity(LightAmbient l) { return l._ambient.x; } -bool getLightHasAmbientMap(LightAmbient l) { return l._ambient.y > 0.0; } -float getLightAmbientMapNumMips(LightAmbient l) { return l._ambient.y; } +vec3 getLightAmbientColor(LightAmbient l) { return l._ambientColor.xyz; } +float getLightAmbientBlend(LightAmbient l) { return l._ambientColor.w; } +float getLightAmbientIntensity(LightAmbient l) { return l._ambientInfo.x; } +bool getLightHasAmbientMap(LightAmbient l) { return l._ambientInfo.y > 0.0; } +float getLightAmbientMapNumMips(LightAmbient l) { return l._ambientInfo.y; } <@func declareLightBuffer(N)@> @@ -71,10 +83,6 @@ Light getKeyLight() { <@endfunc@> - - - - <@func declareLightAmbientBuffer(N)@> <@if N@> diff --git a/libraries/graphics/src/graphics/skybox.slf b/libraries/graphics/src/graphics/skybox.slf index 4ae53a657f..2d5ba4d26b 100755 --- a/libraries/graphics/src/graphics/skybox.slf +++ b/libraries/graphics/src/graphics/skybox.slf @@ -5,17 +5,18 @@ // // Created by Sam Gateau on 5/5/2015. // Copyright 2015 High Fidelity, Inc. +// Copyright 2024 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 graphics/ShaderConstants.h@> +<@include graphics/Light.slh@> <@if HIFI_USE_FORWARD@> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> - <@include graphics/Light.slh@> <$declareLightBuffer()$> <@include graphics/Haze.slh@> @@ -35,17 +36,10 @@ layout(location=0) in vec3 _normal; layout(location=0) out vec4 _fragColor; void main(void) { - // FIXME: For legacy reasons, when skybox.color.a is 0.5, this is equivalent to: - // skyboxColor * skyboxTexel - // It should actually be: - // mix(skyboxColor, skyboxTexel, skybox.color.a) - // and the blend factor should be user controlled - vec3 normal = normalize(_normal); vec3 skyboxTexel = texture(cubeMap, normal).rgb; vec3 skyboxColor = skybox.color.rgb; - _fragColor = vec4(mix(vec3(1.0), skyboxTexel, float(skybox.color.a > 0.0)) * - mix(vec3(1.0), skyboxColor, float(skybox.color.a < 1.0)), 1.0); + _fragColor = vec4(applySkyboxColorMix(skyboxTexel, skyboxColor, skybox.color.a), 1.0); <@if HIFI_USE_FORWARD@> // FIXME: either move this elsewhere or give it access to isHazeEnabled() (which is in render-utils/LightingModel.slh) diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index b4fb5a172d..3ea6abb078 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -296,6 +296,7 @@ enum class EntityVersion : PacketVersion { AudioZones, AnimationSmoothFrames, ProceduralParticles, + AmbientColor, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/render-utils/src/GlobalLight.slh b/libraries/render-utils/src/GlobalLight.slh index de8702ea8c..0593abe375 100644 --- a/libraries/render-utils/src/GlobalLight.slh +++ b/libraries/render-utils/src/GlobalLight.slh @@ -44,14 +44,6 @@ <@endfunc@> -<@func declareEvalAmbientGlobalColor()@> -vec3 evalAmbientGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, float roughness) { - <$prepareGlobalLight(position, normal)$> - color += albedo * getLightColor(light) * obscurance * getLightAmbientIntensity(lightAmbient); - return color; -} -<@endfunc@> - <@func declareEvalAmbientSphereGlobalColor(supportScattering)@> <$declareLightingAmbient(1, _SCRIBE_NULL, _SCRIBE_NULL, $supportScattering$)$> diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index cb76a8e545..81c672292a 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -2,6 +2,7 @@ // // Created by Sam Gateau on 7/5/16. // Copyright 2016 High Fidelity, Inc. +// Copyright 2024 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 @@ -74,7 +75,7 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface, ve } <@endif@> - return specularLight; + return applySkyboxColorMix(specularLight, getLightAmbientColor(ambient), getLightAmbientBlend(ambient)); } <@endfunc@> @@ -104,8 +105,9 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, surface.ndotv, surface.roughness); - diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * - sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), ambientSpaceSurfaceNormal).xyz; + diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * + applySkyboxColorMix(sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), ambientSpaceSurfaceNormal).xyz, + getLightAmbientColor(ambient), getLightAmbientBlend(ambient)); // Specular highlight from ambient vec3 ambientSpaceLightDir = -reflect(ambientSpaceSurfaceEyeDir, ambientSpaceSurfaceNormal); @@ -120,7 +122,8 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie obscurance = min(obscurance, ambientOcclusion); // Diffuse from ambient - diffuse = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), ambientSpaceLowNormal).xyz; + diffuse = applySkyboxColorMix(sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), ambientSpaceLowNormal).xyz, + getLightAmbientColor(ambient), getLightAmbientBlend(ambient)); // Scattering ambient specular is the same as non scattering for now // TODO: we should use the same specular answer as for direct lighting diff --git a/libraries/render-utils/src/zone_drawAmbient.slf b/libraries/render-utils/src/zone_drawAmbient.slf index d780fd0de2..07eab08a35 100644 --- a/libraries/render-utils/src/zone_drawAmbient.slf +++ b/libraries/render-utils/src/zone_drawAmbient.slf @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 5/16/17. // Copyright 2017 High Fidelity, Inc. +// Copyright 2024 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 @@ -41,10 +42,10 @@ void main(void) { vec3 ambientMap = evalSkyboxLight(fragNormal, lod).xyz; vec3 ambientSH = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(lightAmbient), fragNormal).xyz; - // vec3 ambient = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(lightAmbient), fragNormal).xyz; - // _fragColor = vec4( 0.5 * (fragNormal + vec3(1.0)), 1.0); + // _fragColor = vec4( 0.5 * (fragNormal + vec3(1.0)), 1.0); vec3 color = mix(ambientSH, ambientMap, float(sphereUV.x > 0.0)); + color = applySkyboxColorMix(color, getLightAmbientColor(lightAmbient), getLightAmbientBlend(lightAmbient)); color = color * 1.0 - base.w + base.xyz * base.w; const float INV_GAMMA_22 = 1.0 / 2.2; diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index 076f47f5dd..c4503763a3 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -116,6 +116,9 @@ "ambientLight.ambientURL": { "tooltip": "A cube map image that defines the color of the light coming from each direction." }, + "ambientLight.ambientColor": { + "tooltip": "If the URL is blank, this changes the color of the ambient light, otherwise it modifies the color of the ambient map." + }, "hazeMode": { "tooltip": "Configures the haze in the scene." }, diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 1eade2703d..5a1df0feee 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -449,6 +449,12 @@ const GROUPS = [ options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, propertyID: "ambientLightMode", }, + { + label: "Ambient Color", + type: "color", + propertyID: "ambientLight.ambientColor", + showPropertyRule: { "ambientLightMode": "enabled" }, + }, { label: "Ambient Intensity", type: "number-draggable", From 0d0a0a7a157a7313ab033a54719222e60a217399 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 21 Jun 2024 13:07:43 -0700 Subject: [PATCH 044/109] fix create issue --- libraries/entities/src/AmbientLightPropertyGroup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/AmbientLightPropertyGroup.cpp b/libraries/entities/src/AmbientLightPropertyGroup.cpp index ebba47a044..ba03b8d2da 100644 --- a/libraries/entities/src/AmbientLightPropertyGroup.cpp +++ b/libraries/entities/src/AmbientLightPropertyGroup.cpp @@ -29,7 +29,7 @@ void AmbientLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& des auto nodeList = DependencyManager::get(); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_LIGHT_INTENSITY, AmbientLight, ambientLight, AmbientIntensity, ambientIntensity); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_AMBIENT_LIGHT_URL, AmbientLight, ambientLight, AmbientURL, ambientURL); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_LIGHT_COLOR, AmbientLight, ambientLight, AmbientColor, ambientColor); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_AMBIENT_LIGHT_COLOR, AmbientLight, ambientLight, AmbientColor, ambientColor, u8vec3Color); } void AmbientLightPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { From 261cb94ff0f2ac15fa2215522c563e0d78e0da3a Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 21 Jun 2024 14:40:40 -0700 Subject: [PATCH 045/109] fix create issue --- scripts/system/create/assets/data/createAppTooltips.json | 4 ++++ .../create/entityProperties/html/js/entityProperties.js | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index 076f47f5dd..23a351af72 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -8,6 +8,10 @@ "shapeAlpha": { "tooltip": "The opacity of the entity between 0.0 fully transparent and 1.0 completely opaque." }, + "shapeUnlit": { + "tooltip": "If enabled, the entity will not be lit by the keylight or local lights.", + "jsPropertyName": "unlit" + }, "text": { "tooltip": "The text to display on the entity." }, diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 4bbacf8af3..5cd56fc642 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -181,7 +181,8 @@ const GROUPS = [ { label: "Unlit", type: "bool", - propertyID: "unlit", + propertyID: "shapeUnlit", + propertyName: "unlit" } ] }, From 6317bd45acfab769f5b9887f5b8de213e5204422 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Tue, 25 Jun 2024 16:14:15 -0700 Subject: [PATCH 046/109] add tonemapping props to zones, wip ambient occlusion --- .../src/RenderableZoneEntityItem.cpp | 96 +++++- .../src/RenderableZoneEntityItem.h | 34 +- .../src/AmbientOcclusionPropertyGroup.cpp | 300 ++++++++++++++++++ .../src/AmbientOcclusionPropertyGroup.h | 95 ++++++ .../entities/src/EntityItemProperties.cpp | 75 +++++ libraries/entities/src/EntityItemProperties.h | 6 + libraries/entities/src/EntityPropertyFlags.h | 101 ++++-- .../entities/src/TonemappingPropertyGroup.cpp | 167 ++++++++++ .../entities/src/TonemappingPropertyGroup.h | 87 +++++ libraries/entities/src/ZoneEntityItem.cpp | 159 ++++++---- libraries/entities/src/ZoneEntityItem.h | 72 +++-- .../graphics/src/graphics/AmbientOcclusion.h | 59 ++++ libraries/graphics/src/graphics/Tonemapping.h | 35 ++ libraries/networking/src/udt/PacketHeaders.h | 1 + libraries/octree/src/OctreePacketData.h | 4 + .../render-utils/src/AmbientOcclusionEffect.h | 4 +- .../src/AmbientOcclusionStage.cpp | 56 ++++ .../render-utils/src/AmbientOcclusionStage.h | 84 +++++ .../src/AssembleLightingStageTask.cpp | 9 +- .../src/AssembleLightingStageTask.h | 5 +- libraries/render-utils/src/BloomStage.h | 22 -- .../src/DeferredLightingEffect.cpp | 21 +- .../render-utils/src/DeferredLightingEffect.h | 13 +- .../render-utils/src/RenderDeferredTask.cpp | 8 +- .../render-utils/src/RenderForwardTask.cpp | 5 +- .../src/ToneMapAndResampleTask.cpp | 29 +- .../render-utils/src/ToneMapAndResampleTask.h | 48 ++- .../render-utils/src/TonemappingStage.cpp | 56 ++++ libraries/render-utils/src/TonemappingStage.h | 84 +++++ .../render-utils/src/UpdateSceneTask.cpp | 4 + libraries/render-utils/src/ZoneRenderer.cpp | 11 + libraries/render-utils/src/toneMapping.slf | 10 +- .../shared/src/AmbientOcclusionTechnique.cpp | 24 ++ .../shared/src/AmbientOcclusionTechnique.h | 38 +++ libraries/shared/src/TonemappingCurve.cpp | 26 ++ libraries/shared/src/TonemappingCurve.h | 42 +++ .../utilities/render/luci/ToneMapping.qml | 25 +- .../create/assets/data/createAppTooltips.json | 9 + .../html/js/entityProperties.js | 53 +++- 39 files changed, 1754 insertions(+), 223 deletions(-) create mode 100644 libraries/entities/src/AmbientOcclusionPropertyGroup.cpp create mode 100644 libraries/entities/src/AmbientOcclusionPropertyGroup.h create mode 100644 libraries/entities/src/TonemappingPropertyGroup.cpp create mode 100644 libraries/entities/src/TonemappingPropertyGroup.h create mode 100644 libraries/graphics/src/graphics/AmbientOcclusion.h create mode 100644 libraries/graphics/src/graphics/Tonemapping.h create mode 100644 libraries/render-utils/src/AmbientOcclusionStage.cpp create mode 100644 libraries/render-utils/src/AmbientOcclusionStage.h create mode 100644 libraries/render-utils/src/TonemappingStage.cpp create mode 100644 libraries/render-utils/src/TonemappingStage.h create mode 100644 libraries/shared/src/AmbientOcclusionTechnique.cpp create mode 100644 libraries/shared/src/AmbientOcclusionTechnique.h create mode 100644 libraries/shared/src/TonemappingCurve.cpp create mode 100644 libraries/shared/src/TonemappingCurve.h diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 52b27cf211..9bc6de6627 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -68,6 +68,20 @@ void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity _bloomIndex = INVALID_INDEX; } } + + if (_tonemappingStage) { + if (!TonemappingStage::isIndexInvalid(_tonemappingIndex)) { + _tonemappingStage->removeTonemapping(_tonemappingIndex); + _tonemappingIndex = INVALID_INDEX; + } + } + + if (_ambientOcclusionStage) { + if (!AmbientOcclusionStage::isIndexInvalid(_ambientOcclusionIndex)) { + _ambientOcclusionStage->removeAmbientOcclusion(_ambientOcclusionIndex); + _ambientOcclusionIndex = INVALID_INDEX; + } + } } void ZoneEntityRenderer::doRender(RenderArgs* args) { @@ -96,6 +110,16 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { assert(_bloomStage); } + if (!_tonemappingStage) { + _tonemappingStage = args->_scene->getStage(); + assert(_tonemappingStage); + } + + if (!_ambientOcclusionStage) { + _ambientOcclusionStage = args->_scene->getStage(); + assert(_ambientOcclusionStage); + } + { // Sun if (_needSunUpdate) { if (LightStage::isIndexInvalid(_sunIndex)) { @@ -149,6 +173,24 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { } } + { + if (_needTonemappingUpdate) { + if (TonemappingStage::isIndexInvalid(_tonemappingIndex)) { + _tonemappingIndex = _tonemappingStage->addTonemapping(_tonemapping); + } + _needTonemappingUpdate = false; + } + } + + { + if (_needAmbientOcclusionUpdate) { + if (AmbientOcclusionStage::isIndexInvalid(_ambientOcclusionIndex)) { + _ambientOcclusionIndex = _ambientOcclusionStage->addAmbientOcclusion(_ambientOcclusion); + } + _needAmbientOcclusionUpdate = false; + } + } + if (_visible) { // Finally, push the lights visible in the frame // @@ -184,6 +226,18 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { } else if (_bloomMode == COMPONENT_MODE_ENABLED) { _bloomStage->_currentFrame.pushBloom(_bloomIndex); } + + if (_tonemappingMode == COMPONENT_MODE_DISABLED) { + _tonemappingStage->_currentFrame.pushTonemapping(0); // Use the fallback tonemapping for "off" + } else if (_tonemappingMode == COMPONENT_MODE_ENABLED) { + _tonemappingStage->_currentFrame.pushTonemapping(_tonemappingIndex); + } + + if (_ambientOcclusionMode == COMPONENT_MODE_DISABLED) { + _ambientOcclusionStage->_currentFrame.pushAmbientOcclusion(INVALID_INDEX); + } else if (_ambientOcclusionMode == COMPONENT_MODE_ENABLED) { + _ambientOcclusionStage->_currentFrame.pushAmbientOcclusion(_ambientOcclusionIndex); + } } CullTest::_containingZones.insert(_entityID); @@ -216,6 +270,8 @@ void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe bool skyboxChanged = entity->skyboxPropertiesChanged() || proceduralUserDataChanged; bool hazeChanged = entity->hazePropertiesChanged(); bool bloomChanged = entity->bloomPropertiesChanged(); + bool tonemappingChanged = entity->tonemappingPropertiesChanged(); + bool ambientOcclusionChanged = entity->ambientOcclusionPropertiesChanged(); entity->resetRenderingPropertiesChanged(); if (transformChanged) { @@ -255,6 +311,16 @@ void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe updateBloomFromEntity(entity); } + if (tonemappingChanged) { + _tonemappingProperties = entity->getTonemappingProperties(); + updateTonemappingFromEntity(entity); + } + + if (ambientOcclusionChanged) { + _ambientOcclusionProperties = entity->getAmbientOcclusionProperties(); + updateAmbientOcclusionFromEntity(entity); + } + bool visuallyReady = true; uint32_t skyboxMode = entity->getSkyboxMode(); if (skyboxMode == COMPONENT_MODE_ENABLED && !_skyboxTextureURL.isEmpty()) { @@ -275,7 +341,9 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint entity->ambientLightPropertiesChanged() || entity->hazePropertiesChanged() || entity->bloomPropertiesChanged() || - entity->skyboxPropertiesChanged()) { + entity->skyboxPropertiesChanged() || + entity->tonemappingPropertiesChanged() || + entity->ambientOcclusionPropertiesChanged()) { return true; } @@ -360,6 +428,32 @@ void ZoneEntityRenderer::updateBloomFromEntity(const TypedEntityPointer& entity) bloom->setBloomSize(_bloomProperties.getBloomSize()); } +void ZoneEntityRenderer::updateTonemappingFromEntity(const TypedEntityPointer& entity) { + _tonemappingMode = (ComponentMode)entity->getTonemappingMode(); + + const auto& tonemapping = editTonemapping(); + + tonemapping->setCurve(_tonemappingProperties.getCurve()); + tonemapping->setExposure(_tonemappingProperties.getExposure()); +} + +void ZoneEntityRenderer::updateAmbientOcclusionFromEntity(const TypedEntityPointer& entity) { + _ambientOcclusionMode = (ComponentMode)entity->getAmbientOcclusionMode(); + + const auto& ambientOcclusion = editAmbientOcclusion(); + + ambientOcclusion->setTechnique(_ambientOcclusionProperties.getTechnique()); + ambientOcclusion->setJitter(_ambientOcclusionProperties.getJitter()); + ambientOcclusion->setResolutionLevel(_ambientOcclusionProperties.getResolutionLevel()); + ambientOcclusion->setEdgeSharpness(_ambientOcclusionProperties.getEdgeSharpness()); + ambientOcclusion->setBlurRadius(_ambientOcclusionProperties.getBlurRadius()); + ambientOcclusion->setAORadius(_ambientOcclusionProperties.getAORadius()); + ambientOcclusion->setAOObscuranceLevel(_ambientOcclusionProperties.getAOObscuranceLevel()); + ambientOcclusion->setAOFalloffAngle(_ambientOcclusionProperties.getAOFalloffAngle()); + ambientOcclusion->setAONumSamples(_ambientOcclusionProperties.getAONumSamples()); + ambientOcclusion->setSSAONumSpiralTurns(_ambientOcclusionProperties.getSSAONumSpiralTurns()); +} + void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& entity) { _skyboxMode = (ComponentMode)entity->getSkyboxMode(); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index d2ee90b1e4..78694514d2 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include "RenderableEntityItem.h" #include @@ -47,6 +49,8 @@ private: void updateHazeFromEntity(const TypedEntityPointer& entity); void updateKeyBackgroundFromEntity(const TypedEntityPointer& entity); void updateBloomFromEntity(const TypedEntityPointer& entity); + void updateTonemappingFromEntity(const TypedEntityPointer& entity); + void updateAmbientOcclusionFromEntity(const TypedEntityPointer& entity); void updateAmbientMap(); void updateSkyboxMap(); void setAmbientURL(const QString& ambientUrl); @@ -61,6 +65,8 @@ private: graphics::SkyboxPointer editSkybox() { return editBackground()->getSkybox(); } graphics::HazePointer editHaze() { _needHazeUpdate = true; return _haze; } graphics::BloomPointer editBloom() { _needBloomUpdate = true; return _bloom; } + graphics::TonemappingPointer editTonemapping() { _needTonemappingUpdate = true; return _tonemapping; } + graphics::AmbientOcclusionPointer editAmbientOcclusion() { _needAmbientOcclusionUpdate = true; return _ambientOcclusion; } glm::vec3 _lastPosition; glm::vec3 _lastDimensions; @@ -73,12 +79,16 @@ private: const graphics::SunSkyStagePointer _background { std::make_shared() }; const graphics::HazePointer _haze { std::make_shared() }; const graphics::BloomPointer _bloom { std::make_shared() }; + const graphics::TonemappingPointer _tonemapping { std::make_shared() }; + const graphics::AmbientOcclusionPointer _ambientOcclusion { std::make_shared() }; ComponentMode _keyLightMode { COMPONENT_MODE_INHERIT }; ComponentMode _ambientLightMode { COMPONENT_MODE_INHERIT }; ComponentMode _skyboxMode { COMPONENT_MODE_INHERIT }; ComponentMode _hazeMode { COMPONENT_MODE_INHERIT }; ComponentMode _bloomMode { COMPONENT_MODE_INHERIT }; + ComponentMode _tonemappingMode { COMPONENT_MODE_INHERIT }; + ComponentMode _ambientOcclusionMode { COMPONENT_MODE_INHERIT }; indexed_container::Index _sunIndex { LightStage::INVALID_INDEX }; indexed_container::Index _ambientIndex { LightStage::INVALID_INDEX }; @@ -92,27 +102,37 @@ private: BloomStagePointer _bloomStage; BloomStage::Index _bloomIndex { BloomStage::INVALID_INDEX }; - bool _needUpdate{ true }; - bool _needSunUpdate{ true }; - bool _needAmbientUpdate{ true }; - bool _needBackgroundUpdate{ true }; - bool _needHazeUpdate{ true }; + TonemappingStagePointer _tonemappingStage; + TonemappingStage::Index _tonemappingIndex { TonemappingStage::INVALID_INDEX }; + + AmbientOcclusionStagePointer _ambientOcclusionStage; + AmbientOcclusionStage::Index _ambientOcclusionIndex { AmbientOcclusionStage::INVALID_INDEX }; + + bool _needUpdate { true }; + bool _needSunUpdate { true }; + bool _needAmbientUpdate { true }; + bool _needBackgroundUpdate { true }; + bool _needHazeUpdate { true }; bool _needBloomUpdate { true }; + bool _needTonemappingUpdate { true }; + bool _needAmbientOcclusionUpdate { true }; KeyLightPropertyGroup _keyLightProperties; AmbientLightPropertyGroup _ambientLightProperties; HazePropertyGroup _hazeProperties; SkyboxPropertyGroup _skyboxProperties; BloomPropertyGroup _bloomProperties; + TonemappingPropertyGroup _tonemappingProperties; + AmbientOcclusionPropertyGroup _ambientOcclusionProperties; // More attributes used for rendering: QString _ambientTextureURL; NetworkTexturePointer _ambientTexture; - bool _pendingAmbientTexture{ false }; + bool _pendingAmbientTexture { false }; QString _skyboxTextureURL; NetworkTexturePointer _skyboxTexture; - bool _pendingSkyboxTexture{ false }; + bool _pendingSkyboxTexture { false }; QString _proceduralUserData; }; diff --git a/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp b/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp new file mode 100644 index 0000000000..b5617cabb0 --- /dev/null +++ b/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp @@ -0,0 +1,300 @@ +// +// AmbientOcclusionPropertyGroup.cpp +// libraries/entities/src +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "AmbientOcclusionPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" +#include "EntityItemPropertiesMacros.h" + +inline void addAmbientOcclusionTechnique(QHash& lookup, AmbientOcclusionTechnique technique) { lookup[AmbientOcclusionTechniqueHelpers::getNameForAmbientOcclusionTechnique(technique)] = technique; } +const QHash stringToAmbientOcclusionTechniqueLookup = [] { + QHash toReturn; + addAmbientOcclusionTechnique(toReturn, AmbientOcclusionTechnique::SSAO); + addAmbientOcclusionTechnique(toReturn, AmbientOcclusionTechnique::HBAO); + return toReturn; +}(); +QString AmbientOcclusionPropertyGroup::getTechniqueAsString() const { return AmbientOcclusionTechniqueHelpers::getNameForAmbientOcclusionTechnique(_technique); } +void AmbientOcclusionPropertyGroup::setTechniqueFromString(const QString& technique) { + auto techniqueItr = stringToAmbientOcclusionTechniqueLookup.find(technique.toLower()); + if (techniqueItr != stringToAmbientOcclusionTechniqueLookup.end()) { + _technique = techniqueItr.value(); + _techniqueChanged = true; + } +} + +void AmbientOcclusionPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_OCCLUSION_TECHNIQUE, AmbientOcclusion, ambientOcclusion, Technique, technique, getTechniqueAsString); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_JITTER, AmbientOcclusion, ambientOcclusion, Jitter, jitter); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, AmbientOcclusion, ambientOcclusion, ResolutionLevel, resolutionLevel); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, AmbientOcclusion, ambientOcclusion, EdgeSharpness, edgeSharpness); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, AmbientOcclusion, ambientOcclusion, BlurRadius, blurRadius); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AmbientOcclusion, ambientOcclusion, AORadius, aoRadius); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AmbientOcclusion, ambientOcclusion, AOObscuranceLevel, aoObscuranceLevel); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AmbientOcclusion, ambientOcclusion, AOFalloffAngle, aoFalloffAngle); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, AmbientOcclusion, ambientOcclusion, AONumSamples, aoNumSamples); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, AmbientOcclusion, ambientOcclusion, SSAONumSpiralTurns, ssaoNumSpiralTurns); +} + +void AmbientOcclusionPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientOcclusion, technique, Technique); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, jitter, bool, setJitter); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, resolutionLevel, uint8_t, setResolutionLevel); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, edgeSharpness, float, setEdgeSharpness); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, blurRadius, uint8_t, setBlurRadius); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoRadius, float, setAORadius); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoObscuranceLevel, float, setAOObscuranceLevel); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoFalloffAngle, float, setAOFalloffAngle); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoNumSamples, uint8_t, setAONumSamples); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, ssaoNumSpiralTurns, float, setSSAONumSpiralTurns); +} + +void AmbientOcclusionPropertyGroup::merge(const AmbientOcclusionPropertyGroup& other) { + COPY_PROPERTY_IF_CHANGED(technique); + COPY_PROPERTY_IF_CHANGED(jitter); + COPY_PROPERTY_IF_CHANGED(resolutionLevel); + COPY_PROPERTY_IF_CHANGED(edgeSharpness); + COPY_PROPERTY_IF_CHANGED(blurRadius); + COPY_PROPERTY_IF_CHANGED(aoRadius); + COPY_PROPERTY_IF_CHANGED(aoObscuranceLevel); + COPY_PROPERTY_IF_CHANGED(aoFalloffAngle); + COPY_PROPERTY_IF_CHANGED(aoNumSamples); + COPY_PROPERTY_IF_CHANGED(ssaoNumSpiralTurns); +} + +void AmbientOcclusionPropertyGroup::debugDump() const { + qCDebug(entities) << " AmbientOcclusionPropertyGroup: ---------------------------------------------"; + qCDebug(entities) << " Technique:" << getTechniqueAsString(); + qCDebug(entities) << " Jitter:" << getJitter(); + qCDebug(entities) << " ResolutionLevel:" << getResolutionLevel(); + qCDebug(entities) << " EdgeSharpness:" << getEdgeSharpness(); + qCDebug(entities) << " BlurRadius:" << getBlurRadius(); + qCDebug(entities) << " AORadius:" << getAORadius(); + qCDebug(entities) << " AOObscuranceLevel:" << getAOObscuranceLevel(); + qCDebug(entities) << " AOFalloffAngle:" << getAOFalloffAngle(); + qCDebug(entities) << " AONumSamples:" << getAONumSamples(); + qCDebug(entities) << " SSAONumSpiralTurns:" << getSSAONumSpiralTurns(); +} + +void AmbientOcclusionPropertyGroup::listChangedProperties(QList& out) { + if (techniqueChanged()) { + out << "ambientOcclusion-technique"; + } + if (jitterChanged()) { + out << "ambientOcclusion-jitter"; + } + if (resolutionLevelChanged()) { + out << "ambientOcclusion-resolutionLevel"; + } + if (edgeSharpnessChanged()) { + out << "ambientOcclusion-edgeSharpness"; + } + if (blurRadiusChanged()) { + out << "ambientOcclusion-blurRadius"; + } + if (aoRadiusChanged()) { + out << "ambientOcclusion-aoRadius"; + } + if (aoObscuranceLevelChanged()) { + out << "ambientOcclusion-aoObscuranceLevel"; + } + if (aoFalloffAngleChanged()) { + out << "ambientOcclusion-aoFalloffAngle"; + } + if (aoNumSamplesChanged()) { + out << "ambientOcclusion-aoNumSamples"; + } + if (ssaoNumSpiralTurnsChanged()) { + out << "ambientOcclusion-ssaoNumSpiralTurns"; + } +} + +bool AmbientOcclusionPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_TECHNIQUE, (uint32_t)getTechnique()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, getJitter()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, getResolutionLevel()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, getEdgeSharpness()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, getBlurRadius()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, getAORadius()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, getAOObscuranceLevel()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, getAOFalloffAngle()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, getAONumSamples()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, getSSAONumSpiralTurns()); + + return true; +} + +bool AmbientOcclusionPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_TECHNIQUE, AmbientOcclusionTechnique, setTechnique); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, bool, setJitter); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, uint8_t, setResolutionLevel); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, float, setEdgeSharpness); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, uint8_t, setBlurRadius); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, float, setAORadius); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, float, setAOObscuranceLevel); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, float, setAOFalloffAngle); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, uint8_t, setAONumSamples); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, float, setSSAONumSpiralTurns); + + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_TECHNIQUE, Technique); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_JITTER, Jitter); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, ResolutionLevel); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, EdgeSharpness); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, BlurRadius); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AORadius); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AOObscuranceLevel); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AOFalloffAngle); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, AONumSamples); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, SSAONumSpiralTurns); + + processedBytes += bytesRead; + + Q_UNUSED(somethingChanged); + + return true; +} + +void AmbientOcclusionPropertyGroup::markAllChanged() { + _techniqueChanged = true; + _jitterChanged = true; + _resolutionLevelChanged = true; + _edgeSharpnessChanged = true; + _blurRadiusChanged = true; + _aoRadiusChanged = true; + _aoObscuranceLevelChanged = true; + _aoFalloffAngleChanged = true; + _aoNumSamplesChanged = true; + _ssaoNumSpiralTurnsChanged = true; +} + +EntityPropertyFlags AmbientOcclusionPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_TECHNIQUE, technique); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_JITTER, jitter); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, resolutionLevel); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, edgeSharpness); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, blurRadius); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_RADIUS, aoRadius); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, aoObscuranceLevel); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, aoFalloffAngle); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, aoNumSamples); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, ssaoNumSpiralTurns); + + return changedProperties; +} + +void AmbientOcclusionPropertyGroup::getProperties(EntityItemProperties& properties) const { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, Technique, getTechnique); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, Jitter, getJitter); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, ResolutionLevel, getResolutionLevel); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, EdgeSharpness, getEdgeSharpness); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, BlurRadius, getBlurRadius); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AORadius, getAORadius); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AOObscuranceLevel, getAOObscuranceLevel); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AOFalloffAngle, getAOFalloffAngle); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AONumSamples, getAONumSamples); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, SSAONumSpiralTurns, getSSAONumSpiralTurns); +} + +bool AmbientOcclusionPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, Technique, technique, setTechnique); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, Jitter, jitter, setJitter); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, ResolutionLevel, resolutionLevel, setResolutionLevel); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, EdgeSharpness, edgeSharpness, setEdgeSharpness); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, BlurRadius, blurRadius, setBlurRadius); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AORadius, aoRadius, setAORadius); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AOObscuranceLevel, aoObscuranceLevel, setAOObscuranceLevel); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AOFalloffAngle, aoFalloffAngle, setAOFalloffAngle); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AONumSamples, aoNumSamples, setAONumSamples); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, SSAONumSpiralTurns, ssaoNumSpiralTurns, setSSAONumSpiralTurns); + + return somethingChanged; +} + +EntityPropertyFlags AmbientOcclusionPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + + requestedProperties += PROP_AMBIENT_OCCLUSION_TECHNIQUE; + requestedProperties += PROP_AMBIENT_OCCLUSION_JITTER; + requestedProperties += PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL; + requestedProperties += PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS; + requestedProperties += PROP_AMBIENT_OCCLUSION_BLUR_RADIUS; + requestedProperties += PROP_AMBIENT_OCCLUSION_AO_RADIUS; + requestedProperties += PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL; + requestedProperties += PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE; + requestedProperties += PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES; + requestedProperties += PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS; + + return requestedProperties; +} + +void AmbientOcclusionPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_TECHNIQUE, (uint32_t)getTechnique()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, getJitter()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, getResolutionLevel()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, getEdgeSharpness()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, getBlurRadius()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, getAORadius()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, getAOObscuranceLevel()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, getAOFalloffAngle()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, getAONumSamples()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, getSSAONumSpiralTurns()); +} + +int AmbientOcclusionPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_TECHNIQUE, AmbientOcclusionTechnique, setTechnique); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, bool, setJitter); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, uint8_t, setResolutionLevel); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, float, setEdgeSharpness); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, uint8_t, setBlurRadius); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, float, setAORadius); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, float, setAOObscuranceLevel); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, float, setAOFalloffAngle); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, uint8_t, setAONumSamples); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, float, setSSAONumSpiralTurns); + + return bytesRead; +} diff --git a/libraries/entities/src/AmbientOcclusionPropertyGroup.h b/libraries/entities/src/AmbientOcclusionPropertyGroup.h new file mode 100644 index 0000000000..9a44c5c8f0 --- /dev/null +++ b/libraries/entities/src/AmbientOcclusionPropertyGroup.h @@ -0,0 +1,95 @@ +// +// AmbientOcclusionPropertyGroup.h +// libraries/entities/src +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_AmbientOcclusionPropertyGroup_h +#define hifi_AmbientOcclusionPropertyGroup_h + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +/*@jsdoc + * AmbientOcclusion is defined by the following properties: + * @typedef {object} Entities.AmbientOcclusion + * @property {AmbientOcclusionTechnique} technique="ssao" - The AO technique used. + * TODO + */ +class AmbientOcclusionPropertyGroup : public PropertyGroup { +public: + // EntityItemProperty related helpers + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, + ScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const override; + virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; + + void merge(const AmbientOcclusionPropertyGroup& other); + + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; + + virtual bool appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; + + // EntityItem related helpers + // methods for getting/setting all properties of an entity + virtual void getProperties(EntityItemProperties& propertiesOut) const override; + + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_OCCLUSION_TECHNIQUE, Technique, technique, AmbientOcclusionTechnique, AmbientOcclusionTechnique::SSAO); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, Jitter, jitter, bool, false); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, ResolutionLevel, resolutionLevel, uint8_t, 2); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, EdgeSharpness, edgeSharpness, float, 1.0f); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, BlurRadius, blurRadius, uint8_t, 4); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AORadius, aoRadius, float, 1.0f); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AOObscuranceLevel, aoObscuranceLevel, float, 0.5f); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AOFalloffAngle, aoFalloffAngle, float, 0.25f); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, AONumSamples, aoNumSamples, uint8_t, 32); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, SSAONumSpiralTurns, ssaoNumSpiralTurns, float, 7.0f); +}; + +#endif // hifi_AmbientOcclusionPropertyGroup_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 7c1e05c949..4f70d88d8b 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -48,6 +48,8 @@ BloomPropertyGroup EntityItemProperties::_staticBloom; ZoneAudioPropertyGroup EntityItemProperties::_staticAudio; KeyLightPropertyGroup EntityItemProperties::_staticKeyLight; AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight; +TonemappingPropertyGroup EntityItemProperties::_staticTonemapping; +AmbientOcclusionPropertyGroup EntityItemProperties::_staticAmbientOcclusion; GrabPropertyGroup EntityItemProperties::_staticGrab; PulsePropertyGroup EntityItemProperties::_staticPulse; RingGizmoPropertyGroup EntityItemProperties::_staticRing; @@ -90,6 +92,8 @@ void EntityItemProperties::debugDump() const { getAmbientLight().debugDump(); getBloom().debugDump(); getAudio().debugDump(); + getTonemapping().debugDump(); + getAmbientOcclusion().debugDump(); getGrab().debugDump(); qCDebug(entities) << " changed properties..."; @@ -254,6 +258,8 @@ QString EntityItemProperties::getKeyLightModeAsString() const { return getCompon QString EntityItemProperties::getAmbientLightModeAsString() const { return getComponentModeAsString(_ambientLightMode); } QString EntityItemProperties::getHazeModeAsString() const { return getComponentModeAsString(_hazeMode); } QString EntityItemProperties::getBloomModeAsString() const { return getComponentModeAsString(_bloomMode); } +QString EntityItemProperties::getTonemappingModeAsString() const { return getComponentModeAsString(_tonemappingMode); } +QString EntityItemProperties::getAmbientOcclusionModeAsString() const { return getComponentModeAsString(_ambientOcclusionMode); } void EntityItemProperties::setSkyboxModeFromString(const QString& mode) { auto modeItr = stringToComponentMode.find(mode.toLower()); if (modeItr != stringToComponentMode.end()) { @@ -289,6 +295,20 @@ void EntityItemProperties::setBloomModeFromString(const QString& mode) { _bloomModeChanged = true; } } +void EntityItemProperties::setTonemappingModeFromString(const QString& mode) { + auto modeItr = stringToComponentMode.find(mode.toLower()); + if (modeItr != stringToComponentMode.end()) { + _tonemappingMode = modeItr.value(); + _tonemappingModeChanged = true; + } +} +void EntityItemProperties::setAmbientOcclusionModeFromString(const QString& mode) { + auto modeItr = stringToComponentMode.find(mode.toLower()); + if (modeItr != stringToComponentMode.end()) { + _ambientOcclusionMode = modeItr.value(); + _ambientOcclusionModeChanged = true; + } +} inline void addAvatarPriorityMode(QHash& lookup, AvatarPriorityMode mode) { lookup[AvatarPriorityModeHelpers::getNameForAvatarPriorityMode(mode)] = mode; } const QHash stringToAvatarPriority = [] { @@ -625,6 +645,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { changedProperties += _haze.getChangedProperties(); changedProperties += _bloom.getChangedProperties(); changedProperties += _audio.getChangedProperties(); + changedProperties += _tonemapping.getChangedProperties(); + changedProperties += _ambientOcclusion.getChangedProperties(); CHECK_PROPERTY_CHANGE(PROP_FLYING_ALLOWED, flyingAllowed); CHECK_PROPERTY_CHANGE(PROP_GHOSTING_ALLOWED, ghostingAllowed); CHECK_PROPERTY_CHANGE(PROP_FILTER_URL, filterURL); @@ -635,6 +657,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_BLOOM_MODE, bloomMode); CHECK_PROPERTY_CHANGE(PROP_AVATAR_PRIORITY, avatarPriority); CHECK_PROPERTY_CHANGE(PROP_SCREENSHARE, screenshare); + CHECK_PROPERTY_CHANGE(PROP_TONEMAPPING_MODE, tonemappingMode); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_MODE, ambientOcclusionMode); // Polyvox CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); @@ -1547,6 +1571,12 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @property {Entities.ZoneAudio} audio - The audio properties of the zone. * + * @property {Entities.ComponentMode} tonemappingMode="inherit" - Configures the tonemapping in the zone. + * @property {Entities.Tonemapping} tonemapping - The tonemapping properties of the zone. + * + * @property {Entities.ComponentMode} ambientOcclusionMode="inherit" - Configures the ambient occlusion in the zone. + * @property {Entities.AmbientOcclusion} ambientOcclusion - The ambient occlusion properties of the zone. + * * @property {boolean} flyingAllowed=true - true if visitors can fly in the zone; false if they * cannot. Only works for domain entities. * @property {boolean} ghostingAllowed=true - true if visitors with avatar collisions turned off will not @@ -1911,6 +1941,8 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s _haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); _bloom.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); _audio.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); + _tonemapping.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); + _ambientOcclusion.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FLYING_ALLOWED, flyingAllowed); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GHOSTING_ALLOWED, ghostingAllowed); @@ -1923,6 +1955,8 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BLOOM_MODE, bloomMode, getBloomModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AVATAR_PRIORITY, avatarPriority, getAvatarPriorityAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SCREENSHARE, screenshare, getScreenshareAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TONEMAPPING_MODE, tonemappingMode, getTonemappingModeAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_OCCLUSION_MODE, ambientOcclusionMode, getAmbientOcclusionModeAsString()); } // Web only @@ -2297,6 +2331,8 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h _haze.copyFromScriptValue(object, namesSet, _defaultSettings); _bloom.copyFromScriptValue(object, namesSet, _defaultSettings); _audio.copyFromScriptValue(object, namesSet, _defaultSettings); + _tonemapping.copyFromScriptValue(object, namesSet, _defaultSettings); + _ambientOcclusion.copyFromScriptValue(object, namesSet, _defaultSettings); COPY_PROPERTY_FROM_QSCRIPTVALUE(flyingAllowed, bool, setFlyingAllowed); COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed); COPY_PROPERTY_FROM_QSCRIPTVALUE(filterURL, QString, setFilterURL); @@ -2307,6 +2343,8 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(bloomMode, BloomMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(avatarPriority, AvatarPriority); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(screenshare, Screenshare); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(tonemappingMode, TonemappingMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientOcclusionMode, AmbientOcclusionMode); // Polyvox COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, vec3, setVoxelVolumeSize); @@ -2591,6 +2629,8 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { _haze.merge(other._haze); _bloom.merge(other._bloom); _audio.merge(other._audio); + _tonemapping.merge(other._tonemapping); + _ambientOcclusion.merge(other._ambientOcclusion); COPY_PROPERTY_IF_CHANGED(flyingAllowed); COPY_PROPERTY_IF_CHANGED(ghostingAllowed); COPY_PROPERTY_IF_CHANGED(filterURL); @@ -2601,6 +2641,8 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(bloomMode); COPY_PROPERTY_IF_CHANGED(avatarPriority); COPY_PROPERTY_IF_CHANGED(screenshare); + COPY_PROPERTY_IF_CHANGED(tonemappingMode); + COPY_PROPERTY_IF_CHANGED(ambientOcclusionMode); // Polyvox COPY_PROPERTY_IF_CHANGED(voxelVolumeSize); @@ -3007,6 +3049,13 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_GROUP_PROPERTY_TO_MAP(PROP_LISTENER_ZONES, Audio, audio, ListenerZones, listenerZones); ADD_GROUP_PROPERTY_TO_MAP(PROP_LISTENER_ATTENUATION_COEFFICIENTS, Audio, audio, ListenerAttenuationCoefficients, listenerAttenuationCoefficients); } + { // Tonemapping + ADD_GROUP_PROPERTY_TO_MAP(PROP_TONEMAPPING_CURVE, Tonemapping, tonemapping, Curve, curve); + ADD_GROUP_PROPERTY_TO_MAP(PROP_TONEMAPPING_EXPOSURE, Tonemapping, tonemapping, Exposure, exposure); + } + { // Ambient Occlusion + // TODO + } ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool); ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool); ADD_PROPERTY_TO_MAP(PROP_FILTER_URL, FilterURL, filterURL, QString); @@ -3017,6 +3066,8 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t); ADD_PROPERTY_TO_MAP(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, uint32_t); ADD_PROPERTY_TO_MAP(PROP_SCREENSHARE, Screenshare, screenshare, uint32_t); + ADD_PROPERTY_TO_MAP(PROP_TONEMAPPING_MODE, TonemappingMode, tonemappingMode, uint32_t); + ADD_PROPERTY_TO_MAP(PROP_AMBIENT_OCCLUSION_MODE, AmbientOcclusionMode, ambientOcclusionMode, uint32_t); // Polyvox ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, vec3); @@ -3434,6 +3485,12 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticAudio.setProperties(properties); _staticAudio.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + _staticTonemapping.setProperties(properties); + _staticTonemapping.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + + _staticAmbientOcclusion.setProperties(properties); + _staticAmbientOcclusion.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, properties.getFlyingAllowed()); APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, properties.getGhostingAllowed()); APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, properties.getFilterURL()); @@ -3445,6 +3502,8 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)properties.getBloomMode()); APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, (uint32_t)properties.getAvatarPriority()); APPEND_ENTITY_PROPERTY(PROP_SCREENSHARE, (uint32_t)properties.getScreenshare()); + APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_MODE, (uint32_t)properties.getTonemappingMode()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_MODE, (uint32_t)properties.getAmbientOcclusionMode()); } if (properties.getType() == EntityTypes::PolyVox) { @@ -3913,6 +3972,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); properties.getBloom().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); properties.getAudio().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + properties.getTonemapping().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + properties.getAmbientOcclusion().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); @@ -3925,6 +3986,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLOOM_MODE, uint32_t, setBloomMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AVATAR_PRIORITY, uint32_t, setAvatarPriority); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCREENSHARE, uint32_t, setScreenshare); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TONEMAPPING_MODE, uint32_t, setTonemappingMode); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AMBIENT_OCCLUSION_MODE, uint32_t, setAmbientOcclusionMode); } if (properties.getType() == EntityTypes::PolyVox) { @@ -4295,6 +4358,8 @@ void EntityItemProperties::markAllChanged() { _haze.markAllChanged(); _bloom.markAllChanged(); _audio.markAllChanged(); + _tonemapping.markAllChanged(); + _ambientOcclusion.markAllChanged(); _flyingAllowedChanged = true; _ghostingAllowedChanged = true; _filterURLChanged = true; @@ -4305,6 +4370,8 @@ void EntityItemProperties::markAllChanged() { _bloomModeChanged = true; _avatarPriorityChanged = true; _screenshareChanged = true; + _tonemappingModeChanged = true; + _ambientOcclusionModeChanged = true; // Polyvox _voxelVolumeSizeChanged = true; @@ -4907,6 +4974,8 @@ QList EntityItemProperties::listChangedProperties() { getHaze().listChangedProperties(out); getBloom().listChangedProperties(out); getAudio().listChangedProperties(out); + getTonemapping().listChangedProperties(out); + getAmbientOcclusion().listChangedProperties(out); if (flyingAllowedChanged()) { out += "flyingAllowed"; } @@ -4937,6 +5006,12 @@ QList EntityItemProperties::listChangedProperties() { if (screenshareChanged()) { out += "screenshare"; } + if (tonemappingModeChanged()) { + out += "tonemappingMode"; + } + if (ambientOcclusionModeChanged()) { + out += "ambientOcclusionMode"; + } // Polyvox if (voxelVolumeSizeChanged()) { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 363172f4c0..daf9e29026 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -63,6 +63,8 @@ #include "PulsePropertyGroup.h" #include "RingGizmoPropertyGroup.h" #include "ZoneAudioPropertyGroup.h" +#include "TonemappingPropertyGroup.h" +#include "AmbientOcclusionPropertyGroup.h" #include "MaterialMappingMode.h" #include "BillboardMode.h" @@ -343,6 +345,8 @@ public: DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup); DEFINE_PROPERTY_GROUP(Bloom, bloom, BloomPropertyGroup); DEFINE_PROPERTY_GROUP(Audio, audio, ZoneAudioPropertyGroup); + DEFINE_PROPERTY_GROUP(Tonemapping, tonemapping, TonemappingPropertyGroup); + DEFINE_PROPERTY_GROUP(AmbientOcclusion, ambientOcclusion, AmbientOcclusionPropertyGroup); DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED); DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED); DEFINE_PROPERTY(PROP_FILTER_URL, FilterURL, filterURL, QString, ZoneEntityItem::DEFAULT_FILTER_URL); @@ -353,6 +357,8 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_SCREENSHARE, Screenshare, screenshare, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_TONEMAPPING_MODE, TonemappingMode, tonemappingMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_OCCLUSION_MODE, AmbientOcclusionMode, ambientOcclusionMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); // Polyvox DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 14730cc6de..87517bdd3a 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -175,6 +175,23 @@ enum EntityPropertyList { PROP_DERIVED_38, PROP_DERIVED_39, PROP_DERIVED_40, + PROP_DERIVED_41, + PROP_DERIVED_42, + PROP_DERIVED_43, + PROP_DERIVED_44, + PROP_DERIVED_45, + PROP_DERIVED_46, + PROP_DERIVED_47, + PROP_DERIVED_48, + PROP_DERIVED_49, + PROP_DERIVED_50, + PROP_DERIVED_51, + PROP_DERIVED_52, + PROP_DERIVED_53, + PROP_DERIVED_54, + PROP_DERIVED_55, + PROP_DERIVED_56, + PROP_DERIVED_57, PROP_AFTER_LAST_ITEM, @@ -276,44 +293,44 @@ enum EntityPropertyList { // Zone // Keylight - PROP_KEYLIGHT_COLOR = PROP_DERIVED_0, - PROP_KEYLIGHT_INTENSITY = PROP_DERIVED_1, - PROP_KEYLIGHT_DIRECTION = PROP_DERIVED_2, - PROP_KEYLIGHT_CAST_SHADOW = PROP_DERIVED_3, - PROP_KEYLIGHT_SHADOW_BIAS = PROP_DERIVED_4, - PROP_KEYLIGHT_SHADOW_MAX_DISTANCE = PROP_DERIVED_5, + PROP_KEY_LIGHT_MODE = PROP_DERIVED_0, + PROP_KEYLIGHT_COLOR = PROP_DERIVED_1, + PROP_KEYLIGHT_INTENSITY = PROP_DERIVED_2, + PROP_KEYLIGHT_DIRECTION = PROP_DERIVED_3, + PROP_KEYLIGHT_CAST_SHADOW = PROP_DERIVED_4, + PROP_KEYLIGHT_SHADOW_BIAS = PROP_DERIVED_5, + PROP_KEYLIGHT_SHADOW_MAX_DISTANCE = PROP_DERIVED_6, // Ambient light - PROP_AMBIENT_LIGHT_INTENSITY = PROP_DERIVED_6, - PROP_AMBIENT_LIGHT_URL = PROP_DERIVED_7, - PROP_AMBIENT_LIGHT_COLOR = PROP_DERIVED_8, + PROP_AMBIENT_LIGHT_MODE = PROP_DERIVED_7, + PROP_AMBIENT_LIGHT_INTENSITY = PROP_DERIVED_8, + PROP_AMBIENT_LIGHT_URL = PROP_DERIVED_9, + PROP_AMBIENT_LIGHT_COLOR = PROP_DERIVED_10, // Skybox - PROP_SKYBOX_COLOR = PROP_DERIVED_9, - PROP_SKYBOX_URL = PROP_DERIVED_10, + PROP_SKYBOX_MODE = PROP_DERIVED_11, + PROP_SKYBOX_COLOR = PROP_DERIVED_12, + PROP_SKYBOX_URL = PROP_DERIVED_13, // Haze - PROP_HAZE_RANGE = PROP_DERIVED_11, - PROP_HAZE_COLOR = PROP_DERIVED_12, - PROP_HAZE_GLARE_COLOR = PROP_DERIVED_13, - PROP_HAZE_ENABLE_GLARE = PROP_DERIVED_14, - PROP_HAZE_GLARE_ANGLE = PROP_DERIVED_15, - PROP_HAZE_ALTITUDE_EFFECT = PROP_DERIVED_16, - PROP_HAZE_CEILING = PROP_DERIVED_17, - PROP_HAZE_BASE_REF = PROP_DERIVED_18, - PROP_HAZE_BACKGROUND_BLEND = PROP_DERIVED_19, - PROP_HAZE_ATTENUATE_KEYLIGHT = PROP_DERIVED_20, - PROP_HAZE_KEYLIGHT_RANGE = PROP_DERIVED_21, - PROP_HAZE_KEYLIGHT_ALTITUDE = PROP_DERIVED_22, + PROP_HAZE_MODE = PROP_DERIVED_14, + PROP_HAZE_RANGE = PROP_DERIVED_15, + PROP_HAZE_COLOR = PROP_DERIVED_16, + PROP_HAZE_GLARE_COLOR = PROP_DERIVED_17, + PROP_HAZE_ENABLE_GLARE = PROP_DERIVED_18, + PROP_HAZE_GLARE_ANGLE = PROP_DERIVED_19, + PROP_HAZE_ALTITUDE_EFFECT = PROP_DERIVED_20, + PROP_HAZE_CEILING = PROP_DERIVED_21, + PROP_HAZE_BASE_REF = PROP_DERIVED_22, + PROP_HAZE_BACKGROUND_BLEND = PROP_DERIVED_23, + PROP_HAZE_ATTENUATE_KEYLIGHT = PROP_DERIVED_24, + PROP_HAZE_KEYLIGHT_RANGE = PROP_DERIVED_25, + PROP_HAZE_KEYLIGHT_ALTITUDE = PROP_DERIVED_26, // Bloom - PROP_BLOOM_INTENSITY = PROP_DERIVED_23, - PROP_BLOOM_THRESHOLD = PROP_DERIVED_24, - PROP_BLOOM_SIZE = PROP_DERIVED_25, - PROP_FLYING_ALLOWED = PROP_DERIVED_26, - PROP_GHOSTING_ALLOWED = PROP_DERIVED_27, - PROP_FILTER_URL = PROP_DERIVED_28, - PROP_KEY_LIGHT_MODE = PROP_DERIVED_29, - PROP_AMBIENT_LIGHT_MODE = PROP_DERIVED_30, - PROP_SKYBOX_MODE = PROP_DERIVED_31, - PROP_HAZE_MODE = PROP_DERIVED_32, - PROP_BLOOM_MODE = PROP_DERIVED_33, + PROP_BLOOM_MODE = PROP_DERIVED_27, + PROP_BLOOM_INTENSITY = PROP_DERIVED_28, + PROP_BLOOM_THRESHOLD = PROP_DERIVED_29, + PROP_BLOOM_SIZE = PROP_DERIVED_30, + PROP_FLYING_ALLOWED = PROP_DERIVED_31, + PROP_GHOSTING_ALLOWED = PROP_DERIVED_32, + PROP_FILTER_URL = PROP_DERIVED_33, // Avatar priority PROP_AVATAR_PRIORITY = PROP_DERIVED_34, // Screen-sharing @@ -324,6 +341,22 @@ enum EntityPropertyList { PROP_REVERB_WET_LEVEL = PROP_DERIVED_38, PROP_LISTENER_ZONES = PROP_DERIVED_39, PROP_LISTENER_ATTENUATION_COEFFICIENTS = PROP_DERIVED_40, + // Tonemapping + PROP_TONEMAPPING_MODE = PROP_DERIVED_41, + PROP_TONEMAPPING_CURVE = PROP_DERIVED_42, + PROP_TONEMAPPING_EXPOSURE = PROP_DERIVED_43, + // Ambient Occlusion + PROP_AMBIENT_OCCLUSION_MODE = PROP_DERIVED_44, + PROP_AMBIENT_OCCLUSION_TECHNIQUE = PROP_DERIVED_45, + PROP_AMBIENT_OCCLUSION_JITTER = PROP_DERIVED_46, + PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL = PROP_DERIVED_47, + PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS = PROP_DERIVED_48, + PROP_AMBIENT_OCCLUSION_BLUR_RADIUS = PROP_DERIVED_49, + PROP_AMBIENT_OCCLUSION_AO_RADIUS = PROP_DERIVED_50, + PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL = PROP_DERIVED_51, + PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE = PROP_DERIVED_52, + PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES = PROP_DERIVED_53, + PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS = PROP_DERIVED_54, // Polyvox PROP_VOXEL_VOLUME_SIZE = PROP_DERIVED_0, diff --git a/libraries/entities/src/TonemappingPropertyGroup.cpp b/libraries/entities/src/TonemappingPropertyGroup.cpp new file mode 100644 index 0000000000..2613e3144d --- /dev/null +++ b/libraries/entities/src/TonemappingPropertyGroup.cpp @@ -0,0 +1,167 @@ +// +// TonemappingPropertyGroup.cpp +// libraries/entities/src +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "TonemappingPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" +#include "EntityItemPropertiesMacros.h" + +inline void addTonemappingCurve(QHash& lookup, TonemappingCurve curve) { lookup[TonemappingCurveHelpers::getNameForTonemappingCurve(curve)] = curve; } +const QHash stringToTonemappingCurveLookup = [] { + QHash toReturn; + addTonemappingCurve(toReturn, TonemappingCurve::RGB); + addTonemappingCurve(toReturn, TonemappingCurve::SRGB); + addTonemappingCurve(toReturn, TonemappingCurve::FILMIC); + addTonemappingCurve(toReturn, TonemappingCurve::REINHARD); + return toReturn; +}(); +QString TonemappingPropertyGroup::getCurveAsString() const { return TonemappingCurveHelpers::getNameForTonemappingCurve(_curve); } +void TonemappingPropertyGroup::setCurveFromString(const QString& curve) { + auto curveItr = stringToTonemappingCurveLookup.find(curve.toLower()); + if (curveItr != stringToTonemappingCurveLookup.end()) { + _curve = curveItr.value(); + _curveChanged = true; + } +} + +void TonemappingPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TONEMAPPING_CURVE, Tonemapping, tonemapping, Curve, curve, getCurveAsString); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_TONEMAPPING_EXPOSURE, Tonemapping, tonemapping, Exposure, exposure); +} + +void TonemappingPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(tonemapping, curve, Curve); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(tonemapping, exposure, float, setExposure); +} + +void TonemappingPropertyGroup::merge(const TonemappingPropertyGroup& other) { + COPY_PROPERTY_IF_CHANGED(curve); + COPY_PROPERTY_IF_CHANGED(exposure); +} + +void TonemappingPropertyGroup::debugDump() const { + qCDebug(entities) << " TonemappingPropertyGroup: ---------------------------------------------"; + qCDebug(entities) << " Curve:" << getCurveAsString(); + qCDebug(entities) << " Exposure:" << getExposure(); +} + +void TonemappingPropertyGroup::listChangedProperties(QList& out) { + if (curveChanged()) { + out << "tonemapping-curve"; + } + if (exposureChanged()) { + out << "tonemapping-exposure"; + } +} + +bool TonemappingPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_CURVE, (uint32_t)getCurve()); + APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_EXPOSURE, getExposure()); + + return true; +} + + +bool TonemappingPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + + READ_ENTITY_PROPERTY(PROP_TONEMAPPING_CURVE, TonemappingCurve, setCurve); + READ_ENTITY_PROPERTY(PROP_TONEMAPPING_EXPOSURE, float, setExposure); + + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_TONEMAPPING_CURVE, Curve); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_TONEMAPPING_EXPOSURE, Exposure); + + processedBytes += bytesRead; + + Q_UNUSED(somethingChanged); + + return true; +} + +void TonemappingPropertyGroup::markAllChanged() { + _curveChanged = true; + _exposureChanged = true; +} + +EntityPropertyFlags TonemappingPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + + CHECK_PROPERTY_CHANGE(PROP_TONEMAPPING_CURVE, curve); + CHECK_PROPERTY_CHANGE(PROP_TONEMAPPING_EXPOSURE, exposure); + + return changedProperties; +} + +void TonemappingPropertyGroup::getProperties(EntityItemProperties& properties) const { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Tonemapping, Curve, getCurve); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Tonemapping, Exposure, getExposure); +} + +bool TonemappingPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Tonemapping, Curve, curve, setCurve); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Tonemapping, Exposure, exposure, setExposure); + + return somethingChanged; +} + +EntityPropertyFlags TonemappingPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + + requestedProperties += PROP_TONEMAPPING_CURVE; + requestedProperties += PROP_TONEMAPPING_EXPOSURE; + + return requestedProperties; +} + +void TonemappingPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_CURVE, (uint32_t)getCurve()); + APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_EXPOSURE, getExposure()); +} + +int TonemappingPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_TONEMAPPING_CURVE, TonemappingCurve, setCurve); + READ_ENTITY_PROPERTY(PROP_TONEMAPPING_EXPOSURE, float, setExposure); + + return bytesRead; +} diff --git a/libraries/entities/src/TonemappingPropertyGroup.h b/libraries/entities/src/TonemappingPropertyGroup.h new file mode 100644 index 0000000000..70db875d8c --- /dev/null +++ b/libraries/entities/src/TonemappingPropertyGroup.h @@ -0,0 +1,87 @@ +// +// TonemappingPropertyGroup.h +// libraries/entities/src +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_TonemappingPropertyGroup_h +#define hifi_TonemappingPropertyGroup_h + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +/*@jsdoc + * Tonemapping is defined by the following properties: + * @typedef {object} Entities.Tonemapping + * @property {TonemappingCurve} curve="srgb" - The tonemapping curve used. + * @property {number} exposure=0.0 - The applied exposure. + */ +class TonemappingPropertyGroup : public PropertyGroup { +public: + // EntityItemProperty related helpers + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, + ScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const override; + virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; + + void merge(const TonemappingPropertyGroup& other); + + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; + + virtual bool appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; + + // EntityItem related helpers + // methods for getting/setting all properties of an entity + virtual void getProperties(EntityItemProperties& propertiesOut) const override; + + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + DEFINE_PROPERTY_REF_ENUM(PROP_TONEMAPPING_CURVE, Curve, curve, TonemappingCurve, TonemappingCurve::SRGB); + DEFINE_PROPERTY(PROP_TONEMAPPING_EXPOSURE, Exposure, exposure, float, 0.0); +}; + +#endif // hifi_TonemappingPropertyGroup_h diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index cefd2ccb26..ee431493a8 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -61,6 +61,8 @@ EntityItemProperties ZoneEntityItem::getProperties(const EntityPropertyFlags& de _hazeProperties.getProperties(properties); _bloomProperties.getProperties(properties); _audioProperties.getProperties(properties); + _tonemappingProperties.getProperties(properties); + _ambientOcclusionProperties.getProperties(properties); COPY_ENTITY_PROPERTY_TO_PROPERTIES(flyingAllowed, getFlyingAllowed); COPY_ENTITY_PROPERTY_TO_PROPERTIES(ghostingAllowed, getGhostingAllowed); @@ -73,6 +75,8 @@ EntityItemProperties ZoneEntityItem::getProperties(const EntityPropertyFlags& de COPY_ENTITY_PROPERTY_TO_PROPERTIES(bloomMode, getBloomMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(avatarPriority, getAvatarPriority); COPY_ENTITY_PROPERTY_TO_PROPERTIES(screenshare, getScreenshare); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(tonemappingMode, getTonemappingMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientOcclusionMode, getAmbientOcclusionMode); return properties; } @@ -92,6 +96,8 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie _hazePropertiesChanged |= _hazeProperties.setProperties(properties); _bloomPropertiesChanged |= _bloomProperties.setProperties(properties); bool audioPropertiesChanged = _audioProperties.setProperties(properties); + _tonemappingPropertiesChanged |= _tonemappingProperties.setProperties(properties); + _ambientOcclusionPropertiesChanged |= _ambientOcclusionProperties.setProperties(properties); SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed); @@ -104,9 +110,12 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(avatarPriority, setAvatarPriority); SET_ENTITY_PROPERTY_FROM_PROPERTIES(screenshare, setScreenshare); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(tonemappingMode, setTonemappingMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientOcclusionMode, setAmbientOcclusionMode); somethingChanged |= _keyLightPropertiesChanged || _ambientLightPropertiesChanged || _skyboxPropertiesChanged || - _hazePropertiesChanged || _bloomPropertiesChanged || audioPropertiesChanged; + _hazePropertiesChanged || _bloomPropertiesChanged || audioPropertiesChanged || + _tonemappingPropertiesChanged || _ambientOcclusionPropertiesChanged; return somethingChanged; } @@ -177,6 +186,20 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, dataAt += bytesFromAudio; } + { + int bytesFromTonemapping = _tonemappingProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, somethingChanged); + bytesRead += bytesFromTonemapping; + dataAt += bytesFromTonemapping; + } + + { + int bytesFromAmbientOcclusion = _ambientOcclusionProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, somethingChanged); + bytesRead += bytesFromAmbientOcclusion; + dataAt += bytesFromAmbientOcclusion; + } + READ_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); READ_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); READ_ENTITY_PROPERTY(PROP_FILTER_URL, QString, setFilterURL); @@ -188,6 +211,8 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_BLOOM_MODE, uint32_t, setBloomMode); READ_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, uint32_t, setAvatarPriority); READ_ENTITY_PROPERTY(PROP_SCREENSHARE, uint32_t, setScreenshare); + READ_ENTITY_PROPERTY(PROP_TONEMAPPING_MODE, uint32_t, setTonemappingMode); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_MODE, uint32_t, setAmbientOcclusionMode); return bytesRead; } @@ -204,6 +229,8 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p requestedProperties += _hazeProperties.getEntityProperties(params); requestedProperties += _bloomProperties.getEntityProperties(params); requestedProperties += _audioProperties.getEntityProperties(params); + requestedProperties += _tonemappingProperties.getEntityProperties(params); + requestedProperties += _ambientOcclusionProperties.getEntityProperties(params); requestedProperties += PROP_FLYING_ALLOWED; requestedProperties += PROP_GHOSTING_ALLOWED; @@ -216,6 +243,8 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p requestedProperties += PROP_SKYBOX_MODE; requestedProperties += PROP_HAZE_MODE; requestedProperties += PROP_BLOOM_MODE; + requestedProperties += PROP_TONEMAPPING_MODE; + requestedProperties += PROP_AMBIENT_OCCLUSION_MODE; return requestedProperties; } @@ -247,32 +276,40 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits propertyFlags, propertiesDidntFit, propertyCount, appendState); _audioProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + _tonemappingProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + _ambientOcclusionProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, getFlyingAllowed()); APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, getGhostingAllowed()); APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, getFilterURL()); - APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)getKeyLightMode()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)getAmbientLightMode()); - APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)getSkyboxMode()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode()); - APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)getBloomMode()); + APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, getKeyLightMode()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, getAmbientLightMode()); + APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, getSkyboxMode()); + APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, getHazeMode()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, getBloomMode()); APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, getAvatarPriority()); APPEND_ENTITY_PROPERTY(PROP_SCREENSHARE, getScreenshare()); + APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_MODE, getHazeMode()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_MODE, getBloomMode()); } void ZoneEntityItem::debugDump() const { quint64 now = usecTimestampNow(); - qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeAsString(_hazeMode); - qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeAsString(_keyLightMode); - qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeAsString(_ambientLightMode); - qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeAsString(_skyboxMode); - qCDebug(entities) << " _bloomMode:" << EntityItemProperties::getComponentModeAsString(_bloomMode); - qCDebug(entities) << " _avatarPriority:" << getAvatarPriority(); + qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeAsString(_hazeMode); + qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeAsString(_keyLightMode); + qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeAsString(_ambientLightMode); + qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeAsString(_skyboxMode); + qCDebug(entities) << " _bloomMode:" << EntityItemProperties::getComponentModeAsString(_bloomMode); + qCDebug(entities) << " _avatarPriority:" << getAvatarPriority(); + qCDebug(entities) << " _tonemappingMode:" << EntityItemProperties::getComponentModeAsString(_tonemappingMode); + qCDebug(entities) << " _ambientOcclusionMode:" << EntityItemProperties::getComponentModeAsString(_ambientOcclusionMode); _keyLightProperties.debugDump(); _ambientLightProperties.debugDump(); @@ -280,6 +317,8 @@ void ZoneEntityItem::debugDump() const { _hazeProperties.debugDump(); _bloomProperties.debugDump(); _audioProperties.debugDump(); + _tonemappingProperties.debugDump(); + _ambientOcclusionProperties.debugDump(); } void ZoneEntityItem::setShapeType(ShapeType type) { @@ -408,53 +447,11 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() { _skyboxPropertiesChanged = false; _hazePropertiesChanged = false; _bloomPropertiesChanged = false; + _tonemappingPropertiesChanged = false; + _ambientOcclusionPropertiesChanged = false; }); } -void ZoneEntityItem::setHazeMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _hazeMode) { - _hazeMode = value; - _hazePropertiesChanged = true; - } -} - -uint32_t ZoneEntityItem::getHazeMode() const { - return _hazeMode; -} - -void ZoneEntityItem::setBloomMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _bloomMode) { - _bloomMode = value; - _bloomPropertiesChanged = true; - } -} - -uint32_t ZoneEntityItem::getBloomMode() const { - return _bloomMode; -} - -void ZoneEntityItem::setKeyLightMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) { - _keyLightMode = value; - _keyLightPropertiesChanged = true; - } -} - -uint32_t ZoneEntityItem::getKeyLightMode() const { - return _keyLightMode; -} - -void ZoneEntityItem::setAmbientLightMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientLightMode) { - _ambientLightMode = value; - _ambientLightPropertiesChanged = true; - } -} - -uint32_t ZoneEntityItem::getAmbientLightMode() const { - return _ambientLightMode; -} - void ZoneEntityItem::setSkyboxMode(const uint32_t value) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _skyboxMode) { _skyboxMode = value; @@ -462,8 +459,46 @@ void ZoneEntityItem::setSkyboxMode(const uint32_t value) { } } -uint32_t ZoneEntityItem::getSkyboxMode() const { - return _skyboxMode; +void ZoneEntityItem::setKeyLightMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) { + _keyLightMode = value; + _keyLightPropertiesChanged = true; + } +} + +void ZoneEntityItem::setAmbientLightMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientLightMode) { + _ambientLightMode = value; + _ambientLightPropertiesChanged = true; + } +} + +void ZoneEntityItem::setHazeMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _hazeMode) { + _hazeMode = value; + _hazePropertiesChanged = true; + } +} + +void ZoneEntityItem::setBloomMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _bloomMode) { + _bloomMode = value; + _bloomPropertiesChanged = true; + } +} + +void ZoneEntityItem::setTonemappingMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _tonemappingMode) { + _tonemappingMode = value; + _tonemappingPropertiesChanged = true; + } +} + +void ZoneEntityItem::setAmbientOcclusionMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientOcclusionMode) { + _ambientOcclusionMode = value; + _ambientOcclusionPropertiesChanged = true; + } } void ZoneEntityItem::setUserData(const QString& value) { diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index ccf895ca44..6fb0145df7 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -23,6 +23,8 @@ #include "HazePropertyGroup.h" #include "BloomPropertyGroup.h" #include "ZoneAudioPropertyGroup.h" +#include "TonemappingPropertyGroup.h" +#include "AmbientOcclusionPropertyGroup.h" class ZoneEntityItem : public EntityItem { public: @@ -68,36 +70,20 @@ public: virtual bool matchesJSONFilters(const QJsonObject& jsonFilters) const override; - KeyLightPropertyGroup getKeyLightProperties() const { return resultWithReadLock([&] { return _keyLightProperties; }); } - AmbientLightPropertyGroup getAmbientLightProperties() const { return resultWithReadLock([&] { return _ambientLightProperties; }); } - - void setHazeMode(const uint32_t value); - uint32_t getHazeMode() const; + void setSkyboxMode(uint32_t value); + uint32_t getSkyboxMode() const { return _skyboxMode; } void setKeyLightMode(uint32_t value); - uint32_t getKeyLightMode() const; + uint32_t getKeyLightMode() const { return _keyLightMode; } void setAmbientLightMode(uint32_t value); - uint32_t getAmbientLightMode() const; + uint32_t getAmbientLightMode() const { return _ambientLightMode; } - void setSkyboxMode(uint32_t value); - uint32_t getSkyboxMode() const; + void setHazeMode(const uint32_t value); + uint32_t getHazeMode() const { return _hazeMode; } void setBloomMode(const uint32_t value); - uint32_t getBloomMode() const; - - SkyboxPropertyGroup getSkyboxProperties() const { return resultWithReadLock([&] { return _skyboxProperties; }); } - - const HazePropertyGroup& getHazeProperties() const { return _hazeProperties; } - const BloomPropertyGroup& getBloomProperties() const { return _bloomProperties; } - const ZoneAudioPropertyGroup& getAudioProperties() const { return _audioProperties; } - - bool getFlyingAllowed() const { return _flyingAllowed; } - void setFlyingAllowed(bool value) { _flyingAllowed = value; } - bool getGhostingAllowed() const { return _ghostingAllowed; } - void setGhostingAllowed(bool value) { _ghostingAllowed = value; } - QString getFilterURL() const; - void setFilterURL(const QString url); + uint32_t getBloomMode() const { return _bloomMode; } uint32_t getAvatarPriority() const { return _avatarPriority; } void setAvatarPriority(uint32_t value) { _avatarPriority = value; } @@ -105,6 +91,28 @@ public: uint32_t getScreenshare() const { return _screenshare; } void setScreenshare(uint32_t value) { _screenshare = value; } + void setTonemappingMode(uint32_t value); + uint32_t getTonemappingMode() const { return _tonemappingMode; } + + void setAmbientOcclusionMode(const uint32_t value); + uint32_t getAmbientOcclusionMode() const { return _ambientOcclusionMode; } + + SkyboxPropertyGroup getSkyboxProperties() const { return resultWithReadLock([&] { return _skyboxProperties; }); } + KeyLightPropertyGroup getKeyLightProperties() const { return resultWithReadLock([&] { return _keyLightProperties; }); } + AmbientLightPropertyGroup getAmbientLightProperties() const { return resultWithReadLock([&] { return _ambientLightProperties; }); } + const HazePropertyGroup& getHazeProperties() const { return _hazeProperties; } + const BloomPropertyGroup& getBloomProperties() const { return _bloomProperties; } + const ZoneAudioPropertyGroup& getAudioProperties() const { return _audioProperties; } + const TonemappingPropertyGroup& getTonemappingProperties() const { return _tonemappingProperties; } + const AmbientOcclusionPropertyGroup& getAmbientOcclusionProperties() const { return _ambientOcclusionProperties; } + + bool getFlyingAllowed() const { return _flyingAllowed; } + void setFlyingAllowed(bool value) { _flyingAllowed = value; } + bool getGhostingAllowed() const { return _ghostingAllowed; } + void setGhostingAllowed(bool value) { _ghostingAllowed = value; } + QString getFilterURL() const; + void setFilterURL(const QString url); + void setUserData(const QString& value) override; bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; } @@ -112,6 +120,8 @@ public: bool skyboxPropertiesChanged() const { return _skyboxPropertiesChanged; } bool hazePropertiesChanged() const { return _hazePropertiesChanged; } bool bloomPropertiesChanged() const { return _bloomPropertiesChanged; } + bool tonemappingPropertiesChanged() const { return _tonemappingPropertiesChanged; } + bool ambientOcclusionPropertiesChanged() const { return _ambientOcclusionPropertiesChanged; } void resetRenderingPropertiesChanged(); @@ -142,35 +152,35 @@ protected: ShapeType _shapeType { DEFAULT_SHAPE_TYPE }; QString _compoundShapeURL; - // The following 3 values are the defaults for zone creation uint32_t _keyLightMode { COMPONENT_MODE_INHERIT }; uint32_t _skyboxMode { COMPONENT_MODE_INHERIT }; uint32_t _ambientLightMode { COMPONENT_MODE_INHERIT }; - uint32_t _hazeMode { COMPONENT_MODE_INHERIT }; uint32_t _bloomMode { COMPONENT_MODE_INHERIT }; + uint32_t _avatarPriority { COMPONENT_MODE_INHERIT }; + uint32_t _screenshare { COMPONENT_MODE_INHERIT }; + uint32_t _tonemappingMode { COMPONENT_MODE_INHERIT }; + uint32_t _ambientOcclusionMode { COMPONENT_MODE_INHERIT }; SkyboxPropertyGroup _skyboxProperties; HazePropertyGroup _hazeProperties; BloomPropertyGroup _bloomProperties; ZoneAudioPropertyGroup _audioProperties; + TonemappingPropertyGroup _tonemappingProperties; + AmbientOcclusionPropertyGroup _ambientOcclusionProperties; bool _flyingAllowed { DEFAULT_FLYING_ALLOWED }; bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED }; QString _filterURL { DEFAULT_FILTER_URL }; - // Avatar-updates priority - uint32_t _avatarPriority { COMPONENT_MODE_INHERIT }; - - // Screen-sharing zone - uint32_t _screenshare { COMPONENT_MODE_INHERIT }; - // Dirty flags turn true when either keylight properties is changing values. bool _keyLightPropertiesChanged { false }; bool _ambientLightPropertiesChanged { false }; bool _skyboxPropertiesChanged { false }; bool _hazePropertiesChanged { false }; bool _bloomPropertiesChanged { false }; + bool _tonemappingPropertiesChanged { false }; + bool _ambientOcclusionPropertiesChanged { false }; static bool _drawZoneBoundaries; static bool _zonesArePickable; diff --git a/libraries/graphics/src/graphics/AmbientOcclusion.h b/libraries/graphics/src/graphics/AmbientOcclusion.h new file mode 100644 index 0000000000..7579b783a3 --- /dev/null +++ b/libraries/graphics/src/graphics/AmbientOcclusion.h @@ -0,0 +1,59 @@ +// +// AmbientOcclusion.h +// libraries/graphics/src/graphics +// +// Created by HifiExperiments 6/24/24 +// Copyright 2024 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_model_AmbientOcclusion_h +#define hifi_model_AmbientOcclusion_h + +#include + +#include + +namespace graphics { + class AmbientOcclusion { + public: + AmbientOcclusion() {} + + void setTechnique(const AmbientOcclusionTechnique technique) { _technique = technique; } + void setJitter(const bool jitter) { _jitter = jitter; } + void setResolutionLevel(const uint8_t resolutionLevel) { _resolutionLevel = resolutionLevel; } + void setEdgeSharpness(const float edgeSharpness) { _edgeSharpness = edgeSharpness; } + void setBlurRadius(const uint8_t blurRadius) { _blurRadius = blurRadius; } + void setAORadius(const float aoRadius) { _aoRadius = aoRadius; } + void setAOObscuranceLevel(const float aoObscuranceLevel) { _aoObscuranceLevel = aoObscuranceLevel; } + void setAOFalloffAngle(const float aoFalloffAngle) { _aoFalloffAngle = aoFalloffAngle; } + void setAONumSamples(const uint8_t aoNumSamples) { _aoNumSamples = aoNumSamples; } + void setSSAONumSpiralTurns(const float ssaoNumSpiralTurns) { _ssaoNumSpiralTurns = ssaoNumSpiralTurns; } + + AmbientOcclusionTechnique getTechnique() const { return _technique; } + bool getJitter() const { return _jitter; } + uint8_t getResolutionLevel() const { return _resolutionLevel; } + float getEdgeSharpness() const { return _edgeSharpness; } + uint8_t getBlurRadius() const { return _blurRadius; } + float getAORadius() const { return _aoRadius; } + float getAOObscuranceLevel() const { return _aoObscuranceLevel; } + float getAOFalloffAngle() const { return _aoFalloffAngle; } + uint8_t getAONumSamples() const { return _aoNumSamples; } + float getSSAONumSpiralTurns() const { return _ssaoNumSpiralTurns; } + + private: + AmbientOcclusionTechnique _technique { AmbientOcclusionTechnique::SSAO }; + bool _jitter { false }; + uint8_t _resolutionLevel { 2 }; + float _edgeSharpness { 1.0f }; + uint8_t _blurRadius { 4 }; + float _aoRadius { 1.0f }; + float _aoObscuranceLevel { 0.5f }; + float _aoFalloffAngle { 0.25f }; + uint8_t _aoNumSamples { 32 }; + float _ssaoNumSpiralTurns { 7.0f }; + }; + using AmbientOcclusionPointer = std::shared_ptr; +} +#endif // hifi_model_AmbientOcclusion_h diff --git a/libraries/graphics/src/graphics/Tonemapping.h b/libraries/graphics/src/graphics/Tonemapping.h new file mode 100644 index 0000000000..ad7467d131 --- /dev/null +++ b/libraries/graphics/src/graphics/Tonemapping.h @@ -0,0 +1,35 @@ +// +// Tonemapping.h +// libraries/graphics/src/graphics +// +// Created by HifiExperiments on 6/24/24 +// Copyright 2024 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_model_Tonemapping_h +#define hifi_model_Tonemapping_h + +#include + +#include + +namespace graphics { + class Tonemapping { + public: + Tonemapping() {} + + void setCurve(const TonemappingCurve curve) { _curve = curve; } + void setExposure(const float exposure) { _exposure = exposure; } + + TonemappingCurve getCurve() const { return _curve; } + float getExposure() const { return _exposure; } + + private: + TonemappingCurve _curve { TonemappingCurve::SRGB }; + float _exposure { 0.0f }; + }; + using TonemappingPointer = std::shared_ptr; +} +#endif // hifi_model_Tonemapping_h diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index f8d80660f4..7015b51763 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -298,6 +298,7 @@ enum class EntityVersion : PacketVersion { ProceduralParticles, ShapeUnlit, AmbientColor, + TonemappingAndAmbientOcclusion, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index e61bef45e7..6d736fe8cf 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -43,6 +43,8 @@ #include "TextEffect.h" #include "TextAlignment.h" #include "MirrorMode.h" +#include "TonemappingCurve.h" +#include "AmbientOcclusionTechnique.h" #include "OctreeConstants.h" #include "OctreeElement.h" @@ -285,6 +287,8 @@ public: 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, TonemappingCurve& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, AmbientOcclusionTechnique& 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/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 65fc09a814..100de27e1a 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -21,6 +21,7 @@ #include "DeferredFrameTransform.h" #include "DeferredFramebuffer.h" #include "SurfaceGeometryPass.h" +#include "AmbientOcclusionStage.h" #include "ssao_shared.h" @@ -153,7 +154,8 @@ signals: class AmbientOcclusionEffect { public: - using Input = render::VaryingSet4; + using Input = render::VaryingSet5; using Output = render::VaryingSet2; using Config = AmbientOcclusionEffectConfig; using JobModel = render::Job::ModelIO; diff --git a/libraries/render-utils/src/AmbientOcclusionStage.cpp b/libraries/render-utils/src/AmbientOcclusionStage.cpp new file mode 100644 index 0000000000..4adb06bbee --- /dev/null +++ b/libraries/render-utils/src/AmbientOcclusionStage.cpp @@ -0,0 +1,56 @@ +// +// AmbientOcclusionStage.cpp +// +// Created by HifiExperiments on 6/24/24 +// Copyright 2024 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 "AmbientOcclusionStage.h" + +#include + +std::string AmbientOcclusionStage::_stageName { "BLOOM_STAGE"}; +const AmbientOcclusionStage::Index AmbientOcclusionStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; + +AmbientOcclusionStage::Index AmbientOcclusionStage::findAmbientOcclusion(const AmbientOcclusionPointer& ambientOcclusion) const { + auto found = _ambientOcclusionMap.find(ambientOcclusion); + if (found != _ambientOcclusionMap.end()) { + return INVALID_INDEX; + } else { + return (*found).second; + } +} + +AmbientOcclusionStage::Index AmbientOcclusionStage::addAmbientOcclusion(const AmbientOcclusionPointer& ambientOcclusion) { + auto found = _ambientOcclusionMap.find(ambientOcclusion); + if (found == _ambientOcclusionMap.end()) { + auto ambientOcclusionId = _ambientOcclusions.newElement(ambientOcclusion); + // Avoid failing to allocate a ambientOcclusion, just pass + if (ambientOcclusionId != INVALID_INDEX) { + // Insert the ambientOcclusion and its index in the reverse map + _ambientOcclusionMap.insert(AmbientOcclusionMap::value_type(ambientOcclusion, ambientOcclusionId)); + } + return ambientOcclusionId; + } else { + return (*found).second; + } +} + +AmbientOcclusionStage::AmbientOcclusionPointer AmbientOcclusionStage::removeAmbientOcclusion(Index index) { + AmbientOcclusionPointer removed = _ambientOcclusions.freeElement(index); + if (removed) { + _ambientOcclusionMap.erase(removed); + } + return removed; +} + +AmbientOcclusionStageSetup::AmbientOcclusionStageSetup() {} + +void AmbientOcclusionStageSetup::run(const render::RenderContextPointer& renderContext) { + auto stage = renderContext->_scene->getStage(AmbientOcclusionStage::getName()); + if (!stage) { + renderContext->_scene->resetStage(AmbientOcclusionStage::getName(), std::make_shared()); + } +} diff --git a/libraries/render-utils/src/AmbientOcclusionStage.h b/libraries/render-utils/src/AmbientOcclusionStage.h new file mode 100644 index 0000000000..d5dee344ba --- /dev/null +++ b/libraries/render-utils/src/AmbientOcclusionStage.h @@ -0,0 +1,84 @@ +// +// AmbientOcclusionStage.h +// +// Created by HifiExperiments on 6/24/24 +// Copyright 2024 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_render_utils_AmbientOcclusionStage_h +#define hifi_render_utils_AmbientOcclusionStage_h + +#include +#include +#include +#include +#include + +#include +#include +#include + +// AmbientOcclusion stage to set up ambientOcclusion-related rendering tasks +class AmbientOcclusionStage : public render::Stage { +public: + static std::string _stageName; + static const std::string& getName() { return _stageName; } + + using Index = render::indexed_container::Index; + static const Index INVALID_INDEX; + static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } + + using AmbientOcclusionPointer = graphics::AmbientOcclusionPointer; + using AmbientOcclusions = render::indexed_container::IndexedPointerVector; + using AmbientOcclusionMap = std::unordered_map; + + using AmbientOcclusionIndices = std::vector; + + Index findAmbientOcclusion(const AmbientOcclusionPointer& ambientOcclusion) const; + Index addAmbientOcclusion(const AmbientOcclusionPointer& ambientOcclusion); + + AmbientOcclusionPointer removeAmbientOcclusion(Index index); + + bool checkAmbientOcclusionId(Index index) const { return _ambientOcclusions.checkIndex(index); } + + Index getNumAmbientOcclusions() const { return _ambientOcclusions.getNumElements(); } + Index getNumFreeAmbientOcclusions() const { return _ambientOcclusions.getNumFreeIndices(); } + Index getNumAllocatedAmbientOcclusions() const { return _ambientOcclusions.getNumAllocatedIndices(); } + + AmbientOcclusionPointer getAmbientOcclusion(Index ambientOcclusionId) const { + return _ambientOcclusions.get(ambientOcclusionId); + } + + AmbientOcclusions _ambientOcclusions; + AmbientOcclusionMap _ambientOcclusionMap; + + class Frame { + public: + Frame() {} + + void clear() { _ambientOcclusions.clear(); } + + void pushAmbientOcclusion(AmbientOcclusionStage::Index index) { _ambientOcclusions.emplace_back(index); } + + AmbientOcclusionStage::AmbientOcclusionIndices _ambientOcclusions; + }; + using FramePointer = std::shared_ptr; + + Frame _currentFrame; +}; +using AmbientOcclusionStagePointer = std::shared_ptr; + +class AmbientOcclusionStageSetup { +public: + using JobModel = render::Job::Model; + + AmbientOcclusionStageSetup(); + void run(const render::RenderContextPointer& renderContext); + +protected: +}; + +#endif diff --git a/libraries/render-utils/src/AssembleLightingStageTask.cpp b/libraries/render-utils/src/AssembleLightingStageTask.cpp index 589cdb31ea..bc040582bd 100644 --- a/libraries/render-utils/src/AssembleLightingStageTask.cpp +++ b/libraries/render-utils/src/AssembleLightingStageTask.cpp @@ -25,6 +25,14 @@ void FetchCurrentFrames::run(const render::RenderContextPointer& renderContext, auto bloomStage = renderContext->_scene->getStage(); assert(bloomStage); output.edit3() = std::make_shared(bloomStage->_currentFrame); + + auto tonemappingStage = renderContext->_scene->getStage(); + assert(tonemappingStage); + output.edit4() = std::make_shared(tonemappingStage->_currentFrame); + + auto ambientOcclusionStage = renderContext->_scene->getStage(); + assert(ambientOcclusionStage); + output.edit5() = std::make_shared(ambientOcclusionStage->_currentFrame); } @@ -47,4 +55,3 @@ void AssembleLightingStageTask::build(JobModel& task, const render::Varying& inp output = Output(currentStageFrames, zones); } - diff --git a/libraries/render-utils/src/AssembleLightingStageTask.h b/libraries/render-utils/src/AssembleLightingStageTask.h index 9770af473c..17add988ef 100644 --- a/libraries/render-utils/src/AssembleLightingStageTask.h +++ b/libraries/render-utils/src/AssembleLightingStageTask.h @@ -16,13 +16,16 @@ #include "BackgroundStage.h" #include "HazeStage.h" #include "BloomStage.h" +#include "TonemappingStage.h" +#include "AmbientOcclusionStage.h" #include "ZoneRenderer.h" class FetchCurrentFrames { public: - using Output = render::VaryingSet4; + using Output = render::VaryingSet6; using JobModel = render::Job::ModelO; FetchCurrentFrames() {} diff --git a/libraries/render-utils/src/BloomStage.h b/libraries/render-utils/src/BloomStage.h index 7ba7e56035..bb03e181af 100644 --- a/libraries/render-utils/src/BloomStage.h +++ b/libraries/render-utils/src/BloomStage.h @@ -81,26 +81,4 @@ public: protected: }; -class FetchBloomConfig : public render::Job::Config { - Q_OBJECT - Q_PROPERTY(float bloomIntensity MEMBER bloomIntensity WRITE setBloomIntensity NOTIFY dirty); - Q_PROPERTY(float bloomThreshold MEMBER bloomThreshold WRITE setBloomThreshold NOTIFY dirty); - Q_PROPERTY(float bloomSize MEMBER bloomSize WRITE setBloomSize NOTIFY dirty); - -public: - FetchBloomConfig() : render::Job::Config() {} - - float bloomIntensity { graphics::Bloom::INITIAL_BLOOM_INTENSITY }; - float bloomThreshold { graphics::Bloom::INITIAL_BLOOM_THRESHOLD }; - float bloomSize { graphics::Bloom::INITIAL_BLOOM_SIZE }; - -public slots: - void setBloomIntensity(const float value) { bloomIntensity = value; emit dirty(); } - void setBloomThreshold(const float value) { bloomThreshold = value; emit dirty(); } - void setBloomSize(const float value) { bloomSize = value; emit dirty(); } - -signals: - void dirty(); -}; - #endif diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 3eb5924d19..fe9e5eb245 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -662,5 +662,24 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) { _defaultHazeID = hazeStage->addHaze(_defaultHaze); } } -} + if (!_defaultTonemapping) { + auto tonemappingStage = renderContext->_scene->getStage(); + if (tonemappingStage) { + auto tonemapping = std::make_shared(); + + _defaultTonemapping = tonemapping; + _defaultTonemappingID = tonemappingStage->addTonemapping(_defaultTonemapping); + } + } + + if (!_defaultAmbientOcclusion) { + auto ambientOcclusionStage = renderContext->_scene->getStage(); + if (ambientOcclusionStage) { + auto ambientOcclusion = std::make_shared(); + + _defaultAmbientOcclusion = ambientOcclusion; + _defaultAmbientOcclusionID = ambientOcclusionStage->addAmbientOcclusion(_defaultAmbientOcclusion); + } + } +} diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 058e0a4cbf..d19c1de2e9 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -32,6 +32,7 @@ #include "LightClusters.h" #include "BackgroundStage.h" #include "HazeStage.h" +#include "TonemappingStage.h" #include "SurfaceGeometryPass.h" #include "SubsurfaceScattering.h" @@ -162,11 +163,15 @@ public: protected: graphics::LightPointer _defaultLight; - LightStage::Index _defaultLightID{ LightStage::INVALID_INDEX }; + LightStage::Index _defaultLightID { LightStage::INVALID_INDEX }; graphics::SunSkyStagePointer _defaultBackground; - BackgroundStage::Index _defaultBackgroundID{ BackgroundStage::INVALID_INDEX }; - graphics::HazePointer _defaultHaze{ nullptr }; - HazeStage::Index _defaultHazeID{ HazeStage::INVALID_INDEX }; + BackgroundStage::Index _defaultBackgroundID { BackgroundStage::INVALID_INDEX }; + graphics::HazePointer _defaultHaze { nullptr }; + HazeStage::Index _defaultHazeID { HazeStage::INVALID_INDEX }; + graphics::TonemappingPointer _defaultTonemapping { nullptr }; + TonemappingStage::Index _defaultTonemappingID { TonemappingStage::INVALID_INDEX }; + graphics::AmbientOcclusionPointer _defaultAmbientOcclusion { nullptr }; + AmbientOcclusionStage::Index _defaultAmbientOcclusionID { AmbientOcclusionStage::INVALID_INDEX }; graphics::SkyboxPointer _defaultSkybox { new ProceduralSkybox() }; NetworkTexturePointer _defaultSkyboxNetworkTexture; NetworkTexturePointer _defaultAmbientNetworkTexture; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index aa80b5ec6e..4bcfc9663b 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -137,6 +137,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto backgroundFrame = currentStageFrames[1]; const auto& hazeFrame = currentStageFrames[2]; const auto& bloomFrame = currentStageFrames[3]; + const auto& tonemappingFrame = currentStageFrames[4]; + const auto& ambientOcclusionFrame = currentStageFrames[5]; // Shadow Task Outputs const auto& shadowTaskOutputs = inputs.get3(); @@ -198,7 +200,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto scatteringResource = task.addJob("Scattering"); // AO job - const auto ambientOcclusionInputs = AmbientOcclusionEffect::Input(lightingModel, deferredFrameTransform, deferredFramebuffer, linearDepthTarget).asVarying(); + const auto ambientOcclusionInputs = AmbientOcclusionEffect::Input(lightingModel, deferredFrameTransform, deferredFramebuffer, linearDepthTarget, ambientOcclusionFrame).asVarying(); const auto ambientOcclusionOutputs = task.addJob("AmbientOcclusion", ambientOcclusionInputs); const auto ambientOcclusionFramebuffer = ambientOcclusionOutputs.getN(0); const auto ambientOcclusionUniforms = ambientOcclusionOutputs.getN(1); @@ -250,8 +252,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto destFramebuffer = static_cast(nullptr); // Lighting Buffer ready for tone mapping - const auto toneMappingInputs = ToneMapAndResample::Input(lightingFramebuffer, destFramebuffer).asVarying(); - const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs, depth); + const auto toneMappingInputs = ToneMapAndResample::Input(lightingFramebuffer, destFramebuffer, tonemappingFrame).asVarying(); + const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs); // 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 diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 9b483c16c2..cba6f4920f 100644 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -108,6 +108,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto lightFrame = currentStageFrames[0]; const auto backgroundFrame = currentStageFrames[1]; const auto hazeFrame = currentStageFrames[2]; + const auto tonemappingFrame = currentStageFrames[4]; const auto& zones = lightingStageInputs[1]; @@ -178,8 +179,8 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto destFramebuffer = static_cast(nullptr); - const auto toneMappingInputs = ToneMapAndResample::Input(resolvedFramebuffer, destFramebuffer).asVarying(); - const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs, depth); + const auto toneMappingInputs = ToneMapAndResample::Input(resolvedFramebuffer, destFramebuffer, tonemappingFrame).asVarying(); + const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs); // HUD Layer const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(toneMappedBuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying(); task.addJob("RenderHUDLayer", renderHUDLayerInputs); diff --git a/libraries/render-utils/src/ToneMapAndResampleTask.cpp b/libraries/render-utils/src/ToneMapAndResampleTask.cpp index 1ea9deb1fa..d906d82aa7 100644 --- a/libraries/render-utils/src/ToneMapAndResampleTask.cpp +++ b/libraries/render-utils/src/ToneMapAndResampleTask.cpp @@ -25,10 +25,9 @@ using namespace shader::render_utils::program; gpu::PipelinePointer ToneMapAndResample::_pipeline; gpu::PipelinePointer ToneMapAndResample::_mirrorPipeline; -ToneMapAndResample::ToneMapAndResample(size_t depth) { +ToneMapAndResample::ToneMapAndResample() { Parameters parameters; _parametersBuffer = gpu::BufferView(std::make_shared(sizeof(Parameters), (const gpu::Byte*) ¶meters)); - _depth = depth; } void ToneMapAndResample::init() { @@ -44,13 +43,13 @@ void ToneMapAndResample::init() { void ToneMapAndResample::setExposure(float exposure) { auto& params = _parametersBuffer.get(); - if (params._exposure != exposure) { - _parametersBuffer.edit()._exposure = exposure; + if (_exposure != exposure) { + _exposure = exposure; _parametersBuffer.edit()._twoPowExposure = pow(2.0, exposure); } } -void ToneMapAndResample::setToneCurve(ToneCurve curve) { +void ToneMapAndResample::setCurve(TonemappingCurve curve) { auto& params = _parametersBuffer.get(); if (params._toneCurve != (int)curve) { _parametersBuffer.edit()._toneCurve = (int)curve; @@ -58,8 +57,9 @@ void ToneMapAndResample::setToneCurve(ToneCurve curve) { } void ToneMapAndResample::configure(const Config& config) { - setExposure(config.exposure); - setToneCurve((ToneCurve)config.curve); + _debug = config.debug; + _debugExposure = config.exposure; + _debugCurve = (TonemappingCurve)config.curve; } void ToneMapAndResample::run(const RenderContextPointer& renderContext, const Input& input, Output& output) { @@ -70,6 +70,21 @@ void ToneMapAndResample::run(const RenderContextPointer& renderContext, const In auto lightingBuffer = input.get0()->getRenderBuffer(0); auto destinationFramebuffer = input.get1(); + const auto tonemappingFrame = input.get2(); + + const auto& tonemappingStage = renderContext->_scene->getStage(); + graphics::TonemappingPointer tonemapping; + if (tonemappingStage && tonemappingFrame->_tonemappings.size()) { + tonemapping = tonemappingStage->getTonemapping(tonemappingFrame->_tonemappings.front()); + } + + if (_debug) { + setCurve(_debugCurve); + setExposure(_debugExposure); + } else if (tonemapping) { + setCurve(tonemapping->getCurve()); + setExposure(tonemapping->getExposure()); + } if (!destinationFramebuffer) { destinationFramebuffer = args->_blitFramebuffer; diff --git a/libraries/render-utils/src/ToneMapAndResampleTask.h b/libraries/render-utils/src/ToneMapAndResampleTask.h index 3a812cf445..50a35b2fe4 100644 --- a/libraries/render-utils/src/ToneMapAndResampleTask.h +++ b/libraries/render-utils/src/ToneMapAndResampleTask.h @@ -20,28 +20,24 @@ #include #include -enum class ToneCurve { - // Different tone curve available - None, - Gamma22, - Reinhard, - Filmic, -}; +#include "TonemappingStage.h" class ToneMappingConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(float exposure MEMBER exposure WRITE setExposure); - Q_PROPERTY(int curve MEMBER curve WRITE setCurve); + Q_PROPERTY(bool debug MEMBER debug WRITE setDebug NOTIFY dirty); + Q_PROPERTY(int curve MEMBER curve WRITE setCurve NOTIFY dirty); + Q_PROPERTY(float exposure MEMBER exposure WRITE setExposure NOTIFY dirty); public: ToneMappingConfig() : render::Job::Config(true) {} + void setDebug(bool newDebug) { debug = newDebug; emit dirty(); } + void setCurve(int newCurve) { curve = std::max(0, std::min((int)TonemappingCurve::FILMIC, newCurve)); emit dirty(); } void setExposure(float newExposure) { exposure = newExposure; emit dirty(); } - void setCurve(int newCurve) { curve = std::max((int)ToneCurve::None, std::min((int)ToneCurve::Filmic, newCurve)); emit dirty(); } - - float exposure{ 0.0f }; - int curve{ (int)ToneCurve::Gamma22 }; + bool debug { false }; + int curve { (int)TonemappingCurve::SRGB }; + float exposure { 0.0f }; signals: void dirty(); @@ -49,17 +45,14 @@ signals: class ToneMapAndResample { public: - ToneMapAndResample(size_t depth); + ToneMapAndResample(); virtual ~ToneMapAndResample() {} + void setCurve(TonemappingCurve curve); void setExposure(float exposure); - float getExposure() const { return _parametersBuffer.get()._exposure; } - void setToneCurve(ToneCurve curve); - ToneCurve getToneCurve() const { return (ToneCurve)_parametersBuffer.get()._toneCurve; } - - // Inputs: lightingFramebuffer, destinationFramebuffer - using Input = render::VaryingSet2; + // Inputs: lightingFramebuffer, destinationFramebuffer, tonemappingFrame + using Input = render::VaryingSet3; using Output = gpu::FramebufferPointer; using Config = ToneMappingConfig; using JobModel = render::Job::ModelIO; @@ -73,22 +66,19 @@ protected: gpu::FramebufferPointer _destinationFrameBuffer; - float _factor { 2.0f }; - size_t _depth { 0 }; - - gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer); - private: gpu::PipelinePointer _blitLightBuffer; + float _exposure { 0.0f }; + + bool _debug { false }; + TonemappingCurve _debugCurve { TonemappingCurve::SRGB }; + float _debugExposure { 0.0f }; // Class describing the uniform buffer with all the parameters common to the tone mapping shaders class Parameters { public: - float _exposure = 0.0f; float _twoPowExposure = 1.0f; - glm::vec2 spareA; - int _toneCurve = (int)ToneCurve::Gamma22; - glm::vec3 spareB; + int _toneCurve = (int)TonemappingCurve::SRGB; Parameters() {} }; diff --git a/libraries/render-utils/src/TonemappingStage.cpp b/libraries/render-utils/src/TonemappingStage.cpp new file mode 100644 index 0000000000..dcfe3c28c4 --- /dev/null +++ b/libraries/render-utils/src/TonemappingStage.cpp @@ -0,0 +1,56 @@ +// +// TonemappingStage.cpp +// +// Created by HifiExperiments on 6/24/24 +// Copyright 2024 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 "TonemappingStage.h" + +#include + +std::string TonemappingStage::_stageName { "TONEMAPPING_STAGE"}; +const TonemappingStage::Index TonemappingStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; + +TonemappingStage::Index TonemappingStage::findTonemapping(const TonemappingPointer& tonemapping) const { + auto found = _tonemappingMap.find(tonemapping); + if (found != _tonemappingMap.end()) { + return INVALID_INDEX; + } else { + return (*found).second; + } +} + +TonemappingStage::Index TonemappingStage::addTonemapping(const TonemappingPointer& tonemapping) { + auto found = _tonemappingMap.find(tonemapping); + if (found == _tonemappingMap.end()) { + auto tonemappingId = _tonemappings.newElement(tonemapping); + // Avoid failing to allocate a tonemapping, just pass + if (tonemappingId != INVALID_INDEX) { + // Insert the tonemapping and its index in the reverse map + _tonemappingMap.insert(TonemappingMap::value_type(tonemapping, tonemappingId)); + } + return tonemappingId; + } else { + return (*found).second; + } +} + +TonemappingStage::TonemappingPointer TonemappingStage::removeTonemapping(Index index) { + TonemappingPointer removed = _tonemappings.freeElement(index); + if (removed) { + _tonemappingMap.erase(removed); + } + return removed; +} + +TonemappingStageSetup::TonemappingStageSetup() {} + +void TonemappingStageSetup::run(const render::RenderContextPointer& renderContext) { + auto stage = renderContext->_scene->getStage(TonemappingStage::getName()); + if (!stage) { + renderContext->_scene->resetStage(TonemappingStage::getName(), std::make_shared()); + } +} diff --git a/libraries/render-utils/src/TonemappingStage.h b/libraries/render-utils/src/TonemappingStage.h new file mode 100644 index 0000000000..15161094a2 --- /dev/null +++ b/libraries/render-utils/src/TonemappingStage.h @@ -0,0 +1,84 @@ +// +// TonemappingStage.h +// +// Created by HifiExperiments on 6/24/24 +// Copyright 2024 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_render_utils_TonemappingStage_h +#define hifi_render_utils_TonemappingStage_h + +#include +#include +#include +#include +#include + +#include +#include +#include + +// Tonemapping stage to set up tonemapping-related rendering tasks +class TonemappingStage : public render::Stage { +public: + static std::string _stageName; + static const std::string& getName() { return _stageName; } + + using Index = render::indexed_container::Index; + static const Index INVALID_INDEX; + static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } + + using TonemappingPointer = graphics::TonemappingPointer; + using Tonemappings = render::indexed_container::IndexedPointerVector; + using TonemappingMap = std::unordered_map; + + using TonemappingIndices = std::vector; + + Index findTonemapping(const TonemappingPointer& tonemapping) const; + Index addTonemapping(const TonemappingPointer& tonemapping); + + TonemappingPointer removeTonemapping(Index index); + + bool checkTonemappingId(Index index) const { return _tonemappings.checkIndex(index); } + + Index getNumTonemappings() const { return _tonemappings.getNumElements(); } + Index getNumFreeTonemappings() const { return _tonemappings.getNumFreeIndices(); } + Index getNumAllocatedTonemappings() const { return _tonemappings.getNumAllocatedIndices(); } + + TonemappingPointer getTonemapping(Index tonemappingId) const { + return _tonemappings.get(tonemappingId); + } + + Tonemappings _tonemappings; + TonemappingMap _tonemappingMap; + + class Frame { + public: + Frame() {} + + void clear() { _tonemappings.clear(); } + + void pushTonemapping(TonemappingStage::Index index) { _tonemappings.emplace_back(index); } + + TonemappingStage::TonemappingIndices _tonemappings; + }; + using FramePointer = std::shared_ptr; + + Frame _currentFrame; +}; +using TonemappingStagePointer = std::shared_ptr; + +class TonemappingStageSetup { +public: + using JobModel = render::Job::Model; + + TonemappingStageSetup(); + void run(const render::RenderContextPointer& renderContext); + +protected: +}; + +#endif diff --git a/libraries/render-utils/src/UpdateSceneTask.cpp b/libraries/render-utils/src/UpdateSceneTask.cpp index be61953073..3fca91ccb9 100644 --- a/libraries/render-utils/src/UpdateSceneTask.cpp +++ b/libraries/render-utils/src/UpdateSceneTask.cpp @@ -15,6 +15,8 @@ #include "BackgroundStage.h" #include "HazeStage.h" #include "BloomStage.h" +#include "TonemappingStage.h" +#include "AmbientOcclusionStage.h" #include #include #include "DeferredLightingEffect.h" @@ -24,6 +26,8 @@ void UpdateSceneTask::build(JobModel& task, const render::Varying& input, render task.addJob("BackgroundStageSetup"); task.addJob("HazeStageSetup"); task.addJob("BloomStageSetup"); + task.addJob("TonemappingStageSetup"); + task.addJob("AmbientOcclusionStageSetup"); task.addJob("TransitionStageSetup"); task.addJob("HighlightStageSetup"); diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index 5332e13816..397d1bce52 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -26,6 +26,7 @@ #include "DeferredLightingEffect.h" #include "BloomStage.h" +#include "TonemappingStage.h" namespace ru { using render_utils::slot::texture::Texture; @@ -71,6 +72,14 @@ void SetupZones::run(const RenderContextPointer& context, const Input& input) { assert(bloomStage); bloomStage->_currentFrame.clear(); + auto tonemappingStage = context->_scene->getStage(); + assert(tonemappingStage); + tonemappingStage->_currentFrame.clear(); + + auto ambientOcclusionStage = context->_scene->getStage(); + assert(ambientOcclusionStage); + ambientOcclusionStage->_currentFrame.clear(); + // call render over the zones to grab their components in the correct order first... render::renderItems(context, input); @@ -80,6 +89,8 @@ void SetupZones::run(const RenderContextPointer& context, const Input& input) { backgroundStage->_currentFrame.pushBackground(0); hazeStage->_currentFrame.pushHaze(0); bloomStage->_currentFrame.pushBloom(INVALID_INDEX); + tonemappingStage->_currentFrame.pushTonemapping(0); + ambientOcclusionStage->_currentFrame.pushAmbientOcclusion(0); } const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() { diff --git a/libraries/render-utils/src/toneMapping.slf b/libraries/render-utils/src/toneMapping.slf index 32aa2b0788..0c336c8563 100644 --- a/libraries/render-utils/src/toneMapping.slf +++ b/libraries/render-utils/src/toneMapping.slf @@ -16,8 +16,8 @@ <@include render-utils/ShaderConstants.h@> struct ToneMappingParams { - vec4 _exp_2powExp_s0_s1; - ivec4 _toneCurve_s0_s1_s2; + float _2powExp; + int _toneCurve; }; const float GAMMA_22 = 2.2; @@ -31,17 +31,17 @@ LAYOUT(binding=RENDER_UTILS_BUFFER_TM_PARAMS) uniform toneMappingParamsBuffer { ToneMappingParams params; }; float getTwoPowExposure() { - return params._exp_2powExp_s0_s1.y; + return params._2powExp; } int getToneCurve() { - return params._toneCurve_s0_s1_s2.x; + return params._toneCurve; } LAYOUT(binding=RENDER_UTILS_TEXTURE_TM_COLOR) uniform sampler2D colorMap; layout(location=0) in vec2 varTexCoord0; layout(location=0) out vec4 outFragColor; - + void main(void) { <@if HIFI_USE_MIRRORED@> vec4 fragColorRaw = texture(colorMap, vec2(1.0 - varTexCoord0.x, varTexCoord0.y)); diff --git a/libraries/shared/src/AmbientOcclusionTechnique.cpp b/libraries/shared/src/AmbientOcclusionTechnique.cpp new file mode 100644 index 0000000000..d34f1e247e --- /dev/null +++ b/libraries/shared/src/AmbientOcclusionTechnique.cpp @@ -0,0 +1,24 @@ +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 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 "AmbientOcclusionTechnique.h" + +const char* ambientOcclusionTechniqueNames[] = { + "ssao", + "hbao" +}; + +static const size_t AO_TECHNIQUE_NAMES = (sizeof(ambientOcclusionTechniqueNames) / sizeof(ambientOcclusionTechniqueNames[0])); + +QString AmbientOcclusionTechniqueHelpers::getNameForAmbientOcclusionTechnique(AmbientOcclusionTechnique technique) { + if (((int)technique <= 0) || ((int)technique >= (int)AO_TECHNIQUE_NAMES)) { + technique = (AmbientOcclusionTechnique)0; + } + + return ambientOcclusionTechniqueNames[(int)technique]; +} diff --git a/libraries/shared/src/AmbientOcclusionTechnique.h b/libraries/shared/src/AmbientOcclusionTechnique.h new file mode 100644 index 0000000000..15ce034606 --- /dev/null +++ b/libraries/shared/src/AmbientOcclusionTechnique.h @@ -0,0 +1,38 @@ +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 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_AmbientOcclusionTechnique_h +#define hifi_AmbientOcclusionTechnique_h + +#include "QString" + +/*@jsdoc + *

    The technique used for calculating ambient occlusion. Different techniques have different tradeoffs.

    + * + * + * + * + * + * + * + * + *
    ValueDescription
    "ssao"A basic screen-space AO effect.
    "hbao"A form of SSAO that uses the depth buffer for better AO detail, sometimes at the expense of performance.
    + * @typedef {string} AmbientOcclusionTechnique + */ + +enum class AmbientOcclusionTechnique { + SSAO = 0, + HBAO, +}; + +class AmbientOcclusionTechniqueHelpers { +public: + static QString getNameForAmbientOcclusionTechnique(AmbientOcclusionTechnique curve); +}; + +#endif // hifi_AmbientOcclusionTechnique_h diff --git a/libraries/shared/src/TonemappingCurve.cpp b/libraries/shared/src/TonemappingCurve.cpp new file mode 100644 index 0000000000..e65415af04 --- /dev/null +++ b/libraries/shared/src/TonemappingCurve.cpp @@ -0,0 +1,26 @@ +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 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 "TonemappingCurve.h" + +const char* tonemappingCurveNames[] = { + "rgb", + "srgb", + "reinhard", + "filmic" +}; + +static const size_t TONEMAPPING_CURVE_NAMES = (sizeof(tonemappingCurveNames) / sizeof(tonemappingCurveNames[0])); + +QString TonemappingCurveHelpers::getNameForTonemappingCurve(TonemappingCurve curve) { + if (((int)curve <= 0) || ((int)curve >= (int)TONEMAPPING_CURVE_NAMES)) { + curve = (TonemappingCurve)0; + } + + return tonemappingCurveNames[(int)curve]; +} diff --git a/libraries/shared/src/TonemappingCurve.h b/libraries/shared/src/TonemappingCurve.h new file mode 100644 index 0000000000..f13cb3d437 --- /dev/null +++ b/libraries/shared/src/TonemappingCurve.h @@ -0,0 +1,42 @@ +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 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_TonemappingCurve_h +#define hifi_TonemappingCurve_h + +#include "QString" + +/*@jsdoc + *

    The tonemapping curve applied to the final rendering.

    + * + * + * + * + * + * + * + * + * + * + *
    ValueDescription
    "rgb"No tonemapping, colors are kept in RGB.
    "srgb"Colors are converted to sRGB.
    "reinhard"Reinhard tonemapping is applied.
    "filmic"Filmic tonemapping is applied.
    + * @typedef {string} TonemappingCurve + */ + +enum class TonemappingCurve { + RGB = 0, + SRGB, + REINHARD, + FILMIC +}; + +class TonemappingCurveHelpers { +public: + static QString getNameForTonemappingCurve(TonemappingCurve curve); +}; + +#endif // hifi_TonemappingCurve_h diff --git a/scripts/developer/utilities/render/luci/ToneMapping.qml b/scripts/developer/utilities/render/luci/ToneMapping.qml index a76990e37c..3995403917 100644 --- a/scripts/developer/utilities/render/luci/ToneMapping.qml +++ b/scripts/developer/utilities/render/luci/ToneMapping.qml @@ -14,15 +14,11 @@ import "../../lib/prop" as Prop Column { anchors.left: parent.left - anchors.right: parent.right - Prop.PropScalar { - label: "Exposure" + anchors.right: parent.right + Prop.PropBool { + label: "Debug" object: Render.getConfig("RenderMainView.ToneMapping") - property: "exposure" - min: -4 - max: 4 - anchors.left: parent.left - anchors.right: parent.right + property: "debug" } Prop.PropEnum { label: "Tone Curve" @@ -35,6 +31,15 @@ Column { "Filmic", ] anchors.left: parent.left - anchors.right: parent.right - } + anchors.right: parent.right + } + Prop.PropScalar { + label: "Exposure" + object: Render.getConfig("RenderMainView.ToneMapping") + property: "exposure" + min: -4 + max: 4 + anchors.left: parent.left + anchors.right: parent.right + } } diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index 08f5d540b7..32f4ddcbe6 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -165,6 +165,15 @@ "bloom.bloomSize": { "tooltip": "The radius of bloom. The higher the value, the larger the bloom." }, + "tonemappingMode": { + "tooltip": "Configures the tonemapping applied to the final render." + }, + "tonemapping.curve": { + "tooltip": "The tonemapping curve used." + }, + "tonemapping.exposure": { + "tooltip": "The exposure used during tonemapping." + }, "audio.reverbEnabled": { "tooltip": "If reverb should be enabled for listeners in this zone." }, diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index e2cec29a9b..43258dd344 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -618,6 +618,54 @@ const GROUPS = [ } ] }, + { + id: "zone_tonemapping", + label: "ZONE TONEMAPPING", + properties: [ + { + label: "Tonemapping", + type: "dropdown", + options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, + propertyID: "tonemappingMode", + }, + { + label: "Curve", + type: "dropdown", + options: { rgb: "RGB", srgb: "sRGB", reinhard: "Reinhard", filmic: "Filmic" }, + propertyID: "tonemapping.curve", + showPropertyRule: { "tonemappingMode": "enabled" }, + }, + { + label: "Exposure", + type: "number-draggable", + min: -4.0, + max: 4.0, + step: 0.1, + decimals: 1, + propertyID: "tonemapping.exposure", + showPropertyRule: { "tonemappingMode": "enabled" }, + } + ] + }, + { + id: "zone_ambient_occlusion", + label: "ZONE AMBIENT OCCLUSION", + properties: [ + { + label: "Ambient Occlusion", + type: "dropdown", + options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, + propertyID: "ambientOcclusionMode", + }, + { + label: "Ambient Occlusion", + type: "dropdown", + options: { ssao: "SSAO", hbao: "HBAO" }, + propertyID: "ambientOcclusion.technique", + showPropertyRule: { "tonemappingMode": "enabled" }, + } + ] + }, { id: "zone_avatar_priority", label: "ZONE AVATAR PRIORITY", @@ -1882,8 +1930,9 @@ const GROUPS_PER_TYPE = { None: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Shape: [ 'base', 'shape', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Text: [ 'base', 'text', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], - Zone: [ 'base', 'zone', 'zone_key_light', 'zone_skybox', 'zone_ambient_light', 'zone_haze', - 'zone_bloom', 'zone_avatar_priority', 'zone_audio', 'spatial', 'behavior', 'scripts', 'physics' ], + Zone: [ 'base', 'zone', 'zone_key_light', 'zone_skybox', 'zone_ambient_light', 'zone_haze', + 'zone_bloom', 'zone_tonemapping', 'zone_ambient_occlusion', 'zone_avatar_priority', + 'zone_audio', 'spatial', 'behavior', 'scripts', 'physics' ], Model: [ 'base', 'model', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Image: [ 'base', 'image', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Web: [ 'base', 'web', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], From b5da8e7d5cf2aad1f2a171d32cd354151752cd4d Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Wed, 26 Jun 2024 22:37:30 -0700 Subject: [PATCH 047/109] wip ambient occlusion --- .../src/scripting/RenderScriptingInterface.h | 13 +- .../src/RenderableZoneEntityItem.cpp | 2 +- .../src/AmbientOcclusionPropertyGroup.cpp | 32 +-- .../src/AmbientOcclusionPropertyGroup.h | 17 +- .../entities/src/EntityItemProperties.cpp | 13 +- libraries/entities/src/EntityPropertyFlags.h | 2 +- libraries/entities/src/ZoneEntityItem.cpp | 4 +- .../graphics/src/graphics/AmbientOcclusion.h | 6 +- .../src/AmbientOcclusionEffect.cpp | 183 +++++++++--------- .../render-utils/src/AmbientOcclusionEffect.h | 78 ++++---- .../src/DeferredLightingEffect.cpp | 10 - .../render-utils/src/DeferredLightingEffect.h | 2 - libraries/render-utils/src/LightingModel.h | 93 ++++----- libraries/render-utils/src/ZoneRenderer.cpp | 1 + .../utilities/render/ambientOcclusionPass.qml | 5 +- .../create/assets/data/createAppTooltips.json | 33 ++++ .../html/js/entityProperties.js | 74 ++++++- .../html/tabs/zone_ambient_occlusion.png | Bin 0 -> 785 bytes .../html/tabs/zone_tonemapping.png | Bin 0 -> 673 bytes 19 files changed, 338 insertions(+), 230 deletions(-) create mode 100644 scripts/system/create/entityProperties/html/tabs/zone_ambient_occlusion.png create mode 100644 scripts/system/create/entityProperties/html/tabs/zone_tonemapping.png diff --git a/interface/src/scripting/RenderScriptingInterface.h b/interface/src/scripting/RenderScriptingInterface.h index 2025c71510..73ef077c3c 100644 --- a/interface/src/scripting/RenderScriptingInterface.h +++ b/interface/src/scripting/RenderScriptingInterface.h @@ -233,18 +233,17 @@ private: mutable ReadWriteLockable _renderSettingLock; // Runtime value of each settings - int _renderMethod{ RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED }; - bool _shadowsEnabled{ true }; - bool _ambientOcclusionEnabled{ false }; - AntialiasingConfig::Mode _antialiasingMode{ AntialiasingConfig::Mode::NONE }; - float _viewportResolutionScale{ 1.0f }; + int _renderMethod { RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED }; + bool _shadowsEnabled { true }; + bool _ambientOcclusionEnabled { true }; + AntialiasingConfig::Mode _antialiasingMode { AntialiasingConfig::Mode::NONE }; + float _viewportResolutionScale { 1.0f }; QString _fullScreenScreen; - // Actual settings saved on disk Setting::Handle _renderMethodSetting { "renderMethod", RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED }; Setting::Handle _shadowsEnabledSetting { "shadowsEnabled", true }; - Setting::Handle _ambientOcclusionEnabledSetting { "ambientOcclusionEnabled", false }; + Setting::Handle _ambientOcclusionEnabledSetting { "ambientOcclusionEnabled", true }; //Setting::Handle _antialiasingModeSetting { "antialiasingMode", AntialiasingConfig::Mode::TAA }; Setting::Handle _antialiasingModeSetting { "antialiasingMode", AntialiasingConfig::Mode::NONE }; Setting::Handle _viewportResolutionScaleSetting { "viewportResolutionScale", 1.0f }; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 9bc6de6627..8a6768f235 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -450,7 +450,7 @@ void ZoneEntityRenderer::updateAmbientOcclusionFromEntity(const TypedEntityPoint ambientOcclusion->setAORadius(_ambientOcclusionProperties.getAORadius()); ambientOcclusion->setAOObscuranceLevel(_ambientOcclusionProperties.getAOObscuranceLevel()); ambientOcclusion->setAOFalloffAngle(_ambientOcclusionProperties.getAOFalloffAngle()); - ambientOcclusion->setAONumSamples(_ambientOcclusionProperties.getAONumSamples()); + ambientOcclusion->setAOSamplingAmount(_ambientOcclusionProperties.getAOSamplingAmount()); ambientOcclusion->setSSAONumSpiralTurns(_ambientOcclusionProperties.getSSAONumSpiralTurns()); } diff --git a/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp b/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp index b5617cabb0..c16f2a00f3 100644 --- a/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp +++ b/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp @@ -43,7 +43,7 @@ void AmbientOcclusionPropertyGroup::copyToScriptValue(const EntityPropertyFlags& COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AmbientOcclusion, ambientOcclusion, AORadius, aoRadius); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AmbientOcclusion, ambientOcclusion, AOObscuranceLevel, aoObscuranceLevel); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AmbientOcclusion, ambientOcclusion, AOFalloffAngle, aoFalloffAngle); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, AmbientOcclusion, ambientOcclusion, AONumSamples, aoNumSamples); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, AmbientOcclusion, ambientOcclusion, AOSamplingAmount, aoSamplingAmount); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, AmbientOcclusion, ambientOcclusion, SSAONumSpiralTurns, ssaoNumSpiralTurns); } @@ -56,7 +56,7 @@ void AmbientOcclusionPropertyGroup::copyFromScriptValue(const ScriptValue& objec COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoRadius, float, setAORadius); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoObscuranceLevel, float, setAOObscuranceLevel); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoFalloffAngle, float, setAOFalloffAngle); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoNumSamples, uint8_t, setAONumSamples); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoSamplingAmount, float, setAOSamplingAmount); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, ssaoNumSpiralTurns, float, setSSAONumSpiralTurns); } @@ -69,7 +69,7 @@ void AmbientOcclusionPropertyGroup::merge(const AmbientOcclusionPropertyGroup& o COPY_PROPERTY_IF_CHANGED(aoRadius); COPY_PROPERTY_IF_CHANGED(aoObscuranceLevel); COPY_PROPERTY_IF_CHANGED(aoFalloffAngle); - COPY_PROPERTY_IF_CHANGED(aoNumSamples); + COPY_PROPERTY_IF_CHANGED(aoSamplingAmount); COPY_PROPERTY_IF_CHANGED(ssaoNumSpiralTurns); } @@ -83,7 +83,7 @@ void AmbientOcclusionPropertyGroup::debugDump() const { qCDebug(entities) << " AORadius:" << getAORadius(); qCDebug(entities) << " AOObscuranceLevel:" << getAOObscuranceLevel(); qCDebug(entities) << " AOFalloffAngle:" << getAOFalloffAngle(); - qCDebug(entities) << " AONumSamples:" << getAONumSamples(); + qCDebug(entities) << " AOSamplingAmount:" << getAOSamplingAmount(); qCDebug(entities) << " SSAONumSpiralTurns:" << getSSAONumSpiralTurns(); } @@ -112,8 +112,8 @@ void AmbientOcclusionPropertyGroup::listChangedProperties(QList& out) { if (aoFalloffAngleChanged()) { out << "ambientOcclusion-aoFalloffAngle"; } - if (aoNumSamplesChanged()) { - out << "ambientOcclusion-aoNumSamples"; + if (aoSamplingAmountChanged()) { + out << "ambientOcclusion-aoSamplingAmount"; } if (ssaoNumSpiralTurnsChanged()) { out << "ambientOcclusion-ssaoNumSpiralTurns"; @@ -137,7 +137,7 @@ bool AmbientOcclusionPropertyGroup::appendToEditPacket(OctreePacketData* packetD APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, getAORadius()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, getAOObscuranceLevel()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, getAOFalloffAngle()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, getAONumSamples()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, getAOSamplingAmount()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, getSSAONumSpiralTurns()); return true; @@ -157,7 +157,7 @@ bool AmbientOcclusionPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& pr READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, float, setAORadius); READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, float, setAOObscuranceLevel); READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, float, setAOFalloffAngle); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, uint8_t, setAONumSamples); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, float, setAOSamplingAmount); READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, float, setSSAONumSpiralTurns); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_TECHNIQUE, Technique); @@ -168,7 +168,7 @@ bool AmbientOcclusionPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& pr DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AORadius); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AOObscuranceLevel); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AOFalloffAngle); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, AONumSamples); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, AOSamplingAmount); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, SSAONumSpiralTurns); processedBytes += bytesRead; @@ -187,7 +187,7 @@ void AmbientOcclusionPropertyGroup::markAllChanged() { _aoRadiusChanged = true; _aoObscuranceLevelChanged = true; _aoFalloffAngleChanged = true; - _aoNumSamplesChanged = true; + _aoSamplingAmountChanged = true; _ssaoNumSpiralTurnsChanged = true; } @@ -202,7 +202,7 @@ EntityPropertyFlags AmbientOcclusionPropertyGroup::getChangedProperties() const CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_RADIUS, aoRadius); CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, aoObscuranceLevel); CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, aoFalloffAngle); - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, aoNumSamples); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, aoSamplingAmount); CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, ssaoNumSpiralTurns); return changedProperties; @@ -217,7 +217,7 @@ void AmbientOcclusionPropertyGroup::getProperties(EntityItemProperties& properti COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AORadius, getAORadius); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AOObscuranceLevel, getAOObscuranceLevel); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AOFalloffAngle, getAOFalloffAngle); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AONumSamples, getAONumSamples); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AOSamplingAmount, getAOSamplingAmount); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, SSAONumSpiralTurns, getSSAONumSpiralTurns); } @@ -232,7 +232,7 @@ bool AmbientOcclusionPropertyGroup::setProperties(const EntityItemProperties& pr SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AORadius, aoRadius, setAORadius); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AOObscuranceLevel, aoObscuranceLevel, setAOObscuranceLevel); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AOFalloffAngle, aoFalloffAngle, setAOFalloffAngle); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AONumSamples, aoNumSamples, setAONumSamples); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AOSamplingAmount, aoSamplingAmount, setAOSamplingAmount); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, SSAONumSpiralTurns, ssaoNumSpiralTurns, setSSAONumSpiralTurns); return somethingChanged; @@ -249,7 +249,7 @@ EntityPropertyFlags AmbientOcclusionPropertyGroup::getEntityProperties(EncodeBit requestedProperties += PROP_AMBIENT_OCCLUSION_AO_RADIUS; requestedProperties += PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL; requestedProperties += PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE; - requestedProperties += PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES; + requestedProperties += PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT; requestedProperties += PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS; return requestedProperties; @@ -273,7 +273,7 @@ void AmbientOcclusionPropertyGroup::appendSubclassData(OctreePacketData* packetD APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, getAORadius()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, getAOObscuranceLevel()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, getAOFalloffAngle()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, getAONumSamples()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, getAOSamplingAmount()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, getSSAONumSpiralTurns()); } @@ -293,7 +293,7 @@ int AmbientOcclusionPropertyGroup::readEntitySubclassDataFromBuffer(const unsign READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, float, setAORadius); READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, float, setAOObscuranceLevel); READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, float, setAOFalloffAngle); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, uint8_t, setAONumSamples); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, float, setAOSamplingAmount); READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, float, setSSAONumSpiralTurns); return bytesRead; diff --git a/libraries/entities/src/AmbientOcclusionPropertyGroup.h b/libraries/entities/src/AmbientOcclusionPropertyGroup.h index 9a44c5c8f0..c1a734c9a4 100644 --- a/libraries/entities/src/AmbientOcclusionPropertyGroup.h +++ b/libraries/entities/src/AmbientOcclusionPropertyGroup.h @@ -29,9 +29,20 @@ class ScriptValue; /*@jsdoc * AmbientOcclusion is defined by the following properties: * @typedef {object} Entities.AmbientOcclusion - * @property {AmbientOcclusionTechnique} technique="ssao" - The AO technique used. - * TODO + * @property {AmbientOcclusionTechnique} technique="ssao" - The ambient occlusion technique used. Different techniques have + * different tradeoffs. + * @property {boolean} jitter=false - Whether or not the ambient occlusion sampling is jittered. + * @property {number} resolutionLevel=2 - How high the resolution of the ambient occlusion buffer should be. Higher levels + * mean lower resolution buffers. + * @property {number} edgeSharpness=1.0 - How much to sharpen the edges during the ambient occlusion blurring. + * @property {number} blurRadius=4 - The radius used for blurring, in pixels. + * @property {number} aoRadius=1.0 - The radius used for ambient occlusion. + * @property {number} aoObscuranceLevel=0.5 - Intensify or dim ambient occlusion. + * @property {number} aoFalloffAngle=0.25 - The falloff angle for the AO calculation. + * @property {number} aoSamplingAmount=0.5 - The fraction of AO samples to use, out of the maximum for each technique. + * @property {number} ssaoNumSpiralTurns=7.0 - The angle span used to distribute the AO samples ray directions. SSAO only. */ + class AmbientOcclusionPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers @@ -88,7 +99,7 @@ public: DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AORadius, aoRadius, float, 1.0f); DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AOObscuranceLevel, aoObscuranceLevel, float, 0.5f); DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AOFalloffAngle, aoFalloffAngle, float, 0.25f); - DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES, AONumSamples, aoNumSamples, uint8_t, 32); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, AOSamplingAmount, aoSamplingAmount, float, 0.5f); DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, SSAONumSpiralTurns, ssaoNumSpiralTurns, float, 7.0f); }; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 4f70d88d8b..1360ab5a58 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -3051,10 +3051,19 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr } { // Tonemapping ADD_GROUP_PROPERTY_TO_MAP(PROP_TONEMAPPING_CURVE, Tonemapping, tonemapping, Curve, curve); - ADD_GROUP_PROPERTY_TO_MAP(PROP_TONEMAPPING_EXPOSURE, Tonemapping, tonemapping, Exposure, exposure); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_TONEMAPPING_EXPOSURE, Tonemapping, tonemapping, Exposure, exposure, -4.0f, -4.0f); } { // Ambient Occlusion - // TODO + ADD_GROUP_PROPERTY_TO_MAP(PROP_AMBIENT_OCCLUSION_TECHNIQUE, AmbientOcclusion, ambientOcclusion, Technique, technique); + ADD_GROUP_PROPERTY_TO_MAP(PROP_AMBIENT_OCCLUSION_JITTER, AmbientOcclusion, ambientOcclusion, Jitter, jitter); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, AmbientOcclusion, ambientOcclusion, ResolutionLevel, resolutionLevel, 0, 4); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, AmbientOcclusion, ambientOcclusion, EdgeSharpness, edgeSharpness, 0.0f, 1.0f); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, AmbientOcclusion, ambientOcclusion, BlurRadius, blurRadius, 0, 15); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AmbientOcclusion, ambientOcclusion, AORadius, aoRadius, 0.01f, 2.0f); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AmbientOcclusion, ambientOcclusion, AOObscuranceLevel, aoObscuranceLevel, 0.01f, 1.0f); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AmbientOcclusion, ambientOcclusion, AOFalloffAngle, aoFalloffAngle, 0.0f, 1.0f); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, AmbientOcclusion, ambientOcclusion, AOSamplingAmount, aoSamplingAmount, 0.0f, 1.0f); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, AmbientOcclusion, ambientOcclusion, SSAONumSpiralTurns, ssaoNumSpiralTurns, 0.0f, 10.0f); } ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool); ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 87517bdd3a..f8c1790fc6 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -355,7 +355,7 @@ enum EntityPropertyList { PROP_AMBIENT_OCCLUSION_AO_RADIUS = PROP_DERIVED_50, PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL = PROP_DERIVED_51, PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE = PROP_DERIVED_52, - PROP_AMBIENT_OCCLUSION_AO_NUM_SAMPLES = PROP_DERIVED_53, + PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT = PROP_DERIVED_53, PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS = PROP_DERIVED_54, // Polyvox diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index ee431493a8..a8561f49d9 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -292,8 +292,8 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, getBloomMode()); APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, getAvatarPriority()); APPEND_ENTITY_PROPERTY(PROP_SCREENSHARE, getScreenshare()); - APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_MODE, getHazeMode()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_MODE, getBloomMode()); + APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_MODE, getTonemappingMode()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_MODE, getAmbientOcclusionMode()); } void ZoneEntityItem::debugDump() const { diff --git a/libraries/graphics/src/graphics/AmbientOcclusion.h b/libraries/graphics/src/graphics/AmbientOcclusion.h index 7579b783a3..66a1ad0ea7 100644 --- a/libraries/graphics/src/graphics/AmbientOcclusion.h +++ b/libraries/graphics/src/graphics/AmbientOcclusion.h @@ -28,7 +28,7 @@ namespace graphics { void setAORadius(const float aoRadius) { _aoRadius = aoRadius; } void setAOObscuranceLevel(const float aoObscuranceLevel) { _aoObscuranceLevel = aoObscuranceLevel; } void setAOFalloffAngle(const float aoFalloffAngle) { _aoFalloffAngle = aoFalloffAngle; } - void setAONumSamples(const uint8_t aoNumSamples) { _aoNumSamples = aoNumSamples; } + void setAOSamplingAmount(const float aoSamplingAmount) { _aoSamplingAmount = aoSamplingAmount; } void setSSAONumSpiralTurns(const float ssaoNumSpiralTurns) { _ssaoNumSpiralTurns = ssaoNumSpiralTurns; } AmbientOcclusionTechnique getTechnique() const { return _technique; } @@ -39,7 +39,7 @@ namespace graphics { float getAORadius() const { return _aoRadius; } float getAOObscuranceLevel() const { return _aoObscuranceLevel; } float getAOFalloffAngle() const { return _aoFalloffAngle; } - uint8_t getAONumSamples() const { return _aoNumSamples; } + float getAOSamplingAmount() const { return _aoSamplingAmount; } float getSSAONumSpiralTurns() const { return _ssaoNumSpiralTurns; } private: @@ -51,7 +51,7 @@ namespace graphics { float _aoRadius { 1.0f }; float _aoObscuranceLevel { 0.5f }; float _aoFalloffAngle { 0.25f }; - uint8_t _aoNumSamples { 32 }; + float _aoSamplingAmount { 0.5f }; float _ssaoNumSpiralTurns { 7.0f }; }; using AmbientOcclusionPointer = std::shared_ptr; diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 68eb1b5a06..8c14b0b0ef 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -37,6 +37,9 @@ gpu::PipelinePointer AmbientOcclusionEffect::_mipCreationPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_gatherPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_buildNormalsPipeline; +#define MAX_SSAO_SAMPLES 64.0f +#define MAX_HBAO_SAMPLES 6.0f + AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() { } @@ -205,29 +208,7 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { } AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : - render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion"), - perspectiveScale{ 1.0f }, - edgeSharpness{ 1.0f }, - blurRadius{ 4 }, - resolutionLevel{ 2 }, - - ssaoRadius{ 1.0f }, - ssaoObscuranceLevel{ 0.4f }, - ssaoFalloffAngle{ 0.15f }, - ssaoNumSpiralTurns{ 7.0f }, - ssaoNumSamples{ 32 }, - - hbaoRadius{ 0.7f }, - hbaoObscuranceLevel{ 0.75f }, - hbaoFalloffAngle{ 0.3f }, - hbaoNumSamples{ 1 }, - - horizonBased{ false }, - ditheringEnabled{ true }, - borderingEnabled{ true }, - fetchMipsEnabled{ true }, - jitterEnabled{ false }{ -} + render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion") {} void AmbientOcclusionEffectConfig::setSSAORadius(float newRadius) { ssaoRadius = std::max(0.01f, newRadius); emit dirty(); @@ -288,89 +269,105 @@ void AmbientOcclusionEffectConfig::setBlurRadius(int radius) { } AmbientOcclusionEffect::AOParameters::AOParameters() { - _resolutionInfo = glm::vec4{ 0.0f }; - _radiusInfo = glm::vec4{ 0.0f }; - _ditheringInfo = glm::vec4{ 0.0f }; - _sampleInfo = glm::vec4{ 0.0f }; - _falloffInfo = glm::vec4{ 0.0f }; + _resolutionInfo = glm::vec4(0.0f); + _radiusInfo = glm::vec4(0.0f); + _ditheringInfo = glm::vec4(0.0f); + _sampleInfo = glm::vec4(0.0f); + _falloffInfo = glm::vec4(0.0f); } AmbientOcclusionEffect::BlurParameters::BlurParameters() { _blurInfo = { 1.0f, 2.0f, 0.0f, 3.0f }; } -AmbientOcclusionEffect::AmbientOcclusionEffect() { +void AmbientOcclusionEffect::configure(const Config& config) { + _debug = config.debug; + _debugAmbientOcclusion->setTechnique(config.horizonBased ? AmbientOcclusionTechnique::HBAO : AmbientOcclusionTechnique::SSAO); + _debugAmbientOcclusion->setJitter(config.jitterEnabled); + _debugAmbientOcclusion->setResolutionLevel(config.resolutionLevel); + _debugAmbientOcclusion->setEdgeSharpness(config.edgeSharpness); + _debugAmbientOcclusion->setBlurRadius(config.blurRadius); + _debugAmbientOcclusion->setAORadius(config.horizonBased ? config.hbaoRadius : config.ssaoRadius); + _debugAmbientOcclusion->setAOObscuranceLevel(config.horizonBased ? config.hbaoObscuranceLevel : config.ssaoObscuranceLevel); + _debugAmbientOcclusion->setAOFalloffAngle(config.horizonBased ? config.hbaoFalloffAngle : config.ssaoFalloffAngle); + _debugAmbientOcclusion->setAOSamplingAmount(config.horizonBased ? (config.hbaoNumSamples / MAX_HBAO_SAMPLES) : + (config.ssaoNumSamples / MAX_SSAO_SAMPLES)); + _debugAmbientOcclusion->setSSAONumSpiralTurns(config.ssaoNumSpiralTurns); + + _perspectiveScale = config.perspectiveScale; + _ditheringEnabled = config.ditheringEnabled; + _borderingEnabled = config.borderingEnabled; + _fetchMipsEnabled = config.fetchMipsEnabled; } -void AmbientOcclusionEffect::configure(const Config& config) { +void AmbientOcclusionEffect::updateParameters(const graphics::AmbientOcclusionPointer ambientOcclusion) { bool shouldUpdateBlurs = false; bool shouldUpdateTechnique = false; - _isJitterEnabled = config.jitterEnabled; - if (!_framebuffer) { _framebuffer = std::make_shared(); shouldUpdateBlurs = true; } // Update bilateral blur - if (config.blurRadius != _hblurParametersBuffer->getBlurRadius() || _blurEdgeSharpness != config.edgeSharpness) { + if (ambientOcclusion->getBlurRadius() != _hblurParametersBuffer->getBlurRadius() || _blurEdgeSharpness != ambientOcclusion->getEdgeSharpness()) { const float BLUR_EDGE_DISTANCE_SCALE = float(10000 * SSAO_DEPTH_KEY_SCALE); const float BLUR_EDGE_NORMAL_SCALE = 2.0f; auto& hblur = _hblurParametersBuffer.edit()._blurInfo; auto& vblur = _vblurParametersBuffer.edit()._blurInfo; - float blurRadialSigma = float(config.blurRadius) * 0.5f; + float blurRadialSigma = float(ambientOcclusion->getBlurRadius()) * 0.5f; float blurRadialScale = 1.0f / (2.0f*blurRadialSigma*blurRadialSigma); - glm::vec3 blurScales = -glm::vec3(blurRadialScale, glm::vec2(BLUR_EDGE_DISTANCE_SCALE, BLUR_EDGE_NORMAL_SCALE) * config.edgeSharpness); + glm::vec3 blurScales = -glm::vec3(blurRadialScale, glm::vec2(BLUR_EDGE_DISTANCE_SCALE, BLUR_EDGE_NORMAL_SCALE) * ambientOcclusion->getEdgeSharpness()); - _blurEdgeSharpness = config.edgeSharpness; + _blurEdgeSharpness = ambientOcclusion->getEdgeSharpness(); hblur.x = blurScales.x; hblur.y = blurScales.y; hblur.z = blurScales.z; - hblur.w = (float)config.blurRadius; + hblur.w = (float)ambientOcclusion->getBlurRadius(); vblur.x = blurScales.x; vblur.y = blurScales.y; vblur.z = blurScales.z; - vblur.w = (float)config.blurRadius; + vblur.w = (float)ambientOcclusion->getBlurRadius(); } - if (_aoParametersBuffer->isHorizonBased() != config.horizonBased) { + if (_perspectiveScale != _aoParametersBuffer->getPerspectiveScale()) { + _aoParametersBuffer.edit()._resolutionInfo.z = _perspectiveScale; + } + + if (_ditheringEnabled != _aoParametersBuffer->isDitheringEnabled()) { + auto& current = _aoParametersBuffer.edit()._ditheringInfo; + current.x = (float)_ditheringEnabled; + } + + if (_borderingEnabled != _aoParametersBuffer->isBorderingEnabled()) { + auto& current = _aoParametersBuffer.edit()._ditheringInfo; + current.w = (float)_borderingEnabled; + } + + if (_fetchMipsEnabled != _aoParametersBuffer->isFetchMipsEnabled()) { + auto& current = _aoParametersBuffer.edit()._sampleInfo; + current.w = (float)_fetchMipsEnabled; + } + + bool horizonBased = ambientOcclusion->getTechnique() == AmbientOcclusionTechnique::HBAO; + if (_aoParametersBuffer->isHorizonBased() != horizonBased) { auto& current = _aoParametersBuffer.edit()._resolutionInfo; - current.y = config.horizonBased & 1; + current.y = horizonBased & 1; shouldUpdateTechnique = true; } - if (config.fetchMipsEnabled != _aoParametersBuffer->isFetchMipsEnabled()) { - auto& current = _aoParametersBuffer.edit()._sampleInfo; - current.w = (float)config.fetchMipsEnabled; - } - - if (config.perspectiveScale != _aoParametersBuffer->getPerspectiveScale()) { - _aoParametersBuffer.edit()._resolutionInfo.z = config.perspectiveScale; - } - - if (config.resolutionLevel != _aoParametersBuffer->getResolutionLevel()) { + if (ambientOcclusion->getResolutionLevel() != _aoParametersBuffer->getResolutionLevel()) { auto& current = _aoParametersBuffer.edit()._resolutionInfo; - current.x = (float)config.resolutionLevel; + current.x = (float)ambientOcclusion->getResolutionLevel(); shouldUpdateBlurs = true; } - if (config.ditheringEnabled != _aoParametersBuffer->isDitheringEnabled()) { - auto& current = _aoParametersBuffer.edit()._ditheringInfo; - current.x = (float)config.ditheringEnabled; - } - - if (config.borderingEnabled != _aoParametersBuffer->isBorderingEnabled()) { - auto& current = _aoParametersBuffer.edit()._ditheringInfo; - current.w = (float)config.borderingEnabled; - } - - if (config.horizonBased) { + if (horizonBased) { // Configure for HBAO - const auto& radius = config.hbaoRadius; + const auto& radius = ambientOcclusion->getAORadius(); if (shouldUpdateTechnique || radius != _aoParametersBuffer->getRadius()) { auto& current = _aoParametersBuffer.edit()._radiusInfo; current.x = radius; @@ -378,31 +375,32 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.z = 1.0f / current.y; } - if (shouldUpdateTechnique || config.hbaoObscuranceLevel != _aoParametersBuffer->getObscuranceLevel()) { + if (shouldUpdateTechnique || ambientOcclusion->getAOObscuranceLevel() != _aoParametersBuffer->getObscuranceLevel()) { auto& current = _aoParametersBuffer.edit()._radiusInfo; - current.w = config.hbaoObscuranceLevel; + current.w = ambientOcclusion->getAOObscuranceLevel(); } - if (shouldUpdateTechnique || config.hbaoFalloffAngle != _aoParametersBuffer->getFalloffAngle()) { + if (shouldUpdateTechnique || ambientOcclusion->getAOFalloffAngle() != _aoParametersBuffer->getFalloffAngle()) { auto& current = _aoParametersBuffer.edit()._falloffInfo; - current.x = config.hbaoFalloffAngle; + current.x = ambientOcclusion->getAOFalloffAngle(); current.y = 1.0f / (1.0f - current.x); // Compute sin from cos - current.z = sqrtf(1.0f - config.hbaoFalloffAngle * config.hbaoFalloffAngle); + current.z = sqrtf(1.0f - ambientOcclusion->getAOFalloffAngle() * ambientOcclusion->getAOFalloffAngle()); current.w = 1.0f / current.z; } - if (shouldUpdateTechnique || config.hbaoNumSamples != _aoParametersBuffer->getNumSamples()) { + const int numSamples = ambientOcclusion->getAOSamplingAmount() * MAX_SSAO_SAMPLES; + if (shouldUpdateTechnique || numSamples != _aoParametersBuffer->getNumSamples()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; - current.x = config.hbaoNumSamples; - current.y = 1.0f / config.hbaoNumSamples; + current.x = numSamples; + current.y = 1.0f / numSamples; updateRandomSamples(); updateJitterSamples(); } } else { // Configure for SSAO const double RADIUS_POWER = 6.0; - const auto& radius = config.ssaoRadius; + const auto& radius = ambientOcclusion->getAORadius(); if (shouldUpdateTechnique || radius != _aoParametersBuffer->getRadius()) { auto& current = _aoParametersBuffer.edit()._radiusInfo; current.x = radius; @@ -410,25 +408,26 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.z = (float)(10.0 / pow((double)radius, RADIUS_POWER)); } - if (shouldUpdateTechnique || config.ssaoObscuranceLevel != _aoParametersBuffer->getObscuranceLevel()) { + if (shouldUpdateTechnique || ambientOcclusion->getAOObscuranceLevel() != _aoParametersBuffer->getObscuranceLevel()) { auto& current = _aoParametersBuffer.edit()._radiusInfo; - current.w = config.ssaoObscuranceLevel; + current.w = ambientOcclusion->getAOObscuranceLevel(); } - if (shouldUpdateTechnique || config.ssaoFalloffAngle != _aoParametersBuffer->getFalloffAngle()) { + if (shouldUpdateTechnique || ambientOcclusion->getAOFalloffAngle() != _aoParametersBuffer->getFalloffAngle()) { auto& current = _aoParametersBuffer.edit()._falloffInfo; - current.x = config.ssaoFalloffAngle; + current.x = ambientOcclusion->getAOFalloffAngle(); } - if (shouldUpdateTechnique || config.ssaoNumSpiralTurns != _aoParametersBuffer->getNumSpiralTurns()) { + if (shouldUpdateTechnique || ambientOcclusion->getSSAONumSpiralTurns() != _aoParametersBuffer->getNumSpiralTurns()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; - current.z = config.ssaoNumSpiralTurns; + current.z = ambientOcclusion->getSSAONumSpiralTurns(); } - if (shouldUpdateTechnique || config.ssaoNumSamples != _aoParametersBuffer->getNumSamples()) { + const int numSamples = ambientOcclusion->getAOSamplingAmount() * MAX_HBAO_SAMPLES; + if (shouldUpdateTechnique || numSamples != _aoParametersBuffer->getNumSamples()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; - current.x = config.ssaoNumSamples; - current.y = 1.0f / config.ssaoNumSamples; + current.x = numSamples; + current.y = 1.0f / numSamples; updateRandomSamples(); updateJitterSamples(); } @@ -600,12 +599,23 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte RenderArgs* args = renderContext->args; const auto& lightingModel = input.get0(); + const auto ambientOcclusionFrame = input.get4(); - if (!lightingModel->isAmbientOcclusionEnabled()) { + const auto& ambientOcclusionStage = renderContext->_scene->getStage(); + graphics::AmbientOcclusionPointer ambientOcclusion; + if (_debug) { + ambientOcclusion = _debugAmbientOcclusion; + } else if (ambientOcclusionStage && ambientOcclusionFrame->_ambientOcclusions.size()) { + ambientOcclusion = ambientOcclusionStage->getAmbientOcclusion(ambientOcclusionFrame->_ambientOcclusions.front()); + } + + if (!ambientOcclusion || !lightingModel->isAmbientOcclusionEnabled()) { output.edit0().reset(); return; } + updateParameters(ambientOcclusion); + const auto& frameTransform = input.get1(); const auto& linearDepthFramebuffer = input.get3(); @@ -657,7 +667,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #endif // Update sample rotation - if (_isJitterEnabled) { + if (ambientOcclusion->getJitter()) { updateJitterSamples(); _frameId = (_frameId + 1) % (SSAO_RANDOM_SAMPLE_COUNT); } @@ -831,11 +841,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte config->setGPUBatchRunTime(_gpuTimer->getGPUAverage(), _gpuTimer->getBatchAverage()); } -DebugAmbientOcclusion::DebugAmbientOcclusion() { -} - void DebugAmbientOcclusion::configure(const Config& config) { - _showCursorPixel = config.showCursorPixel; auto cursorPos = glm::vec2(_parametersBuffer->pixelInfo); @@ -916,9 +922,6 @@ void DebugAmbientOcclusion::run(const render::RenderContextPointer& renderContex batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, fullResDepthTexture); batch.draw(gpu::TRIANGLE_STRIP, 4); - batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, nullptr); }); - } - diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 100de27e1a..3c49dba3d4 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -71,15 +71,16 @@ protected: #endif glm::ivec2 _frameSize; - int _resolutionLevel{ 0 }; - int _depthResolutionLevel{ 0 }; - bool _isStereo{ false }; + int _resolutionLevel { 0 }; + int _depthResolutionLevel { 0 }; + bool _isStereo { false }; }; using AmbientOcclusionFramebufferPointer = std::shared_ptr; class AmbientOcclusionEffectConfig : public render::GPUJobConfig::Persistent { Q_OBJECT + Q_PROPERTY(bool debug MEMBER debug NOTIFY dirty) Q_PROPERTY(bool horizonBased MEMBER horizonBased NOTIFY dirty) Q_PROPERTY(bool ditheringEnabled MEMBER ditheringEnabled NOTIFY dirty) Q_PROPERTY(bool borderingEnabled MEMBER borderingEnabled NOTIFY dirty) @@ -109,42 +110,44 @@ public: const int MAX_RESOLUTION_LEVEL = 4; const int MAX_BLUR_RADIUS = 15; - void setEdgeSharpness(float sharpness); void setResolutionLevel(int level); + void setEdgeSharpness(float sharpness); void setBlurRadius(int radius); void setSSAORadius(float newRadius); void setSSAOObscuranceLevel(float level); void setSSAOFalloffAngle(float bias); - void setSSAONumSpiralTurns(float turns); void setSSAONumSamples(int samples); + void setSSAONumSpiralTurns(float turns); void setHBAORadius(float newRadius); void setHBAOObscuranceLevel(float level); void setHBAOFalloffAngle(float bias); void setHBAONumSamples(int samples); - float perspectiveScale; - float edgeSharpness; - int blurRadius; // 0 means no blurring - int resolutionLevel; + bool debug { false }; - float ssaoRadius; - float ssaoObscuranceLevel; // intensify or dim down the obscurance effect - float ssaoFalloffAngle; - float ssaoNumSpiralTurns; // defining an angle span to distribute the samples ray directions - int ssaoNumSamples; + bool jitterEnabled { false }; // Add small jittering to AO samples at each frame + bool horizonBased { false }; // Use horizon based AO + int resolutionLevel { 2 }; + float edgeSharpness { 1.0f }; + int blurRadius { 4 }; // 0 means no blurring - float hbaoRadius; - float hbaoObscuranceLevel; // intensify or dim down the obscurance effect - float hbaoFalloffAngle; - int hbaoNumSamples; + float ssaoRadius { 1.0f }; + float ssaoObscuranceLevel { 0.4f }; // intensify or dim down the obscurance effect + float ssaoFalloffAngle { 0.15f }; + int ssaoNumSamples { 32 }; + float ssaoNumSpiralTurns { 7.0f }; // defining an angle span to distribute the samples ray directions - bool horizonBased; // Use horizon based AO - bool ditheringEnabled; // randomize the distribution of taps per pixel, should always be true - bool borderingEnabled; // avoid evaluating information from non existing pixels out of the frame, should always be true - bool fetchMipsEnabled; // fetch taps in sub mips to otpimize cache, should always be true - bool jitterEnabled; // Add small jittering to AO samples at each frame + float hbaoRadius { 0.7f }; + float hbaoObscuranceLevel { 0.75f }; // intensify or dim down the obscurance effect + float hbaoFalloffAngle { 0.3f }; + int hbaoNumSamples { 1 }; + + float perspectiveScale { 1.0f }; + bool ditheringEnabled { true }; // randomize the distribution of taps per pixel, should always be true + bool borderingEnabled { true }; // avoid evaluating information from non existing pixels out of the frame, should always be true + bool fetchMipsEnabled { true }; // fetch taps in sub mips to otpimize cache, should always be true signals: void dirty(); @@ -160,7 +163,7 @@ public: using Config = AmbientOcclusionEffectConfig; using JobModel = render::Job::ModelIO; - AmbientOcclusionEffect(); + AmbientOcclusionEffect() {} void configure(const Config& config); void run(const render::RenderContextPointer& renderContext, const Input& input, Output& output); @@ -168,7 +171,6 @@ public: // Class describing the uniform buffer with all the parameters common to the AO shaders class AOParameters : public AmbientOcclusionParams { public: - AOParameters(); int getResolutionLevel() const { return _resolutionInfo.x; } @@ -184,7 +186,6 @@ public: bool isDitheringEnabled() const { return _ditheringInfo.x != 0.0f; } bool isBorderingEnabled() const { return _ditheringInfo.w != 0.0f; } bool isHorizonBased() const { return _resolutionInfo.y != 0.0f; } - }; using AOParametersBuffer = gpu::StructBuffer; @@ -193,17 +194,15 @@ private: // Class describing the uniform buffer with all the parameters common to the bilateral blur shaders class BlurParameters : public AmbientOcclusionBlurParams { public: - BlurParameters(); float getEdgeSharpness() const { return (float)_blurInfo.x; } int getBlurRadius() const { return (int)_blurInfo.w; } - }; using BlurParametersBuffer = gpu::StructBuffer; - using FrameParametersBuffer = gpu::StructBuffer< AmbientOcclusionFrameParams>; + void updateParameters(const graphics::AmbientOcclusionPointer ambientOcclusion); void updateBlurParameters(); void updateFramebufferSizes(); void updateRandomSamples(); @@ -215,7 +214,7 @@ private: FrameParametersBuffer _aoFrameParametersBuffer[SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT]; BlurParametersBuffer _vblurParametersBuffer; BlurParametersBuffer _hblurParametersBuffer; - float _blurEdgeSharpness{ 0.0f }; + float _blurEdgeSharpness { 0.0f }; static const gpu::PipelinePointer& getOcclusionPipeline(); static const gpu::PipelinePointer& getBilateralBlurPipeline(); @@ -231,9 +230,14 @@ private: AmbientOcclusionFramebufferPointer _framebuffer; std::array _randomSamples; - int _frameId{ 0 }; - bool _isJitterEnabled{ true }; - + int _frameId { 0 }; + bool _debug { false }; + float _perspectiveScale { 1.0f }; + bool _ditheringEnabled { true }; + bool _borderingEnabled { true }; + bool _fetchMipsEnabled { true }; + graphics::AmbientOcclusionPointer _debugAmbientOcclusion { std::make_shared() }; + gpu::RangeTimerPointer _gpuTimer; friend class DebugAmbientOcclusion; @@ -248,8 +252,8 @@ class DebugAmbientOcclusionConfig : public render::Job::Config { public: DebugAmbientOcclusionConfig() : render::Job::Config(false) {} - bool showCursorPixel{ false }; - glm::vec2 debugCursorTexcoord{ 0.5f, 0.5f }; + bool showCursorPixel { false }; + glm::vec2 debugCursorTexcoord { 0.5f, 0.5f }; signals: void dirty(); @@ -262,7 +266,7 @@ public: using Config = DebugAmbientOcclusionConfig; using JobModel = render::Job::ModelI; - DebugAmbientOcclusion(); + DebugAmbientOcclusion() {} void configure(const Config& config); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); @@ -283,7 +287,7 @@ private: gpu::PipelinePointer _debugPipeline; - bool _showCursorPixel{ false }; + bool _showCursorPixel { false }; }; #endif // hifi_AmbientOcclusionEffect_h diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index fe9e5eb245..0ff1c435e1 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -672,14 +672,4 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) { _defaultTonemappingID = tonemappingStage->addTonemapping(_defaultTonemapping); } } - - if (!_defaultAmbientOcclusion) { - auto ambientOcclusionStage = renderContext->_scene->getStage(); - if (ambientOcclusionStage) { - auto ambientOcclusion = std::make_shared(); - - _defaultAmbientOcclusion = ambientOcclusion; - _defaultAmbientOcclusionID = ambientOcclusionStage->addAmbientOcclusion(_defaultAmbientOcclusion); - } - } } diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index d19c1de2e9..73c43c52a3 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -170,8 +170,6 @@ protected: HazeStage::Index _defaultHazeID { HazeStage::INVALID_INDEX }; graphics::TonemappingPointer _defaultTonemapping { nullptr }; TonemappingStage::Index _defaultTonemappingID { TonemappingStage::INVALID_INDEX }; - graphics::AmbientOcclusionPointer _defaultAmbientOcclusion { nullptr }; - AmbientOcclusionStage::Index _defaultAmbientOcclusionID { AmbientOcclusionStage::INVALID_INDEX }; graphics::SkyboxPointer _defaultSkybox { new ProceduralSkybox() }; NetworkTexturePointer _defaultSkyboxNetworkTexture; NetworkTexturePointer _defaultAmbientNetworkTexture; diff --git a/libraries/render-utils/src/LightingModel.h b/libraries/render-utils/src/LightingModel.h index a488abcb09..5298fb2ceb 100644 --- a/libraries/render-utils/src/LightingModel.h +++ b/libraries/render-utils/src/LightingModel.h @@ -4,6 +4,7 @@ // // Created by Sam Gateau 7/1/2016. // Copyright 2016 High Fidelity, Inc. +// Copyright 2024 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 @@ -17,14 +18,13 @@ #include #include -// LightingModel is a helper class gathering in one place the flags to enable the lighting contributions +// LightingModel is a helper class gathering in one place the flags to enable the lighting contributions class LightingModel { public: using UniformBufferView = gpu::BufferView; LightingModel(); - void setUnlit(bool enable); bool isUnlitEnabled() const; @@ -76,7 +76,6 @@ public: void setBlendshape(bool enable); bool isBlendshapeEnabled() const; - void setAmbientOcclusion(bool enable); bool isAmbientOcclusionEnabled() const; void setShadow(bool enable); @@ -86,55 +85,49 @@ public: gpu::TexturePointer getAmbientFresnelLUT() const { return _ambientFresnelLUT; } protected: - - // Class describing the uniform buffer with the transform info common to the AO shaders // It s changing every frame class Parameters { public: - float enableUnlit{ 1.0f }; - float enableEmissive{ 1.0f }; - float enableLightmap{ 1.0f }; - float enableBackground{ 1.0f }; + float enableUnlit { 1.0f }; + float enableEmissive { 1.0f }; + float enableLightmap { 1.0f }; + float enableBackground { 1.0f }; - float enableScattering{ 1.0f }; - float enableDiffuse{ 1.0f }; - float enableSpecular{ 1.0f }; - float enableAlbedo{ 1.0f }; + float enableScattering { 1.0f }; + float enableDiffuse { 1.0f }; + float enableSpecular { 1.0f }; + float enableAlbedo { 1.0f }; - float enableAmbientLight{ 1.0f }; - float enableDirectionalLight{ 1.0f }; - float enablePointLight{ 1.0f }; - float enableSpotLight{ 1.0f }; + float enableAmbientLight { 1.0f }; + float enableDirectionalLight { 1.0f }; + float enablePointLight { 1.0f }; + float enableSpotLight { 1.0f }; float showLightContour { 0.0f }; // false by default - float enableObscurance{ 1.0f }; + float enableObscurance { 1.0f }; float enableMaterialTexturing { 1.0f }; float enableWireframe { 0.0f }; // false by default - float enableHaze{ 1.0f }; - float enableBloom{ 1.0f }; - float enableSkinning{ 1.0f }; - float enableBlendshape{ 1.0f }; + float enableHaze { 1.0f }; + float enableBloom { 1.0f }; + float enableSkinning { 1.0f }; + float enableBlendshape { 1.0f }; - float enableAmbientOcclusion{ 0.0f }; // false by default - float enableShadow{ 1.0f }; - float spare1{ 1.0f }; - float spare2{ 1.0f }; + float enableAmbientOcclusion { 1.0f }; + float enableShadow { 1.0f }; + float spare1 { 1.0f }; + float spare2 { 1.0f }; Parameters() {} }; UniformBufferView _parametersBuffer; static gpu::TexturePointer _ambientFresnelLUT; }; - using LightingModelPointer = std::shared_ptr; - - - class MakeLightingModelConfig : public render::Job::Config { Q_OBJECT @@ -168,39 +161,37 @@ class MakeLightingModelConfig : public render::Job::Config { Q_PROPERTY(bool enableAmbientOcclusion READ isAmbientOcclusionEnabled WRITE setAmbientOcclusion NOTIFY dirty) Q_PROPERTY(bool enableShadow READ isShadowEnabled WRITE setShadow NOTIFY dirty) - public: MakeLightingModelConfig() : render::Job::Config() {} // Make Lighting Model is always on - bool enableUnlit{ true }; - bool enableEmissive{ true }; - bool enableLightmap{ true }; - bool enableBackground{ true }; - bool enableObscurance{ true }; + bool enableUnlit { true }; + bool enableEmissive { true }; + bool enableLightmap { true }; + bool enableBackground { true }; + bool enableObscurance { true }; - bool enableScattering{ true }; - bool enableDiffuse{ true }; - bool enableSpecular{ true }; + bool enableScattering { true }; + bool enableDiffuse { true }; + bool enableSpecular { true }; - bool enableAlbedo{ true }; + bool enableAlbedo { true }; bool enableMaterialTexturing { true }; - bool enableAmbientLight{ true }; - bool enableDirectionalLight{ true }; - bool enablePointLight{ true }; - bool enableSpotLight{ true }; + bool enableAmbientLight { true }; + bool enableDirectionalLight { true }; + bool enablePointLight { true }; + bool enableSpotLight { true }; bool showLightContour { false }; // false by default bool enableWireframe { false }; // false by default - bool enableHaze{ true }; - bool enableBloom{ true }; - bool enableSkinning{ true }; - bool enableBlendshape{ true }; - - bool enableAmbientOcclusion{ false }; // false by default - bool enableShadow{ true }; + bool enableHaze { true }; + bool enableBloom { true }; + bool enableSkinning { true }; + bool enableBlendshape { true }; + bool enableAmbientOcclusion { true }; + bool enableShadow { true }; void setAmbientOcclusion(bool enable) { enableAmbientOcclusion = enable; emit dirty();} bool isAmbientOcclusionEnabled() const { return enableAmbientOcclusion; } diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index 397d1bce52..866b3c407c 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -116,6 +116,7 @@ const gpu::PipelinePointer& DebugZoneLighting::getAmbientPipeline() { } return _ambientPipeline; } + const gpu::PipelinePointer& DebugZoneLighting::getBackgroundPipeline() { if (!_backgroundPipeline) { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::zone_drawSkybox); diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 5c20d64345..75b927a2c9 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -55,6 +55,7 @@ Rectangle { Column { Repeater { model: [ + "debugEnabled:debug", "horizonBased:horizonBased", "jitterEnabled:jitterEnabled", "ditheringEnabled:ditheringEnabled", @@ -72,7 +73,7 @@ Rectangle { Column { Repeater { model: [ - "debugEnabled:showCursorPixel" + "showCursorPixel:showCursorPixel" ] HifiControls.CheckBox { boxSize: 20 @@ -100,8 +101,6 @@ Rectangle { ] } - - TabView { anchors.left: parent.left anchors.right: parent.right diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index 32f4ddcbe6..58b0e36ffb 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -174,6 +174,39 @@ "tonemapping.exposure": { "tooltip": "The exposure used during tonemapping." }, + "ambientOcclusionMode": { + "tooltip": "Configures the ambient occlusion in this zone." + }, + "ambientOcclusion.technique": { + "tooltip": "The ambient occlusion technique used. Different techniques have different tradeoffs." + }, + "ambientOcclusion.jitter": { + "tooltip": "Whether or not the ambient occlusion sampling is jittered." + }, + "ambientOcclusion.resolutionLevel": { + "tooltip": "How high the resolution of the ambient occlusion buffer should be. Higher levels mean lower resolution buffers." + }, + "ambientOcclusion.edgeSharpness": { + "tooltip": "How much to sharpen the edges during the ambient occlusion blurring." + }, + "ambientOcclusion.blurRadius": { + "tooltip": "The radius used for blurring, in pixels." + }, + "ambientOcclusion.aoRadius": { + "tooltip": "The radius used for ambient occlusion." + }, + "ambientOcclusion.aoObscuranceLevel": { + "tooltip": "Intensify or dim ambient occlusion." + }, + "ambientOcclusion.aoFalloffAngle": { + "tooltip": "The falloff angle for the AO calculation." + }, + "ambientOcclusion.aoSamplingAmount": { + "tooltip": "The fraction of AO samples to use, out of the maximum for each technique." + }, + "ambientOcclusion.ssaoNumSpiralTurns": { + "tooltip": "The angle span used to distribute the AO samples ray directions. SSAO only." + }, "audio.reverbEnabled": { "tooltip": "If reverb should be enabled for listeners in this zone." }, diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 43258dd344..f281fc3839 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -658,11 +658,81 @@ const GROUPS = [ propertyID: "ambientOcclusionMode", }, { - label: "Ambient Occlusion", + label: "Technique", type: "dropdown", options: { ssao: "SSAO", hbao: "HBAO" }, propertyID: "ambientOcclusion.technique", - showPropertyRule: { "tonemappingMode": "enabled" }, + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Jitter", + type: "bool", + propertyID: "ambientOcclusion.jitter", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Resolution Level", + type: "number-draggable", + step: 1, + decimals: 0, + propertyID: "ambientOcclusion.resolutionLevel", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Edge Sharpness", + type: "number-draggable", + step: 0.01, + decimals: 2, + propertyID: "ambientOcclusion.edgeSharpness", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Blur Radius (pixels)", + type: "number-draggable", + step: 1, + decimals: 0, + propertyID: "ambientOcclusion.blurRadius", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "AO Radius", + type: "number-draggable", + step: 0.01, + decimals: 2, + propertyID: "ambientOcclusion.aoRadius", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Intensity", + type: "number-draggable", + step: 0.01, + decimals: 2, + propertyID: "ambientOcclusion.aoObscuranceLevel", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Falloff Angle", + type: "number-draggable", + step: 0.01, + decimals: 2, + propertyID: "ambientOcclusion.aoFalloffAngle", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Sampling Amount", + type: "number-draggable", + step: 0.01, + decimals: 2, + propertyID: "ambientOcclusion.aoSamplingAmount", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Num Spiral Turns", + type: "number-draggable", + step: 0.01, + decimals: 2, + propertyID: "ambientOcclusion.ssaoNumSpiralTurns", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, } ] }, diff --git a/scripts/system/create/entityProperties/html/tabs/zone_ambient_occlusion.png b/scripts/system/create/entityProperties/html/tabs/zone_ambient_occlusion.png new file mode 100644 index 0000000000000000000000000000000000000000..ace90dbcbc8d090d778c2a3b3ac4663cd42a6255 GIT binary patch literal 785 zcmWlUe@sjP9KgTt-MhQze!WLI#Z7OKnq|6k^P^Gkx~uLek|aduwAC+|{Nb3SWhx>Ph@1OQTlUY893 z5g~8^0C)F!}{$<0K{P-5O6p{1y-cV)BrrFKJ|BVJpfR9_1T#KCu0DZ3V`V# z0P_vNk^nGn1W3FFFz--ZL0=jGT4~T}a*ls6`GdirmI{ctWY#0KMY^Uosongg!e=X5 zU&N+->0td~Wm6K%@0~@je~v;ZqEKv4D1-@3VL69aj$LRm(=~Z~V*{P|P)^ z%gBYTP4HTmTop(Yw>1Yu&G~EU678!O(ZI(0h1}QKdnC91P>~{DL)Mcp!@}peDI;Rl zWK>8Sd8S;2ywv1lsmp0K$4mPYc*<2F;X%1SL*boNq*=bNfs%bvy0+u(UOssU4IOmR z?zvUOtCZdogrkSE*3$}%q%-ms=MUwV)?|pgfnsR+AehSHKND@mtJHn2ex5nsJN6y% z6*6FAZhu`*=DZ+L%Z_pg`S78%>BAo~%F>>6`KK-GZ%W&JLf-upQ?tahIp=vCD!9RFgd za?J90*_(`{(B{gWN#ILwdSPKFYJG`KxbtFal5FW6K6l?`ssU62i2XZz_tNVb002Yk KMqNva(fS{(|0FN~ literal 0 HcmV?d00001 diff --git a/scripts/system/create/entityProperties/html/tabs/zone_tonemapping.png b/scripts/system/create/entityProperties/html/tabs/zone_tonemapping.png new file mode 100644 index 0000000000000000000000000000000000000000..06f93f57e46af2679a891d7498f7d01adf89e641 GIT binary patch literal 673 zcmWm4ZAcSw901_o-RXr_fGCb)AE zS1q+UOGQXaK?p?6BFK{I3>OL&lwu*nCX@s*k#H0(_Tl;V+%}u^;uuv70EmqSodp0C zQ6vul0@ri*0YK1XxOfdfD2>8kXgE4Jm$b!Nfa&gzKc#&DfT|6aVu1Dp0EZJG9073r z0BAV{uu}<;JpdrT;0d_{!CG-%X&a`h(lM+t@Q~q1hbF9d`;#!pVw`pK7 zEI|%>Xx%X&>)~n)53I=liC${Ldex_C@NNa-+&Ut5)fH@4ygG2F>dn)|Dz6Z2awr;lZkStT^bRVJ4@ zS#}jTRI*#jxTJ=DaSEX@_7d0`)CweO@Ov0G$d;(N(ZbF;%2?VRukV(#nHbIrR5@pL zWAzphAG=`Q#7Sfh?!vT;ns?UzT6_O)L0!C9KaoRZNd2RJ8@{DCKm3xv8YD=>t9!Oh zP@NpRye_es*}Wgi9xKXF-tACZO+wbDU5djv3iw*Pk@dV$FarQ!EHvpR^DDdl1F1gY AP5=M^ literal 0 HcmV?d00001 From b00086ddb025388dd4038bcf01aedd645b91d8fb Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Wed, 26 Jun 2024 23:24:56 -0700 Subject: [PATCH 048/109] it's working! --- libraries/render-utils/src/AmbientOcclusionEffect.cpp | 11 ++++++----- libraries/render-utils/src/AmbientOcclusionStage.cpp | 2 +- libraries/render-utils/src/BackgroundStage.cpp | 2 +- libraries/render-utils/src/BloomStage.cpp | 2 +- libraries/render-utils/src/HazeStage.cpp | 2 +- libraries/render-utils/src/LightStage.cpp | 2 +- libraries/render-utils/src/TonemappingStage.cpp | 2 +- libraries/render-utils/src/ZoneRenderer.cpp | 2 +- 8 files changed, 13 insertions(+), 12 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 8c14b0b0ef..5e7c584e33 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -380,16 +380,17 @@ void AmbientOcclusionEffect::updateParameters(const graphics::AmbientOcclusionPo current.w = ambientOcclusion->getAOObscuranceLevel(); } - if (shouldUpdateTechnique || ambientOcclusion->getAOFalloffAngle() != _aoParametersBuffer->getFalloffAngle()) { + const float falloffAngle = std::min(1.0f - EPSILON, ambientOcclusion->getAOFalloffAngle()); + if (shouldUpdateTechnique || falloffAngle != _aoParametersBuffer->getFalloffAngle()) { auto& current = _aoParametersBuffer.edit()._falloffInfo; - current.x = ambientOcclusion->getAOFalloffAngle(); + current.x = falloffAngle; current.y = 1.0f / (1.0f - current.x); // Compute sin from cos - current.z = sqrtf(1.0f - ambientOcclusion->getAOFalloffAngle() * ambientOcclusion->getAOFalloffAngle()); + current.z = sqrtf(1.0f - current.x * current.x); current.w = 1.0f / current.z; } - const int numSamples = ambientOcclusion->getAOSamplingAmount() * MAX_SSAO_SAMPLES; + const int numSamples = std::max(1, (int)(ambientOcclusion->getAOSamplingAmount() * MAX_HBAO_SAMPLES)); if (shouldUpdateTechnique || numSamples != _aoParametersBuffer->getNumSamples()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; current.x = numSamples; @@ -423,7 +424,7 @@ void AmbientOcclusionEffect::updateParameters(const graphics::AmbientOcclusionPo current.z = ambientOcclusion->getSSAONumSpiralTurns(); } - const int numSamples = ambientOcclusion->getAOSamplingAmount() * MAX_HBAO_SAMPLES; + const int numSamples = std::max(1, (int)(ambientOcclusion->getAOSamplingAmount() * MAX_SSAO_SAMPLES)); if (shouldUpdateTechnique || numSamples != _aoParametersBuffer->getNumSamples()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; current.x = numSamples; diff --git a/libraries/render-utils/src/AmbientOcclusionStage.cpp b/libraries/render-utils/src/AmbientOcclusionStage.cpp index 4adb06bbee..6b3763a39c 100644 --- a/libraries/render-utils/src/AmbientOcclusionStage.cpp +++ b/libraries/render-utils/src/AmbientOcclusionStage.cpp @@ -11,7 +11,7 @@ #include -std::string AmbientOcclusionStage::_stageName { "BLOOM_STAGE"}; +std::string AmbientOcclusionStage::_stageName { "AMBIENT_OCCLUSION_STAGE" }; const AmbientOcclusionStage::Index AmbientOcclusionStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; AmbientOcclusionStage::Index AmbientOcclusionStage::findAmbientOcclusion(const AmbientOcclusionPointer& ambientOcclusion) const { diff --git a/libraries/render-utils/src/BackgroundStage.cpp b/libraries/render-utils/src/BackgroundStage.cpp index 91b766d90b..455f356a45 100644 --- a/libraries/render-utils/src/BackgroundStage.cpp +++ b/libraries/render-utils/src/BackgroundStage.cpp @@ -15,7 +15,7 @@ #include -std::string BackgroundStage::_stageName { "BACKGROUND_STAGE"}; +std::string BackgroundStage::_stageName { "BACKGROUND_STAGE" }; const BackgroundStage::Index BackgroundStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; BackgroundStage::Index BackgroundStage::findBackground(const BackgroundPointer& background) const { diff --git a/libraries/render-utils/src/BloomStage.cpp b/libraries/render-utils/src/BloomStage.cpp index b3ba5f9565..2bedfeea96 100644 --- a/libraries/render-utils/src/BloomStage.cpp +++ b/libraries/render-utils/src/BloomStage.cpp @@ -13,7 +13,7 @@ #include -std::string BloomStage::_stageName { "BLOOM_STAGE"}; +std::string BloomStage::_stageName { "BLOOM_STAGE" }; const BloomStage::Index BloomStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; BloomStage::Index BloomStage::findBloom(const BloomPointer& bloom) const { diff --git a/libraries/render-utils/src/HazeStage.cpp b/libraries/render-utils/src/HazeStage.cpp index c850828be5..9251e1e2f9 100644 --- a/libraries/render-utils/src/HazeStage.cpp +++ b/libraries/render-utils/src/HazeStage.cpp @@ -13,7 +13,7 @@ #include -std::string HazeStage::_stageName { "HAZE_STAGE"}; +std::string HazeStage::_stageName { "HAZE_STAGE" }; const HazeStage::Index HazeStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; HazeStage::Index HazeStage::findHaze(const HazePointer& haze) const { diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index ccdf45cedc..58f32cdc4e 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -15,7 +15,7 @@ #include "ViewFrustum.h" -std::string LightStage::_stageName { "LIGHT_STAGE"}; +std::string LightStage::_stageName { "LIGHT_STAGE" }; // The bias matrix goes from homogeneous coordinates to UV coords (see http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/#basic-shader) const glm::mat4 LightStage::Shadow::_biasMatrix { 0.5, 0.0, 0.0, 0.0, diff --git a/libraries/render-utils/src/TonemappingStage.cpp b/libraries/render-utils/src/TonemappingStage.cpp index dcfe3c28c4..9b6029ca1b 100644 --- a/libraries/render-utils/src/TonemappingStage.cpp +++ b/libraries/render-utils/src/TonemappingStage.cpp @@ -11,7 +11,7 @@ #include -std::string TonemappingStage::_stageName { "TONEMAPPING_STAGE"}; +std::string TonemappingStage::_stageName { "TONEMAPPING_STAGE" }; const TonemappingStage::Index TonemappingStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; TonemappingStage::Index TonemappingStage::findTonemapping(const TonemappingPointer& tonemapping) const { diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index 866b3c407c..d9c0de9934 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -90,7 +90,7 @@ void SetupZones::run(const RenderContextPointer& context, const Input& input) { hazeStage->_currentFrame.pushHaze(0); bloomStage->_currentFrame.pushBloom(INVALID_INDEX); tonemappingStage->_currentFrame.pushTonemapping(0); - ambientOcclusionStage->_currentFrame.pushAmbientOcclusion(0); + ambientOcclusionStage->_currentFrame.pushAmbientOcclusion(INVALID_INDEX); } const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() { From 55907acd704bafcc6e9dbf6ee5fab74c066c6f1c Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 6 Jul 2024 16:39:46 -0700 Subject: [PATCH 049/109] remove attachments --- .../src/avatars/ScriptableAvatar.h | 2 - interface/src/Application.cpp | 75 ---- interface/src/Application.h | 5 - interface/src/AvatarBookmarks.cpp | 5 - interface/src/AvatarBookmarks.h | 7 +- interface/src/avatar/MyAvatar.cpp | 286 +-------------- interface/src/avatar/MyAvatar.h | 43 +-- interface/src/avatar/OtherAvatar.cpp | 3 +- interface/src/ui/DialogsManager.h | 8 - interface/src/ui/ModelsBrowser.cpp | 2 +- .../src/avatars-renderer/Avatar.cpp | 165 --------- .../src/avatars-renderer/Avatar.h | 10 - .../src/avatars-renderer/ScriptAvatar.h | 2 - libraries/avatars/src/AvatarData.cpp | 329 +----------------- libraries/avatars/src/AvatarData.h | 191 ---------- libraries/avatars/src/ScriptAvatarData.cpp | 11 +- libraries/avatars/src/ScriptAvatarData.h | 14 +- .../src/RenderableEntityItem.cpp | 2 - .../src/RenderableModelEntityItem.cpp | 2 - libraries/entities/src/EntityItem.cpp | 4 - libraries/model-serializers/src/FSTReader.cpp | 4 - libraries/model-serializers/src/FSTReader.h | 3 +- .../networking/src/udt/PacketHeaders.cpp | 4 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- .../recording/RecordingScriptingInterface.cpp | 4 - .../recording/RecordingScriptingInterface.h | 17 - .../render-utils/src/SoftAttachmentModel.cpp | 73 ---- .../render-utils/src/SoftAttachmentModel.h | 38 -- libraries/shared/src/SpatiallyNestable.cpp | 16 - libraries/shared/src/SpatiallyNestable.h | 4 - 30 files changed, 19 insertions(+), 1313 deletions(-) delete mode 100644 libraries/render-utils/src/SoftAttachmentModel.cpp delete mode 100644 libraries/render-utils/src/SoftAttachmentModel.h diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 7e79d25cd0..97a5f44adc 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -67,8 +67,6 @@ * @property {boolean} lookAtSnappingEnabled=true - true if the avatar's eyes snap to look at another avatar's * eyes when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. * @property {string} skeletonModelURL - The avatar's FST file. - * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments. - *

    Deprecated: This property is deprecated and will be removed. Use avatar entities instead.

    * @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. * @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. Read-only. * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e756ca276a..67450f3dd7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -316,7 +316,6 @@ static const QString JS_EXTENSION = ".js"; static const QString FST_EXTENSION = ".fst"; static const QString FBX_EXTENSION = ".fbx"; static const QString OBJ_EXTENSION = ".obj"; -static const QString AVA_JSON_EXTENSION = ".ava.json"; static const QString WEB_VIEW_TAG = "noDownload=true"; static const QString ZIP_EXTENSION = ".zip"; static const QString CONTENT_ZIP_EXTENSION = ".content.zip"; @@ -365,7 +364,6 @@ static const QString TESTER_FILE = "/sdcard/_hifi_test_device.txt"; const std::vector> Application::_acceptedExtensions { { SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL }, - { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl }, { JSON_EXTENSION, &Application::importJSONFromURL }, { JS_EXTENSION, &Application::askToLoadScript }, { FST_EXTENSION, &Application::askToSetAvatarUrl }, @@ -7838,74 +7836,6 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { return true; } -bool Application::askToWearAvatarAttachmentUrl(const QString& url) { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest = QNetworkRequest(url); - networkRequest.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::OVERTE_USER_AGENT); - QNetworkReply* reply = networkAccessManager.get(networkRequest); - int requestNumber = ++_avatarAttachmentRequest; - connect(reply, &QNetworkReply::finished, [this, reply, url, requestNumber]() { - - if (requestNumber != _avatarAttachmentRequest) { - // this request has been superseded by another more recent request - reply->deleteLater(); - return; - } - - QNetworkReply::NetworkError networkError = reply->error(); - if (networkError == QNetworkReply::NoError) { - // download success - QByteArray contents = reply->readAll(); - - QJsonParseError jsonError; - auto doc = QJsonDocument::fromJson(contents, &jsonError); - if (jsonError.error == QJsonParseError::NoError) { - - auto jsonObject = doc.object(); - - // retrieve optional name field from JSON - QString name = tr("Unnamed Attachment"); - auto nameValue = jsonObject.value("name"); - if (nameValue.isString()) { - name = nameValue.toString(); - } - - auto avatarAttachmentConfirmationTitle = tr("Avatar Attachment Confirmation"); - auto avatarAttachmentConfirmationMessage = tr("Would you like to wear '%1' on your avatar?").arg(name); - ModalDialogListener* dlg = OffscreenUi::asyncQuestion(avatarAttachmentConfirmationTitle, - avatarAttachmentConfirmationMessage, - QMessageBox::Ok | QMessageBox::Cancel); - QObject::connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) { - QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr); - if (static_cast(answer.toInt()) == QMessageBox::Yes) { - // add attachment to avatar - auto myAvatar = getMyAvatar(); - assert(myAvatar); - auto attachmentDataVec = myAvatar->getAttachmentData(); - AttachmentData attachmentData; - attachmentData.fromJson(jsonObject); - attachmentDataVec.push_back(attachmentData); - myAvatar->setAttachmentData(attachmentDataVec); - } else { - qCDebug(interfaceapp) << "User declined to wear the avatar attachment"; - } - }); - } else { - // json parse error - auto avatarAttachmentParseErrorString = tr("Error parsing attachment JSON from url: \"%1\""); - displayAvatarAttachmentWarning(avatarAttachmentParseErrorString.arg(url)); - } - } else { - // download failure - auto avatarAttachmentDownloadErrorString = tr("Error downloading attachment JSON from url: \"%1\""); - displayAvatarAttachmentWarning(avatarAttachmentDownloadErrorString.arg(url)); - } - reply->deleteLater(); - }); - return true; -} - static const QString CONTENT_SET_NAME_QUERY_PARAM = "name"; void Application::replaceDomainContent(const QString& url, const QString& itemName) { @@ -7984,11 +7914,6 @@ bool Application::askToReplaceDomainContent(const QString& url) { return true; } -void Application::displayAvatarAttachmentWarning(const QString& message) const { - auto avatarAttachmentWarningTitle = tr("Avatar Attachment Failure"); - OffscreenUi::asyncWarning(avatarAttachmentWarningTitle, message); -} - void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const { auto tablet = DependencyManager::get()->getTablet(SYSTEM_TABLET); auto hmd = DependencyManager::get(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 63a035dd45..e93a8338de 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -546,9 +546,6 @@ private slots: bool askToSetAvatarUrl(const QString& url); bool askToLoadScript(const QString& scriptFilenameOrURL); - bool askToWearAvatarAttachmentUrl(const QString& url); - void displayAvatarAttachmentWarning(const QString& message) const; - bool askToReplaceDomainContent(const QString& url); void setSessionUUID(const QUuid& sessionUUID) const; @@ -800,8 +797,6 @@ private: bool _reticleClickPressed { false }; bool _keyboardFocusWaitingOnRenderable { false }; - int _avatarAttachmentRequest = 0; - bool _settingsLoaded { false }; bool _captureMouse { false }; diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 461c55e64e..6485840c80 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -222,8 +222,6 @@ void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { * @property {number} avatarScale - The target scale of the avatar. * @property {Array>} [avatarEntites] - The avatar entities included with the * bookmark. - * @property {AttachmentData[]} [attachments] - The attachments included with the bookmark. - *

    Deprecated: Use avatar entities instead. */ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { @@ -266,8 +264,6 @@ void AvatarBookmarks::loadBookmarkInternal(const QString& bookmarkName) { myAvatar->clearWornAvatarEntities(); const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat(); myAvatar->setAvatarScale(qScale); - QList attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList()).toList(); - myAvatar->setAttachmentsVariant(attachments); QVariantList avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList(); addAvatarEntities(avatarEntities); emit bookmarkLoaded(bookmarkName); @@ -335,7 +331,6 @@ QVariantMap AvatarBookmarks::getAvatarDataToBookmark() { const QString& avatarIcon = QString(""); const QVariant& avatarScale = myAvatar->getAvatarScale(); - // If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION QVariantMap bookmark; bookmark.insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION); bookmark.insert(ENTRY_AVATAR_URL, avatarUrl); diff --git a/interface/src/AvatarBookmarks.h b/interface/src/AvatarBookmarks.h index bf06743b3f..99421bb78b 100644 --- a/interface/src/AvatarBookmarks.h +++ b/interface/src/AvatarBookmarks.h @@ -66,8 +66,7 @@ public slots: void saveBookmark(const QString& bookmarkName); /*@jsdoc - * Loads an avatar bookmark, setting your avatar model, scale, and avatar entities (or attachments if an old bookmark) to - * those in the bookmark. + * Loads an avatar bookmark, setting your avatar model, scale, and avatar entities to those in the bookmark. * @function AvatarBookmarks.loadBookmark * @param {string} bookmarkName - The name of the avatar bookmark to load (case sensitive). */ @@ -104,8 +103,7 @@ public slots: signals: /*@jsdoc - * Triggered when an avatar bookmark is loaded, setting your avatar model, scale, and avatar entities (or attachments if an - * old bookmark) to those in the bookmark. + * Triggered when an avatar bookmark is loaded, setting your avatar model, scale, and avatar entities to those in the bookmark. * @function AvatarBookmarks.bookmarkLoaded * @param {string} bookmarkName - The name of the avatar bookmark loaded. * @returns {Signal} @@ -155,7 +153,6 @@ private: const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json"; const QString ENTRY_AVATAR_URL = "avatarUrl"; const QString ENTRY_AVATAR_ICON = "avatarIcon"; - const QString ENTRY_AVATAR_ATTACHMENTS = "attachments"; const QString ENTRY_AVATAR_ENTITIES = "avatarEntites"; const QString ENTRY_AVATAR_SCALE = "avatarScale"; const QString ENTRY_VERSION = "version"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0c61b0d01a..fc6fae5456 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -272,12 +272,6 @@ MyAvatar::MyAvatar(QThread* thread) : auto hfmModel = getSkeletonModel()->getHFMModel(); qApp->loadAvatarScripts(hfmModel.scripts); _shouldLoadScripts = false; - } - // Load and convert old attachments to avatar entities - if (_oldAttachmentData.size() > 0) { - setAttachmentData(_oldAttachmentData); - _oldAttachmentData.clear(); - _attachmentData.clear(); } }); connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady); @@ -371,10 +365,6 @@ MyAvatar::MyAvatar(QThread* thread) : setWorldPosition(dummyAvatar.getWorldPosition()); setWorldOrientation(dummyAvatar.getWorldOrientation()); - if (!dummyAvatar.getAttachmentData().isEmpty()) { - setAttachmentData(dummyAvatar.getAttachmentData()); - } - auto headData = dummyAvatar.getHeadData(); if (headData && _headData) { // blendshapes @@ -501,11 +491,6 @@ glm::quat MyAvatar::getOrientationOutbound() const { return (slerp(_smoothOrientationInitial, _smoothOrientationTarget, interp)); } -// virtual -void MyAvatar::simulateAttachments(float deltaTime) { - // don't update attachments here, do it in harvestResultsFromPhysicsSimulation() -} - QByteArray MyAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) { CameraMode mode = qApp->getCamera().getMode(); _globalPosition = getWorldPosition(); @@ -982,8 +967,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) { } // we've achived our final adjusted position and rotation for the avatar - // and all of its joints, now update our attachements. - Avatar::simulateAttachments(deltaTime); + // and all of its joints, now update our children. relayJointDataToChildren(); if (applyGrabChanges()) { _cauterizationNeedsUpdate = true; @@ -2175,65 +2159,6 @@ void MyAvatar::loadAvatarEntityDataFromSettings() { }); } -void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const { - Settings settings; - settings.beginGroup("savedAttachmentData"); - settings.beginGroup(_skeletonModel->getURL().toString()); - settings.beginGroup(attachment.modelURL.toString()); - settings.setValue("jointName", attachment.jointName); - - settings.beginGroup(attachment.jointName); - settings.setValue("translation_x", attachment.translation.x); - settings.setValue("translation_y", attachment.translation.y); - settings.setValue("translation_z", attachment.translation.z); - glm::vec3 eulers = safeEulerAngles(attachment.rotation); - settings.setValue("rotation_x", eulers.x); - settings.setValue("rotation_y", eulers.y); - settings.setValue("rotation_z", eulers.z); - settings.setValue("scale", attachment.scale); - - settings.endGroup(); - settings.endGroup(); - settings.endGroup(); - settings.endGroup(); -} - -AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL, const QString& jointName) const { - Settings settings; - settings.beginGroup("savedAttachmentData"); - settings.beginGroup(_skeletonModel->getURL().toString()); - settings.beginGroup(modelURL.toString()); - - AttachmentData attachment; - attachment.modelURL = modelURL; - if (jointName.isEmpty()) { - attachment.jointName = settings.value("jointName").toString(); - } else { - attachment.jointName = jointName; - } - settings.beginGroup(attachment.jointName); - if (settings.contains("translation_x")) { - attachment.translation.x = loadSetting(settings, "translation_x", 0.0f); - attachment.translation.y = loadSetting(settings, "translation_y", 0.0f); - attachment.translation.z = loadSetting(settings, "translation_z", 0.0f); - glm::vec3 eulers; - eulers.x = loadSetting(settings, "rotation_x", 0.0f); - eulers.y = loadSetting(settings, "rotation_y", 0.0f); - eulers.z = loadSetting(settings, "rotation_z", 0.0f); - attachment.rotation = glm::quat(eulers); - attachment.scale = loadSetting(settings, "scale", 1.0f); - } else { - attachment = AttachmentData(); - } - - settings.endGroup(); - settings.endGroup(); - settings.endGroup(); - settings.endGroup(); - - return attachment; -} - bool MyAvatar::isMyAvatarURLProtected() const { return !ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL); } @@ -2994,171 +2919,6 @@ SharedSoundPointer MyAvatar::getCollisionSound() { return _collisionSound; } -void MyAvatar::attach(const QString& modelURL, const QString& jointName, - const glm::vec3& translation, const glm::quat& rotation, - float scale, bool isSoft, - bool allowDuplicates, bool useSaved) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "attach", - Q_ARG(const QString&, modelURL), - Q_ARG(const QString&, jointName), - Q_ARG(const glm::vec3&, translation), - Q_ARG(const glm::quat&, rotation), - Q_ARG(float, scale), - Q_ARG(bool, isSoft), - Q_ARG(bool, allowDuplicates), - Q_ARG(bool, useSaved) - ); - return; - } - if (!DependencyManager::get()->getThisNodeCanRezAvatarEntities()) { - qCDebug(interfaceapp) << "Ignoring attach() because don't have canRezAvatarEntities permission on domain"; - return; - } - - AttachmentData data; - data.modelURL = modelURL; - data.jointName = jointName; - data.translation = translation; - data.rotation = rotation; - data.scale = scale; - data.isSoft = isSoft; - EntityItemProperties properties; - attachmentDataToEntityProperties(data, properties); - DependencyManager::get()->addEntity(properties, true); - emit attachmentsChanged(); -} - -void MyAvatar::detachOne(const QString& modelURL, const QString& jointName) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "detachOne", - Q_ARG(const QString&, modelURL), - Q_ARG(const QString&, jointName) - ); - return; - } - if (!DependencyManager::get()->getThisNodeCanRezAvatarEntities()) { - qCDebug(interfaceapp) << "Ignoring detachOne() because don't have canRezAvatarEntities permission on domain"; - return; - } - - QUuid entityID; - if (findAvatarEntity(modelURL, jointName, entityID)) { - DependencyManager::get()->deleteEntity(entityID); - } - emit attachmentsChanged(); -} - -void MyAvatar::detachAll(const QString& modelURL, const QString& jointName) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "detachAll", - Q_ARG(const QString&, modelURL), - Q_ARG(const QString&, jointName) - ); - return; - } - if (!DependencyManager::get()->getThisNodeCanRezAvatarEntities()) { - qCDebug(interfaceapp) << "Ignoring detachAll() because don't have canRezAvatarEntities permission on domain"; - return; - } - - QUuid entityID; - while (findAvatarEntity(modelURL, jointName, entityID)) { - DependencyManager::get()->deleteEntity(entityID); - } - emit attachmentsChanged(); -} - -void MyAvatar::setAttachmentData(const QVector& attachmentData) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "setAttachmentData", - Q_ARG(const QVector&, attachmentData)); - return; - } - if (!DependencyManager::get()->getThisNodeCanRezAvatarEntities()) { - qCDebug(interfaceapp) << "Ignoring setAttachmentData() because don't have canRezAvatarEntities permission on domain"; - return; - } - - std::vector newEntitiesProperties; - for (auto& data : attachmentData) { - QUuid entityID; - EntityItemProperties properties; - if (findAvatarEntity(data.modelURL.toString(), data.jointName, entityID)) { - properties = DependencyManager::get()->getEntityProperties(entityID); - } - attachmentDataToEntityProperties(data, properties); - newEntitiesProperties.push_back(properties); - } - - // clear any existing wearables - clearWornAvatarEntities(); - - for (auto& properties : newEntitiesProperties) { - DependencyManager::get()->addEntity(properties, true); - } - emit attachmentsChanged(); -} - -QVector MyAvatar::getAttachmentData() const { - QVector attachmentData; - - if (!DependencyManager::get()->getThisNodeCanRezAvatarEntities()) { - qCDebug(interfaceapp) << "Ignoring getAttachmentData() because don't have canRezAvatarEntities permission on domain"; - return attachmentData; - } - - QList avatarEntityIDs; - _avatarEntitiesLock.withReadLock([&] { - avatarEntityIDs = _packedAvatarEntityData.keys(); - }); - for (const auto& entityID : avatarEntityIDs) { - auto properties = DependencyManager::get()->getEntityProperties(entityID); - AttachmentData data = entityPropertiesToAttachmentData(properties); - attachmentData.append(data); - } - return attachmentData; -} - -QVariantList MyAvatar::getAttachmentsVariant() const { - QVariantList result; - - if (!DependencyManager::get()->getThisNodeCanRezAvatarEntities()) { - qCDebug(interfaceapp) - << "Ignoring getAttachmentsVariant() because don't have canRezAvatarEntities permission on domain"; - return result; - } - - for (const auto& attachment : getAttachmentData()) { - result.append(attachment.toVariant()); - } - return result; -} - -void MyAvatar::setAttachmentsVariant(const QVariantList& variant) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "setAttachmentsVariant", - Q_ARG(const QVariantList&, variant)); - return; - } - - if (!DependencyManager::get()->getThisNodeCanRezAvatarEntities()) { - qCDebug(interfaceapp) - << "Ignoring setAttachmentsVariant() because don't have canRezAvatarEntities permission on domain"; - return; - } - - QVector newAttachments; - newAttachments.reserve(variant.size()); - for (const auto& attachmentVar : variant) { - AttachmentData attachment; - if (attachment.fromVariant(attachmentVar)) { - newAttachments.append(attachment); - } - } - setAttachmentData(newAttachments); -} - bool MyAvatar::findAvatarEntity(const QString& modelURL, const QString& jointName, QUuid& entityID) { QList avatarEntityIDs; _avatarEntitiesLock.withReadLock([&] { @@ -3174,34 +2934,6 @@ bool MyAvatar::findAvatarEntity(const QString& modelURL, const QString& jointNam return false; } -AttachmentData MyAvatar::entityPropertiesToAttachmentData(const EntityItemProperties& properties) const { - AttachmentData data; - data.modelURL = properties.getModelURL(); - data.translation = properties.getLocalPosition(); - data.rotation = properties.getLocalRotation(); - data.isSoft = properties.getRelayParentJoints(); - int jointIndex = (int)properties.getParentJointIndex(); - if (jointIndex > -1 && jointIndex < getJointNames().size()) { - data.jointName = getJointNames()[jointIndex]; - } - return data; -} - -void MyAvatar::attachmentDataToEntityProperties(const AttachmentData& data, EntityItemProperties& properties) { - QString url = data.modelURL.toString(); - properties.setName(QFileInfo(url).baseName()); - properties.setType(EntityTypes::Model); - properties.setParentID(AVATAR_SELF_ID); - properties.setLocalPosition(data.translation); - properties.setLocalRotation(data.rotation); - if (!data.isSoft) { - properties.setParentJointIndex(getJointIndex(data.jointName)); - } else { - properties.setRelayParentJoints(true); - } - properties.setModelURL(url); -} - void MyAvatar::initHeadBones() { int neckJointIndex = -1; if (_skeletonModel->isLoaded()) { @@ -3444,22 +3176,6 @@ void MyAvatar::preDisplaySide(const RenderArgs* renderArgs) { if (shouldDrawHead != _prevShouldDrawHead) { _cauterizationNeedsUpdate = true; _skeletonModel->setEnableCauterization(!shouldDrawHead); - - for (int i = 0; i < _attachmentData.size(); i++) { - if (_attachmentData[i].jointName.compare("Head", Qt::CaseInsensitive) == 0 || - _attachmentData[i].jointName.compare("Neck", Qt::CaseInsensitive) == 0 || - _attachmentData[i].jointName.compare("LeftEye", Qt::CaseInsensitive) == 0 || - _attachmentData[i].jointName.compare("RightEye", Qt::CaseInsensitive) == 0 || - _attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 || - _attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) { - uint8_t modelRenderTagBits = shouldDrawHead ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_SECONDARY_VIEW; - - _attachmentModels[i]->setTagMask(modelRenderTagBits); - _attachmentModels[i]->setGroupCulled(false); - _attachmentModels[i]->setCanCastShadow(true); - _attachmentModels[i]->setVisibleInScene(true, qApp->getMain3DScene()); - } - } } _prevShouldDrawHead = shouldDrawHead; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 60c07ad42c..2777d2c82f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -141,8 +141,6 @@ class MyAvatar : public Avatar { * @property {boolean} lookAtSnappingEnabled=true - true if the avatar's eyes snap to look at another avatar's * eyes when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. * @property {string} skeletonModelURL - The avatar's FST file. - * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments. - *

    Deprecated: This property is deprecated and will be removed. Use avatar entities instead.

    * @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. * @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. Read-only. * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the @@ -326,17 +324,10 @@ class MyAvatar : public Avatar { * @borrows Avatar.getJointIndex as getJointIndex * @borrows Avatar.getJointNames as getJointNames * @borrows Avatar.setBlendshape as setBlendshape - * @borrows Avatar.getAttachmentsVariant as getAttachmentsVariant - * @borrows Avatar.setAttachmentsVariant as setAttachmentsVariant * @borrows Avatar.updateAvatarEntity as updateAvatarEntity * @borrows Avatar.clearAvatarEntity as clearAvatarEntity * @borrows Avatar.setForceFaceTrackerConnected as setForceFaceTrackerConnected * @borrows Avatar.setSkeletonModelURL as setSkeletonModelURL - * @borrows Avatar.getAttachmentData as getAttachmentData - * @borrows Avatar.setAttachmentData as setAttachmentData - * @borrows Avatar.attach as attach - * @borrows Avatar.detachOne as detachOne - * @borrows Avatar.detachAll as detachAll * @comment Avatar.getAvatarEntityData as getAvatarEntityData - Don't borrow because implementation is different. * @comment Avatar.setAvatarEntityData as setAvatarEntityData - Don't borrow because implementation is different. * @borrows Avatar.getSensorToWorldMatrix as getSensorToWorldMatrix @@ -590,8 +581,6 @@ public: static void registerMetaTypes(ScriptEnginePointer engine); void registerProperties(ScriptEnginePointer engine); - virtual void simulateAttachments(float deltaTime) override; - AudioListenerMode getAudioListenerModeHead() const { return FROM_HEAD; } AudioListenerMode getAudioListenerModeCamera() const { return FROM_CAMERA; } AudioListenerMode getAudioListenerModeCustom() const { return CUSTOM; } @@ -1073,9 +1062,6 @@ public: void loadData(); void loadAvatarEntityDataFromSettings(); - void saveAttachmentData(const AttachmentData& attachment) const; - AttachmentData loadAttachmentData(const QUrl& modelURL, const QString& jointName = QString()) const; - // Set what driving keys are being pressed to control thrust levels void clearDriveKeys(); void setDriveKey(DriveKeys key, float val); @@ -1822,12 +1808,6 @@ public: float computeStandingHeightMode(const controller::Pose& head); glm::quat computeAverageHeadRotation(const controller::Pose& head); - virtual void setAttachmentData(const QVector& attachmentData) override; - virtual QVector getAttachmentData() const override; - - virtual QVariantList getAttachmentsVariant() const override; - virtual void setAttachmentsVariant(const QVariantList& variant) override; - glm::vec3 getNextPosition() { return _goToPending ? _goToPosition : getWorldPosition(); } void prepareAvatarEntityDataForReload(); @@ -2610,16 +2590,6 @@ signals: */ void sensorToWorldScaleChanged(float sensorToWorldScale); - /*@jsdoc - * Triggered when the a model is attached to or detached from one of the avatar's joints using one of - * {@link MyAvatar.attach|attach}, {@link MyAvatar.detachOne|detachOne}, {@link MyAvatar.detachAll|detachAll}, or - * {@link MyAvatar.setAttachmentData|setAttachmentData}. - * @function MyAvatar.attachmentsChanged - * @returns {Signal} - * @deprecated This signal is deprecated and will be removed. Use avatar entities instead. - */ - void attachmentsChanged(); - /*@jsdoc * Triggered when the avatar's size changes. This can be due to the user changing the size of their avatar or the domain * limiting the size of their avatar. @@ -2701,18 +2671,7 @@ private: void setScriptedMotorFrame(QString frame); void setScriptedMotorMode(QString mode); - // Attachments - virtual void attach(const QString& modelURL, const QString& jointName = QString(), - const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), - float scale = 1.0f, bool isSoft = false, - bool allowDuplicates = false, bool useSaved = true) override; - - virtual void detachOne(const QString& modelURL, const QString& jointName = QString()) override; - virtual void detachAll(const QString& modelURL, const QString& jointName = QString()) override; - - // Attachments/Avatar Entity - void attachmentDataToEntityProperties(const AttachmentData& data, EntityItemProperties& properties); - AttachmentData entityPropertiesToAttachmentData(const EntityItemProperties& properties) const; + // Avatar Entities bool findAvatarEntity(const QString& modelURL, const QString& jointName, QUuid& entityID); void addAvatarEntitiesToTree(); diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index fe0e83dfa0..aacb6f56a9 100644 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -316,7 +316,6 @@ void OtherAvatar::simulate(float deltaTime, bool inView) { { PROFILE_RANGE(simulation, "misc"); measureMotionDerivatives(deltaTime); - simulateAttachments(deltaTime); updatePalms(); } { @@ -384,7 +383,7 @@ void OtherAvatar::debugJointData() const { } void OtherAvatar::handleChangedAvatarEntityData() { - PerformanceTimer perfTimer("attachments"); + PerformanceTimer perfTimer("avatarEntities"); // AVATAR ENTITY UPDATE FLOW // - if queueEditEntityMessage() sees "AvatarEntity" HostType it calls _myAvatar->storeAvatarEntityDataPayload() diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 864174296e..6636c725c8 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -20,14 +20,10 @@ #include "HMDToolsDialog.h" #include "TestingDialog.h" -class AnimationsDialog; -class AttachmentsDialog; -class CachesSizeDialog; class LodToolsDialog; class OctreeStatsDialog; class ScriptEditorWindow; class TestingDialog; -class QMessageBox; class DomainConnectionDialog; class DialogsManager : public QObject, public Dependency { @@ -77,10 +73,6 @@ private: template void maybeCreateDialog(QPointer& member); - QPointer _animationsDialog; - QPointer _attachmentsDialog; - QPointer _cachesSizeDialog; - QPointer _ircInfoBox; QPointer _hmdToolsDialog; QPointer _lodToolsDialog; QPointer _octreeStatsDialog; diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp index 2f422129e3..cd706d0bcb 100644 --- a/interface/src/ui/ModelsBrowser.cpp +++ b/interface/src/ui/ModelsBrowser.cpp @@ -32,7 +32,7 @@ #include #include -const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "skeletons", "attachments" }; +const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "skeletons" }; static const QString S3_URL = NetworkingConstants::HF_PUBLIC_CDN_URL; static const QString PUBLIC_URL = "http://public.overte.org"; // Changed to Overte but not entirely sure what to do with this yet. diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 790b45843c..25473bda90 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include "ModelEntityItem.h" @@ -52,7 +51,6 @@ const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); const float Avatar::MYAVATAR_LOADING_PRIORITY = (float)M_PI; // Entity priority is computed as atan2(maxDim, distance) which is <= PI / 2 const float Avatar::OTHERAVATAR_LOADING_PRIORITY = MYAVATAR_LOADING_PRIORITY - EPSILON; -const float Avatar::ATTACHMENT_LOADING_PRIORITY = OTHERAVATAR_LOADING_PRIORITY - EPSILON; namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { @@ -651,19 +649,6 @@ void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& sc _skeletonModel->setVisibleInScene(_isMeshVisible, scene); processMaterials(); - bool attachmentRenderingNeedsUpdate = false; - for (auto& attachmentModel : _attachmentModels) { - attachmentModel->addToScene(scene, transaction); - attachmentModel->setTagMask(render::hifi::TAG_ALL_VIEWS); - attachmentModel->setGroupCulled(true); - attachmentModel->setCanCastShadow(true); - attachmentModel->setVisibleInScene(_isMeshVisible, scene); - attachmentRenderingNeedsUpdate = true; - } - - if (attachmentRenderingNeedsUpdate) { - updateAttachmentRenderIDs(); - } _mustFadeIn = true; emit DependencyManager::get()->modelAddedToScene(getSessionUUID(), NestableType::Avatar, _skeletonModel); @@ -688,11 +673,6 @@ void Avatar::fadeOut(render::Transaction& transaction, KillAvatarReason reason) void Avatar::fade(render::Transaction& transaction, render::Transition::Type type) { transaction.resetTransitionOnItem(_renderItemID, type); - for (auto& attachmentModel : _attachmentModels) { - for (auto itemId : attachmentModel->fetchRenderItemIDs()) { - transaction.resetTransitionOnItem(itemId, type, _renderItemID); - } - } _lastFadeRequested = type; } @@ -704,9 +684,6 @@ void Avatar::removeFromScene(AvatarSharedPointer self, const render::ScenePointe transaction.removeItem(_renderItemID); render::Item::clearID(_renderItemID); _skeletonModel->removeFromScene(scene, transaction); - for (auto& attachmentModel : _attachmentModels) { - attachmentModel->removeFromScene(scene, transaction); - } emit DependencyManager::get()->modelRemovedFromScene(getSessionUUID(), NestableType::Avatar, _skeletonModel); } @@ -866,8 +843,6 @@ bool Avatar::getEnableMeshVisible() const { void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { bool canTryFade{ false }; - _attachmentsToDelete.clear(); - // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene render::Transaction transaction; @@ -885,27 +860,9 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { canTryFade = true; _isAnimatingScale = true; } - bool attachmentRenderingNeedsUpdate = false; - for (auto attachmentModel : _attachmentModels) { - if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) { - attachmentModel->removeFromScene(scene, transaction); - attachmentModel->addToScene(scene, transaction); - - attachmentModel->setTagMask(render::hifi::TAG_ALL_VIEWS); - attachmentModel->setGroupCulled(true); - attachmentModel->setCanCastShadow(true); - attachmentModel->setVisibleInScene(_isMeshVisible, scene); - attachmentRenderingNeedsUpdate = true; - } - } if (_needMeshVisibleSwitch) { _skeletonModel->setVisibleInScene(_isMeshVisible, scene); - for (auto attachmentModel : _attachmentModels) { - if (attachmentModel->isRenderable()) { - attachmentModel->setVisibleInScene(_isMeshVisible, scene); - } - } updateRenderItem(transaction); _needMeshVisibleSwitch = false; } @@ -916,17 +873,6 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { _mustFadeIn = false; } - for (auto attachmentModelToRemove : _attachmentsToRemove) { - attachmentModelToRemove->removeFromScene(scene, transaction); - attachmentRenderingNeedsUpdate = true; - } - _attachmentsToDelete.insert(_attachmentsToDelete.end(), _attachmentsToRemove.begin(), _attachmentsToRemove.end()); - _attachmentsToRemove.clear(); - - if (attachmentRenderingNeedsUpdate) { - updateAttachmentRenderIDs(); - } - scene->enqueueTransaction(transaction); } @@ -934,48 +880,6 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const { return true; } -void Avatar::simulateAttachments(float deltaTime) { - assert(_attachmentModels.size() == _attachmentModelsTexturesLoaded.size()); - PerformanceTimer perfTimer("attachments"); - for (int i = 0; i < (int)_attachmentModels.size(); i++) { - const AttachmentData& attachment = _attachmentData.at(i); - auto& model = _attachmentModels.at(i); - bool texturesLoaded = _attachmentModelsTexturesLoaded.at(i); - - // Watch for texture loading - if (!texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) { - _attachmentModelsTexturesLoaded[i] = true; - model->updateRenderItems(); - } - - int jointIndex = getJointIndex(attachment.jointName); - glm::vec3 jointPosition; - glm::quat jointRotation; - if (attachment.isSoft) { - // soft attachments do not have transform offsets - model->setTransformNoUpdateRenderItems(Transform(getWorldOrientation() * Quaternions::Y_180, glm::vec3(1.0), getWorldPosition())); - model->simulate(deltaTime); - model->updateRenderItems(); - } else { - if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPosition) && - _skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRotation)) { - model->setTransformNoUpdateRenderItems(Transform(jointRotation * attachment.rotation, glm::vec3(1.0), jointPosition + jointRotation * attachment.translation * getModelScale())); - float scale = getModelScale() * attachment.scale; - model->setScaleToFit(true, model->getNaturalDimensions() * scale, true); // hack to force rescale - model->setSnapModelToCenter(false); // hack to force resnap - model->setSnapModelToCenter(true); - model->simulate(deltaTime); - model->updateRenderItems(); - } - } - } - - if (_ancestorChainRenderableVersion != _lastAncestorChainRenderableVersion) { - _lastAncestorChainRenderableVersion = _ancestorChainRenderableVersion; - updateDescendantRenderIDs(); - } -} - float Avatar::getBoundingRadius() const { return getBounds().getLargestDimension() / 2.0f; } @@ -1631,58 +1535,6 @@ void Avatar::updateFitBoundingBox() { } } -// create new model, can return an instance of a SoftAttachmentModel rather then Model -static std::shared_ptr allocateAttachmentModel(bool isSoft, const Rig& rigOverride, bool isCauterized) { - if (isSoft) { - // cast to std::shared_ptr - std::shared_ptr softModel = std::make_shared(nullptr, rigOverride); - if (isCauterized) { - softModel->flagAsCauterized(); - } - return std::dynamic_pointer_cast(softModel); - } else { - return std::make_shared(); - } -} - -void Avatar::setAttachmentData(const QVector& attachmentData) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "setAttachmentData", - Q_ARG(const QVector, attachmentData)); - return; - } - - auto oldAttachmentData = _attachmentData; - AvatarData::setAttachmentData(attachmentData); - - // if number of attachments has been reduced, remove excess models. - while ((int)_attachmentModels.size() > attachmentData.size()) { - auto attachmentModel = _attachmentModels.back(); - _attachmentModels.pop_back(); - _attachmentModelsTexturesLoaded.pop_back(); - _attachmentsToRemove.push_back(attachmentModel); - } - - for (int i = 0; i < attachmentData.size(); i++) { - if (i == (int)_attachmentModels.size()) { - // if number of attachments has been increased, we need to allocate a new model - _attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar())); - _attachmentModelsTexturesLoaded.push_back(false); - } else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) { - // if the attachment has changed type, we need to re-allocate a new one. - _attachmentsToRemove.push_back(_attachmentModels[i]); - _attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar()); - _attachmentModelsTexturesLoaded[i] = false; - } - // If the model URL has changd, we need to wait for the textures to load - if (_attachmentModels[i]->getURL() != attachmentData[i].modelURL) { - _attachmentModelsTexturesLoaded[i] = false; - } - _attachmentModels[i]->setLoadingPriority(ATTACHMENT_LOADING_PRIORITY); - _attachmentModels[i]->setURL(attachmentData[i].modelURL); - } -} - int Avatar::parseDataFromBuffer(const QByteArray& buffer) { PerformanceTimer perfTimer("unpack"); if (!_initialized) { @@ -2102,11 +1954,6 @@ uint32_t Avatar::appendSubMetaItems(render::ItemIDs& subItems) { return _subItemLock.resultWithReadLock([&] { uint32_t total = 0; - if (_attachmentRenderIDs.size() > 0) { - subItems.insert(subItems.end(), _attachmentRenderIDs.begin(), _attachmentRenderIDs.end()); - total += (uint32_t)_attachmentRenderIDs.size(); - } - if (_descendantRenderIDs.size() > 0) { subItems.insert(subItems.end(), _descendantRenderIDs.begin(), _descendantRenderIDs.end()); total += (uint32_t)_descendantRenderIDs.size(); @@ -2116,18 +1963,6 @@ uint32_t Avatar::appendSubMetaItems(render::ItemIDs& subItems) { }); } -void Avatar::updateAttachmentRenderIDs() { - _subItemLock.withWriteLock([&] { - _attachmentRenderIDs.clear(); - for (auto& attachmentModel : _attachmentModels) { - if (attachmentModel && attachmentModel->isRenderable()) { - auto& metaSubItems = attachmentModel->fetchRenderItemIDs(); - _attachmentRenderIDs.insert(_attachmentRenderIDs.end(), metaSubItems.begin(), metaSubItems.end()); - } - } - }); -} - void Avatar::updateDescendantRenderIDs() { _subItemLock.withWriteLock([&] { auto oldRenderingDescendantEntityIDs = _renderingDescendantEntityIDs; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index c6112d74f2..af6b58a187 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -157,7 +157,6 @@ public: void init(); void removeAvatarEntitiesFromTree(); virtual void simulate(float deltaTime, bool inView) = 0; - virtual void simulateAttachments(float deltaTime); virtual void render(RenderArgs* renderArgs); @@ -344,7 +343,6 @@ public: Q_INVOKABLE glm::quat jointToWorldRotation(const glm::quat& rotation, const int jointIndex = -1) const; Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; - virtual void setAttachmentData(const QVector& attachmentData) override; void updateDisplayNameAlpha(bool showDisplayName); virtual void setSessionDisplayName(const QString& sessionDisplayName) override { }; // no-op @@ -650,10 +648,6 @@ protected: mutable bool _modelJointsCached { false }; glm::vec3 _skeletonOffset; - std::vector> _attachmentModels; - std::vector _attachmentModelsTexturesLoaded; - std::vector> _attachmentsToRemove; - std::vector> _attachmentsToDelete; float _bodyYawDelta { 0.0f }; // degrees/sec float _seatedBodyYawDelta{ 0.0f }; // degrees/renderframe @@ -753,7 +747,6 @@ protected: static const float MYAVATAR_LOADING_PRIORITY; static const float OTHERAVATAR_LOADING_PRIORITY; - static const float ATTACHMENT_LOADING_PRIORITY; LoadingStatus _loadingStatus { LoadingStatus::NoModel }; @@ -773,12 +766,9 @@ protected: VectorOfIDs _grabsToDelete; // deleted grab IDs -- changes needed to entities or physics ReadWriteLockable _subItemLock; - void updateAttachmentRenderIDs(); - render::ItemIDs _attachmentRenderIDs; void updateDescendantRenderIDs(); render::ItemIDs _descendantRenderIDs; std::unordered_set _renderingDescendantEntityIDs; - uint32_t _lastAncestorChainRenderableVersion { 0 }; }; #endif // hifi_Avatar_h diff --git a/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h b/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h index a4595e9bde..bffd0561ab 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h @@ -58,8 +58,6 @@ * when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. * * @property {string} skeletonModelURL - The avatar's FST file. - * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments. - *

    Deprecated: This property is deprecated and will be removed. Use avatar entities instead.

    * @property {string[]} jointNames - The list of joints in the avatar model. * * @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 4068f7c547..d44890b1b8 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -73,17 +73,10 @@ static const float DEFAULT_AVATAR_DENSITY = 1000.0f; // density of water STATIC_SCRIPT_TYPES_INITIALIZER((+[](ScriptManager* manager) { auto scriptEngine = manager->engine().get(); - registerAvatarTypes(scriptEngine); scriptRegisterMetaType(scriptEngine); scriptRegisterMetaType(scriptEngine); })); -STATIC_SCRIPT_INITIALIZER(+[](ScriptManager* manager) { - auto scriptEngine = manager->engine().get(); - - registerAvatarPrototypes(scriptEngine); -}); - size_t AvatarDataPacket::maxFaceTrackerInfoSize(size_t numBlendshapeCoefficients) { return FACE_TRACKER_INFO_SIZE + numBlendshapeCoefficients * sizeof(float); } @@ -2027,7 +2020,6 @@ void AvatarData::processAvatarIdentity(QDataStream& packetStream, bool& identity Identity identity; packetStream - >> identity.attachmentData >> identity.displayName >> identity.sessionDisplayName >> identity.identityFlags @@ -2068,11 +2060,6 @@ void AvatarData::processAvatarIdentity(QDataStream& packetStream, bool& identity } }; - if (identity.attachmentData != _attachmentData) { - setAttachmentData(identity.attachmentData); - identityChanged = true; - } - #ifdef WANT_DEBUG qCDebug(avatars) << __FUNCTION__ << "identity.uuid:" << identity.uuid @@ -2319,7 +2306,6 @@ QByteArray AvatarData::identityByteArray(bool setIsReplicated) const { identityStream << getSessionUUID() << (udt::SequenceNumber::Type) _identitySequenceNumber - << _attachmentData << _displayName << getSessionDisplayNameForTransport() // depends on _sessionDisplayName << identityFlags; @@ -2353,86 +2339,6 @@ void AvatarData::setDisplayName(const QString& displayName) { markIdentityDataChanged(); } -QVector AvatarData::getAttachmentData() const { - if (QThread::currentThread() != thread()) { - QVector result; - BLOCKING_INVOKE_METHOD(const_cast(this), "getAttachmentData", - Q_RETURN_ARG(QVector, result)); - return result; - } - return _attachmentData; -} - -void AvatarData::setAttachmentData(const QVector& attachmentData) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setAttachmentData", Q_ARG(const QVector&, attachmentData)); - return; - } - _attachmentData = attachmentData; - markIdentityDataChanged(); -} - -void AvatarData::attach(const QString& modelURL, const QString& jointName, - const glm::vec3& translation, const glm::quat& rotation, - float scale, bool isSoft, - bool allowDuplicates, bool useSaved) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "attach", Q_ARG(const QString&, modelURL), Q_ARG(const QString&, jointName), - Q_ARG(const glm::vec3&, translation), Q_ARG(const glm::quat&, rotation), - Q_ARG(float, scale), Q_ARG(bool, isSoft), - Q_ARG(bool, allowDuplicates), Q_ARG(bool, useSaved)); - return; - } - QVector attachmentData = getAttachmentData(); - if (!allowDuplicates) { - foreach (const AttachmentData& data, attachmentData) { - if (data.modelURL == modelURL && (jointName.isEmpty() || data.jointName == jointName)) { - return; - } - } - } - AttachmentData data; - data.modelURL = modelURL; - data.jointName = jointName; - data.translation = translation; - data.rotation = rotation; - data.scale = scale; - data.isSoft = isSoft; - attachmentData.append(data); - setAttachmentData(attachmentData); -} - -void AvatarData::detachOne(const QString& modelURL, const QString& jointName) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "detachOne", Q_ARG(const QString&, modelURL), Q_ARG(const QString&, jointName)); - return; - } - QVector attachmentData = getAttachmentData(); - for (QVector::iterator it = attachmentData.begin(); it != attachmentData.end(); ++it) { - if (it->modelURL == modelURL && (jointName.isEmpty() || it->jointName == jointName)) { - attachmentData.erase(it); - setAttachmentData(attachmentData); - return; - } - } -} - -void AvatarData::detachAll(const QString& modelURL, const QString& jointName) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "detachAll", Q_ARG(const QString&, modelURL), Q_ARG(const QString&, jointName)); - return; - } - QVector attachmentData = getAttachmentData(); - for (QVector::iterator it = attachmentData.begin(); it != attachmentData.end(); ) { - if (it->modelURL == modelURL && (jointName.isEmpty() || it->jointName == jointName)) { - it = attachmentData.erase(it); - } else { - ++it; - } - } - setAttachmentData(attachmentData); -} - int AvatarData::sendAvatarDataPacket(bool sendAll) { auto nodeList = DependencyManager::get(); @@ -2495,149 +2401,6 @@ int AvatarData::sendIdentityPacket() { return identityData.size(); } -static const QString JSON_ATTACHMENT_URL = QStringLiteral("modelUrl"); -static const QString JSON_ATTACHMENT_JOINT_NAME = QStringLiteral("jointName"); -static const QString JSON_ATTACHMENT_TRANSFORM = QStringLiteral("transform"); -static const QString JSON_ATTACHMENT_IS_SOFT = QStringLiteral("isSoft"); - -QJsonObject AttachmentData::toJson() const { - QJsonObject result; - if (modelURL.isValid() && !modelURL.isEmpty()) { - result[JSON_ATTACHMENT_URL] = modelURL.toString(); - } - if (!jointName.isEmpty()) { - result[JSON_ATTACHMENT_JOINT_NAME] = jointName; - } - // FIXME the transform constructor that takes rot/scale/translation - // doesn't return the correct value for isIdentity() - Transform transform; - transform.setRotation(rotation); - transform.setScale(scale); - transform.setTranslation(translation); - if (!transform.isIdentity()) { - result[JSON_ATTACHMENT_TRANSFORM] = Transform::toJson(transform); - } - result[JSON_ATTACHMENT_IS_SOFT] = isSoft; - return result; -} - -void AttachmentData::fromJson(const QJsonObject& json) { - if (json.contains(JSON_ATTACHMENT_URL)) { - const QString modelURLTemp = json[JSON_ATTACHMENT_URL].toString(); - if (modelURLTemp != modelURL.toString()) { - modelURL = modelURLTemp; - } - } - - if (json.contains(JSON_ATTACHMENT_JOINT_NAME)) { - const QString jointNameTemp = json[JSON_ATTACHMENT_JOINT_NAME].toString(); - if (jointNameTemp != jointName) { - jointName = jointNameTemp; - } - } - - if (json.contains(JSON_ATTACHMENT_TRANSFORM)) { - Transform transform = Transform::fromJson(json[JSON_ATTACHMENT_TRANSFORM]); - translation = transform.getTranslation(); - rotation = transform.getRotation(); - scale = transform.getScale().x; - } - - if (json.contains(JSON_ATTACHMENT_IS_SOFT)) { - isSoft = json[JSON_ATTACHMENT_IS_SOFT].toBool(); - } -} - -bool AttachmentData::operator==(const AttachmentData& other) const { - return modelURL == other.modelURL && jointName == other.jointName && translation == other.translation && - rotation == other.rotation && scale == other.scale && isSoft == other.isSoft; -} - -QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment) { - return out << attachment.modelURL << attachment.jointName << - attachment.translation << attachment.rotation << attachment.scale << attachment.isSoft; -} - -QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) { - return in >> attachment.modelURL >> attachment.jointName >> - attachment.translation >> attachment.rotation >> attachment.scale >> attachment.isSoft; -} - -void AttachmentDataObject::setModelURL(const QString& modelURL) { - AttachmentData data = scriptvalue_cast(thisObject()); - data.modelURL = modelURL; - Q_ASSERT(engine()); - thisObject() = engine()->toScriptValue(data); -} - -QString AttachmentDataObject::getModelURL() const { - return scriptvalue_cast(thisObject()).modelURL.toString(); -} - -void AttachmentDataObject::setJointName(const QString& jointName) { - AttachmentData data = scriptvalue_cast(thisObject()); - data.jointName = jointName; - Q_ASSERT(engine()); - thisObject() = engine()->toScriptValue(data); -} - -QString AttachmentDataObject::getJointName() const { - return scriptvalue_cast(thisObject()).jointName; -} - -void AttachmentDataObject::setTranslation(const glm::vec3& translation) { - AttachmentData data = scriptvalue_cast(thisObject()); - data.translation = translation; - Q_ASSERT(engine()); - thisObject() = engine()->toScriptValue(data); -} - -glm::vec3 AttachmentDataObject::getTranslation() const { - return scriptvalue_cast(thisObject()).translation; -} - -void AttachmentDataObject::setRotation(const glm::quat& rotation) { - AttachmentData data = scriptvalue_cast(thisObject()); - data.rotation = rotation; - Q_ASSERT(engine()); - thisObject() = engine()->toScriptValue(data); -} - -glm::quat AttachmentDataObject::getRotation() const { - return scriptvalue_cast(thisObject()).rotation; -} - -void AttachmentDataObject::setScale(float scale) { - AttachmentData data = scriptvalue_cast(thisObject()); - data.scale = scale; - Q_ASSERT(engine()); - thisObject() = engine()->toScriptValue(data); -} - -float AttachmentDataObject::getScale() const { - return scriptvalue_cast(thisObject()).scale; -} - -void AttachmentDataObject::setIsSoft(bool isSoft) { - AttachmentData data = scriptvalue_cast(thisObject()); - data.isSoft = isSoft; - Q_ASSERT(engine()); - thisObject() = engine()->toScriptValue(data); -} - -bool AttachmentDataObject::getIsSoft() const { - return scriptvalue_cast(thisObject()).isSoft; -} - -void registerAvatarTypes(ScriptEngine* engine) { - scriptRegisterSequenceMetaType >(engine); -} - -void registerAvatarPrototypes(ScriptEngine* engine) { - engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( - new AttachmentDataObject(), ScriptEngine::ScriptOwnership)); -} - void AvatarData::setRecordingBasis(std::shared_ptr recordingBasis) { if (!recordingBasis) { recordingBasis = std::make_shared(); @@ -2670,7 +2433,6 @@ static const QString JSON_AVATAR_HEAD_MODEL = QStringLiteral("headModel"); static const QString JSON_AVATAR_BODY_MODEL = QStringLiteral("bodyModel"); static const QString JSON_AVATAR_DISPLAY_NAME = QStringLiteral("displayName"); // It isn't meaningful to persist sessionDisplayName. -static const QString JSON_AVATAR_ATTACHMENTS = QStringLiteral("attachments"); static const QString JSON_AVATAR_ENTITIES = QStringLiteral("attachedEntities"); static const QString JSON_AVATAR_SCALE = QStringLiteral("scale"); static const QString JSON_AVATAR_VERSION = QStringLiteral("version"); @@ -2838,24 +2600,11 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { setTargetScale((float)json[JSON_AVATAR_SCALE].toDouble()); } - QVector attachments; - if (json.contains(JSON_AVATAR_ATTACHMENTS) && json[JSON_AVATAR_ATTACHMENTS].isArray()) { - QJsonArray attachmentsJson = json[JSON_AVATAR_ATTACHMENTS].toArray(); - for (auto attachmentJson : attachmentsJson) { - AttachmentData attachment; - attachment.fromJson(attachmentJson.toObject()); - attachments.push_back(attachment); - } - } - if (attachments != getAttachmentData()) { - setAttachmentData(attachments); - } - if (json.contains(JSON_AVATAR_ENTITIES) && json[JSON_AVATAR_ENTITIES].isArray()) { - QJsonArray attachmentsJson = json[JSON_AVATAR_ENTITIES].toArray(); - for (auto attachmentJson : attachmentsJson) { - if (attachmentJson.isObject()) { - QVariantMap entityData = attachmentJson.toObject().toVariantMap(); + QJsonArray avatarEntitiesJSON = json[JSON_AVATAR_ENTITIES].toArray(); + for (auto avatarEntityJSON : avatarEntitiesJSON) { + if (avatarEntityJSON.isObject()) { + QVariantMap entityData = avatarEntityJSON.toObject().toVariantMap(); QUuid id = entityData.value("id").toUuid(); QByteArray data = QByteArray::fromBase64(entityData.value("properties").toByteArray()); updateAvatarEntity(id, data); @@ -2970,30 +2719,6 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const return glm::vec3(); } -/*@jsdoc - * Information on an attachment worn by the avatar. - * @typedef {object} AttachmentData - * @property {string} modelUrl - The URL of the glTF, FBX, or OBJ model file. glTF models may be in JSON or binary format - * (".gltf" or ".glb" URLs respectively). - * @property {string} jointName - The name of the joint that the attachment is parented to. - * @property {Vec3} translation - The offset from the joint that the attachment is positioned at. - * @property {Vec3} rotation - The rotation applied to the model relative to the joint orientation. - * @property {number} scale - The scale applied to the attachment model. - * @property {boolean} soft - If true and the model has a skeleton, the bones of the attached model's skeleton are - * rotated to fit the avatar's current pose. If true, the translation, rotation, and - * scale parameters are ignored. - */ -QVariant AttachmentData::toVariant() const { - QVariantMap result; - result["modelUrl"] = modelURL; - result["jointName"] = jointName; - result["translation"] = vec3ToQMap(translation); - result["rotation"] = vec3ToQMap(glm::degrees(safeEulerAngles(rotation))); - result["scale"] = scale; - result["soft"] = isSoft; - return result; -} - glm::vec3 variantToVec3(const QVariant& var) { auto map = var.toMap(); glm::vec3 result; @@ -3003,52 +2728,6 @@ glm::vec3 variantToVec3(const QVariant& var) { return result; } -bool AttachmentData::fromVariant(const QVariant& variant) { - bool isValid = false; - auto map = variant.toMap(); - if (map.contains("modelUrl")) { - auto urlString = map["modelUrl"].toString(); - modelURL = urlString; - isValid = true; - } - if (map.contains("jointName")) { - jointName = map["jointName"].toString(); - } - if (map.contains("translation")) { - translation = variantToVec3(map["translation"]); - } - if (map.contains("rotation")) { - rotation = glm::quat(glm::radians(variantToVec3(map["rotation"]))); - } - if (map.contains("scale")) { - scale = map["scale"].toFloat(); - } - if (map.contains("soft")) { - isSoft = map["soft"].toBool(); - } - return isValid; -} - -QVariantList AvatarData::getAttachmentsVariant() const { - QVariantList result; - for (const auto& attachment : getAttachmentData()) { - result.append(attachment.toVariant()); - } - return result; -} - -void AvatarData::setAttachmentsVariant(const QVariantList& variant) { - QVector newAttachments; - newAttachments.reserve(variant.size()); - for (const auto& attachmentVar : variant) { - AttachmentData attachment; - if (attachment.fromVariant(attachmentVar)) { - newAttachments.append(attachment); - } - } - setAttachmentData(newAttachments); -} - void AvatarData::storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& data) { bool changed = false; _avatarEntitiesLock.withWriteLock([&] { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index d3bf8a3282..52f2878983 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -451,7 +451,6 @@ Q_DECLARE_METATYPE(KillAvatarReason); class QDataStream; -class AttachmentData; class Transform; using TransformPointer = std::shared_ptr; @@ -523,8 +522,6 @@ class AvatarData : public QObject, public SpatiallyNestable { * @property {boolean} lookAtSnappingEnabled=true - true if the avatar's eyes snap to look at another avatar's * eyes when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. * @property {string} skeletonModelURL - The avatar's FST file. - * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments. - *

    Deprecated: This property is deprecated and will be removed. Use avatar entities instead.

    * @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. * @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. Read-only. * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the @@ -580,7 +577,6 @@ class AvatarData : public QObject, public SpatiallyNestable { Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName WRITE setSessionDisplayName NOTIFY sessionDisplayNameChanged) Q_PROPERTY(bool lookAtSnappingEnabled MEMBER _lookAtSnappingEnabled NOTIFY lookAtSnappingChanged) Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript NOTIFY skeletonModelURLChanged) - Q_PROPERTY(QVector attachmentData READ getAttachmentData WRITE setAttachmentData) Q_PROPERTY(QStringList jointNames READ getJointNames) @@ -1145,27 +1141,6 @@ public: */ Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); } - - /*@jsdoc - * Gets information about the models currently attached to your avatar. - * @function Avatar.getAttachmentsVariant - * @returns {AttachmentData[]} Information about all models attached to your avatar. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - */ - // FIXME: Can this name be improved? Can it be deprecated? - Q_INVOKABLE virtual QVariantList getAttachmentsVariant() const; - - /*@jsdoc - * Sets all models currently attached to your avatar. For example, if you retrieve attachment data using - * {@link MyAvatar.getAttachmentsVariant} or {@link Avatar.getAttachmentsVariant}, make changes to it, and then want to - * update your avatar's attachments per the changed data. - * @function Avatar.setAttachmentsVariant - * @param {AttachmentData[]} variant - The attachment data defining the models to have attached to your avatar. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - */ - // FIXME: Can this name be improved? Can it be deprecated? - Q_INVOKABLE virtual void setAttachmentsVariant(const QVariantList& variant); - virtual void storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& payload); /*@jsdoc @@ -1209,7 +1184,6 @@ public: const HeadData* getHeadData() const { return _headData; } struct Identity { - QVector attachmentData; QString displayName; QString sessionDisplayName; bool isReplicated; @@ -1254,109 +1228,6 @@ public: } virtual bool isCertifyFailed() const { return _verificationFailed; } - /*@jsdoc - * Gets information about the models currently attached to your avatar. - * @function Avatar.getAttachmentData - * @returns {AttachmentData[]} Information about all models attached to your avatar. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - * @example Report the URLs of all current attachments. - * var attachments = MyAvatar.getaAttachmentData(); - * for (var i = 0; i < attachments.length; i++) { - * print(attachments[i].modelURL); - * } - * - * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". - */ - Q_INVOKABLE virtual QVector getAttachmentData() const; - - /*@jsdoc - * Sets all models currently attached to your avatar. For example, if you retrieve attachment data using - * {@link MyAvatar.getAttachmentData} or {@link Avatar.getAttachmentData}, make changes to it, and then want to update your avatar's attachments per the - * changed data. You can also remove all attachments by using setting attachmentData to null. - * @function Avatar.setAttachmentData - * @param {AttachmentData[]} attachmentData - The attachment data defining the models to have attached to your avatar. Use - * null to remove all attachments. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - * @example Remove a hat attachment if your avatar is wearing it. - * var hatURL = "https://apidocs.overte.org/examples/cowboy-hat.fbx"; - * var attachments = MyAvatar.getAttachmentData(); - * - * for (var i = 0; i < attachments.length; i++) { - * if (attachments[i].modelURL === hatURL) { - * attachments.splice(i, 1); - * MyAvatar.setAttachmentData(attachments); - * break; - * } - * } - * - * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". - */ - Q_INVOKABLE virtual void setAttachmentData(const QVector& attachmentData); - - /*@jsdoc - * Attaches a model to your avatar. For example, you can give your avatar a hat to wear, a guitar to hold, or a surfboard to - * stand on. - * @function Avatar.attach - * @param {string} modelURL - The URL of the glTF, FBX, or OBJ model to attach. glTF models may be in JSON or binary format - * (".gltf" or ".glb" URLs respectively). - * @param {string} [jointName=""] - The name of the avatar joint (see {@link MyAvatar.getJointNames} or - * {@link Avatar.getJointNames}) to attach the model to. - * @param {Vec3} [translation=Vec3.ZERO] - The offset to apply to the model relative to the joint position. - * @param {Quat} [rotation=Quat.IDENTITY] - The rotation to apply to the model relative to the joint orientation. - * @param {number} [scale=1.0] - The scale to apply to the model. - * @param {boolean} [isSoft=false] - If the model has a skeleton, set this to true so that the bones of the - * attached model's skeleton are rotated to fit the avatar's current pose. isSoft is used, for example, - * to have clothing that moves with the avatar. - *

    If true, the translation, rotation, and scale parameters are - * ignored.

    - * @param {boolean} [allowDuplicates=false] - If true then more than one copy of any particular model may be - * attached to the same joint; if false then the same model cannot be attached to the same joint. - * @param {boolean} [useSaved=true] - Not used. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - * @example Attach a cowboy hat to your avatar's head. - * var attachment = { - * modelURL: "https://apidocs.overte.org/examples/cowboy-hat.fbx", - * jointName: "Head", - * translation: {"x": 0, "y": 0.25, "z": 0}, - * rotation: {"x": 0, "y": 0, "z": 0, "w": 1}, - * scale: 0.01, - * isSoft: false - * }; - * - * MyAvatar.attach(attachment.modelURL, - * attachment.jointName, - * attachment.translation, - * attachment.rotation, - * attachment.scale, - * attachment.isSoft); - * - * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". - */ - Q_INVOKABLE virtual void attach(const QString& modelURL, const QString& jointName = QString(), - const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), - float scale = 1.0f, bool isSoft = false, - bool allowDuplicates = false, bool useSaved = true); - - /*@jsdoc - * Detaches the most recently attached instance of a particular model from either a specific joint or any joint. - * @function Avatar.detachOne - * @param {string} modelURL - The URL of the model to detach. - * @param {string} [jointName=""] - The name of the joint to detach the model from. If "", then the most - * recently attached model is removed from which ever joint it was attached to. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - */ - Q_INVOKABLE virtual void detachOne(const QString& modelURL, const QString& jointName = QString()); - - /*@jsdoc - * Detaches all instances of a particular model from either a specific joint or all joints. - * @function Avatar.detachAll - * @param {string} modelURL - The URL of the model to detach. - * @param {string} [jointName=""] - The name of the joint to detach the model from. If "", then the model is - * detached from all joints. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - */ - Q_INVOKABLE virtual void detachAll(const QString& modelURL, const QString& jointName = QString()); - QString getSkeletonModelURLFromScript() const; void setSkeletonModelURLFromScript(const QString& skeletonModelString) { setSkeletonModelURL(QUrl(skeletonModelString)); } @@ -1732,8 +1603,6 @@ protected: mutable HeadData* _headData { nullptr }; QUrl _skeletonModelURL; - QVector _attachmentData; - QVector _oldAttachmentData; QString _displayName; QString _sessionDisplayName { }; bool _lookAtSnappingEnabled { true }; @@ -1899,66 +1768,6 @@ Q_DECLARE_METATYPE(AvatarData*) QJsonValue toJsonValue(const JointData& joint); JointData jointDataFromJsonValue(const QJsonValue& q); -class AttachmentData { -public: - QUrl modelURL; - QString jointName; - glm::vec3 translation; - glm::quat rotation; - float scale { 1.0f }; - bool isSoft { false }; - - bool isValid() const { return modelURL.isValid(); } - - bool operator==(const AttachmentData& other) const; - - QJsonObject toJson() const; - void fromJson(const QJsonObject& json); - - QVariant toVariant() const; - bool fromVariant(const QVariant& variant); -}; - -QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment); -QDataStream& operator>>(QDataStream& in, AttachmentData& attachment); - -Q_DECLARE_METATYPE(AttachmentData) -Q_DECLARE_METATYPE(QVector) - -/// Scriptable wrapper for attachments. -class AttachmentDataObject : public QObject, protected Scriptable { - Q_OBJECT - Q_PROPERTY(QString modelURL READ getModelURL WRITE setModelURL) - Q_PROPERTY(QString jointName READ getJointName WRITE setJointName) - Q_PROPERTY(glm::vec3 translation READ getTranslation WRITE setTranslation) - Q_PROPERTY(glm::quat rotation READ getRotation WRITE setRotation) - Q_PROPERTY(float scale READ getScale WRITE setScale) - Q_PROPERTY(bool isSoft READ getIsSoft WRITE setIsSoft) - -public: - - Q_INVOKABLE void setModelURL(const QString& modelURL); - Q_INVOKABLE QString getModelURL() const; - - Q_INVOKABLE void setJointName(const QString& jointName); - Q_INVOKABLE QString getJointName() const; - - Q_INVOKABLE void setTranslation(const glm::vec3& translation); - Q_INVOKABLE glm::vec3 getTranslation() const; - - Q_INVOKABLE void setRotation(const glm::quat& rotation); - Q_INVOKABLE glm::quat getRotation() const; - - Q_INVOKABLE void setScale(float scale); - Q_INVOKABLE float getScale() const; - - Q_INVOKABLE void setIsSoft(bool scale); - Q_INVOKABLE bool getIsSoft() const; -}; - -void registerAvatarTypes(ScriptEngine* engine); -void registerAvatarPrototypes(ScriptEngine* engine); - class RayToAvatarIntersectionResult { public: bool intersects { false }; diff --git a/libraries/avatars/src/ScriptAvatarData.cpp b/libraries/avatars/src/ScriptAvatarData.cpp index 1d93a6e954..43dc5097ef 100644 --- a/libraries/avatars/src/ScriptAvatarData.cpp +++ b/libraries/avatars/src/ScriptAvatarData.cpp @@ -200,7 +200,7 @@ bool ScriptAvatarData::getLookAtSnappingEnabled() const { // // -// ATTACHMENT AND JOINT PROPERTIES +// JOINT PROPERTIES // START // QString ScriptAvatarData::getSkeletonModelURLFromScript() const { @@ -285,15 +285,8 @@ QStringList ScriptAvatarData::getJointNames() const { return QStringList(); } } -QVector ScriptAvatarData::getAttachmentData() const { - if (AvatarSharedPointer sharedAvatarData = _avatarData.lock()) { - return sharedAvatarData->getAttachmentData(); - } else { - return QVector(); - } -} // -// ATTACHMENT AND JOINT PROPERTIES +// JOINT PROPERTIES // END // diff --git a/libraries/avatars/src/ScriptAvatarData.h b/libraries/avatars/src/ScriptAvatarData.h index 8f9b7b77b1..960423a1ee 100644 --- a/libraries/avatars/src/ScriptAvatarData.h +++ b/libraries/avatars/src/ScriptAvatarData.h @@ -50,10 +50,9 @@ class ScriptAvatarData : public QObject { Q_PROPERTY(bool lookAtSnappingEnabled READ getLookAtSnappingEnabled NOTIFY lookAtSnappingChanged) // - // ATTACHMENT AND JOINT PROPERTIES + // JOINT PROPERTIES // Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript NOTIFY skeletonModelURLChanged) - Q_PROPERTY(QVector attachmentData READ getAttachmentData) Q_PROPERTY(QStringList jointNames READ getJointNames) // @@ -104,7 +103,7 @@ public: bool getLookAtSnappingEnabled() const; // - // ATTACHMENT AND JOINT PROPERTIES + // JOINT PROPERTIES // QString getSkeletonModelURLFromScript() const; @@ -204,15 +203,6 @@ public: */ Q_INVOKABLE QStringList getJointNames() const; - /*@jsdoc - * Gets information about the models currently attached to the avatar. - * @function ScriptAvatar.getAttachmentData - * @returns {AttachmentData[]} Information about all models attached to the avatar, or [] if the avatar data - * aren't available. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - */ - Q_INVOKABLE QVector getAttachmentData() const; - #if DEV_BUILD || PR_BUILD Q_INVOKABLE AvatarEntityMap getAvatarEntities() const; #endif diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index c68651d42c..da76961e7d 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -454,7 +454,6 @@ bool EntityRenderer::addToScene(const ScenePointer& scene, Transaction& transact transaction.resetItem(_renderItemID, renderPayload); onAddToScene(_entity); updateInScene(scene, transaction); - _entity->bumpAncestorChainRenderableVersion(); return true; } @@ -462,7 +461,6 @@ void EntityRenderer::removeFromScene(const ScenePointer& scene, Transaction& tra onRemoveFromScene(_entity); transaction.removeItem(_renderItemID); Item::clearID(_renderItemID); - _entity->bumpAncestorChainRenderableVersion(); } void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& transaction) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 9ea1d2f942..1b54d1b3b7 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1261,7 +1261,6 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint if (!_hasModel) { if (model) { model->removeFromScene(scene, transaction); - entity->bumpAncestorChainRenderableVersion(); emit DependencyManager::get()-> modelRemovedFromScene(entity->getEntityItemID(), NestableType::Entity, model); withWriteLock([&] { _model.reset(); }); @@ -1391,7 +1390,6 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint makeStatusGetters(entity, statusGetters); using namespace std::placeholders; model->addToScene(scene, transaction, statusGetters, std::bind(&ModelEntityRenderer::metaBlendshapeOperator, _renderItemID, _1, _2, _3, _4)); - entity->bumpAncestorChainRenderableVersion(); processMaterials(); } } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index cc0d93604a..7037785c48 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -2969,10 +2969,6 @@ void EntityItem::setVisible(bool value) { _needsRenderUpdate |= changed; _visible = value; }); - - if (changed) { - bumpAncestorChainRenderableVersion(); - } } bool EntityItem::isVisibleInSecondaryCamera() const { diff --git a/libraries/model-serializers/src/FSTReader.cpp b/libraries/model-serializers/src/FSTReader.cpp index d7c4b73048..7e84f012a7 100644 --- a/libraries/model-serializers/src/FSTReader.cpp +++ b/libraries/model-serializers/src/FSTReader.cpp @@ -183,7 +183,6 @@ QString FSTReader::getNameFromType(ModelType modelType) { _typesToNames[HEAD_MODEL] = "head"; _typesToNames[BODY_ONLY_MODEL] = "body"; _typesToNames[HEAD_AND_BODY_MODEL] = "body+head"; - _typesToNames[ATTACHMENT_MODEL] = "attachment"; } return _typesToNames[modelType]; } @@ -195,9 +194,6 @@ FSTReader::ModelType FSTReader::getTypeFromName(const QString& name) { _namesToTypes["head"] = HEAD_MODEL ; _namesToTypes["body"] = BODY_ONLY_MODEL; _namesToTypes["body+head"] = HEAD_AND_BODY_MODEL; - - // NOTE: this is not yet implemented, but will be used to allow you to attach fully independent models to your avatar - _namesToTypes["attachment"] = ATTACHMENT_MODEL; } return _namesToTypes[name]; } diff --git a/libraries/model-serializers/src/FSTReader.h b/libraries/model-serializers/src/FSTReader.h index 4ba0428e83..e1b7405346 100644 --- a/libraries/model-serializers/src/FSTReader.h +++ b/libraries/model-serializers/src/FSTReader.h @@ -42,8 +42,7 @@ public: ENTITY_MODEL, HEAD_MODEL, BODY_ONLY_MODEL, - HEAD_AND_BODY_MODEL, - ATTACHMENT_MODEL + HEAD_AND_BODY_MODEL }; /// Reads an FST mapping from the supplied data. diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index e561bfe21e..943c8cce71 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -38,10 +38,10 @@ PacketVersion versionForPacketType(PacketType packetType) { return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: case PacketType::AvatarData: - return static_cast(AvatarMixerPacketVersion::ARKitBlendshapes); + return static_cast(AvatarMixerPacketVersion::RemoveAttachments); case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::ARKitBlendshapes); + return static_cast(AvatarMixerPacketVersion::RemoveAttachments); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); // ICE packets diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index f8d80660f4..86c8e8193a 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -363,7 +363,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { FBXJointOrderChange, HandControllerSection, SendVerificationFailed, - ARKitBlendshapes + ARKitBlendshapes, + RemoveAttachments, }; enum class DomainConnectRequestVersion : PacketVersion { diff --git a/libraries/recording/src/recording/RecordingScriptingInterface.cpp b/libraries/recording/src/recording/RecordingScriptingInterface.cpp index a05ee60604..a50453bb69 100644 --- a/libraries/recording/src/recording/RecordingScriptingInterface.cpp +++ b/libraries/recording/src/recording/RecordingScriptingInterface.cpp @@ -172,10 +172,6 @@ void RecordingScriptingInterface::setPlayerUseDisplayName(bool useDisplayName) { _useDisplayName = useDisplayName; } -void RecordingScriptingInterface::setPlayerUseAttachments(bool useAttachments) { - _useAttachments = useAttachments; -} - void RecordingScriptingInterface::setPlayerUseHeadModel(bool useHeadModel) { _useHeadModel = useHeadModel; } diff --git a/libraries/recording/src/recording/RecordingScriptingInterface.h b/libraries/recording/src/recording/RecordingScriptingInterface.h index 394c3e230d..2f84b9109a 100644 --- a/libraries/recording/src/recording/RecordingScriptingInterface.h +++ b/libraries/recording/src/recording/RecordingScriptingInterface.h @@ -162,14 +162,6 @@ public slots: */ void setPlayerUseDisplayName(bool useDisplayName); - /*@jsdoc - *

    Not used.

    - * @function Recording.setPlayerUseAttachments - * @param {boolean} useAttachments - Use attachments. - * @deprecated This method is deprecated and will be removed. - */ - void setPlayerUseAttachments(bool useAttachments); - /*@jsdoc *

    Not used.

    * @function Recording.setPlayerUseHeadModel @@ -203,14 +195,6 @@ public slots: */ bool getPlayerUseDisplayName() { return _useDisplayName; } - /*@jsdoc - *

    Not used.

    - * @function Recording.getPlayerUseAttachments - * @returns {boolean} Use attachments. - * @deprecated This method is deprecated and will be removed. - */ - bool getPlayerUseAttachments() { return _useAttachments; } - /*@jsdoc *

    Not used.

    * @function Recording.getPlayerUseHeadModel @@ -365,7 +349,6 @@ protected: Flag _playFromCurrentLocation { true }; Flag _useDisplayName { false }; Flag _useHeadModel { false }; - Flag _useAttachments { false }; Flag _useSkeletonModel { false }; recording::ClipPointer _lastClip; diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp deleted file mode 100644 index 24d6081743..0000000000 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// -// Created by Anthony J. Thibault on 12/17/15. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "SoftAttachmentModel.h" - -SoftAttachmentModel::SoftAttachmentModel(QObject* parent, const Rig& rigOverride) : - CauterizedModel(parent), - _rigOverride(rigOverride) { -} - -SoftAttachmentModel::~SoftAttachmentModel() { -} - -// virtual -void SoftAttachmentModel::updateRig(float deltaTime, glm::mat4 parentTransform) { - _needsUpdateClusterMatrices = true; -} - -int SoftAttachmentModel::getJointIndexOverride(int i) const { - QString name = _rig.nameOfJoint(i); - if (name.isEmpty()) { - return -1; - } - return _rigOverride.indexOfJoint(name); -} - -// virtual -// use the _rigOverride matrices instead of the Model::_rig -void SoftAttachmentModel::updateClusterMatrices() { - if (!_needsUpdateClusterMatrices) { - return; - } - if (!isLoaded()) { - return; - } - - _needsUpdateClusterMatrices = false; - - const HFMModel& hfmModel = getHFMModel(); - - for (int i = 0; i < (int) _meshStates.size(); i++) { - MeshState& state = _meshStates[i]; - const HFMMesh& mesh = hfmModel.meshes.at(i); - int meshIndex = i; - for (int j = 0; j < mesh.clusters.size(); j++) { - const HFMCluster& cluster = mesh.clusters.at(j); - - int clusterIndex = j; - // TODO: cache these look-ups as an optimization - int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); - glm::mat4 jointMatrix; - if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { - jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); - } else { - jointMatrix = _rig.getJointTransform(cluster.jointIndex); - } - if (_useDualQuaternionSkinning) { - glm::mat4 m; - glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, m); - state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(m); - } else { - glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); - } - } - } - - updateBlendshapes(); -} diff --git a/libraries/render-utils/src/SoftAttachmentModel.h b/libraries/render-utils/src/SoftAttachmentModel.h deleted file mode 100644 index 4335c1634e..0000000000 --- a/libraries/render-utils/src/SoftAttachmentModel.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// Created by Anthony J. Thibault on 12/17/15. -// Copyright 2015 High Fidelity, Inc. -// -// 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_SoftAttachmentModel_h -#define hifi_SoftAttachmentModel_h - -#include "CauterizedModel.h" - -// A model that allows the creator to specify a secondary rig instance. -// When the cluster matrices are created for rendering, the -// cluster matrices will use the secondary rig for the joint poses -// instead of the primary rig. -// -// This is used by Avatar instances to wear clothing that follows the same -// animated pose as the SkeletonModel. - -class SoftAttachmentModel : public CauterizedModel { - Q_OBJECT - -public: - SoftAttachmentModel(QObject* parent, const Rig& rigOverride); - ~SoftAttachmentModel(); - - void updateRig(float deltaTime, glm::mat4 parentTransform) override; - void updateClusterMatrices() override; - -protected: - int getJointIndexOverride(int i) const; - - const Rig& _rigOverride; -}; - -#endif // hifi_SoftAttachmentModel_h diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 7a02c2dbbb..d8395c8739 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -69,7 +69,6 @@ const QUuid SpatiallyNestable::getParentID() const { } void SpatiallyNestable::setParentID(const QUuid& parentID) { - bumpAncestorChainRenderableVersion(); bool success = false; auto parent = getParentPointer(success); bool parentChanged = false; @@ -89,7 +88,6 @@ void SpatiallyNestable::setParentID(const QUuid& parentID) { success = false; parent = getParentPointer(success); if (success && parent) { - bumpAncestorChainRenderableVersion(); parent->updateQueryAACube(); parent->recalculateChildCauterization(); } @@ -1509,17 +1507,3 @@ QUuid SpatiallyNestable::getEditSenderID() { }); return editSenderID; } - -void SpatiallyNestable::bumpAncestorChainRenderableVersion(int depth) const { - if (depth > MAX_PARENTING_CHAIN_SIZE) { - // can't break the parent chain here, because it will call setParentID, which calls this - return; - } - - _ancestorChainRenderableVersion++; - bool success = false; - auto parent = getParentPointer(success); - if (success && parent) { - parent->bumpAncestorChainRenderableVersion(depth + 1); - } -} diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 29f23afdfb..a04ad62a3a 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -224,8 +224,6 @@ public: bool hasGrabs(); virtual QUuid getEditSenderID(); - void bumpAncestorChainRenderableVersion(int depth = 0) const; - protected: QUuid _id; mutable SpatiallyNestableWeakPointer _parent; @@ -248,8 +246,6 @@ protected: mutable ReadWriteLockable _grabsLock; QSet _grabs; // upon this thing - mutable std::atomic _ancestorChainRenderableVersion { 0 }; - private: SpatiallyNestable() = delete; const NestableType _nestableType; // EntityItem or an AvatarData From 9d2e03a5a4898f815fc67b700aa9b157462bf8b1 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 19 Jul 2024 17:31:51 -0700 Subject: [PATCH 050/109] fix non-localOnly positional sounds not updating --- libraries/entities/src/SimpleEntitySimulation.cpp | 4 ++-- libraries/entities/src/SoundEntityItem.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index d64efdf87f..ecdc37ebfb 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -62,7 +62,7 @@ void SimpleEntitySimulation::addEntityToInternalLists(EntityItemPointer entity) // we don't allow dynamic objects to move without an owner so nothing to do here } else if (entity->isMovingRelativeToParent()) { SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity); - if (itr != _simpleKinematicEntities.end()) { + if (itr == _simpleKinematicEntities.end()) { _simpleKinematicEntities.insert(entity); entity->setLastSimulated(usecTimestampNow()); } @@ -73,7 +73,7 @@ void SimpleEntitySimulation::addEntityToInternalLists(EntityItemPointer entity) if (entity->isMovingRelativeToParent()) { SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity); - if (itr != _simpleKinematicEntities.end()) { + if (itr == _simpleKinematicEntities.end()) { _simpleKinematicEntities.insert(entity); entity->setLastSimulated(usecTimestampNow()); } diff --git a/libraries/entities/src/SoundEntityItem.cpp b/libraries/entities/src/SoundEntityItem.cpp index 71845920f9..d8c648a61a 100644 --- a/libraries/entities/src/SoundEntityItem.cpp +++ b/libraries/entities/src/SoundEntityItem.cpp @@ -359,8 +359,8 @@ void SoundEntityItem::setLocalOnly(bool value) { if (_injector) { DependencyManager::get()->stop(_injector); + _injector = nullptr; } - _injector = nullptr; } if (_sound) { @@ -409,7 +409,7 @@ bool SoundEntityItem::restartSound(bool lock) { options.ambisonic = _sound->isAmbisonic(); if (_injector) { - DependencyManager::get()->setOptionsAndRestart(_injector, options); + DependencyManager::get()->setOptions(_injector, options); } else { _injector = DependencyManager::get()->playSound(_sound, options); } @@ -431,8 +431,8 @@ void SoundEntityItem::updateSound(bool restart) { if (restart) { if (_injector) { DependencyManager::get()->stop(_injector); + _injector = nullptr; } - _injector = nullptr; } if (_playing) { @@ -440,6 +440,7 @@ void SoundEntityItem::updateSound(bool restart) { } else { if (_injector) { DependencyManager::get()->stop(_injector); + _injector = nullptr; } } } From 2c148ca97d72734bd771bb50c5396b0b059b8a78 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 26 Jul 2024 18:23:23 -0700 Subject: [PATCH 051/109] change AO default to HBAO, remove from create --- .../entities/src/AmbientOcclusionPropertyGroup.h | 4 +++- .../entityProperties/html/js/entityProperties.js | 14 +++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/libraries/entities/src/AmbientOcclusionPropertyGroup.h b/libraries/entities/src/AmbientOcclusionPropertyGroup.h index c1a734c9a4..3f348c4d39 100644 --- a/libraries/entities/src/AmbientOcclusionPropertyGroup.h +++ b/libraries/entities/src/AmbientOcclusionPropertyGroup.h @@ -91,7 +91,9 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_OCCLUSION_TECHNIQUE, Technique, technique, AmbientOcclusionTechnique, AmbientOcclusionTechnique::SSAO); + // FIXME: On some machines, SSAO seems to be causing performance problems. Let's default to HBAO for now and maybe + // revisit when we have Vulkan + DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_OCCLUSION_TECHNIQUE, Technique, technique, AmbientOcclusionTechnique, AmbientOcclusionTechnique::HBAO); DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, Jitter, jitter, bool, false); DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, ResolutionLevel, resolutionLevel, uint8_t, 2); DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, EdgeSharpness, edgeSharpness, float, 1.0f); diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index cd129a9eca..4cc870d016 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -657,13 +657,13 @@ const GROUPS = [ options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, propertyID: "ambientOcclusionMode", }, - { - label: "Technique", - type: "dropdown", - options: { ssao: "SSAO", hbao: "HBAO" }, - propertyID: "ambientOcclusion.technique", - showPropertyRule: { "ambientOcclusionMode": "enabled" }, - }, + //{ + // label: "Technique", + // type: "dropdown", + // options: { ssao: "SSAO", hbao: "HBAO" }, + // propertyID: "ambientOcclusion.technique", + // showPropertyRule: { "ambientOcclusionMode": "enabled" }, + //}, { label: "Jitter", type: "bool", From d4ca3d5dc15fda6c50bcd75baeecdb299e02b06e Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 28 Jun 2024 14:50:25 -0700 Subject: [PATCH 052/109] more graphics options --- .../dialogs/graphics/GraphicsSettings.qml | 111 ++++++++++++------ interface/src/Menu.cpp | 6 +- interface/src/PerformanceManager.cpp | 27 +++-- .../scripting/RenderScriptingInterface.cpp | 110 ++++++++++++++--- .../src/scripting/RenderScriptingInterface.h | 59 +++++++++- libraries/render-utils/src/LightingModel.h | 2 + 6 files changed, 254 insertions(+), 61 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml b/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml index 5bac374fb5..69b273ab7d 100644 --- a/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml +++ b/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml @@ -212,8 +212,8 @@ Flickable { ColumnLayout { anchors.left: renderingEffectsHeader.right anchors.leftMargin: 20 - Layout.preferredWidth: parent.width - spacing: 0 + Layout.preferredWidth: parent.width + spacing: 0 enabled: performanceCustom.checked HifiControlsUit.RadioButton { @@ -267,6 +267,45 @@ Flickable { Render.shadowsEnabled = renderingEffectShadows.checked; } } + HifiControlsUit.CheckBox { + id: renderingEffectFog + checked: Render.hazeEnabled + boxSize: 16 + text: "Fog" + spacing: -1 + colorScheme: hifi.colorSchemes.dark + anchors.left: parent.left + anchors.top: renderingEffectShadows.bottom + onCheckedChanged: { + Render.hazeEnabled = renderingEffectFog.checked; + } + } + HifiControlsUit.CheckBox { + id: renderingEffectBloom + checked: Render.bloomEnabled + boxSize: 16 + text: "Bloom" + spacing: -1 + colorScheme: hifi.colorSchemes.dark + anchors.left: parent.left + anchors.top: renderingEffectFog.bottom + onCheckedChanged: { + Render.bloomEnabled = renderingEffectBloom.checked; + } + } + HifiControlsUit.CheckBox { + id: renderingEffectAO + checked: Render.ambientOcclusionEnabled + boxSize: 16 + text: "AO" + spacing: -1 + colorScheme: hifi.colorSchemes.dark + anchors.left: parent.left + anchors.top: renderingEffectBloom.bottom + onCheckedChanged: { + Render.ambientOcclusionEnabled = renderingEffectAO.checked; + } + } HifiControlsUit.CheckBox { id: renderingEffectLocalLights enabled: false @@ -277,41 +316,11 @@ Flickable { spacing: -1 colorScheme: hifi.colorSchemes.dark anchors.left: parent.left - anchors.top: renderingEffectShadows.bottom + anchors.top: renderingEffectAO.bottom //onCheckedChanged: { // Render.localLightsEnabled = renderingEffectLocalLightsEnabled.checked; //} } - HifiControlsUit.CheckBox { - id: renderingEffectFog - enabled: false - //checked: Render.fogEnabled - checked: renderingEffectsEnabled.checked - boxSize: 16 - text: "Fog" - spacing: -1 - colorScheme: hifi.colorSchemes.dark - anchors.left: parent.left - anchors.top: renderingEffectLocalLights.bottom - //onCheckedChanged: { - // Render.fogEnabled = renderingEffectFogEnabled.checked; - //} - } - HifiControlsUit.CheckBox { - id: renderingEffectBloom - enabled: false - //checked: Render.bloomEnabled - checked: renderingEffectsEnabled.checked - boxSize: 16 - text: "Bloom" - spacing: -1 - colorScheme: hifi.colorSchemes.dark - anchors.left: parent.left - anchors.top: renderingEffectFog.bottom - //onCheckedChanged: { - // Render.bloomEnabled = renderingEffectBloomEnabled.checked; - //} - } } } } @@ -811,6 +820,42 @@ Flickable { } } + ColumnLayout { + Layout.topMargin: 20 + Layout.preferredWidth: parent.width + spacing: 0 + + Item { + Layout.preferredWidth: parent.width + Layout.preferredHeight: 35 + + HifiStylesUit.RalewayRegular { + id: proceduralMaterialsHeader + text: "Procedural Materials" + anchors.left: parent.left + anchors.top: parent.top + width: 130 + height: parent.height + size: 16 + color: "#FFFFFF" + } + + HifiControlsUit.CheckBox { + id: renderingEffectProceduralMaterials + checked: Render.proceduralMaterialsEnabled + boxSize: 16 + spacing: -1 + colorScheme: hifi.colorSchemes.dark + anchors.left: proceduralMaterialsHeader.right + anchors.leftMargin: 20 + anchors.top: parent.top + onCheckedChanged: { + Render.proceduralMaterialsEnabled = renderingEffectProceduralMaterials.checked; + } + } + } + } + } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 7017f2a083..2f2c31e27d 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -538,10 +538,8 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ComputeBlendshapes, 0, true, DependencyManager::get().data(), SLOT(setComputeBlendshapes(bool))); - action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::MaterialProceduralShaders, 0, false); - connect(action, &QAction::triggered, [action] { - ModelMeshPartPayload::enableMaterialProceduralShaders = action->isChecked(); - }); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::MaterialProceduralShaders, 0, RenderScriptingInterface::getInstance()->getProceduralMaterialsEnabled(), + RenderScriptingInterface::getInstance(), SLOT(setProceduralMaterialsEnabled(bool))); { auto drawStatusConfig = qApp->getRenderEngine()->getConfiguration()->getConfig("RenderMainView.DrawStatus"); diff --git a/interface/src/PerformanceManager.cpp b/interface/src/PerformanceManager.cpp index fc67de219b..2307edc656 100644 --- a/interface/src/PerformanceManager.cpp +++ b/interface/src/PerformanceManager.cpp @@ -95,11 +95,13 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP RenderScriptingInterface::RenderMethod::DEFERRED : RenderScriptingInterface::RenderMethod::FORWARD ) ); - RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommendedPpiScale); - RenderScriptingInterface::getInstance()->setShadowsEnabled(true); - qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME); + RenderScriptingInterface::getInstance()->setHazeEnabled(true); + RenderScriptingInterface::getInstance()->setBloomEnabled(true); + RenderScriptingInterface::getInstance()->setAmbientOcclusionEnabled(true); + RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommendedPpiScale); + qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME); DependencyManager::get()->setWorldDetailQuality(WORLD_DETAIL_MEDIUM); break; @@ -108,30 +110,39 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP RenderScriptingInterface::RenderMethod::DEFERRED : RenderScriptingInterface::RenderMethod::FORWARD)); + RenderScriptingInterface::getInstance()->setShadowsEnabled(false); + RenderScriptingInterface::getInstance()->setHazeEnabled(true); + RenderScriptingInterface::getInstance()->setBloomEnabled(true); + RenderScriptingInterface::getInstance()->setAmbientOcclusionEnabled(false); RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommendedPpiScale); - RenderScriptingInterface::getInstance()->setShadowsEnabled(false); qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME); DependencyManager::get()->setWorldDetailQuality(WORLD_DETAIL_MEDIUM); break; case PerformancePreset::LOW: RenderScriptingInterface::getInstance()->setRenderMethod(RenderScriptingInterface::RenderMethod::FORWARD); - RenderScriptingInterface::getInstance()->setShadowsEnabled(false); - qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME); + RenderScriptingInterface::getInstance()->setShadowsEnabled(false); + RenderScriptingInterface::getInstance()->setHazeEnabled(true); + RenderScriptingInterface::getInstance()->setBloomEnabled(false); + RenderScriptingInterface::getInstance()->setAmbientOcclusionEnabled(false); RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommendedPpiScale); + qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME); DependencyManager::get()->setWorldDetailQuality(WORLD_DETAIL_LOW); break; case PerformancePreset::LOW_POWER: RenderScriptingInterface::getInstance()->setRenderMethod(RenderScriptingInterface::RenderMethod::FORWARD); - RenderScriptingInterface::getInstance()->setShadowsEnabled(false); - qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::ECO); + RenderScriptingInterface::getInstance()->setShadowsEnabled(false); + RenderScriptingInterface::getInstance()->setHazeEnabled(false); + RenderScriptingInterface::getInstance()->setBloomEnabled(false); + RenderScriptingInterface::getInstance()->setAmbientOcclusionEnabled(false); RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommendedPpiScale); + qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::ECO); DependencyManager::get()->setWorldDetailQuality(WORLD_DETAIL_LOW); break; diff --git a/interface/src/scripting/RenderScriptingInterface.cpp b/interface/src/scripting/RenderScriptingInterface.cpp index 47f772b4bc..4c188dd60c 100644 --- a/interface/src/scripting/RenderScriptingInterface.cpp +++ b/interface/src/scripting/RenderScriptingInterface.cpp @@ -16,6 +16,8 @@ #include #include "ScreenName.h" +#include + STATIC_SCRIPT_TYPES_INITIALIZER((+[](ScriptManager* manager){ auto scriptEngine = manager->engine().get(); @@ -43,16 +45,17 @@ RenderScriptingInterface::RenderScriptingInterface() { }); } - void RenderScriptingInterface::loadSettings() { _renderSettingLock.withReadLock([&] { - _renderMethod = (_renderMethodSetting.get()); - _shadowsEnabled = (_shadowsEnabledSetting.get()); - _ambientOcclusionEnabled = (_ambientOcclusionEnabledSetting.get()); - //_antialiasingMode = (_antialiasingModeSetting.get()); + _renderMethod = _renderMethodSetting.get(); + _shadowsEnabled = _shadowsEnabledSetting.get(); + _hazeEnabled = _hazeEnabledSetting.get(); + _bloomEnabled = _bloomEnabledSetting.get(); + _ambientOcclusionEnabled = _ambientOcclusionEnabledSetting.get(); + _proceduralMaterialsEnabled = _proceduralMaterialsEnabledSetting.get(); _antialiasingMode = static_cast(_antialiasingModeSetting.get()); - _viewportResolutionScale = (_viewportResolutionScaleSetting.get()); - _fullScreenScreen = (_fullScreenScreenSetting.get()); + _viewportResolutionScale = _viewportResolutionScaleSetting.get(); + _fullScreenScreen = _fullScreenScreenSetting.get(); }); // If full screen screen is not initialized, or set to an invalid value, @@ -65,7 +68,10 @@ void RenderScriptingInterface::loadSettings() { forceRenderMethod((RenderMethod)_renderMethod); forceShadowsEnabled(_shadowsEnabled); + forceHazeEnabled(_hazeEnabled); + forceBloomEnabled(_bloomEnabled); forceAmbientOcclusionEnabled(_ambientOcclusionEnabled); + forceProceduralMaterialsEnabled(_proceduralMaterialsEnabled); forceAntialiasingMode(_antialiasingMode); forceViewportResolutionScale(_viewportResolutionScale); } @@ -136,19 +142,76 @@ void RenderScriptingInterface::forceShadowsEnabled(bool enabled) { auto renderConfig = qApp->getRenderEngine()->getConfiguration(); assert(renderConfig); + Menu::getInstance()->setIsOptionChecked(MenuOption::Shadows, enabled); auto lightingModelConfig = renderConfig->getConfig("RenderMainView.LightingModel"); if (lightingModelConfig) { - Menu::getInstance()->setIsOptionChecked(MenuOption::Shadows, enabled); lightingModelConfig->setShadow(enabled); } auto secondaryLightingModelConfig = renderConfig->getConfig("RenderSecondView.LightingModel"); if (secondaryLightingModelConfig) { - Menu::getInstance()->setIsOptionChecked(MenuOption::Shadows, enabled); secondaryLightingModelConfig->setShadow(enabled); } }); } +bool RenderScriptingInterface::getHazeEnabled() const { + return _hazeEnabled; +} + +void RenderScriptingInterface::setHazeEnabled(bool enabled) { + if (_hazeEnabled != enabled) { + forceHazeEnabled(enabled); + emit settingsChanged(); + } +} + +void RenderScriptingInterface::forceHazeEnabled(bool enabled) { + _renderSettingLock.withWriteLock([&] { + _hazeEnabled = (enabled); + _hazeEnabledSetting.set(enabled); + + auto renderConfig = qApp->getRenderEngine()->getConfiguration(); + assert(renderConfig); + auto lightingModelConfig = renderConfig->getConfig("RenderMainView.LightingModel"); + if (lightingModelConfig) { + lightingModelConfig->setHaze(enabled); + } + auto secondaryLightingModelConfig = renderConfig->getConfig("RenderSecondView.LightingModel"); + if (secondaryLightingModelConfig) { + secondaryLightingModelConfig->setHaze(enabled); + } + }); +} + +bool RenderScriptingInterface::getBloomEnabled() const { + return _bloomEnabled; +} + +void RenderScriptingInterface::setBloomEnabled(bool enabled) { + if (_bloomEnabled != enabled) { + forceBloomEnabled(enabled); + emit settingsChanged(); + } +} + +void RenderScriptingInterface::forceBloomEnabled(bool enabled) { + _renderSettingLock.withWriteLock([&] { + _bloomEnabled = (enabled); + _bloomEnabledSetting.set(enabled); + + auto renderConfig = qApp->getRenderEngine()->getConfiguration(); + assert(renderConfig); + auto lightingModelConfig = renderConfig->getConfig("RenderMainView.LightingModel"); + if (lightingModelConfig) { + lightingModelConfig->setBloom(enabled); + } + auto secondaryLightingModelConfig = renderConfig->getConfig("RenderSecondView.LightingModel"); + if (secondaryLightingModelConfig) { + secondaryLightingModelConfig->setBloom(enabled); + } + }); +} + bool RenderScriptingInterface::getAmbientOcclusionEnabled() const { return _ambientOcclusionEnabled; } @@ -165,11 +228,29 @@ void RenderScriptingInterface::forceAmbientOcclusionEnabled(bool enabled) { _ambientOcclusionEnabled = (enabled); _ambientOcclusionEnabledSetting.set(enabled); - auto lightingModelConfig = qApp->getRenderEngine()->getConfiguration()->getConfig("RenderMainView.LightingModel"); - if (lightingModelConfig) { - Menu::getInstance()->setIsOptionChecked(MenuOption::AmbientOcclusion, enabled); - lightingModelConfig->setAmbientOcclusion(enabled); - } + Menu::getInstance()->setIsOptionChecked(MenuOption::AmbientOcclusion, enabled); + ModelMeshPartPayload::enableMaterialProceduralShaders = enabled; + }); +} + +bool RenderScriptingInterface::getProceduralMaterialsEnabled() const { + return _proceduralMaterialsEnabled; +} + +void RenderScriptingInterface::setProceduralMaterialsEnabled(bool enabled) { + if (_proceduralMaterialsEnabled != enabled) { + forceProceduralMaterialsEnabled(enabled); + emit settingsChanged(); + } +} + +void RenderScriptingInterface::forceProceduralMaterialsEnabled(bool enabled) { + _renderSettingLock.withWriteLock([&] { + _proceduralMaterialsEnabled = (enabled); + _proceduralMaterialsEnabledSetting.set(enabled); + + Menu::getInstance()->setIsOptionChecked(MenuOption::MaterialProceduralShaders, enabled); + ModelMeshPartPayload::enableMaterialProceduralShaders = enabled; }); } @@ -233,7 +314,6 @@ void RenderScriptingInterface::forceAntialiasingMode(AntialiasingConfig::Mode mo }); } - float RenderScriptingInterface::getViewportResolutionScale() const { return _viewportResolutionScale; } diff --git a/interface/src/scripting/RenderScriptingInterface.h b/interface/src/scripting/RenderScriptingInterface.h index 73ef077c3c..56b474cf31 100644 --- a/interface/src/scripting/RenderScriptingInterface.h +++ b/interface/src/scripting/RenderScriptingInterface.h @@ -29,8 +29,12 @@ * * @property {Render.RenderMethod} renderMethod - The render method being used. * @property {boolean} shadowsEnabled - true if shadows are enabled, false if they're disabled. + * @property {boolean} hazeEnabled - true if haze (fog) is enabled, false if it's disabled. + * @property {boolean} bloomEnabled - true if bloom is enabled, false if it's disabled. * @property {boolean} ambientOcclusionEnabled - true if ambient occlusion is enabled, false if it's * disabled. + * @property {boolean} proceduralMaterialsEnabled - true if procedural shaders are enabled, false if + * they're disabled. * @property {integer} antialiasingMode - The active anti-aliasing mode. * @property {number} viewportResolutionScale - The view port resolution scale, > 0.0. */ @@ -38,7 +42,10 @@ class RenderScriptingInterface : public QObject { Q_OBJECT Q_PROPERTY(RenderMethod renderMethod READ getRenderMethod WRITE setRenderMethod NOTIFY settingsChanged) Q_PROPERTY(bool shadowsEnabled READ getShadowsEnabled WRITE setShadowsEnabled NOTIFY settingsChanged) + Q_PROPERTY(bool hazeEnabled READ getHazeEnabled WRITE setHazeEnabled NOTIFY settingsChanged) + Q_PROPERTY(bool bloomEnabled READ getBloomEnabled WRITE setBloomEnabled NOTIFY settingsChanged) Q_PROPERTY(bool ambientOcclusionEnabled READ getAmbientOcclusionEnabled WRITE setAmbientOcclusionEnabled NOTIFY settingsChanged) + Q_PROPERTY(bool proceduralMaterialsEnabled READ getProceduralMaterialsEnabled WRITE setProceduralMaterialsEnabled NOTIFY settingsChanged) Q_PROPERTY(AntialiasingConfig::Mode antialiasingMode READ getAntialiasingMode WRITE setAntialiasingMode NOTIFY settingsChanged) Q_PROPERTY(float viewportResolutionScale READ getViewportResolutionScale WRITE setViewportResolutionScale NOTIFY settingsChanged) Q_PROPERTY(float verticalFieldOfView READ getVerticalFieldOfView WRITE setVerticalFieldOfView NOTIFY settingsChanged) @@ -134,6 +141,34 @@ public slots: */ void setShadowsEnabled(bool enabled); + /*@jsdoc + * Gets whether or not haze is enabled. + * @function Render.getHazeEnabled + * @returns {boolean} true if haze is enabled, false if it's disabled. + */ + bool getHazeEnabled() const; + + /*@jsdoc + * Sets whether or not haze is enabled. + * @function Render.setHazeEnabled + * @param {boolean} enabled - true to enable haze, false to disable. + */ + void setHazeEnabled(bool enabled); + + /*@jsdoc + * Gets whether or not bloom is enabled. + * @function Render.getBloomEnabled + * @returns {boolean} true if bloom is enabled, false if it's disabled. + */ + bool getBloomEnabled() const; + + /*@jsdoc + * Sets whether or not bloom is enabled. + * @function Render.setBloomEnabled + * @param {boolean} enabled - true to enable bloom, false to disable. + */ + void setBloomEnabled(bool enabled); + /*@jsdoc * Gets whether or not ambient occlusion is enabled. * @function Render.getAmbientOcclusionEnabled @@ -148,6 +183,20 @@ public slots: */ void setAmbientOcclusionEnabled(bool enabled); + /*@jsdoc + * Gets whether or not procedural materials are enabled. + * @function Render.getProceduralMaterialsEnabled + * @returns {boolean} true if procedural materials are enabled, false if they're disabled. + */ + bool getProceduralMaterialsEnabled() const; + + /*@jsdoc + * Sets whether or not procedural materials are enabled. + * @function Render.setProceduralMaterialsEnabled + * @param {boolean} enabled - true to enable procedural materials, false to disable. + */ + void setProceduralMaterialsEnabled(bool enabled); + /*@jsdoc * Gets the active anti-aliasing mode. * @function Render.getAntialiasingMode @@ -235,7 +284,10 @@ private: // Runtime value of each settings int _renderMethod { RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED }; bool _shadowsEnabled { true }; + bool _hazeEnabled { true }; + bool _bloomEnabled { true }; bool _ambientOcclusionEnabled { true }; + bool _proceduralMaterialsEnabled { true }; AntialiasingConfig::Mode _antialiasingMode { AntialiasingConfig::Mode::NONE }; float _viewportResolutionScale { 1.0f }; QString _fullScreenScreen; @@ -243,8 +295,10 @@ private: // Actual settings saved on disk Setting::Handle _renderMethodSetting { "renderMethod", RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED }; Setting::Handle _shadowsEnabledSetting { "shadowsEnabled", true }; + Setting::Handle _hazeEnabledSetting { "hazeEnabled", true }; + Setting::Handle _bloomEnabledSetting { "bloomEnabled", true }; Setting::Handle _ambientOcclusionEnabledSetting { "ambientOcclusionEnabled", true }; - //Setting::Handle _antialiasingModeSetting { "antialiasingMode", AntialiasingConfig::Mode::TAA }; + Setting::Handle _proceduralMaterialsEnabledSetting { "proceduralMaterialsEnabled", true }; Setting::Handle _antialiasingModeSetting { "antialiasingMode", AntialiasingConfig::Mode::NONE }; Setting::Handle _viewportResolutionScaleSetting { "viewportResolutionScale", 1.0f }; Setting::Handle _fullScreenScreenSetting { "fullScreenScreen", "" }; @@ -252,7 +306,10 @@ private: // Force assign both setting AND runtime value to the parameter value void forceRenderMethod(RenderMethod renderMethod); void forceShadowsEnabled(bool enabled); + void forceHazeEnabled(bool enabled); + void forceBloomEnabled(bool enabled); void forceAmbientOcclusionEnabled(bool enabled); + void forceProceduralMaterialsEnabled(bool enabled); void forceAntialiasingMode(AntialiasingConfig::Mode mode); void forceViewportResolutionScale(float scale); diff --git a/libraries/render-utils/src/LightingModel.h b/libraries/render-utils/src/LightingModel.h index 5298fb2ceb..c5811b1110 100644 --- a/libraries/render-utils/src/LightingModel.h +++ b/libraries/render-utils/src/LightingModel.h @@ -197,6 +197,8 @@ public: bool isAmbientOcclusionEnabled() const { return enableAmbientOcclusion; } void setShadow(bool enable) { enableShadow = enable; emit dirty(); } bool isShadowEnabled() const { return enableShadow; } + void setHaze(bool enable) { enableHaze = enable; emit dirty(); } + void setBloom(bool enable) { enableBloom = enable; emit dirty(); } signals: void dirty(); From 259458394e3c3980a870d0afa425c6f6a08050ad Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 29 Jun 2024 15:26:44 -0700 Subject: [PATCH 053/109] fix AO setting + effects in mirrors --- .../scripting/RenderScriptingInterface.cpp | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/interface/src/scripting/RenderScriptingInterface.cpp b/interface/src/scripting/RenderScriptingInterface.cpp index 4c188dd60c..c9df0374f3 100644 --- a/interface/src/scripting/RenderScriptingInterface.cpp +++ b/interface/src/scripting/RenderScriptingInterface.cpp @@ -124,6 +124,33 @@ QStringList RenderScriptingInterface::getRenderMethodNames() const { return refrenderMethodNames; } +void recursivelyUpdateLightingModel(const QString& parentTaskName, std::function updateLambda, int depth = -1) { + if (depth == -1) { + auto secondaryLightingModelConfig = qApp->getRenderEngine()->getConfiguration()->getConfig("RenderSecondView.LightingModel"); + if (secondaryLightingModelConfig) { + updateLambda(secondaryLightingModelConfig); + } + + auto mainLightingModelConfig = qApp->getRenderEngine()->getConfiguration()->getConfig("RenderMainView.LightingModel"); + if (mainLightingModelConfig) { + updateLambda(mainLightingModelConfig); + } + + recursivelyUpdateLightingModel("RenderMainView", updateLambda, depth + 1); + } else 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); + auto lightingModelConfig = qApp->getRenderEngine()->getConfiguration()->getConfig(mirrorTaskString + ".LightingModel"); + if (lightingModelConfig) { + updateLambda(lightingModelConfig); + recursivelyUpdateLightingModel(QString::fromStdString(mirrorTaskString), updateLambda, depth + 1); + } + } +} + bool RenderScriptingInterface::getShadowsEnabled() const { return _shadowsEnabled; } @@ -140,17 +167,9 @@ void RenderScriptingInterface::forceShadowsEnabled(bool enabled) { _shadowsEnabled = (enabled); _shadowsEnabledSetting.set(enabled); - auto renderConfig = qApp->getRenderEngine()->getConfiguration(); - assert(renderConfig); Menu::getInstance()->setIsOptionChecked(MenuOption::Shadows, enabled); - auto lightingModelConfig = renderConfig->getConfig("RenderMainView.LightingModel"); - if (lightingModelConfig) { - lightingModelConfig->setShadow(enabled); - } - auto secondaryLightingModelConfig = renderConfig->getConfig("RenderSecondView.LightingModel"); - if (secondaryLightingModelConfig) { - secondaryLightingModelConfig->setShadow(enabled); - } + + recursivelyUpdateLightingModel("", [enabled] (MakeLightingModelConfig *config) { config->setShadow(enabled); }); }); } @@ -170,16 +189,7 @@ void RenderScriptingInterface::forceHazeEnabled(bool enabled) { _hazeEnabled = (enabled); _hazeEnabledSetting.set(enabled); - auto renderConfig = qApp->getRenderEngine()->getConfiguration(); - assert(renderConfig); - auto lightingModelConfig = renderConfig->getConfig("RenderMainView.LightingModel"); - if (lightingModelConfig) { - lightingModelConfig->setHaze(enabled); - } - auto secondaryLightingModelConfig = renderConfig->getConfig("RenderSecondView.LightingModel"); - if (secondaryLightingModelConfig) { - secondaryLightingModelConfig->setHaze(enabled); - } + recursivelyUpdateLightingModel("", [enabled] (MakeLightingModelConfig *config) { config->setHaze(enabled); }); }); } @@ -199,16 +209,7 @@ void RenderScriptingInterface::forceBloomEnabled(bool enabled) { _bloomEnabled = (enabled); _bloomEnabledSetting.set(enabled); - auto renderConfig = qApp->getRenderEngine()->getConfiguration(); - assert(renderConfig); - auto lightingModelConfig = renderConfig->getConfig("RenderMainView.LightingModel"); - if (lightingModelConfig) { - lightingModelConfig->setBloom(enabled); - } - auto secondaryLightingModelConfig = renderConfig->getConfig("RenderSecondView.LightingModel"); - if (secondaryLightingModelConfig) { - secondaryLightingModelConfig->setBloom(enabled); - } + recursivelyUpdateLightingModel("", [enabled] (MakeLightingModelConfig *config) { config->setBloom(enabled); }); }); } @@ -229,7 +230,8 @@ void RenderScriptingInterface::forceAmbientOcclusionEnabled(bool enabled) { _ambientOcclusionEnabledSetting.set(enabled); Menu::getInstance()->setIsOptionChecked(MenuOption::AmbientOcclusion, enabled); - ModelMeshPartPayload::enableMaterialProceduralShaders = enabled; + + recursivelyUpdateLightingModel("", [enabled] (MakeLightingModelConfig *config) { config->setAmbientOcclusion(enabled); }); }); } From 3e617d003a02cc89b66afca1e2d6f0d1fd2f50bc Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Tue, 30 Jul 2024 16:02:24 -0700 Subject: [PATCH 054/109] fix AA in mirrors --- .../scripting/RenderScriptingInterface.cpp | 70 ++++++++++++------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/interface/src/scripting/RenderScriptingInterface.cpp b/interface/src/scripting/RenderScriptingInterface.cpp index c9df0374f3..e11bf32702 100644 --- a/interface/src/scripting/RenderScriptingInterface.cpp +++ b/interface/src/scripting/RenderScriptingInterface.cpp @@ -292,43 +292,51 @@ void setAntialiasingModeForView(AntialiasingConfig::Mode mode, JitterSampleConfi } } -void RenderScriptingInterface::forceAntialiasingMode(AntialiasingConfig::Mode mode) { - _renderSettingLock.withWriteLock([&] { - _antialiasingMode = mode; +void recursivelyUpdateAntialiasingMode(const QString& parentTaskName, AntialiasingConfig::Mode mode, int depth = -1) { + if (depth == -1) { + auto secondViewJitterCamConfig = qApp->getRenderEngine()->getConfiguration()->getConfig("RenderSecondView.JitterCam"); + auto secondViewAntialiasingConfig = qApp->getRenderEngine()->getConfiguration()->getConfig("RenderSecondView.Antialiasing"); + if (secondViewJitterCamConfig && secondViewAntialiasingConfig) { + setAntialiasingModeForView(mode, secondViewJitterCamConfig, secondViewAntialiasingConfig); + } auto mainViewJitterCamConfig = qApp->getRenderEngine()->getConfiguration()->getConfig("RenderMainView.JitterCam"); auto mainViewAntialiasingConfig = qApp->getRenderEngine()->getConfiguration()->getConfig("RenderMainView.Antialiasing"); - auto secondViewJitterCamConfig = qApp->getRenderEngine()->getConfiguration()->getConfig("RenderSecondView.JitterCam"); - auto secondViewAntialiasingConfig = qApp->getRenderEngine()->getConfiguration()->getConfig("RenderSecondView.Antialiasing"); - if (mode != AntialiasingConfig::Mode::NONE - && mode != AntialiasingConfig::Mode::TAA - && mode != AntialiasingConfig::Mode::FXAA) { - _antialiasingMode = AntialiasingConfig::Mode::NONE; - } if (mainViewJitterCamConfig && mainViewAntialiasingConfig) { setAntialiasingModeForView( mode, mainViewJitterCamConfig, mainViewAntialiasingConfig); } - if (secondViewJitterCamConfig && secondViewAntialiasingConfig) { - setAntialiasingModeForView( mode, secondViewJitterCamConfig, secondViewAntialiasingConfig); + + recursivelyUpdateAntialiasingMode("RenderMainView", mode, depth + 1); + } else 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); + auto jitterCamConfig = qApp->getRenderEngine()->getConfiguration()->getConfig(mirrorTaskString + ".JitterCam"); + auto antialiasingConfig = qApp->getRenderEngine()->getConfiguration()->getConfig(mirrorTaskString + ".Antialiasing"); + if (jitterCamConfig && antialiasingConfig) { + setAntialiasingModeForView(mode, jitterCamConfig, antialiasingConfig); + recursivelyUpdateAntialiasingMode(QString::fromStdString(mirrorTaskString), mode, depth + 1); } - - _antialiasingModeSetting.set(_antialiasingMode); - }); -} - -float RenderScriptingInterface::getViewportResolutionScale() const { - return _viewportResolutionScale; -} - -void RenderScriptingInterface::setViewportResolutionScale(float scale) { - if (_viewportResolutionScale != scale) { - forceViewportResolutionScale(scale); - emit settingsChanged(); } } +void RenderScriptingInterface::forceAntialiasingMode(AntialiasingConfig::Mode mode) { + if ((int)mode < 0 || mode >= AntialiasingConfig::Mode::MODE_COUNT) { + mode = AntialiasingConfig::Mode::NONE; + } + + _renderSettingLock.withWriteLock([&] { + _antialiasingMode = mode; + _antialiasingModeSetting.set(_antialiasingMode); + + recursivelyUpdateAntialiasingMode("", _antialiasingMode); + }); +} + void RenderScriptingInterface::setVerticalFieldOfView(float fieldOfView) { - if (getViewportResolutionScale() != fieldOfView) { + if (qApp->getFieldOfView() != fieldOfView) { qApp->setFieldOfView(fieldOfView); emit settingsChanged(); } @@ -368,6 +376,16 @@ QString RenderScriptingInterface::getFullScreenScreen() const { return _fullScreenScreen; } +float RenderScriptingInterface::getViewportResolutionScale() const { + return _viewportResolutionScale; +} + +void RenderScriptingInterface::setViewportResolutionScale(float scale) { + if (_viewportResolutionScale != scale) { + forceViewportResolutionScale(scale); + emit settingsChanged(); + } +} void RenderScriptingInterface::forceViewportResolutionScale(float scale) { // just not negative values or zero From 24ed76a5dbb3ba55cc3f4292e7f85b4b0f5f4ac1 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Wed, 14 Aug 2024 15:42:01 -0700 Subject: [PATCH 055/109] alezia's fixes --- .../qml/hifi/dialogs/graphics/GraphicsSettings.qml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml b/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml index 69b273ab7d..a928b1379f 100644 --- a/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml +++ b/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml @@ -4,6 +4,7 @@ // // Created by Zach Fox on 2019-07-10 // Copyright 2019 High Fidelity, Inc. +// Copyright 2024 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 @@ -268,16 +269,16 @@ Flickable { } } HifiControlsUit.CheckBox { - id: renderingEffectFog + id: renderingEffectHaze checked: Render.hazeEnabled boxSize: 16 - text: "Fog" + text: "Haze" spacing: -1 colorScheme: hifi.colorSchemes.dark anchors.left: parent.left anchors.top: renderingEffectShadows.bottom onCheckedChanged: { - Render.hazeEnabled = renderingEffectFog.checked; + Render.hazeEnabled = renderingEffectHaze.checked; } } HifiControlsUit.CheckBox { @@ -288,7 +289,7 @@ Flickable { spacing: -1 colorScheme: hifi.colorSchemes.dark anchors.left: parent.left - anchors.top: renderingEffectFog.bottom + anchors.top: renderingEffectHaze.bottom onCheckedChanged: { Render.bloomEnabled = renderingEffectBloom.checked; } @@ -834,7 +835,7 @@ Flickable { text: "Procedural Materials" anchors.left: parent.left anchors.top: parent.top - width: 130 + width: 150 height: parent.height size: 16 color: "#FFFFFF" From 05661c600724fe71c4a804d00013959ab9e1be47 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Wed, 14 Aug 2024 18:20:06 -0700 Subject: [PATCH 056/109] fix haze in mirrors --- libraries/render-utils/src/DrawHaze.cpp | 42 +++++++++++++++---- libraries/render-utils/src/DrawHaze.h | 8 +++- libraries/render-utils/src/Haze.slf | 12 +++++- .../render-utils/src/RenderDeferredTask.cpp | 2 +- .../render-utils/src/render-utils/haze.slp | 1 + 5 files changed, 54 insertions(+), 11 deletions(-) diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index 012161ec03..71845be731 100644 --- a/libraries/render-utils/src/DrawHaze.cpp +++ b/libraries/render-utils/src/DrawHaze.cpp @@ -31,6 +31,10 @@ namespace gr { using graphics::slot::buffer::Buffer; } +gpu::PipelinePointer DrawHaze::_hazePipeline = nullptr; +gpu::PipelinePointer DrawHaze::_separateHazePipeline = nullptr; +gpu::PipelinePointer DrawHaze::_separateHazeBackgroundPipeline = nullptr; + void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { const auto hazeFrame = inputs.get0(); const auto& hazeStage = renderContext->args->_scene->getStage(); @@ -55,15 +59,29 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu if (!_hazePipeline) { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::haze); - gpu::StatePointer state = std::make_shared(); - state->setBlendFunction(true, - 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); + auto createState = []() { + gpu::StatePointer state = std::make_shared(); + state->setBlendFunction(true, + 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); + return state; + }; // Mask out haze on the tablet - PrepareStencil::testMask(*state); - _hazePipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + auto hazeState = createState(); + PrepareStencil::testMask(*hazeState); + _hazePipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, hazeState)); + + // For our separated passes, we perform one pass on anything marked shape, and one on just the background + auto hazeSeparatedState = createState(); + PrepareStencil::testShape(*hazeSeparatedState); + _separateHazePipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, hazeSeparatedState)); + + gpu::ShaderPointer backgroundProgram = gpu::Shader::createProgram(shader::render_utils::program::haze_background); + auto hazeBackgroundSeparatedState = createState(); + PrepareStencil::testBackground(*hazeBackgroundSeparatedState); + _separateHazeBackgroundPipeline = gpu::PipelinePointer(gpu::Pipeline::create(backgroundProgram, hazeBackgroundSeparatedState)); } auto outputFramebufferSize = glm::ivec2(outputBuffer->getSize()); @@ -77,7 +95,6 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu batch.resetViewTransform(); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(outputFramebufferSize, args->_viewport)); - batch.setPipeline(_hazePipeline); batch.setUniformBuffer(graphics::slot::buffer::Buffer::HazeParams, haze->getHazeParametersBuffer()); batch.setUniformBuffer(ru::Buffer::DeferredFrameTransform, transformBuffer->getFrameTransformBuffer()); @@ -92,6 +109,15 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu } } - batch.draw(gpu::TRIANGLE_STRIP, 4); + if (!_separateBackgroundPass) { + batch.setPipeline(_hazePipeline); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } else { + batch.setPipeline(_separateHazePipeline); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + batch.setPipeline(_separateHazeBackgroundPipeline); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } }); } diff --git a/libraries/render-utils/src/DrawHaze.h b/libraries/render-utils/src/DrawHaze.h index 9236543068..82e8beaa9b 100644 --- a/libraries/render-utils/src/DrawHaze.h +++ b/libraries/render-utils/src/DrawHaze.h @@ -33,10 +33,16 @@ public: using Inputs = render::VaryingSet6; using JobModel = render::Job::ModelI; + DrawHaze(bool separateBackgroundPass) : _separateBackgroundPass(separateBackgroundPass) {} + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); private: - gpu::PipelinePointer _hazePipeline; + bool _separateBackgroundPass { false }; + + static gpu::PipelinePointer _hazePipeline; + static gpu::PipelinePointer _separateHazePipeline; + static gpu::PipelinePointer _separateHazeBackgroundPipeline; }; #endif // hifi_render_utils_DrawHaze_h diff --git a/libraries/render-utils/src/Haze.slf b/libraries/render-utils/src/Haze.slf index 899f36b8eb..4f230ff642 100644 --- a/libraries/render-utils/src/Haze.slf +++ b/libraries/render-utils/src/Haze.slf @@ -21,10 +21,16 @@ <@include graphics/Haze.slh@> +<@if not HIFI_USE_BACKGROUND@> LAYOUT(binding=RENDER_UTILS_TEXTURE_HAZE_LINEAR_DEPTH) uniform sampler2D linearDepthMap; +<@endif@> vec4 unpackPositionFromZeye(vec2 texcoord) { +<@if not HIFI_USE_BACKGROUND@> float Zeye = -texture(linearDepthMap, texcoord).x; +<@else@> + float Zeye = 1.0; // We just want to get the direction first +<@endif@> float check = float(isStereo()); float check2 = check * float(texcoord.x > 0.5); @@ -44,8 +50,12 @@ void main(void) { } vec4 fragPositionES = unpackPositionFromZeye(varTexCoord0); - mat4 viewInverse = getViewInverse(); + +<@if HIFI_USE_BACKGROUND@> + fragPositionES = vec4(-32000.0 * normalize(fragPositionES.xyz), 1.0); +<@endif@> + vec4 fragPositionWS = viewInverse * fragPositionES; vec4 eyePositionWS = viewInverse[3]; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 4bcfc9663b..b8bc9a542b 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -225,7 +225,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawBackgroundDeferred", backgroundInputs); const auto drawHazeInputs = render::Varying(DrawHaze::Inputs(hazeFrame, lightingFramebuffer, linearDepthTarget, deferredFrameTransform, lightingModel, lightFrame)); - task.addJob("DrawHazeDeferred", drawHazeInputs); + task.addJob("DrawHazeDeferred", drawHazeInputs, depth > 0); // Render transparent objects forward in LightingBuffer const auto transparentsInputs = RenderTransparentDeferred::Inputs(transparents, hazeFrame, lightFrame, lightingModel, lightClusters, shadowFrame, jitter).asVarying(); diff --git a/libraries/render-utils/src/render-utils/haze.slp b/libraries/render-utils/src/render-utils/haze.slp index 805b855c8d..c469834837 100644 --- a/libraries/render-utils/src/render-utils/haze.slp +++ b/libraries/render-utils/src/render-utils/haze.slp @@ -1,2 +1,3 @@ VERTEX gpu::vertex::DrawViewportQuadTransformTexcoord FRAGMENT Haze +DEFINES background:f \ No newline at end of file From b1d991deb2c1d6408e700ba805ec2a32eb21f088 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 16 Aug 2024 13:38:59 -0700 Subject: [PATCH 057/109] add comment for SKYBOX_DISTANCE --- libraries/render-utils/src/Haze.slf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/Haze.slf b/libraries/render-utils/src/Haze.slf index 4f230ff642..9be64e7695 100644 --- a/libraries/render-utils/src/Haze.slf +++ b/libraries/render-utils/src/Haze.slf @@ -53,7 +53,9 @@ void main(void) { mat4 viewInverse = getViewInverse(); <@if HIFI_USE_BACKGROUND@> - fragPositionES = vec4(-32000.0 * normalize(fragPositionES.xyz), 1.0); + // We choose an arbitrary large number > BLEND_DISTANCE in Haze.slh + const float SKYBOX_DISTANCE = 32000.0; + fragPositionES = vec4(-SKYBOX_DISTANCE * normalize(fragPositionES.xyz), 1.0); <@endif@> vec4 fragPositionWS = viewInverse * fragPositionES; From 91c72299ab9a97086ac15b1def2aed30d8b125ea Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 16 Aug 2024 14:46:15 -0700 Subject: [PATCH 058/109] new line --- libraries/render-utils/src/render-utils/haze.slp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/render-utils/haze.slp b/libraries/render-utils/src/render-utils/haze.slp index c469834837..7cfd358e3d 100644 --- a/libraries/render-utils/src/render-utils/haze.slp +++ b/libraries/render-utils/src/render-utils/haze.slp @@ -1,3 +1,3 @@ VERTEX gpu::vertex::DrawViewportQuadTransformTexcoord FRAGMENT Haze -DEFINES background:f \ No newline at end of file +DEFINES background:f From fe61d92a09c5fa4454e33da8073da99e13da7317 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 12 Jul 2024 18:09:17 -0700 Subject: [PATCH 059/109] model loading priority updates over time, takes into account out of bounds, avatar entities have higher priority, and fsts can specify to wait for wearables to load before rendering --- interface/resources/qml/Stats.qml | 6 +- interface/src/Application.cpp | 33 ++++++++--- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/avatar/OtherAvatar.cpp | 2 +- .../AccountServicesScriptingInterface.cpp | 4 +- interface/src/ui/Stats.cpp | 32 +++++++---- interface/src/ui/Stats.h | 39 ++++++++++--- libraries/animation/src/AnimNodeLoader.cpp | 2 +- libraries/audio/src/SoundCache.cpp | 2 +- .../src/avatars-renderer/Avatar.cpp | 25 ++++++++- .../src/avatars-renderer/Avatar.h | 8 ++- .../src/RenderableModelEntityItem.cpp | 2 +- .../src/material-networking/TextureCache.cpp | 6 +- .../src/model-networking/ModelCache.cpp | 4 +- .../src/model-networking/ModelCache.h | 2 + libraries/model-serializers/src/FSTReader.cpp | 2 +- libraries/model-serializers/src/FSTReader.h | 1 + libraries/networking/src/ResourceCache.cpp | 55 ++++++++----------- libraries/networking/src/ResourceCache.h | 18 ++---- libraries/render-utils/src/Model.cpp | 2 +- libraries/render-utils/src/Model.h | 4 +- 21 files changed, 156 insertions(+), 95 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 0b5eba99d0..9688f2a492 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -304,16 +304,16 @@ Item { } ListView { width: geoCol.width - height: root.downloadUrls.length * 15 + height: root.downloadUrls.length * 30 visible: root.expanded && root.downloadUrls.length > 0; model: root.downloadUrls delegate: StatText { visible: root.expanded; - text: modelData.length > 30 + text: (modelData.length > 30 ? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22) - : modelData + : modelData) + "\n\t" + "Priority: " + root.downloadPriorities[index] + ", Progress: " + root.downloadProgresses[index] + "%" } } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 764f2bb54d..16831ee091 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2277,12 +2277,12 @@ void Application::initialize(const QCommandLineParser &parser) { auto loadingRequests = ResourceCache::getLoadingRequests(); QJsonArray loadingRequestsStats; - for (const auto& request : loadingRequests) { + for (const auto& requestPair : loadingRequests) { QJsonObject requestStats; - requestStats["filename"] = request->getURL().fileName(); - requestStats["received"] = request->getBytesReceived(); - requestStats["total"] = request->getBytesTotal(); - requestStats["attempts"] = (int)request->getDownloadAttempts(); + requestStats["filename"] = requestPair.first->getURL().fileName(); + requestStats["received"] = requestPair.first->getBytesReceived(); + requestStats["total"] = requestPair.first->getBytesTotal(); + requestStats["attempts"] = (int)requestPair.first->getDownloadAttempts(); loadingRequestsStats.append(requestStats); } @@ -5731,15 +5731,30 @@ void Application::init() { getEntities()->init(); getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) { - auto dims = item.getScaledDimensions(); - auto maxSize = glm::compMax(dims); + if (item.getEntityHostType() == entity::HostType::AVATAR) { + return item.isMyAvatarEntity() ? Avatar::MYAVATAR_ENTITY_LOADING_PRIORITY : Avatar::OTHERAVATAR_ENTITY_LOADING_PRIORITY; + } + const float maxSize = glm::compMax(item.getScaledDimensions()); if (maxSize <= 0.0f) { return 0.0f; } - auto distance = glm::distance(getMyAvatar()->getWorldPosition(), item.getWorldPosition()); - return atan2(maxSize, distance); + const glm::vec3 itemPosition = item.getWorldPosition(); + const float distance = glm::distance(getMyAvatar()->getWorldPosition(), itemPosition); + float result = atan2(maxSize, distance); + + bool isInView = true; + { + QMutexLocker viewLocker(&_viewMutex); + isInView = _viewFrustum.sphereIntersectsKeyhole(itemPosition, maxSize); + } + if (!isInView) { + const float OUT_OF_VIEW_PENALTY = -M_PI_2; + result += OUT_OF_VIEW_PENALTY; + } + + return result; }); ObjectMotionState::setShapeManager(&_shapeManager); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index fc6fae5456..0dcab1e45b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -259,7 +259,7 @@ MyAvatar::MyAvatar(QThread* thread) : _headData = new MyHead(this); _skeletonModel = std::make_shared(this, nullptr); - _skeletonModel->setLoadingPriority(MYAVATAR_LOADING_PRIORITY); + _skeletonModel->setLoadingPriorityOperator([]() { return MYAVATAR_LOADING_PRIORITY; }); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); connect(_skeletonModel.get(), &Model::setURLFinished, this, [this](bool success) { if (success) { diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index aacb6f56a9..2433bdde91 100644 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -44,7 +44,7 @@ OtherAvatar::OtherAvatar(QThread* thread) : Avatar(thread) { // give the pointer to our head to inherited _headData variable from AvatarData _headData = new Head(this); _skeletonModel = std::make_shared(this, nullptr); - _skeletonModel->setLoadingPriority(OTHERAVATAR_LOADING_PRIORITY); + _skeletonModel->setLoadingPriorityOperator([]() { return OTHERAVATAR_LOADING_PRIORITY; }); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady); connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset); diff --git a/interface/src/scripting/AccountServicesScriptingInterface.cpp b/interface/src/scripting/AccountServicesScriptingInterface.cpp index 35e9f3b36d..e77fb13b89 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.cpp +++ b/interface/src/scripting/AccountServicesScriptingInterface.cpp @@ -159,8 +159,8 @@ bool DownloadInfoResultFromScriptValue(const ScriptValue& object, DownloadInfoRe DownloadInfoResult AccountServicesScriptingInterface::getDownloadInfo() { DownloadInfoResult result; - foreach(const auto& resource, ResourceCache::getLoadingRequests()) { - result.downloading.append(resource->getProgress() * 100.0f); + foreach(const auto& resourcePair, ResourceCache::getLoadingRequests()) { + result.downloading.append(resourcePair.first->getProgress() * 100.0f); } result.pending = ResourceCache::getPendingRequestCount(); return result; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 0e3a329375..79e4d0e28c 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -289,8 +289,8 @@ void Stats::updateStats(bool force) { STAT_UPDATE(entityPacketsInKbps, octreeServerCount ? totalEntityKbps / octreeServerCount : -1); - auto loadingRequests = ResourceCache::getLoadingRequests(); - STAT_UPDATE(downloads, loadingRequests.size()); + auto loadingRequestPairs = ResourceCache::getLoadingRequests(); + STAT_UPDATE(downloads, loadingRequestPairs.size()); STAT_UPDATE(downloadLimit, (int)ResourceCache::getRequestLimit()) STAT_UPDATE(downloadsPending, (int)ResourceCache::getPendingRequestCount()); STAT_UPDATE(processing, DependencyManager::get()->getStat("Processing").toInt()); @@ -298,29 +298,37 @@ void Stats::updateStats(bool force) { // See if the active download urls have changed bool shouldUpdateUrls = _downloads != _downloadUrls.size(); + bool shouldUpdateProgresses = false; if (!shouldUpdateUrls) { for (int i = 0; i < _downloads; i++) { - if (loadingRequests[i]->getURL().toString() != _downloadUrls[i]) { + if (loadingRequestPairs[i].first->getURL().toString() != _downloadUrls[i]) { shouldUpdateUrls = true; break; + } else if (loadingRequestPairs[i].first->getProgress() != _downloadProgresses[i]) { + shouldUpdateProgresses = true; } } } // If the urls have changed, update the list if (shouldUpdateUrls) { _downloadUrls.clear(); - foreach (const auto& resource, loadingRequests) { - _downloadUrls << resource->getURL().toString(); + _downloadPriorities.clear(); + foreach (const auto& resourcePair, loadingRequestPairs) { + _downloadUrls << resourcePair.first->getURL().toString(); + _downloadPriorities << resourcePair.second; } emit downloadUrlsChanged(); + emit downloadPrioritiesChanged(); + shouldUpdateProgresses = true; + } + + if (shouldUpdateProgresses) { + _downloadProgresses.clear(); + foreach (const auto& resourcePair, loadingRequestPairs) { + _downloadProgresses << (int)(100.0f * resourcePair.first->getProgress()); + } + emit downloadProgressesChanged(); } - // TODO fix to match original behavior - //stringstream downloads; - //downloads << "Downloads: "; - //foreach(Resource* resource, ) { - // downloads << (int)(resource->getProgress() * 100.0f) << "% "; - //} - //downloads << "(" << << " pending)"; } // Fourth column, octree stats diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index a3366904bd..b6d5e5ac9c 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -211,7 +211,10 @@ private: \ * Read-only. * @property {string[]} downloadUrls - The download URLs. * Read-only. - *

    Note: Property not available in the API.

    + * @property {number[]} downloadProgresses - The download progresses. + * Read-only. + * @property {number[]} downloadPriorities - The download priorities. + * Read-only. * @property {number} processing - The number of completed downloads being processed. * Read-only. * @property {number} processingPending - The number of completed downloads waiting to be processed. @@ -529,6 +532,8 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, downloadLimit, 0) STATS_PROPERTY(int, downloadsPending, 0) Q_PROPERTY(QStringList downloadUrls READ downloadUrls NOTIFY downloadUrlsChanged) + Q_PROPERTY(QList downloadProgresses READ downloadProgresses NOTIFY downloadProgressesChanged) + Q_PROPERTY(QList downloadPriorities READ downloadPriorities NOTIFY downloadPrioritiesChanged) STATS_PROPERTY(int, processing, 0) STATS_PROPERTY(int, processingPending, 0) STATS_PROPERTY(int, triangles, 0) @@ -622,7 +627,9 @@ public: } } - QStringList downloadUrls () { return _downloadUrls; } + QStringList downloadUrls() { return _downloadUrls; } + QList downloadProgresses() { return _downloadProgresses; } + QList downloadPriorities() { return _downloadPriorities; } public slots: @@ -1091,6 +1098,20 @@ signals: */ void downloadUrlsChanged(); + /*@jsdoc + * Triggered when the value of the downloadProgresses property changes. + * @function Stats.downloadProgressesChanged + * @returns {Signal} + */ + void downloadProgressesChanged(); + + /*@jsdoc + * Triggered when the value of the downloadPriorities property changes. + * @function Stats.downloadPrioritiesChanged + * @returns {Signal} + */ + void downloadPrioritiesChanged(); + /*@jsdoc * Triggered when the value of the processing property changes. * @function Stats.processingChanged @@ -1809,14 +1830,16 @@ signals: */ private: - int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process - bool _resetRecentMaxPacketsSoon{ true }; - bool _expanded{ false }; - bool _showTimingDetails{ false }; - bool _showGameUpdateStats{ false }; + int _recentMaxPackets { 0 } ; // recent max incoming voxel packets to process + bool _resetRecentMaxPacketsSoon { true }; + bool _expanded { false }; + bool _showTimingDetails { false }; + bool _showGameUpdateStats { false }; QString _monospaceFont; const AudioIOStats* _audioStats; - QStringList _downloadUrls = QStringList(); + QStringList _downloadUrls { QStringList() }; + QList _downloadProgresses { QList() }; + QList _downloadPriorities { QList() }; }; #endif // hifi_Stats_h diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 9474c0309f..066ccec056 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -1085,7 +1085,7 @@ AnimNodeLoader::AnimNodeLoader(const QUrl& url) : { _resource = QSharedPointer::create(url); _resource->setSelf(_resource); - _resource->setLoadPriority(this, ANIM_GRAPH_LOAD_PRIORITY); + _resource->setLoadPriorityOperator(this, []() { return ANIM_GRAPH_LOAD_PRIORITY; }); connect(_resource.data(), &Resource::loaded, this, &AnimNodeLoader::onRequestDone); connect(_resource.data(), &Resource::failed, this, &AnimNodeLoader::onRequestError); _resource->ensureLoading(); diff --git a/libraries/audio/src/SoundCache.cpp b/libraries/audio/src/SoundCache.cpp index 2b02a566ac..55a32e7237 100644 --- a/libraries/audio/src/SoundCache.cpp +++ b/libraries/audio/src/SoundCache.cpp @@ -35,7 +35,7 @@ SharedSoundPointer SoundCache::getSound(const QUrl& url) { QSharedPointer SoundCache::createResource(const QUrl& url) { auto resource = QSharedPointer(new Sound(url), &Resource::deleter); - resource->setLoadPriority(this, SOUNDS_LOADING_PRIORITY); + resource->setLoadPriorityOperator(this, []() { return SOUNDS_LOADING_PRIORITY; }); return resource; } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 25473bda90..be63127995 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -51,6 +51,8 @@ const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); const float Avatar::MYAVATAR_LOADING_PRIORITY = (float)M_PI; // Entity priority is computed as atan2(maxDim, distance) which is <= PI / 2 const float Avatar::OTHERAVATAR_LOADING_PRIORITY = MYAVATAR_LOADING_PRIORITY - EPSILON; +const float Avatar::MYAVATAR_ENTITY_LOADING_PRIORITY = MYAVATAR_LOADING_PRIORITY - EPSILON; +const float Avatar::OTHERAVATAR_ENTITY_LOADING_PRIORITY = OTHERAVATAR_LOADING_PRIORITY - EPSILON; namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { @@ -841,8 +843,23 @@ bool Avatar::getEnableMeshVisible() const { } void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { - bool canTryFade{ false }; + if (_needsWearablesLoadedCheck) { + bool wearablesAreLoaded = true; + // Technically, we should be checking for descendant avatar entities that are owned by this avatar. + // But it's sufficient to just check all children entities here. + forEachChild([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Entity) { + auto entity = std::dynamic_pointer_cast(child); + if (entity && !entity->isVisuallyReady()) { + wearablesAreLoaded = false; + } + } + }); + setEnableMeshVisible(_isMeshVisible || wearablesAreLoaded); + _needsWearablesLoadedCheck = !wearablesAreLoaded; + } + bool canTryFade = false; // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene render::Transaction transaction; @@ -1484,6 +1501,12 @@ void Avatar::rigReady() { buildSpine2SplineRatioCache(); setSkeletonData(getSkeletonDefaultData()); sendSkeletonData(); + + const bool prevNeedsWearablesLoadedCheck = _needsWearablesLoadedCheck; + _needsWearablesLoadedCheck = _skeletonModel && _skeletonModel->isLoaded() && _skeletonModel->getGeometry()->shouldWaitForWearables(); + if (prevNeedsWearablesLoadedCheck != _needsWearablesLoadedCheck) { + setEnableMeshVisible(!_needsWearablesLoadedCheck); + } } // rig has been reset. diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index af6b58a187..7690104b75 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -554,6 +554,9 @@ public: uint32_t appendSubMetaItems(render::ItemIDs& subItems); + static const float MYAVATAR_ENTITY_LOADING_PRIORITY; + static const float OTHERAVATAR_ENTITY_LOADING_PRIORITY; + signals: /*@jsdoc * Triggered when the avatar's target scale is changed. The target scale is the desired scale of the avatar without any @@ -742,8 +745,9 @@ protected: void processMaterials(); AABox _renderBound; - bool _isMeshVisible{ true }; - bool _needMeshVisibleSwitch{ true }; + bool _isMeshVisible { true }; + bool _needMeshVisibleSwitch { true }; + bool _needsWearablesLoadedCheck { false }; static const float MYAVATAR_LOADING_PRIORITY; static const float OTHERAVATAR_LOADING_PRIORITY; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 1b54d1b3b7..9be5758a4f 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1310,6 +1310,7 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint scene->enqueueTransaction(transaction); }); entity->setModel(model); + model->setLoadingPriorityOperator([entity]() { return EntityTreeRenderer::getEntityLoadingPriority(*entity); }); withWriteLock([&] { _model = model; }); } @@ -1317,7 +1318,6 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint if (_parsedModelURL != model->getURL()) { _texturesLoaded = false; _jointMappingCompleted = false; - model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity)); model->setURL(_parsedModelURL); } diff --git a/libraries/material-networking/src/material-networking/TextureCache.cpp b/libraries/material-networking/src/material-networking/TextureCache.cpp index 496b00ae2c..840fa50a0a 100644 --- a/libraries/material-networking/src/material-networking/TextureCache.cpp +++ b/libraries/material-networking/src/material-networking/TextureCache.cpp @@ -448,9 +448,9 @@ void NetworkTexture::setExtra(void* extra) { _shouldFailOnRedirect = _currentlyLoadingResourceType != ResourceType::KTX; if (_type == image::TextureUsage::SKY_TEXTURE) { - setLoadPriority(this, SKYBOX_LOAD_PRIORITY); + setLoadPriorityOperator(this, []() { return SKYBOX_LOAD_PRIORITY; }); } else if (_currentlyLoadingResourceType == ResourceType::KTX) { - setLoadPriority(this, HIGH_MIPS_LOAD_PRIORITY); + setLoadPriorityOperator(this, []() { return HIGH_MIPS_LOAD_PRIORITY; }); } if (!_url.isValid()) { @@ -704,7 +704,7 @@ void NetworkTexture::startRequestForNextMipLevel() { init(false); float priority = -(float)_originalKtxDescriptor->header.numberOfMipmapLevels + (float)_lowestKnownPopulatedMip; - setLoadPriority(this, priority); + setLoadPriorityOperator(this, [priority]() { return priority; }); _url.setFragment(QString::number(_lowestKnownPopulatedMip - 1)); TextureCache::attemptRequest(self); } diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index ddfdeb79d1..264c6b9801 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -252,7 +252,6 @@ void GeometryResource::downloadFinished(const QByteArray& data) { } auto animGraphVariant = _mapping.value("animGraphUrl"); - if (animGraphVariant.isValid()) { QUrl fstUrl(animGraphVariant.toString()); if (fstUrl.isValid()) { @@ -264,6 +263,8 @@ void GeometryResource::downloadFinished(const QByteArray& data) { _animGraphOverrideUrl = QUrl(); } + _waitForWearables = _mapping.value(WAIT_FOR_WEARABLES_FIELD).toBool(); + auto modelCache = DependencyManager::get(); GeometryExtra extra { GeometryMappingPair(base, _mapping), _textureBaseURL, false }; @@ -452,6 +453,7 @@ Geometry::Geometry(const Geometry& geometry) { } _animGraphOverrideUrl = geometry._animGraphOverrideUrl; + _waitForWearables = geometry._waitForWearables; _mapping = geometry._mapping; } diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 236c6262bf..7902108709 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -58,6 +58,7 @@ public: virtual bool areTexturesLoaded() const; const QUrl& getAnimGraphOverrideUrl() const { return _animGraphOverrideUrl; } + bool shouldWaitForWearables() const { return _waitForWearables; } const QVariantHash& getMapping() const { return _mapping; } protected: @@ -72,6 +73,7 @@ protected: QUrl _animGraphOverrideUrl; QVariantHash _mapping; // parsed contents of FST file. + bool _waitForWearables { false }; private: mutable bool _areTexturesLoaded { false }; diff --git a/libraries/model-serializers/src/FSTReader.cpp b/libraries/model-serializers/src/FSTReader.cpp index 7e84f012a7..9c1ff5a431 100644 --- a/libraries/model-serializers/src/FSTReader.cpp +++ b/libraries/model-serializers/src/FSTReader.cpp @@ -22,7 +22,7 @@ #include -const QStringList SINGLE_VALUE_PROPERTIES{"name", "filename", "texdir", "script", "comment"}; +const QStringList SINGLE_VALUE_PROPERTIES { NAME_FIELD, FILENAME_FIELD, TEXDIR_FIELD, SCRIPT_FIELD, WAIT_FOR_WEARABLES_FIELD, COMMENT_FIELD }; hifi::VariantMultiHash FSTReader::parseMapping(QIODevice* device) { hifi::VariantMultiHash properties; diff --git a/libraries/model-serializers/src/FSTReader.h b/libraries/model-serializers/src/FSTReader.h index e1b7405346..5557df67c6 100644 --- a/libraries/model-serializers/src/FSTReader.h +++ b/libraries/model-serializers/src/FSTReader.h @@ -33,6 +33,7 @@ static const QString BLENDSHAPE_FIELD = "bs"; static const QString SCRIPT_FIELD = "script"; static const QString JOINT_NAME_MAPPING_FIELD = "jointMap"; static const QString MATERIAL_MAPPING_FIELD = "materialMap"; +static const QString WAIT_FOR_WEARABLES_FIELD = "waitForWearables"; static const QString COMMENT_FIELD = "comment"; class FSTReader { diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 4e36fb4646..7a08a396a4 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -33,7 +33,12 @@ bool ResourceCacheSharedItems::appendRequest(QWeakPointer resource) { Lock lock(_mutex); if ((uint32_t)_loadingRequests.size() < _requestLimit) { - _loadingRequests.append(resource); + float priority = 0.0f; + // This should always be true, but just in case + if (QSharedPointer resourceStrong = resource.lock()) { + priority = resourceStrong->getLoadPriority(); + } + _loadingRequests.append({ resource, priority }); return true; } else { _pendingRequests.append(resource); @@ -70,14 +75,14 @@ uint32_t ResourceCacheSharedItems::getPendingRequestsCount() const { return _pendingRequests.size(); } -QList> ResourceCacheSharedItems::getLoadingRequests() const { - QList> result; +QList, float>> ResourceCacheSharedItems::getLoadingRequests() const { + QList, float>> result; Lock lock(_mutex); - foreach(QWeakPointer resource, _loadingRequests) { - auto locked = resource.lock(); + foreach(auto resourcePair, _loadingRequests) { + auto locked = resourcePair.first.lock(); if (locked) { - result.append(locked); + result.append({ locked, resourcePair.second }); } } @@ -96,7 +101,7 @@ void ResourceCacheSharedItems::removeRequest(QWeakPointer resource) { // QWeakPointer has no operator== implementation for two weak ptrs, so // manually loop in case resource has been freed. for (int i = 0; i < _loadingRequests.size();) { - auto request = _loadingRequests.at(i); + auto request = _loadingRequests.at(i).first; // Clear our resource and any freed resources if (!request || request.toStrongRef().data() == resource.toStrongRef().data()) { _loadingRequests.removeAt(i); @@ -519,7 +524,7 @@ void ResourceCache::updateTotalSize(const qint64& deltaSize) { emit dirty(); } -QList> ResourceCache::getLoadingRequests() { +QList, float>> ResourceCache::getLoadingRequests() { return DependencyManager::get()->getLoadingRequests(); } @@ -571,7 +576,7 @@ Resource::Resource(const Resource& other) : _startedLoading(other._startedLoading), _failedToLoad(other._failedToLoad), _loaded(other._loaded), - _loadPriorities(other._loadPriorities), + _loadPriorityOperators(other._loadPriorityOperators), _bytesReceived(other._bytesReceived), _bytesTotal(other._bytesTotal), _bytes(other._bytes), @@ -605,40 +610,24 @@ void Resource::ensureLoading() { } } -void Resource::setLoadPriority(const QPointer& owner, float priority) { +void Resource::setLoadPriorityOperator(const QPointer& owner, std::function priorityOperator) { if (!_failedToLoad) { - _loadPriorities.insert(owner, priority); - } -} - -void Resource::setLoadPriorities(const QHash, float>& priorities) { - if (_failedToLoad) { - return; - } - for (QHash, float>::const_iterator it = priorities.constBegin(); - it != priorities.constEnd(); it++) { - _loadPriorities.insert(it.key(), it.value()); - } -} - -void Resource::clearLoadPriority(const QPointer& owner) { - if (!_failedToLoad) { - _loadPriorities.remove(owner); + _loadPriorityOperators.insert(owner, priorityOperator); } } float Resource::getLoadPriority() { - if (_loadPriorities.size() == 0) { + if (_loadPriorityOperators.size() == 0) { return 0; } float highestPriority = -FLT_MAX; - for (QHash, float>::iterator it = _loadPriorities.begin(); it != _loadPriorities.end(); ) { - if (it.key().isNull()) { - it = _loadPriorities.erase(it); + for (QHash, std::function>::iterator it = _loadPriorityOperators.begin(); it != _loadPriorityOperators.end();) { + if (it.key().isNull() || !it.value()) { + it = _loadPriorityOperators.erase(it); continue; } - highestPriority = qMax(highestPriority, it.value()); + highestPriority = qMax(highestPriority, it.value()()); it++; } return highestPriority; @@ -742,7 +731,7 @@ void Resource::attemptRequest() { void Resource::finishedLoading(bool success) { if (success) { - _loadPriorities.clear(); + _loadPriorityOperators.clear(); _loaded = true; } else { _failedToLoad = true; diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 0eafd1f900..b920e8a7f8 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -73,7 +73,7 @@ public: QList> getPendingRequests() const; QSharedPointer getHighestPendingRequest(); uint32_t getPendingRequestsCount() const; - QList> getLoadingRequests() const; + QList, float>> getLoadingRequests() const; uint32_t getLoadingRequestsCount() const; void clear(); @@ -82,7 +82,7 @@ private: mutable Mutex _mutex; QList> _pendingRequests; - QList> _loadingRequests; + QList, float>> _loadingRequests; const uint32_t DEFAULT_REQUEST_LIMIT = 10; uint32_t _requestLimit { DEFAULT_REQUEST_LIMIT }; }; @@ -216,7 +216,7 @@ public: void setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize); qint64 getUnusedResourceCacheSize() const { return _unusedResourcesMaxSize; } - static QList> getLoadingRequests(); + static QList, float>> getLoadingRequests(); static uint32_t getPendingRequestCount(); static uint32_t getLoadingRequestCount(); @@ -424,13 +424,7 @@ public: void ensureLoading(); /// Sets the load priority for one owner. - virtual void setLoadPriority(const QPointer& owner, float priority); - - /// Sets a set of priorities at once. - virtual void setLoadPriorities(const QHash, float>& priorities); - - /// Clears the load priority for one owner. - virtual void clearLoadPriority(const QPointer& owner); + virtual void setLoadPriorityOperator(const QPointer& owner, std::function priorityOperator); /// Returns the highest load priority across all owners. float getLoadPriority(); @@ -451,7 +445,7 @@ public: qint64 getBytes() const { return _bytes; } /// For loading resources, returns the load progress. - float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; } + float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : ((float)_bytesReceived / _bytesTotal); } /// Refreshes the resource. virtual void refresh(); @@ -537,7 +531,7 @@ protected: bool _failedToLoad = false; bool _loaded = false; - QHash, float> _loadPriorities; + QHash, std::function> _loadPriorityOperators; QWeakPointer _self; QPointer _cache; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b7b645dd8a..69a593ed09 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1343,7 +1343,7 @@ void Model::setURL(const QUrl& url) { auto resource = DependencyManager::get()->getGeometryResource(url); if (resource) { - resource->setLoadPriority(this, _loadingPriority); + resource->setLoadPriorityOperator(this, _loadingPriorityOperator); _renderWatcher.setResource(resource); } _rig.initFlow(false); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 63a96f7253..bc9b8fcfff 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -295,7 +295,7 @@ public: // returns 'true' if needs fullUpdate after geometry change virtual bool updateGeometry(); - void setLoadingPriority(float priority) { _loadingPriority = priority; } + void setLoadingPriorityOperator(std::function priorityOperator) { _loadingPriorityOperator = priorityOperator; } size_t getRenderInfoVertexCount() const { return _renderInfoVertexCount; } size_t getRenderInfoTextureSize(); @@ -518,7 +518,7 @@ protected: uint64_t _created; private: - float _loadingPriority { 0.0f }; + std::function _loadingPriorityOperator { []() { return 0.0f; } }; void calculateTextureInfo(); From 7c3498bf9d5903beeed2b0ff55897a8c1954c37f Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Mon, 15 Jul 2024 14:32:22 -0700 Subject: [PATCH 060/109] add loadPriority to model entities, working on other avatars waitForWearables --- .../qml/+android_interface/Stats.qml | 4 ++-- interface/src/avatar/MyAvatar.cpp | 3 +++ interface/src/avatar/OtherAvatar.cpp | 11 +++++++++- interface/src/avatar/OtherAvatar.h | 10 +++++++++ .../src/avatars-renderer/Avatar.cpp | 2 +- .../src/avatars-renderer/Avatar.h | 1 + libraries/avatars/src/AvatarData.cpp | 2 ++ libraries/avatars/src/AvatarData.h | 3 ++- .../src/RenderableModelEntityItem.cpp | 5 ++++- .../entities/src/EntityItemProperties.cpp | 14 +++++++++++++ libraries/entities/src/EntityItemProperties.h | 1 + libraries/entities/src/EntityPropertyFlags.h | 21 ++++++++++--------- libraries/entities/src/ModelEntityItem.cpp | 18 ++++++++++++++++ libraries/entities/src/ModelEntityItem.h | 4 ++++ libraries/networking/src/ResourceCache.cpp | 19 +++++++---------- libraries/networking/src/ResourceCache.h | 6 +++--- libraries/networking/src/udt/PacketHeaders.h | 1 + 17 files changed, 94 insertions(+), 31 deletions(-) diff --git a/interface/resources/qml/+android_interface/Stats.qml b/interface/resources/qml/+android_interface/Stats.qml index 97d076ab6a..2b2acb6bf4 100644 --- a/interface/resources/qml/+android_interface/Stats.qml +++ b/interface/resources/qml/+android_interface/Stats.qml @@ -241,9 +241,9 @@ Item { model: root.downloadUrls delegate: StatText { visible: root.expanded; - text: modelData.length > 30 + text: (modelData.length > 30 ? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22) - : modelData + : modelData) + "\n\t" + "Priority: " + root.downloadPriorities[index] + ", Progress: " + root.downloadProgresses[index] + "%" } } } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0dcab1e45b..1779d64dc6 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1841,6 +1841,8 @@ void MyAvatar::handleChangedAvatarEntityData() { } }); } + + _hasCheckedForAvatarEntities = true; } bool MyAvatar::updateStaleAvatarEntityBlobs() const { @@ -1896,6 +1898,7 @@ void MyAvatar::prepareAvatarEntityDataForReload() { }); _reloadAvatarEntityDataFromSettings = true; + _hasCheckedForAvatarEntities = false; } AvatarEntityMap MyAvatar::getAvatarEntityData() const { diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index 2433bdde91..ab33a6f001 100644 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -595,7 +595,8 @@ void OtherAvatar::handleChangedAvatarEntityData() { } }); - setAvatarEntityDataChanged(false); + _avatarEntityDataChanged = false; + _hasCheckedForAvatarEntities = true; } void OtherAvatar::onAddAttachedAvatarEntity(const QUuid& id) { @@ -630,3 +631,11 @@ void OtherAvatar::updateAttachedAvatarEntities() { } } } + +void OtherAvatar::onIdentityRecieved() { + if (_avatarEntityIdentityCountdown > 0) { + _avatarEntityIdentityCountdown--; + } else { + _hasCheckedForAvatarEntities = true; + } +} diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index cfe0c8332d..094644a1d3 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -73,6 +73,8 @@ protected: void onAddAttachedAvatarEntity(const QUuid& id); void onRemoveAttachedAvatarEntity(const QUuid& id); + void onIdentityRecieved() override; + class AvatarEntityDataHash { public: AvatarEntityDataHash(uint32_t h) : hash(h) {}; @@ -91,6 +93,14 @@ protected: uint8_t _workloadRegion { workload::Region::INVALID }; BodyLOD _bodyLOD { BodyLOD::Sphere }; bool _needsDetailedRebuild { false }; + +private: + // When determining _hasCheckedForAvatarEntities for OtherAvatars, we can set it to true in + // handleChangedAvatarEntityData if we have avatar entities. But we never receive explicit + // confirmation from the avatar mixer if we don't have any. So instead, we wait to receive + // a few identity packets, and assume that if we haven't gotten any avatar entities by then, + // that we're safe to say there aren't any. + uint8_t _avatarEntityIdentityCountdown { 2 }; }; using OtherAvatarPointer = std::shared_ptr; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index be63127995..03131ed8b9 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -843,7 +843,7 @@ bool Avatar::getEnableMeshVisible() const { } void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { - if (_needsWearablesLoadedCheck) { + if (_needsWearablesLoadedCheck && _hasCheckedForAvatarEntities) { bool wearablesAreLoaded = true; // Technically, we should be checking for descendant avatar entities that are owned by this avatar. // But it's sufficient to just check all children entities here. diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 7690104b75..9e82ecdb7b 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -748,6 +748,7 @@ protected: bool _isMeshVisible { true }; bool _needMeshVisibleSwitch { true }; bool _needsWearablesLoadedCheck { false }; + bool _hasCheckedForAvatarEntities { false }; static const float MYAVATAR_LOADING_PRIORITY; static const float OTHERAVATAR_LOADING_PRIORITY; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d44890b1b8..fc28ad9e02 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2072,6 +2072,8 @@ void AvatarData::processAvatarIdentity(QDataStream& packetStream, bool& identity << "is >=" << (udt::SequenceNumber::Type) incomingSequenceNumber; #endif } + + onIdentityRecieved(); } QUrl AvatarData::getWireSafeSkeletonModelURL() const { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 55b1682974..a31291e1fc 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1276,7 +1276,6 @@ public: */ Q_INVOKABLE virtual void setAvatarEntityData(const AvatarEntityMap& avatarEntityData); - void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; } AvatarEntityIDs getAndClearRecentlyRemovedIDs(); /*@jsdoc @@ -1583,6 +1582,8 @@ protected: virtual const QString& getSessionDisplayNameForTransport() const { return _sessionDisplayName; } virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) { } // No-op in AvatarMixer + virtual void onIdentityRecieved() {} + // Body scale float _targetScale; float _domainMinimumHeight { MIN_AVATAR_HEIGHT }; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 9be5758a4f..793871e55a 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1310,7 +1310,10 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint scene->enqueueTransaction(transaction); }); entity->setModel(model); - model->setLoadingPriorityOperator([entity]() { return EntityTreeRenderer::getEntityLoadingPriority(*entity); }); + model->setLoadingPriorityOperator([entity]() { + float loadPriority = entity->getLoadPriority(); + return fabs(loadPriority) > EPSILON ? loadPriority : EntityTreeRenderer::getEntityLoadingPriority(*entity); + }); withWriteLock([&] { _model = model; }); } diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index c477a3fcef..2f0b1e0945 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -612,6 +612,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_GROUP_CULLED, groupCulled); CHECK_PROPERTY_CHANGE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients); CHECK_PROPERTY_CHANGE(PROP_USE_ORIGINAL_PIVOT, useOriginalPivot); + CHECK_PROPERTY_CHANGE(PROP_LOAD_PRIORITY, loadPriority); changedProperties += _animation.getChangedProperties(); // Light @@ -1090,6 +1091,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {boolean} useOriginalPivot=false - If false, the model will be centered based on its content, * ignoring any offset in the model itself. If true, the model will respect its original offset. Currently, * only pivots relative to {x: 0, y: 0, z: 0} are supported. + * @property {number} loadPriority=0.0 - If 0, the model download will be prioritized based on distance, size, and + * other factors, and assigned a priority automatically between 0 and PI / 2. Otherwise, the + * download will be ordered based on the set loadPriority. * @property {string} textures="" - A JSON string of texture name, URL pairs used when rendering the model in place of the * model's original textures. Use a texture name from the originalTextures property to override that texture. * Only the texture names and URLs to be overridden need be specified; original textures are used where there are no @@ -1919,6 +1923,7 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GROUP_CULLED, groupCulled); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USE_ORIGINAL_PIVOT, useOriginalPivot); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOAD_PRIORITY, loadPriority); _animation.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); } @@ -2350,6 +2355,7 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h COPY_PROPERTY_FROM_QSCRIPTVALUE(groupCulled, bool, setGroupCulled); COPY_PROPERTY_FROM_QSCRIPTVALUE(blendshapeCoefficients, QString, setBlendshapeCoefficients); COPY_PROPERTY_FROM_QSCRIPTVALUE(useOriginalPivot, bool, setUseOriginalPivot); + COPY_PROPERTY_FROM_QSCRIPTVALUE(loadPriority, float, setLoadPriority); _animation.copyFromScriptValue(object, namesSet, _defaultSettings); // Light @@ -2658,6 +2664,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(groupCulled); COPY_PROPERTY_IF_CHANGED(blendshapeCoefficients); COPY_PROPERTY_IF_CHANGED(useOriginalPivot); + COPY_PROPERTY_IF_CHANGED(loadPriority); _animation.merge(other._animation); // Light @@ -3036,6 +3043,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool); ADD_PROPERTY_TO_MAP(PROP_BLENDSHAPE_COEFFICIENTS, BlendshapeCoefficients, blendshapeCoefficients, QString); ADD_PROPERTY_TO_MAP(PROP_USE_ORIGINAL_PIVOT, UseOriginalPivot, useOriginalPivot, bool); + ADD_PROPERTY_TO_MAP(PROP_LOAD_PRIORITY, LoadPriority, loadPriority, float); { // Animation ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_URL, Animation, animation, URL, url); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation); @@ -3517,6 +3525,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, properties.getGroupCulled()); APPEND_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, properties.getBlendshapeCoefficients()); APPEND_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, properties.getUseOriginalPivot()); + APPEND_ENTITY_PROPERTY(PROP_LOAD_PRIORITY, properties.getLoadPriority()); _staticAnimation.setProperties(properties); _staticAnimation.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -4029,6 +4038,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GROUP_CULLED, bool, setGroupCulled); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLENDSHAPE_COEFFICIENTS, QString, setBlendshapeCoefficients); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USE_ORIGINAL_PIVOT, bool, setUseOriginalPivot); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOAD_PRIORITY, float, setLoadPriority); properties.getAnimation().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); } @@ -4438,6 +4448,7 @@ void EntityItemProperties::markAllChanged() { _groupCulledChanged = true; _blendshapeCoefficientsChanged = true; _useOriginalPivotChanged = true; + _loadPriorityChanged = true; _animation.markAllChanged(); // Light @@ -5024,6 +5035,9 @@ QList EntityItemProperties::listChangedProperties() { if (useOriginalPivotChanged()) { out += "useOriginalPivot"; } + if (loadPriorityChanged()) { + out += "loadPriority"; + } getAnimation().listChangedProperties(out); // Light diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index f8e7377df7..e9a4800892 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -315,6 +315,7 @@ public: DEFINE_PROPERTY_REF(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool, false); DEFINE_PROPERTY_REF(PROP_BLENDSHAPE_COEFFICIENTS, BlendshapeCoefficients, blendshapeCoefficients, QString, ""); DEFINE_PROPERTY_REF(PROP_USE_ORIGINAL_PIVOT, UseOriginalPivot, useOriginalPivot, bool, false); + DEFINE_PROPERTY_REF(PROP_LOAD_PRIORITY, LoadPriority, loadPriority, float, 0.0f); DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); // Light diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 2ed634c42c..a95feed55b 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -255,17 +255,18 @@ enum EntityPropertyList { PROP_GROUP_CULLED = PROP_DERIVED_7, PROP_BLENDSHAPE_COEFFICIENTS = PROP_DERIVED_8, PROP_USE_ORIGINAL_PIVOT = PROP_DERIVED_9, + PROP_LOAD_PRIORITY = PROP_DERIVED_10, // Animation - PROP_ANIMATION_URL = PROP_DERIVED_10, - PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_11, - PROP_ANIMATION_FPS = PROP_DERIVED_12, - PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_13, - PROP_ANIMATION_PLAYING = PROP_DERIVED_14, - PROP_ANIMATION_LOOP = PROP_DERIVED_15, - PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_16, - PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_17, - PROP_ANIMATION_HOLD = PROP_DERIVED_18, - PROP_ANIMATION_SMOOTH_FRAMES = PROP_DERIVED_19, + PROP_ANIMATION_URL = PROP_DERIVED_11, + PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_12, + PROP_ANIMATION_FPS = PROP_DERIVED_13, + PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_14, + PROP_ANIMATION_PLAYING = PROP_DERIVED_15, + PROP_ANIMATION_LOOP = PROP_DERIVED_16, + PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_17, + PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_18, + PROP_ANIMATION_HOLD = PROP_DERIVED_19, + PROP_ANIMATION_SMOOTH_FRAMES = PROP_DERIVED_20, // Light PROP_IS_SPOTLIGHT = PROP_DERIVED_0, diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 8910961c50..5306613925 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -72,6 +72,7 @@ EntityItemProperties ModelEntityItem::getProperties(const EntityPropertyFlags& d COPY_ENTITY_PROPERTY_TO_PROPERTIES(groupCulled, getGroupCulled); COPY_ENTITY_PROPERTY_TO_PROPERTIES(blendshapeCoefficients, getBlendshapeCoefficients); COPY_ENTITY_PROPERTY_TO_PROPERTIES(useOriginalPivot, getUseOriginalPivot); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(loadPriority, getLoadPriority); withReadLock([&] { _animationProperties.getProperties(properties); }); @@ -96,6 +97,7 @@ bool ModelEntityItem::setSubClassProperties(const EntityItemProperties& properti SET_ENTITY_PROPERTY_FROM_PROPERTIES(groupCulled, setGroupCulled); SET_ENTITY_PROPERTY_FROM_PROPERTIES(blendshapeCoefficients, setBlendshapeCoefficients); SET_ENTITY_PROPERTY_FROM_PROPERTIES(useOriginalPivot, setUseOriginalPivot); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(loadPriority, setLoadPriority); withWriteLock([&] { AnimationPropertyGroup animationProperties = _animationProperties; @@ -131,6 +133,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_GROUP_CULLED, bool, setGroupCulled); READ_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, QString, setBlendshapeCoefficients); READ_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, bool, setUseOriginalPivot); + READ_ENTITY_PROPERTY(PROP_LOAD_PRIORITY, float, setLoadPriority); // grab a local copy of _animationProperties to avoid multiple locks int bytesFromAnimation; @@ -171,6 +174,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& requestedProperties += PROP_GROUP_CULLED; requestedProperties += PROP_BLENDSHAPE_COEFFICIENTS; requestedProperties += PROP_USE_ORIGINAL_PIVOT; + requestedProperties += PROP_LOAD_PRIORITY; requestedProperties += _animationProperties.getEntityProperties(params); return requestedProperties; @@ -201,6 +205,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, getGroupCulled()); APPEND_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, getBlendshapeCoefficients()); APPEND_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, getUseOriginalPivot()); + APPEND_ENTITY_PROPERTY(PROP_LOAD_PRIORITY, getLoadPriority()); withReadLock([&] { _animationProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -255,6 +260,7 @@ void ModelEntityItem::debugDump() const { qCDebug(entities) << " compound shape URL:" << getCompoundShapeURL(); qCDebug(entities) << " blendshapeCoefficients:" << getBlendshapeCoefficients(); qCDebug(entities) << " useOrigialPivot:" << getUseOriginalPivot(); + qCDebug(entities) << " loadPriority:" << getLoadPriority(); } void ModelEntityItem::setShapeType(ShapeType type) { @@ -768,3 +774,15 @@ bool ModelEntityItem::getUseOriginalPivot() const { return _useOriginalPivot; }); } + +float ModelEntityItem::getLoadPriority() const { + return resultWithReadLock([&] { + return _loadPriority; + }); +} + +void ModelEntityItem::setLoadPriority(float loadPriority) { + withWriteLock([&] { + _loadPriority = loadPriority; + }); +} diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 2e1995be88..f6c3e82073 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -123,6 +123,9 @@ public: bool getUseOriginalPivot() const; void setUseOriginalPivot(bool useOriginalPivot); + float getLoadPriority() const; + void setLoadPriority(float loadPriority); + private: void setAnimationSettings(const QString& value); // only called for old bitstream format bool applyNewAnimationProperties(AnimationPropertyGroup newProperties); @@ -152,6 +155,7 @@ protected: glm::u8vec3 _color; glm::vec3 _modelScale { 1.0f }; QString _modelURL; + float _loadPriority { 0.0f }; bool _relayParentJoints; bool _groupCulled { false }; QVariantMap _blendshapeCoefficientsMap; diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 7a08a396a4..782c40609d 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -30,14 +30,9 @@ #include "NetworkLogging.h" #include "NodeList.h" -bool ResourceCacheSharedItems::appendRequest(QWeakPointer resource) { +bool ResourceCacheSharedItems::appendRequest(QWeakPointer resource, float priority) { Lock lock(_mutex); if ((uint32_t)_loadingRequests.size() < _requestLimit) { - float priority = 0.0f; - // This should always be true, but just in case - if (QSharedPointer resourceStrong = resource.lock()) { - priority = resourceStrong->getLoadPriority(); - } _loadingRequests.append({ resource, priority }); return true; } else { @@ -111,7 +106,7 @@ void ResourceCacheSharedItems::removeRequest(QWeakPointer resource) { } } -QSharedPointer ResourceCacheSharedItems::getHighestPendingRequest() { +std::pair, float> ResourceCacheSharedItems::getHighestPendingRequest() { // look for the highest priority pending request int highestIndex = -1; float highestPriority = -FLT_MAX; @@ -144,7 +139,7 @@ QSharedPointer ResourceCacheSharedItems::getHighestPendingRequest() { _pendingRequests.takeAt(highestIndex); } - return highestResource; + return { highestResource, highestPriority }; } void ResourceCacheSharedItems::clear() { @@ -536,11 +531,11 @@ uint32_t ResourceCache::getLoadingRequestCount() { return DependencyManager::get()->getLoadingRequestsCount(); } -bool ResourceCache::attemptRequest(QSharedPointer resource) { +bool ResourceCache::attemptRequest(QSharedPointer resource, float priority) { Q_ASSERT(!resource.isNull()); auto sharedItems = DependencyManager::get(); - if (sharedItems->appendRequest(resource)) { + if (sharedItems->appendRequest(resource, priority)) { resource->makeRequest(); return true; } @@ -560,8 +555,8 @@ void ResourceCache::requestCompleted(QWeakPointer resource) { bool ResourceCache::attemptHighestPriorityRequest() { auto sharedItems = DependencyManager::get(); - auto resource = sharedItems->getHighestPendingRequest(); - return (resource && attemptRequest(resource)); + auto resourcePair = sharedItems->getHighestPendingRequest(); + return (resourcePair.first && attemptRequest(resourcePair.first, resourcePair.second)); } static int requestID = 0; diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index b920e8a7f8..afd5d949ef 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -66,12 +66,12 @@ class ResourceCacheSharedItems : public Dependency { using Lock = std::unique_lock; public: - bool appendRequest(QWeakPointer newRequest); + bool appendRequest(QWeakPointer newRequest, float priority); void removeRequest(QWeakPointer doneRequest); void setRequestLimit(uint32_t limit); uint32_t getRequestLimit() const; QList> getPendingRequests() const; - QSharedPointer getHighestPendingRequest(); + std::pair, float> getHighestPendingRequest(); uint32_t getPendingRequestsCount() const; QList, float>> getLoadingRequests() const; uint32_t getLoadingRequestsCount() const; @@ -268,7 +268,7 @@ protected: /// Attempt to load a resource if requests are below the limit, otherwise queue the resource for loading /// \return true if the resource began loading, otherwise false if the resource is in the pending queue - static bool attemptRequest(QSharedPointer resource); + static bool attemptRequest(QSharedPointer resource, float priority = NAN); static void requestCompleted(QWeakPointer resource); static bool attemptHighestPriorityRequest(); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 67b9544f2e..960345ffaa 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -359,6 +359,7 @@ enum class EntityVersion : PacketVersion { AmbientColor, SoundEntities, TonemappingAndAmbientOcclusion, + ModelLoadPriority, // Add new versions above here NUM_PACKET_TYPE, From 86f3ca89187889b7be5d6a56c1b0ab8b88cbd539 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 20 Jul 2024 21:22:39 -0700 Subject: [PATCH 061/109] fix build error --- libraries/networking/src/ResourceCache.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index afd5d949ef..d2687f0964 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -16,6 +16,7 @@ #include #include +#include #include #include From e9831d9f22830ba5533a437fcfbf2727b5b562e8 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 18 Aug 2024 21:09:06 -0700 Subject: [PATCH 062/109] try to fix isServer assert --- libraries/entities/src/EntityTree.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index c96cb8d667..209240533b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -368,7 +368,7 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti } } } else { - if (getIsServer()) { + if (isEntityServer()) { bool simulationBlocked = !entity->getSimulatorID().isNull(); if (properties.simulationOwnerChanged()) { QUuid submittedID = properties.getSimulationOwner().getID(); @@ -674,7 +674,7 @@ void EntityTree::deleteEntitiesByID(const std::vector& ids, bool f // this method has two paths: // (a) entity-server: applies delete filter // (b) interface-client: deletes local- and my-avatar-entities immediately, submits domainEntity deletes to the entity-server - if (getIsServer()) { + if (isEntityServer()) { withWriteLock([&] { std::vector entitiesToDelete; entitiesToDelete.reserve(ids.size()); @@ -1468,7 +1468,7 @@ bool EntityTree::isScriptInAllowlist(const QString& scriptProperty) { // NOTE: Caller must lock the tree before calling this. int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) { - if (!getIsServer()) { + if (!isEntityServer()) { qCWarning(entities) << "EntityTree::processEditPacketData() should only be called on a server tree."; return 0; } @@ -2071,7 +2071,7 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo // Which means this is a state synchronization message from the the entity-server. It is saying // "The following domain-entities have already been deleted". While need to perform sanity checking // (e.g. verify these are domain entities) permissions need NOT checked for the domain-entities. - assert(!getIsServer()); + assert(!isEntityServer()); // TODO: remove this stuff out of EntityTree:: and into interface-client code. #ifdef EXTRA_ERASE_DEBUGGING qCDebug(entities) << "EntityTree::processEraseMessage()"; @@ -2141,7 +2141,7 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) { // NOTE: this is called on entity-server when receiving a delete request from an interface-client or agent //TODO: assert(treeIsLocked); - assert(getIsServer()); + assert(isEntityServer()); #ifdef EXTRA_ERASE_DEBUGGING qCDebug(entities) << "EntityTree::processEraseMessageDetails()"; #endif From 3d97f7845baf9d00d962099567fb851f46437570 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Mon, 19 Aug 2024 14:17:20 -0700 Subject: [PATCH 063/109] fix stats + waitForWearables --- .../qml/+android_interface/Stats.qml | 2 +- interface/resources/qml/Stats.qml | 2 +- .../src/avatars-renderer/Avatar.cpp | 19 ++++++++++--------- .../src/avatars-renderer/Avatar.h | 3 +++ 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/interface/resources/qml/+android_interface/Stats.qml b/interface/resources/qml/+android_interface/Stats.qml index 2b2acb6bf4..ef3db5d570 100644 --- a/interface/resources/qml/+android_interface/Stats.qml +++ b/interface/resources/qml/+android_interface/Stats.qml @@ -243,7 +243,7 @@ Item { visible: root.expanded; text: (modelData.length > 30 ? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22) - : modelData) + "\n\t" + "Priority: " + root.downloadPriorities[index] + ", Progress: " + root.downloadProgresses[index] + "%" + : modelData) + "\n\t" + (!isNaN(root.downloadPriorities[index]) ? ("Priority: " + root.downloadPriorities[index] + ", ") : "") + "Progress: " + root.downloadProgresses[index] + "%" } } } diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 9688f2a492..1482b6f92f 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -313,7 +313,7 @@ Item { visible: root.expanded; text: (modelData.length > 30 ? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22) - : modelData) + "\n\t" + "Priority: " + root.downloadPriorities[index] + ", Progress: " + root.downloadProgresses[index] + "%" + : modelData) + "\n\t" + (!isNaN(root.downloadPriorities[index]) ? ("Priority: " + root.downloadPriorities[index] + ", ") : "") + "Progress: " + root.downloadProgresses[index] + "%" } } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 03131ed8b9..6b27a31f8e 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -58,7 +58,7 @@ namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { ItemKey::Builder keyBuilder = ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::hifi::TAG_ALL_VIEWS).withMetaCullGroup(); auto avatarPtr = static_pointer_cast(avatar); - if (!avatarPtr->getEnableMeshVisible()) { + if (!avatarPtr->shouldRender()) { keyBuilder.withInvisible(); } return keyBuilder.build(); @@ -648,7 +648,7 @@ void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& sc _skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS); _skeletonModel->setGroupCulled(true); _skeletonModel->setCanCastShadow(true); - _skeletonModel->setVisibleInScene(_isMeshVisible, scene); + _skeletonModel->setVisibleInScene(shouldRender(), scene); processMaterials(); @@ -855,7 +855,10 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { } } }); - setEnableMeshVisible(_isMeshVisible || wearablesAreLoaded); + _isReadyToDraw = wearablesAreLoaded; + if (_isReadyToDraw) { + _needMeshVisibleSwitch = true; + } _needsWearablesLoadedCheck = !wearablesAreLoaded; } @@ -871,7 +874,7 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { _skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS); _skeletonModel->setGroupCulled(true); _skeletonModel->setCanCastShadow(true); - _skeletonModel->setVisibleInScene(_isMeshVisible, scene); + _skeletonModel->setVisibleInScene(shouldRender(), scene); processMaterials(); canTryFade = true; @@ -879,7 +882,7 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { } if (_needMeshVisibleSwitch) { - _skeletonModel->setVisibleInScene(_isMeshVisible, scene); + _skeletonModel->setVisibleInScene(shouldRender(), scene); updateRenderItem(transaction); _needMeshVisibleSwitch = false; } @@ -1502,11 +1505,9 @@ void Avatar::rigReady() { setSkeletonData(getSkeletonDefaultData()); sendSkeletonData(); - const bool prevNeedsWearablesLoadedCheck = _needsWearablesLoadedCheck; _needsWearablesLoadedCheck = _skeletonModel && _skeletonModel->isLoaded() && _skeletonModel->getGeometry()->shouldWaitForWearables(); - if (prevNeedsWearablesLoadedCheck != _needsWearablesLoadedCheck) { - setEnableMeshVisible(!_needsWearablesLoadedCheck); - } + _needMeshVisibleSwitch = (_isReadyToDraw != !_needsWearablesLoadedCheck); + _isReadyToDraw = !_needsWearablesLoadedCheck; } // rig has been reset. diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 9e82ecdb7b..2e618350a4 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -554,6 +554,8 @@ public: uint32_t appendSubMetaItems(render::ItemIDs& subItems); + virtual bool shouldRender() const { return _isMeshVisible && _isReadyToDraw; } + static const float MYAVATAR_ENTITY_LOADING_PRIORITY; static const float OTHERAVATAR_ENTITY_LOADING_PRIORITY; @@ -747,6 +749,7 @@ protected: AABox _renderBound; bool _isMeshVisible { true }; bool _needMeshVisibleSwitch { true }; + bool _isReadyToDraw { false }; bool _needsWearablesLoadedCheck { false }; bool _hasCheckedForAvatarEntities { false }; From 10dc75af5db9fa345034d88b60986b119b157e35 Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Tue, 27 Aug 2024 15:24:18 -0500 Subject: [PATCH 064/109] Listen for click instead of release. --- scripts/system/create/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index be1ba053ab..8808f3792e 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -2140,7 +2140,7 @@ Controller.keyPressEvent.connect(keyPressEvent); function deleteKey(value) { - if (value === 0) { // on release + if (value === 1) { // on release createApp.deleteSelectedEntities(); } } From 1ab2a7e1e3d1cf8ec90c73844f947c7aae3c1242 Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Tue, 27 Aug 2024 16:10:46 -0500 Subject: [PATCH 065/109] Reverted initial commit. Implemented hack to listen for menu click events. --- scripts/system/create/edit.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index 8808f3792e..5889eab967 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -2130,6 +2130,24 @@ if (isActive) { cameraManager.keyPressEvent(event); } + + // Hacks to get the menu bar buttons to work + // Copy + if (event.key === 67 && event.isControl && !event.isShifted) { + selectionManager.copySelectedEntities(); + } + // Paste + if (event.key === 86 && event.isControl && !event.isShifted) { + selectionManager.pasteEntities(); + } + // Cut + if (event.key === 88 && event.isControl && !event.isShifted) { + selectionManager.cutSelectedEntities(); + } + // Delete + if (event.key === 16777223 && !event.isControl && !event.isShifted) { + createApp.deleteSelectedEntities(); + } }; var keyReleaseEvent = function (event) { if (isActive) { @@ -2140,7 +2158,12 @@ Controller.keyPressEvent.connect(keyPressEvent); function deleteKey(value) { - if (value === 1) { // on release + if (value === 0) { // on press + createApp.deleteSelectedEntities(); + } + } + function copyKey(value){ + if (value === 1) { // on press createApp.deleteSelectedEntities(); } } From 7fc7f5e617763dbfaca7926bf5d022ce6fbf0391 Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Tue, 27 Aug 2024 16:13:12 -0500 Subject: [PATCH 066/109] Missed some reverts. --- scripts/system/create/edit.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index 5889eab967..ac929ee895 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -2130,7 +2130,7 @@ if (isActive) { cameraManager.keyPressEvent(event); } - + // Hacks to get the menu bar buttons to work // Copy if (event.key === 67 && event.isControl && !event.isShifted) { @@ -2158,12 +2158,12 @@ Controller.keyPressEvent.connect(keyPressEvent); function deleteKey(value) { - if (value === 0) { // on press + if (value === 0) { // on release createApp.deleteSelectedEntities(); } } function copyKey(value){ - if (value === 1) { // on press + if (value === 0) { // on release createApp.deleteSelectedEntities(); } } From edb08572f5fb4511ba9b4509fe7838920a9645a4 Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Tue, 27 Aug 2024 16:14:02 -0500 Subject: [PATCH 067/109] Missed another one. --- scripts/system/create/edit.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index ac929ee895..148e1efba1 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -2162,11 +2162,6 @@ createApp.deleteSelectedEntities(); } } - function copyKey(value){ - if (value === 0) { // on release - createApp.deleteSelectedEntities(); - } - } function deselectKey(value) { if (value === 0) { // on release selectionManager.clearSelections(this); From cf3afa8855843aacc2ab7842c87a12ee66ec996f Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Mon, 2 Sep 2024 12:29:44 -0500 Subject: [PATCH 068/109] Prevent duplicate actions. --- scripts/system/create/edit.js | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index 148e1efba1..c9f167e54b 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -2157,11 +2157,6 @@ Controller.keyReleaseEvent.connect(keyReleaseEvent); Controller.keyPressEvent.connect(keyPressEvent); - function deleteKey(value) { - if (value === 0) { // on release - createApp.deleteSelectedEntities(); - } - } function deselectKey(value) { if (value === 0) { // on release selectionManager.clearSelections(this); @@ -3123,11 +3118,6 @@ var isOnMacPlatform = Controller.getValue(Controller.Hardware.Application.PlatformMac); var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); - if (isOnMacPlatform) { - mapping.from([Controller.Hardware.Keyboard.Backspace]).to(deleteKey); - } else { - mapping.from([Controller.Hardware.Keyboard.Delete]).to(deleteKey); - } mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey); mapping.from([Controller.Hardware.Keyboard.F]).to(focusKey); mapping.from([Controller.Hardware.Keyboard.J]).to(gridKey); @@ -3138,15 +3128,9 @@ mapping.from([Controller.Hardware.Keyboard["7"]]).to(quickRotate90xKey); mapping.from([Controller.Hardware.Keyboard["8"]]).to(quickRotate90yKey); mapping.from([Controller.Hardware.Keyboard["9"]]).to(quickRotate90zKey); - mapping.from([Controller.Hardware.Keyboard.X]) - .when([Controller.Hardware.Keyboard.Control]) - .to(whenReleased(function() { selectionManager.cutSelectedEntities() })); mapping.from([Controller.Hardware.Keyboard.C]) .when([Controller.Hardware.Keyboard.Control]) .to(whenReleased(function() { selectionManager.copySelectedEntities() })); - mapping.from([Controller.Hardware.Keyboard.V]) - .when([Controller.Hardware.Keyboard.Control]) - .to(whenReleased(function() { selectionManager.pasteEntities() })); mapping.from([Controller.Hardware.Keyboard.D]) .when([Controller.Hardware.Keyboard.Control]) .to(whenReleased(function() { selectionManager.duplicateSelection() })); @@ -3170,10 +3154,8 @@ var pressedValue = 0.0; - if ((!isOnMacPlatform && keyUpEvent.keyCodeString === "Delete") - || (isOnMacPlatform && keyUpEvent.keyCodeString === "Backspace")) { - - deleteKey(pressedValue); + if (isOnMacPlatform && keyUpEvent.keyCodeString === "Backspace") { + createApp.deleteSelectedEntities(); } else if (keyUpEvent.keyCodeString === "T") { toggleKey(pressedValue); } else if (keyUpEvent.keyCodeString === "F") { @@ -3194,12 +3176,8 @@ quickRotate90yKey(pressedValue); } else if (keyUpEvent.keyCodeString === "9") { quickRotate90zKey(pressedValue); - } else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "X") { - selectionManager.cutSelectedEntities(); } else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "C") { selectionManager.copySelectedEntities(); - } else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "V") { - selectionManager.pasteEntities(); } else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "D") { selectionManager.duplicateSelection(); } else if (!isOnMacPlatform && keyUpEvent.controlKey && !keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "Z") { From 648c3ff9d810ddc5f2f3f84fe056888243b33a6f Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Wed, 4 Sep 2024 01:38:52 -0500 Subject: [PATCH 069/109] Added extra needed checks. --- .../resources/controllers/keyboardMouse.json | 332 +++++++++--------- 1 file changed, 167 insertions(+), 165 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index a91151f4e5..cb9cc319ab 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -1,183 +1,185 @@ { - "name": "Keyboard/Mouse to Actions", - "channels": [ - { "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.Q", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.Q", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.E", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.T", "when": "!Keyboard.Control", "to": "Actions.TogglePushToTalk" }, + "name": "Keyboard/Mouse to Actions", + "channels": [ + { "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton", "Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeRight" }, + { "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton","!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeLeft" }, + { "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeRight" }, + { "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeLeft" }, + { "from": "Keyboard.Q", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.Q", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.E", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.T", "when": "!Keyboard.Control", "to": "Actions.TogglePushToTalk" }, - { "comment" : "Mouse turn need to be small continuous increments", - "from": { "makeAxis" : [ - [ "Keyboard.MouseMoveLeft" ], - [ "Keyboard.MouseMoveRight" ] - ] - }, - "when": [ "Application.InHMD", "Application.SnapTurn", "Keyboard.RightMouseButton" ], - "to": "Actions.StepYaw", - "filters": - [ - "constrainToInteger", - { "type": "pulse", "interval": 0.2 }, - { "type": "scale", "scale": 22.5 } - ] + { "comment" : "Mouse turn need to be small continuous increments", + "from": { "makeAxis" : [ + [ "Keyboard.MouseMoveLeft" ], + [ "Keyboard.MouseMoveRight" ] + ] }, + "when": [ "Application.InHMD", "Application.SnapTurn", "Keyboard.RightMouseButton" ], + "to": "Actions.StepYaw", + "filters": + [ + "constrainToInteger", + { "type": "pulse", "interval": 0.2 }, + { "type": "scale", "scale": 22.5 } + ] + }, - { "comment" : "Touchpad turn need to be small continuous increments, but without the RMB constraint", - "from": { "makeAxis" : [ - [ "Keyboard.TouchpadLeft" ], - [ "Keyboard.TouchpadRight" ] - ] - }, - "when": [ "Application.InHMD", "Application.SnapTurn" ], - "to": "Actions.StepYaw", - "filters": - [ - "constrainToInteger", - { "type": "pulse", "interval": 0.2 }, - { "type": "scale", "scale": 22.5 } - ] + { "comment" : "Touchpad turn need to be small continuous increments, but without the RMB constraint", + "from": { "makeAxis" : [ + [ "Keyboard.TouchpadLeft" ], + [ "Keyboard.TouchpadRight" ] + ] }, + "when": [ "Application.InHMD", "Application.SnapTurn" ], + "to": "Actions.StepYaw", + "filters": + [ + "constrainToInteger", + { "type": "pulse", "interval": 0.2 }, + { "type": "scale", "scale": 22.5 } + ] + }, - { "from": { "makeAxis" : [ - ["Keyboard.Left" ], - ["Keyboard.Right"] - ] - }, - "when": ["Application.InHMD", "Application.SnapTurn", "!Keyboard.Shift"], - "to": "Actions.StepYaw", - "filters": - [ - { "type": "pulse", "interval": 0.5, "resetOnZero": true }, - { "type": "scale", "scale": 22.5 } - ] + { "from": { "makeAxis" : [ + ["Keyboard.Left" ], + ["Keyboard.Right"] + ] }, + "when": ["Application.InHMD", "Application.SnapTurn", "!Keyboard.Shift"], + "to": "Actions.StepYaw", + "filters": + [ + { "type": "pulse", "interval": 0.5, "resetOnZero": true }, + { "type": "scale", "scale": 22.5 } + ] + }, - { "from": { "makeAxis" : [ - ["Keyboard.A"], - ["Keyboard.D"] - ] - }, - "when": [ "Application.InHMD", "Application.SnapTurn" ], - "to": "Actions.StepYaw", - "filters": - [ - { "type": "pulse", "interval": 0.5, "resetOnZero": true }, - { "type": "scale", "scale": 22.5 } - ] + { "from": { "makeAxis" : [ + ["Keyboard.A"], + ["Keyboard.D"] + ] }, + "when": [ "Application.InHMD", "Application.SnapTurn" ], + "to": "Actions.StepYaw", + "filters": + [ + { "type": "pulse", "interval": 0.5, "resetOnZero": true }, + { "type": "scale", "scale": 22.5 } + ] + }, - { "from": { "makeAxis" : [ - ["Keyboard.Left"], - ["Keyboard.Right"] - ] - }, - "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Shift"], - "to": "Actions.Yaw" + { "from": { "makeAxis" : [ + ["Keyboard.Left"], + ["Keyboard.Right"] + ] }, + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Shift"], + "to": "Actions.Yaw" + }, - { "from": "Keyboard.Left", - "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"], - "to": "Actions.LATERAL_LEFT" - }, + { "from": "Keyboard.Left", + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"], + "to": "Actions.LATERAL_LEFT" + }, - { "from": "Keyboard.Right", - "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"], - "to": "Actions.LATERAL_RIGHT" - }, + { "from": "Keyboard.Right", + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"], + "to": "Actions.LATERAL_RIGHT" + }, - { "from": { "makeAxis" : [ - ["Keyboard.A"], - ["Keyboard.D"] - ] - }, - "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Control"], - "to": "Actions.Yaw" + { "from": { "makeAxis" : [ + ["Keyboard.A"], + ["Keyboard.D"] + ] }, + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Control"], + "to": "Actions.Yaw" + }, - { "from": "Keyboard.A", - "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"], - "to": "Actions.LATERAL_LEFT" - }, + { "from": "Keyboard.A", + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"], + "to": "Actions.LATERAL_LEFT" + }, - { "from": "Keyboard.D", - "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"], - "to": "Actions.LATERAL_RIGHT" - }, + { "from": "Keyboard.D", + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"], + "to": "Actions.LATERAL_RIGHT" + }, - { "from": { "makeAxis" : [ - ["Keyboard.TouchpadLeft"], - ["Keyboard.TouchpadRight"] - ] - }, - "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity"], - "to": "Actions.Yaw" + { "from": { "makeAxis" : [ + ["Keyboard.TouchpadLeft"], + ["Keyboard.TouchpadRight"] + ] }, + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity"], + "to": "Actions.Yaw" + }, - { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, - "when": ["Keyboard.RightMouseButton", "!Application.CaptureMouse"], - "to": "Actions.DeltaYaw", - "filters": - [ - { "type": "scale", "scale": 0.6 } - ] - }, + { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, + "when": ["Keyboard.RightMouseButton", "!Application.CaptureMouse"], + "to": "Actions.DeltaYaw", + "filters": + [ + { "type": "scale", "scale": 0.6 } + ] + }, - { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, - "to": "Actions.DeltaYaw", - "when": "Application.CaptureMouse", - "filters": - [ - { "type": "scale", "scale": 0.2 } - ] - }, + { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, + "to": "Actions.DeltaYaw", + "when": "Application.CaptureMouse", + "filters": + [ + { "type": "scale", "scale": 0.2 } + ] + }, - { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, - "when": ["!Application.CameraSelfie", "!Application.CameraLookAt", "!Application.CaptureMouse", "Keyboard.RightMouseButton"], - "to": "Actions.DeltaPitch", - "filters": - [ - { "type": "scale", "scale": 0.6 } - ] - }, + { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, + "when": ["!Application.CameraSelfie", "!Application.CameraLookAt", "!Application.CaptureMouse", "Keyboard.RightMouseButton"], + "to": "Actions.DeltaPitch", + "filters": + [ + { "type": "scale", "scale": 0.6 } + ] + }, - { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, - "to": "Actions.DeltaPitch", - "when": "Application.CaptureMouse", - "filters": - [ - { "type": "scale", "scale": 0.2 } - ] - }, + { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, + "to": "Actions.DeltaPitch", + "when": "Application.CaptureMouse", + "filters": + [ + { "type": "scale", "scale": 0.2 } + ] + }, - { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, - "when": ["Application.CameraLookAt", "Keyboard.RightMouseButton"], - "to": "Actions.DeltaPitch", - "filters": - [ - { "type": "scale", "scale": 0.2 } - ] - }, + { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, + "when": ["Application.CameraLookAt", "Keyboard.RightMouseButton"], + "to": "Actions.DeltaPitch", + "filters": + [ + { "type": "scale", "scale": 0.2 } + ] + }, - { "from": { "makeAxis" : ["Keyboard.MouseMoveDown", "Keyboard.MouseMoveUp"] }, - "when": ["Application.CameraSelfie", "Keyboard.RightMouseButton"], - "to": "Actions.DeltaPitch", - "filters": - [ - { "type": "scale", "scale": 0.2 } - ] - }, + { "from": { "makeAxis" : ["Keyboard.MouseMoveDown", "Keyboard.MouseMoveUp"] }, + "when": ["Application.CameraSelfie", "Keyboard.RightMouseButton"], + "to": "Actions.DeltaPitch", + "filters": + [ + { "type": "scale", "scale": 0.2 } + ] + }, { "from": "Keyboard.W", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.S", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.S", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.W", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_BACKWARD" }, - { "from": "Keyboard.Shift", "when": ["!Keyboard.Left", "!Keyboard.Right"], "to": "Actions.SPRINT" }, - { "from": "Keyboard.C", "when": "!Keyboard.Control", "to": "Actions.VERTICAL_DOWN" }, - { "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.Shift", "when": ["!Keyboard.Left", "!Keyboard.Right"], "to": "Actions.SPRINT" }, + { "from": "Keyboard.C", "when": "!Keyboard.Control", "to": "Actions.VERTICAL_DOWN" }, + { "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.Up", "when": "Application.CameraFirstPersonLookat", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.Up", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, @@ -189,22 +191,22 @@ { "from": "Keyboard.Down", "when": "Application.CameraLookAt", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.Down", "when": "Application.CameraSelfie", "to": "Actions.LONGITUDINAL_FORWARD" }, - { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, - { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, + { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, + { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, - { "from": "Keyboard.TouchpadDown", "to": "Actions.PITCH_DOWN" }, - { "from": "Keyboard.TouchpadUp", "to": "Actions.PITCH_UP" }, + { "from": "Keyboard.TouchpadDown", "to": "Actions.PITCH_DOWN" }, + { "from": "Keyboard.TouchpadUp", "to": "Actions.PITCH_UP" }, - { "from": "Keyboard.MouseWheelUp", "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.MouseWheelDown", "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.MouseWheelLeft", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.02 } ]}, - { "from": "Keyboard.MouseWheelRight", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.02 } ]}, - { "from": "Keyboard.GesturePinchOut", "to": "Actions.BOOM_OUT"}, - { "from": "Keyboard.GesturePinchIn", "to": "Actions.BOOM_IN"}, + { "from": "Keyboard.MouseWheelUp", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.MouseWheelDown", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.MouseWheelLeft", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.02 } ]}, + { "from": "Keyboard.MouseWheelRight", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.02 } ]}, + { "from": "Keyboard.GesturePinchOut", "to": "Actions.BOOM_OUT"}, + { "from": "Keyboard.GesturePinchIn", "to": "Actions.BOOM_IN"}, - { "from": "Keyboard.Space", "to": "Actions.VERTICAL_UP" }, - { "from": "Keyboard.R", "to": "Actions.ACTION1" }, - { "from": "Keyboard.T", "to": "Actions.ACTION2" }, - { "from": "Keyboard.Tab", "to": "Actions.ContextMenu" } - ] + { "from": "Keyboard.Space", "to": "Actions.VERTICAL_UP" }, + { "from": "Keyboard.R", "to": "Actions.ACTION1" }, + { "from": "Keyboard.T", "to": "Actions.ACTION2" }, + { "from": "Keyboard.Tab", "to": "Actions.ContextMenu" } + ] } From 72cb5245aa4d74a7dee3b780681461449bd45a35 Mon Sep 17 00:00:00 2001 From: Armored-Dragon Date: Thu, 5 Sep 2024 18:38:04 +0000 Subject: [PATCH 070/109] Fix without formatting? (#91) --- .../resources/controllers/keyboardMouse.json | 305 +++++++++--------- 1 file changed, 153 insertions(+), 152 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index cb9cc319ab..cdb49f220d 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -1,176 +1,177 @@ { - "name": "Keyboard/Mouse to Actions", - "channels": [ - { "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton", "Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeRight" }, - { "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton","!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeLeft" }, - { "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeRight" }, - { "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeLeft" }, - { "from": "Keyboard.Q", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.Q", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.E", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.T", "when": "!Keyboard.Control", "to": "Actions.TogglePushToTalk" }, + "name": "Keyboard/Mouse to Actions", + "channels": [ + { "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton", "Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeRight" }, + { "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton","!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeLeft" }, + { "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeRight" }, + { "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeLeft" }, + { "from": "Keyboard.Q", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.Q", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.E", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.T", "when": "!Keyboard.Control", "to": "Actions.TogglePushToTalk" }, - { "comment" : "Mouse turn need to be small continuous increments", - "from": { "makeAxis" : [ - [ "Keyboard.MouseMoveLeft" ], - [ "Keyboard.MouseMoveRight" ] - ] + { "comment" : "Mouse turn need to be small continuous increments", + "from": { "makeAxis" : [ + [ "Keyboard.MouseMoveLeft" ], + [ "Keyboard.MouseMoveRight" ] + ] + }, + "when": [ "Application.InHMD", "Application.SnapTurn", "Keyboard.RightMouseButton" ], + "to": "Actions.StepYaw", + "filters": + [ + "constrainToInteger", + { "type": "pulse", "interval": 0.2 }, + { "type": "scale", "scale": 22.5 } + ] }, - "when": [ "Application.InHMD", "Application.SnapTurn", "Keyboard.RightMouseButton" ], - "to": "Actions.StepYaw", - "filters": - [ - "constrainToInteger", - { "type": "pulse", "interval": 0.2 }, - { "type": "scale", "scale": 22.5 } - ] - }, - { "comment" : "Touchpad turn need to be small continuous increments, but without the RMB constraint", - "from": { "makeAxis" : [ - [ "Keyboard.TouchpadLeft" ], - [ "Keyboard.TouchpadRight" ] - ] + { "comment" : "Touchpad turn need to be small continuous increments, but without the RMB constraint", + "from": { "makeAxis" : [ + [ "Keyboard.TouchpadLeft" ], + [ "Keyboard.TouchpadRight" ] + ] + }, + "when": [ "Application.InHMD", "Application.SnapTurn" ], + "to": "Actions.StepYaw", + "filters": + [ + "constrainToInteger", + { "type": "pulse", "interval": 0.2 }, + { "type": "scale", "scale": 22.5 } + ] }, - "when": [ "Application.InHMD", "Application.SnapTurn" ], - "to": "Actions.StepYaw", - "filters": - [ - "constrainToInteger", - { "type": "pulse", "interval": 0.2 }, - { "type": "scale", "scale": 22.5 } - ] - }, - { "from": { "makeAxis" : [ - ["Keyboard.Left" ], - ["Keyboard.Right"] - ] + { "from": { "makeAxis" : [ + ["Keyboard.Left" ], + ["Keyboard.Right"] + ] + }, + "when": ["Application.InHMD", "Application.SnapTurn", "!Keyboard.Shift"], + "to": "Actions.StepYaw", + "filters": + [ + { "type": "pulse", "interval": 0.5, "resetOnZero": true }, + { "type": "scale", "scale": 22.5 } + ] }, - "when": ["Application.InHMD", "Application.SnapTurn", "!Keyboard.Shift"], - "to": "Actions.StepYaw", - "filters": - [ - { "type": "pulse", "interval": 0.5, "resetOnZero": true }, - { "type": "scale", "scale": 22.5 } - ] - }, - { "from": { "makeAxis" : [ - ["Keyboard.A"], - ["Keyboard.D"] - ] + { "from": { "makeAxis" : [ + ["Keyboard.A"], + ["Keyboard.D"] + ] + }, + "when": [ "Application.InHMD", "Application.SnapTurn" ], + "to": "Actions.StepYaw", + "filters": + [ + { "type": "pulse", "interval": 0.5, "resetOnZero": true }, + { "type": "scale", "scale": 22.5 } + ] }, - "when": [ "Application.InHMD", "Application.SnapTurn" ], - "to": "Actions.StepYaw", - "filters": - [ - { "type": "pulse", "interval": 0.5, "resetOnZero": true }, - { "type": "scale", "scale": 22.5 } - ] - }, - { "from": { "makeAxis" : [ - ["Keyboard.Left"], - ["Keyboard.Right"] - ] + { "from": { "makeAxis" : [ + ["Keyboard.Left"], + ["Keyboard.Right"] + ] + }, + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Shift"], + "to": "Actions.Yaw" }, - "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Shift"], - "to": "Actions.Yaw" - }, - { "from": "Keyboard.Left", - "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"], - "to": "Actions.LATERAL_LEFT" - }, - - { "from": "Keyboard.Right", - "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"], - "to": "Actions.LATERAL_RIGHT" - }, - - { "from": { "makeAxis" : [ - ["Keyboard.A"], - ["Keyboard.D"] - ] + { "from": "Keyboard.Left", + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"], + "to": "Actions.LATERAL_LEFT" }, - "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Control"], - "to": "Actions.Yaw" - }, - { "from": "Keyboard.A", - "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"], - "to": "Actions.LATERAL_LEFT" - }, - - { "from": "Keyboard.D", - "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"], - "to": "Actions.LATERAL_RIGHT" - }, - - { "from": { "makeAxis" : [ - ["Keyboard.TouchpadLeft"], - ["Keyboard.TouchpadRight"] - ] + { "from": "Keyboard.Right", + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"], + "to": "Actions.LATERAL_RIGHT" }, - "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity"], - "to": "Actions.Yaw" - }, - { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, - "when": ["Keyboard.RightMouseButton", "!Application.CaptureMouse"], - "to": "Actions.DeltaYaw", - "filters": - [ - { "type": "scale", "scale": 0.6 } - ] - }, + { "from": { "makeAxis" : [ + ["Keyboard.A"], + ["Keyboard.D"] + ] + }, + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Control"], + "to": "Actions.Yaw" + }, - { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, - "to": "Actions.DeltaYaw", - "when": "Application.CaptureMouse", - "filters": - [ - { "type": "scale", "scale": 0.2 } - ] - }, + { "from": "Keyboard.A", + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"], + "to": "Actions.LATERAL_LEFT" + }, - { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, - "when": ["!Application.CameraSelfie", "!Application.CameraLookAt", "!Application.CaptureMouse", "Keyboard.RightMouseButton"], - "to": "Actions.DeltaPitch", - "filters": - [ - { "type": "scale", "scale": 0.6 } - ] - }, + { "from": "Keyboard.D", + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"], + "to": "Actions.LATERAL_RIGHT" + }, - { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, - "to": "Actions.DeltaPitch", - "when": "Application.CaptureMouse", - "filters": - [ - { "type": "scale", "scale": 0.2 } - ] - }, + { "from": { "makeAxis" : [ + ["Keyboard.TouchpadLeft"], + ["Keyboard.TouchpadRight"] + ] + }, + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity"], + "to": "Actions.Yaw" + }, - { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, - "when": ["Application.CameraLookAt", "Keyboard.RightMouseButton"], - "to": "Actions.DeltaPitch", - "filters": - [ - { "type": "scale", "scale": 0.2 } - ] - }, + { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, + "when": ["Keyboard.RightMouseButton", "!Application.CaptureMouse"], + "to": "Actions.DeltaYaw", + "filters": + [ + { "type": "scale", "scale": 0.6 } + ] + }, + + { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, + "to": "Actions.DeltaYaw", + "when": "Application.CaptureMouse", + "filters": + [ + { "type": "scale", "scale": 0.2 } + ] + }, + + { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, + "when": ["!Application.CameraSelfie", "!Application.CameraLookAt", "!Application.CaptureMouse", "Keyboard.RightMouseButton"], + "to": "Actions.DeltaPitch", + "filters": + [ + { "type": "scale", "scale": 0.6 } + ] + }, + + { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, + "to": "Actions.DeltaPitch", + "when": "Application.CaptureMouse", + "filters": + [ + { "type": "scale", "scale": 0.2 } + ] + }, + + { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, + "when": ["Application.CameraLookAt", "Keyboard.RightMouseButton"], + "to": "Actions.DeltaPitch", + "filters": + [ + { "type": "scale", "scale": 0.2 } + ] + }, + + { "from": { "makeAxis" : ["Keyboard.MouseMoveDown", "Keyboard.MouseMoveUp"] }, + "when": ["Application.CameraSelfie", "Keyboard.RightMouseButton"], + "to": "Actions.DeltaPitch", + "filters": + [ + { "type": "scale", "scale": 0.2 } + ] + }, - { "from": { "makeAxis" : ["Keyboard.MouseMoveDown", "Keyboard.MouseMoveUp"] }, - "when": ["Application.CameraSelfie", "Keyboard.RightMouseButton"], - "to": "Actions.DeltaPitch", - "filters": - [ - { "type": "scale", "scale": 0.2 } - ] - }, { "from": "Keyboard.W", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.S", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_BACKWARD" }, From baa37a336ecd74745cfa9f8b500d41924b5c137e Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Thu, 5 Sep 2024 13:42:22 -0500 Subject: [PATCH 071/109] Hopefully fixed formatting. --- .../resources/controllers/keyboardMouse.json | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index cdb49f220d..5514344f8e 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -177,10 +177,10 @@ { "from": "Keyboard.S", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.S", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.W", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_BACKWARD" }, - { "from": "Keyboard.Shift", "when": ["!Keyboard.Left", "!Keyboard.Right"], "to": "Actions.SPRINT" }, - { "from": "Keyboard.C", "when": "!Keyboard.Control", "to": "Actions.VERTICAL_DOWN" }, - { "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.Shift", "when": ["!Keyboard.Left", "!Keyboard.Right"], "to": "Actions.SPRINT" }, + { "from": "Keyboard.C", "when": "!Keyboard.Control", "to": "Actions.VERTICAL_DOWN" }, + { "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.Up", "when": "Application.CameraFirstPersonLookat", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.Up", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, @@ -192,22 +192,22 @@ { "from": "Keyboard.Down", "when": "Application.CameraLookAt", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.Down", "when": "Application.CameraSelfie", "to": "Actions.LONGITUDINAL_FORWARD" }, - { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, - { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, + { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, + { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, - { "from": "Keyboard.TouchpadDown", "to": "Actions.PITCH_DOWN" }, - { "from": "Keyboard.TouchpadUp", "to": "Actions.PITCH_UP" }, + { "from": "Keyboard.TouchpadDown", "to": "Actions.PITCH_DOWN" }, + { "from": "Keyboard.TouchpadUp", "to": "Actions.PITCH_UP" }, - { "from": "Keyboard.MouseWheelUp", "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.MouseWheelDown", "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.MouseWheelLeft", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.02 } ]}, - { "from": "Keyboard.MouseWheelRight", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.02 } ]}, - { "from": "Keyboard.GesturePinchOut", "to": "Actions.BOOM_OUT"}, - { "from": "Keyboard.GesturePinchIn", "to": "Actions.BOOM_IN"}, + { "from": "Keyboard.MouseWheelUp", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.MouseWheelDown", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.MouseWheelLeft", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.02 } ]}, + { "from": "Keyboard.MouseWheelRight", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.02 } ]}, + { "from": "Keyboard.GesturePinchOut", "to": "Actions.BOOM_OUT"}, + { "from": "Keyboard.GesturePinchIn", "to": "Actions.BOOM_IN"}, - { "from": "Keyboard.Space", "to": "Actions.VERTICAL_UP" }, - { "from": "Keyboard.R", "to": "Actions.ACTION1" }, - { "from": "Keyboard.T", "to": "Actions.ACTION2" }, - { "from": "Keyboard.Tab", "to": "Actions.ContextMenu" } + { "from": "Keyboard.Space", "to": "Actions.VERTICAL_UP" }, + { "from": "Keyboard.R", "to": "Actions.ACTION1" }, + { "from": "Keyboard.T", "to": "Actions.ACTION2" }, + { "from": "Keyboard.Tab", "to": "Actions.ContextMenu" } ] } From fe4b8a55188d5fd0a828ee61c10bbf5ba65b4790 Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Thu, 5 Sep 2024 13:43:28 -0500 Subject: [PATCH 072/109] Things can't be too easy. --- interface/resources/controllers/keyboardMouse.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 5514344f8e..a935f65a26 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -172,7 +172,6 @@ ] }, - { "from": "Keyboard.W", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.S", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.S", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_FORWARD" }, @@ -209,5 +208,5 @@ { "from": "Keyboard.R", "to": "Actions.ACTION1" }, { "from": "Keyboard.T", "to": "Actions.ACTION2" }, { "from": "Keyboard.Tab", "to": "Actions.ContextMenu" } - ] + ] } From 7d086405138c6e1ef35648ef704bdffeab02aebd Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Sun, 8 Sep 2024 17:23:52 -0500 Subject: [PATCH 073/109] Remove google poly --- .eslintrc.js | 1 - interface/src/Application.cpp | 29 +-- .../GooglePolyScriptingInterface.cpp | 180 ------------------ .../scripting/GooglePolyScriptingInterface.h | 116 ----------- 4 files changed, 2 insertions(+), 324 deletions(-) delete mode 100644 interface/src/scripting/GooglePolyScriptingInterface.cpp delete mode 100644 interface/src/scripting/GooglePolyScriptingInterface.h diff --git a/.eslintrc.js b/.eslintrc.js index 631d17a792..f778512ac9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -42,7 +42,6 @@ module.exports = { "EventBridge": false, "FaceTracker": false, "GlobalServices": false, - "GooglePoly": false, "Graphics": false, "HifiAbout": false, "HMD": false, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f630bea863..7c8b38daca 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -232,7 +232,6 @@ #include #include -#include #include #include @@ -908,7 +907,6 @@ bool setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted auto entityScriptServerLog = DependencyManager::get(); QObject::connect(scriptEngines.data(), &ScriptEngines::requestingEntityScriptServerLog, entityScriptServerLog.data(), &EntityScriptServerLogClient::requestMessagesForScriptEngines); - DependencyManager::set(); DependencyManager::set(nullptr, qApp->getOcteeSceneStats()); DependencyManager::set(); DependencyManager::set(); @@ -4084,11 +4082,8 @@ bool Application::importFromZIP(const QString& filePath) { qDebug() << "A zip file has been dropped in: " << filePath; QUrl empty; // handle Blocks download from Marketplace - if (filePath.contains("poly.google.com/downloads")) { - addAssetToWorldFromURL(filePath); - } else { - qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true, false); - } + qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true, false); + return true; } @@ -7643,8 +7638,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptManagerPoint scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Users", DependencyManager::get().data()); - //scriptEngine->registerGlobalObject("GooglePoly", DependencyManager::get().data()); - if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptManager.get(), steamClient.get())); } @@ -8043,15 +8036,6 @@ void Application::addAssetToWorldFromURL(QString url) { if (url.contains("filename")) { filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL. } - if (url.contains("poly.google.com/downloads")) { - filename = url.section('/', -1); - if (url.contains("noDownload")) { - filename.remove(".zip?noDownload=false"); - } else { - filename.remove(".zip"); - } - - } if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { QString errorInfo = "You do not have permissions to write to the Asset Server."; @@ -8080,15 +8064,6 @@ void Application::addAssetToWorldFromURLRequestFinished() { if (url.contains("filename")) { filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL. } - if (url.contains("poly.google.com/downloads")) { - filename = url.section('/', -1); - if (url.contains("noDownload")) { - filename.remove(".zip?noDownload=false"); - } else { - filename.remove(".zip"); - } - isBlocks = true; - } if (result == ResourceRequest::Success) { QTemporaryDir temporaryDir; diff --git a/interface/src/scripting/GooglePolyScriptingInterface.cpp b/interface/src/scripting/GooglePolyScriptingInterface.cpp deleted file mode 100644 index ba4afaba27..0000000000 --- a/interface/src/scripting/GooglePolyScriptingInterface.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// -// GooglePolyScriptingInterface.cpp -// interface/src/scripting -// -// Created by Elisa Lupin-Jimenez on 12/3/2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "GooglePolyScriptingInterface.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ScriptEngineLogging.h" - -const QString LIST_POLY_URL = "https://poly.googleapis.com/v1/assets?"; -const QString GET_POLY_URL = "https://poly.googleapis.com/v1/assets/model?"; - -const QStringList VALID_FORMATS = QStringList() << "BLOCKS" << "FBX" << "GLTF" << "GLTF2" << "OBJ" << "TILT" << ""; -const QStringList VALID_CATEGORIES = QStringList() << "animals" << "architecture" << "art" << "food" << - "nature" << "objects" << "people" << "scenes" << "technology" << "transport" << ""; - -GooglePolyScriptingInterface::GooglePolyScriptingInterface() { - // nothing to be implemented -} - -void GooglePolyScriptingInterface::setAPIKey(const QString& key) { - _authCode = key; -} - -QString GooglePolyScriptingInterface::getAssetList(const QString& keyword, const QString& category, const QString& format) { - QUrl url = formatURLQuery(keyword, category, format); - if (!url.isEmpty()) { - QByteArray json = parseJSON(url, 0).toJsonDocument().toJson(); - return (QString) json; - } else { - qCDebug(scriptengine) << "Invalid filters were specified."; - return ""; - } -} - -QString GooglePolyScriptingInterface::getFBX(const QString& keyword, const QString& category) { - QUrl url = formatURLQuery(keyword, category, "FBX"); - return getModelURL(url); -} - -QString GooglePolyScriptingInterface::getOBJ(const QString& keyword, const QString& category) { - QUrl url = formatURLQuery(keyword, category, "OBJ"); - return getModelURL(url); -} - -QString GooglePolyScriptingInterface::getBlocks(const QString& keyword, const QString& category) { - QUrl url = formatURLQuery(keyword, category, "BLOCKS"); - return getModelURL(url); -} - -QString GooglePolyScriptingInterface::getGLTF(const QString& keyword, const QString& category) { - QUrl url = formatURLQuery(keyword, category, "GLTF"); - return getModelURL(url); -} - -QString GooglePolyScriptingInterface::getGLTF2(const QString& keyword, const QString& category) { - QUrl url = formatURLQuery(keyword, category, "GLTF2"); - return getModelURL(url); -} - -// This method will not be useful until we support Tilt models -QString GooglePolyScriptingInterface::getTilt(const QString& keyword, const QString& category) { - QUrl url = formatURLQuery(keyword, category, "TILT"); - return getModelURL(url); -} - -// Can provide asset name or full URL to model -QString GooglePolyScriptingInterface::getModelInfo(const QString& input) { - QString name(input); - if (input.contains("poly.googleapis") || input.contains("poly.google.com")) { - QStringList list = input.split("/"); - if (input.contains("poly.googleapis")) { - name = list[4]; - } else { - name = list.last(); - } - } - QString urlString(GET_POLY_URL); - urlString = urlString.replace("model", name) + "key=" + _authCode; - qCDebug(scriptengine) << "Google URL request"; - QUrl url(urlString); - QString json = parseJSON(url, 2).toString(); - return json; -} - -int GooglePolyScriptingInterface::getRandIntInRange(int length) { - return QRandomGenerator::global()->bounded(length); -} - -QUrl GooglePolyScriptingInterface::formatURLQuery(const QString& keyword, const QString& category, const QString& format) { - QString queries; - if (!VALID_FORMATS.contains(format, Qt::CaseInsensitive) || !VALID_CATEGORIES.contains(category, Qt::CaseInsensitive)) { - return QUrl(""); - } else { - if (!keyword.isEmpty()) { - QString keywords(keyword); - keywords.replace(" ", "+"); - queries.append("&keywords=" + keywords); - } - if (!category.isEmpty()) { - queries.append("&category=" + category); - } - if (!format.isEmpty()) { - queries.append("&format=" + format); - } - QString urlString(LIST_POLY_URL + "key=" + _authCode + queries); - return QUrl(urlString); - } -} - -QString GooglePolyScriptingInterface::getModelURL(const QUrl& url) { - qCDebug(scriptengine) << "Google URL request"; - if (!url.isEmpty()) { - return parseJSON(url, 1).toString(); - } else { - qCDebug(scriptengine) << "Invalid filters were specified."; - return ""; - } -} - -// FIXME: synchronous -QByteArray GooglePolyScriptingInterface::getHTTPRequest(const QUrl& url) { - QNetworkAccessManager manager; - QNetworkReply *response = manager.get(QNetworkRequest(url)); - QEventLoop event; - connect(response, SIGNAL(finished()), &event, SLOT(quit())); - event.exec(); - - return response->readAll(); -} - -// 0 = asset list, 1 = model from asset list, 2 = specific model -QVariant GooglePolyScriptingInterface::parseJSON(const QUrl& url, int fileType) { - QByteArray jsonString = getHTTPRequest(url); - QJsonDocument doc = QJsonDocument::fromJson(jsonString); - QJsonObject obj = doc.object(); - if (obj.isEmpty()) { - qCDebug(scriptengine) << "Assets with specified filters not found"; - return ""; - } - if (obj.keys().first() == "error") { - QString error = obj.value("error").toObject().value("message").toString(); - qCDebug(scriptengine) << error; - return ""; - } - if (fileType == 0 || fileType == 1) { - QJsonArray arr = obj.value("assets").toArray(); - // return model url - if (fileType == 1) { - int random = getRandIntInRange(arr.size()); - QJsonObject json = arr.at(random).toObject(); - // nested JSONs - return json.value("formats").toArray().at(0).toObject().value("root").toObject().value("url"); - } - // return whole asset list - return QJsonDocument(arr); - // return specific object - } else { - return jsonString; - } -} diff --git a/interface/src/scripting/GooglePolyScriptingInterface.h b/interface/src/scripting/GooglePolyScriptingInterface.h deleted file mode 100644 index 693b07f18e..0000000000 --- a/interface/src/scripting/GooglePolyScriptingInterface.h +++ /dev/null @@ -1,116 +0,0 @@ -// -// GooglePolyScriptingInterface.h -// interface/src/scripting -// -// Created by Elisa Lupin-Jimenez on 12/3/2017. -// Copyright 2017 High Fidelity, Inc. -// -// 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_GooglePolyScriptingInterface_h -#define hifi_GooglePolyScriptingInterface_h - -#include -#include - -/*@jsdoc - * The GooglePoly API allows you to interact with Google Poly models direct from inside High Fidelity. - * @namespace GooglePoly - * - * @hifi-interface - * @hifi-client-entity - * @hifi-avatar - */ - -class GooglePolyScriptingInterface : public QObject, public Dependency { - Q_OBJECT - -public: - GooglePolyScriptingInterface(); - -public slots: - - /*@jsdoc - * @function GooglePoly.setAPIKey - * @param {string} key - */ - void setAPIKey(const QString& key); - - /*@jsdoc - * @function GooglePoly.getAssetList - * @param {string} keyword - * @param {string} category - * @param {string} format - * @returns {string} - */ - QString getAssetList(const QString& keyword, const QString& category, const QString& format); - - /*@jsdoc - * @function GooglePoly.getFBX - * @param {string} keyword - * @param {string} category - * @returns {string} - */ - QString getFBX(const QString& keyword, const QString& category); - - /*@jsdoc - * @function GooglePoly.getOBJ - * @param {string} keyword - * @param {string} category - * @returns {string} - */ - QString getOBJ(const QString& keyword, const QString& category); - - /*@jsdoc - * @function GooglePoly.getBlocks - * @param {string} keyword - * @param {string} category - * @returns {string} - */ - QString getBlocks(const QString& keyword, const QString& category); - - /*@jsdoc - * @function GooglePoly.getGLTF - * @param {string} keyword - * @param {string} category - * @returns {string} - */ - QString getGLTF(const QString& keyword, const QString& category); - - /*@jsdoc - * @function GooglePoly.getGLTF2 - * @param {string} keyword - * @param {string} category - * @returns {string} - */ - QString getGLTF2(const QString& keyword, const QString& category); - - /*@jsdoc - * @function GooglePoly.getTilt - * @param {string} keyword - * @param {string} category - * @returns {string} - */ - QString getTilt(const QString& keyword, const QString& category); - - /*@jsdoc - * @function GooglePoly.getModelInfo - * @param {string} input - * @returns {string} - */ - QString getModelInfo(const QString& input); - -private: - QString _authCode; - - QUrl formatURLQuery(const QString& keyword, const QString& category, const QString& format); - QString getModelURL(const QUrl& url); - QByteArray getHTTPRequest(const QUrl& url); - QVariant parseJSON(const QUrl& url, int fileType); - int getRandIntInRange(int length); - -}; - -#endif // hifi_GooglePolyScriptingInterface_h From ff50d6c2cc35de3543328d605fae0c443fd02c99 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Mon, 5 Aug 2024 16:54:19 -0700 Subject: [PATCH 074/109] automated entity property serialization --- assignment-client/CMakeLists.txt | 1 + cmake/macros/GenerateEntityProperties.cmake | 548 ++ cmake/macros/IncludeHifiLibraryHeaders.cmake | 4 + cmake/macros/SetupHifiLibrary.cmake | 7 +- cmake/macros/SetupHifiProject.cmake | 2 +- interface/CMakeLists.txt | 1 + interface/src/Application.cpp | 6 +- interface/src/raypick/ParabolaPointer.h | 2 +- libraries/entities-renderer/CMakeLists.txt | 1 + .../src/RenderableEntityItem.cpp | 2 +- .../src/RenderablePolyVoxEntityItem.cpp | 26 +- .../src/RenderablePolyVoxEntityItem.h | 2 +- .../src/RenderableShapeEntityItem.cpp | 8 +- .../src/RenderableShapeEntityItem.h | 2 +- .../src/RenderableWebEntityItem.cpp | 2 +- .../src/RenderableZoneEntityItem.cpp | 14 +- libraries/entities/CMakeLists.txt | 3 + .../src/AmbientLightPropertyGroup.cpp | 177 - .../src/AmbientLightPropertyGroup.cpp.in | 139 + .../entities/src/AmbientLightPropertyGroup.h | 98 - .../src/AmbientLightPropertyGroup.h.in | 56 + .../src/AmbientOcclusionPropertyGroup.cpp | 300 - .../src/AmbientOcclusionPropertyGroup.cpp.in | 149 + .../src/AmbientOcclusionPropertyGroup.h | 108 - .../src/AmbientOcclusionPropertyGroup.h.in | 58 + .../entities/src/AnimationPropertyGroup.cpp | 410 -- .../src/AnimationPropertyGroup.cpp.in | 261 + .../entities/src/AnimationPropertyGroup.h | 104 - .../entities/src/AnimationPropertyGroup.h.in | 52 + libraries/entities/src/BloomPropertyGroup.cpp | 163 - .../entities/src/BloomPropertyGroup.cpp.in | 134 + libraries/entities/src/BloomPropertyGroup.h | 96 - .../entities/src/BloomPropertyGroup.h.in | 52 + .../src/{EntityItem.cpp => EntityItem.cpp.in} | 938 +-- .../src/{EntityItem.h => EntityItem.h.in} | 186 +- .../src/EntityItemGroupProperties.txt | 101 + .../entities/src/EntityItemProperties.cpp | 5456 ----------------- .../entities/src/EntityItemProperties.cpp.in | 1446 +++++ libraries/entities/src/EntityItemProperties.h | 739 --- .../entities/src/EntityItemProperties.h.in | 289 + .../entities/src/EntityItemProperties.txt | 268 + .../src/EntityItemPropertiesDefaults.h | 2 + .../entities/src/EntityItemPropertiesDocs.cpp | 1004 +++ .../entities/src/EntityItemPropertiesMacros.h | 182 +- libraries/entities/src/EntityPropertyFlags.h | 14 - .../entities/src/EntityScriptingInterface.cpp | 2 +- libraries/entities/src/EntityTree.cpp | 4 +- ...oEntityItem.cpp => GizmoEntityItem.cpp.in} | 86 +- ...GizmoEntityItem.h => GizmoEntityItem.h.in} | 27 +- libraries/entities/src/GrabPropertyGroup.cpp | 354 -- .../entities/src/GrabPropertyGroup.cpp.in | 140 + libraries/entities/src/GrabPropertyGroup.h | 145 - libraries/entities/src/GrabPropertyGroup.h.in | 83 + libraries/entities/src/GridEntityItem.cpp | 203 - libraries/entities/src/GridEntityItem.cpp.in | 132 + libraries/entities/src/GridEntityItem.h | 77 - libraries/entities/src/GridEntityItem.h.in | 39 + libraries/entities/src/HazePropertyGroup.cpp | 366 -- .../entities/src/HazePropertyGroup.cpp.in | 134 + libraries/entities/src/HazePropertyGroup.h | 147 - libraries/entities/src/HazePropertyGroup.h.in | 88 + libraries/entities/src/ImageEntityItem.cpp | 227 - libraries/entities/src/ImageEntityItem.cpp.in | 107 + libraries/entities/src/ImageEntityItem.h | 81 - libraries/entities/src/ImageEntityItem.h.in | 38 + .../entities/src/KeyLightPropertyGroup.cpp | 235 - .../entities/src/KeyLightPropertyGroup.cpp.in | 143 + .../entities/src/KeyLightPropertyGroup.h | 112 - .../entities/src/KeyLightPropertyGroup.h.in | 66 + ...tEntityItem.cpp => LightEntityItem.cpp.in} | 155 +- ...LightEntityItem.h => LightEntityItem.h.in} | 46 +- ...neEntityItem.cpp => LineEntityItem.cpp.in} | 133 +- .../{LineEntityItem.h => LineEntityItem.h.in} | 35 +- libraries/entities/src/MaterialEntityItem.cpp | 290 - .../entities/src/MaterialEntityItem.cpp.in | 132 + libraries/entities/src/MaterialEntityItem.h | 119 - .../entities/src/MaterialEntityItem.h.in | 42 + ...lEntityItem.cpp => ModelEntityItem.cpp.in} | 284 +- ...ModelEntityItem.h => ModelEntityItem.h.in} | 97 +- ...em.cpp => ParticleEffectEntityItem.cpp.in} | 344 +- ...tyItem.h => ParticleEffectEntityItem.h.in} | 145 +- ...tityItem.cpp => PolyLineEntityItem.cpp.in} | 180 +- ...neEntityItem.h => PolyLineEntityItem.h.in} | 63 +- ...ntityItem.cpp => PolyVoxEntityItem.cpp.in} | 211 +- ...VoxEntityItem.h => PolyVoxEntityItem.h.in} | 145 +- .../ProceduralParticleEffectEntityItem.cpp | 174 - .../ProceduralParticleEffectEntityItem.cpp.in | 93 + .../src/ProceduralParticleEffectEntityItem.h | 88 - .../ProceduralParticleEffectEntityItem.h.in | 47 + libraries/entities/src/PropertyGroup.h | 8 - libraries/entities/src/PulsePropertyGroup.cpp | 252 - .../entities/src/PulsePropertyGroup.cpp.in | 183 + libraries/entities/src/PulsePropertyGroup.h | 100 - .../entities/src/PulsePropertyGroup.h.in | 55 + .../entities/src/RingGizmoPropertyGroup.cpp | 492 -- .../src/RingGizmoPropertyGroup.cpp.in | 159 + .../entities/src/RingGizmoPropertyGroup.h | 139 - .../entities/src/RingGizmoPropertyGroup.h.in | 76 + ...eEntityItem.cpp => ShapeEntityItem.cpp.in} | 253 +- ...ShapeEntityItem.h => ShapeEntityItem.h.in} | 66 +- .../entities/src/SkyboxPropertyGroup.cpp | 154 - .../entities/src/SkyboxPropertyGroup.cpp.in | 138 + libraries/entities/src/SkyboxPropertyGroup.h | 94 - .../entities/src/SkyboxPropertyGroup.h.in | 53 + ...dEntityItem.cpp => SoundEntityItem.cpp.in} | 103 +- libraries/entities/src/SoundEntityItem.h | 102 - libraries/entities/src/SoundEntityItem.h.in | 52 + libraries/entities/src/TextEntityItem.cpp | 413 -- libraries/entities/src/TextEntityItem.cpp.in | 117 + libraries/entities/src/TextEntityItem.h | 128 - libraries/entities/src/TextEntityItem.h.in | 48 + .../entities/src/TonemappingPropertyGroup.cpp | 167 - .../src/TonemappingPropertyGroup.cpp.in | 151 + .../entities/src/TonemappingPropertyGroup.h | 87 - .../src/TonemappingPropertyGroup.h.in | 45 + libraries/entities/src/WebEntityItem.cpp | 322 - libraries/entities/src/WebEntityItem.cpp.in | 145 + libraries/entities/src/WebEntityItem.h | 107 - libraries/entities/src/WebEntityItem.h.in | 50 + .../entities/src/ZoneAudioPropertyGroup.cpp | 195 - .../src/ZoneAudioPropertyGroup.cpp.in | 132 + .../entities/src/ZoneAudioPropertyGroup.h | 98 - .../entities/src/ZoneAudioPropertyGroup.h.in | 52 + libraries/entities/src/ZoneEntityItem.cpp | 533 -- libraries/entities/src/ZoneEntityItem.cpp.in | 348 ++ .../{ZoneEntityItem.h => ZoneEntityItem.h.in} | 86 +- libraries/networking/src/udt/PacketHeaders.h | 1 + libraries/octree/src/OctreePacketData.h | 2 + libraries/physics/CMakeLists.txt | 1 + libraries/render-utils/src/GeometryCache.h | 2 +- libraries/shared/src/EntityShape.cpp | 36 + libraries/shared/src/EntityShape.h | 63 + 132 files changed, 8666 insertions(+), 16614 deletions(-) create mode 100644 cmake/macros/GenerateEntityProperties.cmake delete mode 100644 libraries/entities/src/AmbientLightPropertyGroup.cpp create mode 100644 libraries/entities/src/AmbientLightPropertyGroup.cpp.in delete mode 100644 libraries/entities/src/AmbientLightPropertyGroup.h create mode 100644 libraries/entities/src/AmbientLightPropertyGroup.h.in delete mode 100644 libraries/entities/src/AmbientOcclusionPropertyGroup.cpp create mode 100644 libraries/entities/src/AmbientOcclusionPropertyGroup.cpp.in delete mode 100644 libraries/entities/src/AmbientOcclusionPropertyGroup.h create mode 100644 libraries/entities/src/AmbientOcclusionPropertyGroup.h.in delete mode 100644 libraries/entities/src/AnimationPropertyGroup.cpp create mode 100644 libraries/entities/src/AnimationPropertyGroup.cpp.in delete mode 100644 libraries/entities/src/AnimationPropertyGroup.h create mode 100644 libraries/entities/src/AnimationPropertyGroup.h.in delete mode 100644 libraries/entities/src/BloomPropertyGroup.cpp create mode 100644 libraries/entities/src/BloomPropertyGroup.cpp.in delete mode 100644 libraries/entities/src/BloomPropertyGroup.h create mode 100644 libraries/entities/src/BloomPropertyGroup.h.in rename libraries/entities/src/{EntityItem.cpp => EntityItem.cpp.in} (76%) rename libraries/entities/src/{EntityItem.h => EntityItem.h.in} (79%) create mode 100644 libraries/entities/src/EntityItemGroupProperties.txt delete mode 100644 libraries/entities/src/EntityItemProperties.cpp create mode 100644 libraries/entities/src/EntityItemProperties.cpp.in delete mode 100644 libraries/entities/src/EntityItemProperties.h create mode 100644 libraries/entities/src/EntityItemProperties.h.in create mode 100644 libraries/entities/src/EntityItemProperties.txt create mode 100644 libraries/entities/src/EntityItemPropertiesDocs.cpp rename libraries/entities/src/{GizmoEntityItem.cpp => GizmoEntityItem.cpp.in} (77%) rename libraries/entities/src/{GizmoEntityItem.h => GizmoEntityItem.h.in} (54%) delete mode 100644 libraries/entities/src/GrabPropertyGroup.cpp create mode 100644 libraries/entities/src/GrabPropertyGroup.cpp.in delete mode 100644 libraries/entities/src/GrabPropertyGroup.h create mode 100644 libraries/entities/src/GrabPropertyGroup.h.in delete mode 100644 libraries/entities/src/GridEntityItem.cpp create mode 100644 libraries/entities/src/GridEntityItem.cpp.in delete mode 100644 libraries/entities/src/GridEntityItem.h create mode 100644 libraries/entities/src/GridEntityItem.h.in delete mode 100644 libraries/entities/src/HazePropertyGroup.cpp create mode 100644 libraries/entities/src/HazePropertyGroup.cpp.in delete mode 100644 libraries/entities/src/HazePropertyGroup.h create mode 100644 libraries/entities/src/HazePropertyGroup.h.in delete mode 100644 libraries/entities/src/ImageEntityItem.cpp create mode 100644 libraries/entities/src/ImageEntityItem.cpp.in delete mode 100644 libraries/entities/src/ImageEntityItem.h create mode 100644 libraries/entities/src/ImageEntityItem.h.in delete mode 100644 libraries/entities/src/KeyLightPropertyGroup.cpp create mode 100644 libraries/entities/src/KeyLightPropertyGroup.cpp.in delete mode 100644 libraries/entities/src/KeyLightPropertyGroup.h create mode 100644 libraries/entities/src/KeyLightPropertyGroup.h.in rename libraries/entities/src/{LightEntityItem.cpp => LightEntityItem.cpp.in} (65%) rename libraries/entities/src/{LightEntityItem.h => LightEntityItem.h.in} (51%) rename libraries/entities/src/{LineEntityItem.cpp => LineEntityItem.cpp.in} (68%) rename libraries/entities/src/{LineEntityItem.h => LineEntityItem.h.in} (57%) delete mode 100644 libraries/entities/src/MaterialEntityItem.cpp create mode 100644 libraries/entities/src/MaterialEntityItem.cpp.in delete mode 100644 libraries/entities/src/MaterialEntityItem.h create mode 100644 libraries/entities/src/MaterialEntityItem.h.in rename libraries/entities/src/{ModelEntityItem.cpp => ModelEntityItem.cpp.in} (69%) rename libraries/entities/src/{ModelEntityItem.h => ModelEntityItem.h.in} (61%) rename libraries/entities/src/{ParticleEffectEntityItem.cpp => ParticleEffectEntityItem.cpp.in} (68%) rename libraries/entities/src/{ParticleEffectEntityItem.h => ParticleEffectEntityItem.h.in} (67%) rename libraries/entities/src/{PolyLineEntityItem.cpp => PolyLineEntityItem.cpp.in} (59%) rename libraries/entities/src/{PolyLineEntityItem.h => PolyLineEntityItem.h.in} (50%) rename libraries/entities/src/{PolyVoxEntityItem.cpp => PolyVoxEntityItem.cpp.in} (61%) rename libraries/entities/src/{PolyVoxEntityItem.h => PolyVoxEntityItem.h.in} (51%) delete mode 100644 libraries/entities/src/ProceduralParticleEffectEntityItem.cpp create mode 100644 libraries/entities/src/ProceduralParticleEffectEntityItem.cpp.in delete mode 100644 libraries/entities/src/ProceduralParticleEffectEntityItem.h create mode 100644 libraries/entities/src/ProceduralParticleEffectEntityItem.h.in delete mode 100644 libraries/entities/src/PulsePropertyGroup.cpp create mode 100644 libraries/entities/src/PulsePropertyGroup.cpp.in delete mode 100644 libraries/entities/src/PulsePropertyGroup.h create mode 100644 libraries/entities/src/PulsePropertyGroup.h.in delete mode 100644 libraries/entities/src/RingGizmoPropertyGroup.cpp create mode 100644 libraries/entities/src/RingGizmoPropertyGroup.cpp.in delete mode 100644 libraries/entities/src/RingGizmoPropertyGroup.h create mode 100644 libraries/entities/src/RingGizmoPropertyGroup.h.in rename libraries/entities/src/{ShapeEntityItem.cpp => ShapeEntityItem.cpp.in} (61%) rename libraries/entities/src/{ShapeEntityItem.h => ShapeEntityItem.h.in} (54%) delete mode 100644 libraries/entities/src/SkyboxPropertyGroup.cpp create mode 100644 libraries/entities/src/SkyboxPropertyGroup.cpp.in delete mode 100644 libraries/entities/src/SkyboxPropertyGroup.h create mode 100644 libraries/entities/src/SkyboxPropertyGroup.h.in rename libraries/entities/src/{SoundEntityItem.cpp => SoundEntityItem.cpp.in} (76%) delete mode 100644 libraries/entities/src/SoundEntityItem.h create mode 100644 libraries/entities/src/SoundEntityItem.h.in delete mode 100644 libraries/entities/src/TextEntityItem.cpp create mode 100644 libraries/entities/src/TextEntityItem.cpp.in delete mode 100644 libraries/entities/src/TextEntityItem.h create mode 100644 libraries/entities/src/TextEntityItem.h.in delete mode 100644 libraries/entities/src/TonemappingPropertyGroup.cpp create mode 100644 libraries/entities/src/TonemappingPropertyGroup.cpp.in delete mode 100644 libraries/entities/src/TonemappingPropertyGroup.h create mode 100644 libraries/entities/src/TonemappingPropertyGroup.h.in delete mode 100644 libraries/entities/src/WebEntityItem.cpp create mode 100644 libraries/entities/src/WebEntityItem.cpp.in delete mode 100644 libraries/entities/src/WebEntityItem.h create mode 100644 libraries/entities/src/WebEntityItem.h.in delete mode 100644 libraries/entities/src/ZoneAudioPropertyGroup.cpp create mode 100644 libraries/entities/src/ZoneAudioPropertyGroup.cpp.in delete mode 100644 libraries/entities/src/ZoneAudioPropertyGroup.h create mode 100644 libraries/entities/src/ZoneAudioPropertyGroup.h.in delete mode 100644 libraries/entities/src/ZoneEntityItem.cpp create mode 100644 libraries/entities/src/ZoneEntityItem.cpp.in rename libraries/entities/src/{ZoneEntityItem.h => ZoneEntityItem.h.in} (55%) create mode 100644 libraries/shared/src/EntityShape.cpp create mode 100644 libraries/shared/src/EntityShape.h diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index c8e26f5f46..a0ae90ca4d 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -22,6 +22,7 @@ link_hifi_libraries( material-networking model-networking ktx shaders ) include_hifi_library_headers(procedural) +include_hifi_library_headers(entities) add_crashpad() target_breakpad() diff --git a/cmake/macros/GenerateEntityProperties.cmake b/cmake/macros/GenerateEntityProperties.cmake new file mode 100644 index 0000000000..55fef26b3c --- /dev/null +++ b/cmake/macros/GenerateEntityProperties.cmake @@ -0,0 +1,548 @@ +# +# GenerateEntityProperites.cmake +# +# Created by HifiExperiments on 7/21/24 +# Copyright 2024 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 +# + +macro(CAPITALIZE_FIRST_LETTER) + string(SUBSTRING ${ARGV0} 0 1 FIRST_LETTER) + string(TOUPPER ${FIRST_LETTER} FIRST_LETTER) + string(REGEX REPLACE "^.(.*)" "${FIRST_LETTER}\\1" CAPITALIZE_RESULT "${ARGV0}") +endmacro() + +macro(GENERATE_ENTITY_PROPERTIES) + message(STATUS "Entity property processing start") + + set(ENTITY_ITEM_PROPERTY_DEFINES "") + set(ENTITY_ITEM_PROPERTY_INCLUDES "#include \"EntityItem.h\"\n") + set(ENTITY_ITEM_PROPERTY_FRIENDS "\tfriend class EntityItem;\n") + set(ENTITY_ITEM_PROPERTY_STATIC_GROUPS "") + set(ENTITY_ITEM_PROPERTY_DEBUG_DUMP_GROUPS "") + set(ENTITY_ITEM_PROPERTY_CHANGED_PROPERTIES "") + set(ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "") + set(ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT "") + set(ENTITY_ITEM_PROPERTY_MERGE "") + set(ENTITY_ITEM_PROPERTY_ADD_TO_MAP "") + set(ENTITY_ITEM_PROPERTY_APPEND "") + set(ENTITY_ITEM_PROPERTY_READ "") + set(ENTITY_ITEM_PROPERTY_MARK_CHANGED "") + set(ENTITY_ITEM_PROPERTY_LIST_CHANGED "") + set(ENTITY_ITEM_PROPERTY_DEBUG "") + set(CURRENT_TYPE "Base") + list(APPEND ENTITY_TYPES ${CURRENT_TYPE}) + + set(NON_REF_TYPES "bool" "int" "float" "uint8_t" "uint16_t" "quint16" "uint32_t" "quint32" "quint64") + set(BASE_ENTITY_TYPES "Core" "Physics" "Cloning" "Scripts" "LocalProps" "Common") + set(TYPED_SCRIPT_CONVERT "u8vec3Color" "vec3Color" "qVectorVec3Color") + set(COMMON_PROPS false) + set(LOCAL_PROPS false) + set(NEEDS_CLOSING_BRACE false) + + file(STRINGS src/EntityItemProperties.txt ENTITY_PROPERTIES_FILE) + while(ENTITY_PROPERTIES_FILE) + list(POP_FRONT ENTITY_PROPERTIES_FILE LINE) + if(NOT LINE MATCHES ".*:.*") + if(LINE STREQUAL "Common") + set(COMMON_PROPS true) + else() + set(COMMON_PROPS false) + endif() + if(LINE STREQUAL "LocalProps") + set(LOCAL_PROPS true) + else() + set(LOCAL_PROPS false) + endif() + string(CONCAT ENTITY_ITEM_PROPERTY_DEFINES "${ENTITY_ITEM_PROPERTY_DEFINES}" "\t// ${LINE}\n") + string(CONCAT ENTITY_ITEM_PROPERTY_CHANGED_PROPERTIES "${ENTITY_ITEM_PROPERTY_CHANGED_PROPERTIES}" "\t// ${LINE}\n") + if(NEEDS_CLOSING_BRACE) + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\t}\n") + string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t\t\t}\n") + string(CONCAT ENTITY_ITEM_PROPERTY_READ "${ENTITY_ITEM_PROPERTY_READ}" "\t}\n") + endif() + if(NOT COMMON_PROPS) + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\t// ${LINE}\n") + string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t\t\t// ${LINE}\n") + string(CONCAT ENTITY_ITEM_PROPERTY_READ "${ENTITY_ITEM_PROPERTY_READ}" "\t// ${LINE}\n") + endif() + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT}" "\t// ${LINE}\n") + string(CONCAT ENTITY_ITEM_PROPERTY_MERGE "${ENTITY_ITEM_PROPERTY_MERGE}" "\t// ${LINE}\n") + string(CONCAT ENTITY_ITEM_PROPERTY_ADD_TO_MAP "${ENTITY_ITEM_PROPERTY_ADD_TO_MAP}" "\t\t// ${LINE}\n") + string(CONCAT ENTITY_ITEM_PROPERTY_MARK_CHANGED "${ENTITY_ITEM_PROPERTY_MARK_CHANGED}" "\t// ${LINE}\n") + string(CONCAT ENTITY_ITEM_PROPERTY_LIST_CHANGED "${ENTITY_ITEM_PROPERTY_LIST_CHANGED}" "\t// ${LINE}\n") + + if(NOT ${LINE} IN_LIST BASE_ENTITY_TYPES) + string(CONCAT ENTITY_ITEM_PROPERTY_INCLUDES "${ENTITY_ITEM_PROPERTY_INCLUDES}" "#include \"${LINE}EntityItem.h\"\n") + string(CONCAT ENTITY_ITEM_PROPERTY_FRIENDS "${ENTITY_ITEM_PROPERTY_FRIENDS}" "\tfriend class ${LINE}EntityItem;\n") + if(LINE STREQUAL "Shape") + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\tif (_type == EntityTypes::Box) {\n\t\tCOPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, QString(\"Box\"));\n\t}\n") + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\tif (_type == EntityTypes::Sphere) {\n\t\tCOPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, QString(\"Sphere\"));\n\t}\n") + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\tif (_type == EntityTypes::Box || _type == EntityTypes::Sphere || _type == EntityTypes::Shape) {\n") + string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t\t\tif (properties.getType() == EntityTypes::Box || properties.getType() == EntityTypes::Sphere || properties.getType() == EntityTypes::Shape) {\n") + string(CONCAT ENTITY_ITEM_PROPERTY_READ "${ENTITY_ITEM_PROPERTY_READ}" "\tif (properties.getType() == EntityTypes::Box || properties.getType() == EntityTypes::Sphere || properties.getType() == EntityTypes::Shape) {\n") + else() + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\tif (_type == EntityTypes::${LINE}) {\n") + string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t\t\tif (properties.getType() == EntityTypes::${LINE}) {\n") + string(CONCAT ENTITY_ITEM_PROPERTY_READ "${ENTITY_ITEM_PROPERTY_READ}" "\tif (properties.getType() == EntityTypes::${LINE}) {\n") + endif() + set(NEEDS_CLOSING_BRACE true) + endif() + + if (NEEDS_CLOSING_BRACE) + set(CURRENT_TYPE ${LINE}) + list(APPEND ENTITY_TYPES ${LINE}) + endif() + + if (NOT COMMON_PROPS) + string(CONCAT ${CURRENT_TYPE}_REQUESTED_PROPS "${${CURRENT_TYPE}_REQUESTED_PROPS}" "\t// ${LINE}\n") + string(CONCAT ${CURRENT_TYPE}_ENTITY_APPEND "${${CURRENT_TYPE}_ENTITY_APPEND}" "\t\t// ${LINE}\n") + string(CONCAT ${CURRENT_TYPE}_ENTITY_READ "${${CURRENT_TYPE}_ENTITY_READ}" "\t// ${LINE}\n") + string(CONCAT ${CURRENT_TYPE}_ENTITY_COPY_TO "${${CURRENT_TYPE}_ENTITY_COPY_TO}" "\t// ${LINE}\n") + if (NOT LOCAL_PROPS) + string(CONCAT ${CURRENT_TYPE}_ENTITY_SET_FROM "${${CURRENT_TYPE}_ENTITY_SET_FROM}" "\t// ${LINE}\n") + endif() + endif() + elseif(LINE MATCHES "group:.*,") + string(REGEX MATCH ".*group:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_GROUP ${LINE}) + set(ENTITY_PROPERTY_GROUP ${CMAKE_MATCH_1}) + CAPITALIZE_FIRST_LETTER(${ENTITY_PROPERTY_GROUP}) + set(ENTITY_PROPERTY_GROUP_CAPS ${CAPITALIZE_RESULT}) + string(REGEX MATCH ".*type:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_GROUP_TYPE ${LINE}) + if (CMAKE_MATCH_1) + set(ENTITY_PROPERTY_GROUP_TYPE "${CMAKE_MATCH_1}PropertyGroup") + else() + set(ENTITY_PROPERTY_GROUP_TYPE "${ENTITY_PROPERTY_GROUP_CAPS}PropertyGroup") + endif() + + if(NOT LINE MATCHES ".*common( |,).*") + string(CONCAT ENTITY_ITEM_PROPERTY_DEFINES "${ENTITY_ITEM_PROPERTY_DEFINES}" "\tDEFINE_PROPERTY_GROUP(${ENTITY_PROPERTY_GROUP_CAPS}, ${ENTITY_PROPERTY_GROUP}, ${ENTITY_PROPERTY_GROUP_TYPE});\n") + string(CONCAT ENTITY_ITEM_PROPERTY_INCLUDES "${ENTITY_ITEM_PROPERTY_INCLUDES}" "#include \"${ENTITY_PROPERTY_GROUP_TYPE}.h\"\n") + string(CONCAT ENTITY_ITEM_PROPERTY_STATIC_GROUPS "${ENTITY_ITEM_PROPERTY_STATIC_GROUPS}" "${ENTITY_PROPERTY_GROUP_TYPE} EntityItemProperties::_static${ENTITY_PROPERTY_GROUP_CAPS};\n") + string(CONCAT ENTITY_ITEM_PROPERTY_DEBUG_DUMP_GROUPS "${ENTITY_ITEM_PROPERTY_DEBUG_DUMP_GROUPS}" "\tget${ENTITY_PROPERTY_GROUP_CAPS}().debugDump();\n") + string(CONCAT ENTITY_ITEM_PROPERTY_CHANGED_PROPERTIES "${ENTITY_ITEM_PROPERTY_CHANGED_PROPERTIES}" "\tchangedProperties += _${ENTITY_PROPERTY_GROUP}.getChangedProperties();\n") + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT}" "\t_${ENTITY_PROPERTY_GROUP}.copyFromScriptValue(object, namesSet, _defaultSettings);\n") + string(CONCAT ENTITY_ITEM_PROPERTY_MERGE "${ENTITY_ITEM_PROPERTY_MERGE}" "\t_${ENTITY_PROPERTY_GROUP}.merge(other._${ENTITY_PROPERTY_GROUP});\n") + string(CONCAT ENTITY_ITEM_PROPERTY_ADD_TO_MAP "${ENTITY_ITEM_PROPERTY_ADD_TO_MAP}" "\t\t${ENTITY_PROPERTY_GROUP_TYPE}::addPropertyMap(_propertyInfos, _enumsToPropertyStrings);\n") + string(CONCAT ENTITY_ITEM_PROPERTY_MARK_CHANGED "${ENTITY_ITEM_PROPERTY_MARK_CHANGED}" "\t_${ENTITY_PROPERTY_GROUP}.markAllChanged();\n") + string(CONCAT ENTITY_ITEM_PROPERTY_LIST_CHANGED "${ENTITY_ITEM_PROPERTY_LIST_CHANGED}" "\tget${ENTITY_PROPERTY_GROUP_CAPS}().listChangedProperties(out);\n") + endif() + if(NEEDS_CLOSING_BRACE) + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\t") + string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t") + string(CONCAT ENTITY_ITEM_PROPERTY_READ "${ENTITY_ITEM_PROPERTY_READ}" "\t") + endif() + if(NOT COMMON_PROPS) + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\t_${ENTITY_PROPERTY_GROUP}.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity);\n") + string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t\t\t_static${ENTITY_PROPERTY_GROUP_CAPS}.setProperties(properties);\n") + if (NEEDS_CLOSING_BRACE) + string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t") + endif() + string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t\t\t_static${ENTITY_PROPERTY_GROUP_CAPS}.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState);\n") + string(CONCAT ENTITY_ITEM_PROPERTY_READ "${ENTITY_ITEM_PROPERTY_READ}" "\tproperties.get${ENTITY_PROPERTY_GROUP_CAPS}().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);\n") + string(CONCAT ${CURRENT_TYPE}_ENTITY_PROPS "${${CURRENT_TYPE}_ENTITY_PROPS}" "\t${ENTITY_PROPERTY_GROUP_TYPE} _${ENTITY_PROPERTY_GROUP}Properties;\n") + string(CONCAT ${CURRENT_TYPE}_REQUESTED_PROPS "${${CURRENT_TYPE}_REQUESTED_PROPS}" "\trequestedProperties += _${ENTITY_PROPERTY_GROUP}Properties.getEntityProperties(params);\n") + string(CONCAT ${CURRENT_TYPE}_ENTITY_APPEND "${${CURRENT_TYPE}_ENTITY_APPEND}" "\twithReadLock([&] {\n\t\t_${ENTITY_PROPERTY_GROUP}Properties.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState);\n\t});\n") + if (NOT LINE MATCHES ".*customVariableRead( |,).*") + string(CONCAT ${CURRENT_TYPE}_ENTITY_READ "${${CURRENT_TYPE}_ENTITY_READ}" "\twithWriteLock([&] {\n\t\tint bytesFrom${ENTITY_PROPERTY_GROUP_CAPS} = _${ENTITY_PROPERTY_GROUP}Properties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, somethingChanged);\n\t\tbytesRead += bytesFrom${ENTITY_PROPERTY_GROUP_CAPS};\n\t\tdataAt += bytesFrom${ENTITY_PROPERTY_GROUP_CAPS};\n\t});\n") + endif() + string(CONCAT ${CURRENT_TYPE}_ENTITY_COPY_TO "${${CURRENT_TYPE}_ENTITY_COPY_TO}" "\twithReadLock([&] {\n\t\t_${ENTITY_PROPERTY_GROUP}Properties.getProperties(properties);\n\t});\n") + if (NOT LINE MATCHES ".*customVariableSetFrom( |,).*") + if (LINE MATCHES ".*recordChange( |,).*") + string(CONCAT ${CURRENT_TYPE}_ENTITY_SET_FROM "${${CURRENT_TYPE}_ENTITY_SET_FROM}" "\twithWriteLock([&] {\n\t\t_${ENTITY_PROPERTY_GROUP}PropertiesChanged |= _${ENTITY_PROPERTY_GROUP}Properties.setProperties(properties);\n\t});\n") + elseif (LINE MATCHES ".*renderUpdateOnSet( |,).*") + string(CONCAT ${CURRENT_TYPE}_ENTITY_SET_FROM "${${CURRENT_TYPE}_ENTITY_SET_FROM}" "\twithWriteLock([&] {\n\t\tbool ${ENTITY_PROPERTY_GROUP}PropertiesChanged = _${ENTITY_PROPERTY_GROUP}Properties.setProperties(properties);\n\t\tsomethingChanged |= ${ENTITY_PROPERTY_GROUP}PropertiesChanged;\n\t\t_needsRenderUpdate |= ${ENTITY_PROPERTY_GROUP}PropertiesChanged;\n\t});\n") + else() + string(CONCAT ${CURRENT_TYPE}_ENTITY_SET_FROM "${${CURRENT_TYPE}_ENTITY_SET_FROM}" "\twithWriteLock([&] {\n\t\tbool ${ENTITY_PROPERTY_GROUP}PropertiesChanged = _${ENTITY_PROPERTY_GROUP}Properties.setProperties(properties);\n\t\tsomethingChanged |= ${ENTITY_PROPERTY_GROUP}PropertiesChanged;\n\t});\n") + endif() + endif() + string(CONCAT ${CURRENT_TYPE}_ENTITY_DEBUG "${${CURRENT_TYPE}_ENTITY_DEBUG}" "\t_${ENTITY_PROPERTY_GROUP}Properties.debugDump();\n") + endif() + else() + string(REGEX MATCH ".*enum:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_ENUM ${LINE}) + string(CONCAT ENTITY_PROPERTY_ENUM "PROP_" ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*prop:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_NAME ${LINE}) + set(ENTITY_PROPERTY_NAME ${CMAKE_MATCH_1}) + CAPITALIZE_FIRST_LETTER(${ENTITY_PROPERTY_NAME}) + set(ENTITY_PROPERTY_NAME_CAPS ${CAPITALIZE_RESULT}) + string(REGEX MATCH ".*type:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_TYPE ${LINE}) + set(ENTITY_PROPERTY_TYPE ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*default:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_DEFAULT ${LINE}) + set(ENTITY_PROPERTY_DEFAULT ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*min:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_MIN ${LINE}) + set(ENTITY_PROPERTY_MIN ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*max:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_MAX ${LINE}) + set(ENTITY_PROPERTY_MAX ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*fromScriptType:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_FROM_SCRIPT_TYPE ${LINE}) + set(ENTITY_PROPERTY_FROM_SCRIPT_TYPE ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*getter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_GETTER ${LINE}) + set(ENTITY_PROPERTY_GETTER ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*setter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_SETTER ${LINE}) + set(ENTITY_PROPERTY_SETTER ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*types:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_TYPES ${LINE}) + set(ENTITY_PROPERTY_TYPES ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*networkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_NETWORK_GETTER ${LINE}) + set(ENTITY_PROPERTY_NETWORK_GETTER ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*variableNetworkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_VARIABLE_NETWORK_GETTER ${LINE}) + set(ENTITY_VARIABLE_NETWORK_GETTER ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*variableNetworkSetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_VARIABLE_NETWORK_SETTER ${LINE}) + set(ENTITY_VARIABLE_NETWORK_SETTER ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*variableCopyGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_VARIABLE_COPY_GETTER ${LINE}) + set(ENTITY_VARIABLE_COPY_GETTER ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*variableCopySetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_VARIABLE_COPY_SETTER ${LINE}) + set(ENTITY_VARIABLE_COPY_SETTER ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*readType:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_READ_TYPE ${LINE}) + set(ENTITY_PROPERTY_READ_TYPE ${CMAKE_MATCH_1}) + if (NOT ENTITY_PROPERTY_READ_TYPE) + set(ENTITY_PROPERTY_READ_TYPE ${ENTITY_PROPERTY_TYPE}) + endif() + string(REGEX MATCH ".*debugString:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_DEBUG_STRING ${LINE}) + set(ENTITY_PROPERTY_DEBUG_STRING ${CMAKE_MATCH_1}) + if (NOT ENTITY_PROPERTY_DEBUG_STRING) + set(ENTITY_PROPERTY_DEBUG_STRING "\"\"") + endif() + string(REGEX MATCH ".*debugGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_DEBUG_GETTER ${LINE}) + set(ENTITY_PROPERTY_DEBUG_GETTER ${CMAKE_MATCH_1}) + + if(NOT LINE MATCHES ".*legacy( |,).*") + if(NOT LINE MATCHES ".*common( |,).*") + set(DEFINE_FUNC "DEFINE_PROPERTY_REF") + if(LINE MATCHES ".*enum( |,).*") + set(DEFINE_FUNC "DEFINE_PROPERTY_REF_ENUM") + elseif(${ENTITY_PROPERTY_TYPE} IN_LIST NON_REF_TYPES) + set(DEFINE_FUNC "DEFINE_PROPERTY") + elseif(LINE MATCHES ".*propertySetter( |,).*") + set(DEFINE_FUNC "DEFINE_PROPERTY_REF_WITH_SETTER") + endif() + string(CONCAT ENTITY_ITEM_PROPERTY_DEFINES "${ENTITY_ITEM_PROPERTY_DEFINES}" "\t${DEFINE_FUNC}(${ENTITY_PROPERTY_NAME_CAPS}, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_TYPE}, ${ENTITY_PROPERTY_DEFAULT});\n") + string(CONCAT ENTITY_ITEM_PROPERTY_CHANGED_PROPERTIES "${ENTITY_ITEM_PROPERTY_CHANGED_PROPERTIES}" "\tCHECK_PROPERTY_CHANGE(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NAME});\n") + if(NOT LINE MATCHES ".*noScript( |,).*") + if(LINE MATCHES ".*readOnly( |,).*") + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT}" "\tif (!honorReadOnly) {\n\t") + endif() + if(LINE MATCHES ".*enum( |,).*") + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT}" "\tCOPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_NAME_CAPS});\n") + elseif(ENTITY_PROPERTY_GETTER AND ENTITY_PROPERTY_SETTER) + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT}" "\tCOPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_FROM_SCRIPT_TYPE}, ${ENTITY_PROPERTY_SETTER}, ${ENTITY_PROPERTY_GETTER});\n") + else() + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT}" "\tCOPY_PROPERTY_FROM_QSCRIPTVALUE(${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_TYPE}, set${ENTITY_PROPERTY_NAME_CAPS});\n") + endif() + if(LINE MATCHES ".*readOnly( |,).*") + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT}" "\t}\n") + endif() + endif() + string(CONCAT ENTITY_ITEM_PROPERTY_MERGE "${ENTITY_ITEM_PROPERTY_MERGE}" "\tCOPY_PROPERTY_IF_CHANGED(${ENTITY_PROPERTY_NAME});\n") + if(ENTITY_PROPERTY_MIN AND ENTITY_PROPERTY_MAX) + string(CONCAT ENTITY_ITEM_PROPERTY_ADD_TO_MAP "${ENTITY_ITEM_PROPERTY_ADD_TO_MAP}" "\t\tADD_PROPERTY_TO_MAP_WITH_RANGE(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_MIN}, ${ENTITY_PROPERTY_MAX});\n") + else() + string(CONCAT ENTITY_ITEM_PROPERTY_ADD_TO_MAP "${ENTITY_ITEM_PROPERTY_ADD_TO_MAP}" "\t\tADD_PROPERTY_TO_MAP(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_TYPE});\n") + endif() + string(CONCAT ENTITY_ITEM_PROPERTY_MARK_CHANGED "${ENTITY_ITEM_PROPERTY_MARK_CHANGED}" "\t_${ENTITY_PROPERTY_NAME}Changed = true;\n") + string(CONCAT ENTITY_ITEM_PROPERTY_LIST_CHANGED "${ENTITY_ITEM_PROPERTY_LIST_CHANGED}" "\tif (${ENTITY_PROPERTY_NAME}Changed()) {\n\t\tout += \"${ENTITY_PROPERTY_NAME}\";\n\t}\n") + if(LINE MATCHES ".*enum( |,).*") + string(CONCAT ENTITY_ITEM_PROPERTY_DEBUG "${ENTITY_ITEM_PROPERTY_DEBUG}" "\tDEBUG_PROPERTY_IF_CHANGED(debug, properties, ${ENTITY_PROPERTY_NAME_CAPS}AsString, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_DEBUG_STRING});\n") + else() + string(CONCAT ENTITY_ITEM_PROPERTY_DEBUG "${ENTITY_ITEM_PROPERTY_DEBUG}" "\tDEBUG_PROPERTY_IF_CHANGED(debug, properties, ${ENTITY_PROPERTY_NAME_CAPS}, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_DEBUG_STRING});\n") + endif() + endif() + if(NOT LINE MATCHES ".*noScript( |,).*") + if(NEEDS_CLOSING_BRACE) + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\t") + endif() + if(NOT COMMON_PROPS) + if(ENTITY_PROPERTY_GETTER) + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\tCOPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_GETTER}());\n") + elseif(LINE MATCHES ".*enum( |,).*") + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\tCOPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NAME}, get${ENTITY_PROPERTY_NAME_CAPS}AsString());\n") + elseif(${ENTITY_PROPERTY_TYPE} IN_LIST TYPED_SCRIPT_CONVERT) + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\tCOPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_TYPE});\n") + elseif(LINE MATCHES ".*urlPermission( |,).*") + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\tCOPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NAME});\n") + else() + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\tCOPY_PROPERTY_TO_QSCRIPTVALUE(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NAME});\n") + endif() + endif() + endif() + if(NOT LINE MATCHES ".*noNetwork( |,).*") + if(NEEDS_CLOSING_BRACE) + string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t") + string(CONCAT ENTITY_ITEM_PROPERTY_READ "${ENTITY_ITEM_PROPERTY_READ}" "\t") + endif() + if(NOT COMMON_PROPS) + if(LINE MATCHES ".*enum( |,).*") + string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t\t\tAPPEND_ENTITY_PROPERTY(${ENTITY_PROPERTY_ENUM}, (uint32_t)properties.get${ENTITY_PROPERTY_NAME_CAPS}());\n") + elseif(ENTITY_PROPERTY_NETWORK_GETTER) + string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t\t\tAPPEND_ENTITY_PROPERTY(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NETWORK_GETTER});\n") + else() + string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t\t\tAPPEND_ENTITY_PROPERTY(${ENTITY_PROPERTY_ENUM}, properties.get${ENTITY_PROPERTY_NAME_CAPS}());\n") + endif() + string(CONCAT ENTITY_ITEM_PROPERTY_READ "${ENTITY_ITEM_PROPERTY_READ}" "\tREAD_ENTITY_PROPERTY_TO_PROPERTIES(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_READ_TYPE}, set${ENTITY_PROPERTY_NAME_CAPS});\n") + string(CONCAT ${CURRENT_TYPE}_REQUESTED_PROPS "${${CURRENT_TYPE}_REQUESTED_PROPS}" "\trequestedProperties += ${ENTITY_PROPERTY_ENUM};\n") + if(LINE MATCHES ".*enum( |,).*") + string(CONCAT ${CURRENT_TYPE}_ENTITY_APPEND "${${CURRENT_TYPE}_ENTITY_APPEND}" "\tAPPEND_ENTITY_PROPERTY(${ENTITY_PROPERTY_ENUM}, (uint32_t)get${ENTITY_PROPERTY_NAME_CAPS}());\n") + elseif(ENTITY_VARIABLE_NETWORK_GETTER) + string(CONCAT ${CURRENT_TYPE}_ENTITY_APPEND "${${CURRENT_TYPE}_ENTITY_APPEND}" "\tAPPEND_ENTITY_PROPERTY(${ENTITY_PROPERTY_ENUM}, ${ENTITY_VARIABLE_NETWORK_GETTER});\n") + else() + string(CONCAT ${CURRENT_TYPE}_ENTITY_APPEND "${${CURRENT_TYPE}_ENTITY_APPEND}" "\tAPPEND_ENTITY_PROPERTY(${ENTITY_PROPERTY_ENUM}, get${ENTITY_PROPERTY_NAME_CAPS}());\n") + endif() + if(NOT LINE MATCHES ".*noVariableNetworkSetter( |,).*") + if(ENTITY_VARIABLE_NETWORK_SETTER) + string(CONCAT ${CURRENT_TYPE}_ENTITY_READ "${${CURRENT_TYPE}_ENTITY_READ}" "\tREAD_ENTITY_PROPERTY(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_TYPE}, ${ENTITY_VARIABLE_NETWORK_SETTER});\n") + else() + string(CONCAT ${CURRENT_TYPE}_ENTITY_READ "${${CURRENT_TYPE}_ENTITY_READ}" "\tREAD_ENTITY_PROPERTY(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_TYPE}, set${ENTITY_PROPERTY_NAME_CAPS});\n") + endif() + endif() + endif() + endif() + + if (NOT COMMON_PROPS) + set(VARIABLE_DEFINE_FUNC "DEFINE_VARIABLE_REF") + if(LINE MATCHES ".*noGetterSetterProp( |,).*") + set(VARIABLE_DEFINE_FUNC "DEFINE_VARIABLE_NO_GETTER_SETTER") + elseif(LINE MATCHES ".*enum( |,).*") + set(VARIABLE_DEFINE_FUNC "DEFINE_VARIABLE") + elseif(${ENTITY_PROPERTY_TYPE} IN_LIST NON_REF_TYPES) + set(VARIABLE_DEFINE_FUNC "DEFINE_VARIABLE") + endif() + if(LINE MATCHES ".*basicProp( |,).*") + string(REPLACE "DEFINE_VARIABLE" "DEFINE_VARIABLE_BASIC" VARIABLE_DEFINE_FUNC ${VARIABLE_DEFINE_FUNC}) + elseif(LINE MATCHES ".*renderProp( |,).*") + string(REPLACE "DEFINE_VARIABLE" "DEFINE_VARIABLE_RENDER" VARIABLE_DEFINE_FUNC ${VARIABLE_DEFINE_FUNC}) + endif() + if(NOT LINE MATCHES ".*inherited( |,).*") + string(CONCAT ${CURRENT_TYPE}_ENTITY_PROPS "${${CURRENT_TYPE}_ENTITY_PROPS}" "\t${VARIABLE_DEFINE_FUNC}(${ENTITY_PROPERTY_NAME_CAPS}, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_TYPE}, ${ENTITY_PROPERTY_DEFAULT});\n") + endif() + + if(ENTITY_VARIABLE_COPY_GETTER) + string(CONCAT ${CURRENT_TYPE}_ENTITY_COPY_TO "${${CURRENT_TYPE}_ENTITY_COPY_TO}" "\tCOPY_ENTITY_PROPERTY_TO_PROPERTIES(${ENTITY_PROPERTY_NAME}, ${ENTITY_VARIABLE_COPY_GETTER});\n") + else() + string(CONCAT ${CURRENT_TYPE}_ENTITY_COPY_TO "${${CURRENT_TYPE}_ENTITY_COPY_TO}" "\tCOPY_ENTITY_PROPERTY_TO_PROPERTIES(${ENTITY_PROPERTY_NAME}, get${ENTITY_PROPERTY_NAME_CAPS});\n") + endif() + if (NOT LOCAL_PROPS) + if(ENTITY_VARIABLE_COPY_SETTER) + string(CONCAT ${CURRENT_TYPE}_ENTITY_SET_FROM "${${CURRENT_TYPE}_ENTITY_SET_FROM}" "\tSET_ENTITY_PROPERTY_FROM_PROPERTIES(${ENTITY_PROPERTY_NAME}, ${ENTITY_VARIABLE_COPY_SETTER});\n") + else() + string(CONCAT ${CURRENT_TYPE}_ENTITY_SET_FROM "${${CURRENT_TYPE}_ENTITY_SET_FROM}" "\tSET_ENTITY_PROPERTY_FROM_PROPERTIES(${ENTITY_PROPERTY_NAME}, set${ENTITY_PROPERTY_NAME_CAPS});\n") + endif() + endif() + if(LINE MATCHES ".*enum( |,).*") + string(CONCAT ${CURRENT_TYPE}_ENTITY_DEBUG "${${CURRENT_TYPE}_ENTITY_DEBUG}" "\tqCDebug(entities) << \" ${ENTITY_PROPERTY_NAME}:\" << (int)get${ENTITY_PROPERTY_NAME_CAPS}();\n") + elseif(ENTITY_PROPERTY_DEBUG_GETTER) + string(CONCAT ${CURRENT_TYPE}_ENTITY_DEBUG "${${CURRENT_TYPE}_ENTITY_DEBUG}" "\tqCDebug(entities) << \" ${ENTITY_PROPERTY_NAME}:\" << ${ENTITY_PROPERTY_DEBUG_GETTER};\n") + else() + string(CONCAT ${CURRENT_TYPE}_ENTITY_DEBUG "${${CURRENT_TYPE}_ENTITY_DEBUG}" "\tqCDebug(entities) << \" ${ENTITY_PROPERTY_NAME}:\" << get${ENTITY_PROPERTY_NAME_CAPS}();\n") + endif() + endif() + else() + if(NOT LINE MATCHES ".*noScript( |,).*") + string(REGEX MATCH ".*proxy:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_PROXY ${LINE}) + set(ENTITY_PROPERTY_PROXY ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*proxyType:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_PROXY_MAP_TYPE ${LINE}) + set(ENTITY_PROPERTY_PROXY_MAP_TYPE ${CMAKE_MATCH_1}) + if (NOT ENTITY_PROPERTY_PROXY_MAP_TYPE) + set(ENTITY_PROPERTY_PROXY_MAP_TYPE ${ENTITY_PROPERTY_TYPE}) + endif() + + if(NOT COMMON_PROPS) + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\tCOPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_PROXY}, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_GETTER}); // legacy support\n") + endif() + if(LINE MATCHES ".*readOnly( |,).*") + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT}" "\tif (!honorReadOnly) {\n\t") + endif() + if(LINE MATCHES ".*enum( |,).*") + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT}" "\tCOPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_TYPE}); // legacy support\n") + elseif(ENTITY_PROPERTY_GETTER AND ENTITY_PROPERTY_SETTER) + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT}" "\tCOPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_FROM_SCRIPT_TYPE}, ${ENTITY_PROPERTY_SETTER}, ${ENTITY_PROPERTY_GETTER}); // legacy support\n") + else() + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT}" "\tCOPY_PROPERTY_FROM_QSCRIPTVALUE(${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_TYPE}, set${ENTITY_PROPERTY_NAME_CAPS}); // legacy support\n") + endif() + if(LINE MATCHES ".*readOnly( |,).*") + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT}" "\t}\n") + endif() + endif() + if(ENTITY_PROPERTY_MIN AND ENTITY_PROPERTY_MAX) + string(CONCAT ENTITY_ITEM_PROPERTY_ADD_TO_MAP "${ENTITY_ITEM_PROPERTY_ADD_TO_MAP}" "\t\tADD_PROPERTY_TO_MAP_WITH_RANGE(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_PROXY_MAP_TYPE}, ${ENTITY_PROPERTY_MIN}, ${ENTITY_PROPERTY_MAX}); // legacy support\n") + else() + string(CONCAT ENTITY_ITEM_PROPERTY_ADD_TO_MAP "${ENTITY_ITEM_PROPERTY_ADD_TO_MAP}" "\t\tADD_PROPERTY_TO_MAP(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_PROXY_MAP_TYPE}); // legacy support\n") + endif() + endif() + endif() + endwhile() + + string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\t}") + string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t\t\t}") + string(CONCAT ENTITY_ITEM_PROPERTY_READ "${ENTITY_ITEM_PROPERTY_READ}" "\t}") + + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/src/EntityItemProperties.cpp.in + ${CMAKE_CURRENT_BINARY_DIR}/src/EntityItemProperties.cpp) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/src/EntityItemProperties.h.in + ${CMAKE_CURRENT_BINARY_DIR}/src/EntityItemProperties.h) + list(APPEND GENERATE_ENTITIES_LIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/src/EntityItemProperties.h ${CMAKE_CURRENT_BINARY_DIR}/src/EntityItemProperties.cpp) + + foreach(TYPE IN LISTS ENTITY_TYPES) + if(TYPE STREQUAL "Base") + set(TYPE "") + endif() + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/src/${TYPE}EntityItem.cpp.in + ${CMAKE_CURRENT_BINARY_DIR}/src/${TYPE}EntityItem.cpp) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/src/${TYPE}EntityItem.h.in + ${CMAKE_CURRENT_BINARY_DIR}/src/${TYPE}EntityItem.h) + list(APPEND GENERATE_ENTITIES_LIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/src/${TYPE}EntityItem.h ${CMAKE_CURRENT_BINARY_DIR}/src/${TYPE}EntityItem.cpp) + endforeach() + + # Group Properties + set(GROUP_TYPES "") + + file(STRINGS src/EntityItemGroupProperties.txt GROUP_PROPERTIES_FILE) + while(GROUP_PROPERTIES_FILE) + list(POP_FRONT GROUP_PROPERTIES_FILE LINE) + if(NOT LINE MATCHES ".*,") + string(REGEX MATCH ".*type:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+).*" GROUP_PROPERTY_TYPE ${LINE}) + set(GROUP_PROPERTY_TYPE ${CMAKE_MATCH_1}) + + if (GROUP_PROPERTY_TYPE) + string(REGEX MATCH "([A-Z0-9a-z_<>::\/\.\"\(\)\+]+) type:.*" CURRENT_TYPE ${LINE}) + set(CURRENT_TYPE ${CMAKE_MATCH_1}) + CAPITALIZE_FIRST_LETTER(${CURRENT_TYPE}) + set(CURRENT_TYPE_CAPS ${CAPITALIZE_RESULT}) + + list(APPEND GROUP_TYPES ${GROUP_PROPERTY_TYPE}) + else() + set(CURRENT_TYPE ${LINE}) + CAPITALIZE_FIRST_LETTER(${CURRENT_TYPE}) + set(CURRENT_TYPE_CAPS ${CAPITALIZE_RESULT}) + + list(APPEND GROUP_TYPES ${CURRENT_TYPE_CAPS}) + endif() + else() + string(REGEX MATCH ".*enum:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_ENUM ${LINE}) + string(CONCAT GROUP_PROPERTY_ENUM "PROP_" ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*prop:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_NAME ${LINE}) + set(GROUP_PROPERTY_NAME ${CMAKE_MATCH_1}) + CAPITALIZE_FIRST_LETTER(${GROUP_PROPERTY_NAME}) + set(GROUP_PROPERTY_NAME_CAPS ${CAPITALIZE_RESULT}) + string(REGEX MATCH ".*type:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_TYPE ${LINE}) + set(GROUP_PROPERTY_TYPE ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*default:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_DEFAULT ${LINE}) + set(GROUP_PROPERTY_DEFAULT ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*min:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_MIN ${LINE}) + set(GROUP_PROPERTY_MIN ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*max:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_MAX ${LINE}) + set(GROUP_PROPERTY_MAX ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*getter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_GETTER ${LINE}) + set(GROUP_PROPERTY_GETTER ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*setter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_SETTER ${LINE}) + set(GROUP_PROPERTY_SETTER ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*types:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_TYPES ${LINE}) + set(GROUP_PROPERTY_TYPES ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*networkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_NETWORK_GETTER ${LINE}) + set(GROUP_PROPERTY_NETWORK_GETTER ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*variableNetworkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_VARIABLE_NETWORK_GETTER ${LINE}) + set(GROUP_VARIABLE_NETWORK_GETTER ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*variableNetworkSetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_VARIABLE_NETWORK_SETTER ${LINE}) + set(GROUP_VARIABLE_NETWORK_SETTER ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*variableCopyGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_VARIABLE_COPY_GETTER ${LINE}) + set(GROUP_VARIABLE_COPY_GETTER ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*variableCopySetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_VARIABLE_COPY_SETTER ${LINE}) + set(GROUP_VARIABLE_COPY_SETTER ${CMAKE_MATCH_1}) + string(REGEX MATCH ".*debugGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_VARIABLE_DEBUG_GETTER ${LINE}) + set(GROUP_VARIABLE_DEBUG_GETTER ${CMAKE_MATCH_1}) + + set(DEFINE_FUNC "DEFINE_PROPERTY_REF") + if(LINE MATCHES ".*enum( |,).*") + set(DEFINE_FUNC "DEFINE_PROPERTY_REF_ENUM") + elseif(${GROUP_PROPERTY_TYPE} IN_LIST NON_REF_TYPES) + set(DEFINE_FUNC "DEFINE_PROPERTY") + elseif(LINE MATCHES ".*propertySetter( |,).*") + set(DEFINE_FUNC "DEFINE_PROPERTY_REF_WITH_SETTER") + endif() + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_PROPS "${${CURRENT_TYPE_CAPS}_GROUP_PROPS}" "\t${DEFINE_FUNC}(${GROUP_PROPERTY_NAME_CAPS}, ${GROUP_PROPERTY_NAME}, ${GROUP_PROPERTY_TYPE}, ${GROUP_PROPERTY_DEFAULT});\n") + + if(GROUP_PROPERTY_GETTER) + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_COPY_TO_SCRIPT "${${CURRENT_TYPE_CAPS}_GROUP_COPY_TO_SCRIPT}" "\tCOPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(${GROUP_PROPERTY_ENUM}, ${CURRENT_TYPE_CAPS}, ${CURRENT_TYPE}, ${GROUP_PROPERTY_NAME_CAPS}, ${GROUP_PROPERTY_NAME});\n") + elseif(LINE MATCHES ".*enum( |,).*") + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_COPY_TO_SCRIPT "${${CURRENT_TYPE_CAPS}_GROUP_COPY_TO_SCRIPT}" "\tCOPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(${GROUP_PROPERTY_ENUM}, ${CURRENT_TYPE_CAPS}, ${CURRENT_TYPE}, ${GROUP_PROPERTY_NAME_CAPS}, ${GROUP_PROPERTY_NAME}, get${GROUP_PROPERTY_NAME_CAPS}AsString);\n") + elseif(${GROUP_PROPERTY_TYPE} IN_LIST TYPED_SCRIPT_CONVERT) + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_COPY_TO_SCRIPT "${${CURRENT_TYPE_CAPS}_GROUP_COPY_TO_SCRIPT}" "\tCOPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(${GROUP_PROPERTY_ENUM}, ${CURRENT_TYPE_CAPS}, ${CURRENT_TYPE}, ${GROUP_PROPERTY_NAME_CAPS}, ${GROUP_PROPERTY_NAME}, ${GROUP_PROPERTY_TYPE});\n") + elseif(LINE MATCHES ".*urlPermission( |,).*") + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_COPY_TO_SCRIPT "${${CURRENT_TYPE_CAPS}_GROUP_COPY_TO_SCRIPT}" "\tCOPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(${GROUP_PROPERTY_ENUM}, ${CURRENT_TYPE_CAPS}, ${CURRENT_TYPE}, ${GROUP_PROPERTY_NAME_CAPS}, ${GROUP_PROPERTY_NAME});\n") + else() + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_COPY_TO_SCRIPT "${${CURRENT_TYPE_CAPS}_GROUP_COPY_TO_SCRIPT}" "\tCOPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(${GROUP_PROPERTY_ENUM}, ${CURRENT_TYPE_CAPS}, ${CURRENT_TYPE}, ${GROUP_PROPERTY_NAME_CAPS}, ${GROUP_PROPERTY_NAME});\n") + endif() + + if(LINE MATCHES ".*enum( |,).*") + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_COPY_FROM_SCRIPT "${${CURRENT_TYPE_CAPS}_GROUP_COPY_FROM_SCRIPT}" "\tCOPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(${CURRENT_TYPE}, ${GROUP_PROPERTY_NAME}, ${GROUP_PROPERTY_NAME_CAPS});\n") + elseif(GROUP_PROPERTY_GETTER AND GROUP_PROPERTY_SETTER) + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_COPY_FROM_SCRIPT "${${CURRENT_TYPE_CAPS}_GROUP_COPY_FROM_SCRIPT}" "\tCOPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_GETTER(${CURRENT_TYPE}, ${GROUP_PROPERTY_NAME}, ${GROUP_PROPERTY_TYPE}, ${GROUP_PROPERTY_SETTER}, ${GROUP_PROPERTY_GETTER});\n") + else() + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_COPY_FROM_SCRIPT "${${CURRENT_TYPE_CAPS}_GROUP_COPY_FROM_SCRIPT}" "\tCOPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(${CURRENT_TYPE}, ${GROUP_PROPERTY_NAME}, ${GROUP_PROPERTY_TYPE}, set${GROUP_PROPERTY_NAME_CAPS});\n") + endif() + + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_MERGE "${${CURRENT_TYPE_CAPS}_GROUP_MERGE}" "\tCOPY_PROPERTY_IF_CHANGED(${GROUP_PROPERTY_NAME});\n") + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_LIST_CHANGED "${${CURRENT_TYPE_CAPS}_GROUP_LIST_CHANGED}" "\tif (${GROUP_PROPERTY_NAME}Changed()) {\n\t\tout += \"${GROUP_PROPERTY_NAME}\";\n\t}\n") + string(CONCAT ${CURRENT_TYPE_CAPS}_REQUESTED_PROPS "${${CURRENT_TYPE_CAPS}_REQUESTED_PROPS}" "\trequestedProperties += ${GROUP_PROPERTY_ENUM};\n") + if(LINE MATCHES ".*enum( |,).*") + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_APPEND "${${CURRENT_TYPE_CAPS}_GROUP_APPEND}" "\tAPPEND_ENTITY_PROPERTY(${GROUP_PROPERTY_ENUM}, (uint32_t)get${GROUP_PROPERTY_NAME_CAPS}());\n") + elseif(GROUP_VARIABLE_NETWORK_GETTER) + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_APPEND "${${CURRENT_TYPE_CAPS}_GROUP_APPEND}" "\tAPPEND_ENTITY_PROPERTY(${GROUP_PROPERTY_ENUM}, ${GROUP_VARIABLE_NETWORK_GETTER});\n") + else() + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_APPEND "${${CURRENT_TYPE_CAPS}_GROUP_APPEND}" "\tAPPEND_ENTITY_PROPERTY(${GROUP_PROPERTY_ENUM}, get${GROUP_PROPERTY_NAME_CAPS}());\n") + endif() + + if(NOT LINE MATCHES ".*noVariableNetworkSetter( |,).*") + if(GROUP_VARIABLE_NETWORK_SETTER) + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_READ "${${CURRENT_TYPE_CAPS}_GROUP_READ}" "\tREAD_ENTITY_PROPERTY(${GROUP_PROPERTY_ENUM}, ${GROUP_PROPERTY_TYPE}, ${GROUP_VARIABLE_NETWORK_SETTER});\n") + else() + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_READ "${${CURRENT_TYPE_CAPS}_GROUP_READ}" "\tREAD_ENTITY_PROPERTY(${GROUP_PROPERTY_ENUM}, ${GROUP_PROPERTY_TYPE}, set${GROUP_PROPERTY_NAME_CAPS});\n") + endif() + endif() + + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_DECODE_CHANGED "${${CURRENT_TYPE_CAPS}_GROUP_DECODE_CHANGED}" "\tDECODE_GROUP_PROPERTY_HAS_CHANGED(${GROUP_PROPERTY_ENUM}, ${GROUP_PROPERTY_NAME_CAPS});\n") + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_MARK_CHANGED "${${CURRENT_TYPE_CAPS}_GROUP_MARK_CHANGED}" "\t_${GROUP_PROPERTY_NAME}Changed = true;\n") + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_CHANGED_PROPERTIES "${${CURRENT_TYPE_CAPS}_GROUP_CHANGED_PROPERTIES}" "\tCHECK_PROPERTY_CHANGE(${GROUP_PROPERTY_ENUM}, ${GROUP_PROPERTY_NAME});\n") + + if(GROUP_VARIABLE_COPY_GETTER) + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_COPY_TO "${${CURRENT_TYPE_CAPS}_GROUP_COPY_TO}" "\tCOPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(${CURRENT_TYPE_CAPS}, ${GROUP_PROPERTY_NAME_CAPS}, ${GROUP_VARIABLE_COPY_GETTER});\n") + else() + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_COPY_TO "${${CURRENT_TYPE_CAPS}_GROUP_COPY_TO}" "\tCOPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(${CURRENT_TYPE_CAPS}, ${GROUP_PROPERTY_NAME_CAPS}, get${GROUP_PROPERTY_NAME_CAPS});\n") + endif() + if(GROUP_VARIABLE_COPY_SETTER) + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_SET_FROM "${${CURRENT_TYPE_CAPS}_GROUP_SET_FROM}" "\tSET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(${CURRENT_TYPE_CAPS}, ${GROUP_PROPERTY_NAME_CAPS}, ${GROUP_PROPERTY_NAME}, ${GROUP_VARIABLE_COPY_SETTER});\n") + else() + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_SET_FROM "${${CURRENT_TYPE_CAPS}_GROUP_SET_FROM}" "\tSET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(${CURRENT_TYPE_CAPS}, ${GROUP_PROPERTY_NAME_CAPS}, ${GROUP_PROPERTY_NAME}, set${GROUP_PROPERTY_NAME_CAPS});\n") + endif() + + if(ENTITY_PROPERTY_MIN AND ENTITY_PROPERTY_MAX) + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_ADD_TO_MAP "${${CURRENT_TYPE_CAPS}_GROUP_ADD_TO_MAP}" "\tADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(${GROUP_PROPERTY_ENUM}, ${CURRENT_TYPE}, ${GROUP_PROPERTY_NAME}, ${GROUP_PROPERTY_MIN}, ${GROUP_PROPERTY_MAX});\n") + else() + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_ADD_TO_MAP "${${CURRENT_TYPE_CAPS}_GROUP_ADD_TO_MAP}" "\tADD_GROUP_PROPERTY_TO_MAP(${GROUP_PROPERTY_ENUM}, ${CURRENT_TYPE}, ${GROUP_PROPERTY_NAME});\n") + endif() + + if(LINE MATCHES ".*enum( |,).*") + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_DEBUG_DUMP "${${CURRENT_TYPE_CAPS}_GROUP_DEBUG_DUMP}" "\tqCDebug(entities) << \" ${CURRENT_TYPE}.${GROUP_PROPERTY_NAME}:\" << (int)get${GROUP_PROPERTY_NAME_CAPS}();\n") + elseif(GROUP_VARIABLE_DEBUG_GETTER) + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_DEBUG_DUMP "${${CURRENT_TYPE_CAPS}_GROUP_DEBUG_DUMP}" "\tqCDebug(entities) << \" ${CURRENT_TYPE}.${GROUP_PROPERTY_NAME}:\" << ${GROUP_VARIABLE_DEBUG_GETTER};\n") + else() + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_DEBUG_DUMP "${${CURRENT_TYPE_CAPS}_GROUP_DEBUG_DUMP}" "\tqCDebug(entities) << \" ${CURRENT_TYPE}.${GROUP_PROPERTY_NAME}:\" << get${GROUP_PROPERTY_NAME_CAPS}();\n") + endif() + endif() + endwhile() + + foreach(TYPE IN LISTS GROUP_TYPES) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/src/${TYPE}PropertyGroup.cpp.in + ${CMAKE_CURRENT_BINARY_DIR}/src/${TYPE}PropertyGroup.cpp) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/src/${TYPE}PropertyGroup.h.in + ${CMAKE_CURRENT_BINARY_DIR}/src/${TYPE}PropertyGroup.h) + list(APPEND GENERATE_ENTITIES_LIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/src/${TYPE}PropertyGroup.h ${CMAKE_CURRENT_BINARY_DIR}/src/${TYPE}PropertyGroup.cpp) + endforeach() + + message(STATUS "Entity property processing end") +endmacro() diff --git a/cmake/macros/IncludeHifiLibraryHeaders.cmake b/cmake/macros/IncludeHifiLibraryHeaders.cmake index 008d76a8dc..0e9c8f03e6 100644 --- a/cmake/macros/IncludeHifiLibraryHeaders.cmake +++ b/cmake/macros/IncludeHifiLibraryHeaders.cmake @@ -11,4 +11,8 @@ macro(include_hifi_library_headers LIBRARY) target_include_directories(${TARGET_NAME} PRIVATE "${HIFI_LIBRARY_DIR}/${LIBRARY}/src") + if (${LIBRARY} STREQUAL "entities") + target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_SOURCE_DIR}/libraries/entities/src") + target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries/entities/src") + endif() endmacro(include_hifi_library_headers _library _root_dir) \ No newline at end of file diff --git a/cmake/macros/SetupHifiLibrary.cmake b/cmake/macros/SetupHifiLibrary.cmake index 5c6eb0a4c6..feffe518a3 100644 --- a/cmake/macros/SetupHifiLibrary.cmake +++ b/cmake/macros/SetupHifiLibrary.cmake @@ -57,9 +57,9 @@ macro(SETUP_HIFI_LIBRARY) # create a library and set the property so it can be referenced later if (${${TARGET_NAME}_SHARED}) - add_library(${TARGET_NAME} SHARED ${LIB_SRCS} ${AUTOSCRIBE_SHADER_LIB_SRC} ${QT_RESOURCES_FILE}) + add_library(${TARGET_NAME} SHARED ${LIB_SRCS} ${AUTOSCRIBE_SHADER_LIB_SRC} ${GENERATE_ENTITIES_LIB_SRC} ${QT_RESOURCES_FILE}) else () - add_library(${TARGET_NAME} ${LIB_SRCS} ${AUTOSCRIBE_SHADER_LIB_SRC} ${QT_RESOURCES_FILE}) + add_library(${TARGET_NAME} ${LIB_SRCS} ${AUTOSCRIBE_SHADER_LIB_SRC} ${GENERATE_ENTITIES_LIB_SRC} ${QT_RESOURCES_FILE}) endif () set(${TARGET_NAME}_DEPENDENCY_QT_MODULES ${ARGN}) @@ -72,8 +72,9 @@ macro(SETUP_HIFI_LIBRARY) target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE}) endforeach() - # Don't make scribed shaders or QT resource files cumulative + # Don't make scribed shaders, generated entity files, or QT resource files cumulative set(AUTOSCRIBE_SHADER_LIB_SRC "") + set(GENERATE_ENTITIES_LIB_SRC "") set(QT_RESOURCES_FILE "") target_glm() diff --git a/cmake/macros/SetupHifiProject.cmake b/cmake/macros/SetupHifiProject.cmake index 8759c949f3..0961510d18 100644 --- a/cmake/macros/SetupHifiProject.cmake +++ b/cmake/macros/SetupHifiProject.cmake @@ -22,7 +22,7 @@ macro(SETUP_HIFI_PROJECT) endif () endforeach() - add_executable(${TARGET_NAME} ${TARGET_SRCS} ${AUTOSCRIBE_SHADER_LIB_SRC}) + add_executable(${TARGET_NAME} ${TARGET_SRCS} ${AUTOSCRIBE_SHADER_LIB_SRC} ${GENERATE_ENTITIES_LIB_SRC}) # include the generated application version header target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes") diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index d68f14ca52..04ab3ab948 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -233,6 +233,7 @@ link_hifi_libraries( shaders ) include_hifi_library_headers(script-engine) +include_hifi_library_headers(entities) # include the binary directory of render-utils for shader includes target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries/render-utils") diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 16831ee091..4ff848fabe 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5750,7 +5750,7 @@ void Application::init() { isInView = _viewFrustum.sphereIntersectsKeyhole(itemPosition, maxSize); } if (!isInView) { - const float OUT_OF_VIEW_PENALTY = -M_PI_2; + const float OUT_OF_VIEW_PENALTY = -(float)M_PI_2; result += OUT_OF_VIEW_PENALTY; } @@ -9179,7 +9179,7 @@ void Application::createLoginDialog() { properties.getGrab().setGrabbable(false); properties.setIgnorePickIntersection(false); properties.setAlpha(1.0f); - properties.setDPI(DPI); + properties.setDpi(DPI); properties.setVisible(true); auto entityScriptingInterface = DependencyManager::get(); @@ -9263,7 +9263,7 @@ void Application::createAvatarInputsBar() { properties.getGrab().setGrabbable(false); properties.setIgnorePickIntersection(false); properties.setAlpha(1.0f); - properties.setDPI(DPI); + properties.setDpi(DPI); properties.setVisible(true); auto entityScriptingInterface = DependencyManager::get(); diff --git a/interface/src/raypick/ParabolaPointer.h b/interface/src/raypick/ParabolaPointer.h index 59168be5ed..6415baac14 100644 --- a/interface/src/raypick/ParabolaPointer.h +++ b/interface/src/raypick/ParabolaPointer.h @@ -10,7 +10,7 @@ #include "PathPointer.h" -#include +#include #include diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 22851d8aa5..b2ce8f66fd 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -18,6 +18,7 @@ include_hifi_library_headers(avatars) include_hifi_library_headers(controllers) include_hifi_library_headers(task) include_hifi_library_headers(graphics-scripting) # for Forward.h +include_hifi_library_headers(entities) target_bullet() target_polyvox() diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index da76961e7d..7ca3a5c341 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -573,7 +573,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa } void EntityRenderer::doRenderUpdateAsynchronous(const EntityItemPointer& entity) { - setIsVisibleInSecondaryCamera(entity->isVisibleInSecondaryCamera()); + setIsVisibleInSecondaryCamera(entity->getIsVisibleInSecondaryCamera()); setRenderLayer(entity->getRenderLayer()); _billboardMode = entity->getBillboardMode(); _primitiveMode = entity->getPrimitiveMode(); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index bda800abe2..8cf5b93724 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -185,18 +185,6 @@ void RenderablePolyVoxEntityItem::initializePolyVox() { setVoxelVolumeSize(_voxelVolumeSize); } -bool isEdged(PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle) { - switch (surfaceStyle) { - case PolyVoxEntityItem::SURFACE_CUBIC: - case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: - return false; - case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: - return true; - } - return false; -} - void RenderablePolyVoxEntityItem::setVoxelData(const QByteArray& voxelData) { // accept compressed voxel information from the entity-server bool changed = false; @@ -212,7 +200,7 @@ void RenderablePolyVoxEntityItem::setVoxelData(const QByteArray& voxelData) { } } -void RenderablePolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { +void RenderablePolyVoxEntityItem::setVoxelSurfaceStyle(uint16_t voxelSurfaceStyle) { // this controls whether the polyvox surface extractor does marching-cubes or makes a cubic mesh. It // also determines if the extra "edged" layer is used. bool volSizeChanged = false; @@ -224,7 +212,7 @@ void RenderablePolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxel // if we are switching to or from "edged" we need to force a resize of _volData. bool wasEdged = isEdged(); - bool willBeEdged = isEdged(voxelSurfaceStyle); + bool willBeEdged = isEdged((PolyVoxSurfaceStyle)voxelSurfaceStyle); if (wasEdged != willBeEdged) { _volData.reset(); @@ -952,7 +940,7 @@ uint8_t RenderablePolyVoxEntityItem::getVoxel(const ivec3& v) const { uint8_t RenderablePolyVoxEntityItem::getVoxelInternal(const ivec3& v) const { - if (!inUserBounds(_volData, _voxelSurfaceStyle, v)) { + if (!inUserBounds(_volData, (PolyVoxSurfaceStyle)_voxelSurfaceStyle, v)) { return 0; } @@ -998,7 +986,7 @@ bool RenderablePolyVoxEntityItem::setVoxelInternal(const ivec3& v, uint8_t toVal bool RenderablePolyVoxEntityItem::updateOnCount(const ivec3& v, uint8_t toValue) { // keep _onCount up to date - if (!inUserBounds(_volData, _voxelSurfaceStyle, v)) { + if (!inUserBounds(_volData, (PolyVoxSurfaceStyle)_voxelSurfaceStyle, v)) { return false; } @@ -1210,7 +1198,7 @@ void RenderablePolyVoxEntityItem::cacheNeighbors() { void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() { // fill in our upper edges with a copy of our neighbors lower edges so that the meshes knit together - if (_voxelSurfaceStyle != PolyVoxEntityItem::SURFACE_MARCHING_CUBES) { + if ((PolyVoxSurfaceStyle)_voxelSurfaceStyle != PolyVoxEntityItem::SURFACE_MARCHING_CUBES) { return; } @@ -1315,7 +1303,7 @@ void RenderablePolyVoxEntityItem::recomputeMesh() { // use _volData to make a renderable mesh PolyVoxSurfaceStyle voxelSurfaceStyle; withReadLock([&] { - voxelSurfaceStyle = _voxelSurfaceStyle; + voxelSurfaceStyle = (PolyVoxSurfaceStyle)_voxelSurfaceStyle; }); auto entity = std::static_pointer_cast(getThisPointer()); @@ -1414,7 +1402,7 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorker() { graphics::MeshPointer mesh; withReadLock([&] { - voxelSurfaceStyle = _voxelSurfaceStyle; + voxelSurfaceStyle = (PolyVoxSurfaceStyle)_voxelSurfaceStyle; voxelVolumeSize = _voxelVolumeSize; mesh = _mesh; }); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 1debeb957c..2c6f29f4b9 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -81,7 +81,7 @@ public: virtual void setVoxelData(const QByteArray& voxelData) override; virtual void setVoxelVolumeSize(const glm::vec3& voxelVolumeSize) override; - virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) override; + virtual void setVoxelSurfaceStyle(uint16_t voxelSurfaceStyle) override; virtual ShapeType getShapeType() const override; virtual bool isReadyToComputeShape() const override; diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 5f2f06470e..e4f42b6133 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -38,7 +38,7 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce withWriteLock([&] { _shape = entity->getShape(); _renderTransform = getModelTransform(); // contains parent scale, if this entity scales with its parent - if (_shape == entity::Sphere) { + if (_shape == EntityShape::Sphere) { _renderTransform.postScale(SPHERE_ENTITY_SCALE); } @@ -116,7 +116,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { gpu::Batch& batch = *args->_batch; auto geometryCache = DependencyManager::get(); - GeometryCache::Shape geometryShape = geometryCache->getShapeForEntityShape(_shape); + GeometryCache::Shape geometryShape = geometryCache->getShapeForEntityShape((int)_shape); Transform transform; withReadLock([&] { transform = _renderTransform; @@ -127,7 +127,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition(), - _shape < entity::Shape::Cube || _shape > entity::Shape::Icosahedron)); + _shape < EntityShape::Cube || _shape > EntityShape::Icosahedron)); batch.setModelTransform(transform); Pipeline pipelineType = getPipelineType(materials); @@ -184,7 +184,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { scriptable::ScriptableModelBase ShapeEntityRenderer::getScriptableModel() { scriptable::ScriptableModelBase result; auto geometryCache = DependencyManager::get(); - auto geometryShape = geometryCache->getShapeForEntityShape(_shape); + auto geometryShape = geometryCache->getShapeForEntityShape((int)_shape); glm::vec3 vertexColor; { std::lock_guard lock(_materialsLock); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 4ffd728cf9..aa56f9f34d 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -36,7 +36,7 @@ private: virtual bool isTransparent() const override; QString _proceduralData; - entity::Shape _shape { entity::Sphere }; + EntityShape _shape { EntityShape::Sphere }; PulsePropertyGroup _pulseProperties; std::shared_ptr _material { std::make_shared() }; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index c2eddef3aa..c50e2ee1ad 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -168,7 +168,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene withWriteLock([&] { _inputMode = entity->getInputMode(); - _dpi = entity->getDPI(); + _dpi = entity->getDpi(); _color = entity->getColor(); _alpha = entity->getAlpha(); _wantsKeyboardFocus = entity->wantsKeyboardFocus(); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 8a6768f235..178e122c32 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -381,7 +381,7 @@ void ZoneEntityRenderer::updateAmbientLightFromEntity(const TypedEntityPointer& ambientLight->setAmbientIntensity(_ambientLightProperties.getAmbientIntensity()); if (_ambientLightProperties.getAmbientURL().isEmpty()) { - setAmbientURL(_skyboxProperties.getURL()); + setAmbientURL(_skyboxProperties.getUrl()); } else { setAmbientURL(_ambientLightProperties.getAmbientURL()); } @@ -447,11 +447,11 @@ void ZoneEntityRenderer::updateAmbientOcclusionFromEntity(const TypedEntityPoint ambientOcclusion->setResolutionLevel(_ambientOcclusionProperties.getResolutionLevel()); ambientOcclusion->setEdgeSharpness(_ambientOcclusionProperties.getEdgeSharpness()); ambientOcclusion->setBlurRadius(_ambientOcclusionProperties.getBlurRadius()); - ambientOcclusion->setAORadius(_ambientOcclusionProperties.getAORadius()); - ambientOcclusion->setAOObscuranceLevel(_ambientOcclusionProperties.getAOObscuranceLevel()); - ambientOcclusion->setAOFalloffAngle(_ambientOcclusionProperties.getAOFalloffAngle()); - ambientOcclusion->setAOSamplingAmount(_ambientOcclusionProperties.getAOSamplingAmount()); - ambientOcclusion->setSSAONumSpiralTurns(_ambientOcclusionProperties.getSSAONumSpiralTurns()); + ambientOcclusion->setAORadius(_ambientOcclusionProperties.getAoRadius()); + ambientOcclusion->setAOObscuranceLevel(_ambientOcclusionProperties.getAoObscuranceLevel()); + ambientOcclusion->setAOFalloffAngle(_ambientOcclusionProperties.getAoFalloffAngle()); + ambientOcclusion->setAOSamplingAmount(_ambientOcclusionProperties.getAoSamplingAmount()); + ambientOcclusion->setSSAONumSpiralTurns(_ambientOcclusionProperties.getSsaoNumSpiralTurns()); } void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& entity) { @@ -460,7 +460,7 @@ void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& editBackground(); setSkyboxColor(toGlm(_skyboxProperties.getColor())); setProceduralUserData(_proceduralUserData); - setSkyboxURL(_skyboxProperties.getURL()); + setSkyboxURL(_skyboxProperties.getUrl()); } void ZoneEntityRenderer::updateKeyZoneItemFromEntity(const TypedEntityPointer& entity) { diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index 830cecd1ed..5a2e703de8 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -3,8 +3,11 @@ # SPDX-License-Identifier: Apache-2.0 set(TARGET_NAME entities) +generate_entity_properties() setup_hifi_library(Network) target_include_directories(${TARGET_NAME} PRIVATE "${OPENSSL_INCLUDE_DIR}") +target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries/entities/src") +target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_SOURCE_DIR}/libraries/entities/src") include_hifi_library_headers(hfm) include_hifi_library_headers(model-serializers) include_hifi_library_headers(gpu) diff --git a/libraries/entities/src/AmbientLightPropertyGroup.cpp b/libraries/entities/src/AmbientLightPropertyGroup.cpp deleted file mode 100644 index ba03b8d2da..0000000000 --- a/libraries/entities/src/AmbientLightPropertyGroup.cpp +++ /dev/null @@ -1,177 +0,0 @@ -// -// AmbientLightPropertyGroup.cpp -// libraries/entities/src -// -// Created by Nissim Hadar on 2017/12/24. -// Copyright 2013 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#include "AmbientLightPropertyGroup.h" - -#include -#include - -#include "EntityItemProperties.h" -#include "EntityItemPropertiesMacros.h" - -const float AmbientLightPropertyGroup::DEFAULT_AMBIENT_LIGHT_INTENSITY = 0.5f; -const glm::u8vec3 AmbientLightPropertyGroup::DEFAULT_COLOR = { 0, 0, 0 }; - -void AmbientLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const { - - auto nodeList = DependencyManager::get(); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_LIGHT_INTENSITY, AmbientLight, ambientLight, AmbientIntensity, ambientIntensity); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_AMBIENT_LIGHT_URL, AmbientLight, ambientLight, AmbientURL, ambientURL); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_AMBIENT_LIGHT_COLOR, AmbientLight, ambientLight, AmbientColor, ambientColor, u8vec3Color); -} - -void AmbientLightPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientLight, ambientIntensity, float, setAmbientIntensity); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientLight, ambientURL, QString, setAmbientURL); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientLight, ambientColor, u8vec3Color, setAmbientColor); - - // legacy property support - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(ambientLightAmbientIntensity, float, setAmbientIntensity, getAmbientIntensity); -} - -void AmbientLightPropertyGroup::merge(const AmbientLightPropertyGroup& other) { - COPY_PROPERTY_IF_CHANGED(ambientIntensity); - COPY_PROPERTY_IF_CHANGED(ambientURL); - COPY_PROPERTY_IF_CHANGED(ambientColor); -} - -void AmbientLightPropertyGroup::debugDump() const { - qCDebug(entities) << " AmbientLightPropertyGroup: ---------------------------------------------"; - qCDebug(entities) << " ambientIntensity:" << getAmbientIntensity(); - qCDebug(entities) << " ambientURL:" << getAmbientURL(); - qCDebug(entities) << " ambientColor:" << getAmbientColor(); -} - -void AmbientLightPropertyGroup::listChangedProperties(QList& out) { - if (ambientIntensityChanged()) { - out << "ambientLight-ambientIntensity"; - } - if (ambientURLChanged()) { - out << "ambientLight-ambientURL"; - } - if (ambientColorChanged()) { - out << "ambientLight-ambientColor"; - } -} - -bool AmbientLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, getAmbientIntensity()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, getAmbientURL()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_COLOR, getAmbientColor()); - - return true; -} - -bool AmbientLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt, - int& processedBytes) { - - int bytesRead = 0; - bool overwriteLocalData = true; - bool somethingChanged = false; - - READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, float, setAmbientIntensity); - READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, QString, setAmbientURL); - READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_COLOR, u8vec3Color, setAmbientColor); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_LIGHT_INTENSITY, AmbientIntensity); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_LIGHT_URL, AmbientURL); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_LIGHT_COLOR, AmbientColor); - - processedBytes += bytesRead; - - Q_UNUSED(somethingChanged); - - return true; -} - -void AmbientLightPropertyGroup::markAllChanged() { - _ambientIntensityChanged = true; - _ambientURLChanged = true; - _ambientColorChanged = true; -} - -EntityPropertyFlags AmbientLightPropertyGroup::getChangedProperties() const { - EntityPropertyFlags changedProperties; - - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_INTENSITY, ambientIntensity); - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_URL, ambientURL); - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_COLOR, ambientColor); - - return changedProperties; -} - -void AmbientLightPropertyGroup::getProperties(EntityItemProperties& properties) const { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientLight, AmbientIntensity, getAmbientIntensity); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientLight, AmbientURL, getAmbientURL); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientLight, AmbientColor, getAmbientColor); -} - -bool AmbientLightPropertyGroup::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientLight, AmbientIntensity, ambientIntensity, setAmbientIntensity); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientLight, AmbientURL, ambientURL, setAmbientURL); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientLight, AmbientColor, ambientColor, setAmbientColor); - - return somethingChanged; -} - -EntityPropertyFlags AmbientLightPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties; - - requestedProperties += PROP_AMBIENT_LIGHT_INTENSITY; - requestedProperties += PROP_AMBIENT_LIGHT_URL; - requestedProperties += PROP_AMBIENT_LIGHT_COLOR; - - return requestedProperties; -} - -void AmbientLightPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, getAmbientIntensity()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, getAmbientURL()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_COLOR, getAmbientColor()); -} - -int AmbientLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, float, setAmbientIntensity); - READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, QString, setAmbientURL); - READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_COLOR, u8vec3Color, setAmbientColor); - - return bytesRead; -} diff --git a/libraries/entities/src/AmbientLightPropertyGroup.cpp.in b/libraries/entities/src/AmbientLightPropertyGroup.cpp.in new file mode 100644 index 0000000000..374459a937 --- /dev/null +++ b/libraries/entities/src/AmbientLightPropertyGroup.cpp.in @@ -0,0 +1,139 @@ +// +// AmbientLightPropertyGroup.cpp +// libraries/entities/src +// +// Created by Nissim Hadar on 2017/12/24. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "AmbientLightPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" + +const float AmbientLightPropertyGroup::DEFAULT_AMBIENT_LIGHT_INTENSITY = 0.5f; +const glm::u8vec3 AmbientLightPropertyGroup::DEFAULT_COLOR = { 0, 0, 0 }; + +void AmbientLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + + auto nodeList = DependencyManager::get(); + +@AmbientLight_GROUP_COPY_TO_SCRIPT@ + +} + +void AmbientLightPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + +@AmbientLight_GROUP_COPY_FROM_SCRIPT@ + +} + +void AmbientLightPropertyGroup::merge(const AmbientLightPropertyGroup& other) { + +@AmbientLight_GROUP_MERGE@ + +} + +void AmbientLightPropertyGroup::debugDump() const { + +@AmbientLight_GROUP_DEBUG_DUMP@ + +} + +void AmbientLightPropertyGroup::listChangedProperties(QList& out) { + +@AmbientLight_GROUP_LIST_CHANGED@ + +} + +bool AmbientLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@AmbientLight_GROUP_APPEND@ + + return successPropertyFits; +} + +bool AmbientLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + +@AmbientLight_GROUP_READ@ + +@AmbientLight_GROUP_DECODE_CHANGED@ + + processedBytes += bytesRead; + + return somethingChanged; +} + +void AmbientLightPropertyGroup::markAllChanged() { + +@AmbientLight_GROUP_MARK_CHANGED@ + +} + +EntityPropertyFlags AmbientLightPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + +@AmbientLight_GROUP_CHANGED_PROPERTIES@ + + return changedProperties; +} + +void AmbientLightPropertyGroup::getProperties(EntityItemProperties& properties) const { + +@AmbientLight_GROUP_COPY_TO@ + +} + +bool AmbientLightPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@AmbientLight_GROUP_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags AmbientLightPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + +@AmbientLight_REQUESTED_PROPS@ + + return requestedProperties; +} + +int AmbientLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@AmbientLight_GROUP_READ@ + + return bytesRead; +} + +void AmbientLightPropertyGroup::addPropertyMap(QHash& _propertyInfos, + QHash& _enumsToPropertyStrings) { + +@AmbientLight_GROUP_ADD_TO_MAP@ + +} diff --git a/libraries/entities/src/AmbientLightPropertyGroup.h b/libraries/entities/src/AmbientLightPropertyGroup.h deleted file mode 100644 index 020af68b11..0000000000 --- a/libraries/entities/src/AmbientLightPropertyGroup.h +++ /dev/null @@ -1,98 +0,0 @@ -// -// AmbientLightPropertyGroup.h -// libraries/entities/src -// -// Created by Nissim Hadar on 2017/12/24. -// Copyright 2013 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - - -#ifndef hifi_AmbientLightPropertyGroup_h -#define hifi_AmbientLightPropertyGroup_h - -#include - -#include - -#include "EntityItemPropertiesMacros.h" -#include "PropertyGroup.h" - -class EntityItemProperties; -class EncodeBitstreamParams; -class OctreePacketData; -class EntityTreeElementExtraEncodeData; -class ReadBitstreamToTreeParams; -class ScriptEngine; -class ScriptValue; - -/*@jsdoc - * Ambient light is defined by the following properties: - * @typedef {object} Entities.AmbientLight - * @property {number} ambientIntensity=0.5 - The intensity of the light. - * @property {string} ambientURL="" - A cube map image that defines the color of the light coming from each direction. If - * "" then the entity's {@link Entities.Skybox|Skybox} url property value is used, unless that also is "" in which - * case the entity's ambientLightMode property is set to "inherit". - * @property {Color} ambientColor=0,0,0 - Sets the color of the ambient light if ambientURL is "", otherwise modifies the - * color of the cube map image. - */ -class AmbientLightPropertyGroup : public PropertyGroup { -public: - // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const override; - virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; - - void merge(const AmbientLightPropertyGroup& other); - - virtual void debugDump() const override; - virtual void listChangedProperties(QList& out) override; - - virtual bool appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt, int& processedBytes) override; - virtual void markAllChanged() override; - virtual EntityPropertyFlags getChangedProperties() const override; - - // EntityItem related helpers - // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const override; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - static const float DEFAULT_AMBIENT_LIGHT_INTENSITY; - static const glm::u8vec3 DEFAULT_COLOR; - DEFINE_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, AmbientIntensity, ambientIntensity, float, DEFAULT_AMBIENT_LIGHT_INTENSITY); - DEFINE_PROPERTY_REF(PROP_AMBIENT_LIGHT_URL, AmbientURL, ambientURL, QString, ""); - DEFINE_PROPERTY_REF(PROP_AMBIENT_LIGHT_COLOR, AmbientColor, ambientColor, glm::u8vec3, DEFAULT_COLOR); -}; - -#endif // hifi_AmbientLightPropertyGroup_h diff --git a/libraries/entities/src/AmbientLightPropertyGroup.h.in b/libraries/entities/src/AmbientLightPropertyGroup.h.in new file mode 100644 index 0000000000..9d2af7c4fa --- /dev/null +++ b/libraries/entities/src/AmbientLightPropertyGroup.h.in @@ -0,0 +1,56 @@ +// +// AmbientLightPropertyGroup.h +// libraries/entities/src +// +// Created by Nissim Hadar on 2017/12/24. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + + +#ifndef hifi_AmbientLightPropertyGroup_h +#define hifi_AmbientLightPropertyGroup_h + +#include + +#include + +#include "EntityItemPropertiesMacros.h" +#include "PropertyGroup.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +/*@jsdoc + * Ambient light is defined by the following properties: + * @typedef {object} Entities.AmbientLight + * @property {number} ambientIntensity=0.5 - The intensity of the light. + * @property {string} ambientURL="" - A cube map image that defines the color of the light coming from each direction. If + * "" then the entity's {@link Entities.Skybox|Skybox} url property value is used, unless that also is "" in which + * case the entity's ambientLightMode property is set to "inherit". + * @property {Color} ambientColor=0,0,0 - Sets the color of the ambient light if ambientURL is "", otherwise modifies the + * color of the cube map image. + */ +class AmbientLightPropertyGroup : public PropertyGroup { +public: + ENTITY_PROPERTY_GROUP_METHODS(AmbientLightPropertyGroup) + + static const float DEFAULT_AMBIENT_LIGHT_INTENSITY; + static const glm::u8vec3 DEFAULT_COLOR; + +protected: + +@AmbientLight_GROUP_PROPS@ + +}; + +#endif // hifi_AmbientLightPropertyGroup_h diff --git a/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp b/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp deleted file mode 100644 index c16f2a00f3..0000000000 --- a/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp +++ /dev/null @@ -1,300 +0,0 @@ -// -// AmbientOcclusionPropertyGroup.cpp -// libraries/entities/src -// -// Created by HifiExperiments on 6/23/24 -// Copyright 2024 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#include "AmbientOcclusionPropertyGroup.h" - -#include - -#include "EntityItemProperties.h" -#include "EntityItemPropertiesMacros.h" - -inline void addAmbientOcclusionTechnique(QHash& lookup, AmbientOcclusionTechnique technique) { lookup[AmbientOcclusionTechniqueHelpers::getNameForAmbientOcclusionTechnique(technique)] = technique; } -const QHash stringToAmbientOcclusionTechniqueLookup = [] { - QHash toReturn; - addAmbientOcclusionTechnique(toReturn, AmbientOcclusionTechnique::SSAO); - addAmbientOcclusionTechnique(toReturn, AmbientOcclusionTechnique::HBAO); - return toReturn; -}(); -QString AmbientOcclusionPropertyGroup::getTechniqueAsString() const { return AmbientOcclusionTechniqueHelpers::getNameForAmbientOcclusionTechnique(_technique); } -void AmbientOcclusionPropertyGroup::setTechniqueFromString(const QString& technique) { - auto techniqueItr = stringToAmbientOcclusionTechniqueLookup.find(technique.toLower()); - if (techniqueItr != stringToAmbientOcclusionTechniqueLookup.end()) { - _technique = techniqueItr.value(); - _techniqueChanged = true; - } -} - -void AmbientOcclusionPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, - bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_OCCLUSION_TECHNIQUE, AmbientOcclusion, ambientOcclusion, Technique, technique, getTechniqueAsString); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_JITTER, AmbientOcclusion, ambientOcclusion, Jitter, jitter); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, AmbientOcclusion, ambientOcclusion, ResolutionLevel, resolutionLevel); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, AmbientOcclusion, ambientOcclusion, EdgeSharpness, edgeSharpness); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, AmbientOcclusion, ambientOcclusion, BlurRadius, blurRadius); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AmbientOcclusion, ambientOcclusion, AORadius, aoRadius); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AmbientOcclusion, ambientOcclusion, AOObscuranceLevel, aoObscuranceLevel); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AmbientOcclusion, ambientOcclusion, AOFalloffAngle, aoFalloffAngle); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, AmbientOcclusion, ambientOcclusion, AOSamplingAmount, aoSamplingAmount); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, AmbientOcclusion, ambientOcclusion, SSAONumSpiralTurns, ssaoNumSpiralTurns); -} - -void AmbientOcclusionPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientOcclusion, technique, Technique); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, jitter, bool, setJitter); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, resolutionLevel, uint8_t, setResolutionLevel); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, edgeSharpness, float, setEdgeSharpness); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, blurRadius, uint8_t, setBlurRadius); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoRadius, float, setAORadius); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoObscuranceLevel, float, setAOObscuranceLevel); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoFalloffAngle, float, setAOFalloffAngle); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoSamplingAmount, float, setAOSamplingAmount); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, ssaoNumSpiralTurns, float, setSSAONumSpiralTurns); -} - -void AmbientOcclusionPropertyGroup::merge(const AmbientOcclusionPropertyGroup& other) { - COPY_PROPERTY_IF_CHANGED(technique); - COPY_PROPERTY_IF_CHANGED(jitter); - COPY_PROPERTY_IF_CHANGED(resolutionLevel); - COPY_PROPERTY_IF_CHANGED(edgeSharpness); - COPY_PROPERTY_IF_CHANGED(blurRadius); - COPY_PROPERTY_IF_CHANGED(aoRadius); - COPY_PROPERTY_IF_CHANGED(aoObscuranceLevel); - COPY_PROPERTY_IF_CHANGED(aoFalloffAngle); - COPY_PROPERTY_IF_CHANGED(aoSamplingAmount); - COPY_PROPERTY_IF_CHANGED(ssaoNumSpiralTurns); -} - -void AmbientOcclusionPropertyGroup::debugDump() const { - qCDebug(entities) << " AmbientOcclusionPropertyGroup: ---------------------------------------------"; - qCDebug(entities) << " Technique:" << getTechniqueAsString(); - qCDebug(entities) << " Jitter:" << getJitter(); - qCDebug(entities) << " ResolutionLevel:" << getResolutionLevel(); - qCDebug(entities) << " EdgeSharpness:" << getEdgeSharpness(); - qCDebug(entities) << " BlurRadius:" << getBlurRadius(); - qCDebug(entities) << " AORadius:" << getAORadius(); - qCDebug(entities) << " AOObscuranceLevel:" << getAOObscuranceLevel(); - qCDebug(entities) << " AOFalloffAngle:" << getAOFalloffAngle(); - qCDebug(entities) << " AOSamplingAmount:" << getAOSamplingAmount(); - qCDebug(entities) << " SSAONumSpiralTurns:" << getSSAONumSpiralTurns(); -} - -void AmbientOcclusionPropertyGroup::listChangedProperties(QList& out) { - if (techniqueChanged()) { - out << "ambientOcclusion-technique"; - } - if (jitterChanged()) { - out << "ambientOcclusion-jitter"; - } - if (resolutionLevelChanged()) { - out << "ambientOcclusion-resolutionLevel"; - } - if (edgeSharpnessChanged()) { - out << "ambientOcclusion-edgeSharpness"; - } - if (blurRadiusChanged()) { - out << "ambientOcclusion-blurRadius"; - } - if (aoRadiusChanged()) { - out << "ambientOcclusion-aoRadius"; - } - if (aoObscuranceLevelChanged()) { - out << "ambientOcclusion-aoObscuranceLevel"; - } - if (aoFalloffAngleChanged()) { - out << "ambientOcclusion-aoFalloffAngle"; - } - if (aoSamplingAmountChanged()) { - out << "ambientOcclusion-aoSamplingAmount"; - } - if (ssaoNumSpiralTurnsChanged()) { - out << "ambientOcclusion-ssaoNumSpiralTurns"; - } -} - -bool AmbientOcclusionPropertyGroup::appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_TECHNIQUE, (uint32_t)getTechnique()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, getJitter()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, getResolutionLevel()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, getEdgeSharpness()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, getBlurRadius()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, getAORadius()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, getAOObscuranceLevel()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, getAOFalloffAngle()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, getAOSamplingAmount()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, getSSAONumSpiralTurns()); - - return true; -} - -bool AmbientOcclusionPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { - - int bytesRead = 0; - bool overwriteLocalData = true; - bool somethingChanged = false; - - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_TECHNIQUE, AmbientOcclusionTechnique, setTechnique); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, bool, setJitter); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, uint8_t, setResolutionLevel); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, float, setEdgeSharpness); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, uint8_t, setBlurRadius); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, float, setAORadius); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, float, setAOObscuranceLevel); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, float, setAOFalloffAngle); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, float, setAOSamplingAmount); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, float, setSSAONumSpiralTurns); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_TECHNIQUE, Technique); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_JITTER, Jitter); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, ResolutionLevel); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, EdgeSharpness); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, BlurRadius); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AORadius); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AOObscuranceLevel); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AOFalloffAngle); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, AOSamplingAmount); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, SSAONumSpiralTurns); - - processedBytes += bytesRead; - - Q_UNUSED(somethingChanged); - - return true; -} - -void AmbientOcclusionPropertyGroup::markAllChanged() { - _techniqueChanged = true; - _jitterChanged = true; - _resolutionLevelChanged = true; - _edgeSharpnessChanged = true; - _blurRadiusChanged = true; - _aoRadiusChanged = true; - _aoObscuranceLevelChanged = true; - _aoFalloffAngleChanged = true; - _aoSamplingAmountChanged = true; - _ssaoNumSpiralTurnsChanged = true; -} - -EntityPropertyFlags AmbientOcclusionPropertyGroup::getChangedProperties() const { - EntityPropertyFlags changedProperties; - - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_TECHNIQUE, technique); - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_JITTER, jitter); - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, resolutionLevel); - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, edgeSharpness); - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, blurRadius); - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_RADIUS, aoRadius); - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, aoObscuranceLevel); - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, aoFalloffAngle); - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, aoSamplingAmount); - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, ssaoNumSpiralTurns); - - return changedProperties; -} - -void AmbientOcclusionPropertyGroup::getProperties(EntityItemProperties& properties) const { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, Technique, getTechnique); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, Jitter, getJitter); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, ResolutionLevel, getResolutionLevel); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, EdgeSharpness, getEdgeSharpness); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, BlurRadius, getBlurRadius); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AORadius, getAORadius); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AOObscuranceLevel, getAOObscuranceLevel); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AOFalloffAngle, getAOFalloffAngle); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AOSamplingAmount, getAOSamplingAmount); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, SSAONumSpiralTurns, getSSAONumSpiralTurns); -} - -bool AmbientOcclusionPropertyGroup::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, Technique, technique, setTechnique); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, Jitter, jitter, setJitter); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, ResolutionLevel, resolutionLevel, setResolutionLevel); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, EdgeSharpness, edgeSharpness, setEdgeSharpness); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, BlurRadius, blurRadius, setBlurRadius); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AORadius, aoRadius, setAORadius); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AOObscuranceLevel, aoObscuranceLevel, setAOObscuranceLevel); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AOFalloffAngle, aoFalloffAngle, setAOFalloffAngle); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AOSamplingAmount, aoSamplingAmount, setAOSamplingAmount); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, SSAONumSpiralTurns, ssaoNumSpiralTurns, setSSAONumSpiralTurns); - - return somethingChanged; -} - -EntityPropertyFlags AmbientOcclusionPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties; - - requestedProperties += PROP_AMBIENT_OCCLUSION_TECHNIQUE; - requestedProperties += PROP_AMBIENT_OCCLUSION_JITTER; - requestedProperties += PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL; - requestedProperties += PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS; - requestedProperties += PROP_AMBIENT_OCCLUSION_BLUR_RADIUS; - requestedProperties += PROP_AMBIENT_OCCLUSION_AO_RADIUS; - requestedProperties += PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL; - requestedProperties += PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE; - requestedProperties += PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT; - requestedProperties += PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS; - - return requestedProperties; -} - -void AmbientOcclusionPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_TECHNIQUE, (uint32_t)getTechnique()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, getJitter()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, getResolutionLevel()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, getEdgeSharpness()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, getBlurRadius()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, getAORadius()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, getAOObscuranceLevel()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, getAOFalloffAngle()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, getAOSamplingAmount()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, getSSAONumSpiralTurns()); -} - -int AmbientOcclusionPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_TECHNIQUE, AmbientOcclusionTechnique, setTechnique); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, bool, setJitter); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, uint8_t, setResolutionLevel); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, float, setEdgeSharpness); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, uint8_t, setBlurRadius); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, float, setAORadius); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, float, setAOObscuranceLevel); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, float, setAOFalloffAngle); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, float, setAOSamplingAmount); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, float, setSSAONumSpiralTurns); - - return bytesRead; -} diff --git a/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp.in b/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp.in new file mode 100644 index 0000000000..31e03e1594 --- /dev/null +++ b/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp.in @@ -0,0 +1,149 @@ +// +// AmbientOcclusionPropertyGroup.cpp +// libraries/entities/src +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "AmbientOcclusionPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" + +inline void addAmbientOcclusionTechnique(QHash& lookup, AmbientOcclusionTechnique technique) { lookup[AmbientOcclusionTechniqueHelpers::getNameForAmbientOcclusionTechnique(technique)] = technique; } +const QHash stringToAmbientOcclusionTechniqueLookup = [] { + QHash toReturn; + addAmbientOcclusionTechnique(toReturn, AmbientOcclusionTechnique::SSAO); + addAmbientOcclusionTechnique(toReturn, AmbientOcclusionTechnique::HBAO); + return toReturn; +}(); +QString AmbientOcclusionPropertyGroup::getTechniqueAsString() const { return AmbientOcclusionTechniqueHelpers::getNameForAmbientOcclusionTechnique(_technique); } +void AmbientOcclusionPropertyGroup::setTechniqueFromString(const QString& technique) { + auto techniqueItr = stringToAmbientOcclusionTechniqueLookup.find(technique.toLower()); + if (techniqueItr != stringToAmbientOcclusionTechniqueLookup.end()) { + _technique = techniqueItr.value(); + _techniqueChanged = true; + } +} + +void AmbientOcclusionPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + +@AmbientOcclusion_GROUP_COPY_TO_SCRIPT@ + +} + +void AmbientOcclusionPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + +@AmbientOcclusion_GROUP_COPY_FROM_SCRIPT@ + +} + +void AmbientOcclusionPropertyGroup::merge(const AmbientOcclusionPropertyGroup& other) { + +@AmbientOcclusion_GROUP_MERGE@ + +} + +void AmbientOcclusionPropertyGroup::debugDump() const { + +@AmbientOcclusion_GROUP_DEBUG_DUMP@ + +} + +void AmbientOcclusionPropertyGroup::listChangedProperties(QList& out) { + +@AmbientOcclusion_GROUP_LIST_CHANGED@ + +} + +bool AmbientOcclusionPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@AmbientOcclusion_GROUP_APPEND@ + + return successPropertyFits; +} + +bool AmbientOcclusionPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + +@AmbientOcclusion_GROUP_READ@ + +@AmbientOcclusion_GROUP_DECODE_CHANGED@ + + processedBytes += bytesRead; + + return somethingChanged; +} + +void AmbientOcclusionPropertyGroup::markAllChanged() { + +@AmbientOcclusion_GROUP_MARK_CHANGED@ + +} + +EntityPropertyFlags AmbientOcclusionPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + +@AmbientOcclusion_GROUP_CHANGED_PROPERTIES@ + + return changedProperties; +} + +void AmbientOcclusionPropertyGroup::getProperties(EntityItemProperties& properties) const { + +@AmbientOcclusion_GROUP_COPY_TO@ + +} + +bool AmbientOcclusionPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@AmbientOcclusion_GROUP_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags AmbientOcclusionPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + +@AmbientOcclusion_REQUESTED_PROPS@ + + return requestedProperties; +} + +int AmbientOcclusionPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@AmbientOcclusion_GROUP_READ@ + + return bytesRead; +} + +void AmbientOcclusionPropertyGroup::addPropertyMap(QHash& _propertyInfos, + QHash& _enumsToPropertyStrings) { + +@AmbientOcclusion_GROUP_ADD_TO_MAP@ + +} diff --git a/libraries/entities/src/AmbientOcclusionPropertyGroup.h b/libraries/entities/src/AmbientOcclusionPropertyGroup.h deleted file mode 100644 index 3f348c4d39..0000000000 --- a/libraries/entities/src/AmbientOcclusionPropertyGroup.h +++ /dev/null @@ -1,108 +0,0 @@ -// -// AmbientOcclusionPropertyGroup.h -// libraries/entities/src -// -// Created by HifiExperiments on 6/23/24 -// Copyright 2024 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#ifndef hifi_AmbientOcclusionPropertyGroup_h -#define hifi_AmbientOcclusionPropertyGroup_h - -#include - -#include "PropertyGroup.h" -#include "EntityItemPropertiesMacros.h" - -class EntityItemProperties; -class EncodeBitstreamParams; -class OctreePacketData; -class EntityTreeElementExtraEncodeData; -class ReadBitstreamToTreeParams; -class ScriptEngine; -class ScriptValue; - -/*@jsdoc - * AmbientOcclusion is defined by the following properties: - * @typedef {object} Entities.AmbientOcclusion - * @property {AmbientOcclusionTechnique} technique="ssao" - The ambient occlusion technique used. Different techniques have - * different tradeoffs. - * @property {boolean} jitter=false - Whether or not the ambient occlusion sampling is jittered. - * @property {number} resolutionLevel=2 - How high the resolution of the ambient occlusion buffer should be. Higher levels - * mean lower resolution buffers. - * @property {number} edgeSharpness=1.0 - How much to sharpen the edges during the ambient occlusion blurring. - * @property {number} blurRadius=4 - The radius used for blurring, in pixels. - * @property {number} aoRadius=1.0 - The radius used for ambient occlusion. - * @property {number} aoObscuranceLevel=0.5 - Intensify or dim ambient occlusion. - * @property {number} aoFalloffAngle=0.25 - The falloff angle for the AO calculation. - * @property {number} aoSamplingAmount=0.5 - The fraction of AO samples to use, out of the maximum for each technique. - * @property {number} ssaoNumSpiralTurns=7.0 - The angle span used to distribute the AO samples ray directions. SSAO only. - */ - -class AmbientOcclusionPropertyGroup : public PropertyGroup { -public: - // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const override; - virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; - - void merge(const AmbientOcclusionPropertyGroup& other); - - virtual void debugDump() const override; - virtual void listChangedProperties(QList& out) override; - - virtual bool appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt, int& processedBytes) override; - virtual void markAllChanged() override; - virtual EntityPropertyFlags getChangedProperties() const override; - - // EntityItem related helpers - // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const override; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - // FIXME: On some machines, SSAO seems to be causing performance problems. Let's default to HBAO for now and maybe - // revisit when we have Vulkan - DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_OCCLUSION_TECHNIQUE, Technique, technique, AmbientOcclusionTechnique, AmbientOcclusionTechnique::HBAO); - DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, Jitter, jitter, bool, false); - DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, ResolutionLevel, resolutionLevel, uint8_t, 2); - DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, EdgeSharpness, edgeSharpness, float, 1.0f); - DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, BlurRadius, blurRadius, uint8_t, 4); - DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AORadius, aoRadius, float, 1.0f); - DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AOObscuranceLevel, aoObscuranceLevel, float, 0.5f); - DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AOFalloffAngle, aoFalloffAngle, float, 0.25f); - DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, AOSamplingAmount, aoSamplingAmount, float, 0.5f); - DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, SSAONumSpiralTurns, ssaoNumSpiralTurns, float, 7.0f); -}; - -#endif // hifi_AmbientOcclusionPropertyGroup_h diff --git a/libraries/entities/src/AmbientOcclusionPropertyGroup.h.in b/libraries/entities/src/AmbientOcclusionPropertyGroup.h.in new file mode 100644 index 0000000000..27ca160fba --- /dev/null +++ b/libraries/entities/src/AmbientOcclusionPropertyGroup.h.in @@ -0,0 +1,58 @@ +// +// AmbientOcclusionPropertyGroup.h +// libraries/entities/src +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_AmbientOcclusionPropertyGroup_h +#define hifi_AmbientOcclusionPropertyGroup_h + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +/*@jsdoc + * AmbientOcclusion is defined by the following properties: + * @typedef {object} Entities.AmbientOcclusion + * @property {AmbientOcclusionTechnique} technique="ssao" - The ambient occlusion technique used. Different techniques have + * different tradeoffs. + * @property {boolean} jitter=false - Whether or not the ambient occlusion sampling is jittered. + * @property {number} resolutionLevel=2 - How high the resolution of the ambient occlusion buffer should be. Higher levels + * mean lower resolution buffers. + * @property {number} edgeSharpness=1.0 - How much to sharpen the edges during the ambient occlusion blurring. + * @property {number} blurRadius=4 - The radius used for blurring, in pixels. + * @property {number} aoRadius=1.0 - The radius used for ambient occlusion. + * @property {number} aoObscuranceLevel=0.5 - Intensify or dim ambient occlusion. + * @property {number} aoFalloffAngle=0.25 - The falloff angle for the AO calculation. + * @property {number} aoSamplingAmount=0.5 - The fraction of AO samples to use, out of the maximum for each technique. + * @property {number} ssaoNumSpiralTurns=7.0 - The angle span used to distribute the AO samples ray directions. SSAO only. + */ + +class AmbientOcclusionPropertyGroup : public PropertyGroup { +public: + ENTITY_PROPERTY_GROUP_METHODS(AmbientOcclusionPropertyGroup) + +protected: + + // FIXME: On some machines, SSAO seems to be causing performance problems. Let's default to HBAO for now and maybe + // revisit when we have Vulkan +@AmbientOcclusion_GROUP_PROPS@ + +}; + +#endif // hifi_AmbientOcclusionPropertyGroup_h diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp deleted file mode 100644 index b1b5c08295..0000000000 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ /dev/null @@ -1,410 +0,0 @@ -// -// AnimationPropertyGroup.cpp -// libraries/entities/src -// -// Created by Brad Hefta-Gaub on 12/4/13. -// Copyright 2013 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#include "AnimationPropertyGroup.h" - -#include -#include - - -#include "EntityItemProperties.h" -#include "EntityItemPropertiesMacros.h" - -const float AnimationPropertyGroup::MAXIMUM_POSSIBLE_FRAME = 100000.0f; - -bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { - return - (a._currentFrame == b._currentFrame) && - (a._running == b._running) && - (a._loop == b._loop) && - (a._hold == b._hold) && - (a._firstFrame == b._firstFrame) && - (a._lastFrame == b._lastFrame) && - (a._fps == b._fps) && - (a._allowTranslation == b._allowTranslation) && - (a._url == b._url) && - (a._smoothFrames == b._smoothFrames); -} - - -/*@jsdoc - * An animation is configured by the following properties: - * @typedef {object} Entities.AnimationProperties - * @property {string} url="" - The URL of the glTF or FBX file that has the animation. glTF files may be in JSON or binary - * format (".gltf" or ".glb" URLs respectively). - *

    Warning: glTF animations currently do not always animate correctly.

    - * @property {boolean} allowTranslation=true - true to enable translations contained in the animation to be - * played, false to disable translations. - * @property {number} fps=30 - The speed in frames/s that the animation is played at. - * @property {number} firstFrame=0 - The first frame to play in the animation. - * @property {number} lastFrame=100000 - The last frame to play in the animation. - * @property {number} currentFrame=0 - The current frame being played in the animation. - * @property {boolean} running=false - true if the animation should play, false if it shouldn't. - * @property {boolean} loop=true - true if the animation is continuously repeated in a loop, false if - * it isn't. - * @property {boolean} hold=false - true if the rotations and translations of the last frame played are - * maintained when the animation stops playing, false if they aren't. - * @property {boolean} smoothFrames=true - true if the frames of the animation should be linearly interpolated to - * create smoother movement, false if the frames should not be interpolated. - */ -void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, - bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const { - auto nodeList = DependencyManager::get(); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_ANIMATION_URL, Animation, animation, URL, url); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FPS, Animation, animation, FPS, fps); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_PLAYING, Animation, animation, Running, running); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_LOOP, Animation, animation, Loop, loop); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_SMOOTH_FRAMES, Animation, animation, SmoothFrames, smoothFrames); -} - - -void AnimationPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { - - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, url, QString, setURL); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, allowTranslation, bool, setAllowTranslation); - - // legacy property support - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationURL, QString, setURL, getURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE_NOCHECK(animationSettings, QString, setFromOldAnimationSettings); - - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, fps, float, setFPS); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, currentFrame, float, setCurrentFrame); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, running, bool, setRunning); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, loop, bool, setLoop); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, firstFrame, float, setFirstFrame); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, lastFrame, float, setLastFrame); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, hold, bool, setHold); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, smoothFrames, bool, setSmoothFrames); - - // legacy property support - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFPS, float, setFPS, getFPS); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationIsPlaying, bool, setRunning, getRunning); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFrameIndex, float, setCurrentFrame, getCurrentFrame); -} - -void AnimationPropertyGroup::merge(const AnimationPropertyGroup& other) { - COPY_PROPERTY_IF_CHANGED(url); - COPY_PROPERTY_IF_CHANGED(allowTranslation); - COPY_PROPERTY_IF_CHANGED(fps); - COPY_PROPERTY_IF_CHANGED(currentFrame); - COPY_PROPERTY_IF_CHANGED(running); - COPY_PROPERTY_IF_CHANGED(loop); - COPY_PROPERTY_IF_CHANGED(firstFrame); - COPY_PROPERTY_IF_CHANGED(lastFrame); - COPY_PROPERTY_IF_CHANGED(hold); - COPY_PROPERTY_IF_CHANGED(smoothFrames); -} - -void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { - // the animations setting is a JSON string that may contain various animation settings. - // if it includes fps, currentFrame, or running, those values will be parsed out and - // will over ride the regular animation settings - - bool allowTranslation = getAllowTranslation(); - float fps = getFPS(); - float currentFrame = getCurrentFrame(); - bool running = getRunning(); - bool loop = getLoop(); - float firstFrame = getFirstFrame(); - float lastFrame = getLastFrame(); - bool hold = getHold(); - - QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8()); - QJsonObject settingsAsJsonObject = settingsAsJson.object(); - QVariantMap settingsMap = settingsAsJsonObject.toVariantMap(); - - if (settingsMap.contains("allowTranslation")) { - allowTranslation = settingsMap["allowTranslation"].toBool(); - } - - if (settingsMap.contains("fps")) { - fps = settingsMap["fps"].toFloat(); - } - - // old settings had frameIndex - if (settingsMap.contains("frameIndex")) { - currentFrame = settingsMap["frameIndex"].toFloat(); - } - - if (settingsMap.contains("running")) { - running = settingsMap["running"].toBool(); - } - - if (settingsMap.contains("firstFrame")) { - firstFrame = settingsMap["firstFrame"].toFloat(); - } - - if (settingsMap.contains("loop")) { - loop = settingsMap["loop"].toBool(); - } - - if (settingsMap.contains("lastFrame")) { - lastFrame = settingsMap["lastFrame"].toFloat(); - } - - if (settingsMap.contains("hold")) { - hold = settingsMap["hold"].toBool(); - } - - setAllowTranslation(allowTranslation); - setFPS(fps); - setCurrentFrame(currentFrame); - setRunning(running); - setLoop(loop); - setFirstFrame(firstFrame); - setLastFrame(lastFrame); - setHold(hold); -} - - -void AnimationPropertyGroup::debugDump() const { - qCDebug(entities) << " AnimationPropertyGroup: ---------------------------------------------"; - qCDebug(entities) << " fps:" << getFPS() << " has changed:" << fpsChanged(); - qCDebug(entities) << "currentFrame:" << getCurrentFrame() << " has changed:" << currentFrameChanged(); - qCDebug(entities) << "allowTranslation:" << getAllowTranslation() << " has changed:" << allowTranslationChanged(); -} - -void AnimationPropertyGroup::listChangedProperties(QList& out) { - if (urlChanged()) { - out << "animation-url"; - } - if (allowTranslationChanged()) { - out << "animation-allowTranslation"; - } - if (fpsChanged()) { - out << "animation-fps"; - } - if (currentFrameChanged()) { - out << "animation-currentFrame"; - } - if (runningChanged()) { - out << "animation-running"; - } - if (loopChanged()) { - out << "animation-loop"; - } - if (firstFrameChanged()) { - out << "animation-firstFrame"; - } - if (lastFrameChanged()) { - out << "animation-lastFrame"; - } - if (holdChanged()) { - out << "animation-hold"; - } - if (smoothFramesChanged()) { - out << "animation-smoothFrames"; - } -} - - -bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, getAllowTranslation()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, getLoop()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, getSmoothFrames()); - - return true; -} - - -bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { - int bytesRead = 0; - bool overwriteLocalData = true; - bool somethingChanged = false; - - READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL); - READ_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, bool, setAllowTranslation); - READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS); - READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setRunning); - READ_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, bool, setLoop); - READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); - READ_ENTITY_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, bool, setSmoothFrames); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_URL, URL); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FPS, FPS); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FRAME_INDEX, CurrentFrame); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_PLAYING, Running); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LOOP, Loop); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FIRST_FRAME, FirstFrame); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LAST_FRAME, LastFrame); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_HOLD, Hold); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_SMOOTH_FRAMES, SmoothFrames); - - processedBytes += bytesRead; - - Q_UNUSED(somethingChanged); - - return true; -} - -void AnimationPropertyGroup::markAllChanged() { - _urlChanged = true; - _allowTranslationChanged = true; - _fpsChanged = true; - _currentFrameChanged = true; - _runningChanged = true; - _loopChanged = true; - _firstFrameChanged = true; - _lastFrameChanged = true; - _holdChanged = true; - _smoothFramesChanged = true; -} - -EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const { - EntityPropertyFlags changedProperties; - - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_URL, url); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_ALLOW_TRANSLATION, allowTranslation); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FPS, fps); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FRAME_INDEX, currentFrame); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_PLAYING, running); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LOOP, loop); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FIRST_FRAME, firstFrame); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LAST_FRAME, lastFrame); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_HOLD, hold); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_SMOOTH_FRAMES, smoothFrames); - - return changedProperties; -} - -void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) const { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, URL, getURL); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, AllowTranslation, getAllowTranslation); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FPS, getFPS); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, CurrentFrame, getCurrentFrame); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Running, getRunning); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Loop, getLoop); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FirstFrame, getFirstFrame); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, LastFrame, getLastFrame); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Hold, getHold); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, SmoothFrames, getSmoothFrames); -} - -bool AnimationPropertyGroup::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, URL, url, setURL); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, AllowTranslation, allowTranslation, setAllowTranslation); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FPS, fps, setFPS); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, CurrentFrame, currentFrame, setCurrentFrame); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Running, running, setRunning); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Loop, loop, setLoop); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FirstFrame, firstFrame, setFirstFrame); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, LastFrame, lastFrame, setLastFrame); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Hold, hold, setHold); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, SmoothFrames, smoothFrames, setSmoothFrames); - return somethingChanged; -} - -EntityPropertyFlags AnimationPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties; - - requestedProperties += PROP_ANIMATION_URL; - requestedProperties += PROP_ANIMATION_ALLOW_TRANSLATION; - requestedProperties += PROP_ANIMATION_FPS; - requestedProperties += PROP_ANIMATION_FRAME_INDEX; - requestedProperties += PROP_ANIMATION_PLAYING; - requestedProperties += PROP_ANIMATION_LOOP; - requestedProperties += PROP_ANIMATION_FIRST_FRAME; - requestedProperties += PROP_ANIMATION_LAST_FRAME; - requestedProperties += PROP_ANIMATION_HOLD; - requestedProperties += PROP_ANIMATION_SMOOTH_FRAMES; - - return requestedProperties; -} - -void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, getAllowTranslation()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, getLoop()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, getSmoothFrames()); -} - -int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL); - READ_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, bool, setAllowTranslation); - READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS); - READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setRunning); - READ_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, bool, setLoop); - READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); - READ_ENTITY_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, bool, setSmoothFrames); - return bytesRead; -} - -float AnimationPropertyGroup::getNumFrames() const { - return _lastFrame - _firstFrame + 1.0f; -} - -float AnimationPropertyGroup::computeLoopedFrame(float frame) const { - float numFrames = getNumFrames(); - if (numFrames > 1.0f) { - frame = getFirstFrame() + fmodf(frame - getFirstFrame(), numFrames); - } else { - frame = getFirstFrame(); - } - return frame; -} - -bool AnimationPropertyGroup::isValidAndRunning() const { - return getRunning() && (getFPS() > 0.0f) && (getNumFrames() > 1.0f) && !(getURL().isEmpty()); -} diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp.in b/libraries/entities/src/AnimationPropertyGroup.cpp.in new file mode 100644 index 0000000000..1c71360d74 --- /dev/null +++ b/libraries/entities/src/AnimationPropertyGroup.cpp.in @@ -0,0 +1,261 @@ +// +// AnimationPropertyGroup.cpp +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "AnimationPropertyGroup.h" + +#include +#include + +#include "EntityItemProperties.h" + +const float AnimationPropertyGroup::MAXIMUM_POSSIBLE_FRAME = 100000.0f; + +bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { + return + (a._currentFrame == b._currentFrame) && + (a._running == b._running) && + (a._loop == b._loop) && + (a._hold == b._hold) && + (a._firstFrame == b._firstFrame) && + (a._lastFrame == b._lastFrame) && + (a._fps == b._fps) && + (a._allowTranslation == b._allowTranslation) && + (a._url == b._url) && + (a._smoothFrames == b._smoothFrames); +} + + +/*@jsdoc + * An animation is configured by the following properties: + * @typedef {object} Entities.AnimationProperties + * @property {string} url="" - The URL of the glTF or FBX file that has the animation. glTF files may be in JSON or binary + * format (".gltf" or ".glb" URLs respectively). + *

    Warning: glTF animations currently do not always animate correctly.

    + * @property {boolean} allowTranslation=true - true to enable translations contained in the animation to be + * played, false to disable translations. + * @property {number} fps=30 - The speed in frames/s that the animation is played at. + * @property {number} firstFrame=0 - The first frame to play in the animation. + * @property {number} lastFrame=100000 - The last frame to play in the animation. + * @property {number} currentFrame=0 - The current frame being played in the animation. + * @property {boolean} running=false - true if the animation should play, false if it shouldn't. + * @property {boolean} loop=true - true if the animation is continuously repeated in a loop, false if + * it isn't. + * @property {boolean} hold=false - true if the rotations and translations of the last frame played are + * maintained when the animation stops playing, false if they aren't. + * @property {boolean} smoothFrames=true - true if the frames of the animation should be linearly interpolated to + * create smoother movement, false if the frames should not be interpolated. + */ + +void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + + auto nodeList = DependencyManager::get(); + +@Animation_GROUP_COPY_TO_SCRIPT@ + +} + +void AnimationPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + +@Animation_GROUP_COPY_FROM_SCRIPT@ + + // legacy property support + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationURL, QString, setUrl, getUrl); + COPY_PROPERTY_FROM_QSCRIPTVALUE_NOCHECK(animationSettings, QString, setFromOldAnimationSettings); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFPS, float, setFps, getFps); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationIsPlaying, bool, setRunning, getRunning); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFrameIndex, float, setCurrentFrame, getCurrentFrame); + +} + +void AnimationPropertyGroup::merge(const AnimationPropertyGroup& other) { + +@Animation_GROUP_MERGE@ + +} + +void AnimationPropertyGroup::debugDump() const { + +@Animation_GROUP_DEBUG_DUMP@ + +} + +void AnimationPropertyGroup::listChangedProperties(QList& out) { + +@Animation_GROUP_LIST_CHANGED@ + +} + +bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Animation_GROUP_APPEND@ + + return successPropertyFits; +} + +bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + +@Animation_GROUP_READ@ + +@Animation_GROUP_DECODE_CHANGED@ + + processedBytes += bytesRead; + + return somethingChanged; +} + +void AnimationPropertyGroup::markAllChanged() { + +@Animation_GROUP_MARK_CHANGED@ + +} + +EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + +@Animation_GROUP_CHANGED_PROPERTIES@ + + return changedProperties; +} + +void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) const { + +@Animation_GROUP_COPY_TO@ + +} + +bool AnimationPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Animation_GROUP_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags AnimationPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + +@Animation_REQUESTED_PROPS@ + + return requestedProperties; +} + +int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Animation_GROUP_READ@ + + return bytesRead; +} + +void AnimationPropertyGroup::addPropertyMap(QHash& _propertyInfos, + QHash& _enumsToPropertyStrings) { + +@Animation_GROUP_ADD_TO_MAP@ + +} + +void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { + // the animations setting is a JSON string that may contain various animation settings. + // if it includes fps, currentFrame, or running, those values will be parsed out and + // will over ride the regular animation settings + + bool allowTranslation = getAllowTranslation(); + float fps = getFps(); + float currentFrame = getCurrentFrame(); + bool running = getRunning(); + bool loop = getLoop(); + float firstFrame = getFirstFrame(); + float lastFrame = getLastFrame(); + bool hold = getHold(); + + QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8()); + QJsonObject settingsAsJsonObject = settingsAsJson.object(); + QVariantMap settingsMap = settingsAsJsonObject.toVariantMap(); + + if (settingsMap.contains("allowTranslation")) { + allowTranslation = settingsMap["allowTranslation"].toBool(); + } + + if (settingsMap.contains("fps")) { + fps = settingsMap["fps"].toFloat(); + } + + // old settings had frameIndex + if (settingsMap.contains("frameIndex")) { + currentFrame = settingsMap["frameIndex"].toFloat(); + } + + if (settingsMap.contains("running")) { + running = settingsMap["running"].toBool(); + } + + if (settingsMap.contains("firstFrame")) { + firstFrame = settingsMap["firstFrame"].toFloat(); + } + + if (settingsMap.contains("loop")) { + loop = settingsMap["loop"].toBool(); + } + + if (settingsMap.contains("lastFrame")) { + lastFrame = settingsMap["lastFrame"].toFloat(); + } + + if (settingsMap.contains("hold")) { + hold = settingsMap["hold"].toBool(); + } + + setAllowTranslation(allowTranslation); + setFps(fps); + setCurrentFrame(currentFrame); + setRunning(running); + setLoop(loop); + setFirstFrame(firstFrame); + setLastFrame(lastFrame); + setHold(hold); +} + +float AnimationPropertyGroup::getNumFrames() const { + return _lastFrame - _firstFrame + 1.0f; +} + +float AnimationPropertyGroup::computeLoopedFrame(float frame) const { + float numFrames = getNumFrames(); + if (numFrames > 1.0f) { + frame = getFirstFrame() + fmodf(frame - getFirstFrame(), numFrames); + } else { + frame = getFirstFrame(); + } + return frame; +} + +bool AnimationPropertyGroup::isValidAndRunning() const { + return getRunning() && (getFps() > 0.0f) && (getNumFrames() > 1.0f) && !(getUrl().isEmpty()); +} diff --git a/libraries/entities/src/AnimationPropertyGroup.h b/libraries/entities/src/AnimationPropertyGroup.h deleted file mode 100644 index 875a1f7126..0000000000 --- a/libraries/entities/src/AnimationPropertyGroup.h +++ /dev/null @@ -1,104 +0,0 @@ -// -// AnimationPropertyGroup.h -// libraries/entities/src -// -// Created by Brad Hefta-Gaub on 2015/9/30. -// Copyright 2013 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - - -#ifndef hifi_AnimationPropertyGroup_h -#define hifi_AnimationPropertyGroup_h - -#include - -#include - -#include "EntityItemPropertiesMacros.h" -#include "PropertyGroup.h" - -class EntityItemProperties; -class EncodeBitstreamParams; -class OctreePacketData; -class EntityTreeElementExtraEncodeData; -class ReadBitstreamToTreeParams; -class ScriptEngine; -class ScriptValue; - -class AnimationPropertyGroup : public PropertyGroup { -public: - static const float MAXIMUM_POSSIBLE_FRAME; - - // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const override; - virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; - - void merge(const AnimationPropertyGroup& other); - - virtual void debugDump() const override; - virtual void listChangedProperties(QList& out) override; - - virtual bool appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt, int& processedBytes) override; - virtual void markAllChanged() override; - virtual EntityPropertyFlags getChangedProperties() const override; - - // EntityItem related helpers - // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const override; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - float getNumFrames() const; - float computeLoopedFrame(float frame) const; - bool isValidAndRunning() const; - - DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, URL, url, QString, ""); - DEFINE_PROPERTY(PROP_ANIMATION_FPS, FPS, fps, float, 30.0f); - DEFINE_PROPERTY(PROP_ANIMATION_FRAME_INDEX, CurrentFrame, currentFrame, float, 0.0f); - DEFINE_PROPERTY(PROP_ANIMATION_PLAYING, Running, running, bool, false); // was animationIsPlaying - DEFINE_PROPERTY(PROP_ANIMATION_LOOP, Loop, loop, bool, true); // was animationSettings.loop - DEFINE_PROPERTY(PROP_ANIMATION_FIRST_FRAME, FirstFrame, firstFrame, float, 0.0f); // was animationSettings.firstFrame - DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame - DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold - DEFINE_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation, allowTranslation, bool, true); - DEFINE_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, SmoothFrames, smoothFrames, bool, true); - -protected: - friend bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b); - friend bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { return !(a == b); } - void setFromOldAnimationSettings(const QString& value); -}; - -#endif // hifi_AnimationPropertyGroup_h diff --git a/libraries/entities/src/AnimationPropertyGroup.h.in b/libraries/entities/src/AnimationPropertyGroup.h.in new file mode 100644 index 0000000000..3de4fd2038 --- /dev/null +++ b/libraries/entities/src/AnimationPropertyGroup.h.in @@ -0,0 +1,52 @@ +// +// AnimationPropertyGroup.h +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 2015/9/30. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + + +#ifndef hifi_AnimationPropertyGroup_h +#define hifi_AnimationPropertyGroup_h + +#include + +#include + +#include "EntityItemPropertiesMacros.h" +#include "PropertyGroup.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +class AnimationPropertyGroup : public PropertyGroup { +public: + ENTITY_PROPERTY_GROUP_METHODS(AnimationPropertyGroup) + + static const float MAXIMUM_POSSIBLE_FRAME; + + float getNumFrames() const; + float computeLoopedFrame(float frame) const; + bool isValidAndRunning() const; + +protected: + +@Animation_GROUP_PROPS@ + + friend bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b); + friend bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { return !(a == b); } + void setFromOldAnimationSettings(const QString& value); +}; + +#endif // hifi_AnimationPropertyGroup_h diff --git a/libraries/entities/src/BloomPropertyGroup.cpp b/libraries/entities/src/BloomPropertyGroup.cpp deleted file mode 100644 index 18ad85d797..0000000000 --- a/libraries/entities/src/BloomPropertyGroup.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// -// BloomPropertyGroup.cpp -// libraries/entities/src -// -// Created by Sam Gondelman on 8/7/2018 -// Copyright 2018 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#include "BloomPropertyGroup.h" - -#include - -#include "EntityItemProperties.h" -#include "EntityItemPropertiesMacros.h" - -void BloomPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, - bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_INTENSITY, Bloom, bloom, BloomIntensity, bloomIntensity); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_THRESHOLD, Bloom, bloom, BloomThreshold, bloomThreshold); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_SIZE, Bloom, bloom, BloomSize, bloomSize); -} - -void BloomPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomIntensity, float, setBloomIntensity); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomThreshold, float, setBloomThreshold); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomSize, float, setBloomSize); -} - -void BloomPropertyGroup::merge(const BloomPropertyGroup& other) { - COPY_PROPERTY_IF_CHANGED(bloomIntensity); - COPY_PROPERTY_IF_CHANGED(bloomThreshold); - COPY_PROPERTY_IF_CHANGED(bloomSize); -} - -void BloomPropertyGroup::debugDump() const { - qCDebug(entities) << " BloomPropertyGroup: ---------------------------------------------"; - qCDebug(entities) << " _bloomIntensity:" << _bloomIntensity; - qCDebug(entities) << " _bloomThreshold:" << _bloomThreshold; - qCDebug(entities) << " _bloomSize:" << _bloomSize; -} - -void BloomPropertyGroup::listChangedProperties(QList& out) { - if (bloomIntensityChanged()) { - out << "bloom-bloomIntensity"; - } - if (bloomThresholdChanged()) { - out << "bloom-bloomThreshold"; - } - if (bloomSizeChanged()) { - out << "bloom-bloomSize"; - } -} - -bool BloomPropertyGroup::appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, getBloomIntensity()); - APPEND_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, getBloomThreshold()); - APPEND_ENTITY_PROPERTY(PROP_BLOOM_SIZE, getBloomSize()); - - return true; -} - -bool BloomPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { - int bytesRead = 0; - bool overwriteLocalData = true; - bool somethingChanged = false; - - READ_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, float, setBloomIntensity); - READ_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, float, setBloomThreshold); - READ_ENTITY_PROPERTY(PROP_BLOOM_SIZE, float, setBloomSize); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_INTENSITY, BloomIntensity); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_THRESHOLD, BloomThreshold); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_SIZE, BloomSize); - - processedBytes += bytesRead; - - Q_UNUSED(somethingChanged); - - return true; -} - -void BloomPropertyGroup::markAllChanged() { - _bloomIntensityChanged = true; - _bloomThresholdChanged = true; - _bloomSizeChanged = true; -} - -EntityPropertyFlags BloomPropertyGroup::getChangedProperties() const { - EntityPropertyFlags changedProperties; - - CHECK_PROPERTY_CHANGE(PROP_BLOOM_INTENSITY, bloomIntensity); - CHECK_PROPERTY_CHANGE(PROP_BLOOM_THRESHOLD, bloomThreshold); - CHECK_PROPERTY_CHANGE(PROP_BLOOM_SIZE, bloomSize); - - return changedProperties; -} - -void BloomPropertyGroup::getProperties(EntityItemProperties& properties) const { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomIntensity, getBloomIntensity); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomThreshold, getBloomThreshold); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomSize, getBloomSize); -} - -bool BloomPropertyGroup::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomIntensity, bloomIntensity, setBloomIntensity); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomThreshold, bloomThreshold, setBloomThreshold); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomSize, bloomSize, setBloomSize); - - return somethingChanged; -} - -EntityPropertyFlags BloomPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties; - - requestedProperties += PROP_BLOOM_INTENSITY; - requestedProperties += PROP_BLOOM_THRESHOLD; - requestedProperties += PROP_BLOOM_SIZE; - - return requestedProperties; -} - -void BloomPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, getBloomIntensity()); - APPEND_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, getBloomThreshold()); - APPEND_ENTITY_PROPERTY(PROP_BLOOM_SIZE, getBloomSize()); -} - -int BloomPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, float, setBloomIntensity); - READ_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, float, setBloomThreshold); - READ_ENTITY_PROPERTY(PROP_BLOOM_SIZE, float, setBloomSize); - - return bytesRead; -} diff --git a/libraries/entities/src/BloomPropertyGroup.cpp.in b/libraries/entities/src/BloomPropertyGroup.cpp.in new file mode 100644 index 0000000000..b567d645b1 --- /dev/null +++ b/libraries/entities/src/BloomPropertyGroup.cpp.in @@ -0,0 +1,134 @@ +// +// BloomPropertyGroup.cpp +// libraries/entities/src +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "BloomPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" + +void BloomPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + +@Bloom_GROUP_COPY_TO_SCRIPT@ + +} + +void BloomPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + +@Bloom_GROUP_COPY_FROM_SCRIPT@ + +} + +void BloomPropertyGroup::merge(const BloomPropertyGroup& other) { + +@Bloom_GROUP_MERGE@ + +} + +void BloomPropertyGroup::debugDump() const { + +@Bloom_GROUP_DEBUG_DUMP@ + +} + +void BloomPropertyGroup::listChangedProperties(QList& out) { + +@Bloom_GROUP_LIST_CHANGED@ + +} + +bool BloomPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Bloom_GROUP_APPEND@ + + return successPropertyFits; +} + +bool BloomPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + +@Bloom_GROUP_READ@ + +@Bloom_GROUP_DECODE_CHANGED@ + + processedBytes += bytesRead; + + return somethingChanged; +} + +void BloomPropertyGroup::markAllChanged() { + +@Bloom_GROUP_MARK_CHANGED@ + +} + +EntityPropertyFlags BloomPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + +@Bloom_GROUP_CHANGED_PROPERTIES@ + + return changedProperties; +} + +void BloomPropertyGroup::getProperties(EntityItemProperties& properties) const { + +@Bloom_GROUP_COPY_TO@ + +} + +bool BloomPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Bloom_GROUP_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags BloomPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + +@Bloom_REQUESTED_PROPS@ + + return requestedProperties; +} + +int BloomPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Bloom_GROUP_READ@ + + return bytesRead; +} + +void BloomPropertyGroup::addPropertyMap(QHash& _propertyInfos, + QHash& _enumsToPropertyStrings) { + +@Bloom_GROUP_ADD_TO_MAP@ + +} diff --git a/libraries/entities/src/BloomPropertyGroup.h b/libraries/entities/src/BloomPropertyGroup.h deleted file mode 100644 index d459bb2f3c..0000000000 --- a/libraries/entities/src/BloomPropertyGroup.h +++ /dev/null @@ -1,96 +0,0 @@ -// -// BloomPropertyGroup.h -// libraries/entities/src -// -// Created by Sam Gondelman on 8/7/2018 -// Copyright 2018 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#ifndef hifi_BloomPropertyGroup_h -#define hifi_BloomPropertyGroup_h - -#include -#include - -#include "PropertyGroup.h" -#include "EntityItemPropertiesMacros.h" - -class EntityItemProperties; -class EncodeBitstreamParams; -class OctreePacketData; -class EntityTreeElementExtraEncodeData; -class ReadBitstreamToTreeParams; -class ScriptEngine; -class ScriptValue; - -static const float INITIAL_BLOOM_INTENSITY { 0.25f }; -static const float INITIAL_BLOOM_THRESHOLD { 0.7f }; -static const float INITIAL_BLOOM_SIZE { 0.9f }; - -/*@jsdoc - * Bloom is defined by the following properties: - * @typedef {object} Entities.Bloom - * @property {number} bloomIntensity=0.25 - The intensity of the bloom effect. - * @property {number} bloomThreshold=0.7 - The threshold for the bloom effect. - * @property {number} bloomSize=0.9 - The size of the bloom effect. - */ -class BloomPropertyGroup : public PropertyGroup { -public: - // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const override; - virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; - - void merge(const BloomPropertyGroup& other); - - virtual void debugDump() const override; - virtual void listChangedProperties(QList& out) override; - - virtual bool appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt, int& processedBytes) override; - virtual void markAllChanged() override; - virtual EntityPropertyFlags getChangedProperties() const override; - - // EntityItem related helpers - // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const override; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - DEFINE_PROPERTY(PROP_BLOOM_INTENSITY, BloomIntensity, bloomIntensity, float, INITIAL_BLOOM_INTENSITY); - DEFINE_PROPERTY(PROP_BLOOM_THRESHOLD, BloomThreshold, bloomThreshold, float, INITIAL_BLOOM_THRESHOLD); - DEFINE_PROPERTY(PROP_BLOOM_SIZE, BloomSize, bloomSize, float, INITIAL_BLOOM_SIZE); - -}; - -#endif // hifi_BloomPropertyGroup_h diff --git a/libraries/entities/src/BloomPropertyGroup.h.in b/libraries/entities/src/BloomPropertyGroup.h.in new file mode 100644 index 0000000000..10e938655e --- /dev/null +++ b/libraries/entities/src/BloomPropertyGroup.h.in @@ -0,0 +1,52 @@ +// +// BloomPropertyGroup.h +// libraries/entities/src +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_BloomPropertyGroup_h +#define hifi_BloomPropertyGroup_h + +#include +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +static const float INITIAL_BLOOM_INTENSITY { 0.25f }; +static const float INITIAL_BLOOM_THRESHOLD { 0.7f }; +static const float INITIAL_BLOOM_SIZE { 0.9f }; + +/*@jsdoc + * Bloom is defined by the following properties: + * @typedef {object} Entities.Bloom + * @property {number} bloomIntensity=0.25 - The intensity of the bloom effect. + * @property {number} bloomThreshold=0.7 - The threshold for the bloom effect. + * @property {number} bloomSize=0.9 - The size of the bloom effect. + */ +class BloomPropertyGroup : public PropertyGroup { +public: + ENTITY_PROPERTY_GROUP_METHODS(BloomPropertyGroup) + +protected: + +@Bloom_GROUP_PROPS@ + +}; + +#endif // hifi_BloomPropertyGroup_h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp.in similarity index 76% rename from libraries/entities/src/EntityItem.cpp rename to libraries/entities/src/EntityItem.cpp.in index 7037785c48..aed0b2bcb6 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp.in @@ -75,83 +75,7 @@ EntityItem::~EntityItem() { EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties; - // Core - requestedProperties += PROP_SIMULATION_OWNER; - requestedProperties += PROP_PARENT_ID; - requestedProperties += PROP_PARENT_JOINT_INDEX; - requestedProperties += PROP_VISIBLE; - requestedProperties += PROP_NAME; - requestedProperties += PROP_LOCKED; - requestedProperties += PROP_USER_DATA; - requestedProperties += PROP_PRIVATE_USER_DATA; - requestedProperties += PROP_HREF; - requestedProperties += PROP_DESCRIPTION; - requestedProperties += PROP_POSITION; - requestedProperties += PROP_DIMENSIONS; - requestedProperties += PROP_ROTATION; - requestedProperties += PROP_REGISTRATION_POINT; - requestedProperties += PROP_CREATED; - requestedProperties += PROP_LAST_EDITED_BY; - requestedProperties += PROP_ENTITY_HOST_TYPE; - requestedProperties += PROP_OWNING_AVATAR_ID; - requestedProperties += PROP_PARENT_ID; - requestedProperties += PROP_PARENT_JOINT_INDEX; - requestedProperties += PROP_QUERY_AA_CUBE; - requestedProperties += PROP_CAN_CAST_SHADOW; - requestedProperties += PROP_VISIBLE_IN_SECONDARY_CAMERA; - requestedProperties += PROP_RENDER_LAYER; - requestedProperties += PROP_PRIMITIVE_MODE; - requestedProperties += PROP_IGNORE_PICK_INTERSECTION; - requestedProperties += PROP_RENDER_WITH_ZONES; - requestedProperties += PROP_BILLBOARD_MODE; - requestedProperties += PROP_TAGS; - requestedProperties += _grabProperties.getEntityProperties(params); - requestedProperties += PROP_MIRROR_MODE; - requestedProperties += PROP_PORTAL_EXIT_ID; - - // Physics - requestedProperties += PROP_DENSITY; - requestedProperties += PROP_VELOCITY; - requestedProperties += PROP_ANGULAR_VELOCITY; - requestedProperties += PROP_GRAVITY; - requestedProperties += PROP_ACCELERATION; - requestedProperties += PROP_DAMPING; - requestedProperties += PROP_ANGULAR_DAMPING; - requestedProperties += PROP_RESTITUTION; - requestedProperties += PROP_FRICTION; - requestedProperties += PROP_LIFETIME; - requestedProperties += PROP_COLLISIONLESS; - requestedProperties += PROP_COLLISION_MASK; - requestedProperties += PROP_DYNAMIC; - requestedProperties += PROP_COLLISION_SOUND_URL; - requestedProperties += PROP_ACTION_DATA; - - // Cloning - requestedProperties += PROP_CLONEABLE; - requestedProperties += PROP_CLONE_LIFETIME; - requestedProperties += PROP_CLONE_LIMIT; - requestedProperties += PROP_CLONE_DYNAMIC; - requestedProperties += PROP_CLONE_AVATAR_ENTITY; - requestedProperties += PROP_CLONE_ORIGIN_ID; - - // Scripts - requestedProperties += PROP_SCRIPT; - requestedProperties += PROP_SCRIPT_TIMESTAMP; - requestedProperties += PROP_SERVER_SCRIPTS; - - // Certifiable properties - requestedProperties += PROP_ITEM_NAME; - requestedProperties += PROP_ITEM_DESCRIPTION; - requestedProperties += PROP_ITEM_CATEGORIES; - requestedProperties += PROP_ITEM_ARTIST; - requestedProperties += PROP_ITEM_LICENSE; - requestedProperties += PROP_LIMITED_RUN; - requestedProperties += PROP_MARKETPLACE_ID; - requestedProperties += PROP_EDITION_NUMBER; - requestedProperties += PROP_ENTITY_INSTANCE_NUMBER; - requestedProperties += PROP_CERTIFICATE_ID; - requestedProperties += PROP_CERTIFICATE_TYPE; - requestedProperties += PROP_STATIC_CERTIFICATE_VERSION; +@Base_REQUESTED_PROPS@ return requestedProperties; } @@ -272,89 +196,14 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet // PROP_PAGED_PROPERTY, // PROP_CUSTOM_PROPERTIES_INCLUDED, - APPEND_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, _simulationOwner.toByteArray()); // convert AVATAR_SELF_ID to actual sessionUUID. QUuid actualParentID = getParentID(); auto nodeList = DependencyManager::get(); if (actualParentID == AVATAR_SELF_ID) { actualParentID = nodeList->getSessionUUID(); } - APPEND_ENTITY_PROPERTY(PROP_PARENT_ID, actualParentID); - APPEND_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, getParentJointIndex()); - APPEND_ENTITY_PROPERTY(PROP_VISIBLE, getVisible()); - APPEND_ENTITY_PROPERTY(PROP_NAME, getName()); - APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked()); - APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData()); - APPEND_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, privateUserData); - APPEND_ENTITY_PROPERTY(PROP_HREF, getHref()); - APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription()); - APPEND_ENTITY_PROPERTY(PROP_POSITION, getLocalPosition()); - APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, getScaledDimensions()); - APPEND_ENTITY_PROPERTY(PROP_ROTATION, getLocalOrientation()); - APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, getRegistrationPoint()); - APPEND_ENTITY_PROPERTY(PROP_CREATED, getCreated()); - APPEND_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, getLastEditedBy()); - // APPEND_ENTITY_PROPERTY(PROP_ENTITY_HOST_TYPE, (uint32_t)getEntityHostType()); // not sent over the wire - // APPEND_ENTITY_PROPERTY(PROP_OWNING_AVATAR_ID, getOwningAvatarID()); // not sent over the wire - APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, getQueryAACube()); - APPEND_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, getCanCastShadow()); - // APPEND_ENTITY_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, getIsVisibleInSecondaryCamera()); // not sent over the wire - APPEND_ENTITY_PROPERTY(PROP_RENDER_LAYER, (uint32_t)getRenderLayer()); - APPEND_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, (uint32_t)getPrimitiveMode()); - APPEND_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, getIgnorePickIntersection()); - APPEND_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, getRenderWithZones()); - APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); - APPEND_ENTITY_PROPERTY(PROP_TAGS, getTags()); - withReadLock([&] { - _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()); - APPEND_ENTITY_PROPERTY(PROP_VELOCITY, getLocalVelocity()); - APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, getLocalAngularVelocity()); - APPEND_ENTITY_PROPERTY(PROP_GRAVITY, getGravity()); - APPEND_ENTITY_PROPERTY(PROP_ACCELERATION, getAcceleration()); - APPEND_ENTITY_PROPERTY(PROP_DAMPING, getDamping()); - APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, getAngularDamping()); - APPEND_ENTITY_PROPERTY(PROP_RESTITUTION, getRestitution()); - APPEND_ENTITY_PROPERTY(PROP_FRICTION, getFriction()); - APPEND_ENTITY_PROPERTY(PROP_LIFETIME, getLifetime()); - APPEND_ENTITY_PROPERTY(PROP_COLLISIONLESS, getCollisionless()); - APPEND_ENTITY_PROPERTY(PROP_COLLISION_MASK, getCollisionMask()); - APPEND_ENTITY_PROPERTY(PROP_DYNAMIC, getDynamic()); - APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL()); - APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, getDynamicData()); - - // Cloning - APPEND_ENTITY_PROPERTY(PROP_CLONEABLE, getCloneable()); - APPEND_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, getCloneLifetime()); - APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, getCloneLimit()); - APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, getCloneDynamic()); - APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, getCloneAvatarEntity()); - APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, getCloneOriginID()); - - // Scripts - APPEND_ENTITY_PROPERTY(PROP_SCRIPT, getScript()); - APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, getScriptTimestamp()); - APPEND_ENTITY_PROPERTY(PROP_SERVER_SCRIPTS, getServerScripts()); - - // Certifiable Properties - APPEND_ENTITY_PROPERTY(PROP_ITEM_NAME, QString()); - APPEND_ENTITY_PROPERTY(PROP_ITEM_DESCRIPTION, QString()); - APPEND_ENTITY_PROPERTY(PROP_ITEM_CATEGORIES, QString()); - APPEND_ENTITY_PROPERTY(PROP_ITEM_ARTIST, QString()); - APPEND_ENTITY_PROPERTY(PROP_ITEM_LICENSE, QString()); - APPEND_ENTITY_PROPERTY(PROP_LIMITED_RUN, quint32(-1)); - APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString()); - APPEND_ENTITY_PROPERTY(PROP_EDITION_NUMBER, 0U); - APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, 0U); - APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, QString()); - APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_TYPE, QString()); - APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, 0U); +@Base_ENTITY_APPEND@ appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -810,6 +659,61 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef return otherOverwrites && simulationChanged && (valueChanged || filterRejection); }; + // When we own the simulation we don't accept updates to the entity's transform/velocities + // we also want to ignore any duplicate packets that have the same "recently updated" values + // as a packet we've already recieved. This is because we want multiple edits of the same + // information to be idempotent, but if we applied new physics properties we'd resimulation + // with small differences in results. + + // Because the regular streaming property "setters" only have access to the new value, we've + // made these lambdas that can access other details about the previous updates to suppress + // any duplicates. + + // Note: duplicate packets are expected and not wrong. They may be sent for any number of + // reasons and the contract is that the client handles them in an idempotent manner. + auto customUpdatePositionFromNetwork = [this, shouldUpdate, lastEdited](glm::vec3 value) { + if (shouldUpdate(_lastUpdatedPositionTimestamp, value != _lastUpdatedPositionValue)) { + setPosition(value); + _lastUpdatedPositionTimestamp = lastEdited; + _lastUpdatedPositionValue = value; + } + }; + auto customUpdateRotationFromNetwork = [this, shouldUpdate, lastEdited](glm::quat value) { + if (shouldUpdate(_lastUpdatedRotationTimestamp, value != _lastUpdatedRotationValue)) { + setRotation(value); + _lastUpdatedRotationTimestamp = lastEdited; + _lastUpdatedRotationValue = value; + } + }; + auto customUpdateQueryAACubeFromNetwork = [this, shouldUpdate, lastEdited](AACube value) { + if (shouldUpdate(_lastUpdatedQueryAACubeTimestamp, value != _lastUpdatedQueryAACubeValue)) { + setQueryAACube(value); + _lastUpdatedQueryAACubeTimestamp = lastEdited; + _lastUpdatedQueryAACubeValue = value; + } + }; + auto customUpdateVelocityFromNetwork = [this, shouldUpdate, lastEdited](glm::vec3 value) { + if (shouldUpdate(_lastUpdatedVelocityTimestamp, value != _lastUpdatedVelocityValue)) { + setVelocity(value); + _lastUpdatedVelocityTimestamp = lastEdited; + _lastUpdatedVelocityValue = value; + } + }; + auto customUpdateAngularVelocityFromNetwork = [this, shouldUpdate, lastEdited](glm::vec3 value){ + if (shouldUpdate(_lastUpdatedAngularVelocityTimestamp, value != _lastUpdatedAngularVelocityValue)) { + setAngularVelocity(value); + _lastUpdatedAngularVelocityTimestamp = lastEdited; + _lastUpdatedAngularVelocityValue = value; + } + }; + auto customSetAcceleration = [this, shouldUpdate, lastEdited](glm::vec3 value){ + if (shouldUpdate(_lastUpdatedAccelerationTimestamp, value != _lastUpdatedAccelerationValue)) { + setAcceleration(value); + _lastUpdatedAccelerationTimestamp = lastEdited; + _lastUpdatedAccelerationValue = value; + } + }; + // Core // PROP_SIMULATION_OWNER handled above { // parentID and parentJointIndex are protected by simulation ownership @@ -819,128 +723,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, quint16, setParentJointIndex); overwriteLocalData = oldOverwrite; } - READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible); - READ_ENTITY_PROPERTY(PROP_NAME, QString, setName); - READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked); - READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData); - READ_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, QString, setPrivateUserData); - READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref); - READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription); - { // When we own the simulation we don't accept updates to the entity's transform/velocities - // we also want to ignore any duplicate packets that have the same "recently updated" values - // as a packet we've already recieved. This is because we want multiple edits of the same - // information to be idempotent, but if we applied new physics properties we'd resimulation - // with small differences in results. - // Because the regular streaming property "setters" only have access to the new value, we've - // made these lambdas that can access other details about the previous updates to suppress - // any duplicates. +@Base_ENTITY_READ@ - // Note: duplicate packets are expected and not wrong. They may be sent for any number of - // reasons and the contract is that the client handles them in an idempotent manner. - auto customUpdatePositionFromNetwork = [this, shouldUpdate, lastEdited](glm::vec3 value) { - if (shouldUpdate(_lastUpdatedPositionTimestamp, value != _lastUpdatedPositionValue)) { - setPosition(value); - _lastUpdatedPositionTimestamp = lastEdited; - _lastUpdatedPositionValue = value; - } - }; - READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, customUpdatePositionFromNetwork); - } - READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, setScaledDimensions); - { // See comment above - auto customUpdateRotationFromNetwork = [this, shouldUpdate, lastEdited](glm::quat value) { - if (shouldUpdate(_lastUpdatedRotationTimestamp, value != _lastUpdatedRotationValue)) { - setRotation(value); - _lastUpdatedRotationTimestamp = lastEdited; - _lastUpdatedRotationValue = value; - } - }; - READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, customUpdateRotationFromNetwork); - } - READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint); - READ_ENTITY_PROPERTY(PROP_CREATED, quint64, setCreated); - READ_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, QUuid, setLastEditedBy); - // READ_ENTITY_PROPERTY(PROP_ENTITY_HOST_TYPE, entity::HostType, setEntityHostType); // not sent over the wire - // READ_ENTITY_PROPERTY(PROP_OWNING_AVATAR_ID, QUuuid, setOwningAvatarID); // not sent over the wire - { // See comment above - auto customUpdateQueryAACubeFromNetwork = [this, shouldUpdate, lastEdited](AACube value) { - if (shouldUpdate(_lastUpdatedQueryAACubeTimestamp, value != _lastUpdatedQueryAACubeValue)) { - setQueryAACube(value); - _lastUpdatedQueryAACubeTimestamp = lastEdited; - _lastUpdatedQueryAACubeValue = value; - } - }; - READ_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, AACube, customUpdateQueryAACubeFromNetwork); - } - READ_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow); - // READ_ENTITY_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, bool, setIsVisibleInSecondaryCamera); // not sent over the wire - READ_ENTITY_PROPERTY(PROP_RENDER_LAYER, RenderLayer, setRenderLayer); - READ_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, PrimitiveMode, setPrimitiveMode); - READ_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, bool, setIgnorePickIntersection); - READ_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, QVector, setRenderWithZones); - READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); - READ_ENTITY_PROPERTY(PROP_TAGS, QSet, setTags); - withWriteLock([&] { - int bytesFromGrab = _grabProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, - somethingChanged); - 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); - { - auto customUpdateVelocityFromNetwork = [this, shouldUpdate, lastEdited](glm::vec3 value) { - if (shouldUpdate(_lastUpdatedVelocityTimestamp, value != _lastUpdatedVelocityValue)) { - setVelocity(value); - _lastUpdatedVelocityTimestamp = lastEdited; - _lastUpdatedVelocityValue = value; - } - }; - READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, customUpdateVelocityFromNetwork); - auto customUpdateAngularVelocityFromNetwork = [this, shouldUpdate, lastEdited](glm::vec3 value){ - if (shouldUpdate(_lastUpdatedAngularVelocityTimestamp, value != _lastUpdatedAngularVelocityValue)) { - setAngularVelocity(value); - _lastUpdatedAngularVelocityTimestamp = lastEdited; - _lastUpdatedAngularVelocityValue = value; - } - }; - READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, customUpdateAngularVelocityFromNetwork); - READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, setGravity); - auto customSetAcceleration = [this, shouldUpdate, lastEdited](glm::vec3 value){ - if (shouldUpdate(_lastUpdatedAccelerationTimestamp, value != _lastUpdatedAccelerationValue)) { - setAcceleration(value); - _lastUpdatedAccelerationTimestamp = lastEdited; - _lastUpdatedAccelerationValue = value; - } - }; - READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, customSetAcceleration); - } - READ_ENTITY_PROPERTY(PROP_DAMPING, float, setDamping); - READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, setAngularDamping); - READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, setRestitution); - READ_ENTITY_PROPERTY(PROP_FRICTION, float, setFriction); - READ_ENTITY_PROPERTY(PROP_LIFETIME, float, setLifetime); - READ_ENTITY_PROPERTY(PROP_COLLISIONLESS, bool, setCollisionless); - READ_ENTITY_PROPERTY(PROP_COLLISION_MASK, uint16_t, setCollisionMask); - READ_ENTITY_PROPERTY(PROP_DYNAMIC, bool, setDynamic); - READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); - READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setDynamicData); - - // Cloning - READ_ENTITY_PROPERTY(PROP_CLONEABLE, bool, setCloneable); - READ_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, float, setCloneLifetime); - READ_ENTITY_PROPERTY(PROP_CLONE_LIMIT, float, setCloneLimit); - READ_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, bool, setCloneDynamic); - READ_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity); - READ_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID); - - // Scripts - READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript); - READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp); { // We use this scope to work around an issue stopping server script changes // from being received by an entity script server running a script that continously updates an entity. @@ -952,20 +737,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef overwriteLocalData = oldOverwrite; } - // Certifiable props - SKIP_ENTITY_PROPERTY(PROP_ITEM_NAME, QString); - SKIP_ENTITY_PROPERTY(PROP_ITEM_DESCRIPTION, QString); - SKIP_ENTITY_PROPERTY(PROP_ITEM_CATEGORIES, QString); - SKIP_ENTITY_PROPERTY(PROP_ITEM_ARTIST, QString); - SKIP_ENTITY_PROPERTY(PROP_ITEM_LICENSE, QString); - SKIP_ENTITY_PROPERTY(PROP_LIMITED_RUN, quint32); - SKIP_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString); - SKIP_ENTITY_PROPERTY(PROP_EDITION_NUMBER, quint32); - SKIP_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, quint32); - SKIP_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, QString); - SKIP_ENTITY_PROPERTY(PROP_CERTIFICATE_TYPE, QString); - SKIP_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, quint32); - bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, somethingChanged); @@ -1017,11 +788,15 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef } void EntityItem::debugDump() const { - auto position = getWorldPosition(); - qCDebug(entities) << "EntityItem id:" << getEntityItemID(); - qCDebug(entities, " edited ago:%f", (double)getEditedAgo()); - qCDebug(entities, " position:%f,%f,%f", (double)position.x, (double)position.y, (double)position.z); - qCDebug(entities) << " dimensions:" << getScaledDimensions(); + qCDebug(entities) << "EntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@Base_ENTITY_DEBUG@ + } // adjust any internal timestamps to fix clock skew for this server @@ -1084,7 +859,7 @@ void EntityItem::setMass(float mass) { }); } -void EntityItem::setHref(QString value) { +void EntityItem::setHref(const QString& value) { auto href = value.toLower(); // Let's let the user set the value of this property to anything, then let consumers of the property // decide what to do with it. Currently, the only in-engine consumers are `EntityTreeRenderer::mousePressEvent()` @@ -1340,77 +1115,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire properties._type = getType(); - // Core - COPY_ENTITY_PROPERTY_TO_PROPERTIES(simulationOwner, getSimulationOwner); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentID, getParentID); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentJointIndex, getParentJointIndex); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(visible, getVisible); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(privateUserData, getPrivateUserData); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getLocalPosition); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getScaledDimensions); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotation, getLocalOrientation); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(registrationPoint, getRegistrationPoint); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(created, getCreated); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(lastEditedBy, getLastEditedBy); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(entityHostType, getEntityHostType); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(owningAvatarID, getOwningAvatarIDForProperties); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(queryAACube, getQueryAACube); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(canCastShadow, getCanCastShadow); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(isVisibleInSecondaryCamera, isVisibleInSecondaryCamera); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(renderLayer, getRenderLayer); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(primitiveMode, getPrimitiveMode); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(ignorePickIntersection, getIgnorePickIntersection); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(renderWithZones, getRenderWithZones); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(tags, getTags); - 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); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(velocity, getLocalVelocity); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularVelocity, getLocalAngularVelocity); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(gravity, getGravity); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(acceleration, getAcceleration); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(damping, getDamping); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularDamping, getAngularDamping); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(restitution, getRestitution); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(friction, getFriction); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifetime, getLifetime); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionless, getCollisionless); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionMask, getCollisionMask); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(dynamic, getDynamic); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionSoundURL, getCollisionSoundURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(actionData, getDynamicData); - - // Cloning - COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneable, getCloneable); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneLifetime, getCloneLifetime); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneLimit, getCloneLimit); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneDynamic, getCloneDynamic); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneAvatarEntity, getCloneAvatarEntity); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneOriginID, getCloneOriginID); - - // Scripts - COPY_ENTITY_PROPERTY_TO_PROPERTIES(script, getScript); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(scriptTimestamp, getScriptTimestamp); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(serverScripts, getServerScripts); - - // Script local data - COPY_ENTITY_PROPERTY_TO_PROPERTIES(localPosition, getLocalPosition); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(localRotation, getLocalOrientation); - // FIXME: are these needed? - //COPY_ENTITY_PROPERTY_TO_PROPERTIES(localVelocity, getLocalVelocity); - //COPY_ENTITY_PROPERTY_TO_PROPERTIES(localAngularVelocity, getLocalAngularVelocity); - //COPY_ENTITY_PROPERTY_TO_PROPERTIES(localDimensions, getLocalDimensions); +@Base_ENTITY_COPY_TO@ properties._defaultSettings = false; @@ -1480,70 +1185,7 @@ bool EntityItem::stillWaitingToTakeOwnership(uint64_t timestamp) const { bool EntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = false; - // Core - SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulationOwner, setSimulationOwner); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentID, setParentID); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentJointIndex, setParentJointIndex); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(privateUserData, setPrivateUserData); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPosition); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, setScaledDimensions); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, setRotation); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(registrationPoint, setRegistrationPoint); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(created, setCreated); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(entityHostType, setEntityHostType); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(owningAvatarID, setOwningAvatarID); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(canCastShadow, setCanCastShadow); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(isVisibleInSecondaryCamera, setIsVisibleInSecondaryCamera); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(renderLayer, setRenderLayer); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(primitiveMode, setPrimitiveMode); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignorePickIntersection, setIgnorePickIntersection); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(renderWithZones, setRenderWithZones); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(tags, setTags); - withWriteLock([&] { - 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); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, setVelocity); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularVelocity, setAngularVelocity); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, setGravity); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(acceleration, setAcceleration); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, setDamping); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularDamping, setAngularDamping); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(restitution, setRestitution); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(friction, setFriction); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, setLifetime); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionless, setCollisionless); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionMask, setCollisionMask); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(dynamic, setDynamic); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionSoundURL, setCollisionSoundURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setDynamicData); - - // Cloning - SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneable, setCloneable); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneLifetime, setCloneLifetime); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneLimit, setCloneLimit); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneDynamic, setCloneDynamic); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneAvatarEntity, setCloneAvatarEntity); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneOriginID, setCloneOriginID); - - // Scripts - SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(scriptTimestamp, setScriptTimestamp); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(serverScripts, setServerScripts); +@Base_ENTITY_SET_FROM@ if (updateQueryAACube()) { somethingChanged = true; @@ -1822,7 +1464,7 @@ void EntityItem::setRegistrationPoint(const glm::vec3& value) { bool changed = false; withWriteLock([&] { if (value != _registrationPoint) { - _registrationPoint = glm::clamp(value, glm::vec3(ENTITY_ITEM_MIN_REGISTRATION_POINT), + _registrationPoint = glm::clamp(value, glm::vec3(ENTITY_ITEM_MIN_REGISTRATION_POINT), glm::vec3(ENTITY_ITEM_MAX_REGISTRATION_POINT)); changed = true; } @@ -2148,6 +1790,10 @@ void EntityItem::computeCollisionGroupAndFinalMask(int32_t& group, int32_t& mask } } +SimulationOwner EntityItem::getSimulationOwner() const { + return _simulationOwner; +} + void EntityItem::setSimulationOwner(const QUuid& id, uint8_t priority) { if (wantTerseEditLogging() && (id != _simulationOwner.getID() || priority != _simulationOwner.getPriority())) { qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << id << priority; @@ -2716,11 +2362,9 @@ bool EntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const { } quint64 EntityItem::getLastSimulated() const { - quint64 result; - withReadLock([&] { - result = _lastSimulated; + return resultWithReadLock([&] { + return _lastSimulated; }); - return result; } void EntityItem::setLastSimulated(quint64 now) { @@ -2730,11 +2374,9 @@ void EntityItem::setLastSimulated(quint64 now) { } quint64 EntityItem::getLastEdited() const { - quint64 result; - withReadLock([&] { - result = _lastEdited; + return resultWithReadLock([&] { + return _lastEdited; }); - return result; } void EntityItem::setLastEdited(quint64 lastEdited) { @@ -2753,11 +2395,9 @@ void EntityItem::markAsChangedOnServer() { } quint64 EntityItem::getLastChangedOnServer() const { - quint64 result; - withReadLock([&] { - result = _changedOnServer; + return resultWithReadLock([&] { + return _changedOnServer; }); - return result; } void EntityItem::update(const quint64& now) { @@ -2767,11 +2407,9 @@ void EntityItem::update(const quint64& now) { } quint64 EntityItem::getLastUpdated() const { - quint64 result; - withReadLock([&] { - result = _lastUpdated; + return resultWithReadLock([&] { + return _lastUpdated; }); - return result; } void EntityItem::requiresRecalcBoxes() { @@ -2782,111 +2420,65 @@ void EntityItem::requiresRecalcBoxes() { }); } -QString EntityItem::getHref() const { - QString result; - withReadLock([&] { - result = _href; +QString EntityItem::getUserData() const { + return resultWithReadLock([&] { + return _userData; }); - return result; } -QString EntityItem::getDescription() const { - QString result; - withReadLock([&] { - result = _description; - }); - return result; -} - -void EntityItem::setDescription(const QString& value) { +void EntityItem::setUserData(const QString& value) { withWriteLock([&] { - _description = value; + _userData = value; + }); +} + +QString EntityItem::getHref() const { + return resultWithReadLock([&] { + return _href; }); } glm::vec3 EntityItem::getGravity() const { - glm::vec3 result; - withReadLock([&] { - result = _gravity; - }); - return result; -} - -glm::vec3 EntityItem::getAcceleration() const { - glm::vec3 result; - withReadLock([&] { - result = _acceleration; - }); - return result; -} - -void EntityItem::setAcceleration(const glm::vec3& value) { - withWriteLock([&] { - _acceleration = value; + return resultWithReadLock([&] { + return _gravity; }); } float EntityItem::getDamping() const { - float result; - withReadLock([&] { - result = _damping; + return resultWithReadLock([&] { + return _damping; }); - return result; } float EntityItem::getRestitution() const { - float result; - withReadLock([&] { - result = _restitution; + return resultWithReadLock([&] { + return _restitution; }); - return result; } float EntityItem::getFriction() const { - float result; - withReadLock([&] { - result = _friction; + return resultWithReadLock([&] { + return _friction; }); - return result; } // lifetime related properties. float EntityItem::getLifetime() const { - float result; - withReadLock([&] { - result = _lifetime; + return resultWithReadLock([&] { + return _lifetime; }); - return result; } quint64 EntityItem::getCreated() const { - quint64 result; - withReadLock([&] { - result = _created; - }); - return result; -} - -QString EntityItem::getScript() const { - QString result; - withReadLock([&] { - result = _script; - }); - return result; -} - -void EntityItem::setScript(const QString& value) { - withWriteLock([&] { - _script = value; + return resultWithReadLock([&] { + return _created; }); } quint64 EntityItem::getScriptTimestamp() const { - quint64 result; - withReadLock([&] { - result = _scriptTimestamp; + return resultWithReadLock([&] { + return _scriptTimestamp; }); - return result; } void EntityItem::setScriptTimestamp(const quint64 value) { @@ -2896,11 +2488,9 @@ void EntityItem::setScriptTimestamp(const quint64 value) { } QString EntityItem::getServerScripts() const { - QString result; - withReadLock([&] { - result = _serverScripts; + return resultWithReadLock([&] { + return _serverScripts; }); - return result; } void EntityItem::setServerScripts(const QString& serverScripts) { @@ -2911,11 +2501,9 @@ void EntityItem::setServerScripts(const QString& serverScripts) { } QString EntityItem::getCollisionSoundURL() const { - QString result; - withReadLock([&] { - result = _collisionSoundURL; + return resultWithReadLock([&] { + return _collisionSoundURL; }); - return result; } glm::vec3 EntityItem::getRegistrationPoint() const { @@ -2925,19 +2513,15 @@ glm::vec3 EntityItem::getRegistrationPoint() const { } float EntityItem::getAngularDamping() const { - float result; - withReadLock([&] { - result = _angularDamping; + return resultWithReadLock([&] { + return _angularDamping; }); - return result; } QString EntityItem::getName() const { - QString result; - withReadLock([&] { - result = _name; + return resultWithReadLock([&] { + return _name; }); - return result; } void EntityItem::setName(const QString& value) { @@ -2954,64 +2538,6 @@ QString EntityItem::getDebugName() { return result; } -bool EntityItem::getVisible() const { - bool result; - withReadLock([&] { - result = _visible; - }); - return result; -} - -void EntityItem::setVisible(bool value) { - bool changed; - withWriteLock([&] { - changed = _visible != value; - _needsRenderUpdate |= changed; - _visible = value; - }); -} - -bool EntityItem::isVisibleInSecondaryCamera() const { - bool result; - withReadLock([&] { - result = _isVisibleInSecondaryCamera; - }); - return result; -} - -void EntityItem::setIsVisibleInSecondaryCamera(bool value) { - withWriteLock([&] { - _needsRenderUpdate |= _isVisibleInSecondaryCamera != value; - _isVisibleInSecondaryCamera = value; - }); -} - -RenderLayer EntityItem::getRenderLayer() const { - return resultWithReadLock([&] { - return _renderLayer; - }); -} - -void EntityItem::setRenderLayer(RenderLayer value) { - withWriteLock([&] { - _needsRenderUpdate |= _renderLayer != value; - _renderLayer = value; - }); -} - -PrimitiveMode EntityItem::getPrimitiveMode() const { - return resultWithReadLock([&] { - return _primitiveMode; - }); -} - -void EntityItem::setPrimitiveMode(PrimitiveMode value) { - withWriteLock([&] { - _needsRenderUpdate |= _primitiveMode != value; - _primitiveMode = value; - }); -} - bool EntityItem::getCauterized() const { return resultWithReadLock([&] { return _cauterized; @@ -3030,39 +2556,10 @@ void EntityItem::setCauterized(bool value) { } } -bool EntityItem::getIgnorePickIntersection() const { - return resultWithReadLock([&] { - return _ignorePickIntersection; - }); -} - -void EntityItem::setIgnorePickIntersection(bool value) { - withWriteLock([&] { - _ignorePickIntersection = value; - }); -} - -bool EntityItem::getCanCastShadow() const { - bool result; - withReadLock([&] { - result = _canCastShadow; - }); - return result; -} - -void EntityItem::setCanCastShadow(bool value) { - withWriteLock([&] { - _needsRenderUpdate |= _canCastShadow != value; - _canCastShadow = value; - }); -} - bool EntityItem::getCullWithParent() const { - bool result; - withReadLock([&] { - result = _cullWithParent; + return resultWithReadLock([&] { + return _cullWithParent; }); - return result; } void EntityItem::setCullWithParent(bool value) { @@ -3083,11 +2580,9 @@ bool EntityItem::isChildOfMyAvatar() const { } bool EntityItem::getCollisionless() const { - bool result; - withReadLock([&] { - result = _collisionless; + return resultWithReadLock([&] { + return _collisionless; }); - return result; } uint16_t EntityItem::getCollisionMask() const { @@ -3122,40 +2617,10 @@ void EntityItem::setLocked(bool value) { } } -QString EntityItem::getUserData() const { - QString result; - withReadLock([&] { - result = _userData; - }); - return result; -} - -void EntityItem::setUserData(const QString& value) { - withWriteLock([&] { - _userData = value; - }); -} - -QString EntityItem::getPrivateUserData() const { - QString result; - withReadLock([&] { - result = _privateUserData; - }); - return result; -} - -void EntityItem::setPrivateUserData(const QString& value) { - withWriteLock([&] { - _privateUserData = value; - }); -} - uint32_t EntityItem::getDirtyFlags() const { - uint32_t result; - withReadLock([&] { - result = _flags & Simulation::DIRTY_FLAGS_MASK; + return resultWithReadLock([&] { + return _flags & Simulation::DIRTY_FLAGS_MASK; }); - return result; } void EntityItem::markDirtyFlags(uint32_t mask) { @@ -3173,11 +2638,9 @@ void EntityItem::clearDirtyFlags(uint32_t mask) { } uint32_t EntityItem::getSpecialFlags() const { - uint32_t result; - withReadLock([&] { - result = _flags & Simulation::SPECIAL_FLAGS_MASK; + return resultWithReadLock([&] { + return _flags & Simulation::SPECIAL_FLAGS_MASK; }); - return result; } void EntityItem::markSpecialFlags(uint32_t mask) { @@ -3195,11 +2658,9 @@ void EntityItem::clearSpecialFlags(uint32_t mask) { } float EntityItem::getDensity() const { - float result; - withReadLock([&] { - result = _density; + return resultWithReadLock([&] { + return _density; }); - return result; } EntityItem::ChangeHandlerId EntityItem::registerChangeHandler(const ChangeHandlerCallback& handler) { @@ -3250,90 +2711,6 @@ void EntityItem::setSpaceIndex(int32_t index) { void EntityItem::preDelete() { } -bool EntityItem::getCloneable() const { - bool result; - withReadLock([&] { - result = _cloneable; - }); - return result; -} - -void EntityItem::setCloneable(bool value) { - withWriteLock([&] { - _cloneable = value; - }); -} - -float EntityItem::getCloneLifetime() const { - float result; - withReadLock([&] { - result = _cloneLifetime; - }); - return result; -} - -void EntityItem::setCloneLifetime(float value) { - withWriteLock([&] { - _cloneLifetime = value; - }); -} - -float EntityItem::getCloneLimit() const { - float result; - withReadLock([&] { - result = _cloneLimit; - }); - return result; -} - -void EntityItem::setCloneLimit(float value) { - withWriteLock([&] { - _cloneLimit = value; - }); -} - -bool EntityItem::getCloneDynamic() const { - bool result; - withReadLock([&] { - result = _cloneDynamic; - }); - return result; -} - -void EntityItem::setCloneDynamic(bool value) { - withWriteLock([&] { - _cloneDynamic = value; - }); -} - -bool EntityItem::getCloneAvatarEntity() const { - bool result; - withReadLock([&] { - result = _cloneAvatarEntity; - }); - return result; -} - -void EntityItem::setCloneAvatarEntity(bool value) { - withWriteLock([&] { - _cloneAvatarEntity = value; - }); -} - -const QUuid EntityItem::getCloneOriginID() const { - QUuid result; - withReadLock([&] { - result = _cloneOriginID; - }); - return result; -} - -void EntityItem::setCloneOriginID(const QUuid& value) { - withWriteLock([&] { - _cloneOriginID = value; - }); -} - void EntityItem::addCloneID(const QUuid& cloneID) { withWriteLock([&] { if (!_cloneIDs.contains(cloneID)) { @@ -3352,11 +2729,9 @@ void EntityItem::removeCloneID(const QUuid& cloneID) { } const QVector EntityItem::getCloneIDs() const { - QVector result; - withReadLock([&] { - result = _cloneIDs; + return resultWithReadLock>([&] { + return _cloneIDs; }); - return result; } void EntityItem::setCloneIDs(const QVector& cloneIDs) { @@ -3418,7 +2793,7 @@ bool EntityItem::isWearable() const { } bool EntityItem::isMyAvatarEntity() const { - return _hostType == entity::HostType::AVATAR && AVATAR_SELF_ID == _owningAvatarID; + return _entityHostType == entity::HostType::AVATAR && AVATAR_SELF_ID == _owningAvatarID; }; QUuid EntityItem::getOwningAvatarIDForProperties() const { @@ -3431,6 +2806,10 @@ QUuid EntityItem::getOwningAvatarIDForProperties() const { return _owningAvatarID; } +QUuid EntityItem::getOwningAvatarID() const { + return _owningAvatarID; +} + void EntityItem::setOwningAvatarID(const QUuid& owningAvatarID) { if (!owningAvatarID.isNull() && owningAvatarID == Physics::getSessionUUID()) { _owningAvatarID = AVATAR_SELF_ID; @@ -3551,54 +2930,3 @@ QVector EntityItem::getRenderWithZones() const { return _renderWithZones; }); } - -BillboardMode EntityItem::getBillboardMode() const { - return resultWithReadLock([&] { - return _billboardMode; - }); -} - -void EntityItem::setBillboardMode(BillboardMode value) { - withWriteLock([&] { - _needsRenderUpdate |= _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; - }); -} - -void EntityItem::setTags(const QSet& tags) { - withWriteLock([&] { - _tags = tags; - }); -} - -QSet EntityItem::getTags() const { - return resultWithReadLock>([&] { - return _tags; - }); -} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h.in similarity index 79% rename from libraries/entities/src/EntityItem.h rename to libraries/entities/src/EntityItem.h.in index 84ec70e9a1..d1d4c49352 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h.in @@ -147,9 +147,6 @@ public: int& propertyCount, OctreeElement::AppendState& appendState) const { /* do nothing*/ }; - static EntityItemID readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args); - int readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, @@ -182,25 +179,21 @@ public: const glm::vec3& acceleration, const glm::vec3& viewFrustumPos, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) const { return true; } + virtual bool getRotateForPicking() const { return false; } // attributes applicable to all entity types EntityTypes::EntityType getType() const { return _type; } + QString getUserData() const; + virtual void setUserData(const QString& value); + inline glm::vec3 getCenterPosition(bool& success) const { return getTransformToCenter(success).getTranslation(); } - void setCenterPosition(const glm::vec3& position); const Transform getTransformToCenter(bool& success) const; const Transform getTransformToCenterWithOnlyLocalRotation(bool& success) const; void requiresRecalcBoxes(); - // Hyperlink related getters and setters - QString getHref() const; - void setHref(QString value); - - QString getDescription() const; - void setDescription(const QString& value); - /// Dimensions in meters (0.0 - TREE_SCALE) virtual glm::vec3 getScaledDimensions() const; virtual void setScaledDimensions(const glm::vec3& value); @@ -210,39 +203,14 @@ public: glm::vec3 getUnscaledDimensions() const; virtual void setUnscaledDimensions(const glm::vec3& value); - void setDensity(float density); float computeMass() const; void setMass(float mass); - float getDensity() const; - bool hasVelocity() const { return getWorldVelocity() != ENTITY_ITEM_ZERO_VEC3; } bool hasLocalVelocity() const { return getLocalVelocity() != ENTITY_ITEM_ZERO_VEC3; } - - glm::vec3 getGravity() const; /// get gravity in meters - void setGravity(const glm::vec3& value); /// gravity in meters bool hasGravity() const { return getGravity() != ENTITY_ITEM_ZERO_VEC3; } - - glm::vec3 getAcceleration() const; /// get acceleration in meters/second/second - void setAcceleration(const glm::vec3& value); /// acceleration in meters/second/second bool hasAcceleration() const { return getAcceleration() != ENTITY_ITEM_ZERO_VEC3; } - float getDamping() const; - void setDamping(float value); - - float getRestitution() const; - void setRestitution(float value); - - float getFriction() const; - void setFriction(float value); - - // lifetime related properties. - float getLifetime() const; /// get the lifetime in seconds for the entity - void setLifetime(float value); /// set the lifetime in seconds for the entity - - quint64 getCreated() const; /// get the created-time in useconds for the entity - void setCreated(quint64 value); /// set the created-time in useconds for the entity - /// is this entity immortal, in that it has no lifetime set, and will exist until manually deleted bool isImmortal() const { return getLifetime() == ENTITY_ITEM_IMMORTAL_LIFETIME; } @@ -263,18 +231,6 @@ public: virtual AACube getQueryAACube(bool& success) const override; virtual bool shouldPuffQueryAACube() const override; - QString getScript() const; - void setScript(const QString& value); - - quint64 getScriptTimestamp() const; - void setScriptTimestamp(const quint64 value); - - QString getServerScripts() const; - void setServerScripts(const QString& serverScripts); - - QString getCollisionSoundURL() const; - void setCollisionSoundURL(const QString& value); - glm::vec3 getRegistrationPoint() const; /// registration point as ratio of entity /// registration point as ratio of entity virtual void setRegistrationPoint(const glm::vec3& value); // FIXME: this is suspicious! @@ -284,31 +240,10 @@ public: virtual void setAngularVelocity(const glm::vec3& angularVelocity); - float getAngularDamping() const; - void setAngularDamping(float value); - virtual QString getName() const override; void setName(const QString& value); QString getDebugName(); - bool getVisible() const; - void setVisible(bool value); - - bool isVisibleInSecondaryCamera() const; - void setIsVisibleInSecondaryCamera(bool value); - - RenderLayer getRenderLayer() const; - void setRenderLayer(RenderLayer value); - - PrimitiveMode getPrimitiveMode() const; - void setPrimitiveMode(PrimitiveMode value); - - bool getIgnorePickIntersection() const; - void setIgnorePickIntersection(bool value); - - bool getCanCastShadow() const; - void setCanCastShadow(bool value); - bool getCullWithParent() const; void setCullWithParent(bool value); @@ -320,33 +255,13 @@ public: bool isChildOfMyAvatar() const; - bool getCollisionless() const; - void setCollisionless(bool value); - - uint16_t getCollisionMask() const; - void setCollisionMask(uint16_t value); - void computeCollisionGroupAndFinalMask(int32_t& group, int32_t& mask) const; - bool getDynamic() const; - void setDynamic(bool value); - virtual bool shouldBePhysical() const { return !isDead() && getShapeType() != SHAPE_TYPE_NONE && !isLocalEntity(); } bool isVisuallyReady() const { return _visuallyReady; } - bool getLocked() const; - void setLocked(bool value); - - QString getUserData() const; - virtual void setUserData(const QString& value); // FIXME: This is suspicious - - QString getPrivateUserData() const; - void setPrivateUserData(const QString& value); - // FIXME not thread safe? - const SimulationOwner& getSimulationOwner() const { return _simulationOwner; } void setSimulationOwner(const QUuid& id, uint8_t priority); - void setSimulationOwner(const SimulationOwner& owner); uint8_t getSimulationPriority() const { return _simulationOwner.getPriority(); } QUuid getSimulatorID() const { return _simulationOwner.getID(); } @@ -363,19 +278,6 @@ public: bool pendingRelease(uint64_t timestamp) const; bool stillWaitingToTakeOwnership(uint64_t timestamp) const; - bool getCloneable() const; - void setCloneable(bool value); - float getCloneLifetime() const; - void setCloneLifetime(float value); - float getCloneLimit() const; - void setCloneLimit(float value); - bool getCloneDynamic() const; - void setCloneDynamic(bool value); - bool getCloneAvatarEntity() const; - void setCloneAvatarEntity(bool value); - const QUuid getCloneOriginID() const; - void setCloneOriginID(const QUuid& value); - // TODO: get rid of users of getRadius()... float getRadius() const; @@ -486,26 +388,19 @@ public: void setScriptHasFinishedPreload(bool value); bool isScriptPreloadFinished(); virtual bool isWearable() const; - bool isDomainEntity() const { return _hostType == entity::HostType::DOMAIN; } - bool isAvatarEntity() const { return _hostType == entity::HostType::AVATAR; } + bool isDomainEntity() const { return _entityHostType == entity::HostType::DOMAIN; } + bool isAvatarEntity() const { return _entityHostType == entity::HostType::AVATAR; } bool isMyAvatarEntity() const; - bool isLocalEntity() const { return _hostType == entity::HostType::LOCAL; } - entity::HostType getEntityHostType() const { return _hostType; } - virtual void setEntityHostType(entity::HostType hostType) { _hostType = hostType; } + bool isLocalEntity() const { return _entityHostType == entity::HostType::LOCAL; } // if this entity is an avatar entity, which avatar is it associated with? - QUuid getOwningAvatarID() const { return _owningAvatarID; } QUuid getOwningAvatarIDForProperties() const; - void setOwningAvatarID(const QUuid& owningAvatarID); virtual bool wantsHandControllerPointerEvents() const { return false; } virtual bool wantsKeyboardFocus() const { return false; } virtual void setProxyWindow(QWindow* proxyWindow) {} virtual QObject* getEventHandler() { return nullptr; } - QUuid getLastEditedBy() const { return _lastEditedBy; } - void setLastEditedBy(QUuid value) { _lastEditedBy = value; } - virtual bool matchesJSONFilters(const QJsonObject& jsonFilters) const; virtual bool getMeshes(MeshProxyList& result) { return true; } @@ -550,28 +445,16 @@ public: bool needsRenderUpdate() const { return _needsRenderUpdate; } void setNeedsRenderUpdate(bool needsRenderUpdate) { _needsRenderUpdate = needsRenderUpdate; } - void setRenderWithZones(const QVector& renderWithZones); - QVector getRenderWithZones() const; bool needsZoneOcclusionUpdate() const { return _needsZoneOcclusionUpdate; } void resetNeedsZoneOcclusionUpdate() { withWriteLock([&] { _needsZoneOcclusionUpdate = false; }); } - void setBillboardMode(BillboardMode value); - BillboardMode getBillboardMode() const; - virtual bool getRotateForPicking() const { return false; } - - MirrorMode getMirrorMode() const; - void setMirrorMode(MirrorMode value); - - QUuid getPortalExitID() const; - void setPortalExitID(const QUuid& value); - - void setTags(const QSet& tags); - QSet getTags() const; - signals: void spaceUpdate(std::pair data); protected: + +@Base_ENTITY_PROPS@ + QHash _changeHandlers; void somethingChangedNotification(); @@ -590,12 +473,10 @@ protected: // and physics changes quint64 _lastUpdated { 0 }; // last time this entity called update(), this includes animations and non-physics changes quint64 _lastEdited { 0 }; // last official local or remote edit time - QUuid _lastEditedBy { ENTITY_ITEM_DEFAULT_LAST_EDITED_BY }; // id of last editor quint64 _lastBroadcast; // the last time we sent an edit packet about this entity quint64 _lastEditedFromRemote { 0 }; // last time we received and edit from the server quint64 _lastEditedFromRemoteInRemoteTime { 0 }; // last time we received an edit from the server (in server-time-frame) - quint64 _created { 0 }; quint64 _changedOnServer { 0 }; mutable AABox _cachedAABox; @@ -605,24 +486,14 @@ protected: mutable bool _recalcMinAACube { true }; mutable bool _recalcMaxAACube { true }; - float _density { ENTITY_ITEM_DEFAULT_DENSITY }; // kg/m^3 // NOTE: _volumeMultiplier is used to allow some mass properties code exist in the EntityItem base class // rather than in all of the derived classes. If we ever collapse these classes to one we could do it a // different way. float _volumeMultiplier { 1.0f }; - glm::vec3 _gravity { ENTITY_ITEM_DEFAULT_GRAVITY }; - glm::vec3 _acceleration { ENTITY_ITEM_DEFAULT_ACCELERATION }; - float _damping { ENTITY_ITEM_DEFAULT_DAMPING }; - float _restitution { ENTITY_ITEM_DEFAULT_RESTITUTION }; - float _friction { ENTITY_ITEM_DEFAULT_FRICTION }; - float _lifetime { ENTITY_ITEM_DEFAULT_LIFETIME }; - QString _script { ENTITY_ITEM_DEFAULT_SCRIPT }; /// the value of the script property QString _loadedScript; /// the value of _script when the last preload signal was sent - quint64 _scriptTimestamp { ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP }; /// the script loaded property used for forced reload bool _scriptPreloadFinished { false }; - QString _serverScripts; /// keep track of time when _serverScripts property was last changed quint64 _serverScriptsChangedTimestamp { ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP }; @@ -630,27 +501,8 @@ protected: // NOTE: on construction we want this to be different from _scriptTimestamp so we intentionally bump it quint64 _loadedScriptTimestamp { ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP + 1 }; - QString _collisionSoundURL { ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL }; glm::vec3 _registrationPoint { ENTITY_ITEM_DEFAULT_REGISTRATION_POINT }; - float _angularDamping { ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING }; - bool _visible { ENTITY_ITEM_DEFAULT_VISIBLE }; - bool _isVisibleInSecondaryCamera { ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA }; - RenderLayer _renderLayer { RenderLayer::WORLD }; - PrimitiveMode _primitiveMode { PrimitiveMode::SOLID }; - bool _canCastShadow{ ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW }; - bool _ignorePickIntersection { false }; - bool _collisionless { ENTITY_ITEM_DEFAULT_COLLISIONLESS }; - uint16_t _collisionMask { ENTITY_COLLISION_MASK_DEFAULT }; - bool _dynamic { ENTITY_ITEM_DEFAULT_DYNAMIC }; - bool _locked { ENTITY_ITEM_DEFAULT_LOCKED }; - QString _userData { ENTITY_ITEM_DEFAULT_USER_DATA }; - QString _privateUserData{ ENTITY_ITEM_DEFAULT_PRIVATE_USER_DATA }; - SimulationOwner _simulationOwner; bool _shouldHighlight { false }; - QString _name { ENTITY_ITEM_DEFAULT_NAME }; - QString _href; //Hyperlink href - QString _description; //Hyperlink description - // NOTE: Damping is applied like this: v *= pow(1 - damping, dt) // @@ -696,9 +548,7 @@ protected: QUuid _sourceUUID; /// the server node UUID we came from - entity::HostType _hostType { entity::HostType::DOMAIN }; bool _transitingWithAvatar{ false }; - QUuid _owningAvatarID; // physics related changes from the network to suppress any duplicates and make // sure redundant applications are idempotent @@ -730,30 +580,14 @@ protected: bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera - bool _cloneable { ENTITY_ITEM_DEFAULT_CLONEABLE }; - float _cloneLifetime { ENTITY_ITEM_DEFAULT_CLONE_LIFETIME }; - float _cloneLimit { ENTITY_ITEM_DEFAULT_CLONE_LIMIT }; - bool _cloneDynamic { ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC }; - bool _cloneAvatarEntity { ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY }; - QUuid _cloneOriginID; QVector _cloneIDs; - GrabPropertyGroup _grabProperties; - QHash _grabActions; - QVector _renderWithZones; mutable bool _needsZoneOcclusionUpdate { false }; - BillboardMode _billboardMode { BillboardMode::NONE }; - bool _cullWithParent { false }; - MirrorMode _mirrorMode { MirrorMode::NONE }; - QUuid _portalExitID; - - QSet _tags; - mutable bool _needsRenderUpdate { false }; }; diff --git a/libraries/entities/src/EntityItemGroupProperties.txt b/libraries/entities/src/EntityItemGroupProperties.txt new file mode 100644 index 0000000000..46051c76cb --- /dev/null +++ b/libraries/entities/src/EntityItemGroupProperties.txt @@ -0,0 +1,101 @@ +grab +enum:GRAB_GRABBABLE prop:grabbable type:bool default:INITIAL_GRABBABLE, +enum:GRAB_KINEMATIC prop:grabKinematic type:bool default:INITIAL_KINEMATIC, +enum:GRAB_FOLLOWS_CONTROLLER prop:grabFollowsController type:bool default:INITIAL_FOLLOWS_CONTROLLER, +enum:GRAB_TRIGGERABLE prop:triggerable type:bool default:INITIAL_TRIGGERABLE, +enum:GRAB_EQUIPPABLE prop:equippable type:bool default:INITIAL_EQUIPPABLE, +enum:GRAB_DELEGATE_TO_PARENT prop:grabDelegateToParent type:bool default:INITIAL_GRAB_DELEGATE_TO_PARENT, +enum:GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET prop:equippableLeftPosition type:vec3 default:INITIAL_LEFT_EQUIPPABLE_POSITION, +enum:GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET prop:equippableLeftRotation type:quat default:INITIAL_LEFT_EQUIPPABLE_ROTATION, +enum:GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET prop:equippableRightPosition type:vec3 default:INITIAL_RIGHT_EQUIPPABLE_POSITION, +enum:GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET prop:equippableRightRotation type:quat default:INITIAL_RIGHT_EQUIPPABLE_ROTATION, +enum:GRAB_EQUIPPABLE_INDICATOR_URL prop:equippableIndicatorURL type:QString default:"", +enum:GRAB_EQUIPPABLE_INDICATOR_SCALE prop:equippableIndicatorScale type:vec3 default:INITIAL_EQUIPPABLE_INDICATOR_SCALE, +enum:GRAB_EQUIPPABLE_INDICATOR_OFFSET prop:equippableIndicatorOffset type:vec3 default:INITIAL_EQUIPPABLE_INDICATOR_OFFSET, +pulse +enum:PULSE_MIN prop:min type:float default:0.0f, +enum:PULSE_MAX prop:max type:float default:1.0f, +enum:PULSE_PERIOD prop:period type:float default:1.0f, +enum:PULSE_COLOR_MODE prop:colorMode type:PulseMode default:PulseMode::NONE enum, +enum:PULSE_ALPHA_MODE prop:alphaMode type:PulseMode default:PulseMode::NONE enum, +animation +enum:ANIMATION_URL prop:url type:QString default:"" urlPermission, +enum:ANIMATION_FPS prop:fps type:float default:30.0f, +enum:ANIMATION_FRAME_INDEX prop:currentFrame type:float default:0.0f, +enum:ANIMATION_PLAYING prop:running type:bool default:false, +enum:ANIMATION_LOOP prop:loop type:bool default:true, +enum:ANIMATION_FIRST_FRAME prop:firstFrame type:float default:0.0f, +enum:ANIMATION_LAST_FRAME prop:lastFrame type:float default:MAXIMUM_POSSIBLE_FRAME, +enum:ANIMATION_HOLD prop:hold type:bool default:false, +enum:ANIMATION_ALLOW_TRANSLATION prop:allowTranslation type:bool default:true, +enum:ANIMATION_SMOOTH_FRAMES prop:smoothFrames type:bool default:true, +keyLight +enum:KEYLIGHT_COLOR prop:color type:u8vec3Color default:DEFAULT_KEYLIGHT_COLOR, +enum:KEYLIGHT_INTENSITY prop:intensity type:float default:DEFAULT_KEYLIGHT_INTENSITY, +enum:KEYLIGHT_DIRECTION prop:direction type:vec3 default:DEFAULT_KEYLIGHT_DIRECTION, +enum:KEYLIGHT_CAST_SHADOW prop:castShadows type:bool default:DEFAULT_KEYLIGHT_CAST_SHADOWS, +enum:KEYLIGHT_SHADOW_BIAS prop:shadowBias type:float default:DEFAULT_KEYLIGHT_SHADOW_BIAS min:0.0f max:1.0f, +enum:KEYLIGHT_SHADOW_MAX_DISTANCE prop:shadowMaxDistance type:float default:DEFAULT_KEYLIGHT_SHADOW_MAX_DISTANCE min:1.0f max:250.0f, +ambientLight +enum:AMBIENT_LIGHT_INTENSITY prop:ambientIntensity type:float default:DEFAULT_AMBIENT_LIGHT_INTENSITY, +enum:AMBIENT_LIGHT_URL prop:ambientURL type:QString default:"" urlPermission, +enum:AMBIENT_LIGHT_COLOR prop:ambientColor type:u8vec3Color default:DEFAULT_COLOR, +skybox +enum:SKYBOX_COLOR prop:color type:u8vec3Color default:DEFAULT_COLOR, +enum:SKYBOX_URL prop:url type:QString default:"" urlPermission, +haze +enum:HAZE_RANGE prop:hazeRange type:float default:INITIAL_HAZE_RANGE, +enum:HAZE_COLOR prop:hazeColor type:u8vec3Color default:initialHazeColor, +enum:HAZE_GLARE_COLOR prop:hazeGlareColor type:u8vec3Color default:initialHazeGlareColor, +enum:HAZE_ENABLE_GLARE prop:hazeEnableGlare type:bool default:false, +enum:HAZE_GLARE_ANGLE prop:hazeGlareAngle type:float default:INITIAL_HAZE_GLARE_ANGLE, +enum:HAZE_ALTITUDE_EFFECT prop:hazeAltitudeEffect type:bool default:false, +enum:HAZE_CEILING prop:hazeCeiling type:float default:INITIAL_HAZE_BASE_REFERENCE+INITIAL_HAZE_HEIGHT, +enum:HAZE_BASE_REF prop:hazeBaseRef type:float default:INITIAL_HAZE_BASE_REFERENCE, +enum:HAZE_BACKGROUND_BLEND prop:hazeBackgroundBlend type:float default:INITIAL_HAZE_BACKGROUND_BLEND, +enum:HAZE_ALTITUDE_EFFECT prop:hazeAttenuateKeyLight type:bool default:false, +enum:HAZE_CEILING prop:hazeKeyLightRange type:float default:INITIAL_KEY_LIGHT_RANGE, +enum:HAZE_BASE_REF prop:hazeKeyLightAltitude type:float default:INITIAL_KEY_LIGHT_ALTITUDE, +bloom +enum:BLOOM_INTENSITY prop:bloomIntensity type:float default:INITIAL_BLOOM_INTENSITY, +enum:BLOOM_THRESHOLD prop:bloomThreshold type:float default:INITIAL_BLOOM_THRESHOLD, +enum:BLOOM_SIZE prop:bloomSize type:float default:INITIAL_BLOOM_SIZE, +audio type:ZoneAudio +enum:REVERB_ENABLED prop:reverbEnabled type:bool default:false, +enum:REVERB_TIME prop:reverbTime type:float default:1.0f, +enum:REVERB_WET_LEVEL prop:reverbWetLevel type:float default:50.0f, +enum:LISTENER_ZONES prop:listenerZones type:qVectorQUuid default:QVector(), +enum:LISTENER_ATTENUATION_COEFFICIENTS prop:listenerAttenuationCoefficients type:qVectorFloat default:QVector(), +tonemapping +enum:TONEMAPPING_CURVE prop:curve type:TonemappingCurve default:TonemappingCurve::SRGB enum, +enum:TONEMAPPING_EXPOSURE prop:exposure type:float default:0.0f min:-4.0f max:4.0f, +ambientOcclusion +enum:AMBIENT_OCCLUSION_TECHNIQUE prop:technique type:AmbientOcclusionTechnique default:AmbientOcclusionTechnique::HBAO enum, +enum:AMBIENT_OCCLUSION_JITTER prop:jitter type:bool default:false, +enum:AMBIENT_OCCLUSION_RESOLUTION_LEVEL prop:resolutionLevel type:uint8_t default:2 min:0 max:4, +enum:AMBIENT_OCCLUSION_EDGE_SHARPNESS prop:edgeSharpness type:float default:1.0f min:0.0f max:1.0f, +enum:AMBIENT_OCCLUSION_BLUR_RADIUS prop:blurRadius type:uint8_t default:4 min:0 max:15, +enum:AMBIENT_OCCLUSION_AO_RADIUS prop:aoRadius type:float default:1.0f min:0.01f max:2.0f, +enum:AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL prop:aoObscuranceLevel type:float default:0.5f min:0.01f max:1.0f, +enum:AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE prop:aoFalloffAngle type:float default:0.25f min:0.0f max:1.0f, +enum:AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT prop:aoSamplingAmount type:float default:0.5f min:0.0f max:1.0f, +enum:AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS prop:ssaoNumSpiralTurns type:float default:7.0f min:0.0f max:10.0f, +ring type:RingGizmo +enum:START_ANGLE prop:startAngle type:float default:0.0f min:RingGizmoPropertyGroup::MIN_ANGLE max:RingGizmoPropertyGroup::MAX_ANGLE, +enum:END_ANGLE prop:endAngle type:float default:360.0f min:RingGizmoPropertyGroup::MIN_ANGLE max:RingGizmoPropertyGroup::MAX_ANGLE, +enum:INNER_RADIUS prop:innerRadius type:float default:0.0f min:RingGizmoPropertyGroup::MIN_RADIUS max:RingGizmoPropertyGroup::MAX_RADIUS, +enum:INNER_START_COLOR prop:innerStartColor type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR, +enum:INNER_END_COLOR prop:innerEndColor type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR, +enum:OUTER_START_COLOR prop:outerStartColor type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR, +enum:OUTER_END_COLOR prop:outerEndColor type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR, +enum:INNER_START_ALPHA prop:innerStartAlpha type:float default:ENTITY_ITEM_DEFAULT_ALPHA min:RingGizmoPropertyGroup::MIN_ALPHA max:RingGizmoPropertyGroup::MAX_ALPHA, +enum:INNER_END_ALPHA prop:innerEndAlpha type:float default:ENTITY_ITEM_DEFAULT_ALPHA min:RingGizmoPropertyGroup::MIN_ALPHA max:RingGizmoPropertyGroup::MAX_ALPHA, +enum:OUTER_START_ALPHA prop:outerStartAlpha type:float default:ENTITY_ITEM_DEFAULT_ALPHA min:RingGizmoPropertyGroup::MIN_ALPHA max:RingGizmoPropertyGroup::MAX_ALPHA, +enum:OUTER_END_ALPHA prop:outerEndAlpha type:float default:ENTITY_ITEM_DEFAULT_ALPHA min:RingGizmoPropertyGroup::MIN_ALPHA max:RingGizmoPropertyGroup::MAX_ALPHA, +enum:HAS_TICK_MARKS prop:hasTickMarks type:bool default:false, +enum:MAJOR_TICK_MARKS_ANGLE prop:majorTickMarksAngle type:float default:0.0f min:RingGizmoPropertyGroup::MIN_ANGLE max:RingGizmoPropertyGroup::MAX_ANGLE, +enum:MINOR_TICK_MARKS_ANGLE prop:minorTickMarksAngle type:float default:0.0f min:RingGizmoPropertyGroup::MIN_ANGLE max:RingGizmoPropertyGroup::MAX_ANGLE, +enum:MAJOR_TICK_MARKS_LENGTH prop:majorTickMarksLength type:float default:0.0f, +enum:MINOR_TICK_MARKS_LENGTH prop:minorTickMarksLength type:float default:0.0f, +enum:MAJOR_TICK_MARKS_COLOR prop:majorTickMarksColor type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR, +enum:MINOR_TICK_MARKS_COLOR prop:minorTickMarksColor type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR, \ No newline at end of file diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp deleted file mode 100644 index 2f0b1e0945..0000000000 --- a/libraries/entities/src/EntityItemProperties.cpp +++ /dev/null @@ -1,5456 +0,0 @@ -// -// EntityItemProperties.cpp -// libraries/entities/src -// -// Created by Brad Hefta-Gaub on 12/4/13. -// Copyright 2013 High Fidelity, Inc. -// Copyright 2020 Vircadia contributors. -// Copyright 2022-2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#include "EntityItemProperties.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "EntitiesLogging.h" -#include "EntityItem.h" -#include "ModelEntityItem.h" -#include "PolyLineEntityItem.h" -#include "WarningsSuppression.h" - -AnimationPropertyGroup EntityItemProperties::_staticAnimation; -SkyboxPropertyGroup EntityItemProperties::_staticSkybox; -HazePropertyGroup EntityItemProperties::_staticHaze; -BloomPropertyGroup EntityItemProperties::_staticBloom; -ZoneAudioPropertyGroup EntityItemProperties::_staticAudio; -KeyLightPropertyGroup EntityItemProperties::_staticKeyLight; -AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight; -TonemappingPropertyGroup EntityItemProperties::_staticTonemapping; -AmbientOcclusionPropertyGroup EntityItemProperties::_staticAmbientOcclusion; -GrabPropertyGroup EntityItemProperties::_staticGrab; -PulsePropertyGroup EntityItemProperties::_staticPulse; -RingGizmoPropertyGroup EntityItemProperties::_staticRing; - -EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1); - -EntityItemProperties::EntityItemProperties(EntityPropertyFlags desiredProperties) : - _id(UNKNOWN_ENTITY_ID), - _idSet(false), - _lastEdited(0), - _type(EntityTypes::Unknown), - - _defaultSettings(true), - _naturalDimensions(1.0f, 1.0f, 1.0f), - _naturalPosition(0.0f, 0.0f, 0.0f), - _desiredProperties(desiredProperties) -{ - -} - -void EntityItemProperties::calculateNaturalPosition(const vec3& min, const vec3& max) { - vec3 halfDimension = (max - min) / 2.0f; - _naturalPosition = max - halfDimension; -} - -void EntityItemProperties::debugDump() const { - qCDebug(entities) << "EntityItemProperties..."; - qCDebug(entities) << " _type=" << EntityTypes::getEntityTypeName(_type); - qCDebug(entities) << " _id=" << _id; - qCDebug(entities) << " _idSet=" << _idSet; - qCDebug(entities) << " _position=" << _position.x << "," << _position.y << "," << _position.z; - qCDebug(entities) << " _dimensions=" << getDimensions(); - qCDebug(entities) << " _modelURL=" << _modelURL; - qCDebug(entities) << " _compoundShapeURL=" << _compoundShapeURL; - - getAnimation().debugDump(); - getSkybox().debugDump(); - getHaze().debugDump(); - getKeyLight().debugDump(); - getAmbientLight().debugDump(); - getBloom().debugDump(); - getAudio().debugDump(); - getTonemapping().debugDump(); - getAmbientOcclusion().debugDump(); - getGrab().debugDump(); - - qCDebug(entities) << " changed properties..."; - EntityPropertyFlags props = getChangedProperties(); - props.debugDumpBits(); -} - -void EntityItemProperties::setLastEdited(quint64 usecTime) { - _lastEdited = usecTime > _created ? usecTime : _created; -} - -bool EntityItemProperties::constructFromBuffer(const unsigned char* data, int dataLength) { - ReadBitstreamToTreeParams args; - EntityItemPointer tempEntity = EntityTypes::constructEntityItem(data, dataLength); - if (!tempEntity) { - return false; - } - tempEntity->readEntityDataFromBuffer(data, dataLength, args); - (*this) = tempEntity->getProperties(); - return true; -} - -inline void addShapeType(QHash& lookup, ShapeType type) { lookup[ShapeInfo::getNameForShapeType(type)] = type; } -QHash stringToShapeTypeLookup = [] { - QHash toReturn; - addShapeType(toReturn, SHAPE_TYPE_NONE); - addShapeType(toReturn, SHAPE_TYPE_BOX); - addShapeType(toReturn, SHAPE_TYPE_SPHERE); - addShapeType(toReturn, SHAPE_TYPE_CAPSULE_X); - addShapeType(toReturn, SHAPE_TYPE_CAPSULE_Y); - addShapeType(toReturn, SHAPE_TYPE_CAPSULE_Z); - addShapeType(toReturn, SHAPE_TYPE_CYLINDER_X); - addShapeType(toReturn, SHAPE_TYPE_CYLINDER_Y); - addShapeType(toReturn, SHAPE_TYPE_CYLINDER_Z); - addShapeType(toReturn, SHAPE_TYPE_HULL); - addShapeType(toReturn, SHAPE_TYPE_PLANE); - addShapeType(toReturn, SHAPE_TYPE_COMPOUND); - addShapeType(toReturn, SHAPE_TYPE_SIMPLE_HULL); - addShapeType(toReturn, SHAPE_TYPE_SIMPLE_COMPOUND); - addShapeType(toReturn, SHAPE_TYPE_STATIC_MESH); - addShapeType(toReturn, SHAPE_TYPE_ELLIPSOID); - addShapeType(toReturn, SHAPE_TYPE_CIRCLE); - return toReturn; -}(); -QString EntityItemProperties::getShapeTypeAsString() const { return ShapeInfo::getNameForShapeType(_shapeType); } -void EntityItemProperties::setShapeTypeFromString(const QString& shapeName) { - auto shapeTypeItr = stringToShapeTypeLookup.find(shapeName.toLower()); - if (shapeTypeItr != stringToShapeTypeLookup.end()) { - _shapeType = shapeTypeItr.value(); - _shapeTypeChanged = true; - } -} - -inline void addMaterialMappingMode(QHash& lookup, MaterialMappingMode mode) { lookup[MaterialMappingModeHelpers::getNameForMaterialMappingMode(mode)] = mode; } -const QHash stringToMaterialMappingModeLookup = [] { - QHash toReturn; - addMaterialMappingMode(toReturn, UV); - addMaterialMappingMode(toReturn, PROJECTED); - return toReturn; -}(); -QString EntityItemProperties::getMaterialMappingModeAsString() const { return MaterialMappingModeHelpers::getNameForMaterialMappingMode(_materialMappingMode); } -void EntityItemProperties::setMaterialMappingModeFromString(const QString& materialMappingMode) { - auto materialMappingModeItr = stringToMaterialMappingModeLookup.find(materialMappingMode.toLower()); - if (materialMappingModeItr != stringToMaterialMappingModeLookup.end()) { - _materialMappingMode = materialMappingModeItr.value(); - _materialMappingModeChanged = true; - } -} - -inline void addBillboardMode(QHash& lookup, BillboardMode mode) { lookup[BillboardModeHelpers::getNameForBillboardMode(mode)] = mode; } -const QHash stringToBillboardModeLookup = [] { - QHash toReturn; - addBillboardMode(toReturn, BillboardMode::NONE); - addBillboardMode(toReturn, BillboardMode::YAW); - addBillboardMode(toReturn, BillboardMode::FULL); - return toReturn; -}(); -QString EntityItemProperties::getBillboardModeAsString() const { return BillboardModeHelpers::getNameForBillboardMode(_billboardMode); } -void EntityItemProperties::setBillboardModeFromString(const QString& billboardMode) { - auto billboardModeItr = stringToBillboardModeLookup.find(billboardMode.toLower()); - if (billboardModeItr != stringToBillboardModeLookup.end()) { - _billboardMode = billboardModeItr.value(); - _billboardModeChanged = true; - } -} - -inline void addRenderLayer(QHash& lookup, RenderLayer mode) { lookup[RenderLayerHelpers::getNameForRenderLayer(mode)] = mode; } -const QHash stringToRenderLayerLookup = [] { - QHash toReturn; - addRenderLayer(toReturn, RenderLayer::WORLD); - addRenderLayer(toReturn, RenderLayer::FRONT); - addRenderLayer(toReturn, RenderLayer::HUD); - return toReturn; -}(); -QString EntityItemProperties::getRenderLayerAsString() const { return RenderLayerHelpers::getNameForRenderLayer(_renderLayer); } -void EntityItemProperties::setRenderLayerFromString(const QString& renderLayer) { - auto renderLayerItr = stringToRenderLayerLookup.find(renderLayer.toLower()); - if (renderLayerItr != stringToRenderLayerLookup.end()) { - _renderLayer = renderLayerItr.value(); - _renderLayerChanged = true; - } -} - -inline void addPrimitiveMode(QHash& lookup, PrimitiveMode mode) { lookup[PrimitiveModeHelpers::getNameForPrimitiveMode(mode)] = mode; } -const QHash stringToPrimitiveModeLookup = [] { - QHash toReturn; - addPrimitiveMode(toReturn, PrimitiveMode::SOLID); - addPrimitiveMode(toReturn, PrimitiveMode::LINES); - return toReturn; -}(); -QString EntityItemProperties::getPrimitiveModeAsString() const { return PrimitiveModeHelpers::getNameForPrimitiveMode(_primitiveMode); } -void EntityItemProperties::setPrimitiveModeFromString(const QString& primitiveMode) { - auto primitiveModeItr = stringToPrimitiveModeLookup.find(primitiveMode.toLower()); - if (primitiveModeItr != stringToPrimitiveModeLookup.end()) { - _primitiveMode = primitiveModeItr.value(); - _primitiveModeChanged = true; - } -} - -inline void addWebInputMode(QHash& lookup, WebInputMode mode) { lookup[WebInputModeHelpers::getNameForWebInputMode(mode)] = mode; } -const QHash stringToWebInputModeLookup = [] { - QHash toReturn; - addWebInputMode(toReturn, WebInputMode::TOUCH); - addWebInputMode(toReturn, WebInputMode::MOUSE); - return toReturn; -}(); -QString EntityItemProperties::getInputModeAsString() const { return WebInputModeHelpers::getNameForWebInputMode(_inputMode); } -void EntityItemProperties::setInputModeFromString(const QString& webInputMode) { - auto webInputModeItr = stringToWebInputModeLookup.find(webInputMode.toLower()); - if (webInputModeItr != stringToWebInputModeLookup.end()) { - _inputMode = webInputModeItr.value(); - _inputModeChanged = true; - } -} - -inline void addGizmoType(QHash& lookup, GizmoType mode) { lookup[GizmoTypeHelpers::getNameForGizmoType(mode)] = mode; } -const QHash stringToGizmoTypeLookup = [] { - QHash toReturn; - addGizmoType(toReturn, GizmoType::RING); - return toReturn; -}(); -QString EntityItemProperties::getGizmoTypeAsString() const { return GizmoTypeHelpers::getNameForGizmoType(_gizmoType); } -void EntityItemProperties::setGizmoTypeFromString(const QString& gizmoType) { - auto gizmoTypeItr = stringToGizmoTypeLookup.find(gizmoType.toLower()); - if (gizmoTypeItr != stringToGizmoTypeLookup.end()) { - _gizmoType = gizmoTypeItr.value(); - _gizmoTypeChanged = true; - } -} - -inline void addComponentMode(QHash& lookup, ComponentMode mode) { lookup[ComponentModeHelpers::getNameForComponentMode(mode)] = mode; } -const QHash stringToComponentMode = [] { - QHash toReturn; - addComponentMode(toReturn, ComponentMode::COMPONENT_MODE_INHERIT); - addComponentMode(toReturn, ComponentMode::COMPONENT_MODE_DISABLED); - addComponentMode(toReturn, ComponentMode::COMPONENT_MODE_ENABLED); - return toReturn; -}(); -QString EntityItemProperties::getComponentModeAsString(uint32_t mode) { return ComponentModeHelpers::getNameForComponentMode((ComponentMode)mode); } -QString EntityItemProperties::getSkyboxModeAsString() const { return getComponentModeAsString(_skyboxMode); } -QString EntityItemProperties::getKeyLightModeAsString() const { return getComponentModeAsString(_keyLightMode); } -QString EntityItemProperties::getAmbientLightModeAsString() const { return getComponentModeAsString(_ambientLightMode); } -QString EntityItemProperties::getHazeModeAsString() const { return getComponentModeAsString(_hazeMode); } -QString EntityItemProperties::getBloomModeAsString() const { return getComponentModeAsString(_bloomMode); } -QString EntityItemProperties::getTonemappingModeAsString() const { return getComponentModeAsString(_tonemappingMode); } -QString EntityItemProperties::getAmbientOcclusionModeAsString() const { return getComponentModeAsString(_ambientOcclusionMode); } -void EntityItemProperties::setSkyboxModeFromString(const QString& mode) { - auto modeItr = stringToComponentMode.find(mode.toLower()); - if (modeItr != stringToComponentMode.end()) { - _skyboxMode = modeItr.value(); - _skyboxModeChanged = true; - } -} -void EntityItemProperties::setKeyLightModeFromString(const QString& mode) { - auto modeItr = stringToComponentMode.find(mode.toLower()); - if (modeItr != stringToComponentMode.end()) { - _keyLightMode = modeItr.value(); - _keyLightModeChanged = true; - } -} -void EntityItemProperties::setAmbientLightModeFromString(const QString& mode) { - auto modeItr = stringToComponentMode.find(mode.toLower()); - if (modeItr != stringToComponentMode.end()) { - _ambientLightMode = modeItr.value(); - _ambientLightModeChanged = true; - } -} -void EntityItemProperties::setHazeModeFromString(const QString& mode) { - auto modeItr = stringToComponentMode.find(mode.toLower()); - if (modeItr != stringToComponentMode.end()) { - _hazeMode = modeItr.value(); - _hazeModeChanged = true; - } -} -void EntityItemProperties::setBloomModeFromString(const QString& mode) { - auto modeItr = stringToComponentMode.find(mode.toLower()); - if (modeItr != stringToComponentMode.end()) { - _bloomMode = modeItr.value(); - _bloomModeChanged = true; - } -} -void EntityItemProperties::setTonemappingModeFromString(const QString& mode) { - auto modeItr = stringToComponentMode.find(mode.toLower()); - if (modeItr != stringToComponentMode.end()) { - _tonemappingMode = modeItr.value(); - _tonemappingModeChanged = true; - } -} -void EntityItemProperties::setAmbientOcclusionModeFromString(const QString& mode) { - auto modeItr = stringToComponentMode.find(mode.toLower()); - if (modeItr != stringToComponentMode.end()) { - _ambientOcclusionMode = modeItr.value(); - _ambientOcclusionModeChanged = true; - } -} - -inline void addAvatarPriorityMode(QHash& lookup, AvatarPriorityMode mode) { lookup[AvatarPriorityModeHelpers::getNameForAvatarPriorityMode(mode)] = mode; } -const QHash stringToAvatarPriority = [] { - QHash toReturn; - addAvatarPriorityMode(toReturn, AvatarPriorityMode::AVATAR_PRIORITY_INHERIT); - addAvatarPriorityMode(toReturn, AvatarPriorityMode::AVATAR_PRIORITY_CROWD); - addAvatarPriorityMode(toReturn, AvatarPriorityMode::AVATAR_PRIORITY_HERO); - return toReturn; -}(); -QString EntityItemProperties::getAvatarPriorityAsString() const { return AvatarPriorityModeHelpers::getNameForAvatarPriorityMode((AvatarPriorityMode)_avatarPriority); } -void EntityItemProperties::setAvatarPriorityFromString(const QString& mode) { - auto modeItr = stringToAvatarPriority.find(mode.toLower()); - if (modeItr != stringToAvatarPriority.end()) { - _avatarPriority = modeItr.value(); - _avatarPriorityChanged = true; - } -} - -QString EntityItemProperties::getScreenshareAsString() const { return getComponentModeAsString(_screenshare); } -void EntityItemProperties::setScreenshareFromString(const QString& mode) { - auto modeItr = stringToComponentMode.find(mode.toLower()); - if (modeItr != stringToComponentMode.end()) { - _screenshare = modeItr.value(); - _screenshareChanged = true; - } -} - -inline void addTextEffect(QHash& lookup, TextEffect effect) { lookup[TextEffectHelpers::getNameForTextEffect(effect)] = effect; } -const QHash stringToTextEffectLookup = [] { - QHash toReturn; - addTextEffect(toReturn, TextEffect::NO_EFFECT); - addTextEffect(toReturn, TextEffect::OUTLINE_EFFECT); - addTextEffect(toReturn, TextEffect::OUTLINE_WITH_FILL_EFFECT); - addTextEffect(toReturn, TextEffect::SHADOW_EFFECT); - return toReturn; -}(); -QString EntityItemProperties::getTextEffectAsString() const { return TextEffectHelpers::getNameForTextEffect(_textEffect); } -void EntityItemProperties::setTextEffectFromString(const QString& effect) { - auto textEffectItr = stringToTextEffectLookup.find(effect.toLower()); - if (textEffectItr != stringToTextEffectLookup.end()) { - _textEffect = textEffectItr.value(); - _textEffectChanged = true; - } -} - -inline void addTextAlignment(QHash& lookup, TextAlignment alignment) { lookup[TextAlignmentHelpers::getNameForTextAlignment(alignment)] = alignment; } -const QHash stringToTextAlignmentLookup = [] { - QHash toReturn; - addTextAlignment(toReturn, TextAlignment::LEFT); - addTextAlignment(toReturn, TextAlignment::CENTER); - addTextAlignment(toReturn, TextAlignment::RIGHT); - return toReturn; -}(); -QString EntityItemProperties::getAlignmentAsString() const { return TextAlignmentHelpers::getNameForTextAlignment(_alignment); } -void EntityItemProperties::setAlignmentFromString(const QString& alignment) { - auto textAlignmentItr = stringToTextAlignmentLookup.find(alignment.toLower()); - if (textAlignmentItr != stringToTextAlignmentLookup.end()) { - _alignment = textAlignmentItr.value(); - _alignmentChanged = true; - } -} - -QString getCollisionGroupAsString(uint16_t group) { - switch (group) { - case USER_COLLISION_GROUP_DYNAMIC: - return "dynamic"; - case USER_COLLISION_GROUP_STATIC: - return "static"; - case USER_COLLISION_GROUP_KINEMATIC: - return "kinematic"; - case USER_COLLISION_GROUP_MY_AVATAR: - return "myAvatar"; - case USER_COLLISION_GROUP_OTHER_AVATAR: - return "otherAvatar"; - }; - return ""; -} - -uint16_t getCollisionGroupAsBitMask(const QStringRef& name) { - if (0 == name.compare(QString("dynamic"))) { - return USER_COLLISION_GROUP_DYNAMIC; - } else if (0 == name.compare(QString("static"))) { - return USER_COLLISION_GROUP_STATIC; - } else if (0 == name.compare(QString("kinematic"))) { - return USER_COLLISION_GROUP_KINEMATIC; - } else if (0 == name.compare(QString("myAvatar"))) { - return USER_COLLISION_GROUP_MY_AVATAR; - } else if (0 == name.compare(QString("otherAvatar"))) { - return USER_COLLISION_GROUP_OTHER_AVATAR; - } - return 0; -} - -QString EntityItemProperties::getCollisionMaskAsString() const { - QString maskString(""); - for (int i = 0; i < NUM_USER_COLLISION_GROUPS; ++i) { - uint16_t group = 0x0001 << i; - if (group & _collisionMask) { - maskString.append(getCollisionGroupAsString(group)); - maskString.append(','); - } - } - return maskString; -} - -void EntityItemProperties::setCollisionMaskFromString(const QString& maskString) { - QVector groups = maskString.splitRef(','); - uint16_t mask = 0x0000; - for (auto groupName : groups) { - mask |= getCollisionGroupAsBitMask(groupName); - } - _collisionMask = mask; - _collisionMaskChanged = true; -} - -QString EntityItemProperties::getEntityHostTypeAsString() const { - switch (_entityHostType) { - case entity::HostType::DOMAIN: - return "domain"; - case entity::HostType::AVATAR: - return "avatar"; - case entity::HostType::LOCAL: - return "local"; - default: - return ""; - } -} - -void EntityItemProperties::setEntityHostTypeFromString(const QString& entityHostType) { - if (entityHostType == "domain") { - _entityHostType = entity::HostType::DOMAIN; - } else if (entityHostType == "avatar") { - _entityHostType = entity::HostType::AVATAR; - } else if (entityHostType == "local") { - _entityHostType = entity::HostType::LOCAL; - } -} - -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; - } -} - -QVector EntityItemProperties::getTagsAsVector() const { - QVector tags; - for (const QString& tag : _tags) { - tags.push_back(tag); - } - return tags; -} - -void EntityItemProperties::setTagsFromVector(const QVector& tags) { - _tags.clear(); - for (const QString& tag : tags) { - _tags.insert(tag); - } -} - -EntityPropertyFlags EntityItemProperties::getChangedProperties() const { - EntityPropertyFlags changedProperties; - - // Core - CHECK_PROPERTY_CHANGE(PROP_SIMULATION_OWNER, simulationOwner); - CHECK_PROPERTY_CHANGE(PROP_PARENT_ID, parentID); - CHECK_PROPERTY_CHANGE(PROP_PARENT_JOINT_INDEX, parentJointIndex); - CHECK_PROPERTY_CHANGE(PROP_VISIBLE, visible); - CHECK_PROPERTY_CHANGE(PROP_NAME, name); - CHECK_PROPERTY_CHANGE(PROP_LOCKED, locked); - CHECK_PROPERTY_CHANGE(PROP_USER_DATA, userData); - CHECK_PROPERTY_CHANGE(PROP_PRIVATE_USER_DATA, privateUserData); - CHECK_PROPERTY_CHANGE(PROP_HREF, href); - CHECK_PROPERTY_CHANGE(PROP_DESCRIPTION, description); - CHECK_PROPERTY_CHANGE(PROP_POSITION, position); - CHECK_PROPERTY_CHANGE(PROP_DIMENSIONS, dimensions); - CHECK_PROPERTY_CHANGE(PROP_ROTATION, rotation); - CHECK_PROPERTY_CHANGE(PROP_REGISTRATION_POINT, registrationPoint); - CHECK_PROPERTY_CHANGE(PROP_CREATED, created); - CHECK_PROPERTY_CHANGE(PROP_LAST_EDITED_BY, lastEditedBy); - CHECK_PROPERTY_CHANGE(PROP_ENTITY_HOST_TYPE, entityHostType); - CHECK_PROPERTY_CHANGE(PROP_OWNING_AVATAR_ID, owningAvatarID); - CHECK_PROPERTY_CHANGE(PROP_QUERY_AA_CUBE, queryAACube); - CHECK_PROPERTY_CHANGE(PROP_CAN_CAST_SHADOW, canCastShadow); - CHECK_PROPERTY_CHANGE(PROP_VISIBLE_IN_SECONDARY_CAMERA, isVisibleInSecondaryCamera); - CHECK_PROPERTY_CHANGE(PROP_RENDER_LAYER, renderLayer); - CHECK_PROPERTY_CHANGE(PROP_PRIMITIVE_MODE, primitiveMode); - CHECK_PROPERTY_CHANGE(PROP_IGNORE_PICK_INTERSECTION, ignorePickIntersection); - CHECK_PROPERTY_CHANGE(PROP_RENDER_WITH_ZONES, renderWithZones); - CHECK_PROPERTY_CHANGE(PROP_BILLBOARD_MODE, billboardMode); - CHECK_PROPERTY_CHANGE(PROP_TAGS, tags); - 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); - CHECK_PROPERTY_CHANGE(PROP_VELOCITY, velocity); - CHECK_PROPERTY_CHANGE(PROP_ANGULAR_VELOCITY, angularVelocity); - CHECK_PROPERTY_CHANGE(PROP_GRAVITY, gravity); - CHECK_PROPERTY_CHANGE(PROP_ACCELERATION, acceleration); - CHECK_PROPERTY_CHANGE(PROP_DAMPING, damping); - CHECK_PROPERTY_CHANGE(PROP_ANGULAR_DAMPING, angularDamping); - CHECK_PROPERTY_CHANGE(PROP_RESTITUTION, restitution); - CHECK_PROPERTY_CHANGE(PROP_FRICTION, friction); - CHECK_PROPERTY_CHANGE(PROP_LIFETIME, lifetime); - CHECK_PROPERTY_CHANGE(PROP_COLLISIONLESS, collisionless); - CHECK_PROPERTY_CHANGE(PROP_COLLISION_MASK, collisionMask); - CHECK_PROPERTY_CHANGE(PROP_DYNAMIC, dynamic); - CHECK_PROPERTY_CHANGE(PROP_COLLISION_SOUND_URL, collisionSoundURL); - CHECK_PROPERTY_CHANGE(PROP_ACTION_DATA, actionData); - - // Cloning - CHECK_PROPERTY_CHANGE(PROP_CLONEABLE, cloneable); - CHECK_PROPERTY_CHANGE(PROP_CLONE_LIFETIME, cloneLifetime); - CHECK_PROPERTY_CHANGE(PROP_CLONE_LIMIT, cloneLimit); - CHECK_PROPERTY_CHANGE(PROP_CLONE_DYNAMIC, cloneDynamic); - CHECK_PROPERTY_CHANGE(PROP_CLONE_AVATAR_ENTITY, cloneAvatarEntity); - CHECK_PROPERTY_CHANGE(PROP_CLONE_ORIGIN_ID, cloneOriginID); - - // Scripts - CHECK_PROPERTY_CHANGE(PROP_SCRIPT, script); - CHECK_PROPERTY_CHANGE(PROP_SCRIPT_TIMESTAMP, scriptTimestamp); - CHECK_PROPERTY_CHANGE(PROP_SERVER_SCRIPTS, serverScripts); - - // Location data for scripts - CHECK_PROPERTY_CHANGE(PROP_LOCAL_POSITION, localPosition); - CHECK_PROPERTY_CHANGE(PROP_LOCAL_ROTATION, localRotation); - CHECK_PROPERTY_CHANGE(PROP_LOCAL_VELOCITY, localVelocity); - CHECK_PROPERTY_CHANGE(PROP_LOCAL_ANGULAR_VELOCITY, localAngularVelocity); - CHECK_PROPERTY_CHANGE(PROP_LOCAL_DIMENSIONS, localDimensions); - - // Common - CHECK_PROPERTY_CHANGE(PROP_SHAPE_TYPE, shapeType); - CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); - CHECK_PROPERTY_CHANGE(PROP_COLOR, color); - CHECK_PROPERTY_CHANGE(PROP_ALPHA, alpha); - CHECK_PROPERTY_CHANGE(PROP_UNLIT, unlit); - changedProperties += _pulse.getChangedProperties(); - CHECK_PROPERTY_CHANGE(PROP_TEXTURES, textures); - - // Particles - CHECK_PROPERTY_CHANGE(PROP_MAX_PARTICLES, maxParticles); - CHECK_PROPERTY_CHANGE(PROP_LIFESPAN, lifespan); - CHECK_PROPERTY_CHANGE(PROP_EMITTING_PARTICLES, isEmitting); - CHECK_PROPERTY_CHANGE(PROP_EMIT_RATE, emitRate); - CHECK_PROPERTY_CHANGE(PROP_EMIT_SPEED, emitSpeed); - CHECK_PROPERTY_CHANGE(PROP_SPEED_SPREAD, speedSpread); - CHECK_PROPERTY_CHANGE(PROP_EMIT_ORIENTATION, emitOrientation); - CHECK_PROPERTY_CHANGE(PROP_EMIT_DIMENSIONS, emitDimensions); - CHECK_PROPERTY_CHANGE(PROP_EMIT_RADIUS_START, emitRadiusStart); - CHECK_PROPERTY_CHANGE(PROP_POLAR_START, polarStart); - CHECK_PROPERTY_CHANGE(PROP_POLAR_FINISH, polarFinish); - CHECK_PROPERTY_CHANGE(PROP_AZIMUTH_START, azimuthStart); - CHECK_PROPERTY_CHANGE(PROP_AZIMUTH_FINISH, azimuthFinish); - CHECK_PROPERTY_CHANGE(PROP_EMIT_ACCELERATION, emitAcceleration); - CHECK_PROPERTY_CHANGE(PROP_ACCELERATION_SPREAD, accelerationSpread); - CHECK_PROPERTY_CHANGE(PROP_PARTICLE_RADIUS, particleRadius); - CHECK_PROPERTY_CHANGE(PROP_RADIUS_SPREAD, radiusSpread); - CHECK_PROPERTY_CHANGE(PROP_RADIUS_START, radiusStart); - CHECK_PROPERTY_CHANGE(PROP_RADIUS_FINISH, radiusFinish); - CHECK_PROPERTY_CHANGE(PROP_COLOR_SPREAD, colorSpread); - CHECK_PROPERTY_CHANGE(PROP_COLOR_START, colorStart); - CHECK_PROPERTY_CHANGE(PROP_COLOR_FINISH, colorFinish); - CHECK_PROPERTY_CHANGE(PROP_ALPHA_SPREAD, alphaSpread); - CHECK_PROPERTY_CHANGE(PROP_ALPHA_START, alphaStart); - CHECK_PROPERTY_CHANGE(PROP_ALPHA_FINISH, alphaFinish); - CHECK_PROPERTY_CHANGE(PROP_EMITTER_SHOULD_TRAIL, emitterShouldTrail); - CHECK_PROPERTY_CHANGE(PROP_PARTICLE_SPIN, particleSpin); - CHECK_PROPERTY_CHANGE(PROP_SPIN_SPREAD, spinSpread); - CHECK_PROPERTY_CHANGE(PROP_SPIN_START, spinStart); - CHECK_PROPERTY_CHANGE(PROP_SPIN_FINISH, spinFinish); - CHECK_PROPERTY_CHANGE(PROP_PARTICLE_ROTATE_WITH_ENTITY, rotateWithEntity); - - // Procedural Particles - CHECK_PROPERTY_CHANGE(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, numParticles); - CHECK_PROPERTY_CHANGE(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, numTrianglesPerParticle); - CHECK_PROPERTY_CHANGE(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, numUpdateProps); - CHECK_PROPERTY_CHANGE(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, particleTransparent); - CHECK_PROPERTY_CHANGE(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, particleUpdateData); - CHECK_PROPERTY_CHANGE(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, particleRenderData); - - // Model - CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); - CHECK_PROPERTY_CHANGE(PROP_MODEL_SCALE, modelScale); - CHECK_PROPERTY_CHANGE(PROP_JOINT_ROTATIONS_SET, jointRotationsSet); - CHECK_PROPERTY_CHANGE(PROP_JOINT_ROTATIONS, jointRotations); - CHECK_PROPERTY_CHANGE(PROP_JOINT_TRANSLATIONS_SET, jointTranslationsSet); - CHECK_PROPERTY_CHANGE(PROP_JOINT_TRANSLATIONS, jointTranslations); - CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); - CHECK_PROPERTY_CHANGE(PROP_GROUP_CULLED, groupCulled); - CHECK_PROPERTY_CHANGE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients); - CHECK_PROPERTY_CHANGE(PROP_USE_ORIGINAL_PIVOT, useOriginalPivot); - CHECK_PROPERTY_CHANGE(PROP_LOAD_PRIORITY, loadPriority); - changedProperties += _animation.getChangedProperties(); - - // Light - CHECK_PROPERTY_CHANGE(PROP_IS_SPOTLIGHT, isSpotlight); - CHECK_PROPERTY_CHANGE(PROP_INTENSITY, intensity); - CHECK_PROPERTY_CHANGE(PROP_EXPONENT, exponent); - CHECK_PROPERTY_CHANGE(PROP_CUTOFF, cutoff); - CHECK_PROPERTY_CHANGE(PROP_FALLOFF_RADIUS, falloffRadius); - - // Text - CHECK_PROPERTY_CHANGE(PROP_TEXT, text); - CHECK_PROPERTY_CHANGE(PROP_LINE_HEIGHT, lineHeight); - CHECK_PROPERTY_CHANGE(PROP_TEXT_COLOR, textColor); - CHECK_PROPERTY_CHANGE(PROP_TEXT_ALPHA, textAlpha); - CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_COLOR, backgroundColor); - CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_ALPHA, backgroundAlpha); - CHECK_PROPERTY_CHANGE(PROP_LEFT_MARGIN, leftMargin); - CHECK_PROPERTY_CHANGE(PROP_RIGHT_MARGIN, rightMargin); - CHECK_PROPERTY_CHANGE(PROP_TOP_MARGIN, topMargin); - CHECK_PROPERTY_CHANGE(PROP_BOTTOM_MARGIN, bottomMargin); - CHECK_PROPERTY_CHANGE(PROP_FONT, font); - CHECK_PROPERTY_CHANGE(PROP_TEXT_EFFECT, textEffect); - CHECK_PROPERTY_CHANGE(PROP_TEXT_EFFECT_COLOR, textEffectColor); - CHECK_PROPERTY_CHANGE(PROP_TEXT_EFFECT_THICKNESS, textEffectThickness); - CHECK_PROPERTY_CHANGE(PROP_TEXT_ALIGNMENT, alignment); - - // Zone - changedProperties += _keyLight.getChangedProperties(); - changedProperties += _ambientLight.getChangedProperties(); - changedProperties += _skybox.getChangedProperties(); - changedProperties += _haze.getChangedProperties(); - changedProperties += _bloom.getChangedProperties(); - changedProperties += _audio.getChangedProperties(); - changedProperties += _tonemapping.getChangedProperties(); - changedProperties += _ambientOcclusion.getChangedProperties(); - CHECK_PROPERTY_CHANGE(PROP_FLYING_ALLOWED, flyingAllowed); - CHECK_PROPERTY_CHANGE(PROP_GHOSTING_ALLOWED, ghostingAllowed); - CHECK_PROPERTY_CHANGE(PROP_FILTER_URL, filterURL); - CHECK_PROPERTY_CHANGE(PROP_KEY_LIGHT_MODE, keyLightMode); - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_MODE, ambientLightMode); - CHECK_PROPERTY_CHANGE(PROP_SKYBOX_MODE, skyboxMode); - CHECK_PROPERTY_CHANGE(PROP_HAZE_MODE, hazeMode); - CHECK_PROPERTY_CHANGE(PROP_BLOOM_MODE, bloomMode); - CHECK_PROPERTY_CHANGE(PROP_AVATAR_PRIORITY, avatarPriority); - CHECK_PROPERTY_CHANGE(PROP_SCREENSHARE, screenshare); - CHECK_PROPERTY_CHANGE(PROP_TONEMAPPING_MODE, tonemappingMode); - CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_MODE, ambientOcclusionMode); - - // Polyvox - CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); - CHECK_PROPERTY_CHANGE(PROP_VOXEL_DATA, voxelData); - CHECK_PROPERTY_CHANGE(PROP_VOXEL_SURFACE_STYLE, voxelSurfaceStyle); - CHECK_PROPERTY_CHANGE(PROP_X_TEXTURE_URL, xTextureURL); - CHECK_PROPERTY_CHANGE(PROP_Y_TEXTURE_URL, yTextureURL); - CHECK_PROPERTY_CHANGE(PROP_Z_TEXTURE_URL, zTextureURL); - CHECK_PROPERTY_CHANGE(PROP_X_N_NEIGHBOR_ID, xNNeighborID); - CHECK_PROPERTY_CHANGE(PROP_Y_N_NEIGHBOR_ID, yNNeighborID); - CHECK_PROPERTY_CHANGE(PROP_Z_N_NEIGHBOR_ID, zNNeighborID); - CHECK_PROPERTY_CHANGE(PROP_X_P_NEIGHBOR_ID, xPNeighborID); - CHECK_PROPERTY_CHANGE(PROP_Y_P_NEIGHBOR_ID, yPNeighborID); - CHECK_PROPERTY_CHANGE(PROP_Z_P_NEIGHBOR_ID, zPNeighborID); - - // Web - CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl); - CHECK_PROPERTY_CHANGE(PROP_DPI, dpi); - CHECK_PROPERTY_CHANGE(PROP_SCRIPT_URL, scriptURL); - CHECK_PROPERTY_CHANGE(PROP_MAX_FPS, maxFPS); - CHECK_PROPERTY_CHANGE(PROP_INPUT_MODE, inputMode); - CHECK_PROPERTY_CHANGE(PROP_WANTS_KEYBOARD_FOCUS, wantsKeyboardFocus); - CHECK_PROPERTY_CHANGE(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, showKeyboardFocusHighlight); - CHECK_PROPERTY_CHANGE(PROP_WEB_USE_BACKGROUND, useBackground); - CHECK_PROPERTY_CHANGE(PROP_USER_AGENT, userAgent); - - // Polyline - CHECK_PROPERTY_CHANGE(PROP_LINE_POINTS, linePoints); - CHECK_PROPERTY_CHANGE(PROP_STROKE_WIDTHS, strokeWidths); - CHECK_PROPERTY_CHANGE(PROP_STROKE_NORMALS, normals); - CHECK_PROPERTY_CHANGE(PROP_STROKE_COLORS, strokeColors); - CHECK_PROPERTY_CHANGE(PROP_IS_UV_MODE_STRETCH, isUVModeStretch); - CHECK_PROPERTY_CHANGE(PROP_LINE_GLOW, glow); - CHECK_PROPERTY_CHANGE(PROP_LINE_FACE_CAMERA, faceCamera); - - // Shape - CHECK_PROPERTY_CHANGE(PROP_SHAPE, shape); - - // Material - CHECK_PROPERTY_CHANGE(PROP_MATERIAL_URL, materialURL); - CHECK_PROPERTY_CHANGE(PROP_MATERIAL_MAPPING_MODE, materialMappingMode); - CHECK_PROPERTY_CHANGE(PROP_MATERIAL_PRIORITY, priority); - CHECK_PROPERTY_CHANGE(PROP_PARENT_MATERIAL_NAME, parentMaterialName); - CHECK_PROPERTY_CHANGE(PROP_MATERIAL_MAPPING_POS, materialMappingPos); - CHECK_PROPERTY_CHANGE(PROP_MATERIAL_MAPPING_SCALE, materialMappingScale); - CHECK_PROPERTY_CHANGE(PROP_MATERIAL_MAPPING_ROT, materialMappingRot); - CHECK_PROPERTY_CHANGE(PROP_MATERIAL_DATA, materialData); - CHECK_PROPERTY_CHANGE(PROP_MATERIAL_REPEAT, materialRepeat); - - // Image - CHECK_PROPERTY_CHANGE(PROP_IMAGE_URL, imageURL); - CHECK_PROPERTY_CHANGE(PROP_EMISSIVE, emissive); - CHECK_PROPERTY_CHANGE(PROP_KEEP_ASPECT_RATIO, keepAspectRatio); - CHECK_PROPERTY_CHANGE(PROP_SUB_IMAGE, subImage); - - // Grid - CHECK_PROPERTY_CHANGE(PROP_GRID_FOLLOW_CAMERA, followCamera); - CHECK_PROPERTY_CHANGE(PROP_MAJOR_GRID_EVERY, majorGridEvery); - CHECK_PROPERTY_CHANGE(PROP_MINOR_GRID_EVERY, minorGridEvery); - - // Gizmo - CHECK_PROPERTY_CHANGE(PROP_GIZMO_TYPE, gizmoType); - changedProperties += _ring.getChangedProperties(); - - // Sound - CHECK_PROPERTY_CHANGE(PROP_SOUND_URL, soundURL); - CHECK_PROPERTY_CHANGE(PROP_SOUND_VOLUME, volume); - CHECK_PROPERTY_CHANGE(PROP_SOUND_TIME_OFFSET, timeOffset); - CHECK_PROPERTY_CHANGE(PROP_SOUND_PITCH, pitch); - CHECK_PROPERTY_CHANGE(PROP_SOUND_PLAYING, playing); - CHECK_PROPERTY_CHANGE(PROP_SOUND_LOOP, loop); - CHECK_PROPERTY_CHANGE(PROP_SOUND_POSITIONAL, positional); - CHECK_PROPERTY_CHANGE(PROP_SOUND_LOCAL_ONLY, localOnly); - - return changedProperties; -} - -/*@jsdoc - * Different entity types have different properties: some common to all entities (listed in the table) and some specific to - * each {@link Entities.EntityType|EntityType} (linked to below). - * - * @typedef {object} Entities.EntityProperties - * @property {Uuid} id - The ID of the entity. Read-only. - * @property {string} name="" - A name for the entity. Need not be unique. - * @property {Entities.EntityType} type - The entity's type. You cannot change the type of an entity after it's created. - * However, its value may switch among "Box", "Shape", and "Sphere" depending on - * changes to the shape property set for entities of these types. Read-only. - * - * @property {Entities.EntityHostType} entityHostType="domain" - How the entity is hosted and sent to others for display. - * The value can only be set at entity creation by one of the {@link Entities.addEntity} methods. Read-only. - * @property {boolean} avatarEntity=false - true if the entity is an {@link Entities.EntityHostType|avatar entity}, - * false if it isn't. The value is per the entityHostType property value, set at entity creation - * by one of the {@link Entities.addEntity} methods. Read-only. - * @property {boolean} clientOnly=false - A synonym for avatarEntity. Read-only. - * @property {boolean} localEntity=false - true if the entity is a {@link Entities.EntityHostType|local entity}, - * false if it isn't. The value is per the entityHostType property value, set at entity creation - * by one of the {@link Entities.addEntity} methods. Read-only. - * - * @property {Uuid} owningAvatarID=Uuid.NULL - The session ID of the owning avatar if avatarEntity is - * true, otherwise {@link Uuid(0)|Uuid.NULL}. Read-only. - * - * @property {number} created - When the entity was created, expressed as the number of microseconds since - * 1970-01-01T00:00:00 UTC. Read-only. - * @property {number} age - The age of the entity in seconds since it was created. Read-only. - * @property {string} ageAsText - The age of the entity since it was created, formatted as h hours m minutes s - * seconds. - * @property {number} lifetime=-1 - How long an entity lives for, in seconds, before being automatically deleted. A value of - * -1 means that the entity lives for ever. - * @property {number} lastEdited - When the entity was last edited, expressed as the number of microseconds since - * 1970-01-01T00:00:00 UTC. Read-only. - * @property {Uuid} lastEditedBy - The session ID of the avatar or agent that most recently created or edited the entity. - * Read-only. - * - * @property {boolean} locked=false - true if properties other than locked cannot be changed and the - * entity cannot be deleted, false if all properties can be changed and the entity can be deleted. - * @property {boolean} visible=true - true if the entity is rendered, false if it isn't. - * @property {boolean} canCastShadow=true - true if the entity can cast a shadow, false if it can't. - * Currently applicable only to {@link Entities.EntityProperties-Model|Model} and - * {@link Entities.EntityProperties-Shape|Shape} entities. Shadows are cast if inside a - * {@link Entities.EntityProperties-Zone|Zone} entity with castShadows enabled in its keyLight - * property. - * @property {boolean} isVisibleInSecondaryCamera=true - true if the entity is rendered in the secondary camera, - * false if it isn't. - * @property {Entities.RenderLayer} renderLayer="world" - The layer that the entity renders in. - * @property {Entities.PrimitiveMode} primitiveMode="solid" - How the entity's geometry is rendered. - * @property {boolean} ignorePickIntersection=false - true if {@link Picks} and {@link RayPick} ignore the entity, - * false if they don't. - * - * @property {Vec3} position=0,0,0 - The position of the entity in world coordinates. - * @property {Quat} rotation=0,0,0,1 - The orientation of the entity in world coordinates. - * @property {Vec3} registrationPoint=0.5,0.5,0.5 - The point in the entity that is set to the entity's position and is rotated - * about, range {@link Vec3(0)|Vec3.ZERO} – {@link Vec3(0)|Vec3.ONE}. A value of {@link Vec3(0)|Vec3.ZERO} is the - * entity's minimum x, y, z corner; a value of {@link Vec3(0)|Vec3.ONE} is the entity's maximum x, y, z corner. - * - * @property {Vec3} naturalPosition=0,0,0 - The center of the entity's unscaled mesh model if it has one, otherwise - * {@link Vec3(0)|Vec3.ZERO}. Read-only. - * @property {Vec3} naturalDimensions - The dimensions of the entity's unscaled mesh model or image if it has one, otherwise - * {@link Vec3(0)|Vec3.ONE}. Read-only. - * - * @property {Vec3} velocity=0,0,0 - The linear velocity of the entity in m/s with respect to world coordinates. - * @property {number} damping=0.39347 - How much the linear velocity of an entity slows down over time, range - * 0.01.0. A higher damping value slows down the entity more quickly. The default value - * is for an exponential decay timescale of 2.0s, where it takes 2.0s for the movement to slow to 1/e = 0.368 - * of its initial value. - * @property {Vec3} angularVelocity=0,0,0 - The angular velocity of the entity in rad/s with respect to its axes, about its - * registration point. - * @property {number} angularDamping=0.39347 - How much the angular velocity of an entity slows down over time, range - * 0.01.0. A higher damping value slows down the entity more quickly. The default value - * is for an exponential decay timescale of 2.0s, where it takes 2.0s for the movement to slow to 1/e = 0.368 - * of its initial value. - * - * @property {Vec3} gravity=0,0,0 - The acceleration due to gravity in m/s2 that the entity should move with, in - * world coordinates. Use a value of { x: 0, y: -9.8, z: 0 } to simulate Earth's gravity. Gravity is applied - * to an entity's motion only if its dynamic property is true. - *

    If changing an entity's gravity from {@link Vec3(0)|Vec3.ZERO}, you need to give it a small - * velocity in order to kick off physics simulation.

    - * @property {Vec3} acceleration - The current, measured acceleration of the entity, in m/s2. - *

    Deprecated: This property is deprecated and will be removed.

    - * @property {number} restitution=0.5 - The "bounciness" of an entity when it collides, range 0.0 – - * 0.99. The higher the value, the more bouncy. - * @property {number} friction=0.5 - How much an entity slows down when it's moving against another, range 0.0 - * – 10.0. The higher the value, the more quickly it slows down. Examples: 0.1 for ice, - * 0.9 for sandpaper. - * @property {number} density=1000 - The density of the entity in kg/m3, range 100 – - * 10000. Examples: 100 for balsa wood, 10000 for silver. The density is used in - * conjunction with the entity's bounding box volume to work out its mass in the application of physics. - * - * @property {boolean} collisionless=false - true if the entity shouldn't collide, false if it - * collides with items per its collisionMask property. - * @property {boolean} ignoreForCollisions - Synonym for collisionless. - * @property {CollisionMask} collisionMask=31 - What types of items the entity should collide with. - * @property {string} collidesWith="static,dynamic,kinematic,myAvatar,otherAvatar," - Synonym for collisionMask, - * in text format. - * @property {string} collisionSoundURL="" - The sound that's played when the entity experiences a collision. Valid file - * formats are per {@link SoundObject}. - * @property {boolean} dynamic=false - true if the entity's movement is affected by collisions, false - * if it isn't. - * @property {boolean} collisionsWillMove - A synonym for dynamic. - * - * @property {string} href="" - A "hifi://" directory services address that a user is teleported to when they click on the entity. - * @property {string} description="" - A description of the href property value. - * - * @property {string} userData="" - Used to store extra data about the entity in JSON format. - *

    Warning: Other apps may also use this property, so make sure you handle data stored by other apps: - * edit only your bit and leave the rest of the data intact. You can use JSON.parse() to parse the string into - * a JavaScript object which you can manipulate the properties of, and use JSON.stringify() to convert the - * object into a string to put back in the property.

    - * - * @property {string} privateUserData="" - Like userData, but only accessible by server entity scripts, assignment - * client scripts, and users who have "Can Get and Set Private User Data" permissions in the domain. - * - * @property {string} script="" - The URL of the client entity script, if any, that is attached to the entity. - * @property {number} scriptTimestamp=0 - Used to indicate when the client entity script was loaded. Should be - * an integer number of milliseconds since midnight GMT on January 1, 1970 (e.g., as supplied by Date.now(). - * If you update the property's value, the script is re-downloaded and reloaded. This is how the "reload" - * button beside the "script URL" field in properties tab of the Create app works. - * @property {string} serverScripts="" - The URL of the server entity script, if any, that is attached to the entity. - * - * @property {Uuid} parentID=Uuid.NULL - The ID of the entity or avatar that the entity is parented to. A value of - * {@link Uuid(0)|Uuid.NULL} is used if the entity is not parented. - * @property {number} parentJointIndex=65535 - The joint of the entity or avatar that the entity is parented to. Use - * 65535 or -1 to parent to the entity or avatar's position and orientation rather than a joint. - * @property {Vec3} localPosition=0,0,0 - The position of the entity relative to its parent if the entity is parented, - * otherwise the same value as position. If the entity is parented to an avatar and is an avatar entity - * so that it scales with the avatar, this value remains the original local position value while the avatar scale changes. - * @property {Quat} localRotation=0,0,0,1 - The rotation of the entity relative to its parent if the entity is parented, - * otherwise the same value as rotation. - * @property {Vec3} localVelocity=0,0,0 - The velocity of the entity relative to its parent if the entity is parented, - * otherwise the same value as velocity. - * @property {Vec3} localAngularVelocity=0,0,0 - The angular velocity of the entity relative to its parent if the entity is - * parented, otherwise the same value as angularVelocity. - * @property {Vec3} localDimensions - The dimensions of the entity. If the entity is parented to an avatar and is an - * avatar entity so that it scales with the avatar, this value remains the original dimensions value while the - * avatar scale changes. - * - * @property {Entities.BoundingBox} boundingBox - The axis-aligned bounding box that tightly encloses the entity. - * Read-only. - * @property {AACube} queryAACube - The axis-aligned cube that determines where the entity lives in the entity server's octree. - * The cube may be considerably larger than the entity in some situations, e.g., when the entity is grabbed by an avatar: - * the position of the entity is determined through avatar mixer updates and so the AA cube is expanded in order to reduce - * unnecessary entity server updates. Scripts should not change this property's value. - * - * @property {string} actionData="" - Base-64 encoded compressed dump of the actions associated with the entity. This property - * is typically not used in scripts directly; rather, functions that manipulate an entity's actions update it, e.g., - * {@link Entities.addAction}. The size of this property increases with the number of actions. Because this property value - * has to fit within a Overte datagram packet, there is a limit to the number of actions that an entity can have; - * edits which would result in overflow are rejected. Read-only. - * @property {Entities.RenderInfo} renderInfo - Information on the cost of rendering the entity. Currently information is only - * provided for Model entities. Read-only. - * - * @property {boolean} cloneable=false - true if the domain or avatar entity can be cloned via - * {@link Entities.cloneEntity}, false if it can't be. - * @property {number} cloneLifetime=300 - The entity lifetime for clones created from this entity. - * @property {number} cloneLimit=0 - The total number of clones of this entity that can exist in the domain at any given time. - * @property {boolean} cloneDynamic=false - true if clones created from this entity will have their - * dynamic property set to true, false if they won't. - * @property {boolean} cloneAvatarEntity=false - true if clones created from this entity will be created as - * avatar entities, false if they won't be. - * @property {Uuid} cloneOriginID - The ID of the entity that this entity was cloned from. - * - * @property {Uuid[]} renderWithZones=[] - A list of entity IDs representing with which zones this entity should render. - * If it is empty, this entity will render normally. Otherwise, this entity will only render if your avatar is within - * one of the zones in this list. - * @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera. Use the rotation - * property to control which axis is facing you. - * @property {string[]} tags=[] - A set of tags describing this entity. - * - * @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} - * @see {@link Entities.EntityProperties-Grid|EntityProperties-Grid} - * @see {@link Entities.EntityProperties-Image|EntityProperties-Image} - * @see {@link Entities.EntityProperties-Light|EntityProperties-Light} - * @see {@link Entities.EntityProperties-Line|EntityProperties-Line} - * @see {@link Entities.EntityProperties-Material|EntityProperties-Material} - * @see {@link Entities.EntityProperties-Model|EntityProperties-Model} - * @see {@link Entities.EntityProperties-ParticleEffect|EntityProperties-ParticleEffect} - * @see {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine} - * @see {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox} - * @see {@link Entities.EntityProperties-ProceduralParticleEffect|EntityProperties-ProceduralParticleEffect} - * @see {@link Entities.EntityProperties-Shape|EntityProperties-Shape} - * @see {@link Entities.EntityProperties-Sound|EntityProperties-Sound} - * @see {@link Entities.EntityProperties-Sphere|EntityProperties-Sphere} - * @see {@link Entities.EntityProperties-Text|EntityProperties-Text} - * @see {@link Entities.EntityProperties-Web|EntityProperties-Web} - * @see {@link Entities.EntityProperties-Zone|EntityProperties-Zone} - */ - -/*@jsdoc - * The "Box" {@link Entities.EntityType|EntityType} is the same as the "Shape" - * {@link Entities.EntityType|EntityType} except that its shape value is always set to "Cube" - * when the entity is created. If its shape property value is subsequently changed then the entity's - * type will be reported as "Sphere" if the shape is set to "Sphere", - * otherwise it will be reported as "Shape". - * - * @typedef {object} Entities.EntityProperties-Box - * @see {@link Entities.EntityProperties-Shape|EntityProperties-Shape} - */ - -/*@jsdoc - * The "Light" {@link Entities.EntityType|EntityType} adds local lighting effects. It has properties in addition - * to the common {@link Entities.EntityProperties|EntityProperties}. - * - * @typedef {object} Entities.EntityProperties-Light - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. Surfaces outside these dimensions are not lit - * by the light. - * @property {Color} color=255,255,255 - The color of the light emitted. - * @property {number} intensity=1 - The brightness of the light. - * @property {number} falloffRadius=0.1 - The distance from the light's center at which intensity is reduced by 25%. - * @property {boolean} isSpotlight=false - true if the light is directional, emitting along the entity's - * local negative z-axis; false if the light is a point light which emanates in all directions. - * @property {number} exponent=0 - Affects the softness of the spotlight beam: the higher the value the softer the beam. - * @property {number} cutoff=1.57 - Affects the size of the spotlight beam: the higher the value the larger the beam. - * @example Create a spotlight pointing at the ground. - * Entities.addEntity({ - * type: "Light", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -4 })), - * rotation: Quat.fromPitchYawRollDegrees(-75, 0, 0), - * dimensions: { x: 5, y: 5, z: 5 }, - * intensity: 100, - * falloffRadius: 0.3, - * isSpotlight: true, - * exponent: 20, - * cutoff: 30, - * lifetime: 300 // Delete after 5 minutes. - * }); - */ - -/*@jsdoc - * The "Line" {@link Entities.EntityType|EntityType} draws thin, straight lines between a sequence of two or more - * points. It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. - *

    Deprecated: Use {@link Entities.EntityProperties-PolyLine|PolyLine} entities instead.

    - * - * @typedef {object} Entities.EntityProperties-Line - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. Must be sufficient to contain all the - * linePoints. - * @property {Vec3[]} linePoints=[]] - The sequence of points to draw lines between. The values are relative to the entity's - * position. A maximum of 70 points can be specified. The property's value is set only if all the linePoints - * lie within the entity's dimensions. - * @property {Color} color=255,255,255 - The color of the line. - * @example Draw lines in a "V". - * var entity = Entities.addEntity({ - * type: "Line", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), - * rotation: MyAvatar.orientation, - * dimensions: { x: 2, y: 2, z: 1 }, - * linePoints: [ - * { x: -1, y: 1, z: 0 }, - * { x: 0, y: -1, z: 0 }, - * { x: 1, y: 1, z: 0 }, - * ], - * color: { red: 255, green: 0, blue: 0 }, - * lifetime: 300 // Delete after 5 minutes. - * }); - */ - -/*@jsdoc - * The "Material" {@link Entities.EntityType|EntityType} modifies existing materials on entities and avatars. It - * has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. - *

    To apply a material to an entity, set the material entity's parentID property to the entity ID. - * To apply a material to an avatar, set the material entity's parentID property to the avatar's session UUID. - * To apply a material to your avatar such that it persists across domains and log-ins, create the material as an avatar entity - * by setting the entityHostType parameter in {@link Entities.addEntity} to "avatar" and set the - * entity's parentID property to MyAvatar.SELF_ID. - * Material entities render as non-scalable spheres if they don't have their parent set.

    - * - * @typedef {object} Entities.EntityProperties-Material - * @property {Vec3} dimensions=0.1,0.1,0.1 - Used when materialMappingMode == "projected". - * @property {string} materialURL="" - URL to a {@link Entities.MaterialResource|MaterialResource}. Alternatively, set the - * property value to "materialData" to use the materialData property for the - * {@link Entities.MaterialResource|MaterialResource} values. If you append "#name" to the URL, the material - * with that name will be applied to the entity. You can also use the ID of another Material entity as the URL, in which - * case this material will act as a copy of that material, with its own unique material transform, priority, etc. - * @property {string} materialData="" - Used to store {@link Entities.MaterialResource|MaterialResource} data as a JSON string. - * You can use JSON.parse() to parse the string into a JavaScript object which you can manipulate the - * properties of, and use JSON.stringify() to convert the object into a string to put in the property. - * @property {number} priority=0 - The priority for applying the material to its parent. Only the highest priority material is - * applied, with materials of the same priority randomly assigned. Materials that come with the model have a priority of - * 0. - * @property {string} parentMaterialName="0" - Selects the mesh part or parts within the parent to which to apply the material. - * If in the format "mat::string", all mesh parts with material name "string" are replaced. - * If "all", then all mesh parts are replaced. - * Otherwise the property value is parsed as an unsigned integer, specifying the mesh part index to modify. - *

    If the string represents an array (starts with "[" and ends with "]"), the string is split - * at each "," and each element parsed as either a number or a string if it starts with "mat::". - * For example, "[0,1,mat::string,mat::string2]" will replace mesh parts 0 and 1, and any mesh parts with - * material "string" or "string2". Do not put spaces around the commas. Invalid values are parsed - * to 0.

    - * @property {string} materialMappingMode="uv" - How the material is mapped to the entity. Either "uv" or - * "projected". In "uv" mode, the material is evaluated within the UV space of the mesh it is - * applied to. In "projected" mode, the 3D transform (position, rotation, and dimensions) of the Material - * entity is used to evaluate the texture coordinates for the material. - * @property {Vec2} materialMappingPos=0,0 - Offset position in UV-space of the top left of the material, range - * { x: 0, y: 0 }{ x: 1, y: 1 }. - * @property {Vec2} materialMappingScale=1,1 - How much to scale the material within the parent's UV-space. - * @property {number} materialMappingRot=0 - How much to rotate the material within the parent's UV-space, in degrees. - * @property {boolean} materialRepeat=true - true if the material repeats, false if it doesn't. If - * false, fragments outside of texCoord 0 – 1 will be discarded. Works in both "uv" and - * "projected" modes. - * @example Color a sphere using a Material entity. - * var entityID = Entities.addEntity({ - * type: "Sphere", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), - * dimensions: { x: 1, y: 1, z: 1 }, - * color: { red: 128, green: 128, blue: 128 }, - * lifetime: 300 // Delete after 5 minutes. - * }); - * - * var materialID = Entities.addEntity({ - * type: "Material", - * parentID: entityID, - * materialURL: "materialData", - * priority: 1, - * materialData: JSON.stringify({ - * materialVersion: 1, - * materials: { - * // Value overrides entity's "color" property. - * albedo: [1.0, 1.0, 0] // Yellow - * } - * }) - * }); - */ - -/*@jsdoc - * The "Model" {@link Entities.EntityType|EntityType} displays a glTF, FBX, or OBJ model. When adding an entity, - * if no dimensions value is specified then the model is automatically sized to its - * {@link Entities.EntityProperties|naturalDimensions}. It has properties in addition to the common - * {@link Entities.EntityProperties|EntityProperties}. - * - * @typedef {object} Entities.EntityProperties-Model - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. When adding an entity, if no dimensions - * value is specified then the model is automatically sized to its - * {@link Entities.EntityProperties|naturalDimensions}. - * @property {string} modelURL="" - The URL of the glTF, FBX, or OBJ model. glTF models may be in JSON or binary format - * (".gltf" or ".glb" URLs respectively). Baked models' URLs have ".baked" before the file type. Model files may also be - * compressed in GZ format, in which case the URL ends in ".gz". - * @property {Vec3} modelScale - The scale factor applied to the model's dimensions. - *

    Deprecated: This property is deprecated and will be removed.

    - * @property {string} blendshapeCoefficients - A JSON string of a map of blendshape names to values. Only stores set values. - * When editing this property, only coefficients that you are editing will change; it will not explicitly reset other - * coefficients. - * @property {boolean} useOriginalPivot=false - If false, the model will be centered based on its content, - * ignoring any offset in the model itself. If true, the model will respect its original offset. Currently, - * only pivots relative to {x: 0, y: 0, z: 0} are supported. - * @property {number} loadPriority=0.0 - If 0, the model download will be prioritized based on distance, size, and - * other factors, and assigned a priority automatically between 0 and PI / 2. Otherwise, the - * download will be ordered based on the set loadPriority. - * @property {string} textures="" - A JSON string of texture name, URL pairs used when rendering the model in place of the - * model's original textures. Use a texture name from the originalTextures property to override that texture. - * Only the texture names and URLs to be overridden need be specified; original textures are used where there are no - * overrides. You can use JSON.stringify() to convert a JavaScript object of name, URL pairs into a JSON - * string. - * @property {string} originalTextures="{}" - A JSON string of texture name, URL pairs used in the model. The property value is - * filled in after the entity has finished rezzing (i.e., textures have loaded). You can use JSON.parse() to - * parse the JSON string into a JavaScript object of name, URL pairs. Read-only. - * @property {Color} color=255,255,255 - Currently not used. - * - * @property {ShapeType} shapeType="none" - The shape of the collision hull used if collisions are enabled. - * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType is - * "compound". - * - * @property {Entities.AnimationProperties} animation - An animation to play on the model. - * - * @property {Quat[]} jointRotations=[]] - Joint rotations applied to the model; [] if none are applied or the - * model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Rotations are relative to - * each joint's parent. - *

    Joint rotations can be set by {@link Entities.setLocalJointRotation|setLocalJointRotation} and similar functions, or - * by setting the value of this property. If you set a joint rotation using this property, you also need to set the - * corresponding jointRotationsSet value to true.

    - * @property {boolean[]} jointRotationsSet=[]] - true values for joints that have had rotations applied, - * false otherwise; [] if none are applied or the model hasn't loaded. The array indexes are per - * {@link Entities.getJointIndex|getJointIndex}. - * @property {Vec3[]} jointTranslations=[]] - Joint translations applied to the model; [] if none are applied or - * the model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Translations are - * relative to each joint's parent. - *

    Joint translations can be set by {@link Entities.setLocalJointTranslation|setLocalJointTranslation} and similar - * functions, or by setting the value of this property. If you set a joint translation using this property you also need to - * set the corresponding jointTranslationsSet value to true.

    - * @property {boolean[]} jointTranslationsSet=[]] - true values for joints that have had translations applied, - * false otherwise; [] if none are applied or the model hasn't loaded. The array indexes are per - * {@link Entities.getJointIndex|getJointIndex}. - * @property {boolean} relayParentJoints=false - true if when the entity is parented to an avatar, the avatar's - * joint rotations are applied to the entity's joints; false if a parent avatar's joint rotations are not - * applied to the entity's joints. - * @property {boolean} groupCulled=false - true if the mesh parts of the model are LOD culled as a group, - * false if separate mesh parts are LOD culled individually. - * - * @example Rez a cowboy hat. - * var entity = Entities.addEntity({ - * type: "Model", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -2 })), - * rotation: MyAvatar.orientation, - * modelURL: "https://apidocs.overte.org/examples/cowboy-hat.fbx", - * dimensions: { x: 0.8569, y: 0.3960, z: 1.0744 }, - * lifetime: 300 // Delete after 5 minutes. - * }); - */ - -/*@jsdoc - * The "ParticleEffect" {@link Entities.EntityType|EntityType} displays a particle system that can be used to - * simulate things such as fire, smoke, snow, magic spells, etc. The particles emanate from an ellipsoid or part thereof. - * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. - * - * @typedef {object} Entities.EntityProperties-ParticleEffect - * @property {boolean} isEmitting=true - true if particles are being emitted, false if they aren't. - * @property {number} maxParticles=1000 - The maximum number of particles to render at one time. Older particles are deleted if - * necessary when new ones are created. - * @property {number} lifespan=3s - How long, in seconds, each particle lives. - * @property {number} emitRate=15 - The number of particles per second to emit. - * @property {number} emitSpeed=5 - The speed, in m/s, that each particle is emitted at. - * @property {number} speedSpread=1 - The spread in speeds at which particles are emitted at. For example, if - * emitSpeed == 5 and speedSpread == 1, particles will be emitted with speeds in the range - * 46m/s. - * @property {Vec3} emitAcceleration=0,-9.8,0 - The acceleration that is applied to each particle during its lifetime. The - * default is Earth's gravity value. - * @property {Vec3} accelerationSpread=0,0,0 - The spread in accelerations that each particle is given. For example, if - * emitAccelerations == {x: 0, y: -9.8, z: 0} and accelerationSpread == - * {x: 0, y: 1, z: 0}, each particle will have an acceleration in the range {x: 0, y: -10.8, z: 0} - * – {x: 0, y: -8.8, z: 0}. - * @property {Vec3} dimensions - The dimensions of the particle effect, i.e., a bounding box containing all the particles - * during their lifetimes, assuming that emitterShouldTrail == false. Read-only. - * @property {boolean} emitterShouldTrail=false - true if particles are "left behind" as the emitter moves, - * false if they stay within the entity's dimensions. - * - * @property {Quat} emitOrientation=-0.707,0,0,0.707 - The orientation of particle emission relative to the entity's axes. By - * default, particles emit along the entity's local z-axis, and azimuthStart and azimuthFinish - * are relative to the entity's local x-axis. The default value is a rotation of -90 degrees about the local x-axis, i.e., - * the particles emit vertically. - * - * @property {ShapeType} shapeType="ellipsoid" - The shape from which particles are emitted. - * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType == - * "compound". - * @property {Vec3} emitDimensions=0,0,0 - The dimensions of the shape from which particles are emitted. - * @property {number} emitRadiusStart=1 - The starting radius within the shape at which particles start being emitted; - * range 0.01.0 for the center to the surface, respectively. - * Particles are emitted from the portion of the shape that lies between emitRadiusStart and the - * shape's surface. - * @property {number} polarStart=0 - The angle in radians from the entity's local z-axis at which particles start being emitted - * within the shape; range 0Math.PI. Particles are emitted from the portion of the - * shape that lies between polarStart and polarFinish. Only used if shapeType is - * "ellipsoid" or "sphere". - * @property {number} polarFinish=0 - The angle in radians from the entity's local z-axis at which particles stop being emitted - * within the shape; range 0Math.PI. Particles are emitted from the portion of the - * shape that lies between polarStart and polarFinish. Only used if shapeType is - * "ellipsoid" or "sphere". - * @property {number} azimuthStart=-Math.PI - The angle in radians from the entity's local x-axis about the entity's local - * z-axis at which particles start being emitted; range -Math.PIMath.PI. Particles are - * emitted from the portion of the shape that lies between azimuthStart and azimuthFinish. - * Only used if shapeType is "ellipsoid", "sphere", or "circle". - * @property {number} azimuthFinish=Math.PI - The angle in radians from the entity's local x-axis about the entity's local - * z-axis at which particles stop being emitted; range -Math.PIMath.PI. Particles are - * emitted from the portion of the shape that lies between azimuthStart and azimuthFinish. - * Only used if shapeType is "ellipsoid", "sphere", or "circle". - * - * @property {string} textures="" - The URL of a JPG or PNG image file to display for each particle. If you want transparency, - * use PNG format. - * @property {number} particleRadius=0.025 - The radius of each particle at the middle of its life. - * @property {number} radiusStart=null - The radius of each particle at the start of its life. If null, the - * particleRadius value is used. - * @property {number} radiusFinish=null - The radius of each particle at the end of its life. If null, the - * particleRadius value is used. - * @property {number} radiusSpread=0 - The spread in radius that each particle is given. For example, if - * particleRadius == 0.5 and radiusSpread == 0.25, each particle will have a radius in the range - * 0.250.75. - * @property {Color} color=255,255,255 - The color of each particle at the middle of its life. - * @property {ColorFloat} colorStart=null,null,null - The color of each particle at the start of its life. If any of the - * component values are undefined, the color value is used. - * @property {ColorFloat} colorFinish=null,null,null - The color of each particle at the end of its life. If any of the - * component values are undefined, the color value is used. - * @property {Color} colorSpread=0,0,0 - The spread in color that each particle is given. For example, if - * color == {red: 100, green: 100, blue: 100} and colorSpread == - * {red: 10, green: 25, blue: 50}, each particle will have a color in the range - * {red: 90, green: 75, blue: 50}{red: 110, green: 125, blue: 150}. - * @property {number} alpha=1 - The opacity of each particle at the middle of its life. - * @property {number} alphaStart=null - The opacity of each particle at the start of its life. If null, the - * alpha value is used. - * @property {number} alphaFinish=null - The opacity of each particle at the end of its life. If null, the - * alpha value is used. - * @property {number} alphaSpread=0 - The spread in alpha that each particle is given. For example, if - * alpha == 0.5 and alphaSpread == 0.25, each particle will have an alpha in the range - * 0.250.75. - * @property {Entities.Pulse} pulse - Color and alpha pulse. - *

    Deprecated: This property is deprecated and will be removed.

    - * @property {number} particleSpin=0 - The rotation of each particle at the middle of its life, range -2 * Math.PI - * – 2 * Math.PI radians. - * @property {number} spinStart=null - The rotation of each particle at the start of its life, range -2 * Math.PI - * – 2 * Math.PI radians. If null, the particleSpin value is used. - * @property {number} spinFinish=null - The rotation of each particle at the end of its life, range -2 * Math.PI - * – 2 * Math.PI radians. If null, the particleSpin value is used. - * @property {number} spinSpread=0 - The spread in spin that each particle is given, range 0 – - * 2 * Math.PI radians. For example, if particleSpin == Math.PI and - * spinSpread == Math.PI / 2, each particle will have a rotation in the range Math.PI / 2 – - * 3 * Math.PI / 2. - * @property {boolean} rotateWithEntity=false - true if the particles' rotations are relative to the entity's - * instantaneous rotation, false if they're relative to world coordinates. If true with - * particleSpin == 0, the particles keep oriented per the entity's orientation. - * - * @example Create a ball of green smoke. - * particles = Entities.addEntity({ - * type: "ParticleEffect", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -4 })), - * lifespan: 5, - * emitRate: 10, - * emitSpeed: 0.02, - * speedSpread: 0.01, - * emitAcceleration: { x: 0, y: 0.02, z: 0 }, - * polarFinish: Math.PI, - * textures: "https://content.overte.org/Bazaar/Assets/Textures/Defaults/Interface/default_particle.png", - * particleRadius: 0.1, - * color: { red: 0, green: 255, blue: 0 }, - * alphaFinish: 0, - * lifetime: 300 // Delete after 5 minutes. - * }); - */ - -/*@jsdoc - * The "PolyLine" {@link Entities.EntityType|EntityType} draws textured, straight lines between a sequence of - * points. It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. - * - * @typedef {object} Entities.EntityProperties-PolyLine - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity, i.e., the size of the bounding box that contains the - * lines drawn. Read-only. - * @property {Vec3[]} linePoints=[]] - The sequence of points to draw lines between. The values are relative to the entity's - * position. A maximum of 70 points can be specified. - * @property {Vec3[]} normals=[]] - The normal vectors for the line's surface at the linePoints. The values are - * relative to the entity's orientation. Must be specified in order for the entity to render. - * @property {number[]} strokeWidths=[]] - The widths, in m, of the line at the linePoints. Must be specified in - * order for the entity to render. - * @property {Vec3[]} strokeColors=[]] - The base colors of each point, with values in the range 0.0,0.0,0.0 - * – 1.0,1.0,1.0. These colors are multiplied with the color of the texture. If there are more line - * points than stroke colors, the color property value is used for the remaining points. - *

    Warning: The ordinate values are in the range 0.01.0.

    - * @property {Color} color=255,255,255 - Used as the color for each point if strokeColors doesn't have a value for - * the point. - * @property {string} textures="" - The URL of a JPG or PNG texture to use for the lines. If you want transparency, use PNG - * format. - * @property {boolean} isUVModeStretch=true - true if the texture is stretched to fill the whole line, - * false if the texture repeats along the line. - * @property {boolean} glow=false - true if the opacity of the strokes drops off away from the line center, - * false if it doesn't. - * @property {boolean} faceCamera=false - true if each line segment rotates to face the camera, false - * if they don't. - * @example Draw a textured "V". - * var entity = Entities.addEntity({ - * type: "PolyLine", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), - * rotation: MyAvatar.orientation, - * linePoints: [ - * { x: -1, y: 0.5, z: 0 }, - * { x: 0, y: 0, z: 0 }, - * { x: 1, y: 0.5, z: 0 } - * ], - * normals: [ - * { x: 0, y: 0, z: 1 }, - * { x: 0, y: 0, z: 1 }, - * { x: 0, y: 0, z: 1 } - * ], - * strokeWidths: [ 0.1, 0.1, 0.1 ], - * color: { red: 255, green: 0, blue: 0 }, // Use just the red channel from the image. - * textures: "https://hifi-content/DomainContent/Toybox/flowArts/trails.png", - * isUVModeStretch: true, - * lifetime: 300 // Delete after 5 minutes. - * }); - */ - -/*@jsdoc - * The "PolyVox" {@link Entities.EntityType|EntityType} displays a set of textured voxels. - * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. - * If you have two or more neighboring PolyVox entities of the same size abutting each other, you can display them as joined by - * configuring their voxelSurfaceStyle and various neighbor ID properties. - *
    - * - * @typedef {object} Entities.EntityProperties-PolyVox - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. - * @property {Vec3} voxelVolumeSize=32,32,32 - Integer number of voxels along each axis of the entity, in the range - * 1,1,1 to 128,128,128. The dimensions of each voxel is - * dimensions / voxelVolumesize. - * @property {string} voxelData="ABAAEAAQAAAAHgAAEAB42u3BAQ0AAADCoPdPbQ8HFAAAAPBuEAAAAQ==" - Base-64 encoded compressed dump of - * the PolyVox data. This property is typically not used in scripts directly; rather, functions that manipulate a PolyVox - * entity update it. - *

    The size of this property increases with the size and complexity of the PolyVox entity, with the size depending on how - * the particular entity's voxels compress. Because this property value has to fit within a Overte datagram packet, - * there is a limit to the size and complexity of a PolyVox entity; edits which would result in an overflow are rejected.

    - * @property {Entities.PolyVoxSurfaceStyle} voxelSurfaceStyle=2 - The style of rendering the voxels' surface and how - * neighboring PolyVox entities are joined. - * @property {string} xTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local x-axis. - * JPG or PNG format. If no texture is specified the surfaces display white. - * @property {string} yTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local y-axis. - * JPG or PNG format. If no texture is specified the surfaces display white. - * @property {string} zTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local z-axis. - * JPG or PNG format. If no texture is specified the surfaces display white. - * @property {Uuid} xNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local x-axis - * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} yNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local y-axis - * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} zNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local z-axis - * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} xPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local x-axis - * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} yPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local y-axis - * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} zPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local z-axis - * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @example Create a textured PolyVox sphere. - * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })); - * var texture = "http://public.highfidelity.com/cozza13/tuscany/Concrete2.jpg"; - * var polyVox = Entities.addEntity({ - * type: "PolyVox", - * position: position, - * dimensions: { x: 2, y: 2, z: 2 }, - * voxelVolumeSize: { x: 16, y: 16, z: 16 }, - * voxelSurfaceStyle: 2, - * xTextureURL: texture, - * yTextureURL: texture, - * zTextureURL: texture, - * lifetime: 300 // Delete after 5 minutes. - * }); - * Entities.setVoxelSphere(polyVox, position, 0.8, 255); - */ - -/*@jsdoc - * The "ProceduralParticleEffect" {@link Entities.EntityType|EntityType} displays a particle system that can be - * used to simulate things such as fire, smoke, snow, magic spells, etc. The particles are fully controlled by the provided - * update and rendering shaders on the GPU. - * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. - * - * @typedef {object} Entities.EntityProperties-ProceduralParticleEffect - * @property {number} numParticles=10000 - The number of particles to render. - * @property {number} numTrianglesPerParticle=1 - The number of triangles to render per particle. By default, these triangles - * still need to be positioned in the particleRenderData vertex shader. - * @property {number} numUpdateProps=0 - The number of persistent Vec4 values stored per particle and updated once per frame. - * These can be modified in the particleUpdateData fragment shader and read in the - * particleRenderData vertex/fragment shaders. - * @property {boolean} particleTransparent=false - Whether the particles should render as transparent (with additive blending) - * or opaque. - * @property {ProceduralData} particleUpdateData="" - Used to store {@link ProceduralData} data as a JSON string to control - * per-particle updates if numUpdateProps > 0. You can use JSON.parse() to parse the string - * into a JavaScript object which you can manipulate the properties of, and use JSON.stringify() to convert - * the object into a string to put in the property. - * @property {ProceduralData} particleRenderData="" - Used to store {@link ProceduralData} data as a JSON string to control - * per-particle rendering. You can use JSON.parse() to parse the string into a JavaScript object which you - * can manipulate the properties of, and use JSON.stringify() to convert the object into a string to put in - * the property. - * - * @example A cube of oscillating, unlit, billboarded triangles, with the oscillation in the update (computed once per particle instead of once per vertex). - * particles = Entities.addEntity({ - * type: "ProceduralParticleEffect", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -4 })), - * dimensions: 3, - * numParticles: 10000, - * numTrianglesPerParticle: 1, - * numUpdateProps: 1, - * particleUpdateData: JSON.stringify({ - * version: 1.0, - * fragmentShaderURL: "https://gist.githubusercontent.com/HifiExperiments/9049fb4a8dcd2c1401ff4321103dce16/raw/4f9474ed82c66c1f94c1055d2724af808cd7aace/proceduralParticleUpdate.fs", - * }), - * particleRenderData: JSON.stringify({ - * version: 1.0, - * vertexShaderURL: "https://gist.github.com/HifiExperiments/5dda24e28e7de1719e3a594d81306343/raw/92e0c5b82a9fa87685064cdbab92ed0c16f49f94/proceduralParticle2.vs", - * fragmentShaderURL: "https://gist.github.com/HifiExperiments/7def54504362c7bc79b5c85cd515b98b/raw/93b3828c2ec66b12b789a625dd141f533c595ede/proceduralParticle.fs", - * uniforms: { - * radius: 0.03 - * } - * }), - * lifetime: 300 // Delete after 5 minutes. - * }); - */ - -/*@jsdoc - * The "Shape" {@link Entities.EntityType|EntityType} displays an entity of a specified shape. - * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. - * - * @typedef {object} Entities.EntityProperties-Shape - * @property {Entities.Shape} shape="Sphere" - The shape of the entity. - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. - * @property {Color} color=255,255,255 - The color of the entity. - * @property {number} alpha=1 - The opacity of the entity, range 0.01.0. - * @property {boolean} unlit=false - true if the entity is unaffected by lighting, false if it is lit - * by the key light and local lights. - * @property {Entities.Pulse} pulse - Color and alpha pulse. - *

    Deprecated: This property is deprecated and will be removed.

    - * @example Create a cylinder. - * var shape = Entities.addEntity({ - * type: "Shape", - * shape: "Cylinder", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), - * dimensions: { x: 0.4, y: 0.6, z: 0.4 }, - * lifetime: 300 // Delete after 5 minutes. - * }); - */ - -/*@jsdoc - * The "Sound" {@link Entities.EntityType|EntityType} plays a sound from a URL. It has properties in addition to - * the common {@link Entities.EntityProperties|EntityProperties}. - * - * @typedef {object} Entities.EntityProperties-Sound - * @property {string} soundURL="" - The URL of the sound to play, as a wav, mp3, or raw file. Supports stereo and ambisonic. - * Note: ambisonic sounds can only play as localOnly. - * @property {boolean} playing=true - Whether or not the sound should play. - * @property {number} volume=1.0 - The volume of the sound, from 0 to 1. - * @property {number} pitch=1.0 - The relative sample rate at which to resample the sound, within +/- 2 octaves. - * @property {number} timeOffset=0.0 - The time (in seconds) at which to start playback within the sound file. If looping, - * this only affects the first loop. - * @property {boolean} loop=true - Whether or not to loop the sound. - * @property {boolean} positional=true - Whether or not the volume of the sound should decay with distance. - * @property {boolean} localOnly=false - Whether or not the sound should play locally for everyone (unsynced), or synchronously - * for everyone via the Entity Mixer. - * @example Create a Sound entity. - * var entity = Entities.addEntity({ - * type: "Sound", - * soundURL: "https://themushroomkingdom.net/sounds/wav/lm/lm_gold_mouse.wav", - * positional: true, - * volume: 0.75, - * localOnly: true, - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -4 })), - * rotation: MyAvatar.orientation, - * lifetime: 300 // Delete after 5 minutes. - * }); - */ - -/*@jsdoc - * The "Sphere" {@link Entities.EntityType|EntityType} is the same as the "Shape" - * {@link Entities.EntityType|EntityType} except that its shape value is always set to "Sphere" - * when the entity is created. If its shape property value is subsequently changed then the entity's - * type will be reported as "Box" if the shape is set to "Cube", - * otherwise it will be reported as "Shape". - * - * @typedef {object} Entities.EntityProperties-Sphere - * @see {@link Entities.EntityProperties-Shape|EntityProperties-Shape} - */ - -/*@jsdoc - * The "Text" {@link Entities.EntityType|EntityType} displays a 2D rectangle of text in the domain. - * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. - * - * @typedef {object} Entities.EntityProperties-Text - * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. - * @property {string} text="" - The text to display on the face of the entity. Text wraps if necessary to fit. New lines can be - * created using \n. Overflowing lines are not displayed. - * @property {number} lineHeight=0.1 - The height of each line of text (thus determining the font size). - * @property {Color} textColor=255,255,255 - The color of the text. - * @property {number} textAlpha=1.0 - The opacity of the text. - * @property {Color} backgroundColor=0,0,0 - The color of the background rectangle. - * @property {number} backgroundAlpha=1.0 - The opacity of the background. - * @property {Entities.Pulse} pulse - Color and alpha pulse. - *

    Deprecated: This property is deprecated and will be removed.

    - * @property {number} leftMargin=0.0 - The left margin, in meters. - * @property {number} rightMargin=0.0 - The right margin, in meters. - * @property {number} topMargin=0.0 - The top margin, in meters. - * @property {number} bottomMargin=0.0 - The bottom margin, in meters. - * @property {boolean} unlit=false - true if the entity is unaffected by lighting, false if it is lit - * by the key light and local lights. - * @property {string} font="" - The font to render the text with. It can be one of the following: "Courier", - * "Inconsolata", "Roboto", "Timeless", or a path to a PNG MTSDF .arfont file generated - * by the msdf-atlas-gen tool (https://github.com/Chlumsky/msdf-atlas-gen). - * @property {Entities.TextEffect} textEffect="none" - The effect that is applied to the text. - * @property {Color} textEffectColor=255,255,255 - The color of the effect. - * @property {number} textEffectThickness=0.2 - The magnitude of the text effect, range 0.00.5. - * @property {Entities.TextAlignment} alignment="left" - How the text is aligned against its background. - * @property {boolean} faceCamera - true if billboardMode is "yaw", false - * if it isn't. Setting this property to false sets the billboardMode to "none". - *

    Deprecated: This property is deprecated and will be removed.

    - * @property {boolean} isFacingAvatar - true if billboardMode is "full", - * false if it isn't. Setting this property to false sets the billboardMode to - * "none". - *

    Deprecated: This property is deprecated and will be removed.

    - * @example Create a text entity. - * var text = Entities.addEntity({ - * type: "Text", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), - * dimensions: { x: 0.6, y: 0.3, z: 0.01 }, - * lineHeight: 0.12, - * text: "Hello\nthere!", - * billboardMode: "yaw", - * lifetime: 300 // Delete after 5 minutes. - * }); - */ - -/*@jsdoc - * The "Web" {@link Entities.EntityType|EntityType} displays a browsable web page. Each user views their own copy - * of the web page: if one user navigates to another page on the entity, other users do not see the change; if a video is being - * played, users don't see it in sync. Internally, a Web entity is rendered as a non-repeating, upside down texture, so additional - * transformations may be necessary if you reference a Web entity texture by UUID. It has properties in addition to the common - * {@link Entities.EntityProperties|EntityProperties}. - * - * @typedef {object} Entities.EntityProperties-Web - * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. - * @property {string} sourceUrl="" - The URL of the web page to display. This value does not change as you or others navigate - * on the Web entity. - * @property {Color} color=255,255,255 - The color of the web surface. This color tints the web page displayed: the pixel - * colors on the web page are multiplied by the property color. For example, a value of - * { red: 255, green: 0, blue: 0 } lets only the red channel of pixels' colors through. - * @property {number} alpha=1 - The opacity of the web surface. - * @property {Entities.Pulse} pulse - Color and alpha pulse. - *

    Deprecated: This property is deprecated and will be removed.

    - * @property {boolean} faceCamera - true if billboardMode is "yaw", false - * if it isn't. Setting this property to false sets the billboardMode to "none". - *

    Deprecated: This property is deprecated and will be removed.

    - * @property {boolean} isFacingAvatar - true if billboardMode is "full", - * false if it isn't. Setting this property to false sets the billboardMode to - * "none". - *

    Deprecated: This property is deprecated and will be removed.

    - * @property {number} dpi=30 - The resolution to display the page at, in dots per inch. If you convert this to dots per meter - * (multiply by 1 / 0.0254 = 39.3701) then multiply dimensions.x and dimensions.y by that value - * you get the resolution in pixels. - * @property {string} scriptURL="" - The URL of a JavaScript file to inject into the web page. - * @property {number} maxFPS=10 - The maximum update rate for the web content, in frames/second. - * @property {WebInputMode} inputMode="touch" - The user input mode to use. - * @property {boolean} wantsKeyboardFocus=true - true if the entity should capture keyboard focus, false if it - * shouldn't. - * @property {boolean} showKeyboardFocusHighlight=true - true if the entity is highlighted when it has keyboard - * focus, false if it isn't. - * @property {boolean} useBackground=true - true if the web entity should have a background, - * false if the web entity's background should be transparent. The webpage must have CSS properties for transparency set - * on the background-color for this property to have an effect. - * @property {string} userAgent - The user agent for the web entity to use when visiting web pages. - * Default value: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) - * Chrome/69.0.3497.113 Mobile Safari/537.36 - * @example Create a Web entity displaying at 1920 x 1080 resolution. - * var METERS_TO_INCHES = 39.3701; - * var entity = Entities.addEntity({ - * type: "Web", - * sourceUrl: "https://overte.org/", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -4 })), - * rotation: MyAvatar.orientation, - * dimensions: { - * x: 3, - * y: 3 * 1080 / 1920, - * z: 0.01 - * }, - * dpi: 1920 / (3 * METERS_TO_INCHES), - * lifetime: 300 // Delete after 5 minutes. - * }); - */ - -/*@jsdoc - * The "Zone" {@link Entities.EntityType|EntityType} is a volume of lighting effects and avatar permissions. - * Avatar interaction events such as {@link Entities.enterEntity} are also often used with a Zone entity. It has properties in - * addition to the common {@link Entities.EntityProperties|EntityProperties}. - * - * @typedef {object} Entities.EntityProperties-Zone - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the volume in which the zone's lighting effects and avatar - * permissions have effect. - * - * @property {ShapeType} shapeType="box" - The shape of the volume in which the zone's lighting effects and avatar - * permissions have effect. Reverts to the default value if set to "none", or set to "compound" - * and compoundShapeURL is "". - * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType is - * "compound". - * - * @property {Entities.ComponentMode} keyLightMode="inherit" - Configures the key light in the zone. - * @property {Entities.KeyLight} keyLight - The key light properties of the zone. - * - * @property {Entities.ComponentMode} ambientLightMode="inherit" - Configures the ambient light in the zone. - * @property {Entities.AmbientLight} ambientLight - The ambient light properties of the zone. - * - * @property {Entities.ComponentMode} skyboxMode="inherit" - Configures the skybox displayed in the zone. - * @property {Entities.Skybox} skybox - The skybox properties of the zone. - * - * @property {Entities.ComponentMode} hazeMode="inherit" - Configures the haze in the zone. - * @property {Entities.Haze} haze - The haze properties of the zone. - * - * @property {Entities.ComponentMode} bloomMode="inherit" - Configures the bloom in the zone. - * @property {Entities.Bloom} bloom - The bloom properties of the zone. - * - * @property {Entities.ZoneAudio} audio - The audio properties of the zone. - * - * @property {Entities.ComponentMode} tonemappingMode="inherit" - Configures the tonemapping in the zone. - * @property {Entities.Tonemapping} tonemapping - The tonemapping properties of the zone. - * - * @property {Entities.ComponentMode} ambientOcclusionMode="inherit" - Configures the ambient occlusion in the zone. - * @property {Entities.AmbientOcclusion} ambientOcclusion - The ambient occlusion properties of the zone. - * - * @property {boolean} flyingAllowed=true - true if visitors can fly in the zone; false if they - * cannot. Only works for domain entities. - * @property {boolean} ghostingAllowed=true - true if visitors with avatar collisions turned off will not - * collide with content in the zone; false if visitors will always collide with content in the zone. Only - * works for domain entities. - * - * @property {string} filterURL="" - The URL of a JavaScript file that filters changes to properties of entities within the - * zone. It is periodically executed for each entity in the zone. It can, for example, be used to not allow changes to - * certain properties: - *
    - * function filter(properties) {
    - *     // Check and edit properties object values,
    - *     // e.g., properties.modelURL, as required.
    - *     return properties;
    - * }
    - * 
    - * - * @property {Entities.AvatarPriorityMode} avatarPriority="inherit" - Configures the priority of updates from avatars in the - * zone to other clients. - * - * @property {Entities.ScreenshareMode} screenshare="inherit" - Configures a zone for screen-sharing. - * - * @example Create a zone that casts a red key light along the x-axis. - * var zone = Entities.addEntity({ - * type: "Zone", - * position: MyAvatar.position, - * dimensions: { x: 100, y: 100, z: 100 }, - * keyLightMode: "enabled", - * keyLight: { - * "color": { "red": 255, "green": 0, "blue": 0 }, - * "direction": { "x": 1, "y": 0, "z": 0 } - * }, - * lifetime: 300 // Delete after 5 minutes. - * }); - */ - -/*@jsdoc - * The "Image" {@link Entities.EntityType|EntityType} displays an image on a 2D rectangle in the domain. - * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. - * - * @typedef {object} Entities.EntityProperties-Image - * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. - * @property {string} imageURL="" - The URL of the image to use. - * @property {boolean} emissive=false - true if the image should be emissive (unlit), false if it - * shouldn't. - * @property {boolean} keepAspectRatio=true - true if the image should maintain its aspect ratio, - * false if it shouldn't. - * @property {Rect} subImage=0,0,0,0 - The portion of the image to display. If width or height are 0, it defaults - * to the full image in that dimension. - * @property {Color} color=255,255,255 - The color of the image. - * @property {number} alpha=1 - The opacity of the image. - * @property {Entities.Pulse} pulse - Color and alpha pulse. - *

    Deprecated: This property is deprecated and will be removed.

    - * @property {boolean} faceCamera - true if billboardMode is "yaw", false - * if it isn't. Setting this property to false sets the billboardMode to "none". - *

    Deprecated: This property is deprecated and will be removed.

    - * @property {boolean} isFacingAvatar - true if billboardMode is "full", - * false if it isn't. Setting this property to false sets the billboardMode to - * "none". - *

    Deprecated: This property is deprecated and will be removed.

    - * @example Create an image entity. - * var image = Entities.addEntity({ - * type: "Image", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), - * dimensions: { x: 0.6, y: 0.3, z: 0.01 }, - * imageURL: "https://images.pexels.com/photos/1020315/pexels-photo-1020315.jpeg", - * billboardMode: "yaw", - * lifetime: 300 // Delete after 5 minutes. - * }); - */ - -/*@jsdoc - * The "Grid" {@link Entities.EntityType|EntityType} displays a grid on a 2D plane. - * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. - * - * @typedef {object} Entities.EntityProperties-Grid - * @property {Vec3} dimensions - 0.1,0.1,0.01 - The dimensions of the entity. - * @property {Color} color=255,255,255 - The color of the grid. - * @property {number} alpha=1 - The opacity of the grid. - * @property {Entities.Pulse} pulse - Color and alpha pulse. - *

    Deprecated: This property is deprecated and will be removed.

    - * @property {boolean} followCamera=true - true if the grid is always visible even as the camera moves to another - * position, false if it doesn't follow the camrmea. - * @property {number} majorGridEvery=5 - Integer number of minorGridEvery intervals at which to draw a thick grid - * line. Minimum value = 1. - * @property {number} minorGridEvery=1 - Real number of meters at which to draw thin grid lines. Minimum value = - * 0.001. - * @example Create a grid entity. - * var grid = Entities.addEntity({ - * type: "Grid", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), - * dimensions: { x: 100.0, y: 100.0, z: 0.01 }, - * followCamera: false, - * majorGridEvery: 4, - * minorGridEvery: 0.5, - * lifetime: 300 // Delete after 5 minutes. - * }); - */ - -/*@jsdoc - * The "Gizmo" {@link Entities.EntityType|EntityType} displays an entity that could be used as UI. - * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. - * - * @typedef {object} Entities.EntityProperties-Gizmo - * @property {Vec3} dimensions=0.1,0.001,0.1 - The dimensions of the entity. - * @property {Entities.GizmoType} gizmoType="ring" - The gizmo type of the entity. - * @property {Entities.RingGizmo} ring - The ring gizmo properties. - */ - -ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool skipDefaults, bool allowUnknownCreateTime, - bool strictSemantics, - EntityPseudoPropertyFlags pseudoPropertyFlags) const { - - // If strictSemantics is true and skipDefaults is false, then all and only those properties are copied for which the property flag - // is included in _desiredProperties, or is one of the specially enumerated ALWAYS properties below. - // (There may be exceptions, but if so, they are bugs.) - // In all other cases, you are welcome to inspect the code and try to figure out what was intended. I wish you luck. -HRS 1/18/17 - ScriptValue properties = engine->newObject(); - EntityItemProperties defaultEntityProperties; - - const bool pseudoPropertyFlagsActive = pseudoPropertyFlags.test(EntityPseudoPropertyFlag::FlagsActive); - // Fix to skip the default return all mechanism, when pseudoPropertyFlagsActive - const bool returnNothingOnEmptyPropertyFlags = pseudoPropertyFlagsActive; - - if (_created == UNKNOWN_CREATED_TIME && !allowUnknownCreateTime) { - // No entity properties can have been set so return without setting any default, zero property values. - return properties; - } - - auto nodeList = DependencyManager::get(); - bool isMyOwnAvatarEntity = _entityHostType == entity::HostType::AVATAR && (_owningAvatarID == AVATAR_SELF_ID || _owningAvatarID == Physics::getSessionUUID()); - if (_idSet && (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::ID))) { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(id, _id.toString()); - } - if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::Type)) { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(type, EntityTypes::getEntityTypeName(_type)); - } - if ((!skipDefaults || _lifetime != defaultEntityProperties._lifetime) && !strictSemantics) { - if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::Age)) { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(age, getAge()); // gettable, but not settable - } - if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::AgeAsText)) { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable - } - } - if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::LastEdited)) { - properties.setProperty("lastEdited", convertScriptValue(engine, _lastEdited)); - } - if (!skipDefaults) { - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DIMENSIONS, naturalDimensions); // gettable, but not settable - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POSITION, naturalPosition); - } - - // Core properties - //COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SIMULATION_OWNER, simulationOwner); // not exposed yet - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARENT_ID, parentID); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARENT_JOINT_INDEX, parentJointIndex); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VISIBLE, visible); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCKED, locked); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_DATA, userData); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PRIVATE_USER_DATA, privateUserData); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_HREF, href); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DESCRIPTION, description); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POSITION, position); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DIMENSIONS, dimensions); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ROTATION, rotation); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_REGISTRATION_POINT, registrationPoint); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CREATED, created); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LAST_EDITED_BY, lastEditedBy); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ENTITY_HOST_TYPE, entityHostType, getEntityHostTypeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_QUERY_AA_CUBE, queryAACube); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CAN_CAST_SHADOW, canCastShadow); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VISIBLE_IN_SECONDARY_CAMERA, isVisibleInSecondaryCamera); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_RENDER_LAYER, renderLayer, getRenderLayerAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_PRIMITIVE_MODE, primitiveMode, getPrimitiveModeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IGNORE_PICK_INTERSECTION, ignorePickIntersection); - 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_TAGS, tags, getTagsAsVector()); - _grab.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - 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); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VELOCITY, velocity); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_VELOCITY, angularVelocity); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAVITY, gravity); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ACCELERATION, acceleration); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DAMPING, damping); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_DAMPING, angularDamping); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RESTITUTION, restitution); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FRICTION, friction); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LIFETIME, lifetime); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISIONLESS, collisionless); - COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_COLLISIONLESS, collisionless, ignoreForCollisions, getCollisionless()); // legacy support - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_MASK, collisionMask); - COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_COLLISION_MASK, collisionMask, collidesWith, getCollisionMaskAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DYNAMIC, dynamic); - COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_DYNAMIC, dynamic, collisionsWillMove, getDynamic()); // legacy support - COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_COLLISION_SOUND_URL, collisionSoundURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ACTION_DATA, actionData); - - // Cloning - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE, cloneable); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_LIFETIME, cloneLifetime); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_LIMIT, cloneLimit); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_DYNAMIC, cloneDynamic); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_AVATAR_ENTITY, cloneAvatarEntity); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_ORIGIN_ID, cloneOriginID); - - // Scripts - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SCRIPT, script); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SCRIPT_TIMESTAMP, scriptTimestamp); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SERVER_SCRIPTS, serverScripts); - - // Local props for scripts - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_POSITION, localPosition); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ROTATION, localRotation); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_VELOCITY, localVelocity); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ANGULAR_VELOCITY, localAngularVelocity); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_DIMENSIONS, localDimensions); - - // Particles only - if (_type == EntityTypes::ParticleEffect) { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); - _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_PARTICLES, maxParticles); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LIFESPAN, lifespan); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMITTING_PARTICLES, isEmitting); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_RATE, emitRate); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_SPEED, emitSpeed); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPEED_SPREAD, speedSpread); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_ORIENTATION, emitOrientation); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_DIMENSIONS, emitDimensions); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_RADIUS_START, emitRadiusStart); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POLAR_START, polarStart); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POLAR_FINISH, polarFinish); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_AZIMUTH_START, azimuthStart); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_AZIMUTH_FINISH, azimuthFinish); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_ACCELERATION, emitAcceleration); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ACCELERATION_SPREAD, accelerationSpread); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARTICLE_RADIUS, particleRadius); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_SPREAD, radiusSpread); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_START, radiusStart); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_FINISH, radiusFinish); - - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR_SPREAD, colorSpread, u8vec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR_START, colorStart, vec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR_FINISH, colorFinish, vec3Color); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_SPREAD, alphaSpread); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_START, alphaStart); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_FINISH, alphaFinish); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMITTER_SHOULD_TRAIL, emitterShouldTrail); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARTICLE_SPIN, particleSpin); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPIN_SPREAD, spinSpread); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPIN_START, spinStart); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPIN_FINISH, spinFinish); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARTICLE_ROTATE_WITH_ENTITY, rotateWithEntity); - } - - // Procedural Particles - if (_type == EntityTypes::ProceduralParticleEffect) { - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, numParticles); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, numTrianglesPerParticle); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, numUpdateProps); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, particleTransparent); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, particleUpdateData); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, particleRenderData); - } - - // Models only - if (_type == EntityTypes::Model) { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); - - COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_MODEL_URL, modelURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MODEL_SCALE, modelScale); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_ROTATIONS_SET, jointRotationsSet); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_ROTATIONS, jointRotations); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS_SET, jointTranslationsSet); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS, jointTranslations); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GROUP_CULLED, groupCulled); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USE_ORIGINAL_PIVOT, useOriginalPivot); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOAD_PRIORITY, loadPriority); - _animation.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - } - - // FIXME: Shouldn't provide a shapeType property for Box and Sphere entities. - if (_type == EntityTypes::Box) { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, QString("Box")); - } - if (_type == EntityTypes::Sphere) { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, QString("Sphere")); - } - - if (_type == EntityTypes::Box || _type == EntityTypes::Sphere || _type == EntityTypes::Shape) { - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_UNLIT, unlit); - _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHAPE, shape); - } - - // Lights only - if (_type == EntityTypes::Light) { - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IS_SPOTLIGHT, isSpotlight); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_INTENSITY, intensity); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EXPONENT, exponent); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CUTOFF, cutoff); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FALLOFF_RADIUS, falloffRadius); - } - - // Text only - if (_type == EntityTypes::Text) { - _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT, text); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_HEIGHT, lineHeight); - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_TEXT_COLOR, textColor, u8vec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT_ALPHA, textAlpha); - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_BACKGROUND_COLOR, backgroundColor, u8vec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BACKGROUND_ALPHA, backgroundAlpha); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LEFT_MARGIN, leftMargin); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RIGHT_MARGIN, rightMargin); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TOP_MARGIN, topMargin); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BOTTOM_MARGIN, bottomMargin); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_UNLIT, unlit); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FONT, font); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TEXT_EFFECT, textEffect, getTextEffectAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_TEXT_EFFECT_COLOR, textEffectColor, u8vec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT_EFFECT_THICKNESS, textEffectThickness); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TEXT_ALIGNMENT, alignment, getAlignmentAsString()); - } - - // Zones only - if (_type == EntityTypes::Zone) { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); - - _keyLight.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - _ambientLight.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - _skybox.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - _haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - _bloom.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - _audio.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - _tonemapping.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - _ambientOcclusion.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FLYING_ALLOWED, flyingAllowed); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GHOSTING_ALLOWED, ghostingAllowed); - COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_FILTER_URL, filterURL); - - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_KEY_LIGHT_MODE, keyLightMode, getKeyLightModeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_LIGHT_MODE, ambientLightMode, getAmbientLightModeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SKYBOX_MODE, skyboxMode, getSkyboxModeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_HAZE_MODE, hazeMode, getHazeModeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BLOOM_MODE, bloomMode, getBloomModeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AVATAR_PRIORITY, avatarPriority, getAvatarPriorityAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SCREENSHARE, screenshare, getScreenshareAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TONEMAPPING_MODE, tonemappingMode, getTonemappingModeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_OCCLUSION_MODE, ambientOcclusionMode, getAmbientOcclusionModeAsString()); - } - - // Web only - if (_type == EntityTypes::Web) { - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); - _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - - COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_SOURCE_URL, sourceUrl); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DPI, dpi); - COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_SCRIPT_URL, scriptURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_FPS, maxFPS); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_INPUT_MODE, inputMode, getInputModeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_WANTS_KEYBOARD_FOCUS, wantsKeyboardFocus); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, showKeyboardFocusHighlight); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_WEB_USE_BACKGROUND, useBackground); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_AGENT, userAgent); - } - - // PolyVoxel only - if (_type == EntityTypes::PolyVox) { - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VOXEL_DATA, voxelData); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VOXEL_SURFACE_STYLE, voxelSurfaceStyle); - COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_X_TEXTURE_URL, xTextureURL); - COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_Y_TEXTURE_URL, yTextureURL); - COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_Z_TEXTURE_URL, zTextureURL); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_X_N_NEIGHBOR_ID, xNNeighborID); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_Y_N_NEIGHBOR_ID, yNNeighborID); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_Z_N_NEIGHBOR_ID, zNNeighborID); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_X_P_NEIGHBOR_ID, xPNeighborID); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_Y_P_NEIGHBOR_ID, yPNeighborID); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_Z_P_NEIGHBOR_ID, zPNeighborID); - } - - // Lines - if (_type == EntityTypes::Line) { - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_POINTS, linePoints); - } - - // Polylines - if (_type == EntityTypes::PolyLine) { - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_POINTS, linePoints); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_WIDTHS, strokeWidths); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_NORMALS, normals); - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_STROKE_COLORS, strokeColors, qVectorVec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IS_UV_MODE_STRETCH, isUVModeStretch); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_GLOW, glow); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_FACE_CAMERA, faceCamera); - } - - // Materials - if (_type == EntityTypes::Material) { - COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_MATERIAL_URL, materialURL); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_MATERIAL_MAPPING_MODE, materialMappingMode, getMaterialMappingModeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_PRIORITY, priority); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARENT_MATERIAL_NAME, parentMaterialName); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_MAPPING_POS, materialMappingPos); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_MAPPING_SCALE, materialMappingScale); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_MAPPING_ROT, materialMappingRot); - COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_MATERIAL_DATA, materialData); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_REPEAT, materialRepeat); - } - - // Image only - if (_type == EntityTypes::Image) { - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); - _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - - COPY_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_IMAGE_URL, imageURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMISSIVE, emissive); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_KEEP_ASPECT_RATIO, keepAspectRatio); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SUB_IMAGE, subImage); - - // Handle conversions to old 'textures' property from "imageURL" - if ((isMyOwnAvatarEntity || nodeList->getThisNodeCanViewAssetURLs()) && - ((!returnNothingOnEmptyPropertyFlags && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(PROP_IMAGE_URL)) && - (!skipDefaults || defaultEntityProperties._imageURL != _imageURL)) { - ScriptValue textures = engine->newObject(); - textures.setProperty("tex.picture", _imageURL); - properties.setProperty("textures", textures); - } - } - - // Grid only - if (_type == EntityTypes::Grid) { - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); - _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GRID_FOLLOW_CAMERA, followCamera); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAJOR_GRID_EVERY, majorGridEvery); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MINOR_GRID_EVERY, minorGridEvery); - } - - // Gizmo only - if (_type == EntityTypes::Gizmo) { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_GIZMO_TYPE, gizmoType, getGizmoTypeAsString()); - _ring.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); - } - - // Sound only - if (_type == EntityTypes::Sound) { - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_URL, soundURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_VOLUME, volume); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_TIME_OFFSET, timeOffset); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_PITCH, pitch); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_PLAYING, playing); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_LOOP, loop); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_POSITIONAL, positional); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_LOCAL_ONLY, localOnly); - } - - /*@jsdoc - * The axis-aligned bounding box of an entity. - * @typedef {object} Entities.BoundingBox - * @property {Vec3} brn - The bottom right near (minimum axes values) corner of the AA box. - * @property {Vec3} tfl - The top far left (maximum axes values) corner of the AA box. - * @property {Vec3} center - The center of the AA box. - * @property {Vec3} dimensions - The dimensions of the AA box. - */ - if (!skipDefaults && !strictSemantics && - (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::BoundingBox))) { - - AABox aaBox = getAABox(); - ScriptValue boundingBox = engine->newObject(); - ScriptValue bottomRightNear = vec3ToScriptValue(engine, aaBox.getCorner()); - ScriptValue topFarLeft = vec3ToScriptValue(engine, aaBox.calcTopFarLeft()); - ScriptValue center = vec3ToScriptValue(engine, aaBox.calcCenter()); - ScriptValue boundingBoxDimensions = vec3ToScriptValue(engine, aaBox.getDimensions()); - boundingBox.setProperty("brn", bottomRightNear); - boundingBox.setProperty("tfl", topFarLeft); - boundingBox.setProperty("center", center); - boundingBox.setProperty("dimensions", boundingBoxDimensions); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(boundingBox, boundingBox); // gettable, but not settable - } - - QString textureNamesStr = QJsonDocument::fromVariant(_textureNames).toJson(); - if (!skipDefaults && !strictSemantics && (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::OriginalTextures))) { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(originalTextures, textureNamesStr); // gettable, but not settable - } - - // Rendering info - if (!skipDefaults && !strictSemantics && - (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::RenderInfo))) { - - ScriptValue renderInfo = engine->newObject(); - - /*@jsdoc - * Information on how an entity is rendered. Properties are only filled in for Model entities; other - * entity types have an empty object, {}. - * @typedef {object} Entities.RenderInfo - * @property {number} verticesCount - The number of vertices in the entity. - * @property {number} texturesCount - The number of textures in the entity. - * @property {number} texturesSize - The total size of the textures in the entity, in bytes. - * @property {boolean} hasTransparent - true if any of the textures has transparency, false - * if none of them do. - * @property {number} drawCalls - The number of draw calls required to render the entity. - */ - // currently only supported by models - if (_type == EntityTypes::Model) { - renderInfo.setProperty("verticesCount", (int)getRenderInfoVertexCount()); // FIXME - theoretically the number of vertex could be > max int - renderInfo.setProperty("texturesSize", (int)getRenderInfoTextureSize()); // FIXME - theoretically the size of textures could be > max int - renderInfo.setProperty("hasTransparent", getRenderInfoHasTransparent()); - renderInfo.setProperty("drawCalls", getRenderInfoDrawCalls()); - renderInfo.setProperty("texturesCount", getRenderInfoTextureCount()); - } - - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(renderInfo, renderInfo); // Gettable but not settable - } - - if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::ClientOnly)) { - properties.setProperty("clientOnly", convertScriptValue(engine, getEntityHostType() == entity::HostType::AVATAR)); - } - if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::AvatarEntity)) { - properties.setProperty("avatarEntity", convertScriptValue(engine, getEntityHostType() == entity::HostType::AVATAR)); - } - if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::LocalEntity)) { - properties.setProperty("localEntity", convertScriptValue(engine, getEntityHostType() == entity::HostType::LOCAL)); - } - - if (_type != EntityTypes::PolyLine && (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::FaceCamera))) { - properties.setProperty("faceCamera", convertScriptValue(engine, getBillboardMode() == BillboardMode::YAW)); - } - if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::IsFacingAvatar)) { - properties.setProperty("isFacingAvatar", convertScriptValue(engine, getBillboardMode() == BillboardMode::FULL)); - } - - return properties; -} - -void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool honorReadOnly) { - //qDebug() << "EntityItemProperties::copyFromScriptValue: properties: " << object.getPropertyNames(); - QList namesList = object.getPropertyNames(); - - QSet namesSet; - for (auto name = namesList.cbegin(); name != namesList.cend(); name++) { - namesSet.insert(*name); - } - - ScriptValue typeScriptValue = object.property("type"); - if (typeScriptValue.isValid()) { - setType(typeScriptValue.toVariant().toString()); - } - - // Core - if (!honorReadOnly) { - // not handled yet - // COPY_PROPERTY_FROM_QSCRIPTVALUE(simulationOwner, SimulationOwner, setSimulationOwner); - } - COPY_PROPERTY_FROM_QSCRIPTVALUE(parentID, QUuid, setParentID); - COPY_PROPERTY_FROM_QSCRIPTVALUE(parentJointIndex, quint16, setParentJointIndex); - COPY_PROPERTY_FROM_QSCRIPTVALUE(visible, bool, setVisible); - COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName); - COPY_PROPERTY_FROM_QSCRIPTVALUE(locked, bool, setLocked); - COPY_PROPERTY_FROM_QSCRIPTVALUE(userData, QString, setUserData); - COPY_PROPERTY_FROM_QSCRIPTVALUE(privateUserData, QString, setPrivateUserData); - COPY_PROPERTY_FROM_QSCRIPTVALUE(href, QString, setHref); - COPY_PROPERTY_FROM_QSCRIPTVALUE(description, QString, setDescription); - COPY_PROPERTY_FROM_QSCRIPTVALUE(position, vec3, setPosition); - COPY_PROPERTY_FROM_QSCRIPTVALUE(dimensions, vec3, setDimensions); - COPY_PROPERTY_FROM_QSCRIPTVALUE(rotation, quat, setRotation); - COPY_PROPERTY_FROM_QSCRIPTVALUE(registrationPoint, vec3, setRegistrationPoint); - if (!honorReadOnly) { - COPY_PROPERTY_FROM_QSCRIPTVALUE(created, quint64, setCreated); - COPY_PROPERTY_FROM_QSCRIPTVALUE(lastEditedBy, QUuid, setLastEditedBy); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(entityHostType, EntityHostType); - COPY_PROPERTY_FROM_QSCRIPTVALUE(owningAvatarID, QUuid, setOwningAvatarID); - } - COPY_PROPERTY_FROM_QSCRIPTVALUE(queryAACube, AACube, setQueryAACube); // TODO: should scripts be able to set this? - COPY_PROPERTY_FROM_QSCRIPTVALUE(canCastShadow, bool, setCanCastShadow); - COPY_PROPERTY_FROM_QSCRIPTVALUE(isVisibleInSecondaryCamera, bool, setIsVisibleInSecondaryCamera); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(renderLayer, RenderLayer); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(primitiveMode, PrimitiveMode); - COPY_PROPERTY_FROM_QSCRIPTVALUE(ignorePickIntersection, bool, setIgnorePickIntersection); - COPY_PROPERTY_FROM_QSCRIPTVALUE(renderWithZones, qVectorQUuid, setRenderWithZones); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(billboardMode, BillboardMode); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(tags, qVectorQString, setTagsFromVector, getTagsAsVector); - _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); - COPY_PROPERTY_FROM_QSCRIPTVALUE(velocity, vec3, setVelocity); - COPY_PROPERTY_FROM_QSCRIPTVALUE(angularVelocity, vec3, setAngularVelocity); - COPY_PROPERTY_FROM_QSCRIPTVALUE(gravity, vec3, setGravity); - COPY_PROPERTY_FROM_QSCRIPTVALUE(acceleration, vec3, setAcceleration); - COPY_PROPERTY_FROM_QSCRIPTVALUE(damping, float, setDamping); - COPY_PROPERTY_FROM_QSCRIPTVALUE(angularDamping, float, setAngularDamping); - COPY_PROPERTY_FROM_QSCRIPTVALUE(restitution, float, setRestitution); - COPY_PROPERTY_FROM_QSCRIPTVALUE(friction, float, setFriction); - COPY_PROPERTY_FROM_QSCRIPTVALUE(lifetime, float, setLifetime); - COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionless, bool, setCollisionless); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(ignoreForCollisions, bool, setCollisionless, getCollisionless); // legacy support - COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionMask, uint16_t, setCollisionMask); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(collidesWith, CollisionMask); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(collisionsWillMove, bool, setDynamic, getDynamic); // legacy support - COPY_PROPERTY_FROM_QSCRIPTVALUE(dynamic, bool, setDynamic); - COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionSoundURL, QString, setCollisionSoundURL); - if (!honorReadOnly) { - COPY_PROPERTY_FROM_QSCRIPTVALUE(actionData, QByteArray, setActionData); - } - - // Cloning - COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneable, bool, setCloneable); - COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneLifetime, float, setCloneLifetime); - COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneLimit, float, setCloneLimit); - COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneDynamic, bool, setCloneDynamic); - COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneAvatarEntity, bool, setCloneAvatarEntity); - COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneOriginID, QUuid, setCloneOriginID); - - // Scripts - COPY_PROPERTY_FROM_QSCRIPTVALUE(script, QString, setScript); - COPY_PROPERTY_FROM_QSCRIPTVALUE(scriptTimestamp, quint64, setScriptTimestamp); - COPY_PROPERTY_FROM_QSCRIPTVALUE(serverScripts, QString, setServerScripts); - - // Script location data - COPY_PROPERTY_FROM_QSCRIPTVALUE(localPosition, vec3, setLocalPosition); - COPY_PROPERTY_FROM_QSCRIPTVALUE(localRotation, quat, setLocalRotation); - COPY_PROPERTY_FROM_QSCRIPTVALUE(localVelocity, vec3, setLocalVelocity); - COPY_PROPERTY_FROM_QSCRIPTVALUE(localAngularVelocity, vec3, setLocalAngularVelocity); - COPY_PROPERTY_FROM_QSCRIPTVALUE(localDimensions, vec3, setLocalDimensions); - - // Common - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(shapeType, ShapeType); - COPY_PROPERTY_FROM_QSCRIPTVALUE(compoundShapeURL, QString, setCompoundShapeURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE(color, u8vec3Color, setColor); - COPY_PROPERTY_FROM_QSCRIPTVALUE(alpha, float, setAlpha); - COPY_PROPERTY_FROM_QSCRIPTVALUE(unlit, bool, setUnlit); - _pulse.copyFromScriptValue(object, namesSet, _defaultSettings); - COPY_PROPERTY_FROM_QSCRIPTVALUE(textures, QString, setTextures); - - // Particles - COPY_PROPERTY_FROM_QSCRIPTVALUE(maxParticles, quint32, setMaxParticles); - COPY_PROPERTY_FROM_QSCRIPTVALUE(lifespan, float, setLifespan); - COPY_PROPERTY_FROM_QSCRIPTVALUE(isEmitting, bool, setIsEmitting); - COPY_PROPERTY_FROM_QSCRIPTVALUE(emitRate, float, setEmitRate); - COPY_PROPERTY_FROM_QSCRIPTVALUE(emitSpeed, float, setEmitSpeed); - COPY_PROPERTY_FROM_QSCRIPTVALUE(speedSpread, float, setSpeedSpread); - COPY_PROPERTY_FROM_QSCRIPTVALUE(emitOrientation, quat, setEmitOrientation); - COPY_PROPERTY_FROM_QSCRIPTVALUE(emitDimensions, vec3, setEmitDimensions); - COPY_PROPERTY_FROM_QSCRIPTVALUE(emitRadiusStart, float, setEmitRadiusStart); - COPY_PROPERTY_FROM_QSCRIPTVALUE(polarStart, float, setPolarStart); - COPY_PROPERTY_FROM_QSCRIPTVALUE(polarFinish, float, setPolarFinish); - COPY_PROPERTY_FROM_QSCRIPTVALUE(azimuthStart, float, setAzimuthStart); - COPY_PROPERTY_FROM_QSCRIPTVALUE(azimuthFinish, float, setAzimuthFinish); - COPY_PROPERTY_FROM_QSCRIPTVALUE(emitAcceleration, vec3, setEmitAcceleration); - COPY_PROPERTY_FROM_QSCRIPTVALUE(accelerationSpread, vec3, setAccelerationSpread); - COPY_PROPERTY_FROM_QSCRIPTVALUE(particleRadius, float, setParticleRadius); - COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusSpread, float, setRadiusSpread); - COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusStart, float, setRadiusStart); - COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusFinish, float, setRadiusFinish); - COPY_PROPERTY_FROM_QSCRIPTVALUE(colorSpread, u8vec3Color, setColorSpread); - COPY_PROPERTY_FROM_QSCRIPTVALUE(colorStart, vec3Color, setColorStart); - COPY_PROPERTY_FROM_QSCRIPTVALUE(colorFinish, vec3Color, setColorFinish); - COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaSpread, float, setAlphaSpread); - COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaStart, float, setAlphaStart); - COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaFinish, float, setAlphaFinish); - COPY_PROPERTY_FROM_QSCRIPTVALUE(emitterShouldTrail, bool, setEmitterShouldTrail); - COPY_PROPERTY_FROM_QSCRIPTVALUE(particleSpin, float, setParticleSpin); - COPY_PROPERTY_FROM_QSCRIPTVALUE(spinSpread, float, setSpinSpread); - COPY_PROPERTY_FROM_QSCRIPTVALUE(spinStart, float, setSpinStart); - COPY_PROPERTY_FROM_QSCRIPTVALUE(spinFinish, float, setSpinFinish); - COPY_PROPERTY_FROM_QSCRIPTVALUE(rotateWithEntity, bool, setRotateWithEntity); - - // Procedural Particles - COPY_PROPERTY_FROM_QSCRIPTVALUE(numParticles, uint32_t, setNumParticles); - COPY_PROPERTY_FROM_QSCRIPTVALUE(numTrianglesPerParticle, uint8_t, setNumTrianglesPerParticle); - COPY_PROPERTY_FROM_QSCRIPTVALUE(numUpdateProps, uint8_t, setNumUpdateProps); - COPY_PROPERTY_FROM_QSCRIPTVALUE(particleTransparent, bool, setParticleTransparent); - COPY_PROPERTY_FROM_QSCRIPTVALUE(particleUpdateData, QString, setParticleUpdateData); - COPY_PROPERTY_FROM_QSCRIPTVALUE(particleRenderData, QString, setParticleRenderData); - - // Model - COPY_PROPERTY_FROM_QSCRIPTVALUE(modelURL, QString, setModelURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE(modelScale, vec3, setModelScale); - COPY_PROPERTY_FROM_QSCRIPTVALUE(jointRotationsSet, qVectorBool, setJointRotationsSet); - COPY_PROPERTY_FROM_QSCRIPTVALUE(jointRotations, qVectorQuat, setJointRotations); - COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslationsSet, qVectorBool, setJointTranslationsSet); - COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslations, qVectorVec3, setJointTranslations); - COPY_PROPERTY_FROM_QSCRIPTVALUE(relayParentJoints, bool, setRelayParentJoints); - COPY_PROPERTY_FROM_QSCRIPTVALUE(groupCulled, bool, setGroupCulled); - COPY_PROPERTY_FROM_QSCRIPTVALUE(blendshapeCoefficients, QString, setBlendshapeCoefficients); - COPY_PROPERTY_FROM_QSCRIPTVALUE(useOriginalPivot, bool, setUseOriginalPivot); - COPY_PROPERTY_FROM_QSCRIPTVALUE(loadPriority, float, setLoadPriority); - _animation.copyFromScriptValue(object, namesSet, _defaultSettings); - - // Light - COPY_PROPERTY_FROM_QSCRIPTVALUE(isSpotlight, bool, setIsSpotlight); - COPY_PROPERTY_FROM_QSCRIPTVALUE(intensity, float, setIntensity); - COPY_PROPERTY_FROM_QSCRIPTVALUE(exponent, float, setExponent); - COPY_PROPERTY_FROM_QSCRIPTVALUE(cutoff, float, setCutoff); - COPY_PROPERTY_FROM_QSCRIPTVALUE(falloffRadius, float, setFalloffRadius); - - // Text - COPY_PROPERTY_FROM_QSCRIPTVALUE(text, QString, setText); - COPY_PROPERTY_FROM_QSCRIPTVALUE(lineHeight, float, setLineHeight); - COPY_PROPERTY_FROM_QSCRIPTVALUE(textColor, u8vec3Color, setTextColor); - COPY_PROPERTY_FROM_QSCRIPTVALUE(textAlpha, float, setTextAlpha); - COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundColor, u8vec3Color, setBackgroundColor); - COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundAlpha, float, setBackgroundAlpha); - COPY_PROPERTY_FROM_QSCRIPTVALUE(leftMargin, float, setLeftMargin); - COPY_PROPERTY_FROM_QSCRIPTVALUE(rightMargin, float, setRightMargin); - COPY_PROPERTY_FROM_QSCRIPTVALUE(topMargin, float, setTopMargin); - COPY_PROPERTY_FROM_QSCRIPTVALUE(bottomMargin, float, setBottomMargin); - COPY_PROPERTY_FROM_QSCRIPTVALUE(font, QString, setFont); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(textEffect, TextEffect); - COPY_PROPERTY_FROM_QSCRIPTVALUE(textEffectColor, u8vec3Color, setTextEffectColor); - COPY_PROPERTY_FROM_QSCRIPTVALUE(textEffectThickness, float, setTextEffectThickness); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(alignment, Alignment); - - // Zone - _keyLight.copyFromScriptValue(object, namesSet, _defaultSettings); - _ambientLight.copyFromScriptValue(object, namesSet, _defaultSettings); - _skybox.copyFromScriptValue(object, namesSet, _defaultSettings); - _haze.copyFromScriptValue(object, namesSet, _defaultSettings); - _bloom.copyFromScriptValue(object, namesSet, _defaultSettings); - _audio.copyFromScriptValue(object, namesSet, _defaultSettings); - _tonemapping.copyFromScriptValue(object, namesSet, _defaultSettings); - _ambientOcclusion.copyFromScriptValue(object, namesSet, _defaultSettings); - COPY_PROPERTY_FROM_QSCRIPTVALUE(flyingAllowed, bool, setFlyingAllowed); - COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed); - COPY_PROPERTY_FROM_QSCRIPTVALUE(filterURL, QString, setFilterURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(keyLightMode, KeyLightMode); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientLightMode, AmbientLightMode); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(skyboxMode, SkyboxMode); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(hazeMode, HazeMode); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(bloomMode, BloomMode); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(avatarPriority, AvatarPriority); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(screenshare, Screenshare); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(tonemappingMode, TonemappingMode); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientOcclusionMode, AmbientOcclusionMode); - - // Polyvox - COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, vec3, setVoxelVolumeSize); - COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelData, QByteArray, setVoxelData); - COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelSurfaceStyle, uint16_t, setVoxelSurfaceStyle); - COPY_PROPERTY_FROM_QSCRIPTVALUE(xTextureURL, QString, setXTextureURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE(yTextureURL, QString, setYTextureURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE(zTextureURL, QString, setZTextureURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE(xNNeighborID, EntityItemID, setXNNeighborID); - COPY_PROPERTY_FROM_QSCRIPTVALUE(yNNeighborID, EntityItemID, setYNNeighborID); - COPY_PROPERTY_FROM_QSCRIPTVALUE(zNNeighborID, EntityItemID, setZNNeighborID); - COPY_PROPERTY_FROM_QSCRIPTVALUE(xPNeighborID, EntityItemID, setXPNeighborID); - COPY_PROPERTY_FROM_QSCRIPTVALUE(yPNeighborID, EntityItemID, setYPNeighborID); - COPY_PROPERTY_FROM_QSCRIPTVALUE(zPNeighborID, EntityItemID, setZPNeighborID); - - // Web - COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl); - COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI); - COPY_PROPERTY_FROM_QSCRIPTVALUE(scriptURL, QString, setScriptURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE(maxFPS, uint8_t, setMaxFPS); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(inputMode, InputMode); - COPY_PROPERTY_FROM_QSCRIPTVALUE(wantsKeyboardFocus, bool, setWantsKeyboardFocus); - COPY_PROPERTY_FROM_QSCRIPTVALUE(showKeyboardFocusHighlight, bool, setShowKeyboardFocusHighlight); - COPY_PROPERTY_FROM_QSCRIPTVALUE(useBackground, bool, setUseBackground); - COPY_PROPERTY_FROM_QSCRIPTVALUE(userAgent, QString, setUserAgent); - - // Polyline - COPY_PROPERTY_FROM_QSCRIPTVALUE(linePoints, qVectorVec3, setLinePoints); - COPY_PROPERTY_FROM_QSCRIPTVALUE(strokeWidths, qVectorFloat, setStrokeWidths); - COPY_PROPERTY_FROM_QSCRIPTVALUE(normals, qVectorVec3, setNormals); - COPY_PROPERTY_FROM_QSCRIPTVALUE(strokeColors, qVectorVec3, setStrokeColors); - COPY_PROPERTY_FROM_QSCRIPTVALUE(isUVModeStretch, bool, setIsUVModeStretch); - COPY_PROPERTY_FROM_QSCRIPTVALUE(glow, bool, setGlow); - COPY_PROPERTY_FROM_QSCRIPTVALUE(faceCamera, bool, setFaceCamera); - - // Shape - COPY_PROPERTY_FROM_QSCRIPTVALUE(shape, QString, setShape); - - // Material - COPY_PROPERTY_FROM_QSCRIPTVALUE(materialURL, QString, setMaterialURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(materialMappingMode, MaterialMappingMode); - COPY_PROPERTY_FROM_QSCRIPTVALUE(priority, quint16, setPriority); - COPY_PROPERTY_FROM_QSCRIPTVALUE(parentMaterialName, QString, setParentMaterialName); - COPY_PROPERTY_FROM_QSCRIPTVALUE(materialMappingPos, vec2, setMaterialMappingPos); - COPY_PROPERTY_FROM_QSCRIPTVALUE(materialMappingScale, vec2, setMaterialMappingScale); - COPY_PROPERTY_FROM_QSCRIPTVALUE(materialMappingRot, float, setMaterialMappingRot); - COPY_PROPERTY_FROM_QSCRIPTVALUE(materialData, QString, setMaterialData); - COPY_PROPERTY_FROM_QSCRIPTVALUE(materialRepeat, bool, setMaterialRepeat); - - // Image - COPY_PROPERTY_FROM_QSCRIPTVALUE(imageURL, QString, setImageURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE(emissive, bool, setEmissive); - COPY_PROPERTY_FROM_QSCRIPTVALUE(keepAspectRatio, bool, setKeepAspectRatio); - COPY_PROPERTY_FROM_QSCRIPTVALUE(subImage, QRect, setSubImage); - - // Grid - COPY_PROPERTY_FROM_QSCRIPTVALUE(followCamera, bool, setFollowCamera); - COPY_PROPERTY_FROM_QSCRIPTVALUE(majorGridEvery, uint32_t, setMajorGridEvery); - COPY_PROPERTY_FROM_QSCRIPTVALUE(minorGridEvery, float, setMinorGridEvery); - - // Gizmo - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(gizmoType, GizmoType); - _ring.copyFromScriptValue(object, namesSet, _defaultSettings); - - // Sound - COPY_PROPERTY_FROM_QSCRIPTVALUE(soundURL, QString, setSoundURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE(volume, float, setVolume); - COPY_PROPERTY_FROM_QSCRIPTVALUE(timeOffset, float, setTimeOffset); - COPY_PROPERTY_FROM_QSCRIPTVALUE(pitch, float, setPitch); - COPY_PROPERTY_FROM_QSCRIPTVALUE(playing, bool, setPlaying); - COPY_PROPERTY_FROM_QSCRIPTVALUE(loop, bool, setLoop); - COPY_PROPERTY_FROM_QSCRIPTVALUE(positional, bool, setPositional); - COPY_PROPERTY_FROM_QSCRIPTVALUE(localOnly, bool, setLocalOnly); - - // Handle conversions from old 'textures' property to "imageURL" - if (namesSet.contains("textures")) { - ScriptValue V = object.property("textures"); - if (_type == EntityTypes::Image && V.isValid() && !object.property("imageURL").isValid()) { - bool isValid = false; - QString textures = QString_convertFromScriptValue(V, isValid); - if (isValid) { - QVariantMap texturesMap = parseTexturesToMap(textures, QVariantMap()); - auto texPicture = texturesMap.find("tex.picture"); - if (texPicture != texturesMap.end()) { - auto imageURL = texPicture.value().toString(); - if (_defaultSettings || imageURL != _imageURL) { - setImageURL(imageURL); - } - } - } - } - } - - // Handle old "faceCamera" and "isFacingAvatar" props - if (_type != EntityTypes::PolyLine && namesSet.contains("textures")) { - ScriptValue P = object.property("faceCamera"); - if (P.isValid() && !object.property("billboardMode").isValid()) { - bool newValue = P.toVariant().toBool(); - bool oldValue = getBillboardMode() == BillboardMode::YAW; - if (_defaultSettings || newValue != oldValue) { - setBillboardMode(newValue ? BillboardMode::YAW : BillboardMode::NONE); - } - } - } - if (namesSet.contains("isFacingAvatar")) { - ScriptValue P = object.property("isFacingAvatar"); - if (P.isValid() && !object.property("billboardMode").isValid() && !object.property("faceCamera").isValid()) { - bool newValue = P.toVariant().toBool(); - bool oldValue = getBillboardMode() == BillboardMode::FULL; - if (_defaultSettings || newValue != oldValue) { - setBillboardMode(newValue ? BillboardMode::FULL : BillboardMode::NONE); - } - } - } - - _lastEdited = usecTimestampNow(); -} - -void EntityItemProperties::copyFromJSONString(ScriptEngine& scriptEngine, const QString& jsonString) { - // DANGER: this method is expensive - QJsonDocument propertiesDoc = QJsonDocument::fromJson(jsonString.toUtf8()); - QJsonObject propertiesObj = propertiesDoc.object(); - QVariant propertiesVariant(propertiesObj); - QVariantMap propertiesMap = propertiesVariant.toMap(); - ScriptValue propertiesScriptValue = variantMapToScriptValue(propertiesMap, scriptEngine); - bool honorReadOnly = true; - copyFromScriptValue(propertiesScriptValue, honorReadOnly); -} - - -void EntityItemProperties::merge(const EntityItemProperties& other) { - // Core - COPY_PROPERTY_IF_CHANGED(simulationOwner); - COPY_PROPERTY_IF_CHANGED(parentID); - COPY_PROPERTY_IF_CHANGED(parentJointIndex); - COPY_PROPERTY_IF_CHANGED(visible); - COPY_PROPERTY_IF_CHANGED(name); - COPY_PROPERTY_IF_CHANGED(locked); - COPY_PROPERTY_IF_CHANGED(userData); - COPY_PROPERTY_IF_CHANGED(privateUserData); - COPY_PROPERTY_IF_CHANGED(href); - COPY_PROPERTY_IF_CHANGED(description); - COPY_PROPERTY_IF_CHANGED(position); - COPY_PROPERTY_IF_CHANGED(dimensions); - COPY_PROPERTY_IF_CHANGED(rotation); - COPY_PROPERTY_IF_CHANGED(registrationPoint); - COPY_PROPERTY_IF_CHANGED(created); - COPY_PROPERTY_IF_CHANGED(lastEditedBy); - COPY_PROPERTY_IF_CHANGED(entityHostType); - COPY_PROPERTY_IF_CHANGED(owningAvatarID); - COPY_PROPERTY_IF_CHANGED(queryAACube); - COPY_PROPERTY_IF_CHANGED(canCastShadow); - COPY_PROPERTY_IF_CHANGED(isVisibleInSecondaryCamera); - COPY_PROPERTY_IF_CHANGED(renderLayer); - COPY_PROPERTY_IF_CHANGED(primitiveMode); - COPY_PROPERTY_IF_CHANGED(ignorePickIntersection); - COPY_PROPERTY_IF_CHANGED(renderWithZones); - COPY_PROPERTY_IF_CHANGED(billboardMode); - COPY_PROPERTY_IF_CHANGED(tags); - _grab.merge(other._grab); - COPY_PROPERTY_IF_CHANGED(mirrorMode); - COPY_PROPERTY_IF_CHANGED(portalExitID); - - // Physics - COPY_PROPERTY_IF_CHANGED(density); - COPY_PROPERTY_IF_CHANGED(velocity); - COPY_PROPERTY_IF_CHANGED(angularVelocity); - COPY_PROPERTY_IF_CHANGED(gravity); - COPY_PROPERTY_IF_CHANGED(acceleration); - COPY_PROPERTY_IF_CHANGED(damping); - COPY_PROPERTY_IF_CHANGED(angularDamping); - COPY_PROPERTY_IF_CHANGED(restitution); - COPY_PROPERTY_IF_CHANGED(friction); - COPY_PROPERTY_IF_CHANGED(lifetime); - COPY_PROPERTY_IF_CHANGED(collisionless); - COPY_PROPERTY_IF_CHANGED(collisionMask); - COPY_PROPERTY_IF_CHANGED(dynamic); - COPY_PROPERTY_IF_CHANGED(collisionSoundURL); - COPY_PROPERTY_IF_CHANGED(actionData); - - // Cloning - COPY_PROPERTY_IF_CHANGED(cloneable); - COPY_PROPERTY_IF_CHANGED(cloneLifetime); - COPY_PROPERTY_IF_CHANGED(cloneLimit); - COPY_PROPERTY_IF_CHANGED(cloneDynamic); - COPY_PROPERTY_IF_CHANGED(cloneAvatarEntity); - COPY_PROPERTY_IF_CHANGED(cloneOriginID); - - // Scripts - COPY_PROPERTY_IF_CHANGED(script); - COPY_PROPERTY_IF_CHANGED(scriptTimestamp); - COPY_PROPERTY_IF_CHANGED(serverScripts); - - // Local props for scripts - COPY_PROPERTY_IF_CHANGED(localPosition); - COPY_PROPERTY_IF_CHANGED(localRotation); - COPY_PROPERTY_IF_CHANGED(localVelocity); - COPY_PROPERTY_IF_CHANGED(localAngularVelocity); - COPY_PROPERTY_IF_CHANGED(localDimensions); - - // Common - COPY_PROPERTY_IF_CHANGED(shapeType); - COPY_PROPERTY_IF_CHANGED(compoundShapeURL); - COPY_PROPERTY_IF_CHANGED(color); - COPY_PROPERTY_IF_CHANGED(alpha); - COPY_PROPERTY_IF_CHANGED(unlit); - _pulse.merge(other._pulse); - COPY_PROPERTY_IF_CHANGED(textures); - - // Particles - COPY_PROPERTY_IF_CHANGED(maxParticles); - COPY_PROPERTY_IF_CHANGED(lifespan); - COPY_PROPERTY_IF_CHANGED(isEmitting); - COPY_PROPERTY_IF_CHANGED(emitRate); - COPY_PROPERTY_IF_CHANGED(emitSpeed); - COPY_PROPERTY_IF_CHANGED(speedSpread); - COPY_PROPERTY_IF_CHANGED(emitOrientation); - COPY_PROPERTY_IF_CHANGED(emitDimensions); - COPY_PROPERTY_IF_CHANGED(emitRadiusStart); - COPY_PROPERTY_IF_CHANGED(polarStart); - COPY_PROPERTY_IF_CHANGED(polarFinish); - COPY_PROPERTY_IF_CHANGED(azimuthStart); - COPY_PROPERTY_IF_CHANGED(azimuthFinish); - COPY_PROPERTY_IF_CHANGED(emitAcceleration); - COPY_PROPERTY_IF_CHANGED(accelerationSpread); - COPY_PROPERTY_IF_CHANGED(particleRadius); - COPY_PROPERTY_IF_CHANGED(radiusSpread); - COPY_PROPERTY_IF_CHANGED(radiusStart); - COPY_PROPERTY_IF_CHANGED(radiusFinish); - COPY_PROPERTY_IF_CHANGED(colorSpread); - COPY_PROPERTY_IF_CHANGED(colorStart); - COPY_PROPERTY_IF_CHANGED(colorFinish); - COPY_PROPERTY_IF_CHANGED(alphaSpread); - COPY_PROPERTY_IF_CHANGED(alphaStart); - COPY_PROPERTY_IF_CHANGED(alphaFinish); - COPY_PROPERTY_IF_CHANGED(emitterShouldTrail); - COPY_PROPERTY_IF_CHANGED(particleSpin); - COPY_PROPERTY_IF_CHANGED(spinSpread); - COPY_PROPERTY_IF_CHANGED(spinStart); - COPY_PROPERTY_IF_CHANGED(spinFinish); - COPY_PROPERTY_IF_CHANGED(rotateWithEntity); - - // Procedural Particles - COPY_PROPERTY_IF_CHANGED(numParticles); - COPY_PROPERTY_IF_CHANGED(numTrianglesPerParticle); - COPY_PROPERTY_IF_CHANGED(numUpdateProps); - COPY_PROPERTY_IF_CHANGED(particleTransparent); - COPY_PROPERTY_IF_CHANGED(particleUpdateData); - COPY_PROPERTY_IF_CHANGED(particleRenderData); - - // Model - COPY_PROPERTY_IF_CHANGED(modelURL); - COPY_PROPERTY_IF_CHANGED(modelScale); - COPY_PROPERTY_IF_CHANGED(jointRotationsSet); - COPY_PROPERTY_IF_CHANGED(jointRotations); - COPY_PROPERTY_IF_CHANGED(jointTranslationsSet); - COPY_PROPERTY_IF_CHANGED(jointTranslations); - COPY_PROPERTY_IF_CHANGED(relayParentJoints); - COPY_PROPERTY_IF_CHANGED(groupCulled); - COPY_PROPERTY_IF_CHANGED(blendshapeCoefficients); - COPY_PROPERTY_IF_CHANGED(useOriginalPivot); - COPY_PROPERTY_IF_CHANGED(loadPriority); - _animation.merge(other._animation); - - // Light - COPY_PROPERTY_IF_CHANGED(isSpotlight); - COPY_PROPERTY_IF_CHANGED(intensity); - COPY_PROPERTY_IF_CHANGED(exponent); - COPY_PROPERTY_IF_CHANGED(cutoff); - COPY_PROPERTY_IF_CHANGED(falloffRadius); - - // Text - COPY_PROPERTY_IF_CHANGED(text); - COPY_PROPERTY_IF_CHANGED(lineHeight); - COPY_PROPERTY_IF_CHANGED(textColor); - COPY_PROPERTY_IF_CHANGED(textAlpha); - COPY_PROPERTY_IF_CHANGED(backgroundColor); - COPY_PROPERTY_IF_CHANGED(backgroundAlpha); - COPY_PROPERTY_IF_CHANGED(leftMargin); - COPY_PROPERTY_IF_CHANGED(rightMargin); - COPY_PROPERTY_IF_CHANGED(topMargin); - COPY_PROPERTY_IF_CHANGED(bottomMargin); - COPY_PROPERTY_IF_CHANGED(font); - COPY_PROPERTY_IF_CHANGED(textEffect); - COPY_PROPERTY_IF_CHANGED(textEffectColor); - COPY_PROPERTY_IF_CHANGED(textEffectThickness); - COPY_PROPERTY_IF_CHANGED(alignment); - - // Zone - _keyLight.merge(other._keyLight); - _ambientLight.merge(other._ambientLight); - _skybox.merge(other._skybox); - _haze.merge(other._haze); - _bloom.merge(other._bloom); - _audio.merge(other._audio); - _tonemapping.merge(other._tonemapping); - _ambientOcclusion.merge(other._ambientOcclusion); - COPY_PROPERTY_IF_CHANGED(flyingAllowed); - COPY_PROPERTY_IF_CHANGED(ghostingAllowed); - COPY_PROPERTY_IF_CHANGED(filterURL); - COPY_PROPERTY_IF_CHANGED(keyLightMode); - COPY_PROPERTY_IF_CHANGED(ambientLightMode); - COPY_PROPERTY_IF_CHANGED(skyboxMode); - COPY_PROPERTY_IF_CHANGED(hazeMode); - COPY_PROPERTY_IF_CHANGED(bloomMode); - COPY_PROPERTY_IF_CHANGED(avatarPriority); - COPY_PROPERTY_IF_CHANGED(screenshare); - COPY_PROPERTY_IF_CHANGED(tonemappingMode); - COPY_PROPERTY_IF_CHANGED(ambientOcclusionMode); - - // Polyvox - COPY_PROPERTY_IF_CHANGED(voxelVolumeSize); - COPY_PROPERTY_IF_CHANGED(voxelData); - COPY_PROPERTY_IF_CHANGED(voxelSurfaceStyle); - COPY_PROPERTY_IF_CHANGED(xTextureURL); - COPY_PROPERTY_IF_CHANGED(yTextureURL); - COPY_PROPERTY_IF_CHANGED(zTextureURL); - COPY_PROPERTY_IF_CHANGED(xNNeighborID); - COPY_PROPERTY_IF_CHANGED(yNNeighborID); - COPY_PROPERTY_IF_CHANGED(zNNeighborID); - COPY_PROPERTY_IF_CHANGED(xPNeighborID); - COPY_PROPERTY_IF_CHANGED(yPNeighborID); - COPY_PROPERTY_IF_CHANGED(zPNeighborID); - - // Web - COPY_PROPERTY_IF_CHANGED(sourceUrl); - COPY_PROPERTY_IF_CHANGED(dpi); - COPY_PROPERTY_IF_CHANGED(scriptURL); - COPY_PROPERTY_IF_CHANGED(maxFPS); - COPY_PROPERTY_IF_CHANGED(inputMode); - COPY_PROPERTY_IF_CHANGED(wantsKeyboardFocus); - COPY_PROPERTY_IF_CHANGED(showKeyboardFocusHighlight); - COPY_PROPERTY_IF_CHANGED(useBackground); - COPY_PROPERTY_IF_CHANGED(userAgent); - - // Polyline - COPY_PROPERTY_IF_CHANGED(linePoints); - COPY_PROPERTY_IF_CHANGED(strokeWidths); - COPY_PROPERTY_IF_CHANGED(normals); - COPY_PROPERTY_IF_CHANGED(strokeColors); - COPY_PROPERTY_IF_CHANGED(isUVModeStretch); - COPY_PROPERTY_IF_CHANGED(glow); - COPY_PROPERTY_IF_CHANGED(faceCamera); - - // Shape - COPY_PROPERTY_IF_CHANGED(shape); - - // Material - COPY_PROPERTY_IF_CHANGED(materialURL); - COPY_PROPERTY_IF_CHANGED(materialMappingMode); - COPY_PROPERTY_IF_CHANGED(priority); - COPY_PROPERTY_IF_CHANGED(parentMaterialName); - COPY_PROPERTY_IF_CHANGED(materialMappingPos); - COPY_PROPERTY_IF_CHANGED(materialMappingScale); - COPY_PROPERTY_IF_CHANGED(materialMappingRot); - COPY_PROPERTY_IF_CHANGED(materialData); - COPY_PROPERTY_IF_CHANGED(materialRepeat); - - // Image - COPY_PROPERTY_IF_CHANGED(imageURL); - COPY_PROPERTY_IF_CHANGED(emissive); - COPY_PROPERTY_IF_CHANGED(keepAspectRatio); - COPY_PROPERTY_IF_CHANGED(subImage); - - // Grid - COPY_PROPERTY_IF_CHANGED(followCamera); - COPY_PROPERTY_IF_CHANGED(majorGridEvery); - COPY_PROPERTY_IF_CHANGED(minorGridEvery); - - // Gizmo - COPY_PROPERTY_IF_CHANGED(gizmoType); - _ring.merge(other._ring); - - // Sound - COPY_PROPERTY_IF_CHANGED(soundURL); - COPY_PROPERTY_IF_CHANGED(volume); - COPY_PROPERTY_IF_CHANGED(timeOffset); - COPY_PROPERTY_IF_CHANGED(pitch); - COPY_PROPERTY_IF_CHANGED(playing); - COPY_PROPERTY_IF_CHANGED(loop); - COPY_PROPERTY_IF_CHANGED(positional); - COPY_PROPERTY_IF_CHANGED(localOnly); - - _lastEdited = usecTimestampNow(); -} - -ScriptValue EntityItemPropertiesToScriptValue(ScriptEngine* engine, const EntityItemProperties& properties) { - return properties.copyToScriptValue(engine, false); -} - -ScriptValue EntityItemNonDefaultPropertiesToScriptValue(ScriptEngine* engine, const EntityItemProperties& properties) { - return properties.copyToScriptValue(engine, true); -} - -bool EntityItemPropertiesFromScriptValueIgnoreReadOnly(const ScriptValue &object, EntityItemProperties& properties) { - properties.copyFromScriptValue(object, false); - return true; -} - -bool EntityItemPropertiesFromScriptValueHonorReadOnly(const ScriptValue &object, EntityItemProperties& properties) { - properties.copyFromScriptValue(object, true); - return true; -} - -ScriptValue EntityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags) { - return EntityItemProperties::entityPropertyFlagsToScriptValue(engine, flags); -} - -bool EntityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags) { - return EntityItemProperties::entityPropertyFlagsFromScriptValue(object, flags); -} - - -ScriptValue EntityItemProperties::entityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags) { - ScriptValue result = engine->newObject(); - return result; -} - -bool EntityItemProperties::entityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags) { - if (object.isString()) { - EntityPropertyInfo propertyInfo; - if (getPropertyInfo(object.toString(), propertyInfo)) { - flags << propertyInfo.propertyEnums; - } - } - else if (object.isArray()) { - quint32 length = object.property("length").toInt32(); - for (quint32 i = 0; i < length; i++) { - QString propertyName = object.property(i).toString(); - EntityPropertyInfo propertyInfo; - if (getPropertyInfo(propertyName, propertyInfo)) { - flags << propertyInfo.propertyEnums; - } - } - } - return true; -} - -static QHash _propertyInfos; -static QHash _enumsToPropertyStrings; - -bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPropertyInfo& propertyInfo) { - - static std::once_flag initMap; - // V8TODO: Probably needs mutex before call_once - std::call_once(initMap, []() { - // Core - ADD_PROPERTY_TO_MAP(PROP_SIMULATION_OWNER, SimulationOwner, simulationOwner, SimulationOwner); - ADD_PROPERTY_TO_MAP(PROP_PARENT_ID, ParentID, parentID, QUuid); - ADD_PROPERTY_TO_MAP(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, uint16_t); - ADD_PROPERTY_TO_MAP(PROP_VISIBLE, Visible, visible, bool); - ADD_PROPERTY_TO_MAP(PROP_NAME, Name, name, QString); - ADD_PROPERTY_TO_MAP(PROP_LOCKED, Locked, locked, bool); - ADD_PROPERTY_TO_MAP(PROP_USER_DATA, UserData, userData, QString); - ADD_PROPERTY_TO_MAP(PROP_PRIVATE_USER_DATA, PrivateUserData, privateUserData, QString); - ADD_PROPERTY_TO_MAP(PROP_HREF, Href, href, QString); - ADD_PROPERTY_TO_MAP(PROP_DESCRIPTION, Description, description, QString); - ADD_PROPERTY_TO_MAP(PROP_POSITION, Position, position, vec3); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DIMENSIONS, Dimensions, dimensions, vec3, ENTITY_ITEM_MIN_DIMENSION, FLT_MAX); - ADD_PROPERTY_TO_MAP(PROP_ROTATION, Rotation, rotation, quat); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, vec3, - ENTITY_ITEM_MIN_REGISTRATION_POINT, ENTITY_ITEM_MAX_REGISTRATION_POINT); - ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); - ADD_PROPERTY_TO_MAP(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid); - ADD_PROPERTY_TO_MAP(PROP_ENTITY_HOST_TYPE, EntityHostType, entityHostType, entity::HostType); - ADD_PROPERTY_TO_MAP(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid); - ADD_PROPERTY_TO_MAP(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube); - ADD_PROPERTY_TO_MAP(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool); - ADD_PROPERTY_TO_MAP(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool); - ADD_PROPERTY_TO_MAP(PROP_RENDER_LAYER, RenderLayer, renderLayer, RenderLayer); - ADD_PROPERTY_TO_MAP(PROP_PRIMITIVE_MODE, PrimitiveMode, primitiveMode, PrimitiveMode); - ADD_PROPERTY_TO_MAP(PROP_IGNORE_PICK_INTERSECTION, IgnorePickIntersection, ignorePickIntersection, bool); - ADD_PROPERTY_TO_MAP(PROP_RENDER_WITH_ZONES, RenderWithZones, renderWithZones, QVector); - ADD_PROPERTY_TO_MAP(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode); - ADD_PROPERTY_TO_MAP(PROP_TAGS, Tags, tags, QSet); - { // Grab - ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_GRABBABLE, Grab, grab, Grabbable, grabbable); - ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_KINEMATIC, Grab, grab, GrabKinematic, grabKinematic); - ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_FOLLOWS_CONTROLLER, Grab, grab, GrabFollowsController, grabFollowsController); - ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_TRIGGERABLE, Grab, grab, Triggerable, triggerable); - ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE, Grab, grab, Equippable, equippable); - ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_DELEGATE_TO_PARENT, Grab, grab, GrabDelegateToParent, grabDelegateToParent); - ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, Grab, grab, - EquippableLeftPosition, equippableLeftPosition); - ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, - EquippableLeftRotation, equippableLeftRotation); - ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, Grab, grab, - EquippableRightPosition, equippableRightPosition); - ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, - EquippableRightRotation, equippableRightRotation); - ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, Grab, grab, - EquippableIndicatorURL, equippableIndicatorURL); - ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, Grab, grab, - EquippableIndicatorScale, equippableIndicatorScale); - 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, - ENTITY_ITEM_MIN_DENSITY, ENTITY_ITEM_MAX_DENSITY); - ADD_PROPERTY_TO_MAP(PROP_VELOCITY, Velocity, velocity, vec3); - ADD_PROPERTY_TO_MAP(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, vec3); - ADD_PROPERTY_TO_MAP(PROP_GRAVITY, Gravity, gravity, vec3); - ADD_PROPERTY_TO_MAP(PROP_ACCELERATION, Acceleration, acceleration, vec3); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DAMPING, Damping, damping, float, - ENTITY_ITEM_MIN_DAMPING, ENTITY_ITEM_MAX_DAMPING); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float, - ENTITY_ITEM_MIN_DAMPING, ENTITY_ITEM_MAX_DAMPING); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RESTITUTION, Restitution, restitution, float, - ENTITY_ITEM_MIN_RESTITUTION, ENTITY_ITEM_MAX_RESTITUTION); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_FRICTION, Friction, friction, float, - ENTITY_ITEM_MIN_FRICTION, ENTITY_ITEM_MAX_FRICTION); - ADD_PROPERTY_TO_MAP(PROP_LIFETIME, Lifetime, lifetime, float); - ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, Collisionless, collisionless, bool); - ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, unused, ignoreForCollisions, bool); // legacy support - ADD_PROPERTY_TO_MAP(PROP_COLLISION_MASK, unused, collisionMask, uint16_t); - ADD_PROPERTY_TO_MAP(PROP_COLLISION_MASK, unused, collidesWith, uint16_t); - ADD_PROPERTY_TO_MAP(PROP_DYNAMIC, unused, collisionsWillMove, bool); // legacy support - ADD_PROPERTY_TO_MAP(PROP_DYNAMIC, unused, dynamic, bool); - ADD_PROPERTY_TO_MAP(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString); - ADD_PROPERTY_TO_MAP(PROP_ACTION_DATA, ActionData, actionData, QByteArray); - - // Cloning - ADD_PROPERTY_TO_MAP(PROP_CLONEABLE, Cloneable, cloneable, bool); - ADD_PROPERTY_TO_MAP(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float); - ADD_PROPERTY_TO_MAP(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float); - ADD_PROPERTY_TO_MAP(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool); - ADD_PROPERTY_TO_MAP(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool); - ADD_PROPERTY_TO_MAP(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid); - - // Scripts - ADD_PROPERTY_TO_MAP(PROP_SCRIPT, Script, script, QString); - ADD_PROPERTY_TO_MAP(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64); - ADD_PROPERTY_TO_MAP(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString); - - // Local script props - ADD_PROPERTY_TO_MAP(PROP_LOCAL_POSITION, LocalPosition, localPosition, vec3); - ADD_PROPERTY_TO_MAP(PROP_LOCAL_ROTATION, LocalRotation, localRotation, quat); - ADD_PROPERTY_TO_MAP(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, vec3); - ADD_PROPERTY_TO_MAP(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, vec3); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, vec3, - ENTITY_ITEM_MIN_DIMENSION, FLT_MAX); - - // Common - ADD_PROPERTY_TO_MAP(PROP_SHAPE_TYPE, ShapeType, shapeType, ShapeType); - ADD_PROPERTY_TO_MAP(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString); - ADD_PROPERTY_TO_MAP(PROP_COLOR, Color, color, u8vec3Color); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA, Alpha, alpha, float, particle::MINIMUM_ALPHA, particle::MAXIMUM_ALPHA); - ADD_PROPERTY_TO_MAP(PROP_UNLIT, Unlit, unlit, bool); - { // Pulse - ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_MIN, Pulse, pulse, Min, min); - ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_MAX, Pulse, pulse, Max, max); - ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_PERIOD, Pulse, pulse, Period, period); - ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_COLOR_MODE, Pulse, pulse, ColorMode, colorMode); - ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_ALPHA_MODE, Pulse, pulse, AlphaMode, alphaMode); - } - ADD_PROPERTY_TO_MAP(PROP_TEXTURES, Textures, textures, QString); - - // Particles - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32, - particle::MINIMUM_MAX_PARTICLES, particle::MAXIMUM_MAX_PARTICLES); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_LIFESPAN, Lifespan, lifespan, float, - particle::MINIMUM_LIFESPAN, particle::MAXIMUM_LIFESPAN); - ADD_PROPERTY_TO_MAP(PROP_EMITTING_PARTICLES, IsEmitting, isEmitting, bool); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_RATE, EmitRate, emitRate, float, - particle::MINIMUM_EMIT_RATE, particle::MAXIMUM_EMIT_RATE); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_SPEED, EmitSpeed, emitSpeed, vec3, - particle::MINIMUM_EMIT_SPEED, particle::MAXIMUM_EMIT_SPEED); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPEED_SPREAD, SpeedSpread, speedSpread, vec3, - particle::MINIMUM_EMIT_SPEED, particle::MAXIMUM_EMIT_SPEED); - ADD_PROPERTY_TO_MAP(PROP_EMIT_ORIENTATION, EmitOrientation, emitOrientation, quat); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, vec3, - particle::MINIMUM_EMIT_DIMENSION, particle::MAXIMUM_EMIT_DIMENSION); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_RADIUS_START, EmitRadiusStart, emitRadiusStart, float, - particle::MINIMUM_EMIT_RADIUS_START, particle::MAXIMUM_EMIT_RADIUS_START); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_POLAR_START, EmitPolarStart, polarStart, float, - particle::MINIMUM_POLAR, particle::MAXIMUM_POLAR); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_POLAR_FINISH, EmitPolarFinish, polarFinish, float, - particle::MINIMUM_POLAR, particle::MAXIMUM_POLAR); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_AZIMUTH_START, EmitAzimuthStart, azimuthStart, float, - particle::MINIMUM_AZIMUTH, particle::MAXIMUM_AZIMUTH); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_AZIMUTH_FINISH, EmitAzimuthFinish, azimuthFinish, float, - particle::MINIMUM_AZIMUTH, particle::MAXIMUM_AZIMUTH); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, vec3, - particle::MINIMUM_EMIT_ACCELERATION, particle::MAXIMUM_EMIT_ACCELERATION); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, vec3, - particle::MINIMUM_ACCELERATION_SPREAD, particle::MAXIMUM_ACCELERATION_SPREAD); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float, - particle::MINIMUM_PARTICLE_RADIUS, particle::MAXIMUM_PARTICLE_RADIUS); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float, - particle::MINIMUM_PARTICLE_RADIUS, particle::MAXIMUM_PARTICLE_RADIUS); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_START, RadiusStart, radiusStart, float, - particle::MINIMUM_PARTICLE_RADIUS, particle::MAXIMUM_PARTICLE_RADIUS); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, - particle::MINIMUM_PARTICLE_RADIUS, particle::MAXIMUM_PARTICLE_RADIUS); - ADD_PROPERTY_TO_MAP(PROP_COLOR_SPREAD, ColorSpread, colorSpread, u8vec3Color); - ADD_PROPERTY_TO_MAP(PROP_COLOR_START, ColorStart, colorStart, vec3Color); - ADD_PROPERTY_TO_MAP(PROP_COLOR_FINISH, ColorFinish, colorFinish, vec3Color); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float, - particle::MINIMUM_ALPHA, particle::MAXIMUM_ALPHA); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_START, AlphaStart, alphaStart, float, - particle::MINIMUM_ALPHA, particle::MAXIMUM_ALPHA); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float, - particle::MINIMUM_ALPHA, particle::MAXIMUM_ALPHA); - ADD_PROPERTY_TO_MAP(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PARTICLE_SPIN, ParticleSpin, particleSpin, float, - particle::MINIMUM_PARTICLE_SPIN, particle::MAXIMUM_PARTICLE_SPIN); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_SPREAD, SpinSpread, spinSpread, float, - particle::MINIMUM_PARTICLE_SPIN, particle::MAXIMUM_PARTICLE_SPIN); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_START, SpinStart, spinStart, float, - particle::MINIMUM_PARTICLE_SPIN, particle::MAXIMUM_PARTICLE_SPIN); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_FINISH, SpinFinish, spinFinish, float, - particle::MINIMUM_PARTICLE_SPIN, particle::MAXIMUM_PARTICLE_SPIN); - ADD_PROPERTY_TO_MAP(PROP_PARTICLE_ROTATE_WITH_ENTITY, RotateWithEntity, rotateWithEntity, float); - - // Procedural Particles - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, NumParticles, numParticles, uint32_t, - particle::MINIMUM_MAX_PARTICLES, particle::MAXIMUM_NUM_PROCEDURAL_PARTICLES); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, NumTrianglesPerParticle, numTrianglesPerParticle, uint8_t, - particle::MINIMUM_TRIS_PER, particle::MAXIMUM_TRIS_PER); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, NumUpdateProps, numUpdateProps, uint8_t, - particle::MINIMUM_NUM_UPDATE_PROPS, particle::MAXIMUM_NUM_UPDATE_PROPS); - ADD_PROPERTY_TO_MAP(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, ParticleTransparent, particleTransparent, bool); - ADD_PROPERTY_TO_MAP(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, ParticleUpdateData, particleUpdateData, QString); - ADD_PROPERTY_TO_MAP(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, ParticleRenderData, particleRenderData, QString); - - // Model - ADD_PROPERTY_TO_MAP(PROP_MODEL_URL, ModelURL, modelURL, QString); - ADD_PROPERTY_TO_MAP(PROP_MODEL_SCALE, ModelScale, modelScale, vec3); - ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS_SET, JointRotationsSet, jointRotationsSet, QVector); - ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector); - ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector); - ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector); - ADD_PROPERTY_TO_MAP(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool); - ADD_PROPERTY_TO_MAP(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool); - ADD_PROPERTY_TO_MAP(PROP_BLENDSHAPE_COEFFICIENTS, BlendshapeCoefficients, blendshapeCoefficients, QString); - ADD_PROPERTY_TO_MAP(PROP_USE_ORIGINAL_PIVOT, UseOriginalPivot, useOriginalPivot, bool); - ADD_PROPERTY_TO_MAP(PROP_LOAD_PRIORITY, LoadPriority, loadPriority, float); - { // Animation - ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_URL, Animation, animation, URL, url); - ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation); - ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FPS, Animation, animation, FPS, fps); - ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame); - ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_PLAYING, Animation, animation, Running, running); - ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_LOOP, Animation, animation, Loop, loop); - ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame); - ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); - ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); - ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_SMOOTH_FRAMES, Animation, animation, SmoothFrames, smoothFrames); - } - - // Light - ADD_PROPERTY_TO_MAP(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool); - ADD_PROPERTY_TO_MAP(PROP_INTENSITY, Intensity, intensity, float); - ADD_PROPERTY_TO_MAP(PROP_EXPONENT, Exponent, exponent, float); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_CUTOFF, Cutoff, cutoff, float, - LightEntityItem::MIN_CUTOFF, LightEntityItem::MAX_CUTOFF); - ADD_PROPERTY_TO_MAP(PROP_FALLOFF_RADIUS, FalloffRadius, falloffRadius, float); - - // Text - ADD_PROPERTY_TO_MAP(PROP_TEXT, Text, text, QString); - ADD_PROPERTY_TO_MAP(PROP_LINE_HEIGHT, LineHeight, lineHeight, float); - ADD_PROPERTY_TO_MAP(PROP_TEXT_COLOR, TextColor, textColor, u8vec3Color); - ADD_PROPERTY_TO_MAP(PROP_TEXT_ALPHA, TextAlpha, textAlpha, float); - ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, u8vec3Color); - ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_ALPHA, BackgroundAlpha, backgroundAlpha, float); - ADD_PROPERTY_TO_MAP(PROP_LEFT_MARGIN, LeftMargin, leftMargin, float); - ADD_PROPERTY_TO_MAP(PROP_RIGHT_MARGIN, RightMargin, rightMargin, float); - ADD_PROPERTY_TO_MAP(PROP_TOP_MARGIN, TopMargin, topMargin, float); - ADD_PROPERTY_TO_MAP(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float); - ADD_PROPERTY_TO_MAP(PROP_FONT, Font, font, QString); - ADD_PROPERTY_TO_MAP(PROP_TEXT_EFFECT, TextEffect, textEffect, TextEffect); - ADD_PROPERTY_TO_MAP(PROP_TEXT_EFFECT_COLOR, TextEffectColor, textEffectColor, u8vec3Color); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_TEXT_EFFECT_THICKNESS, TextEffectThickness, textEffectThickness, float, 0.0, 0.5); - ADD_PROPERTY_TO_MAP(PROP_TEXT_ALIGNMENT, Alignment, alignment, TextAlignment); - - // Zone - { // Keylight - ADD_GROUP_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLight, keyLight, Color, color); - ADD_GROUP_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLight, keyLight, Intensity, intensity); - ADD_GROUP_PROPERTY_TO_MAP(PROP_KEYLIGHT_DIRECTION, KeyLight, keyLight, Direction, direction); - ADD_GROUP_PROPERTY_TO_MAP(PROP_KEYLIGHT_CAST_SHADOW, KeyLight, keyLight, CastShadows, castShadows); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_KEYLIGHT_SHADOW_BIAS, KeyLight, keyLight, ShadowBias, shadowBias, 0.0f, 1.0f); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_KEYLIGHT_SHADOW_MAX_DISTANCE, KeyLight, keyLight, ShadowMaxDistance, shadowMaxDistance, 1.0f, 250.0f); - } - { // Ambient light - ADD_GROUP_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_INTENSITY, AmbientLight, ambientLight, AmbientIntensity, ambientIntensity); - ADD_GROUP_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_URL, AmbientLight, ambientLight, AmbientURL, ambientURL); - ADD_GROUP_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_COLOR, AmbientLight, ambientLight, AmbientColor, ambientColor); - } - { // Skybox - ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color); - ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_URL, Skybox, skybox, URL, url); - } - { // Haze - ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_RANGE, Haze, haze, HazeRange, hazeRange); - ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_COLOR, Haze, haze, HazeColor, hazeColor); - ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_GLARE_COLOR, Haze, haze, HazeGlareColor, hazeGlareColor); - ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_ENABLE_GLARE, Haze, haze, HazeEnableGlare, hazeEnableGlare); - ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_GLARE_ANGLE, Haze, haze, HazeGlareAngle, hazeGlareAngle); - - ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_ALTITUDE_EFFECT, Haze, haze, HazeAltitudeEffect, hazeAltitudeEfect); - ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_CEILING, Haze, haze, HazeCeiling, hazeCeiling); - ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_BASE_REF, Haze, haze, HazeBaseRef, hazeBaseRef); - - ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_BACKGROUND_BLEND, Haze, haze, HazeBackgroundBlend, hazeBackgroundBlend); - - ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_ATTENUATE_KEYLIGHT, Haze, haze, HazeAttenuateKeyLight, hazeAttenuateKeyLight); - ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_KEYLIGHT_RANGE, Haze, haze, HazeKeyLightRange, hazeKeyLightRange); - ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_KEYLIGHT_ALTITUDE, Haze, haze, HazeKeyLightAltitude, hazeKeyLightAltitude); - } - { // Bloom - ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_INTENSITY, Bloom, bloom, BloomIntensity, bloomIntensity); - ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_THRESHOLD, Bloom, bloom, BloomThreshold, bloomThreshold); - ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_SIZE, Bloom, bloom, BloomSize, bloomSize); - } - { // Audio - ADD_GROUP_PROPERTY_TO_MAP(PROP_REVERB_ENABLED, Audio, audio, ReverbEnabled, reverbEnabled); - ADD_GROUP_PROPERTY_TO_MAP(PROP_REVERB_TIME, Audio, audio, ReverbTime, reverbTime); - ADD_GROUP_PROPERTY_TO_MAP(PROP_REVERB_WET_LEVEL, Audio, audio, ReverbWetLevel, reverbWetLevel); - ADD_GROUP_PROPERTY_TO_MAP(PROP_LISTENER_ZONES, Audio, audio, ListenerZones, listenerZones); - ADD_GROUP_PROPERTY_TO_MAP(PROP_LISTENER_ATTENUATION_COEFFICIENTS, Audio, audio, ListenerAttenuationCoefficients, listenerAttenuationCoefficients); - } - { // Tonemapping - ADD_GROUP_PROPERTY_TO_MAP(PROP_TONEMAPPING_CURVE, Tonemapping, tonemapping, Curve, curve); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_TONEMAPPING_EXPOSURE, Tonemapping, tonemapping, Exposure, exposure, -4.0f, -4.0f); - } - { // Ambient Occlusion - ADD_GROUP_PROPERTY_TO_MAP(PROP_AMBIENT_OCCLUSION_TECHNIQUE, AmbientOcclusion, ambientOcclusion, Technique, technique); - ADD_GROUP_PROPERTY_TO_MAP(PROP_AMBIENT_OCCLUSION_JITTER, AmbientOcclusion, ambientOcclusion, Jitter, jitter); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, AmbientOcclusion, ambientOcclusion, ResolutionLevel, resolutionLevel, 0, 4); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, AmbientOcclusion, ambientOcclusion, EdgeSharpness, edgeSharpness, 0.0f, 1.0f); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, AmbientOcclusion, ambientOcclusion, BlurRadius, blurRadius, 0, 15); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AmbientOcclusion, ambientOcclusion, AORadius, aoRadius, 0.01f, 2.0f); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AmbientOcclusion, ambientOcclusion, AOObscuranceLevel, aoObscuranceLevel, 0.01f, 1.0f); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AmbientOcclusion, ambientOcclusion, AOFalloffAngle, aoFalloffAngle, 0.0f, 1.0f); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, AmbientOcclusion, ambientOcclusion, AOSamplingAmount, aoSamplingAmount, 0.0f, 1.0f); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, AmbientOcclusion, ambientOcclusion, SSAONumSpiralTurns, ssaoNumSpiralTurns, 0.0f, 10.0f); - } - ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool); - ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool); - ADD_PROPERTY_TO_MAP(PROP_FILTER_URL, FilterURL, filterURL, QString); - ADD_PROPERTY_TO_MAP(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t); - ADD_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t); - ADD_PROPERTY_TO_MAP(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t); - ADD_PROPERTY_TO_MAP(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t); - ADD_PROPERTY_TO_MAP(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t); - ADD_PROPERTY_TO_MAP(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, uint32_t); - ADD_PROPERTY_TO_MAP(PROP_SCREENSHARE, Screenshare, screenshare, uint32_t); - ADD_PROPERTY_TO_MAP(PROP_TONEMAPPING_MODE, TonemappingMode, tonemappingMode, uint32_t); - ADD_PROPERTY_TO_MAP(PROP_AMBIENT_OCCLUSION_MODE, AmbientOcclusionMode, ambientOcclusionMode, uint32_t); - - // Polyvox - ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, vec3); - ADD_PROPERTY_TO_MAP(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray); - ADD_PROPERTY_TO_MAP(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t); - ADD_PROPERTY_TO_MAP(PROP_X_TEXTURE_URL, XTextureURL, xTextureURL, QString); - ADD_PROPERTY_TO_MAP(PROP_Y_TEXTURE_URL, YTextureURL, yTextureURL, QString); - ADD_PROPERTY_TO_MAP(PROP_Z_TEXTURE_URL, ZTextureURL, zTextureURL, QString); - ADD_PROPERTY_TO_MAP(PROP_X_N_NEIGHBOR_ID, XNNeighborID, xNNeighborID, EntityItemID); - ADD_PROPERTY_TO_MAP(PROP_Y_N_NEIGHBOR_ID, YNNeighborID, yNNeighborID, EntityItemID); - ADD_PROPERTY_TO_MAP(PROP_Z_N_NEIGHBOR_ID, ZNNeighborID, zNNeighborID, EntityItemID); - ADD_PROPERTY_TO_MAP(PROP_X_P_NEIGHBOR_ID, XPNeighborID, xPNeighborID, EntityItemID); - ADD_PROPERTY_TO_MAP(PROP_Y_P_NEIGHBOR_ID, YPNeighborID, yPNeighborID, EntityItemID); - ADD_PROPERTY_TO_MAP(PROP_Z_P_NEIGHBOR_ID, ZPNeighborID, zPNeighborID, EntityItemID); - - // Web - ADD_PROPERTY_TO_MAP(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString); - ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t); - ADD_PROPERTY_TO_MAP(PROP_SCRIPT_URL, ScriptURL, scriptURL, QString); - ADD_PROPERTY_TO_MAP(PROP_MAX_FPS, MaxFPS, maxFPS, uint8_t); - ADD_PROPERTY_TO_MAP(PROP_INPUT_MODE, InputMode, inputMode, WebInputMode); - ADD_PROPERTY_TO_MAP(PROP_WANTS_KEYBOARD_FOCUS, WantsKeyboardFocus, wantsKeyboardFocus, bool); - ADD_PROPERTY_TO_MAP(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, ShowKeyboardFocusHighlight, showKeyboardFocusHighlight, bool); - ADD_PROPERTY_TO_MAP(PROP_WEB_USE_BACKGROUND, useBackground, useBackground, bool); - ADD_PROPERTY_TO_MAP(PROP_USER_AGENT, UserAgent, userAgent, QString); - - // Polyline - ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector); - ADD_PROPERTY_TO_MAP(PROP_STROKE_WIDTHS, StrokeWidths, strokeWidths, QVector); - ADD_PROPERTY_TO_MAP(PROP_STROKE_NORMALS, Normals, normals, QVector); - ADD_PROPERTY_TO_MAP(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector); - ADD_PROPERTY_TO_MAP(PROP_IS_UV_MODE_STRETCH, IsUVModeStretch, isUVModeStretch, QVector); - ADD_PROPERTY_TO_MAP(PROP_LINE_GLOW, Glow, glow, bool); - ADD_PROPERTY_TO_MAP(PROP_LINE_FACE_CAMERA, FaceCamera, faceCamera, bool); - - // Shape - ADD_PROPERTY_TO_MAP(PROP_SHAPE, Shape, shape, QString); - - // Material - ADD_PROPERTY_TO_MAP(PROP_MATERIAL_URL, MaterialURL, materialURL, QString); - ADD_PROPERTY_TO_MAP(PROP_MATERIAL_MAPPING_MODE, MaterialMappingMode, materialMappingMode, MaterialMappingMode); - ADD_PROPERTY_TO_MAP(PROP_MATERIAL_PRIORITY, Priority, priority, quint16); - ADD_PROPERTY_TO_MAP(PROP_PARENT_MATERIAL_NAME, ParentMaterialName, parentMaterialName, QString); - ADD_PROPERTY_TO_MAP(PROP_MATERIAL_MAPPING_POS, MaterialMappingPos, materialMappingPos, vec2); - ADD_PROPERTY_TO_MAP(PROP_MATERIAL_MAPPING_SCALE, MaterialMappingScale, materialMappingScale, vec2); - ADD_PROPERTY_TO_MAP(PROP_MATERIAL_MAPPING_ROT, MaterialMappingRot, materialMappingRot, float); - ADD_PROPERTY_TO_MAP(PROP_MATERIAL_DATA, MaterialData, materialData, QString); - ADD_PROPERTY_TO_MAP(PROP_MATERIAL_REPEAT, MaterialRepeat, materialRepeat, bool); - - // Image - ADD_PROPERTY_TO_MAP(PROP_IMAGE_URL, ImageURL, imageURL, QString); - ADD_PROPERTY_TO_MAP(PROP_EMISSIVE, Emissive, emissive, bool); - ADD_PROPERTY_TO_MAP(PROP_KEEP_ASPECT_RATIO, KeepAspectRatio, keepAspectRatio, bool); - ADD_PROPERTY_TO_MAP(PROP_SUB_IMAGE, SubImage, subImage, QRect); - - // Grid - ADD_PROPERTY_TO_MAP(PROP_GRID_FOLLOW_CAMERA, FollowCamera, followCamera, bool); - ADD_PROPERTY_TO_MAP(PROP_MAJOR_GRID_EVERY, MajorGridEvery, majorGridEvery, uint32_t); - ADD_PROPERTY_TO_MAP(PROP_MINOR_GRID_EVERY, MinorGridEvery, minorGridEvery, float); - - // Gizmo - ADD_PROPERTY_TO_MAP(PROP_GIZMO_TYPE, GizmoType, gizmoType, GizmoType); - { // RingGizmo - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_START_ANGLE, Ring, ring, StartAngle, startAngle, RingGizmoPropertyGroup::MIN_ANGLE, RingGizmoPropertyGroup::MAX_ANGLE); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_END_ANGLE, Ring, ring, EndAngle, endAngle, RingGizmoPropertyGroup::MIN_ANGLE, RingGizmoPropertyGroup::MAX_ANGLE); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_INNER_RADIUS, Ring, ring, InnerRadius, innerRadius, RingGizmoPropertyGroup::MIN_RADIUS, RingGizmoPropertyGroup::MAX_RADIUS); - - ADD_GROUP_PROPERTY_TO_MAP(PROP_INNER_START_COLOR, Ring, ring, InnerStartColor, innerStartColor); - ADD_GROUP_PROPERTY_TO_MAP(PROP_INNER_END_COLOR, Ring, ring, InnerEndColor, innerEndColor); - ADD_GROUP_PROPERTY_TO_MAP(PROP_OUTER_START_COLOR, Ring, ring, OuterStartColor, outerStartColor); - ADD_GROUP_PROPERTY_TO_MAP(PROP_OUTER_END_COLOR, Ring, ring, OuterEndColor, outerEndColor); - - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_INNER_START_ALPHA, Ring, ring, InnerStartAlpha, innerStartAlpha, RingGizmoPropertyGroup::MIN_ALPHA, RingGizmoPropertyGroup::MAX_ALPHA); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_INNER_END_ALPHA, Ring, ring, InnerEndAlpha, innerEndAlpha, RingGizmoPropertyGroup::MIN_ALPHA, RingGizmoPropertyGroup::MAX_ALPHA); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_OUTER_START_ALPHA, Ring, ring, OuterStartAlpha, outerStartAlpha, RingGizmoPropertyGroup::MIN_ALPHA, RingGizmoPropertyGroup::MAX_ALPHA); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_OUTER_END_ALPHA, Ring, ring, OuterEndAlpha, outerEndAlpha, RingGizmoPropertyGroup::MIN_ALPHA, RingGizmoPropertyGroup::MAX_ALPHA); - - ADD_GROUP_PROPERTY_TO_MAP(PROP_HAS_TICK_MARKS, Ring, ring, HasTickMarks, hasTickMarks); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_MAJOR_TICK_MARKS_ANGLE, Ring, ring, MajorTickMarksAngle, majorTickMarksAngle, RingGizmoPropertyGroup::MIN_ANGLE, RingGizmoPropertyGroup::MAX_ANGLE); - ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_MINOR_TICK_MARKS_ANGLE, Ring, ring, MinorTickMarksAngle, minorTickMarksAngle, RingGizmoPropertyGroup::MIN_ANGLE, RingGizmoPropertyGroup::MAX_ANGLE); - ADD_GROUP_PROPERTY_TO_MAP(PROP_MAJOR_TICK_MARKS_LENGTH, Ring, ring, MajorTickMarksLength, majorTickMarksLength); - ADD_GROUP_PROPERTY_TO_MAP(PROP_MINOR_TICK_MARKS_LENGTH, Ring, ring, MinorTickMarksLength, minorTickMarksLength); - ADD_GROUP_PROPERTY_TO_MAP(PROP_MAJOR_TICK_MARKS_COLOR, Ring, ring, MajorTickMarksColor, majorTickMarksColor); - ADD_GROUP_PROPERTY_TO_MAP(PROP_MINOR_TICK_MARKS_COLOR, Ring, ring, MinorTickMarksColor, minorTickMarksColor); - } - - // Sound - ADD_PROPERTY_TO_MAP(PROP_SOUND_URL, SoundURL, soundURL, QString); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SOUND_VOLUME, Volume, volume, float, 0.0f, 1.0f); - ADD_PROPERTY_TO_MAP(PROP_SOUND_TIME_OFFSET, TimeOffset, timeOffset, float); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SOUND_PITCH, Pitch, pitch, float, 1.0f / 16.0f, 16.0f); - ADD_PROPERTY_TO_MAP(PROP_SOUND_PLAYING, Playing, playing, bool); - ADD_PROPERTY_TO_MAP(PROP_SOUND_LOOP, Loop, loop, bool); - ADD_PROPERTY_TO_MAP(PROP_SOUND_POSITIONAL, Positional, positional, bool); - ADD_PROPERTY_TO_MAP(PROP_SOUND_LOCAL_ONLY, LocalOnly, localOnly, bool); - }); - - auto iter = _propertyInfos.find(propertyName); - if (iter != _propertyInfos.end()) { - propertyInfo = *iter; - return true; - } - - return false; -} - -/*@jsdoc - * Information about an entity property. - * @typedef {object} Entities.EntityPropertyInfo - * @property {number} propertyEnum - The internal number of the property. - * @property {string} minimum - The minimum numerical value the property may have, if available, otherwise "". - * @property {string} maximum - The maximum numerical value the property may have, if available, otherwise "". - */ -ScriptValue EntityPropertyInfoToScriptValue(ScriptEngine* engine, const EntityPropertyInfo& propertyInfo) { - ScriptValue obj = engine->newObject(); - obj.setProperty("propertyEnum", propertyInfo.propertyEnums.firstFlag()); - obj.setProperty("minimum", propertyInfo.minimum.toString()); - obj.setProperty("maximum", propertyInfo.maximum.toString()); - return obj; -} - -bool EntityPropertyInfoFromScriptValue(const ScriptValue& object, EntityPropertyInfo& propertyInfo) { - propertyInfo.propertyEnums = (EntityPropertyList)object.property("propertyEnum").toVariant().toUInt(); - propertyInfo.minimum = object.property("minimum").toVariant(); - propertyInfo.maximum = object.property("maximum").toVariant(); - return true; -} - -// TODO: Implement support for edit packets that can span an MTU sized buffer. We need to implement a mechanism for the -// encodeEntityEditPacket() method to communicate the the caller which properties couldn't fit in the buffer. Similar -// to how we handle this in the Octree streaming case. -// -// TODO: Right now, all possible properties for all subclasses are handled here. Ideally we'd prefer -// to handle this in a more generic way. Allowing subclasses of EntityItem to register their properties -// -// TODO: There's a lot of repeated patterns in the code below to handle each property. It would be nice if the property -// registration mechanism allowed us to collapse these repeated sections of code into a single implementation that -// utilized the registration table to shorten up and simplify this code. -// -// TODO: Implement support for paged properties, spanning MTU, and custom properties -// -// TODO: Implement support for script and visible properties. -// -OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties, - QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties) { - - OctreePacketData ourDataPacket(false, buffer.size()); // create a packetData object to add out packet details too. - OctreePacketData* packetData = &ourDataPacket; // we want a pointer to this so we can use our APPEND_ENTITY_PROPERTY macro - - bool success = true; // assume the best - OctreeElement::AppendState appendState = OctreeElement::COMPLETED; // assume the best - - // TODO: We need to review how jurisdictions should be handled for entities. (The old Models and Particles code - // didn't do anything special for jurisdictions, so we're keeping that same behavior here.) - // - // Always include the root octcode. This is only because the OctreeEditPacketSender will check these octcodes - // to determine which server to send the changes to in the case of multiple jurisdictions. The root will be sent - // to all servers. - vec3 rootPosition(0); - float rootScale = 0.5f; - unsigned char* octcode = pointToOctalCode(rootPosition.x, rootPosition.y, rootPosition.z, rootScale); - - success = packetData->startSubTree(octcode); - delete[] octcode; - - // assuming we have room to fit our octalCode, proceed... - if (success) { - - // Now add our edit content details... - - // id - // encode our ID as a byte count coded byte stream - QByteArray encodedID = id.toRfc4122(); // NUM_BYTES_RFC4122_UUID - - // encode our ID as a byte count coded byte stream - ByteCountCoded tokenCoder; - QByteArray encodedToken; - - // encode our type as a byte count coded byte stream - ByteCountCoded typeCoder = (quint32)properties.getType(); - QByteArray encodedType = typeCoder; - - quint64 updateDelta = 0; // this is an edit so by definition, it's update is in sync - ByteCountCoded updateDeltaCoder = updateDelta; - QByteArray encodedUpdateDelta = updateDeltaCoder; - - EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); - EntityPropertyFlags propertiesDidntFit = requestedProperties; - - LevelDetails entityLevel = packetData->startLevel(); - - // Last Edited quint64 always first, before any other details, which allows us easy access to adjusting this - // timestamp for clock skew - quint64 lastEdited = properties.getLastEdited(); - bool successLastEditedFits = packetData->appendValue(lastEdited); - - bool successIDFits = packetData->appendRawData(encodedID); - if (successIDFits) { - successIDFits = packetData->appendRawData(encodedToken); - } - bool successTypeFits = packetData->appendRawData(encodedType); - - // NOTE: We intentionally do not send "created" times in edit messages. This is because: - // 1) if the edit is to an existing entity, the created time can not be changed - // 2) if the edit is to a new entity, the created time is the last edited time - - // TODO: Should we get rid of this in this in edit packets, since this has to always be 0? - bool successLastUpdatedFits = packetData->appendRawData(encodedUpdateDelta); - - int propertyFlagsOffset = packetData->getUncompressedByteOffset(); - QByteArray encodedPropertyFlags = propertyFlags; - int oldPropertyFlagsLength = encodedPropertyFlags.length(); - bool successPropertyFlagsFits = packetData->appendRawData(encodedPropertyFlags); - int propertyCount = 0; - - bool headerFits = successIDFits && successTypeFits && successLastEditedFits && - successLastUpdatedFits && successPropertyFlagsFits; - - int startOfEntityItemData = packetData->getUncompressedByteOffset(); - - if (headerFits) { - bool successPropertyFits; - propertyFlags -= PROP_LAST_ITEM; // clear the last item for now, we may or may not set it as the actual item - - // These items would go here once supported.... - // PROP_PAGED_PROPERTY, - // PROP_CUSTOM_PROPERTIES_INCLUDED, - - - APPEND_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, properties._simulationOwner.toByteArray()); - APPEND_ENTITY_PROPERTY(PROP_PARENT_ID, properties.getParentID()); - APPEND_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, properties.getParentJointIndex()); - APPEND_ENTITY_PROPERTY(PROP_VISIBLE, properties.getVisible()); - APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName()); - APPEND_ENTITY_PROPERTY(PROP_LOCKED, properties.getLocked()); - APPEND_ENTITY_PROPERTY(PROP_USER_DATA, properties.getUserData()); - APPEND_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, properties.getPrivateUserData()); - APPEND_ENTITY_PROPERTY(PROP_HREF, properties.getHref()); - APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, properties.getDescription()); - APPEND_ENTITY_PROPERTY(PROP_POSITION, properties.getPosition()); - APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, properties.getDimensions()); - APPEND_ENTITY_PROPERTY(PROP_ROTATION, properties.getRotation()); - APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, properties.getRegistrationPoint()); - APPEND_ENTITY_PROPERTY(PROP_CREATED, properties.getCreated()); - APPEND_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, properties.getLastEditedBy()); - // APPEND_ENTITY_PROPERTY(PROP_ENTITY_HOST_TYPE, (uint32_t)properties.getEntityHostType()); // not sent over the wire - // APPEND_ENTITY_PROPERTY(PROP_OWNING_AVATAR_ID, properties.getOwningAvatarID()); // not sent over the wire - APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, properties.getQueryAACube()); - APPEND_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, properties.getCanCastShadow()); - // APPEND_ENTITY_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, properties.getIsVisibleInSecondaryCamera()); // not sent over the wire - APPEND_ENTITY_PROPERTY(PROP_RENDER_LAYER, (uint32_t)properties.getRenderLayer()); - APPEND_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, (uint32_t)properties.getPrimitiveMode()); - APPEND_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, properties.getIgnorePickIntersection()); - APPEND_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, properties.getRenderWithZones()); - APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); - APPEND_ENTITY_PROPERTY(PROP_TAGS, properties.getTags()); - _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()); - APPEND_ENTITY_PROPERTY(PROP_VELOCITY, properties.getVelocity()); - APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, properties.getAngularVelocity()); - APPEND_ENTITY_PROPERTY(PROP_GRAVITY, properties.getGravity()); - APPEND_ENTITY_PROPERTY(PROP_ACCELERATION, properties.getAcceleration()); - APPEND_ENTITY_PROPERTY(PROP_DAMPING, properties.getDamping()); - APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, properties.getAngularDamping()); - APPEND_ENTITY_PROPERTY(PROP_RESTITUTION, properties.getRestitution()); - APPEND_ENTITY_PROPERTY(PROP_FRICTION, properties.getFriction()); - APPEND_ENTITY_PROPERTY(PROP_LIFETIME, properties.getLifetime()); - APPEND_ENTITY_PROPERTY(PROP_COLLISIONLESS, properties.getCollisionless()); - APPEND_ENTITY_PROPERTY(PROP_COLLISION_MASK, properties.getCollisionMask()); - APPEND_ENTITY_PROPERTY(PROP_DYNAMIC, properties.getDynamic()); - APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL()); - APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData()); - - // Cloning - APPEND_ENTITY_PROPERTY(PROP_CLONEABLE, properties.getCloneable()); - APPEND_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, properties.getCloneLifetime()); - APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, properties.getCloneLimit()); - APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, properties.getCloneDynamic()); - APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, properties.getCloneAvatarEntity()); - APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, properties.getCloneOriginID()); - - // Scripts - APPEND_ENTITY_PROPERTY(PROP_SCRIPT, properties.getScript()); - APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, properties.getScriptTimestamp()); - APPEND_ENTITY_PROPERTY(PROP_SERVER_SCRIPTS, properties.getServerScripts()); - - if (properties.getType() == EntityTypes::ParticleEffect) { - APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)(properties.getShapeType())); - APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL()); - APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha()); - _staticPulse.setProperties(properties); - _staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags, - propertiesDidntFit, propertyCount, appendState); - APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures()); - - APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, properties.getMaxParticles()); - APPEND_ENTITY_PROPERTY(PROP_LIFESPAN, properties.getLifespan()); - - APPEND_ENTITY_PROPERTY(PROP_EMITTING_PARTICLES, properties.getIsEmitting()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_RATE, properties.getEmitRate()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_SPEED, properties.getEmitSpeed()); - APPEND_ENTITY_PROPERTY(PROP_SPEED_SPREAD, properties.getSpeedSpread()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_ORIENTATION, properties.getEmitOrientation()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_DIMENSIONS, properties.getEmitDimensions()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_RADIUS_START, properties.getEmitRadiusStart()); - - APPEND_ENTITY_PROPERTY(PROP_POLAR_START, properties.getPolarStart()); - APPEND_ENTITY_PROPERTY(PROP_POLAR_FINISH, properties.getPolarFinish()); - APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, properties.getAzimuthStart()); - APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, properties.getAzimuthFinish()); - - APPEND_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, properties.getEmitAcceleration()); - APPEND_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, properties.getAccelerationSpread()); - - APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, properties.getParticleRadius()); - APPEND_ENTITY_PROPERTY(PROP_RADIUS_SPREAD, properties.getRadiusSpread()); - APPEND_ENTITY_PROPERTY(PROP_RADIUS_START, properties.getRadiusStart()); - APPEND_ENTITY_PROPERTY(PROP_RADIUS_FINISH, properties.getRadiusFinish()); - - APPEND_ENTITY_PROPERTY(PROP_COLOR_SPREAD, properties.getColorSpread()); - APPEND_ENTITY_PROPERTY(PROP_COLOR_START, properties.getColorStart()); - APPEND_ENTITY_PROPERTY(PROP_COLOR_FINISH, properties.getColorFinish()); - - APPEND_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, properties.getAlphaSpread()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, properties.getAlphaStart()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, properties.getAlphaFinish()); - - APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, properties.getEmitterShouldTrail()); - - APPEND_ENTITY_PROPERTY(PROP_PARTICLE_SPIN, properties.getParticleSpin()); - APPEND_ENTITY_PROPERTY(PROP_SPIN_SPREAD, properties.getSpinSpread()); - APPEND_ENTITY_PROPERTY(PROP_SPIN_START, properties.getSpinStart()); - APPEND_ENTITY_PROPERTY(PROP_SPIN_FINISH, properties.getSpinFinish()); - APPEND_ENTITY_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, properties.getRotateWithEntity()) - } - - if (properties.getType() == EntityTypes::ProceduralParticleEffect) { - APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, properties.getNumParticles()); - APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, properties.getNumTrianglesPerParticle()); - APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, properties.getNumUpdateProps()); - APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, properties.getParticleTransparent()); - APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, properties.getParticleUpdateData()); - APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, properties.getParticleRenderData()); - } - - if (properties.getType() == EntityTypes::Model) { - APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)(properties.getShapeType())); - APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL()); - APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); - APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures()); - - APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, properties.getModelURL()); - APPEND_ENTITY_PROPERTY(PROP_MODEL_SCALE, properties.getModelScale()); - APPEND_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS_SET, properties.getJointRotationsSet()); - APPEND_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, properties.getJointRotations()); - APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, properties.getJointTranslationsSet()); - APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, properties.getJointTranslations()); - APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, properties.getRelayParentJoints()); - APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, properties.getGroupCulled()); - APPEND_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, properties.getBlendshapeCoefficients()); - APPEND_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, properties.getUseOriginalPivot()); - APPEND_ENTITY_PROPERTY(PROP_LOAD_PRIORITY, properties.getLoadPriority()); - - _staticAnimation.setProperties(properties); - _staticAnimation.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - } - - if (properties.getType() == EntityTypes::Light) { - APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); - APPEND_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, properties.getIsSpotlight()); - APPEND_ENTITY_PROPERTY(PROP_INTENSITY, properties.getIntensity()); - APPEND_ENTITY_PROPERTY(PROP_EXPONENT, properties.getExponent()); - APPEND_ENTITY_PROPERTY(PROP_CUTOFF, properties.getCutoff()); - APPEND_ENTITY_PROPERTY(PROP_FALLOFF_RADIUS, properties.getFalloffRadius()); - } - - if (properties.getType() == EntityTypes::Text) { - _staticPulse.setProperties(properties); - _staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags, - propertiesDidntFit, propertyCount, appendState); - - APPEND_ENTITY_PROPERTY(PROP_TEXT, properties.getText()); - APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, properties.getLineHeight()); - APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, properties.getTextColor()); - APPEND_ENTITY_PROPERTY(PROP_TEXT_ALPHA, properties.getTextAlpha()); - APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, properties.getBackgroundColor()); - APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_ALPHA, properties.getBackgroundAlpha()); - APPEND_ENTITY_PROPERTY(PROP_LEFT_MARGIN, properties.getLeftMargin()); - APPEND_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, properties.getRightMargin()); - APPEND_ENTITY_PROPERTY(PROP_TOP_MARGIN, properties.getTopMargin()); - APPEND_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, properties.getBottomMargin()); - APPEND_ENTITY_PROPERTY(PROP_UNLIT, properties.getUnlit()); - APPEND_ENTITY_PROPERTY(PROP_FONT, properties.getFont()); - APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT, (uint32_t)properties.getTextEffect()); - APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_COLOR, properties.getTextEffectColor()); - APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, properties.getTextEffectThickness()); - APPEND_ENTITY_PROPERTY(PROP_TEXT_ALIGNMENT, (uint32_t)properties.getAlignment()); - } - - if (properties.getType() == EntityTypes::Zone) { - APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)properties.getShapeType()); - APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL()); - - _staticKeyLight.setProperties(properties); - _staticKeyLight.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - - _staticAmbientLight.setProperties(properties); - _staticAmbientLight.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - - _staticSkybox.setProperties(properties); - _staticSkybox.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - - _staticHaze.setProperties(properties); - _staticHaze.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - - _staticBloom.setProperties(properties); - _staticBloom.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - - _staticAudio.setProperties(properties); - _staticAudio.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - - _staticTonemapping.setProperties(properties); - _staticTonemapping.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - - _staticAmbientOcclusion.setProperties(properties); - _staticAmbientOcclusion.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - - APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, properties.getFlyingAllowed()); - APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, properties.getGhostingAllowed()); - APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, properties.getFilterURL()); - - APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)properties.getKeyLightMode()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)properties.getAmbientLightMode()); - APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)properties.getSkyboxMode()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)properties.getHazeMode()); - APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)properties.getBloomMode()); - APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, (uint32_t)properties.getAvatarPriority()); - APPEND_ENTITY_PROPERTY(PROP_SCREENSHARE, (uint32_t)properties.getScreenshare()); - APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_MODE, (uint32_t)properties.getTonemappingMode()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_MODE, (uint32_t)properties.getAmbientOcclusionMode()); - } - - if (properties.getType() == EntityTypes::PolyVox) { - APPEND_ENTITY_PROPERTY(PROP_VOXEL_VOLUME_SIZE, properties.getVoxelVolumeSize()); - APPEND_ENTITY_PROPERTY(PROP_VOXEL_DATA, properties.getVoxelData()); - APPEND_ENTITY_PROPERTY(PROP_VOXEL_SURFACE_STYLE, properties.getVoxelSurfaceStyle()); - APPEND_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, properties.getXTextureURL()); - APPEND_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, properties.getYTextureURL()); - APPEND_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, properties.getZTextureURL()); - APPEND_ENTITY_PROPERTY(PROP_X_N_NEIGHBOR_ID, properties.getXNNeighborID()); - APPEND_ENTITY_PROPERTY(PROP_Y_N_NEIGHBOR_ID, properties.getYNNeighborID()); - APPEND_ENTITY_PROPERTY(PROP_Z_N_NEIGHBOR_ID, properties.getZNNeighborID()); - APPEND_ENTITY_PROPERTY(PROP_X_P_NEIGHBOR_ID, properties.getXPNeighborID()); - APPEND_ENTITY_PROPERTY(PROP_Y_P_NEIGHBOR_ID, properties.getYPNeighborID()); - APPEND_ENTITY_PROPERTY(PROP_Z_P_NEIGHBOR_ID, properties.getZPNeighborID()); - } - - if (properties.getType() == EntityTypes::Web) { - APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha()); - _staticPulse.setProperties(properties); - _staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags, - propertiesDidntFit, propertyCount, appendState); - - APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl()); - APPEND_ENTITY_PROPERTY(PROP_DPI, properties.getDPI()); - APPEND_ENTITY_PROPERTY(PROP_SCRIPT_URL, properties.getScriptURL()); - APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, properties.getMaxFPS()); - APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)properties.getInputMode()); - APPEND_ENTITY_PROPERTY(PROP_WANTS_KEYBOARD_FOCUS, properties.getWantsKeyboardFocus()); - APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, properties.getShowKeyboardFocusHighlight()); - APPEND_ENTITY_PROPERTY(PROP_WEB_USE_BACKGROUND, properties.getUseBackground()); - APPEND_ENTITY_PROPERTY(PROP_USER_AGENT, properties.getUserAgent()); - } - - if (properties.getType() == EntityTypes::Line) { - APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); - - APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, properties.getLinePoints()); - } - - if (properties.getType() == EntityTypes::PolyLine) { - APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); - APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures()); - - APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, properties.getLinePoints()); - APPEND_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, properties.getStrokeWidths()); - APPEND_ENTITY_PROPERTY(PROP_STROKE_NORMALS, properties.getPackedNormals()); - APPEND_ENTITY_PROPERTY(PROP_STROKE_COLORS, properties.getPackedStrokeColors()); - APPEND_ENTITY_PROPERTY(PROP_IS_UV_MODE_STRETCH, properties.getIsUVModeStretch()); - APPEND_ENTITY_PROPERTY(PROP_LINE_GLOW, properties.getGlow()); - APPEND_ENTITY_PROPERTY(PROP_LINE_FACE_CAMERA, properties.getFaceCamera()); - } - - // NOTE: Spheres and Boxes are just special cases of Shape, and they need to include their PROP_SHAPE - // when encoding/decoding edits because otherwise they can't polymorph to other shape types - if (properties.getType() == EntityTypes::Shape || - properties.getType() == EntityTypes::Box || - properties.getType() == EntityTypes::Sphere) { - APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha()); - APPEND_ENTITY_PROPERTY(PROP_UNLIT, properties.getUnlit()); - _staticPulse.setProperties(properties); - _staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags, - propertiesDidntFit, propertyCount, appendState); - APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape()); - } - - // Materials - if (properties.getType() == EntityTypes::Material) { - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_URL, properties.getMaterialURL()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_MAPPING_MODE, (uint32_t)properties.getMaterialMappingMode()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_PRIORITY, properties.getPriority()); - APPEND_ENTITY_PROPERTY(PROP_PARENT_MATERIAL_NAME, properties.getParentMaterialName()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_MAPPING_POS, properties.getMaterialMappingPos()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_MAPPING_SCALE, properties.getMaterialMappingScale()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_MAPPING_ROT, properties.getMaterialMappingRot()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_DATA, properties.getMaterialData()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_REPEAT, properties.getMaterialRepeat()); - } - - // Image - if (properties.getType() == EntityTypes::Image) { - APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha()); - _staticPulse.setProperties(properties); - _staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags, - propertiesDidntFit, propertyCount, appendState); - - APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, properties.getImageURL()); - APPEND_ENTITY_PROPERTY(PROP_EMISSIVE, properties.getEmissive()); - APPEND_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, properties.getKeepAspectRatio()); - APPEND_ENTITY_PROPERTY(PROP_SUB_IMAGE, properties.getSubImage()); - } - - // Grid - if (properties.getType() == EntityTypes::Grid) { - APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha()); - _staticPulse.setProperties(properties); - _staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags, - propertiesDidntFit, propertyCount, appendState); - - APPEND_ENTITY_PROPERTY(PROP_GRID_FOLLOW_CAMERA, properties.getFollowCamera()); - APPEND_ENTITY_PROPERTY(PROP_MAJOR_GRID_EVERY, properties.getMajorGridEvery()); - APPEND_ENTITY_PROPERTY(PROP_MINOR_GRID_EVERY, properties.getMinorGridEvery()); - } - - if (properties.getType() == EntityTypes::Gizmo) { - APPEND_ENTITY_PROPERTY(PROP_GIZMO_TYPE, (uint32_t)properties.getGizmoType()); - _staticRing.setProperties(properties); - _staticRing.appendToEditPacket(packetData, requestedProperties, propertyFlags, - propertiesDidntFit, propertyCount, appendState); - } - - if (properties.getType() == EntityTypes::Sound) { - APPEND_ENTITY_PROPERTY(PROP_SOUND_URL, properties.getSoundURL()); - APPEND_ENTITY_PROPERTY(PROP_SOUND_VOLUME, properties.getVolume()); - APPEND_ENTITY_PROPERTY(PROP_SOUND_TIME_OFFSET, properties.getTimeOffset()); - APPEND_ENTITY_PROPERTY(PROP_SOUND_PITCH, properties.getPitch()); - APPEND_ENTITY_PROPERTY(PROP_SOUND_PLAYING, properties.getPlaying()); - APPEND_ENTITY_PROPERTY(PROP_SOUND_LOOP, properties.getLoop()); - APPEND_ENTITY_PROPERTY(PROP_SOUND_POSITIONAL, properties.getPositional()); - APPEND_ENTITY_PROPERTY(PROP_SOUND_LOCAL_ONLY, properties.getLocalOnly()); - } - } - - if (propertyCount > 0) { - int endOfEntityItemData = packetData->getUncompressedByteOffset(); - - encodedPropertyFlags = propertyFlags; - int newPropertyFlagsLength = encodedPropertyFlags.length(); - packetData->updatePriorBytes(propertyFlagsOffset, (const unsigned char*)encodedPropertyFlags.constData(), - encodedPropertyFlags.length()); - - // if the size of the PropertyFlags shrunk, we need to shift everything down to front of packet. - if (newPropertyFlagsLength < oldPropertyFlagsLength) { - int oldSize = packetData->getUncompressedSize(); - - const unsigned char* modelItemData = packetData->getUncompressedData(propertyFlagsOffset + oldPropertyFlagsLength); - int modelItemDataLength = endOfEntityItemData - startOfEntityItemData; - int newEntityItemDataStart = propertyFlagsOffset + newPropertyFlagsLength; - packetData->updatePriorBytes(newEntityItemDataStart, modelItemData, modelItemDataLength); - - int newSize = oldSize - (oldPropertyFlagsLength - newPropertyFlagsLength); - packetData->setUncompressedSize(newSize); - - } else { - assert(newPropertyFlagsLength == oldPropertyFlagsLength); // should not have grown - } - - packetData->endLevel(entityLevel); - } else { - packetData->discardLevel(entityLevel); - appendState = OctreeElement::NONE; // if we got here, then we didn't include the item - } - - // If any part of the model items didn't fit, then the element is considered partial - if (appendState != OctreeElement::COMPLETED) { - didntFitProperties = propertiesDidntFit; - } - - packetData->endSubTree(); - - const char* finalizedData = reinterpret_cast(packetData->getFinalizedData()); - int finalizedSize = packetData->getFinalizedSize(); - - if (finalizedSize <= buffer.size()) { - buffer.replace(0, finalizedSize, finalizedData, finalizedSize); - buffer.resize(finalizedSize); - } else { - qCDebug(entities) << "ERROR - encoded edit message doesn't fit in output buffer."; - appendState = OctreeElement::NONE; // if we got here, then we didn't include the item - // maybe we should assert!!! - } - } else { - packetData->discardSubTree(); - } - - return appendState; -} - -QByteArray EntityItemProperties::getPackedNormals() const { - return packNormals(getNormals()); -} - -QByteArray EntityItemProperties::packNormals(const QVector& normals) const { - int normalsSize = normals.size(); - QByteArray packedNormals = QByteArray(normalsSize * 6 + 1, '0'); - // add size of the array - packedNormals[0] = ((uint8_t)normalsSize); - - int index = 1; - for (int i = 0; i < normalsSize; i++) { - int numBytes = packFloatVec3ToSignedTwoByteFixed((unsigned char*)packedNormals.data() + index, normals[i], 15); - index += numBytes; - } - return packedNormals; -} - -QByteArray EntityItemProperties::getPackedStrokeColors() const { - return packStrokeColors(getStrokeColors()); -} -QByteArray EntityItemProperties::packStrokeColors(const QVector& strokeColors) const { - int strokeColorsSize = strokeColors.size(); - QByteArray packedStrokeColors = QByteArray(strokeColorsSize * 3 + 1, '0'); - - // add size of the array - packedStrokeColors[0] = ((uint8_t)strokeColorsSize); - - - for (int i = 0; i < strokeColorsSize; i++) { - // add the color to the QByteArray - packedStrokeColors[i * 3 + 1] = strokeColors[i].x * 255; - packedStrokeColors[i * 3 + 2] = strokeColors[i].y * 255; - packedStrokeColors[i * 3 + 3] = strokeColors[i].z * 255; - } - return packedStrokeColors; -} - -// TODO: -// how to handle lastEdited? -// how to handle lastUpdated? -// consider handling case where no properties are included... we should just ignore this packet... -// -// TODO: Right now, all possible properties for all subclasses are handled here. Ideally we'd prefer -// to handle this in a more generic way. Allowing subclasses of EntityItem to register their properties -// -// TODO: There's a lot of repeated patterns in the code below to handle each property. It would be nice if the property -// registration mechanism allowed us to collapse these repeated sections of code into a single implementation that -// utilized the registration table to shorten up and simplify this code. -// -// TODO: Implement support for paged properties, spanning MTU, and custom properties -// -// TODO: Implement support for script and visible properties. -// -bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes, - EntityItemID& entityID, EntityItemProperties& properties) { - bool valid = false; - - const unsigned char* dataAt = data; - processedBytes = 0; - - // the first part of the data is an octcode, this is a required element of the edit packet format, but we don't - // actually use it, we do need to skip it and read to the actual data we care about. - int octets = numberOfThreeBitSectionsInCode(data); - int bytesToReadOfOctcode = (int)bytesRequiredForCodeLength(octets); - - // we don't actually do anything with this octcode... - dataAt += bytesToReadOfOctcode; - processedBytes += bytesToReadOfOctcode; - - // Edit packets have a last edited time stamp immediately following the octcode. - // NOTE: the edit times have been set by the editor to match out clock, so we don't need to adjust - // these times for clock skew at this point. - quint64 lastEdited; - memcpy(&lastEdited, dataAt, sizeof(lastEdited)); - dataAt += sizeof(lastEdited); - processedBytes += sizeof(lastEdited); - properties.setLastEdited(lastEdited); - - // encoded id - QUuid editID = QUuid::fromRfc4122(QByteArray::fromRawData(reinterpret_cast(dataAt), NUM_BYTES_RFC4122_UUID)); - dataAt += NUM_BYTES_RFC4122_UUID; - processedBytes += NUM_BYTES_RFC4122_UUID; - - entityID = editID; - valid = true; - - // Entity Type... - QByteArray encodedType((const char*)dataAt, (bytesToRead - processedBytes)); - ByteCountCoded typeCoder = encodedType; - quint32 entityTypeCode = typeCoder; - properties.setType((EntityTypes::EntityType)entityTypeCode); - encodedType = typeCoder; // determine true bytesToRead - dataAt += encodedType.size(); - processedBytes += encodedType.size(); - - // Update Delta - when was this item updated relative to last edit... this really should be 0 - // TODO: Should we get rid of this in this in edit packets, since this has to always be 0? - // TODO: do properties need to handle lastupdated??? - - // last updated is stored as ByteCountCoded delta from lastEdited - QByteArray encodedUpdateDelta((const char*)dataAt, (bytesToRead - processedBytes)); - ByteCountCoded updateDeltaCoder = encodedUpdateDelta; - encodedUpdateDelta = updateDeltaCoder; // determine true bytesToRead - dataAt += encodedUpdateDelta.size(); - processedBytes += encodedUpdateDelta.size(); - - // TODO: Do we need this lastUpdated?? We don't seem to use it. - //quint64 updateDelta = updateDeltaCoder; - //quint64 lastUpdated = lastEdited + updateDelta; // don't adjust for clock skew since we already did that for lastEdited - - // Property Flags... - QByteArray encodedPropertyFlags((const char*)dataAt, (bytesToRead - processedBytes)); - EntityPropertyFlags propertyFlags = encodedPropertyFlags; - dataAt += propertyFlags.getEncodedLength(); - processedBytes += propertyFlags.getEncodedLength(); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATION_OWNER, QByteArray, setSimulationOwner); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARENT_ID, QUuid, setParentID); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARENT_JOINT_INDEX, quint16, setParentJointIndex); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PRIVATE_USER_DATA, QString, setPrivateUserData); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HREF, QString, setHref); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DESCRIPTION, QString, setDescription); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, vec3, setPosition); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, vec3, setDimensions); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ROTATION, quat, setRotation); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, vec3, setRegistrationPoint); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CREATED, quint64, setCreated); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LAST_EDITED_BY, QUuid, setLastEditedBy); - // READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ENTITY_HOST_TYPE, entity::HostType, setEntityHostType); // not sent over the wire - // READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_OWNING_AVATAR_ID, QUuid, setOwningAvatarID); // not sent over the wire - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_QUERY_AA_CUBE, AACube, setQueryAACube); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow); - // READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE_IN_SECONDARY_CAMERA, bool, setIsVisibleInSecondaryCamera); // not sent over the wire - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RENDER_LAYER, RenderLayer, setRenderLayer); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PRIMITIVE_MODE, PrimitiveMode, setPrimitiveMode); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IGNORE_PICK_INTERSECTION, bool, setIgnorePickIntersection); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RENDER_WITH_ZONES, QVector, setRenderWithZones); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TAGS, QSet, setTags); - 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); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY, vec3, setVelocity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, vec3, setAngularVelocity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRAVITY, vec3, setGravity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACCELERATION, vec3, setAcceleration); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DAMPING, float, setDamping); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_DAMPING, float, setAngularDamping); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RESTITUTION, float, setRestitution); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FRICTION, float, setFriction); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFETIME, float, setLifetime); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONLESS, bool, setCollisionless); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_MASK, uint16_t, setCollisionMask); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DYNAMIC, bool, setDynamic); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACTION_DATA, QByteArray, setActionData); - - // Cloning - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE, bool, setCloneable); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_LIFETIME, float, setCloneLifetime); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_LIMIT, float, setCloneLimit); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_DYNAMIC, bool, setCloneDynamic); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID); - - // Scripts - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT, QString, setScript); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SERVER_SCRIPTS, QString, setServerScripts); - - if (properties.getType() == EntityTypes::ParticleEffect) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); - properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_PARTICLES, quint32, setMaxParticles); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFESPAN, float, setLifespan); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMITTING_PARTICLES, bool, setIsEmitting); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_RATE, float, setEmitRate); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_SPEED, float, setEmitSpeed); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPEED_SPREAD, float, setSpeedSpread); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ORIENTATION, quat, setEmitOrientation); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_DIMENSIONS, vec3, setEmitDimensions); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_RADIUS_START, float, setEmitRadiusStart); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POLAR_START, float, setPolarStart); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POLAR_FINISH, float, setPolarFinish); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AZIMUTH_START, float, setAzimuthStart); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AZIMUTH_FINISH, float, setAzimuthFinish); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ACCELERATION, vec3, setEmitAcceleration); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACCELERATION_SPREAD, vec3, setAccelerationSpread); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_RADIUS, float, setParticleRadius); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_SPREAD, float, setRadiusSpread); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_START, float, setRadiusStart); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_FINISH, float, setRadiusFinish); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_SPREAD, u8vec3Color, setColorSpread); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_START, vec3Color, setColorStart); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_FINISH, vec3Color, setColorFinish); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_SPREAD, float, setAlphaSpread); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_START, float, setAlphaStart); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_FINISH, float, setAlphaFinish); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_SPIN, float, setParticleSpin); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPIN_SPREAD, float, setSpinSpread); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPIN_START, float, setSpinStart); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPIN_FINISH, float, setSpinFinish); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_ROTATE_WITH_ENTITY, bool, setRotateWithEntity); - } - - if (properties.getType() == EntityTypes::ProceduralParticleEffect) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, uint32_t, setNumParticles); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, uint8_t, setNumTrianglesPerParticle); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, uint8_t, setNumUpdateProps); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, bool, setParticleTransparent); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, QString, setParticleUpdateData); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, QString, setParticleRenderData); - } - - if (properties.getType() == EntityTypes::Model) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MODEL_URL, QString, setModelURL); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MODEL_SCALE, vec3, setModelScale); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_ROTATIONS_SET, QVector, setJointRotationsSet); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_ROTATIONS, QVector, setJointRotations); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS_SET, QVector, setJointTranslationsSet); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS, QVector, setJointTranslations); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GROUP_CULLED, bool, setGroupCulled); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLENDSHAPE_COEFFICIENTS, QString, setBlendshapeCoefficients); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USE_ORIGINAL_PIVOT, bool, setUseOriginalPivot); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOAD_PRIORITY, float, setLoadPriority); - - properties.getAnimation().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - } - - if (properties.getType() == EntityTypes::Light) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IS_SPOTLIGHT, bool, setIsSpotlight); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_INTENSITY, float, setIntensity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EXPONENT, float, setExponent); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CUTOFF, float, setCutoff); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FALLOFF_RADIUS, float, setFalloffRadius); - } - - if (properties.getType() == EntityTypes::Text) { - properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT, QString, setText); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_COLOR, u8vec3Color, setTextColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_ALPHA, float, setTextAlpha); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_COLOR, u8vec3Color, setBackgroundColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_ALPHA, float, setBackgroundAlpha); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LEFT_MARGIN, float, setLeftMargin); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RIGHT_MARGIN, float, setRightMargin); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TOP_MARGIN, float, setTopMargin); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BOTTOM_MARGIN, float, setBottomMargin); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_UNLIT, bool, setUnlit); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FONT, QString, setFont); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_EFFECT, TextEffect, setTextEffect); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_EFFECT_COLOR, u8vec3Color, setTextEffectColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_EFFECT_THICKNESS, float, setTextEffectThickness); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_ALIGNMENT, TextAlignment, setAlignment); - } - - if (properties.getType() == EntityTypes::Zone) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); - - properties.getKeyLight().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - properties.getAmbientLight().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - properties.getSkybox().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - properties.getBloom().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - properties.getAudio().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - properties.getTonemapping().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - properties.getAmbientOcclusion().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FILTER_URL, QString, setFilterURL); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLOOM_MODE, uint32_t, setBloomMode); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AVATAR_PRIORITY, uint32_t, setAvatarPriority); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCREENSHARE, uint32_t, setScreenshare); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TONEMAPPING_MODE, uint32_t, setTonemappingMode); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AMBIENT_OCCLUSION_MODE, uint32_t, setAmbientOcclusionMode); - } - - if (properties.getType() == EntityTypes::PolyVox) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_VOLUME_SIZE, vec3, setVoxelVolumeSize); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_DATA, QByteArray, setVoxelData); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_SURFACE_STYLE, uint16_t, setVoxelSurfaceStyle); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_TEXTURE_URL, QString, setXTextureURL); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Y_TEXTURE_URL, QString, setYTextureURL); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Z_TEXTURE_URL, QString, setZTextureURL); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_N_NEIGHBOR_ID, EntityItemID, setXNNeighborID); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Y_N_NEIGHBOR_ID, EntityItemID, setYNNeighborID); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Z_N_NEIGHBOR_ID, EntityItemID, setZNNeighborID); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_P_NEIGHBOR_ID, EntityItemID, setXPNeighborID); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Y_P_NEIGHBOR_ID, EntityItemID, setYPNeighborID); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Z_P_NEIGHBOR_ID, EntityItemID, setZPNeighborID); - } - - if (properties.getType() == EntityTypes::Web) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); - properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DPI, uint16_t, setDPI); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT_URL, QString, setScriptURL); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_FPS, uint8_t, setMaxFPS); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_INPUT_MODE, WebInputMode, setInputMode); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_WANTS_KEYBOARD_FOCUS, bool, setWantsKeyboardFocus); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, bool, setShowKeyboardFocusHighlight); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_WEB_USE_BACKGROUND, bool, setUseBackground); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_AGENT, QString, setUserAgent); - } - - if (properties.getType() == EntityTypes::Line) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector, setLinePoints); - } - - if (properties.getType() == EntityTypes::PolyLine) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector, setLinePoints); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_WIDTHS, QVector, setStrokeWidths); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_NORMALS, QByteArray, setPackedNormals); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_COLORS, QByteArray, setPackedStrokeColors); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IS_UV_MODE_STRETCH, bool, setIsUVModeStretch); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_GLOW, bool, setGlow); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_FACE_CAMERA, bool, setFaceCamera); - } - - // NOTE: Spheres and Boxes are just special cases of Shape, and they need to include their PROP_SHAPE - // when encoding/decoding edits because otherwise they can't polymorph to other shape types - if (properties.getType() == EntityTypes::Shape || - properties.getType() == EntityTypes::Box || - properties.getType() == EntityTypes::Sphere) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_UNLIT, bool, setUnlit); - properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE, QString, setShape); - } - - // Materials - if (properties.getType() == EntityTypes::Material) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_URL, QString, setMaterialURL); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_MAPPING_MODE, MaterialMappingMode, setMaterialMappingMode); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_PRIORITY, quint16, setPriority); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARENT_MATERIAL_NAME, QString, setParentMaterialName); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_MAPPING_POS, vec2, setMaterialMappingPos); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_MAPPING_SCALE, vec2, setMaterialMappingScale); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_MAPPING_ROT, float, setMaterialMappingRot); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_DATA, QString, setMaterialData); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_REPEAT, bool, setMaterialRepeat); - } - - // Image - if (properties.getType() == EntityTypes::Image) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); - properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IMAGE_URL, QString, setImageURL); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMISSIVE, bool, setEmissive); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEEP_ASPECT_RATIO, bool, setKeepAspectRatio); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SUB_IMAGE, QRect, setSubImage); - } - - // Grid - if (properties.getType() == EntityTypes::Grid) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); - properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRID_FOLLOW_CAMERA, bool, setFollowCamera); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAJOR_GRID_EVERY, uint32_t, setMajorGridEvery); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MINOR_GRID_EVERY, float, setMinorGridEvery); - } - - if (properties.getType() == EntityTypes::Gizmo) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GIZMO_TYPE, GizmoType, setGizmoType); - properties.getRing().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - } - - if (properties.getType() == EntityTypes::Sound) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_URL, QString, setSoundURL); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_VOLUME, float, setVolume); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_TIME_OFFSET, float, setTimeOffset); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_PITCH, float, setPitch); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_PLAYING, bool, setPlaying); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_LOOP, bool, setLoop); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_POSITIONAL, bool, setPositional); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_LOCAL_ONLY, bool, setLocalOnly); - } - - return valid; -} - -void EntityItemProperties::setPackedNormals(const QByteArray& value) { - setNormals(unpackNormals(value)); -} - -QVector EntityItemProperties::unpackNormals(const QByteArray& normals) { - // the size of the vector is packed first - QVector unpackedNormals = QVector((int)normals[0]); - - if ((int)normals[0] == normals.size() / 6) { - int j = 0; - for (int i = 1; i < normals.size();) { - vec3 aux = vec3(); - i += unpackFloatVec3FromSignedTwoByteFixed((unsigned char*)normals.data() + i, aux, 15); - unpackedNormals[j] = aux; - j++; - } - } else { - qCDebug(entities) << "WARNING - Expected received size for normals does not match. Expected: " << (int)normals[0] - << " Received: " << (normals.size() / 6); - } - return unpackedNormals; -} - -void EntityItemProperties::setPackedStrokeColors(const QByteArray& value) { - setStrokeColors(unpackStrokeColors(value)); -} - -QVector EntityItemProperties::unpackStrokeColors(const QByteArray& strokeColors) { - // the size of the vector is packed first - QVector unpackedStrokeColors = QVector((int)strokeColors[0]); - - if ((int)strokeColors[0] == strokeColors.size() / 3) { - int j = 0; - for (int i = 1; i < strokeColors.size();) { - - float r = (uint8_t)strokeColors[i++] / 255.0f; - float g = (uint8_t)strokeColors[i++] / 255.0f; - float b = (uint8_t)strokeColors[i++] / 255.0f; - unpackedStrokeColors[j++] = vec3(r, g, b); - } - } else { - qCDebug(entities) << "WARNING - Expected received size for stroke colors does not match. Expected: " - << (int)strokeColors[0] << " Received: " << (strokeColors.size() / 3); - } - - return unpackedStrokeColors; -} - -// NOTE: This version will only encode the portion of the edit message immediately following the -// header it does not include the send times and sequence number because that is handled by the -// edit packet sender... -bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer) { - - uint16_t numberOfIds = 1; // only one entity ID in this message - - if (buffer.size() < (int)(sizeof(numberOfIds) + NUM_BYTES_RFC4122_UUID)) { - qCDebug(entities) << "ERROR - encodeEraseEntityMessage() called with buffer that is too small!"; - return false; - } - - buffer.resize(0); - buffer.append(reinterpret_cast(&numberOfIds), sizeof(numberOfIds)); - buffer.append(entityItemID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID); - - return true; -} - -bool EntityItemProperties::encodeCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID, QByteArray& buffer) { - if (buffer.size() < (int)(NUM_BYTES_RFC4122_UUID * 2)) { - qCDebug(entities) << "ERROR - encodeCloneEntityMessage() called with buffer that is too small!"; - return false; - } - - buffer.resize(0); - buffer.append(entityIDToClone.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID); - buffer.append(newEntityID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID); - - return true; -} - -bool EntityItemProperties::decodeCloneEntityMessage(const QByteArray& buffer, int& processedBytes, EntityItemID& entityIDToClone, EntityItemID& newEntityID) { - const unsigned char* packetData = (const unsigned char*)buffer.constData(); - const unsigned char* dataAt = packetData; - size_t packetLength = buffer.size(); - processedBytes = 0; - - if (NUM_BYTES_RFC4122_UUID * 2 > packetLength) { - qCDebug(entities) << "EntityItemProperties::decodeCloneEntityMessage().... bailing because not enough bytes in buffer"; - return false; // bail to prevent buffer overflow - } - - QByteArray encodedID = buffer.mid((int)processedBytes, NUM_BYTES_RFC4122_UUID); - entityIDToClone = QUuid::fromRfc4122(encodedID); - dataAt += encodedID.size(); - processedBytes += encodedID.size(); - - encodedID = buffer.mid((int)processedBytes, NUM_BYTES_RFC4122_UUID); - newEntityID = QUuid::fromRfc4122(encodedID); - dataAt += encodedID.size(); - processedBytes += encodedID.size(); - - return true; -} - -void EntityItemProperties::markAllChanged() { - // Core - _simulationOwnerChanged = true; - _parentIDChanged = true; - _parentJointIndexChanged = true; - _visibleChanged = true; - _nameChanged = true; - _lockedChanged = true; - _userDataChanged = true; - _privateUserDataChanged = true; - _hrefChanged = true; - _descriptionChanged = true; - _positionChanged = true; - _dimensionsChanged = true; - _rotationChanged = true; - _registrationPointChanged = true; - _createdChanged = true; - _lastEditedByChanged = true; - _entityHostTypeChanged = true; - _owningAvatarIDChanged = true; - _queryAACubeChanged = true; - _canCastShadowChanged = true; - _isVisibleInSecondaryCameraChanged = true; - _renderLayerChanged = true; - _primitiveModeChanged = true; - _ignorePickIntersectionChanged = true; - _renderWithZonesChanged = true; - _billboardModeChanged = true; - _tagsChanged = true; - _grab.markAllChanged(); - _mirrorModeChanged = true; - _portalExitIDChanged = true; - - // Physics - _densityChanged = true; - _velocityChanged = true; - _angularVelocityChanged = true; - _gravityChanged = true; - _accelerationChanged = true; - _dampingChanged = true; - _angularDampingChanged = true; - _restitutionChanged = true; - _frictionChanged = true; - _lifetimeChanged = true; - _collisionlessChanged = true; - _collisionMaskChanged = true; - _dynamicChanged = true; - _collisionSoundURLChanged = true; - _actionDataChanged = true; - - // Cloning - _cloneableChanged = true; - _cloneLifetimeChanged = true; - _cloneLimitChanged = true; - _cloneDynamicChanged = true; - _cloneAvatarEntityChanged = true; - _cloneOriginIDChanged = true; - - // Scripts - _scriptChanged = true; - _scriptTimestampChanged = true; - _serverScriptsChanged = true; - - // Common - _shapeTypeChanged = true; - _compoundShapeURLChanged = true; - _colorChanged = true; - _alphaChanged = true; - _unlitChanged = true; - _pulse.markAllChanged(); - _texturesChanged = true; - - // Particles - _maxParticlesChanged = true; - _lifespanChanged = true; - _isEmittingChanged = true; - _emitRateChanged = true; - _emitSpeedChanged = true; - _speedSpreadChanged = true; - _emitOrientationChanged = true; - _emitDimensionsChanged = true; - _emitRadiusStartChanged = true; - _polarStartChanged = true; - _polarFinishChanged = true; - _azimuthStartChanged = true; - _azimuthFinishChanged = true; - _emitAccelerationChanged = true; - _accelerationSpreadChanged = true; - _particleRadiusChanged = true; - _radiusSpreadChanged = true; - _radiusStartChanged = true; - _radiusFinishChanged = true; - _colorSpreadChanged = true; - _colorStartChanged = true; - _colorFinishChanged = true; - _alphaSpreadChanged = true; - _alphaStartChanged = true; - _alphaFinishChanged = true; - _emitterShouldTrailChanged = true; - _particleSpinChanged = true; - _spinStartChanged = true; - _spinFinishChanged = true; - _spinSpreadChanged = true; - _rotateWithEntityChanged = true; - - // Procedural Particles - _numParticlesChanged = true; - _numTrianglesPerParticleChanged = true; - _numUpdatePropsChanged = true; - _particleTransparentChanged = true; - _particleUpdateDataChanged = true; - _particleRenderDataChanged = true; - - // Model - _modelURLChanged = true; - _modelScaleChanged = true; - _jointRotationsSetChanged = true; - _jointRotationsChanged = true; - _jointTranslationsSetChanged = true; - _jointTranslationsChanged = true; - _relayParentJointsChanged = true; - _groupCulledChanged = true; - _blendshapeCoefficientsChanged = true; - _useOriginalPivotChanged = true; - _loadPriorityChanged = true; - _animation.markAllChanged(); - - // Light - _isSpotlightChanged = true; - _intensityChanged = true; - _exponentChanged = true; - _cutoffChanged = true; - _falloffRadiusChanged = true; - - // Text - _textChanged = true; - _lineHeightChanged = true; - _textColorChanged = true; - _textAlphaChanged = true; - _backgroundColorChanged = true; - _backgroundAlphaChanged = true; - _leftMarginChanged = true; - _rightMarginChanged = true; - _topMarginChanged = true; - _bottomMarginChanged = true; - _fontChanged = true; - _textEffectChanged = true; - _textEffectColorChanged = true; - _textEffectThicknessChanged = true; - _alignmentChanged = true; - - // Zone - _keyLight.markAllChanged(); - _ambientLight.markAllChanged(); - _skybox.markAllChanged(); - _haze.markAllChanged(); - _bloom.markAllChanged(); - _audio.markAllChanged(); - _tonemapping.markAllChanged(); - _ambientOcclusion.markAllChanged(); - _flyingAllowedChanged = true; - _ghostingAllowedChanged = true; - _filterURLChanged = true; - _keyLightModeChanged = true; - _ambientLightModeChanged = true; - _skyboxModeChanged = true; - _hazeModeChanged = true; - _bloomModeChanged = true; - _avatarPriorityChanged = true; - _screenshareChanged = true; - _tonemappingModeChanged = true; - _ambientOcclusionModeChanged = true; - - // Polyvox - _voxelVolumeSizeChanged = true; - _voxelDataChanged = true; - _voxelSurfaceStyleChanged = true; - _xTextureURLChanged = true; - _yTextureURLChanged = true; - _zTextureURLChanged = true; - _xNNeighborIDChanged = true; - _yNNeighborIDChanged = true; - _zNNeighborIDChanged = true; - _xPNeighborIDChanged = true; - _yPNeighborIDChanged = true; - _zPNeighborIDChanged = true; - - // Web - _sourceUrlChanged = true; - _dpiChanged = true; - _scriptURLChanged = true; - _maxFPSChanged = true; - _inputModeChanged = true; - _wantsKeyboardFocusChanged = true; - _showKeyboardFocusHighlightChanged = true; - _useBackgroundChanged = true; - _userAgentChanged = true; - - // Polyline - _linePointsChanged = true; - _strokeWidthsChanged = true; - _normalsChanged = true; - _strokeColorsChanged = true; - _isUVModeStretchChanged = true; - _glowChanged = true; - _faceCameraChanged = true; - - // Shape - _shapeChanged = true; - - // Material - _materialURLChanged = true; - _materialMappingModeChanged = true; - _priorityChanged = true; - _parentMaterialNameChanged = true; - _materialMappingPosChanged = true; - _materialMappingScaleChanged = true; - _materialMappingRotChanged = true; - _materialDataChanged = true; - _materialRepeatChanged = true; - - // Image - _imageURLChanged = true; - _emissiveChanged = true; - _keepAspectRatioChanged = true; - _subImageChanged = true; - - // Grid - _followCameraChanged = true; - _majorGridEveryChanged = true; - _minorGridEveryChanged = true; - - // Gizmo - _gizmoTypeChanged = true; - _ring.markAllChanged(); - - // Sound - _soundURLChanged = true; - _volumeChanged = true; - _timeOffsetChanged = true; - _pitchChanged = true; - _playingChanged = true; - _loopChanged = true; - _positionalChanged = true; - _localOnlyChanged = true; -} - -// The minimum bounding box for the entity. -AABox EntityItemProperties::getAABox() const { - - // _position represents the position of the registration point. - vec3 registrationRemainder = vec3(1.0f) - _registrationPoint; - - vec3 unrotatedMinRelativeToEntity = -(_dimensions * _registrationPoint); - vec3 unrotatedMaxRelativeToEntity = _dimensions * registrationRemainder; - Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity }; - Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(_rotation); - - // shift the extents to be relative to the position/registration point - rotatedExtentsRelativeToRegistrationPoint.shiftBy(_position); - - return AABox(rotatedExtentsRelativeToRegistrationPoint); -} - -bool EntityItemProperties::hasTransformOrVelocityChanges() const { - return _positionChanged || _localPositionChanged - || _rotationChanged || _localRotationChanged - || _velocityChanged || _localVelocityChanged - || _angularVelocityChanged || _localAngularVelocityChanged - || _accelerationChanged; -} - -void EntityItemProperties::clearTransformOrVelocityChanges() { - _positionChanged = false; - _localPositionChanged = false; - _rotationChanged = false; - _localRotationChanged = false; - _velocityChanged = false; - _localVelocityChanged = false; - _angularVelocityChanged = false; - _localAngularVelocityChanged = false; - _accelerationChanged = false; -} - -bool EntityItemProperties::hasMiscPhysicsChanges() const { - return _gravityChanged || _dimensionsChanged || _densityChanged || _frictionChanged - || _restitutionChanged || _dampingChanged || _angularDampingChanged || _registrationPointChanged || - _compoundShapeURLChanged || _dynamicChanged || _collisionlessChanged || _collisionMaskChanged; -} - -bool EntityItemProperties::hasSimulationRestrictedChanges() const { - return _positionChanged || _localPositionChanged - || _rotationChanged || _localRotationChanged - || _velocityChanged || _localVelocityChanged - || _localDimensionsChanged || _dimensionsChanged - || _angularVelocityChanged || _localAngularVelocityChanged - || _accelerationChanged - || _parentIDChanged || _parentJointIndexChanged; -} - -void EntityItemProperties::copySimulationRestrictedProperties(const EntityItemPointer& entity) { - if (!_parentIDChanged) { - setParentID(entity->getParentID()); - } - if (!_parentJointIndexChanged) { - setParentJointIndex(entity->getParentJointIndex()); - } - if (!_localPositionChanged && !_positionChanged) { - setPosition(entity->getWorldPosition()); - } - if (!_localRotationChanged && !_rotationChanged) { - setRotation(entity->getWorldOrientation()); - } - if (!_localVelocityChanged && !_velocityChanged) { - setVelocity(entity->getWorldVelocity()); - } - if (!_localAngularVelocityChanged && !_angularVelocityChanged) { - setAngularVelocity(entity->getWorldAngularVelocity()); - } - if (!_accelerationChanged) { - setAcceleration(entity->getAcceleration()); - } - if (!_localDimensionsChanged && !_dimensionsChanged) { - setLocalDimensions(entity->getScaledDimensions()); - } -} - -void EntityItemProperties::clearSimulationRestrictedProperties() { - _positionChanged = false; - _localPositionChanged = false; - _rotationChanged = false; - _localRotationChanged = false; - _velocityChanged = false; - _localVelocityChanged = false; - _angularVelocityChanged = false; - _localAngularVelocityChanged = false; - _accelerationChanged = false; - _parentIDChanged = false; - _parentJointIndexChanged = false; -} - -void EntityItemProperties::clearSimulationOwner() { - _simulationOwner.clear(); - _simulationOwnerChanged = true; -} - -void EntityItemProperties::setSimulationOwner(const QUuid& id, uint8_t priority) { - if (!_simulationOwner.matchesValidID(id) || _simulationOwner.getPriority() != priority) { - _simulationOwner.set(id, priority); - _simulationOwnerChanged = true; - } -} - -void EntityItemProperties::setSimulationOwner(const QByteArray& data) { - if (_simulationOwner.fromByteArray(data)) { - _simulationOwnerChanged = true; - } -} - -uint8_t EntityItemProperties::computeSimulationBidPriority() const { - uint8_t priority = 0; - if (_parentIDChanged || _parentJointIndexChanged) { - // we need higher simulation ownership priority to chang parenting info - priority = SCRIPT_GRAB_SIMULATION_PRIORITY; - } else if (_positionChanged || _localPositionChanged - || _rotationChanged || _localRotationChanged - || _velocityChanged || _localVelocityChanged - || _angularVelocityChanged || _localAngularVelocityChanged) { - priority = SCRIPT_POKE_SIMULATION_PRIORITY; - } - return priority; -} - -QList EntityItemProperties::listChangedProperties() { - QList out; - - // Core - if (simulationOwnerChanged()) { - out += "simulationOwner"; - } - if (parentIDChanged()) { - out += "parentID"; - } - if (parentJointIndexChanged()) { - out += "parentJointIndex"; - } - if (visibleChanged()) { - out += "visible"; - } - if (nameChanged()) { - out += "name"; - } - if (lockedChanged()) { - out += "locked"; - } - if (userDataChanged()) { - out += "userData"; - } - if (privateUserDataChanged()) { - out += "privateUserData"; - } - if (hrefChanged()) { - out += "href"; - } - if (descriptionChanged()) { - out += "description"; - } - if (containsPositionChange()) { - out += "position"; - } - if (dimensionsChanged()) { - out += "dimensions"; - } - if (rotationChanged()) { - out += "rotation"; - } - if (registrationPointChanged()) { - out += "registrationPoint"; - } - if (createdChanged()) { - out += "created"; - } - if (lastEditedByChanged()) { - out += "lastEditedBy"; - } - if (entityHostTypeChanged()) { - out += "entityHostType"; - } - if (owningAvatarIDChanged()) { - out += "owningAvatarID"; - } - if (queryAACubeChanged()) { - out += "queryAACube"; - } - if (canCastShadowChanged()) { - out += "canCastShadow"; - } - if (isVisibleInSecondaryCameraChanged()) { - out += "isVisibleInSecondaryCamera"; - } - if (renderLayerChanged()) { - out += "renderLayer"; - } - if (primitiveModeChanged()) { - out += "primitiveMode"; - } - if (ignorePickIntersectionChanged()) { - out += "ignorePickIntersection"; - } - if (renderWithZonesChanged()) { - out += "renderWithZones"; - } - if (billboardModeChanged()) { - out += "billboardMode"; - } - if (tagsChanged()) { - out += "tags"; - } - getGrab().listChangedProperties(out); - if (mirrorModeChanged()) { - out += "mirrorMode"; - } - if (portalExitIDChanged()) { - out += "portalExitID"; - } - - // Physics - if (densityChanged()) { - out += "density"; - } - if (velocityChanged()) { - out += "velocity"; - } - if (angularVelocityChanged()) { - out += "angularVelocity"; - } - if (gravityChanged()) { - out += "gravity"; - } - if (accelerationChanged()) { - out += "acceleration"; - } - if (dampingChanged()) { - out += "damping"; - } - if (angularDampingChanged()) { - out += "angularDamping"; - } - if (restitutionChanged()) { - out += "restitution"; - } - if (frictionChanged()) { - out += "friction"; - } - if (lifetimeChanged()) { - out += "lifetime"; - } - if (collisionlessChanged()) { - out += "collisionless"; - } - if (collisionMaskChanged()) { - out += "collisionMask"; - } - if (dynamicChanged()) { - out += "dynamic"; - } - if (collisionSoundURLChanged()) { - out += "collisionSoundURL"; - } - if (actionDataChanged()) { - out += "actionData"; - } - - // Cloning - if (cloneableChanged()) { - out += "cloneable"; - } - if (cloneLifetimeChanged()) { - out += "cloneLifetime"; - } - if (cloneLimitChanged()) { - out += "cloneLimit"; - } - if (cloneDynamicChanged()) { - out += "cloneDynamic"; - } - if (cloneAvatarEntityChanged()) { - out += "cloneAvatarEntity"; - } - if (cloneOriginIDChanged()) { - out += "cloneOriginID"; - } - - // Scripts - if (scriptChanged()) { - out += "script"; - } - if (scriptTimestampChanged()) { - out += "scriptTimestamp"; - } - if (serverScriptsChanged()) { - out += "serverScripts"; - } - - // Common - if (shapeTypeChanged()) { - out += "shapeType"; - } - if (compoundShapeURLChanged()) { - out += "compoundShapeURL"; - } - if (colorChanged()) { - out += "color"; - } - if (alphaChanged()) { - out += "alpha"; - } - if (unlitChanged()) { - out += "unlit"; - } - getPulse().listChangedProperties(out); - if (texturesChanged()) { - out += "textures"; - } - - // Particles - if (maxParticlesChanged()) { - out += "maxParticles"; - } - if (lifespanChanged()) { - out += "lifespan"; - } - if (isEmittingChanged()) { - out += "isEmitting"; - } - if (emitRateChanged()) { - out += "emitRate"; - } - if (emitSpeedChanged()) { - out += "emitSpeed"; - } - if (speedSpreadChanged()) { - out += "speedSpread"; - } - if (emitOrientationChanged()) { - out += "emitOrientation"; - } - if (emitDimensionsChanged()) { - out += "emitDimensions"; - } - if (emitRadiusStartChanged()) { - out += "emitRadiusStart"; - } - if (polarStartChanged()) { - out += "polarStart"; - } - if (polarFinishChanged()) { - out += "polarFinish"; - } - if (azimuthStartChanged()) { - out += "azimuthStart"; - } - if (azimuthFinishChanged()) { - out += "azimuthFinish"; - } - if (emitAccelerationChanged()) { - out += "emitAcceleration"; - } - if (accelerationSpreadChanged()) { - out += "accelerationSpread"; - } - if (particleRadiusChanged()) { - out += "particleRadius"; - } - if (radiusSpreadChanged()) { - out += "radiusSpread"; - } - if (radiusStartChanged()) { - out += "radiusStart"; - } - if (radiusFinishChanged()) { - out += "radiusFinish"; - } - if (colorSpreadChanged()) { - out += "colorSpread"; - } - if (colorStartChanged()) { - out += "colorStart"; - } - if (colorFinishChanged()) { - out += "colorFinish"; - } - if (alphaSpreadChanged()) { - out += "alphaSpread"; - } - if (alphaStartChanged()) { - out += "alphaStart"; - } - if (alphaFinishChanged()) { - out += "alphaFinish"; - } - if (emitterShouldTrailChanged()) { - out += "emitterShouldTrail"; - } - if (particleSpinChanged()) { - out += "particleSpin"; - } - if (spinSpreadChanged()) { - out += "spinSpread"; - } - if (spinStartChanged()) { - out += "spinStart"; - } - if (spinFinishChanged()) { - out += "spinFinish"; - } - if (rotateWithEntityChanged()) { - out += "rotateWithEntity"; - } - - // Procedural Particles - if (numParticlesChanged()) { - out += "numParticles"; - } - if (numTrianglesPerParticleChanged()) { - out += "numTrianglesPerParticle"; - } - if (numUpdatePropsChanged()) { - out += "numUpdateProps"; - } - if (particleTransparentChanged()) { - out += "particleTransparent"; - } - if (particleUpdateDataChanged()) { - out += "particleUpdateData"; - } - if (particleRenderDataChanged()) { - out += "particleRenderData"; - } - - // Model - if (modelURLChanged()) { - out += "modelURL"; - } - if (modelScaleChanged()) { - out += "scale"; - } - if (jointRotationsSetChanged()) { - out += "jointRotationsSet"; - } - if (jointRotationsChanged()) { - out += "jointRotations"; - } - if (jointTranslationsSetChanged()) { - out += "jointTranslationsSet"; - } - if (jointTranslationsChanged()) { - out += "jointTranslations"; - } - if (relayParentJointsChanged()) { - out += "relayParentJoints"; - } - if (groupCulledChanged()) { - out += "groupCulled"; - } - if (blendshapeCoefficientsChanged()) { - out += "blendshapeCoefficients"; - } - if (useOriginalPivotChanged()) { - out += "useOriginalPivot"; - } - if (loadPriorityChanged()) { - out += "loadPriority"; - } - getAnimation().listChangedProperties(out); - - // Light - if (isSpotlightChanged()) { - out += "isSpotlight"; - } - if (intensityChanged()) { - out += "intensity"; - } - if (exponentChanged()) { - out += "exponent"; - } - if (cutoffChanged()) { - out += "cutoff"; - } - if (falloffRadiusChanged()) { - out += "falloffRadius"; - } - - // Text - if (textChanged()) { - out += "text"; - } - if (lineHeightChanged()) { - out += "lineHeight"; - } - if (textColorChanged()) { - out += "textColor"; - } - if (textAlphaChanged()) { - out += "textAlpha"; - } - if (backgroundColorChanged()) { - out += "backgroundColor"; - } - if (backgroundAlphaChanged()) { - out += "backgroundAlpha"; - } - if (leftMarginChanged()) { - out += "leftMargin"; - } - if (rightMarginChanged()) { - out += "rightMargin"; - } - if (topMarginChanged()) { - out += "topMargin"; - } - if (bottomMarginChanged()) { - out += "bottomMargin"; - } - if (fontChanged()) { - out += "font"; - } - if (textEffectChanged()) { - out += "textEffect"; - } - if (textEffectColorChanged()) { - out += "textEffectColor"; - } - if (textEffectThicknessChanged()) { - out += "textEffectThickness"; - } - if (alignmentChanged()) { - out += "alignment"; - } - - // Zone - getKeyLight().listChangedProperties(out); - getAmbientLight().listChangedProperties(out); - getSkybox().listChangedProperties(out); - getHaze().listChangedProperties(out); - getBloom().listChangedProperties(out); - getAudio().listChangedProperties(out); - getTonemapping().listChangedProperties(out); - getAmbientOcclusion().listChangedProperties(out); - if (flyingAllowedChanged()) { - out += "flyingAllowed"; - } - if (ghostingAllowedChanged()) { - out += "ghostingAllowed"; - } - if (filterURLChanged()) { - out += "filterURL"; - } - if (keyLightModeChanged()) { - out += "keyLightMode"; - } - if (ambientLightModeChanged()) { - out += "ambientLightMode"; - } - if (skyboxModeChanged()) { - out += "skyboxMode"; - } - if (hazeModeChanged()) { - out += "hazeMode"; - } - if (bloomModeChanged()) { - out += "bloomMode"; - } - if (avatarPriorityChanged()) { - out += "avatarPriority"; - } - if (screenshareChanged()) { - out += "screenshare"; - } - if (tonemappingModeChanged()) { - out += "tonemappingMode"; - } - if (ambientOcclusionModeChanged()) { - out += "ambientOcclusionMode"; - } - - // Polyvox - if (voxelVolumeSizeChanged()) { - out += "voxelVolumeSize"; - } - if (voxelDataChanged()) { - out += "voxelData"; - } - if (voxelSurfaceStyleChanged()) { - out += "voxelSurfaceStyle"; - } - if (xTextureURLChanged()) { - out += "xTextureURL"; - } - if (yTextureURLChanged()) { - out += "yTextureURL"; - } - if (zTextureURLChanged()) { - out += "zTextureURL"; - } - if (xNNeighborIDChanged()) { - out += "xNNeighborID"; - } - if (yNNeighborIDChanged()) { - out += "yNNeighborID"; - } - if (zNNeighborIDChanged()) { - out += "zNNeighborID"; - } - if (xPNeighborIDChanged()) { - out += "xPNeighborID"; - } - if (yPNeighborIDChanged()) { - out += "yPNeighborID"; - } - if (zPNeighborIDChanged()) { - out += "zPNeighborID"; - } - - // Web - if (sourceUrlChanged()) { - out += "sourceUrl"; - } - if (dpiChanged()) { - out += "dpi"; - } - if (scriptURLChanged()) { - out += "scriptURL"; - } - if (maxFPSChanged()) { - out += "maxFPS"; - } - if (inputModeChanged()) { - out += "inputMode"; - } - - // Polyline - if (linePointsChanged()) { - out += "linePoints"; - } - if (strokeWidthsChanged()) { - out += "strokeWidths"; - } - if (normalsChanged()) { - out += "normals"; - } - if (strokeColorsChanged()) { - out += "strokeColors"; - } - if (isUVModeStretchChanged()) { - out += "isUVModeStretch"; - } - if (glowChanged()) { - out += "glow"; - } - if (faceCameraChanged()) { - out += "faceCamera"; - } - if (wantsKeyboardFocusChanged()) { - out += "wantsKeyboardFocus"; - } - if (showKeyboardFocusHighlightChanged()) { - out += "showKeyboardFocusHighlight"; - } - if (useBackgroundChanged()) { - out += "useBackground"; - } - if (userAgentChanged()) { - out += "userAgent"; - } - - // Shape - if (shapeChanged()) { - out += "shape"; - } - - // Material - if (materialURLChanged()) { - out += "materialURL"; - } - if (materialMappingModeChanged()) { - out += "materialMappingMode"; - } - if (priorityChanged()) { - out += "priority"; - } - if (parentMaterialNameChanged()) { - out += "parentMaterialName"; - } - if (materialMappingPosChanged()) { - out += "materialMappingPos"; - } - if (materialMappingScaleChanged()) { - out += "materialMappingScale"; - } - if (materialMappingRotChanged()) { - out += "materialMappingRot"; - } - if (materialDataChanged()) { - out += "materialData"; - } - if (materialRepeatChanged()) { - out += "materialRepeat"; - } - - // Image - if (imageURLChanged()) { - out += "imageURL"; - } - if (emissiveChanged()) { - out += "emissive"; - } - if (keepAspectRatioChanged()) { - out += "keepAspectRatio"; - } - if (subImageChanged()) { - out += "subImage"; - } - - // Grid - if (followCameraChanged()) { - out += "followCamera"; - } - if (majorGridEveryChanged()) { - out += "majorGridEvery"; - } - if (minorGridEveryChanged()) { - out += "minorGridEvery"; - } - - // Gizmo - if (gizmoTypeChanged()) { - out += "gizmoType"; - } - getRing().listChangedProperties(out); - - // Sound - if (soundURLChanged()) { - out += "soundURL"; - } - if (volumeChanged()) { - out += "volume"; - } - if (timeOffsetChanged()) { - out += "timeOffset"; - } - if (pitchChanged()) { - out += "pitch"; - } - if (playingChanged()) { - out += "playing"; - } - if (loopChanged()) { - out += "loop"; - } - if (positionalChanged()) { - out += "positional"; - } - if (localOnlyChanged()) { - out += "localOnly"; - } - - return out; -} - -bool EntityItemProperties::transformChanged() const { - return positionChanged() || rotationChanged() || - localPositionChanged() || localRotationChanged(); -} - -bool EntityItemProperties::getScalesWithParent() const { - // keep this logic the same as in EntityItem::getScalesWithParent - bool scalesWithParent { false }; - if (parentIDChanged()) { - bool success; - SpatiallyNestablePointer parent = SpatiallyNestable::findByID(getParentID(), success); - if (success && parent) { - bool avatarAncestor = (parent->getNestableType() == NestableType::Avatar || - parent->hasAncestorOfType(NestableType::Avatar)); - scalesWithParent = getEntityHostType() == entity::HostType::AVATAR && avatarAncestor; - } - } - return scalesWithParent; -} - -bool EntityItemProperties::parentRelatedPropertyChanged() const { - return positionChanged() || rotationChanged() || - localPositionChanged() || localRotationChanged() || - localDimensionsChanged() || - parentIDChanged() || parentJointIndexChanged(); -} - -bool EntityItemProperties::queryAACubeRelatedPropertyChanged() const { - return parentRelatedPropertyChanged() || dimensionsChanged(); -} - -bool EntityItemProperties::grabbingRelatedPropertyChanged() const { - const GrabPropertyGroup& grabProperties = getGrab(); - return grabProperties.triggerableChanged() || grabProperties.grabbableChanged() || - grabProperties.grabFollowsControllerChanged() || grabProperties.grabKinematicChanged() || - grabProperties.equippableChanged() || grabProperties.equippableLeftPositionChanged() || - grabProperties.equippableRightPositionChanged() || grabProperties.equippableLeftRotationChanged() || - grabProperties.equippableRightRotationChanged() || grabProperties.equippableIndicatorURLChanged() || - grabProperties.equippableIndicatorScaleChanged() || grabProperties.equippableIndicatorOffsetChanged(); -} - -void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityIDToClone) { - setName(getName() + "-clone-" + entityIDToClone.toString()); - setLocked(false); - setParentID(QUuid()); - setParentJointIndex(-1); - setLifetime(getCloneLifetime()); - setDynamic(getCloneDynamic()); - if (getEntityHostType() != entity::HostType::LOCAL) { - setEntityHostType(getCloneAvatarEntity() ? entity::HostType::AVATAR : entity::HostType::DOMAIN); - } else { - // Local Entities clone as local entities - setEntityHostType(entity::HostType::LOCAL); - setCollisionless(true); - } - uint64_t now = usecTimestampNow(); - setCreated(now); - setLastEdited(now); - setCloneable(ENTITY_ITEM_DEFAULT_CLONEABLE); - setCloneLifetime(ENTITY_ITEM_DEFAULT_CLONE_LIFETIME); - setCloneLimit(ENTITY_ITEM_DEFAULT_CLONE_LIMIT); - setCloneDynamic(ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC); - setCloneAvatarEntity(ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); -} - -bool EntityItemProperties::blobToProperties(ScriptEngine& scriptEngine, const QByteArray& blob, EntityItemProperties& properties) { - // DANGER: this method is NOT efficient. - // begin recipe for converting unfortunately-formatted-binary-blob to EntityItemProperties - OVERTE_IGNORE_DEPRECATED_BEGIN - QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(blob); - OVERTE_IGNORE_DEPRECATED_END - if (jsonProperties.isEmpty() || jsonProperties.isNull() || !jsonProperties.isObject() || jsonProperties.object().isEmpty()) { - qCDebug(entities) << "bad avatarEntityData json" << QString(blob.toHex()); - return false; - } - QVariant variant = jsonProperties.toVariant(); - QVariantMap variantMap = variant.toMap(); - ScriptValue scriptValue = variantMapToScriptValue(variantMap, scriptEngine); - EntityItemPropertiesFromScriptValueIgnoreReadOnly(scriptValue, properties); - // end recipe - return true; -} - -void EntityItemProperties::propertiesToBlob(ScriptEngine& scriptEngine, const QUuid& myAvatarID, - const EntityItemProperties& properties, QByteArray& blob, bool allProperties) { - // DANGER: this method is NOT efficient. - // begin recipe for extracting unfortunately-formatted-binary-blob from EntityItem - ScriptValue scriptValue = allProperties - ? EntityItemPropertiesToScriptValue(&scriptEngine, properties) - : EntityItemNonDefaultPropertiesToScriptValue(&scriptEngine, properties); - QVariant variantProperties = scriptValue.toVariant(); - QJsonDocument jsonProperties = QJsonDocument::fromVariant(variantProperties); - // the ID of the parent/avatar changes from session to session. use a special UUID to indicate the avatar - QJsonObject jsonObject = jsonProperties.object(); - if (jsonObject.contains("parentID")) { - if (QUuid(jsonObject["parentID"].toString()) == myAvatarID) { - jsonObject["parentID"] = AVATAR_SELF_ID.toString(); - } - } - jsonProperties = QJsonDocument(jsonObject); - OVERTE_IGNORE_DEPRECATED_BEGIN - blob = jsonProperties.toBinaryData(); - OVERTE_IGNORE_DEPRECATED_END - // end recipe -} - -QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f) { - QString result = "[ "; - - for (int i = 0; i < PROP_AFTER_LAST_ITEM; i++) { - auto prop = EntityPropertyList(i); - if (f.getHasProperty(prop)) { - result = result + _enumsToPropertyStrings[prop] + " "; - } - } - - result += "]"; - dbg.nospace() << result; - return dbg; -} diff --git a/libraries/entities/src/EntityItemProperties.cpp.in b/libraries/entities/src/EntityItemProperties.cpp.in new file mode 100644 index 0000000000..bc8804c396 --- /dev/null +++ b/libraries/entities/src/EntityItemProperties.cpp.in @@ -0,0 +1,1446 @@ +// +// EntityItemProperties.cpp +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. +// Copyright 2022-2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "EntityItemProperties.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "EntitiesLogging.h" +#include "WarningsSuppression.h" + +@ENTITY_ITEM_PROPERTY_STATIC_GROUPS@ + +EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1); + +EntityItemProperties::EntityItemProperties(EntityPropertyFlags desiredProperties) : + _id(UNKNOWN_ENTITY_ID), + _idSet(false), + _lastEdited(0), + _type(EntityTypes::Unknown), + + _defaultSettings(true), + _naturalDimensions(1.0f, 1.0f, 1.0f), + _naturalPosition(0.0f, 0.0f, 0.0f), + _desiredProperties(desiredProperties) +{ + +} + +void EntityItemProperties::calculateNaturalPosition(const vec3& min, const vec3& max) { + vec3 halfDimension = (max - min) / 2.0f; + _naturalPosition = max - halfDimension; +} + +void EntityItemProperties::debugDump() const { + qCDebug(entities) << "EntityItemProperties..."; + qCDebug(entities) << " _type=" << EntityTypes::getEntityTypeName(_type); + qCDebug(entities) << " _id=" << _id; + qCDebug(entities) << " _idSet=" << _idSet; + qCDebug(entities) << " _position=" << _position.x << "," << _position.y << "," << _position.z; + qCDebug(entities) << " _dimensions=" << getDimensions(); + qCDebug(entities) << " _modelURL=" << _modelURL; + qCDebug(entities) << " _compoundShapeURL=" << _compoundShapeURL; + +@ENTITY_ITEM_PROPERTY_DEBUG_DUMP_GROUPS@ + + qCDebug(entities) << " changed properties..."; + EntityPropertyFlags props = getChangedProperties(); + props.debugDumpBits(); +} + +void EntityItemProperties::setLastEdited(quint64 usecTime) { + _lastEdited = usecTime > _created ? usecTime : _created; +} + +bool EntityItemProperties::constructFromBuffer(const unsigned char* data, int dataLength) { + ReadBitstreamToTreeParams args; + EntityItemPointer tempEntity = EntityTypes::constructEntityItem(data, dataLength); + if (!tempEntity) { + return false; + } + tempEntity->readEntityDataFromBuffer(data, dataLength, args); + (*this) = tempEntity->getProperties(); + return true; +} + +inline void addEntityShape(QHash& lookup, EntityShape type) { lookup[EntityShapeHelpers::getNameForEntityShape(type)] = type; } +QHash stringToEntityShapeLookup = [] { + QHash toReturn; + addEntityShape(toReturn, EntityShape::Triangle); + addEntityShape(toReturn, EntityShape::Quad); + addEntityShape(toReturn, EntityShape::Hexagon); + addEntityShape(toReturn, EntityShape::Octagon); + addEntityShape(toReturn, EntityShape::Circle); + addEntityShape(toReturn, EntityShape::Cube); + addEntityShape(toReturn, EntityShape::Sphere); + addEntityShape(toReturn, EntityShape::Tetrahedron); + addEntityShape(toReturn, EntityShape::Octahedron); + addEntityShape(toReturn, EntityShape::Dodecahedron); + addEntityShape(toReturn, EntityShape::Icosahedron); + addEntityShape(toReturn, EntityShape::Torus); + addEntityShape(toReturn, EntityShape::Cone); + addEntityShape(toReturn, EntityShape::Cylinder); + return toReturn; +}(); +QString EntityItemProperties::getShapeAsString() const { return EntityShapeHelpers::getNameForEntityShape(_shape); } +void EntityItemProperties::setShapeFromString(const QString& shapeName) { + auto shapeItr = stringToEntityShapeLookup.find(shapeName); + if (shapeItr != stringToEntityShapeLookup.end()) { + _shape = shapeItr.value(); + _shapeChanged = true; + } +} + +inline void addShapeType(QHash& lookup, ShapeType type) { lookup[ShapeInfo::getNameForShapeType(type)] = type; } +QHash stringToShapeTypeLookup = [] { + QHash toReturn; + addShapeType(toReturn, SHAPE_TYPE_NONE); + addShapeType(toReturn, SHAPE_TYPE_BOX); + addShapeType(toReturn, SHAPE_TYPE_SPHERE); + addShapeType(toReturn, SHAPE_TYPE_CAPSULE_X); + addShapeType(toReturn, SHAPE_TYPE_CAPSULE_Y); + addShapeType(toReturn, SHAPE_TYPE_CAPSULE_Z); + addShapeType(toReturn, SHAPE_TYPE_CYLINDER_X); + addShapeType(toReturn, SHAPE_TYPE_CYLINDER_Y); + addShapeType(toReturn, SHAPE_TYPE_CYLINDER_Z); + addShapeType(toReturn, SHAPE_TYPE_HULL); + addShapeType(toReturn, SHAPE_TYPE_PLANE); + addShapeType(toReturn, SHAPE_TYPE_COMPOUND); + addShapeType(toReturn, SHAPE_TYPE_SIMPLE_HULL); + addShapeType(toReturn, SHAPE_TYPE_SIMPLE_COMPOUND); + addShapeType(toReturn, SHAPE_TYPE_STATIC_MESH); + addShapeType(toReturn, SHAPE_TYPE_ELLIPSOID); + addShapeType(toReturn, SHAPE_TYPE_CIRCLE); + return toReturn; +}(); +QString EntityItemProperties::getShapeTypeAsString() const { return ShapeInfo::getNameForShapeType(_shapeType); } +void EntityItemProperties::setShapeTypeFromString(const QString& shapeName) { + auto shapeTypeItr = stringToShapeTypeLookup.find(shapeName.toLower()); + if (shapeTypeItr != stringToShapeTypeLookup.end()) { + _shapeType = shapeTypeItr.value(); + _shapeTypeChanged = true; + } +} + +inline void addMaterialMappingMode(QHash& lookup, MaterialMappingMode mode) { lookup[MaterialMappingModeHelpers::getNameForMaterialMappingMode(mode)] = mode; } +const QHash stringToMaterialMappingModeLookup = [] { + QHash toReturn; + addMaterialMappingMode(toReturn, UV); + addMaterialMappingMode(toReturn, PROJECTED); + return toReturn; +}(); +QString EntityItemProperties::getMaterialMappingModeAsString() const { return MaterialMappingModeHelpers::getNameForMaterialMappingMode(_materialMappingMode); } +void EntityItemProperties::setMaterialMappingModeFromString(const QString& materialMappingMode) { + auto materialMappingModeItr = stringToMaterialMappingModeLookup.find(materialMappingMode.toLower()); + if (materialMappingModeItr != stringToMaterialMappingModeLookup.end()) { + _materialMappingMode = materialMappingModeItr.value(); + _materialMappingModeChanged = true; + } +} + +inline void addBillboardMode(QHash& lookup, BillboardMode mode) { lookup[BillboardModeHelpers::getNameForBillboardMode(mode)] = mode; } +const QHash stringToBillboardModeLookup = [] { + QHash toReturn; + addBillboardMode(toReturn, BillboardMode::NONE); + addBillboardMode(toReturn, BillboardMode::YAW); + addBillboardMode(toReturn, BillboardMode::FULL); + return toReturn; +}(); +QString EntityItemProperties::getBillboardModeAsString() const { return BillboardModeHelpers::getNameForBillboardMode(_billboardMode); } +void EntityItemProperties::setBillboardModeFromString(const QString& billboardMode) { + auto billboardModeItr = stringToBillboardModeLookup.find(billboardMode.toLower()); + if (billboardModeItr != stringToBillboardModeLookup.end()) { + _billboardMode = billboardModeItr.value(); + _billboardModeChanged = true; + } +} + +inline void addRenderLayer(QHash& lookup, RenderLayer mode) { lookup[RenderLayerHelpers::getNameForRenderLayer(mode)] = mode; } +const QHash stringToRenderLayerLookup = [] { + QHash toReturn; + addRenderLayer(toReturn, RenderLayer::WORLD); + addRenderLayer(toReturn, RenderLayer::FRONT); + addRenderLayer(toReturn, RenderLayer::HUD); + return toReturn; +}(); +QString EntityItemProperties::getRenderLayerAsString() const { return RenderLayerHelpers::getNameForRenderLayer(_renderLayer); } +void EntityItemProperties::setRenderLayerFromString(const QString& renderLayer) { + auto renderLayerItr = stringToRenderLayerLookup.find(renderLayer.toLower()); + if (renderLayerItr != stringToRenderLayerLookup.end()) { + _renderLayer = renderLayerItr.value(); + _renderLayerChanged = true; + } +} + +inline void addPrimitiveMode(QHash& lookup, PrimitiveMode mode) { lookup[PrimitiveModeHelpers::getNameForPrimitiveMode(mode)] = mode; } +const QHash stringToPrimitiveModeLookup = [] { + QHash toReturn; + addPrimitiveMode(toReturn, PrimitiveMode::SOLID); + addPrimitiveMode(toReturn, PrimitiveMode::LINES); + return toReturn; +}(); +QString EntityItemProperties::getPrimitiveModeAsString() const { return PrimitiveModeHelpers::getNameForPrimitiveMode(_primitiveMode); } +void EntityItemProperties::setPrimitiveModeFromString(const QString& primitiveMode) { + auto primitiveModeItr = stringToPrimitiveModeLookup.find(primitiveMode.toLower()); + if (primitiveModeItr != stringToPrimitiveModeLookup.end()) { + _primitiveMode = primitiveModeItr.value(); + _primitiveModeChanged = true; + } +} + +inline void addWebInputMode(QHash& lookup, WebInputMode mode) { lookup[WebInputModeHelpers::getNameForWebInputMode(mode)] = mode; } +const QHash stringToWebInputModeLookup = [] { + QHash toReturn; + addWebInputMode(toReturn, WebInputMode::TOUCH); + addWebInputMode(toReturn, WebInputMode::MOUSE); + return toReturn; +}(); +QString EntityItemProperties::getInputModeAsString() const { return WebInputModeHelpers::getNameForWebInputMode(_inputMode); } +void EntityItemProperties::setInputModeFromString(const QString& webInputMode) { + auto webInputModeItr = stringToWebInputModeLookup.find(webInputMode.toLower()); + if (webInputModeItr != stringToWebInputModeLookup.end()) { + _inputMode = webInputModeItr.value(); + _inputModeChanged = true; + } +} + +inline void addGizmoType(QHash& lookup, GizmoType mode) { lookup[GizmoTypeHelpers::getNameForGizmoType(mode)] = mode; } +const QHash stringToGizmoTypeLookup = [] { + QHash toReturn; + addGizmoType(toReturn, GizmoType::RING); + return toReturn; +}(); +QString EntityItemProperties::getGizmoTypeAsString() const { return GizmoTypeHelpers::getNameForGizmoType(_gizmoType); } +void EntityItemProperties::setGizmoTypeFromString(const QString& gizmoType) { + auto gizmoTypeItr = stringToGizmoTypeLookup.find(gizmoType.toLower()); + if (gizmoTypeItr != stringToGizmoTypeLookup.end()) { + _gizmoType = gizmoTypeItr.value(); + _gizmoTypeChanged = true; + } +} + +inline void addComponentMode(QHash& lookup, ComponentMode mode) { lookup[ComponentModeHelpers::getNameForComponentMode(mode)] = mode; } +const QHash stringToComponentMode = [] { + QHash toReturn; + addComponentMode(toReturn, ComponentMode::COMPONENT_MODE_INHERIT); + addComponentMode(toReturn, ComponentMode::COMPONENT_MODE_DISABLED); + addComponentMode(toReturn, ComponentMode::COMPONENT_MODE_ENABLED); + return toReturn; +}(); +QString EntityItemProperties::getComponentModeAsString(uint32_t mode) { return ComponentModeHelpers::getNameForComponentMode((ComponentMode)mode); } +QString EntityItemProperties::getSkyboxModeAsString() const { return getComponentModeAsString(_skyboxMode); } +QString EntityItemProperties::getKeyLightModeAsString() const { return getComponentModeAsString(_keyLightMode); } +QString EntityItemProperties::getAmbientLightModeAsString() const { return getComponentModeAsString(_ambientLightMode); } +QString EntityItemProperties::getHazeModeAsString() const { return getComponentModeAsString(_hazeMode); } +QString EntityItemProperties::getBloomModeAsString() const { return getComponentModeAsString(_bloomMode); } +QString EntityItemProperties::getTonemappingModeAsString() const { return getComponentModeAsString(_tonemappingMode); } +QString EntityItemProperties::getAmbientOcclusionModeAsString() const { return getComponentModeAsString(_ambientOcclusionMode); } +void EntityItemProperties::setSkyboxModeFromString(const QString& mode) { + auto modeItr = stringToComponentMode.find(mode.toLower()); + if (modeItr != stringToComponentMode.end()) { + _skyboxMode = modeItr.value(); + _skyboxModeChanged = true; + } +} +void EntityItemProperties::setKeyLightModeFromString(const QString& mode) { + auto modeItr = stringToComponentMode.find(mode.toLower()); + if (modeItr != stringToComponentMode.end()) { + _keyLightMode = modeItr.value(); + _keyLightModeChanged = true; + } +} +void EntityItemProperties::setAmbientLightModeFromString(const QString& mode) { + auto modeItr = stringToComponentMode.find(mode.toLower()); + if (modeItr != stringToComponentMode.end()) { + _ambientLightMode = modeItr.value(); + _ambientLightModeChanged = true; + } +} +void EntityItemProperties::setHazeModeFromString(const QString& mode) { + auto modeItr = stringToComponentMode.find(mode.toLower()); + if (modeItr != stringToComponentMode.end()) { + _hazeMode = modeItr.value(); + _hazeModeChanged = true; + } +} +void EntityItemProperties::setBloomModeFromString(const QString& mode) { + auto modeItr = stringToComponentMode.find(mode.toLower()); + if (modeItr != stringToComponentMode.end()) { + _bloomMode = modeItr.value(); + _bloomModeChanged = true; + } +} +void EntityItemProperties::setTonemappingModeFromString(const QString& mode) { + auto modeItr = stringToComponentMode.find(mode.toLower()); + if (modeItr != stringToComponentMode.end()) { + _tonemappingMode = modeItr.value(); + _tonemappingModeChanged = true; + } +} +void EntityItemProperties::setAmbientOcclusionModeFromString(const QString& mode) { + auto modeItr = stringToComponentMode.find(mode.toLower()); + if (modeItr != stringToComponentMode.end()) { + _ambientOcclusionMode = modeItr.value(); + _ambientOcclusionModeChanged = true; + } +} + +inline void addAvatarPriorityMode(QHash& lookup, AvatarPriorityMode mode) { lookup[AvatarPriorityModeHelpers::getNameForAvatarPriorityMode(mode)] = mode; } +const QHash stringToAvatarPriority = [] { + QHash toReturn; + addAvatarPriorityMode(toReturn, AvatarPriorityMode::AVATAR_PRIORITY_INHERIT); + addAvatarPriorityMode(toReturn, AvatarPriorityMode::AVATAR_PRIORITY_CROWD); + addAvatarPriorityMode(toReturn, AvatarPriorityMode::AVATAR_PRIORITY_HERO); + return toReturn; +}(); +QString EntityItemProperties::getAvatarPriorityAsString() const { return AvatarPriorityModeHelpers::getNameForAvatarPriorityMode((AvatarPriorityMode)_avatarPriority); } +void EntityItemProperties::setAvatarPriorityFromString(const QString& mode) { + auto modeItr = stringToAvatarPriority.find(mode.toLower()); + if (modeItr != stringToAvatarPriority.end()) { + _avatarPriority = modeItr.value(); + _avatarPriorityChanged = true; + } +} + +QString EntityItemProperties::getScreenshareAsString() const { return getComponentModeAsString(_screenshare); } +void EntityItemProperties::setScreenshareFromString(const QString& mode) { + auto modeItr = stringToComponentMode.find(mode.toLower()); + if (modeItr != stringToComponentMode.end()) { + _screenshare = modeItr.value(); + _screenshareChanged = true; + } +} + +inline void addTextEffect(QHash& lookup, TextEffect effect) { lookup[TextEffectHelpers::getNameForTextEffect(effect)] = effect; } +const QHash stringToTextEffectLookup = [] { + QHash toReturn; + addTextEffect(toReturn, TextEffect::NO_EFFECT); + addTextEffect(toReturn, TextEffect::OUTLINE_EFFECT); + addTextEffect(toReturn, TextEffect::OUTLINE_WITH_FILL_EFFECT); + addTextEffect(toReturn, TextEffect::SHADOW_EFFECT); + return toReturn; +}(); +QString EntityItemProperties::getTextEffectAsString() const { return TextEffectHelpers::getNameForTextEffect(_textEffect); } +void EntityItemProperties::setTextEffectFromString(const QString& effect) { + auto textEffectItr = stringToTextEffectLookup.find(effect.toLower()); + if (textEffectItr != stringToTextEffectLookup.end()) { + _textEffect = textEffectItr.value(); + _textEffectChanged = true; + } +} + +inline void addTextAlignment(QHash& lookup, TextAlignment alignment) { lookup[TextAlignmentHelpers::getNameForTextAlignment(alignment)] = alignment; } +const QHash stringToTextAlignmentLookup = [] { + QHash toReturn; + addTextAlignment(toReturn, TextAlignment::LEFT); + addTextAlignment(toReturn, TextAlignment::CENTER); + addTextAlignment(toReturn, TextAlignment::RIGHT); + return toReturn; +}(); +QString EntityItemProperties::getAlignmentAsString() const { return TextAlignmentHelpers::getNameForTextAlignment(_alignment); } +void EntityItemProperties::setAlignmentFromString(const QString& alignment) { + auto textAlignmentItr = stringToTextAlignmentLookup.find(alignment.toLower()); + if (textAlignmentItr != stringToTextAlignmentLookup.end()) { + _alignment = textAlignmentItr.value(); + _alignmentChanged = true; + } +} + +QString getCollisionGroupAsString(uint16_t group) { + switch (group) { + case USER_COLLISION_GROUP_DYNAMIC: + return "dynamic"; + case USER_COLLISION_GROUP_STATIC: + return "static"; + case USER_COLLISION_GROUP_KINEMATIC: + return "kinematic"; + case USER_COLLISION_GROUP_MY_AVATAR: + return "myAvatar"; + case USER_COLLISION_GROUP_OTHER_AVATAR: + return "otherAvatar"; + }; + return ""; +} + +uint16_t getCollisionGroupAsBitMask(const QStringRef& name) { + if (0 == name.compare(QString("dynamic"))) { + return USER_COLLISION_GROUP_DYNAMIC; + } else if (0 == name.compare(QString("static"))) { + return USER_COLLISION_GROUP_STATIC; + } else if (0 == name.compare(QString("kinematic"))) { + return USER_COLLISION_GROUP_KINEMATIC; + } else if (0 == name.compare(QString("myAvatar"))) { + return USER_COLLISION_GROUP_MY_AVATAR; + } else if (0 == name.compare(QString("otherAvatar"))) { + return USER_COLLISION_GROUP_OTHER_AVATAR; + } + return 0; +} + +QString EntityItemProperties::getCollisionMaskAsString() const { + QString maskString(""); + for (int i = 0; i < NUM_USER_COLLISION_GROUPS; ++i) { + uint16_t group = 0x0001 << i; + if (group & _collisionMask) { + maskString.append(getCollisionGroupAsString(group)); + maskString.append(','); + } + } + return maskString; +} + +void EntityItemProperties::setCollisionMaskFromString(const QString& maskString) { + QVector groups = maskString.splitRef(','); + uint16_t mask = 0x0000; + for (auto groupName : groups) { + mask |= getCollisionGroupAsBitMask(groupName); + } + _collisionMask = mask; + _collisionMaskChanged = true; +} + +QString EntityItemProperties::getEntityHostTypeAsString() const { + switch (_entityHostType) { + case entity::HostType::DOMAIN: + return "domain"; + case entity::HostType::AVATAR: + return "avatar"; + case entity::HostType::LOCAL: + return "local"; + default: + return ""; + } +} + +void EntityItemProperties::setEntityHostTypeFromString(const QString& entityHostType) { + if (entityHostType == "domain") { + _entityHostType = entity::HostType::DOMAIN; + } else if (entityHostType == "avatar") { + _entityHostType = entity::HostType::AVATAR; + } else if (entityHostType == "local") { + _entityHostType = entity::HostType::LOCAL; + } +} + +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; + } +} + +QVector EntityItemProperties::getTagsAsVector() const { + QVector tags; + for (const QString& tag : _tags) { + tags.push_back(tag); + } + return tags; +} + +void EntityItemProperties::setTagsFromVector(const QVector& tags) { + _tags.clear(); + for (const QString& tag : tags) { + _tags.insert(tag); + } +} + +EntityPropertyFlags EntityItemProperties::getChangedProperties() const { + EntityPropertyFlags changedProperties; + +@ENTITY_ITEM_PROPERTY_CHANGED_PROPERTIES@ + + return changedProperties; +} + +ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool skipDefaults, bool allowUnknownCreateTime, + bool strictSemantics, + EntityPseudoPropertyFlags pseudoPropertyFlags) const { + + // If strictSemantics is true and skipDefaults is false, then all and only those properties are copied for which the property flag + // is included in _desiredProperties, or is one of the specially enumerated ALWAYS properties below. + // (There may be exceptions, but if so, they are bugs.) + // In all other cases, you are welcome to inspect the code and try to figure out what was intended. I wish you luck. -HRS 1/18/17 + ScriptValue properties = engine->newObject(); + EntityItemProperties defaultEntityProperties; + + const bool pseudoPropertyFlagsActive = pseudoPropertyFlags.test(EntityPseudoPropertyFlag::FlagsActive); + // Fix to skip the default return all mechanism, when pseudoPropertyFlagsActive + const bool returnNothingOnEmptyPropertyFlags = pseudoPropertyFlagsActive; + + if (_created == UNKNOWN_CREATED_TIME && !allowUnknownCreateTime) { + // No entity properties can have been set so return without setting any default, zero property values. + return properties; + } + + auto nodeList = DependencyManager::get(); + bool isMyOwnAvatarEntity = _entityHostType == entity::HostType::AVATAR && (_owningAvatarID == AVATAR_SELF_ID || _owningAvatarID == Physics::getSessionUUID()); + if (_idSet && (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::ID))) { + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(id, _id.toString()); + } + if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::Type)) { + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(type, EntityTypes::getEntityTypeName(_type)); + } + if ((!skipDefaults || _lifetime != defaultEntityProperties._lifetime) && !strictSemantics) { + if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::Age)) { + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(age, getAge()); // gettable, but not settable + } + if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::AgeAsText)) { + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable + } + } + if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::LastEdited)) { + properties.setProperty("lastEdited", convertScriptValue(engine, _lastEdited)); + } + if (!skipDefaults) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DIMENSIONS, naturalDimensions); // gettable, but not settable + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POSITION, naturalPosition); + } + + // FIXME: Shouldn't provide a shapeType property for Box and Sphere entities. +@ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT@ + + if (_type == EntityTypes::Image) { + // Handle conversions to old 'textures' property from "imageURL" + if ((isMyOwnAvatarEntity || nodeList->getThisNodeCanViewAssetURLs()) && + ((!returnNothingOnEmptyPropertyFlags && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(PROP_IMAGE_URL)) && + (!skipDefaults || defaultEntityProperties._imageURL != _imageURL)) { + ScriptValue textures = engine->newObject(); + textures.setProperty("tex.picture", _imageURL); + properties.setProperty("textures", textures); + } + } + + /*@jsdoc + * The axis-aligned bounding box of an entity. + * @typedef {object} Entities.BoundingBox + * @property {Vec3} brn - The bottom right near (minimum axes values) corner of the AA box. + * @property {Vec3} tfl - The top far left (maximum axes values) corner of the AA box. + * @property {Vec3} center - The center of the AA box. + * @property {Vec3} dimensions - The dimensions of the AA box. + */ + if (!skipDefaults && !strictSemantics && + (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::BoundingBox))) { + + AABox aaBox = getAABox(); + ScriptValue boundingBox = engine->newObject(); + ScriptValue bottomRightNear = vec3ToScriptValue(engine, aaBox.getCorner()); + ScriptValue topFarLeft = vec3ToScriptValue(engine, aaBox.calcTopFarLeft()); + ScriptValue center = vec3ToScriptValue(engine, aaBox.calcCenter()); + ScriptValue boundingBoxDimensions = vec3ToScriptValue(engine, aaBox.getDimensions()); + boundingBox.setProperty("brn", bottomRightNear); + boundingBox.setProperty("tfl", topFarLeft); + boundingBox.setProperty("center", center); + boundingBox.setProperty("dimensions", boundingBoxDimensions); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(boundingBox, boundingBox); // gettable, but not settable + } + + QString textureNamesStr = QJsonDocument::fromVariant(_textureNames).toJson(); + if (!skipDefaults && !strictSemantics && (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::OriginalTextures))) { + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(originalTextures, textureNamesStr); // gettable, but not settable + } + + // Rendering info + if (!skipDefaults && !strictSemantics && + (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::RenderInfo))) { + + ScriptValue renderInfo = engine->newObject(); + + /*@jsdoc + * Information on how an entity is rendered. Properties are only filled in for Model entities; other + * entity types have an empty object, {}. + * @typedef {object} Entities.RenderInfo + * @property {number} verticesCount - The number of vertices in the entity. + * @property {number} texturesCount - The number of textures in the entity. + * @property {number} texturesSize - The total size of the textures in the entity, in bytes. + * @property {boolean} hasTransparent - true if any of the textures has transparency, false + * if none of them do. + * @property {number} drawCalls - The number of draw calls required to render the entity. + */ + // currently only supported by models + if (_type == EntityTypes::Model) { + renderInfo.setProperty("verticesCount", (int)getRenderInfoVertexCount()); // FIXME - theoretically the number of vertex could be > max int + renderInfo.setProperty("texturesSize", (int)getRenderInfoTextureSize()); // FIXME - theoretically the size of textures could be > max int + renderInfo.setProperty("hasTransparent", getRenderInfoHasTransparent()); + renderInfo.setProperty("drawCalls", getRenderInfoDrawCalls()); + renderInfo.setProperty("texturesCount", getRenderInfoTextureCount()); + } + + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(renderInfo, renderInfo); // Gettable but not settable + } + + if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::ClientOnly)) { + properties.setProperty("clientOnly", convertScriptValue(engine, getEntityHostType() == entity::HostType::AVATAR)); + } + if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::AvatarEntity)) { + properties.setProperty("avatarEntity", convertScriptValue(engine, getEntityHostType() == entity::HostType::AVATAR)); + } + if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::LocalEntity)) { + properties.setProperty("localEntity", convertScriptValue(engine, getEntityHostType() == entity::HostType::LOCAL)); + } + + if (_type != EntityTypes::PolyLine && (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::FaceCamera))) { + properties.setProperty("faceCamera", convertScriptValue(engine, getBillboardMode() == BillboardMode::YAW)); + } + if (!pseudoPropertyFlagsActive || pseudoPropertyFlags.test(EntityPseudoPropertyFlag::IsFacingAvatar)) { + properties.setProperty("isFacingAvatar", convertScriptValue(engine, getBillboardMode() == BillboardMode::FULL)); + } + + return properties; +} + +void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool honorReadOnly) { + //qDebug() << "EntityItemProperties::copyFromScriptValue: properties: " << object.getPropertyNames(); + QList namesList = object.getPropertyNames(); + + QSet namesSet; + for (auto name = namesList.cbegin(); name != namesList.cend(); name++) { + namesSet.insert(*name); + } + + ScriptValue typeScriptValue = object.property("type"); + if (typeScriptValue.isValid()) { + setType(typeScriptValue.toVariant().toString()); + } + + // TODO: should scripts be able to set queryAACube? +@ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT@ + + // Handle conversions from old 'textures' property to "imageURL" + if (namesSet.contains("textures")) { + ScriptValue V = object.property("textures"); + if (_type == EntityTypes::Image && V.isValid() && !object.property("imageURL").isValid()) { + bool isValid = false; + QString textures = QString_convertFromScriptValue(V, isValid); + if (isValid) { + QVariantMap texturesMap = parseTexturesToMap(textures, QVariantMap()); + auto texPicture = texturesMap.find("tex.picture"); + if (texPicture != texturesMap.end()) { + auto imageURL = texPicture.value().toString(); + if (_defaultSettings || imageURL != _imageURL) { + setImageURL(imageURL); + } + } + } + } + } + + // Handle old "faceCamera" and "isFacingAvatar" props + if (_type != EntityTypes::PolyLine && namesSet.contains("faceCamera")) { + ScriptValue P = object.property("faceCamera"); + if (P.isValid() && !object.property("billboardMode").isValid()) { + bool newValue = P.toVariant().toBool(); + bool oldValue = getBillboardMode() == BillboardMode::YAW; + if (_defaultSettings || newValue != oldValue) { + setBillboardMode(newValue ? BillboardMode::YAW : BillboardMode::NONE); + } + } + } + if (namesSet.contains("isFacingAvatar")) { + ScriptValue P = object.property("isFacingAvatar"); + if (P.isValid() && !object.property("billboardMode").isValid() && !object.property("faceCamera").isValid()) { + bool newValue = P.toVariant().toBool(); + bool oldValue = getBillboardMode() == BillboardMode::FULL; + if (_defaultSettings || newValue != oldValue) { + setBillboardMode(newValue ? BillboardMode::FULL : BillboardMode::NONE); + } + } + } + + _lastEdited = usecTimestampNow(); +} + +void EntityItemProperties::copyFromJSONString(ScriptEngine& scriptEngine, const QString& jsonString) { + // DANGER: this method is expensive + QJsonDocument propertiesDoc = QJsonDocument::fromJson(jsonString.toUtf8()); + QJsonObject propertiesObj = propertiesDoc.object(); + QVariant propertiesVariant(propertiesObj); + QVariantMap propertiesMap = propertiesVariant.toMap(); + ScriptValue propertiesScriptValue = variantMapToScriptValue(propertiesMap, scriptEngine); + bool honorReadOnly = true; + copyFromScriptValue(propertiesScriptValue, honorReadOnly); +} + + +void EntityItemProperties::merge(const EntityItemProperties& other) { +@ENTITY_ITEM_PROPERTY_MERGE@ + + _lastEdited = usecTimestampNow(); +} + +ScriptValue EntityItemPropertiesToScriptValue(ScriptEngine* engine, const EntityItemProperties& properties) { + return properties.copyToScriptValue(engine, false); +} + +ScriptValue EntityItemNonDefaultPropertiesToScriptValue(ScriptEngine* engine, const EntityItemProperties& properties) { + return properties.copyToScriptValue(engine, true); +} + +bool EntityItemPropertiesFromScriptValueIgnoreReadOnly(const ScriptValue &object, EntityItemProperties& properties) { + properties.copyFromScriptValue(object, false); + return true; +} + +bool EntityItemPropertiesFromScriptValueHonorReadOnly(const ScriptValue &object, EntityItemProperties& properties) { + properties.copyFromScriptValue(object, true); + return true; +} + +ScriptValue EntityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags) { + return EntityItemProperties::entityPropertyFlagsToScriptValue(engine, flags); +} + +bool EntityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags) { + return EntityItemProperties::entityPropertyFlagsFromScriptValue(object, flags); +} + + +ScriptValue EntityItemProperties::entityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags) { + ScriptValue result = engine->newObject(); + return result; +} + +bool EntityItemProperties::entityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags) { + if (object.isString()) { + EntityPropertyInfo propertyInfo; + if (getPropertyInfo(object.toString(), propertyInfo)) { + flags << propertyInfo.propertyEnums; + } + } + else if (object.isArray()) { + quint32 length = object.property("length").toInt32(); + for (quint32 i = 0; i < length; i++) { + QString propertyName = object.property(i).toString(); + EntityPropertyInfo propertyInfo; + if (getPropertyInfo(propertyName, propertyInfo)) { + flags << propertyInfo.propertyEnums; + } + } + } + return true; +} + +static QHash _propertyInfos; +static QHash _enumsToPropertyStrings; + +bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPropertyInfo& propertyInfo) { + + static std::once_flag initMap; + // V8TODO: Probably needs mutex before call_once + std::call_once(initMap, []() { +@ENTITY_ITEM_PROPERTY_ADD_TO_MAP@ + }); + + auto iter = _propertyInfos.find(propertyName); + if (iter != _propertyInfos.end()) { + propertyInfo = *iter; + return true; + } + + return false; +} + +/*@jsdoc + * Information about an entity property. + * @typedef {object} Entities.EntityPropertyInfo + * @property {number} propertyEnum - The internal number of the property. + * @property {string} minimum - The minimum numerical value the property may have, if available, otherwise "". + * @property {string} maximum - The maximum numerical value the property may have, if available, otherwise "". + */ +ScriptValue EntityPropertyInfoToScriptValue(ScriptEngine* engine, const EntityPropertyInfo& propertyInfo) { + ScriptValue obj = engine->newObject(); + obj.setProperty("propertyEnum", propertyInfo.propertyEnums.firstFlag()); + obj.setProperty("minimum", propertyInfo.minimum.toString()); + obj.setProperty("maximum", propertyInfo.maximum.toString()); + return obj; +} + +bool EntityPropertyInfoFromScriptValue(const ScriptValue& object, EntityPropertyInfo& propertyInfo) { + propertyInfo.propertyEnums = (EntityPropertyList)object.property("propertyEnum").toVariant().toUInt(); + propertyInfo.minimum = object.property("minimum").toVariant(); + propertyInfo.maximum = object.property("maximum").toVariant(); + return true; +} + +// TODO: Implement support for edit packets that can span an MTU sized buffer. We need to implement a mechanism for the +// encodeEntityEditPacket() method to communicate the the caller which properties couldn't fit in the buffer. Similar +// to how we handle this in the Octree streaming case. +// +// TODO: Right now, all possible properties for all subclasses are handled here. Ideally we'd prefer +// to handle this in a more generic way. Allowing subclasses of EntityItem to register their properties +// +// TODO: There's a lot of repeated patterns in the code below to handle each property. It would be nice if the property +// registration mechanism allowed us to collapse these repeated sections of code into a single implementation that +// utilized the registration table to shorten up and simplify this code. +// +// TODO: Implement support for paged properties, spanning MTU, and custom properties +// +// TODO: Implement support for script and visible properties. +// +OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties, + QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties) { + + OctreePacketData ourDataPacket(false, buffer.size()); // create a packetData object to add out packet details too. + OctreePacketData* packetData = &ourDataPacket; // we want a pointer to this so we can use our APPEND_ENTITY_PROPERTY macro + + bool success = true; // assume the best + OctreeElement::AppendState appendState = OctreeElement::COMPLETED; // assume the best + + // TODO: We need to review how jurisdictions should be handled for entities. (The old Models and Particles code + // didn't do anything special for jurisdictions, so we're keeping that same behavior here.) + // + // Always include the root octcode. This is only because the OctreeEditPacketSender will check these octcodes + // to determine which server to send the changes to in the case of multiple jurisdictions. The root will be sent + // to all servers. + vec3 rootPosition(0); + float rootScale = 0.5f; + unsigned char* octcode = pointToOctalCode(rootPosition.x, rootPosition.y, rootPosition.z, rootScale); + + success = packetData->startSubTree(octcode); + delete[] octcode; + + // assuming we have room to fit our octalCode, proceed... + if (success) { + + // Now add our edit content details... + + // id + // encode our ID as a byte count coded byte stream + QByteArray encodedID = id.toRfc4122(); // NUM_BYTES_RFC4122_UUID + + // encode our ID as a byte count coded byte stream + ByteCountCoded tokenCoder; + QByteArray encodedToken; + + // encode our type as a byte count coded byte stream + ByteCountCoded typeCoder = (quint32)properties.getType(); + QByteArray encodedType = typeCoder; + + quint64 updateDelta = 0; // this is an edit so by definition, it's update is in sync + ByteCountCoded updateDeltaCoder = updateDelta; + QByteArray encodedUpdateDelta = updateDeltaCoder; + + EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); + EntityPropertyFlags propertiesDidntFit = requestedProperties; + + LevelDetails entityLevel = packetData->startLevel(); + + // Last Edited quint64 always first, before any other details, which allows us easy access to adjusting this + // timestamp for clock skew + quint64 lastEdited = properties.getLastEdited(); + bool successLastEditedFits = packetData->appendValue(lastEdited); + + bool successIDFits = packetData->appendRawData(encodedID); + if (successIDFits) { + successIDFits = packetData->appendRawData(encodedToken); + } + bool successTypeFits = packetData->appendRawData(encodedType); + + // NOTE: We intentionally do not send "created" times in edit messages. This is because: + // 1) if the edit is to an existing entity, the created time can not be changed + // 2) if the edit is to a new entity, the created time is the last edited time + + // TODO: Should we get rid of this in this in edit packets, since this has to always be 0? + bool successLastUpdatedFits = packetData->appendRawData(encodedUpdateDelta); + + int propertyFlagsOffset = packetData->getUncompressedByteOffset(); + QByteArray encodedPropertyFlags = propertyFlags; + int oldPropertyFlagsLength = encodedPropertyFlags.length(); + bool successPropertyFlagsFits = packetData->appendRawData(encodedPropertyFlags); + int propertyCount = 0; + + bool headerFits = successIDFits && successTypeFits && successLastEditedFits && + successLastUpdatedFits && successPropertyFlagsFits; + + int startOfEntityItemData = packetData->getUncompressedByteOffset(); + + if (headerFits) { + bool successPropertyFits; + propertyFlags -= PROP_LAST_ITEM; // clear the last item for now, we may or may not set it as the actual item + + // These items would go here once supported.... + // PROP_PAGED_PROPERTY, + // PROP_CUSTOM_PROPERTIES_INCLUDED, + +@ENTITY_ITEM_PROPERTY_APPEND@ + + } + + if (propertyCount > 0) { + int endOfEntityItemData = packetData->getUncompressedByteOffset(); + + encodedPropertyFlags = propertyFlags; + int newPropertyFlagsLength = encodedPropertyFlags.length(); + packetData->updatePriorBytes(propertyFlagsOffset, (const unsigned char*)encodedPropertyFlags.constData(), + encodedPropertyFlags.length()); + + // if the size of the PropertyFlags shrunk, we need to shift everything down to front of packet. + if (newPropertyFlagsLength < oldPropertyFlagsLength) { + int oldSize = packetData->getUncompressedSize(); + + const unsigned char* modelItemData = packetData->getUncompressedData(propertyFlagsOffset + oldPropertyFlagsLength); + int modelItemDataLength = endOfEntityItemData - startOfEntityItemData; + int newEntityItemDataStart = propertyFlagsOffset + newPropertyFlagsLength; + packetData->updatePriorBytes(newEntityItemDataStart, modelItemData, modelItemDataLength); + + int newSize = oldSize - (oldPropertyFlagsLength - newPropertyFlagsLength); + packetData->setUncompressedSize(newSize); + + } else { + assert(newPropertyFlagsLength == oldPropertyFlagsLength); // should not have grown + } + + packetData->endLevel(entityLevel); + } else { + packetData->discardLevel(entityLevel); + appendState = OctreeElement::NONE; // if we got here, then we didn't include the item + } + + // If any part of the model items didn't fit, then the element is considered partial + if (appendState != OctreeElement::COMPLETED) { + didntFitProperties = propertiesDidntFit; + } + + packetData->endSubTree(); + + const char* finalizedData = reinterpret_cast(packetData->getFinalizedData()); + int finalizedSize = packetData->getFinalizedSize(); + + if (finalizedSize <= buffer.size()) { + buffer.replace(0, finalizedSize, finalizedData, finalizedSize); + buffer.resize(finalizedSize); + } else { + qCDebug(entities) << "ERROR - encoded edit message doesn't fit in output buffer."; + appendState = OctreeElement::NONE; // if we got here, then we didn't include the item + // maybe we should assert!!! + } + } else { + packetData->discardSubTree(); + } + + return appendState; +} + +QByteArray EntityItemProperties::getPackedNormals() const { + return packNormals(getNormals()); +} + +QByteArray EntityItemProperties::packNormals(const QVector& normals) const { + int normalsSize = normals.size(); + QByteArray packedNormals = QByteArray(normalsSize * 6 + 1, '0'); + // add size of the array + packedNormals[0] = ((uint8_t)normalsSize); + + int index = 1; + for (int i = 0; i < normalsSize; i++) { + int numBytes = packFloatVec3ToSignedTwoByteFixed((unsigned char*)packedNormals.data() + index, normals[i], 15); + index += numBytes; + } + return packedNormals; +} + +QByteArray EntityItemProperties::getPackedStrokeColors() const { + return packStrokeColors(getStrokeColors()); +} +QByteArray EntityItemProperties::packStrokeColors(const QVector& strokeColors) const { + int strokeColorsSize = strokeColors.size(); + QByteArray packedStrokeColors = QByteArray(strokeColorsSize * 3 + 1, '0'); + + // add size of the array + packedStrokeColors[0] = ((uint8_t)strokeColorsSize); + + + for (int i = 0; i < strokeColorsSize; i++) { + // add the color to the QByteArray + packedStrokeColors[i * 3 + 1] = strokeColors[i].x * 255; + packedStrokeColors[i * 3 + 2] = strokeColors[i].y * 255; + packedStrokeColors[i * 3 + 3] = strokeColors[i].z * 255; + } + return packedStrokeColors; +} + +// TODO: +// how to handle lastEdited? +// how to handle lastUpdated? +// consider handling case where no properties are included... we should just ignore this packet... +// +// TODO: Right now, all possible properties for all subclasses are handled here. Ideally we'd prefer +// to handle this in a more generic way. Allowing subclasses of EntityItem to register their properties +// +// TODO: There's a lot of repeated patterns in the code below to handle each property. It would be nice if the property +// registration mechanism allowed us to collapse these repeated sections of code into a single implementation that +// utilized the registration table to shorten up and simplify this code. +// +// TODO: Implement support for paged properties, spanning MTU, and custom properties +// +// TODO: Implement support for script and visible properties. +// +bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes, + EntityItemID& entityID, EntityItemProperties& properties) { + bool valid = false; + + const unsigned char* dataAt = data; + processedBytes = 0; + + // the first part of the data is an octcode, this is a required element of the edit packet format, but we don't + // actually use it, we do need to skip it and read to the actual data we care about. + int octets = numberOfThreeBitSectionsInCode(data); + int bytesToReadOfOctcode = (int)bytesRequiredForCodeLength(octets); + + // we don't actually do anything with this octcode... + dataAt += bytesToReadOfOctcode; + processedBytes += bytesToReadOfOctcode; + + // Edit packets have a last edited time stamp immediately following the octcode. + // NOTE: the edit times have been set by the editor to match out clock, so we don't need to adjust + // these times for clock skew at this point. + quint64 lastEdited; + memcpy(&lastEdited, dataAt, sizeof(lastEdited)); + dataAt += sizeof(lastEdited); + processedBytes += sizeof(lastEdited); + properties.setLastEdited(lastEdited); + + // encoded id + QUuid editID = QUuid::fromRfc4122(QByteArray::fromRawData(reinterpret_cast(dataAt), NUM_BYTES_RFC4122_UUID)); + dataAt += NUM_BYTES_RFC4122_UUID; + processedBytes += NUM_BYTES_RFC4122_UUID; + + entityID = editID; + valid = true; + + // Entity Type... + QByteArray encodedType((const char*)dataAt, (bytesToRead - processedBytes)); + ByteCountCoded typeCoder = encodedType; + quint32 entityTypeCode = typeCoder; + properties.setType((EntityTypes::EntityType)entityTypeCode); + encodedType = typeCoder; // determine true bytesToRead + dataAt += encodedType.size(); + processedBytes += encodedType.size(); + + // Update Delta - when was this item updated relative to last edit... this really should be 0 + // TODO: Should we get rid of this in this in edit packets, since this has to always be 0? + // TODO: do properties need to handle lastupdated??? + + // last updated is stored as ByteCountCoded delta from lastEdited + QByteArray encodedUpdateDelta((const char*)dataAt, (bytesToRead - processedBytes)); + ByteCountCoded updateDeltaCoder = encodedUpdateDelta; + encodedUpdateDelta = updateDeltaCoder; // determine true bytesToRead + dataAt += encodedUpdateDelta.size(); + processedBytes += encodedUpdateDelta.size(); + + // TODO: Do we need this lastUpdated?? We don't seem to use it. + //quint64 updateDelta = updateDeltaCoder; + //quint64 lastUpdated = lastEdited + updateDelta; // don't adjust for clock skew since we already did that for lastEdited + + // Property Flags... + QByteArray encodedPropertyFlags((const char*)dataAt, (bytesToRead - processedBytes)); + EntityPropertyFlags propertyFlags = encodedPropertyFlags; + dataAt += propertyFlags.getEncodedLength(); + processedBytes += propertyFlags.getEncodedLength(); + +@ENTITY_ITEM_PROPERTY_READ@ + + return valid; +} + +void EntityItemProperties::setPackedNormals(const QByteArray& value) { + setNormals(unpackNormals(value)); +} + +QVector EntityItemProperties::unpackNormals(const QByteArray& normals) { + // the size of the vector is packed first + QVector unpackedNormals = QVector((int)normals[0]); + + if ((int)normals[0] == normals.size() / 6) { + int j = 0; + for (int i = 1; i < normals.size();) { + vec3 aux = vec3(); + i += unpackFloatVec3FromSignedTwoByteFixed((unsigned char*)normals.data() + i, aux, 15); + unpackedNormals[j] = aux; + j++; + } + } else { + qCDebug(entities) << "WARNING - Expected received size for normals does not match. Expected: " << (int)normals[0] + << " Received: " << (normals.size() / 6); + } + return unpackedNormals; +} + +void EntityItemProperties::setPackedStrokeColors(const QByteArray& value) { + setStrokeColors(unpackStrokeColors(value)); +} + +QVector EntityItemProperties::unpackStrokeColors(const QByteArray& strokeColors) { + // the size of the vector is packed first + QVector unpackedStrokeColors = QVector((int)strokeColors[0]); + + if ((int)strokeColors[0] == strokeColors.size() / 3) { + int j = 0; + for (int i = 1; i < strokeColors.size();) { + + float r = (uint8_t)strokeColors[i++] / 255.0f; + float g = (uint8_t)strokeColors[i++] / 255.0f; + float b = (uint8_t)strokeColors[i++] / 255.0f; + unpackedStrokeColors[j++] = vec3(r, g, b); + } + } else { + qCDebug(entities) << "WARNING - Expected received size for stroke colors does not match. Expected: " + << (int)strokeColors[0] << " Received: " << (strokeColors.size() / 3); + } + + return unpackedStrokeColors; +} + +// NOTE: This version will only encode the portion of the edit message immediately following the +// header it does not include the send times and sequence number because that is handled by the +// edit packet sender... +bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer) { + + uint16_t numberOfIds = 1; // only one entity ID in this message + + if (buffer.size() < (int)(sizeof(numberOfIds) + NUM_BYTES_RFC4122_UUID)) { + qCDebug(entities) << "ERROR - encodeEraseEntityMessage() called with buffer that is too small!"; + return false; + } + + buffer.resize(0); + buffer.append(reinterpret_cast(&numberOfIds), sizeof(numberOfIds)); + buffer.append(entityItemID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID); + + return true; +} + +bool EntityItemProperties::encodeCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID, QByteArray& buffer) { + if (buffer.size() < (int)(NUM_BYTES_RFC4122_UUID * 2)) { + qCDebug(entities) << "ERROR - encodeCloneEntityMessage() called with buffer that is too small!"; + return false; + } + + buffer.resize(0); + buffer.append(entityIDToClone.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID); + buffer.append(newEntityID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID); + + return true; +} + +bool EntityItemProperties::decodeCloneEntityMessage(const QByteArray& buffer, int& processedBytes, EntityItemID& entityIDToClone, EntityItemID& newEntityID) { + const unsigned char* packetData = (const unsigned char*)buffer.constData(); + const unsigned char* dataAt = packetData; + size_t packetLength = buffer.size(); + processedBytes = 0; + + if (NUM_BYTES_RFC4122_UUID * 2 > packetLength) { + qCDebug(entities) << "EntityItemProperties::decodeCloneEntityMessage().... bailing because not enough bytes in buffer"; + return false; // bail to prevent buffer overflow + } + + QByteArray encodedID = buffer.mid((int)processedBytes, NUM_BYTES_RFC4122_UUID); + entityIDToClone = QUuid::fromRfc4122(encodedID); + dataAt += encodedID.size(); + processedBytes += encodedID.size(); + + encodedID = buffer.mid((int)processedBytes, NUM_BYTES_RFC4122_UUID); + newEntityID = QUuid::fromRfc4122(encodedID); + dataAt += encodedID.size(); + processedBytes += encodedID.size(); + + return true; +} + +void EntityItemProperties::markAllChanged() { +@ENTITY_ITEM_PROPERTY_MARK_CHANGED@ +} + +// The minimum bounding box for the entity. +AABox EntityItemProperties::getAABox() const { + + // _position represents the position of the registration point. + vec3 registrationRemainder = vec3(1.0f) - _registrationPoint; + + vec3 unrotatedMinRelativeToEntity = -(_dimensions * _registrationPoint); + vec3 unrotatedMaxRelativeToEntity = _dimensions * registrationRemainder; + Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity }; + Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(_rotation); + + // shift the extents to be relative to the position/registration point + rotatedExtentsRelativeToRegistrationPoint.shiftBy(_position); + + return AABox(rotatedExtentsRelativeToRegistrationPoint); +} + +bool EntityItemProperties::hasTransformOrVelocityChanges() const { + return _positionChanged || _localPositionChanged + || _rotationChanged || _localRotationChanged + || _velocityChanged || _localVelocityChanged + || _angularVelocityChanged || _localAngularVelocityChanged + || _accelerationChanged; +} + +void EntityItemProperties::clearTransformOrVelocityChanges() { + _positionChanged = false; + _localPositionChanged = false; + _rotationChanged = false; + _localRotationChanged = false; + _velocityChanged = false; + _localVelocityChanged = false; + _angularVelocityChanged = false; + _localAngularVelocityChanged = false; + _accelerationChanged = false; +} + +bool EntityItemProperties::hasMiscPhysicsChanges() const { + return _gravityChanged || _dimensionsChanged || _densityChanged || _frictionChanged + || _restitutionChanged || _dampingChanged || _angularDampingChanged || _registrationPointChanged || + _compoundShapeURLChanged || _dynamicChanged || _collisionlessChanged || _collisionMaskChanged; +} + +bool EntityItemProperties::hasSimulationRestrictedChanges() const { + return _positionChanged || _localPositionChanged + || _rotationChanged || _localRotationChanged + || _velocityChanged || _localVelocityChanged + || _localDimensionsChanged || _dimensionsChanged + || _angularVelocityChanged || _localAngularVelocityChanged + || _accelerationChanged + || _parentIDChanged || _parentJointIndexChanged; +} + +void EntityItemProperties::copySimulationRestrictedProperties(const EntityItemPointer& entity) { + if (!_parentIDChanged) { + setParentID(entity->getParentID()); + } + if (!_parentJointIndexChanged) { + setParentJointIndex(entity->getParentJointIndex()); + } + if (!_localPositionChanged && !_positionChanged) { + setPosition(entity->getWorldPosition()); + } + if (!_localRotationChanged && !_rotationChanged) { + setRotation(entity->getWorldOrientation()); + } + if (!_localVelocityChanged && !_velocityChanged) { + setVelocity(entity->getWorldVelocity()); + } + if (!_localAngularVelocityChanged && !_angularVelocityChanged) { + setAngularVelocity(entity->getWorldAngularVelocity()); + } + if (!_accelerationChanged) { + setAcceleration(entity->getAcceleration()); + } + if (!_localDimensionsChanged && !_dimensionsChanged) { + setLocalDimensions(entity->getScaledDimensions()); + } +} + +void EntityItemProperties::clearSimulationRestrictedProperties() { + _positionChanged = false; + _localPositionChanged = false; + _rotationChanged = false; + _localRotationChanged = false; + _velocityChanged = false; + _localVelocityChanged = false; + _angularVelocityChanged = false; + _localAngularVelocityChanged = false; + _accelerationChanged = false; + _parentIDChanged = false; + _parentJointIndexChanged = false; +} + +void EntityItemProperties::clearSimulationOwner() { + _simulationOwner.clear(); + _simulationOwnerChanged = true; +} + +void EntityItemProperties::setSimulationOwner(const QUuid& id, uint8_t priority) { + if (!_simulationOwner.matchesValidID(id) || _simulationOwner.getPriority() != priority) { + _simulationOwner.set(id, priority); + _simulationOwnerChanged = true; + } +} + +void EntityItemProperties::setSimulationOwner(const QByteArray& data) { + if (_simulationOwner.fromByteArray(data)) { + _simulationOwnerChanged = true; + } +} + +uint8_t EntityItemProperties::computeSimulationBidPriority() const { + uint8_t priority = 0; + if (_parentIDChanged || _parentJointIndexChanged) { + // we need higher simulation ownership priority to chang parenting info + priority = SCRIPT_GRAB_SIMULATION_PRIORITY; + } else if (_positionChanged || _localPositionChanged + || _rotationChanged || _localRotationChanged + || _velocityChanged || _localVelocityChanged + || _angularVelocityChanged || _localAngularVelocityChanged) { + priority = SCRIPT_POKE_SIMULATION_PRIORITY; + } + return priority; +} + +QList EntityItemProperties::listChangedProperties() { + QList out; + +@ENTITY_ITEM_PROPERTY_LIST_CHANGED@ + + return out; +} + +bool EntityItemProperties::transformChanged() const { + return positionChanged() || rotationChanged() || + localPositionChanged() || localRotationChanged(); +} + +bool EntityItemProperties::getScalesWithParent() const { + // keep this logic the same as in EntityItem::getScalesWithParent + bool scalesWithParent { false }; + if (parentIDChanged()) { + bool success; + SpatiallyNestablePointer parent = SpatiallyNestable::findByID(getParentID(), success); + if (success && parent) { + bool avatarAncestor = (parent->getNestableType() == NestableType::Avatar || + parent->hasAncestorOfType(NestableType::Avatar)); + scalesWithParent = getEntityHostType() == entity::HostType::AVATAR && avatarAncestor; + } + } + return scalesWithParent; +} + +bool EntityItemProperties::parentRelatedPropertyChanged() const { + return positionChanged() || rotationChanged() || + localPositionChanged() || localRotationChanged() || + localDimensionsChanged() || + parentIDChanged() || parentJointIndexChanged(); +} + +bool EntityItemProperties::queryAACubeRelatedPropertyChanged() const { + return parentRelatedPropertyChanged() || dimensionsChanged(); +} + +bool EntityItemProperties::grabbingRelatedPropertyChanged() const { + const GrabPropertyGroup& grabProperties = getGrab(); + return grabProperties.triggerableChanged() || grabProperties.grabbableChanged() || + grabProperties.grabFollowsControllerChanged() || grabProperties.grabKinematicChanged() || + grabProperties.equippableChanged() || grabProperties.equippableLeftPositionChanged() || + grabProperties.equippableRightPositionChanged() || grabProperties.equippableLeftRotationChanged() || + grabProperties.equippableRightRotationChanged() || grabProperties.equippableIndicatorURLChanged() || + grabProperties.equippableIndicatorScaleChanged() || grabProperties.equippableIndicatorOffsetChanged(); +} + +void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityIDToClone) { + setName(getName() + "-clone-" + entityIDToClone.toString()); + setLocked(false); + setParentID(QUuid()); + setParentJointIndex(-1); + setLifetime(getCloneLifetime()); + setDynamic(getCloneDynamic()); + if (getEntityHostType() != entity::HostType::LOCAL) { + setEntityHostType(getCloneAvatarEntity() ? entity::HostType::AVATAR : entity::HostType::DOMAIN); + } else { + // Local Entities clone as local entities + setEntityHostType(entity::HostType::LOCAL); + setCollisionless(true); + } + uint64_t now = usecTimestampNow(); + setCreated(now); + setLastEdited(now); + setCloneable(ENTITY_ITEM_DEFAULT_CLONEABLE); + setCloneLifetime(ENTITY_ITEM_DEFAULT_CLONE_LIFETIME); + setCloneLimit(ENTITY_ITEM_DEFAULT_CLONE_LIMIT); + setCloneDynamic(ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC); + setCloneAvatarEntity(ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); +} + +bool EntityItemProperties::blobToProperties(ScriptEngine& scriptEngine, const QByteArray& blob, EntityItemProperties& properties) { + // DANGER: this method is NOT efficient. + // begin recipe for converting unfortunately-formatted-binary-blob to EntityItemProperties + OVERTE_IGNORE_DEPRECATED_BEGIN + QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(blob); + OVERTE_IGNORE_DEPRECATED_END + if (jsonProperties.isEmpty() || jsonProperties.isNull() || !jsonProperties.isObject() || jsonProperties.object().isEmpty()) { + qCDebug(entities) << "bad avatarEntityData json" << QString(blob.toHex()); + return false; + } + QVariant variant = jsonProperties.toVariant(); + QVariantMap variantMap = variant.toMap(); + ScriptValue scriptValue = variantMapToScriptValue(variantMap, scriptEngine); + EntityItemPropertiesFromScriptValueIgnoreReadOnly(scriptValue, properties); + // end recipe + return true; +} + +void EntityItemProperties::propertiesToBlob(ScriptEngine& scriptEngine, const QUuid& myAvatarID, + const EntityItemProperties& properties, QByteArray& blob, bool allProperties) { + // DANGER: this method is NOT efficient. + // begin recipe for extracting unfortunately-formatted-binary-blob from EntityItem + ScriptValue scriptValue = allProperties + ? EntityItemPropertiesToScriptValue(&scriptEngine, properties) + : EntityItemNonDefaultPropertiesToScriptValue(&scriptEngine, properties); + QVariant variantProperties = scriptValue.toVariant(); + QJsonDocument jsonProperties = QJsonDocument::fromVariant(variantProperties); + // the ID of the parent/avatar changes from session to session. use a special UUID to indicate the avatar + QJsonObject jsonObject = jsonProperties.object(); + if (jsonObject.contains("parentID")) { + if (QUuid(jsonObject["parentID"].toString()) == myAvatarID) { + jsonObject["parentID"] = AVATAR_SELF_ID.toString(); + } + } + jsonProperties = QJsonDocument(jsonObject); + OVERTE_IGNORE_DEPRECATED_BEGIN + blob = jsonProperties.toBinaryData(); + OVERTE_IGNORE_DEPRECATED_END + // end recipe +} + +QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f) { + QString result = "[ "; + + for (int i = 0; i < PROP_AFTER_LAST_ITEM; i++) { + auto prop = EntityPropertyList(i); + if (f.getHasProperty(prop)) { + result = result + _enumsToPropertyStrings[prop] + " "; + } + } + + result += "]"; + dbg.nospace() << result; + return dbg; +} diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h deleted file mode 100644 index e9a4800892..0000000000 --- a/libraries/entities/src/EntityItemProperties.h +++ /dev/null @@ -1,739 +0,0 @@ -// -// EntityItemProperties.h -// libraries/entities/src -// -// Created by Brad Hefta-Gaub on 12/4/13. -// Copyright 2013 High Fidelity, Inc. -// Copyright 2020 Vircadia contributors. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#ifndef hifi_EntityItemProperties_h -#define hifi_EntityItemProperties_h - -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include "FontFamilies.h" -#include - -#include -#include "EntityItemPropertiesDefaults.h" -#include "EntityItemPropertiesMacros.h" -#include "EntityTypes.h" -#include "EntityPropertyFlags.h" -#include "EntityPseudoPropertyFlags.h" -#include "SimulationOwner.h" - -#include "TextEntityItem.h" -#include "WebEntityItem.h" -#include "ParticleEffectEntityItem.h" -#include "ProceduralParticleEffectEntityItem.h" -#include "LineEntityItem.h" -#include "PolyVoxEntityItem.h" -#include "GridEntityItem.h" -#include "GizmoEntityItem.h" -#include "SoundEntityItem.h" -#include "LightEntityItem.h" -#include "ZoneEntityItem.h" - -#include "AnimationPropertyGroup.h" -#include "SkyboxPropertyGroup.h" -#include "HazePropertyGroup.h" -#include "BloomPropertyGroup.h" -#include "PulsePropertyGroup.h" -#include "RingGizmoPropertyGroup.h" -#include "ZoneAudioPropertyGroup.h" -#include "TonemappingPropertyGroup.h" -#include "AmbientOcclusionPropertyGroup.h" - -#include "MaterialMappingMode.h" -#include "BillboardMode.h" -#include "RenderLayer.h" -#include "PrimitiveMode.h" -#include "WebInputMode.h" -#include "GizmoType.h" -#include "TextEffect.h" -#include "TextAlignment.h" -#include "MirrorMode.h" - -class ScriptEngine; - -const quint64 UNKNOWN_CREATED_TIME = 0; - -using vec3Color = glm::vec3; -using u8vec3Color = glm::u8vec3; - -struct EntityPropertyInfo { - EntityPropertyInfo(EntityPropertyList propEnum) : - propertyEnums(propEnum) {} - EntityPropertyInfo(EntityPropertyList propEnum, QVariant min, QVariant max) : - propertyEnums(propEnum), minimum(min), maximum(max) {} - EntityPropertyInfo() = default; - EntityPropertyFlags propertyEnums; - QVariant minimum; - QVariant maximum; -}; - -template -EntityPropertyInfo makePropertyInfo(EntityPropertyList p, typename std::enable_if::value>::type* = 0) { - return EntityPropertyInfo(p); -} - -template -EntityPropertyInfo makePropertyInfo(EntityPropertyList p, typename std::enable_if::value>::type* = 0) { - return EntityPropertyInfo(p, std::numeric_limits::min(), std::numeric_limits::max()); -} - -/// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an -/// entity and a JavaScript style hash/ScriptValue storing a set of properties. Used in scripting to set/get the complete -/// set of entity item properties via JavaScript hashes/QScriptValues -/// all units for SI units (meter, second, radian, etc) -class EntityItemProperties { - // TODO: consider removing these friend relationship and use public methods - friend class EntityItem; - friend class BoxEntityItem; - friend class SphereEntityItem; - friend class ShapeEntityItem; - friend class ModelEntityItem; - friend class TextEntityItem; - friend class ImageEntityItem; - friend class WebEntityItem; - friend class ParticleEffectEntityItem; - friend class ProceduralParticleEffectEntityItem; - friend class LineEntityItem; - friend class PolyLineEntityItem; - friend class PolyVoxEntityItem; - friend class GridEntityItem; - friend class GizmoEntityItem; - friend class LightEntityItem; - friend class ZoneEntityItem; - friend class MaterialEntityItem; - friend class SoundEntityItem; - -public: - static bool blobToProperties(ScriptEngine& scriptEngine, const QByteArray& blob, EntityItemProperties& properties); - static void propertiesToBlob(ScriptEngine& scriptEngine, const QUuid& myAvatarID, const EntityItemProperties& properties, - QByteArray& blob, bool allProperties = false); - - EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()); - EntityItemProperties(const EntityItemProperties&) = default; - - virtual ~EntityItemProperties() = default; - - void merge(const EntityItemProperties& other); - - EntityTypes::EntityType getType() const { return _type; } - void setType(EntityTypes::EntityType type) { _type = type; } - - virtual ScriptValue copyToScriptValue(ScriptEngine* engine, bool skipDefaults, bool allowUnknownCreateTime = false, - bool strictSemantics = false, EntityPseudoPropertyFlags pseudoPropertyFlags = EntityPseudoPropertyFlags()) const; - virtual void copyFromScriptValue(const ScriptValue& object, bool honorReadOnly); - void copyFromJSONString(ScriptEngine& scriptEngine, const QString& jsonString); - - static ScriptValue entityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags); - static bool entityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags); - - static bool getPropertyInfo(const QString& propertyName, EntityPropertyInfo& propertyInfo); - - // editing related features supported by all entities - quint64 getLastEdited() const { return _lastEdited; } - float getEditedAgo() const /// Elapsed seconds since this entity was last edited - { return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; } - - EntityPropertyFlags getChangedProperties() const; - - bool transformChanged() const; - bool getScalesWithParent() const; - bool parentRelatedPropertyChanged() const; - bool queryAACubeRelatedPropertyChanged() const; - bool grabbingRelatedPropertyChanged() const; - - AABox getAABox() const; - - void debugDump() const; - void setLastEdited(quint64 usecTime); - EntityPropertyFlags getDesiredProperties() { return _desiredProperties; } - void setDesiredProperties(EntityPropertyFlags properties) { _desiredProperties = properties; } - - bool constructFromBuffer(const unsigned char* data, int dataLength); - - // Note: DEFINE_PROPERTY(PROP_FOO, Foo, foo, type, value) creates the following methods and variables: - // type getFoo() const; - // void setFoo(type); - // bool fooChanged() const; - // type _foo { value }; - // bool _fooChanged { false }; - - // Core Properties - DEFINE_PROPERTY_REF(PROP_SIMULATION_OWNER, SimulationOwner, simulationOwner, SimulationOwner, SimulationOwner()); - DEFINE_PROPERTY_REF(PROP_PARENT_ID, ParentID, parentID, QUuid, UNKNOWN_ENTITY_ID); - DEFINE_PROPERTY_REF(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, -1); - DEFINE_PROPERTY(PROP_VISIBLE, Visible, visible, bool, ENTITY_ITEM_DEFAULT_VISIBLE); - DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString, ENTITY_ITEM_DEFAULT_NAME); - DEFINE_PROPERTY(PROP_LOCKED, Locked, locked, bool, ENTITY_ITEM_DEFAULT_LOCKED); - DEFINE_PROPERTY_REF(PROP_USER_DATA, UserData, userData, QString, ENTITY_ITEM_DEFAULT_USER_DATA); - DEFINE_PROPERTY_REF(PROP_PRIVATE_USER_DATA, PrivateUserData, privateUserData, QString, ENTITY_ITEM_DEFAULT_PRIVATE_USER_DATA); - DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString, ""); - DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString, ""); - DEFINE_PROPERTY_REF_WITH_SETTER(PROP_POSITION, Position, position, glm::vec3, ENTITY_ITEM_ZERO_VEC3); - DEFINE_PROPERTY_REF(PROP_DIMENSIONS, Dimensions, dimensions, glm::vec3, ENTITY_ITEM_DEFAULT_DIMENSIONS); - DEFINE_PROPERTY_REF(PROP_ROTATION, Rotation, rotation, glm::quat, ENTITY_ITEM_DEFAULT_ROTATION); - DEFINE_PROPERTY_REF(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3, ENTITY_ITEM_DEFAULT_REGISTRATION_POINT); - DEFINE_PROPERTY(PROP_CREATED, Created, created, quint64, UNKNOWN_CREATED_TIME); - DEFINE_PROPERTY_REF(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid, ENTITY_ITEM_DEFAULT_LAST_EDITED_BY); - DEFINE_PROPERTY_REF_ENUM(PROP_ENTITY_HOST_TYPE, EntityHostType, entityHostType, entity::HostType, entity::HostType::DOMAIN); - DEFINE_PROPERTY_REF_WITH_SETTER(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid, UNKNOWN_ENTITY_ID); - DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube()); - DEFINE_PROPERTY(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool, ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW); - DEFINE_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool, ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA); - DEFINE_PROPERTY_REF_ENUM(PROP_RENDER_LAYER, RenderLayer, renderLayer, RenderLayer, RenderLayer::WORLD); - DEFINE_PROPERTY_REF_ENUM(PROP_PRIMITIVE_MODE, PrimitiveMode, primitiveMode, PrimitiveMode, PrimitiveMode::SOLID); - DEFINE_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, IgnorePickIntersection, ignorePickIntersection, bool, false); - 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_REF(PROP_TAGS, Tags, tags, QSet, QSet()); - 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); - DEFINE_PROPERTY_REF(PROP_VELOCITY, Velocity, velocity, glm::vec3, ENTITY_ITEM_DEFAULT_VELOCITY); - DEFINE_PROPERTY_REF(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3, ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY); - DEFINE_PROPERTY_REF(PROP_GRAVITY, Gravity, gravity, glm::vec3, ENTITY_ITEM_DEFAULT_GRAVITY); - DEFINE_PROPERTY_REF(PROP_ACCELERATION, Acceleration, acceleration, glm::vec3, ENTITY_ITEM_DEFAULT_ACCELERATION); - DEFINE_PROPERTY(PROP_DAMPING, Damping, damping, float, ENTITY_ITEM_DEFAULT_DAMPING); - DEFINE_PROPERTY(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float, ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING); - DEFINE_PROPERTY(PROP_RESTITUTION, Restitution, restitution, float, ENTITY_ITEM_DEFAULT_RESTITUTION); - DEFINE_PROPERTY(PROP_FRICTION, Friction, friction, float, ENTITY_ITEM_DEFAULT_FRICTION); - DEFINE_PROPERTY(PROP_LIFETIME, Lifetime, lifetime, float, ENTITY_ITEM_DEFAULT_LIFETIME); - DEFINE_PROPERTY(PROP_COLLISIONLESS, Collisionless, collisionless, bool, ENTITY_ITEM_DEFAULT_COLLISIONLESS); - DEFINE_PROPERTY(PROP_COLLISION_MASK, CollisionMask, collisionMask, uint16_t, ENTITY_COLLISION_MASK_DEFAULT); - DEFINE_PROPERTY(PROP_DYNAMIC, Dynamic, dynamic, bool, ENTITY_ITEM_DEFAULT_DYNAMIC); - DEFINE_PROPERTY_REF(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString, ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL); - DEFINE_PROPERTY_REF(PROP_ACTION_DATA, ActionData, actionData, QByteArray, QByteArray()); - - // Cloning - DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_DEFAULT_CLONEABLE); - DEFINE_PROPERTY(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_DEFAULT_CLONE_LIFETIME); - DEFINE_PROPERTY(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_DEFAULT_CLONE_LIMIT); - DEFINE_PROPERTY(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC); - DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); - DEFINE_PROPERTY_REF(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID); - - // Scripts - DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString, ENTITY_ITEM_DEFAULT_SCRIPT); - DEFINE_PROPERTY(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64, ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP); - DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS); - - // these are used when bouncing location data into and out of scripts - DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, glm::vec3, ENTITY_ITEM_ZERO_VEC3); - DEFINE_PROPERTY_REF(PROP_LOCAL_ROTATION, LocalRotation, localRotation, quat, ENTITY_ITEM_DEFAULT_ROTATION); - DEFINE_PROPERTY_REF(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, glm::vec3, ENTITY_ITEM_ZERO_VEC3); - DEFINE_PROPERTY_REF(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, glm::vec3, ENTITY_ITEM_ZERO_VEC3); - DEFINE_PROPERTY_REF(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, glm::vec3, ENTITY_ITEM_ZERO_VEC3); - - // Common - DEFINE_PROPERTY_REF_ENUM(PROP_SHAPE_TYPE, ShapeType, shapeType, ShapeType, SHAPE_TYPE_NONE); - DEFINE_PROPERTY_REF(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString, ""); - DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, u8vec3Color, ENTITY_ITEM_DEFAULT_COLOR); - DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float, ENTITY_ITEM_DEFAULT_ALPHA); - DEFINE_PROPERTY_REF(PROP_UNLIT, Unlit, unlit, bool, false); - DEFINE_PROPERTY_GROUP(Pulse, pulse, PulsePropertyGroup); - DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString, ""); - - // Particles - DEFINE_PROPERTY(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32, particle::DEFAULT_MAX_PARTICLES); - DEFINE_PROPERTY(PROP_LIFESPAN, Lifespan, lifespan, float, particle::DEFAULT_LIFESPAN); - DEFINE_PROPERTY(PROP_EMITTING_PARTICLES, IsEmitting, isEmitting, bool, true); - DEFINE_PROPERTY(PROP_EMIT_RATE, EmitRate, emitRate, float, particle::DEFAULT_EMIT_RATE); - DEFINE_PROPERTY(PROP_EMIT_SPEED, EmitSpeed, emitSpeed, float, particle::DEFAULT_EMIT_SPEED); - DEFINE_PROPERTY(PROP_SPEED_SPREAD, SpeedSpread, speedSpread, float, particle::DEFAULT_SPEED_SPREAD); - DEFINE_PROPERTY_REF(PROP_EMIT_ORIENTATION, EmitOrientation, emitOrientation, glm::quat, particle::DEFAULT_EMIT_ORIENTATION); - DEFINE_PROPERTY_REF(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, glm::vec3, particle::DEFAULT_EMIT_DIMENSIONS); - DEFINE_PROPERTY(PROP_EMIT_RADIUS_START, EmitRadiusStart, emitRadiusStart, float, particle::DEFAULT_EMIT_RADIUS_START); - DEFINE_PROPERTY(PROP_POLAR_START, PolarStart, polarStart, float, particle::DEFAULT_POLAR_START); - DEFINE_PROPERTY(PROP_POLAR_FINISH, PolarFinish, polarFinish, float, particle::DEFAULT_POLAR_FINISH); - DEFINE_PROPERTY(PROP_AZIMUTH_START, AzimuthStart, azimuthStart, float, particle::DEFAULT_AZIMUTH_START); - DEFINE_PROPERTY(PROP_AZIMUTH_FINISH, AzimuthFinish, azimuthFinish, float, particle::DEFAULT_AZIMUTH_FINISH); - DEFINE_PROPERTY_REF(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, glm::vec3, particle::DEFAULT_EMIT_ACCELERATION); - DEFINE_PROPERTY_REF(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, glm::vec3, particle::DEFAULT_ACCELERATION_SPREAD); - DEFINE_PROPERTY(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float, particle::DEFAULT_PARTICLE_RADIUS); - DEFINE_PROPERTY(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float, particle::DEFAULT_RADIUS_SPREAD); - DEFINE_PROPERTY(PROP_RADIUS_START, RadiusStart, radiusStart, float, particle::DEFAULT_RADIUS_START); - DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, particle::DEFAULT_RADIUS_FINISH); - DEFINE_PROPERTY_REF(PROP_COLOR_SPREAD, ColorSpread, colorSpread, u8vec3Color, particle::DEFAULT_COLOR_SPREAD); - DEFINE_PROPERTY_REF(PROP_COLOR_START, ColorStart, colorStart, glm::vec3, particle::DEFAULT_COLOR_UNINITIALIZED); - DEFINE_PROPERTY_REF(PROP_COLOR_FINISH, ColorFinish, colorFinish, glm::vec3, particle::DEFAULT_COLOR_UNINITIALIZED); - DEFINE_PROPERTY(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float, particle::DEFAULT_ALPHA_SPREAD); - DEFINE_PROPERTY(PROP_ALPHA_START, AlphaStart, alphaStart, float, particle::DEFAULT_ALPHA_START); - DEFINE_PROPERTY(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float, particle::DEFAULT_ALPHA_FINISH); - DEFINE_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool, particle::DEFAULT_EMITTER_SHOULD_TRAIL); - DEFINE_PROPERTY(PROP_PARTICLE_SPIN, ParticleSpin, particleSpin, float, particle::DEFAULT_PARTICLE_SPIN); - DEFINE_PROPERTY(PROP_SPIN_SPREAD, SpinSpread, spinSpread, float, particle::DEFAULT_SPIN_SPREAD); - DEFINE_PROPERTY(PROP_SPIN_START, SpinStart, spinStart, float, particle::DEFAULT_SPIN_START); - DEFINE_PROPERTY(PROP_SPIN_FINISH, SpinFinish, spinFinish, float, particle::DEFAULT_SPIN_FINISH); - DEFINE_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, RotateWithEntity, rotateWithEntity, bool, particle::DEFAULT_ROTATE_WITH_ENTITY); - - // Procedural Particles - DEFINE_PROPERTY_REF(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, NumParticles, numParticles, uint32_t, particle::DEFAULT_NUM_PROCEDURAL_PARTICLES); - DEFINE_PROPERTY_REF(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, NumTrianglesPerParticle, numTrianglesPerParticle, uint8_t, particle::DEFAULT_NUM_TRIS_PER); - DEFINE_PROPERTY_REF(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, NumUpdateProps, numUpdateProps, uint8_t, particle::DEFAULT_NUM_UPDATE_PROPS); - DEFINE_PROPERTY_REF(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, ParticleTransparent, particleTransparent, bool, false); - DEFINE_PROPERTY_REF(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, ParticleUpdateData, particleUpdateData, QString, ""); - DEFINE_PROPERTY_REF(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, ParticleRenderData, particleRenderData, QString, ""); - - // Model - DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString, ""); - DEFINE_PROPERTY_REF(PROP_MODEL_SCALE, ModelScale, modelScale, glm::vec3, glm::vec3(1.0f)); - DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS_SET, JointRotationsSet, jointRotationsSet, QVector, QVector()); - DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector, QVector()); - DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector, QVector()); - DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); - DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS); - DEFINE_PROPERTY_REF(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool, false); - DEFINE_PROPERTY_REF(PROP_BLENDSHAPE_COEFFICIENTS, BlendshapeCoefficients, blendshapeCoefficients, QString, ""); - DEFINE_PROPERTY_REF(PROP_USE_ORIGINAL_PIVOT, UseOriginalPivot, useOriginalPivot, bool, false); - DEFINE_PROPERTY_REF(PROP_LOAD_PRIORITY, LoadPriority, loadPriority, float, 0.0f); - DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); - - // Light - DEFINE_PROPERTY(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool, LightEntityItem::DEFAULT_IS_SPOTLIGHT); - DEFINE_PROPERTY(PROP_INTENSITY, Intensity, intensity, float, LightEntityItem::DEFAULT_INTENSITY); - DEFINE_PROPERTY(PROP_EXPONENT, Exponent, exponent, float, LightEntityItem::DEFAULT_EXPONENT); - DEFINE_PROPERTY(PROP_CUTOFF, Cutoff, cutoff, float, LightEntityItem::DEFAULT_CUTOFF); - DEFINE_PROPERTY(PROP_FALLOFF_RADIUS, FalloffRadius, falloffRadius, float, LightEntityItem::DEFAULT_FALLOFF_RADIUS); - - // Text - DEFINE_PROPERTY_REF(PROP_TEXT, Text, text, QString, TextEntityItem::DEFAULT_TEXT); - DEFINE_PROPERTY(PROP_LINE_HEIGHT, LineHeight, lineHeight, float, TextEntityItem::DEFAULT_LINE_HEIGHT); - DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, u8vec3Color, TextEntityItem::DEFAULT_TEXT_COLOR); - DEFINE_PROPERTY_REF(PROP_TEXT_ALPHA, TextAlpha, textAlpha, float, TextEntityItem::DEFAULT_TEXT_ALPHA); - DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, u8vec3Color, TextEntityItem::DEFAULT_BACKGROUND_COLOR); - DEFINE_PROPERTY_REF(PROP_BACKGROUND_ALPHA, BackgroundAlpha, backgroundAlpha, float, TextEntityItem::DEFAULT_TEXT_ALPHA); - DEFINE_PROPERTY_REF(PROP_LEFT_MARGIN, LeftMargin, leftMargin, float, TextEntityItem::DEFAULT_MARGIN); - DEFINE_PROPERTY_REF(PROP_RIGHT_MARGIN, RightMargin, rightMargin, float, TextEntityItem::DEFAULT_MARGIN); - DEFINE_PROPERTY_REF(PROP_TOP_MARGIN, TopMargin, topMargin, float, TextEntityItem::DEFAULT_MARGIN); - DEFINE_PROPERTY_REF(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float, TextEntityItem::DEFAULT_MARGIN); - DEFINE_PROPERTY_REF(PROP_FONT, Font, font, QString, ROBOTO_FONT_FAMILY); - DEFINE_PROPERTY_REF_ENUM(PROP_TEXT_EFFECT, TextEffect, textEffect, TextEffect, TextEffect::NO_EFFECT); - DEFINE_PROPERTY_REF(PROP_TEXT_EFFECT_COLOR, TextEffectColor, textEffectColor, u8vec3Color, TextEntityItem::DEFAULT_TEXT_COLOR); - DEFINE_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, TextEffectThickness, textEffectThickness, float, TextEntityItem::DEFAULT_TEXT_EFFECT_THICKNESS); - DEFINE_PROPERTY_REF_ENUM(PROP_TEXT_ALIGNMENT, Alignment, alignment, TextAlignment, TextAlignment::LEFT); - - // Zone - DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup); - DEFINE_PROPERTY_GROUP(AmbientLight, ambientLight, AmbientLightPropertyGroup); - DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup); - DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup); - DEFINE_PROPERTY_GROUP(Bloom, bloom, BloomPropertyGroup); - DEFINE_PROPERTY_GROUP(Audio, audio, ZoneAudioPropertyGroup); - DEFINE_PROPERTY_GROUP(Tonemapping, tonemapping, TonemappingPropertyGroup); - DEFINE_PROPERTY_GROUP(AmbientOcclusion, ambientOcclusion, AmbientOcclusionPropertyGroup); - DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED); - DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED); - DEFINE_PROPERTY(PROP_FILTER_URL, FilterURL, filterURL, QString, ZoneEntityItem::DEFAULT_FILTER_URL); - DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); - DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); - DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); - DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); - DEFINE_PROPERTY_REF_ENUM(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); - DEFINE_PROPERTY_REF_ENUM(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); - DEFINE_PROPERTY_REF_ENUM(PROP_SCREENSHARE, Screenshare, screenshare, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); - DEFINE_PROPERTY_REF_ENUM(PROP_TONEMAPPING_MODE, TonemappingMode, tonemappingMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); - DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_OCCLUSION_MODE, AmbientOcclusionMode, ambientOcclusionMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); - - // Polyvox - DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); - DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray, PolyVoxEntityItem::DEFAULT_VOXEL_DATA); - DEFINE_PROPERTY_REF(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t, PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE); - DEFINE_PROPERTY_REF(PROP_X_TEXTURE_URL, XTextureURL, xTextureURL, QString, ""); - DEFINE_PROPERTY_REF(PROP_Y_TEXTURE_URL, YTextureURL, yTextureURL, QString, ""); - DEFINE_PROPERTY_REF(PROP_Z_TEXTURE_URL, ZTextureURL, zTextureURL, QString, ""); - DEFINE_PROPERTY_REF(PROP_X_N_NEIGHBOR_ID, XNNeighborID, xNNeighborID, EntityItemID, UNKNOWN_ENTITY_ID); - DEFINE_PROPERTY_REF(PROP_Y_N_NEIGHBOR_ID, YNNeighborID, yNNeighborID, EntityItemID, UNKNOWN_ENTITY_ID); - DEFINE_PROPERTY_REF(PROP_Z_N_NEIGHBOR_ID, ZNNeighborID, zNNeighborID, EntityItemID, UNKNOWN_ENTITY_ID); - DEFINE_PROPERTY_REF(PROP_X_P_NEIGHBOR_ID, XPNeighborID, xPNeighborID, EntityItemID, UNKNOWN_ENTITY_ID); - DEFINE_PROPERTY_REF(PROP_Y_P_NEIGHBOR_ID, YPNeighborID, yPNeighborID, EntityItemID, UNKNOWN_ENTITY_ID); - DEFINE_PROPERTY_REF(PROP_Z_P_NEIGHBOR_ID, ZPNeighborID, zPNeighborID, EntityItemID, UNKNOWN_ENTITY_ID); - - // Web - DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, WebEntityItem::DEFAULT_SOURCE_URL); - DEFINE_PROPERTY_REF(PROP_DPI, DPI, dpi, uint16_t, ENTITY_ITEM_DEFAULT_DPI); - DEFINE_PROPERTY_REF(PROP_SCRIPT_URL, ScriptURL, scriptURL, QString, ""); - DEFINE_PROPERTY_REF(PROP_MAX_FPS, MaxFPS, maxFPS, uint8_t, WebEntityItem::DEFAULT_MAX_FPS); - DEFINE_PROPERTY_REF_ENUM(PROP_INPUT_MODE, InputMode, inputMode, WebInputMode, WebInputMode::TOUCH); - DEFINE_PROPERTY_REF(PROP_WANTS_KEYBOARD_FOCUS, WantsKeyboardFocus, wantsKeyboardFocus, bool, true); - DEFINE_PROPERTY_REF(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, ShowKeyboardFocusHighlight, showKeyboardFocusHighlight, bool, true); - DEFINE_PROPERTY_REF(PROP_WEB_USE_BACKGROUND, UseBackground, useBackground, bool, true); - DEFINE_PROPERTY_REF(PROP_USER_AGENT, UserAgent, userAgent, QString, WebEntityItem::DEFAULT_USER_AGENT); - - // Polyline - DEFINE_PROPERTY_REF(PROP_LINE_POINTS, LinePoints, linePoints, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); - DEFINE_PROPERTY(PROP_STROKE_WIDTHS, StrokeWidths, strokeWidths, QVector, QVector()); - DEFINE_PROPERTY(PROP_STROKE_NORMALS, Normals, normals, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); - DEFINE_PROPERTY(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); - DEFINE_PROPERTY(PROP_IS_UV_MODE_STRETCH, IsUVModeStretch, isUVModeStretch, bool, true); - DEFINE_PROPERTY(PROP_LINE_GLOW, Glow, glow, bool, false); - DEFINE_PROPERTY(PROP_LINE_FACE_CAMERA, FaceCamera, faceCamera, bool, false); - - // Shape - DEFINE_PROPERTY_REF(PROP_SHAPE, Shape, shape, QString, "Sphere"); - - // Material - DEFINE_PROPERTY_REF(PROP_MATERIAL_URL, MaterialURL, materialURL, QString, ""); - DEFINE_PROPERTY_REF_ENUM(PROP_MATERIAL_MAPPING_MODE, MaterialMappingMode, materialMappingMode, MaterialMappingMode, UV); - DEFINE_PROPERTY_REF(PROP_MATERIAL_PRIORITY, Priority, priority, quint16, 0); - DEFINE_PROPERTY_REF(PROP_PARENT_MATERIAL_NAME, ParentMaterialName, parentMaterialName, QString, "0"); - DEFINE_PROPERTY_REF(PROP_MATERIAL_MAPPING_POS, MaterialMappingPos, materialMappingPos, glm::vec2, glm::vec2(0.0f)); - DEFINE_PROPERTY_REF(PROP_MATERIAL_MAPPING_SCALE, MaterialMappingScale, materialMappingScale, glm::vec2, glm::vec2(1.0f)); - DEFINE_PROPERTY_REF(PROP_MATERIAL_MAPPING_ROT, MaterialMappingRot, materialMappingRot, float, 0); - DEFINE_PROPERTY_REF(PROP_MATERIAL_DATA, MaterialData, materialData, QString, ""); - DEFINE_PROPERTY_REF(PROP_MATERIAL_REPEAT, MaterialRepeat, materialRepeat, bool, true); - - // Image - DEFINE_PROPERTY_REF(PROP_IMAGE_URL, ImageURL, imageURL, QString, ""); - DEFINE_PROPERTY_REF(PROP_EMISSIVE, Emissive, emissive, bool, false); - DEFINE_PROPERTY_REF(PROP_KEEP_ASPECT_RATIO, KeepAspectRatio, keepAspectRatio, bool, true); - DEFINE_PROPERTY_REF(PROP_SUB_IMAGE, SubImage, subImage, QRect, QRect()); - - // Grid - DEFINE_PROPERTY_REF(PROP_GRID_FOLLOW_CAMERA, FollowCamera, followCamera, bool, true); - DEFINE_PROPERTY(PROP_MAJOR_GRID_EVERY, MajorGridEvery, majorGridEvery, uint32_t, GridEntityItem::DEFAULT_MAJOR_GRID_EVERY); - DEFINE_PROPERTY(PROP_MINOR_GRID_EVERY, MinorGridEvery, minorGridEvery, float, GridEntityItem::DEFAULT_MINOR_GRID_EVERY); - - // Gizmo - DEFINE_PROPERTY_REF_ENUM(PROP_GIZMO_TYPE, GizmoType, gizmoType, GizmoType, GizmoType::RING); - DEFINE_PROPERTY_GROUP(Ring, ring, RingGizmoPropertyGroup); - - // Sound - DEFINE_PROPERTY_REF(PROP_SOUND_URL, SoundURL, soundURL, QString, ""); - DEFINE_PROPERTY(PROP_SOUND_VOLUME, Volume, volume, float, 1.0f); - DEFINE_PROPERTY(PROP_SOUND_TIME_OFFSET, TimeOffset, timeOffset, float, 0.0f); - DEFINE_PROPERTY(PROP_SOUND_PITCH, Pitch, pitch, float, 1.0f); - DEFINE_PROPERTY(PROP_SOUND_PLAYING, Playing, playing, bool, true); - DEFINE_PROPERTY(PROP_SOUND_LOOP, Loop, loop, bool, true); - DEFINE_PROPERTY(PROP_SOUND_POSITIONAL, Positional, positional, bool, true); - DEFINE_PROPERTY(PROP_SOUND_LOCAL_ONLY, LocalOnly, localOnly, bool, false); - - static QString getComponentModeAsString(uint32_t mode); - -public: - float getMaxDimension() const { return glm::compMax(_dimensions); } - - float getAge() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; } - bool hasCreatedTime() const { return (_created != UNKNOWN_CREATED_TIME); } - - bool containsBoundsProperties() const { return (_positionChanged || _dimensionsChanged); } - bool containsPositionChange() const { return _positionChanged; } - bool containsDimensionsChange() const { return _dimensionsChanged; } - - static OctreeElement::AppendState encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties, - QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties); - - static bool encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer); - static bool encodeCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID, QByteArray& buffer); - static bool decodeCloneEntityMessage(const QByteArray& buffer, int& processedBytes, EntityItemID& entityIDToClone, EntityItemID& newEntityID); - - static bool decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes, - EntityItemID& entityID, EntityItemProperties& properties); - - void clearID() { _id = UNKNOWN_ENTITY_ID; _idSet = false; } - void markAllChanged(); - - const glm::vec3& getNaturalDimensions() const { return _naturalDimensions; } - void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; } - - const glm::vec3& getNaturalPosition() const { return _naturalPosition; } - void calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max); - - const QVariantMap& getTextureNames() const { return _textureNames; } - void setTextureNames(const QVariantMap& value) { _textureNames = value; } - - QString getSimulatorIDAsString() const { return _simulationOwner.getID().toString().mid(1,36).toUpper(); } - - void setVoxelDataDirty() { _voxelDataChanged = true; } - - void setLinePointsDirty() {_linePointsChanged = true; } - - void setQueryAACubeDirty() { _queryAACubeChanged = true; } - - void setLocationDirty() { _positionChanged = true; _rotationChanged = true; } - - bool hasTransformOrVelocityChanges() const; - void clearTransformOrVelocityChanges(); - bool hasMiscPhysicsChanges() const; - - bool hasSimulationRestrictedChanges() const; - void copySimulationRestrictedProperties(const EntityItemPointer& entity); - void clearSimulationRestrictedProperties(); - - void clearSimulationOwner(); - void setSimulationOwner(const QUuid& id, uint8_t priority); - void setSimulationOwner(const QByteArray& data); - void setSimulationPriority(uint8_t priority) { _simulationOwner.setPriority(priority); } - uint8_t computeSimulationBidPriority() const; - - void setActionDataDirty() { _actionDataChanged = true; } - - QList listChangedProperties(); - - bool getDimensionsInitialized() const { return _dimensionsInitialized; } - void setDimensionsInitialized(bool dimensionsInitialized) { _dimensionsInitialized = dimensionsInitialized; } - - void setJointRotationsDirty() { _jointRotationsSetChanged = true; _jointRotationsChanged = true; } - void setJointTranslationsDirty() { _jointTranslationsSetChanged = true; _jointTranslationsChanged = true; } - - // render info related items - size_t getRenderInfoVertexCount() const { return _renderInfoVertexCount; } - void setRenderInfoVertexCount(size_t value) { _renderInfoVertexCount = value; } - int getRenderInfoTextureCount() const { return _renderInfoTextureCount; } - void setRenderInfoTextureCount(int value) { _renderInfoTextureCount = value; } - size_t getRenderInfoTextureSize() const { return _renderInfoTextureSize; } - void setRenderInfoTextureSize(size_t value) { _renderInfoTextureSize = value; } - int getRenderInfoDrawCalls() const { return _renderInfoDrawCalls; } - void setRenderInfoDrawCalls(int value) { _renderInfoDrawCalls = value; } - bool getRenderInfoHasTransparent() const { return _renderInfoHasTransparent; } - void setRenderInfoHasTransparent(bool value) { _renderInfoHasTransparent = value; } - - void setPackedNormals(const QByteArray& value); - QVector unpackNormals(const QByteArray& normals); - - void setPackedStrokeColors(const QByteArray& value); - QVector unpackStrokeColors(const QByteArray& strokeColors); - - QByteArray getPackedNormals() const; - QByteArray packNormals(const QVector& normals) const; - - QByteArray getPackedStrokeColors() const; - QByteArray packStrokeColors(const QVector& strokeColors) const; - - void convertToCloneProperties(const EntityItemID& entityIDToClone); - -protected: - QString getCollisionMaskAsString() const; - void setCollisionMaskFromString(const QString& maskString); - - QVector getTagsAsVector() const; - void setTagsFromVector(const QVector& tags); - -private: - QUuid _id; - bool _idSet; - quint64 _lastEdited; - EntityTypes::EntityType _type; - void setType(const QString& typeName) { _type = EntityTypes::getEntityTypeFromName(typeName); } - - bool _defaultSettings; - bool _dimensionsInitialized = true; // Only false if creating an entity locally with no dimensions properties - - // NOTE: The following are pseudo client only properties. They are only used in clients which can access - // properties of model geometry. But these properties are not serialized like other properties. - QVariantMap _textureNames; - glm::vec3 _naturalDimensions; - glm::vec3 _naturalPosition; - - size_t _renderInfoVertexCount { 0 }; - int _renderInfoTextureCount { 0 }; - size_t _renderInfoTextureSize { 0 }; - int _renderInfoDrawCalls { 0 }; - bool _renderInfoHasTransparent { false }; - - EntityPropertyFlags _desiredProperties; // if set will narrow scopes of copy/to/from to just these properties -}; - -Q_DECLARE_METATYPE(EntityItemProperties); -ScriptValue EntityItemPropertiesToScriptValue(ScriptEngine* engine, const EntityItemProperties& properties); -ScriptValue EntityItemNonDefaultPropertiesToScriptValue(ScriptEngine* engine, const EntityItemProperties& properties); -bool EntityItemPropertiesFromScriptValueIgnoreReadOnly(const ScriptValue& object, EntityItemProperties& properties); -bool EntityItemPropertiesFromScriptValueHonorReadOnly(const ScriptValue& object, EntityItemProperties& properties); - -Q_DECLARE_METATYPE(EntityPropertyFlags); -ScriptValue EntityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags); -bool EntityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags); - -Q_DECLARE_METATYPE(EntityPropertyInfo); -ScriptValue EntityPropertyInfoToScriptValue(ScriptEngine* engine, const EntityPropertyInfo& propertyInfo); -bool EntityPropertyInfoFromScriptValue(const ScriptValue& object, EntityPropertyInfo& propertyInfo); - -// define these inline here so the macros work -inline void EntityItemProperties::setPosition(const glm::vec3& value) - { _position = glm::clamp(value, (float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); _positionChanged = true; } - -inline void EntityItemProperties::setOwningAvatarID(const QUuid& id) { - _owningAvatarID = id; - if (!_owningAvatarID.isNull()) { - // for AvatarEntities there's no entity-server to tell us we're the simulation owner, - // so always set the simulationOwner to the owningAvatarID and a high priority. - setSimulationOwner(_owningAvatarID, AVATAR_ENTITY_SIMULATION_PRIORITY); - } - _owningAvatarIDChanged = true; -} - -QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f); - -inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { - debug << "EntityItemProperties[" << "\n"; - - debug << " _type:" << properties.getType() << "\n"; - - // TODO: figure out why position and animationSettings don't seem to like the macro approach - if (properties.containsPositionChange()) { - debug << " position:" << properties.getPosition() << "in meters" << "\n"; - } - - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Dimensions, dimensions, "in meters"); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Velocity, velocity, "in meters"); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Name, name, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Visible, visible, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, CanCastShadow, canCastShadow, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Rotation, rotation, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Density, density, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Gravity, gravity, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Acceleration, acceleration, "in meters per second"); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Damping, damping, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Restitution, restitution, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Friction, friction, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Created, created, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Lifetime, lifetime, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Script, script, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ScriptTimestamp, scriptTimestamp, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, CollisionSoundURL, collisionSoundURL, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Color, color, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ColorSpread, colorSpread, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ColorStart, colorStart, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ColorFinish, colorFinish, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Alpha, alpha, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, AlphaSpread, alphaSpread, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, AlphaStart, alphaStart, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, AlphaFinish, alphaFinish, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ModelURL, modelURL, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, CompoundShapeURL, compoundShapeURL, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, RegistrationPoint, registrationPoint, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, AngularVelocity, angularVelocity, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, AngularDamping, angularDamping, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Collisionless, collisionless, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Dynamic, dynamic, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, IsSpotlight, isSpotlight, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Intensity, intensity, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, FalloffRadius, falloffRadius, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Exponent, exponent, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Cutoff, cutoff, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Locked, locked, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Textures, textures, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, UserData, userData, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, PrivateUserData, privateUserData, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, SimulationOwner, simulationOwner, SimulationOwner()); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Text, text, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, LineHeight, lineHeight, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, TextColor, textColor, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundColor, backgroundColor, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ShapeType, shapeType, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, MaxParticles, maxParticles, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Lifespan, lifespan, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, IsEmitting, isEmitting, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitRate, emitRate, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitSpeed, emitSpeed, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, SpeedSpread, speedSpread, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitOrientation, emitOrientation, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitDimensions, emitDimensions, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitRadiusStart, emitRadiusStart, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, PolarStart, polarStart, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, PolarFinish, polarFinish, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, AzimuthStart, azimuthStart, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, AzimuthFinish, azimuthFinish, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitAcceleration, emitAcceleration, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, AccelerationSpread, accelerationSpread, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParticleRadius, particleRadius, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, RadiusSpread, radiusSpread, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, RadiusStart, radiusStart, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, RadiusFinish, radiusFinish, ""); - - DEBUG_PROPERTY_IF_CHANGED(debug, properties, LocalPosition, localPosition, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, LocalRotation, localRotation, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, LocalVelocity, localVelocity, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, LocalAngularVelocity, localAngularVelocity, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, LocalDimensions, localDimensions, ""); - - DEBUG_PROPERTY_IF_CHANGED(debug, properties, HazeMode, hazeMode, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, KeyLightMode, keyLightMode, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, AmbientLightMode, ambientLightMode, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, SkyboxMode, skyboxMode, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, BloomMode, bloomMode, ""); - - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Cloneable, cloneable, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneLifetime, cloneLifetime, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneLimit, cloneLimit, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneDynamic, cloneDynamic, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneAvatarEntity, cloneAvatarEntity, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneOriginID, cloneOriginID, ""); - - DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Href, href, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Description, description, ""); - if (properties.actionDataChanged()) { - debug << " " << "actionData" << ":" << properties.getActionData().toHex() << "" << "\n"; - } - DEBUG_PROPERTY_IF_CHANGED(debug, properties, XTextureURL, xTextureURL, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, YTextureURL, yTextureURL, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZTextureURL, zTextureURL, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, XNNeighborID, xNNeighborID, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, YNNeighborID, yNNeighborID, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZNNeighborID, zNNeighborID, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, XPNeighborID, xPNeighborID, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, YPNeighborID, yPNeighborID, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZPNeighborID, zPNeighborID, ""); - - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParentID, parentID, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParentJointIndex, parentJointIndex, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, QueryAACube, queryAACube, ""); - - DEBUG_PROPERTY_IF_CHANGED(debug, properties, JointRotationsSet, jointRotationsSet, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, JointRotations, jointRotations, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, JointTranslationsSet, jointTranslationsSet, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, JointTranslations, jointTranslations, ""); - - DEBUG_PROPERTY_IF_CHANGED(debug, properties, FlyingAllowed, flyingAllowed, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, GhostingAllowed, ghostingAllowed, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, FilterURL, filterURL, ""); - - DEBUG_PROPERTY_IF_CHANGED(debug, properties, AvatarPriority, avatarPriority, ""); - - DEBUG_PROPERTY_IF_CHANGED(debug, properties, Screenshare, screenshare, ""); - - DEBUG_PROPERTY_IF_CHANGED(debug, properties, EntityHostTypeAsString, entityHostType, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, OwningAvatarID, owningAvatarID, ""); - - DEBUG_PROPERTY_IF_CHANGED(debug, properties, LastEditedBy, lastEditedBy, ""); - - debug << " last edited:" << properties.getLastEdited() << "\n"; - debug << " edited ago:" << properties.getEditedAgo() << "\n"; - debug << "]"; - - return debug; -} - -#endif // hifi_EntityItemProperties_h diff --git a/libraries/entities/src/EntityItemProperties.h.in b/libraries/entities/src/EntityItemProperties.h.in new file mode 100644 index 0000000000..9c904837df --- /dev/null +++ b/libraries/entities/src/EntityItemProperties.h.in @@ -0,0 +1,289 @@ +// +// EntityItemProperties.h +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_EntityItemProperties_h +#define hifi_EntityItemProperties_h + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "FontFamilies.h" +#include + +#include +#include "EntityItemPropertiesDefaults.h" +#include "EntityItemPropertiesMacros.h" +#include "EntityTypes.h" +#include "EntityPropertyFlags.h" +#include "EntityPseudoPropertyFlags.h" +#include "SimulationOwner.h" + +@ENTITY_ITEM_PROPERTY_INCLUDES@ + +#include "MaterialMappingMode.h" +#include "BillboardMode.h" +#include "RenderLayer.h" +#include "PrimitiveMode.h" +#include "WebInputMode.h" +#include "GizmoType.h" +#include "TextEffect.h" +#include "TextAlignment.h" +#include "MirrorMode.h" +#include "EntityShape.h" + +class ScriptEngine; + +/// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an +/// entity and a JavaScript style hash/ScriptValue storing a set of properties. Used in scripting to set/get the complete +/// set of entity item properties via JavaScript hashes/QScriptValues +/// all units for SI units (meter, second, radian, etc) +class EntityItemProperties { + // TODO: consider removing these friend relationship and use public methods +@ENTITY_ITEM_PROPERTY_FRIENDS@ +public: + static bool blobToProperties(ScriptEngine& scriptEngine, const QByteArray& blob, EntityItemProperties& properties); + static void propertiesToBlob(ScriptEngine& scriptEngine, const QUuid& myAvatarID, const EntityItemProperties& properties, + QByteArray& blob, bool allProperties = false); + + EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()); + EntityItemProperties(const EntityItemProperties&) = default; + + virtual ~EntityItemProperties() = default; + + void merge(const EntityItemProperties& other); + + EntityTypes::EntityType getType() const { return _type; } + void setType(EntityTypes::EntityType type) { _type = type; } + + virtual ScriptValue copyToScriptValue(ScriptEngine* engine, bool skipDefaults, bool allowUnknownCreateTime = false, + bool strictSemantics = false, EntityPseudoPropertyFlags pseudoPropertyFlags = EntityPseudoPropertyFlags()) const; + virtual void copyFromScriptValue(const ScriptValue& object, bool honorReadOnly); + void copyFromJSONString(ScriptEngine& scriptEngine, const QString& jsonString); + + static ScriptValue entityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags); + static bool entityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags); + + static bool getPropertyInfo(const QString& propertyName, EntityPropertyInfo& propertyInfo); + + // editing related features supported by all entities + quint64 getLastEdited() const { return _lastEdited; } + float getEditedAgo() const /// Elapsed seconds since this entity was last edited + { return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; } + + EntityPropertyFlags getChangedProperties() const; + + bool transformChanged() const; + bool getScalesWithParent() const; + bool parentRelatedPropertyChanged() const; + bool queryAACubeRelatedPropertyChanged() const; + bool grabbingRelatedPropertyChanged() const; + + AABox getAABox() const; + + void debugDump() const; + void setLastEdited(quint64 usecTime); + EntityPropertyFlags getDesiredProperties() { return _desiredProperties; } + void setDesiredProperties(EntityPropertyFlags properties) { _desiredProperties = properties; } + + bool constructFromBuffer(const unsigned char* data, int dataLength); + +@ENTITY_ITEM_PROPERTY_DEFINES@ + + static QString getComponentModeAsString(uint32_t mode); + +public: + float getMaxDimension() const { return glm::compMax(_dimensions); } + + float getAge() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; } + bool hasCreatedTime() const { return (_created != UNKNOWN_CREATED_TIME); } + + bool containsBoundsProperties() const { return (_positionChanged || _dimensionsChanged); } + bool containsPositionChange() const { return _positionChanged; } + bool containsDimensionsChange() const { return _dimensionsChanged; } + + static OctreeElement::AppendState encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties, + QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties); + + static bool encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer); + static bool encodeCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID, QByteArray& buffer); + static bool decodeCloneEntityMessage(const QByteArray& buffer, int& processedBytes, EntityItemID& entityIDToClone, EntityItemID& newEntityID); + + static bool decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes, + EntityItemID& entityID, EntityItemProperties& properties); + + void clearID() { _id = UNKNOWN_ENTITY_ID; _idSet = false; } + void markAllChanged(); + + const glm::vec3& getNaturalDimensions() const { return _naturalDimensions; } + void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; } + + const glm::vec3& getNaturalPosition() const { return _naturalPosition; } + void calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max); + + const QVariantMap& getTextureNames() const { return _textureNames; } + void setTextureNames(const QVariantMap& value) { _textureNames = value; } + + QString getSimulatorIDAsString() const { return _simulationOwner.getID().toString().mid(1,36).toUpper(); } + + void setVoxelDataDirty() { _voxelDataChanged = true; } + + void setLinePointsDirty() {_linePointsChanged = true; } + + void setQueryAACubeDirty() { _queryAACubeChanged = true; } + + void setLocationDirty() { _positionChanged = true; _rotationChanged = true; } + + bool hasTransformOrVelocityChanges() const; + void clearTransformOrVelocityChanges(); + bool hasMiscPhysicsChanges() const; + + bool hasSimulationRestrictedChanges() const; + void copySimulationRestrictedProperties(const EntityItemPointer& entity); + void clearSimulationRestrictedProperties(); + + void clearSimulationOwner(); + void setSimulationOwner(const QUuid& id, uint8_t priority); + void setSimulationOwner(const QByteArray& data); + void setSimulationPriority(uint8_t priority) { _simulationOwner.setPriority(priority); } + uint8_t computeSimulationBidPriority() const; + + void setActionDataDirty() { _actionDataChanged = true; } + + QList listChangedProperties(); + + bool getDimensionsInitialized() const { return _dimensionsInitialized; } + void setDimensionsInitialized(bool dimensionsInitialized) { _dimensionsInitialized = dimensionsInitialized; } + + void setJointRotationsDirty() { _jointRotationsSetChanged = true; _jointRotationsChanged = true; } + void setJointTranslationsDirty() { _jointTranslationsSetChanged = true; _jointTranslationsChanged = true; } + + // render info related items + size_t getRenderInfoVertexCount() const { return _renderInfoVertexCount; } + void setRenderInfoVertexCount(size_t value) { _renderInfoVertexCount = value; } + int getRenderInfoTextureCount() const { return _renderInfoTextureCount; } + void setRenderInfoTextureCount(int value) { _renderInfoTextureCount = value; } + size_t getRenderInfoTextureSize() const { return _renderInfoTextureSize; } + void setRenderInfoTextureSize(size_t value) { _renderInfoTextureSize = value; } + int getRenderInfoDrawCalls() const { return _renderInfoDrawCalls; } + void setRenderInfoDrawCalls(int value) { _renderInfoDrawCalls = value; } + bool getRenderInfoHasTransparent() const { return _renderInfoHasTransparent; } + void setRenderInfoHasTransparent(bool value) { _renderInfoHasTransparent = value; } + + void setPackedNormals(const QByteArray& value); + QVector unpackNormals(const QByteArray& normals); + + void setPackedStrokeColors(const QByteArray& value); + QVector unpackStrokeColors(const QByteArray& strokeColors); + + QByteArray getPackedNormals() const; + QByteArray packNormals(const QVector& normals) const; + + QByteArray getPackedStrokeColors() const; + QByteArray packStrokeColors(const QVector& strokeColors) const; + + void convertToCloneProperties(const EntityItemID& entityIDToClone); + +protected: + QString getCollisionMaskAsString() const; + void setCollisionMaskFromString(const QString& maskString); + + QVector getTagsAsVector() const; + void setTagsFromVector(const QVector& tags); + +private: + QUuid _id; + bool _idSet; + quint64 _lastEdited; + EntityTypes::EntityType _type; + void setType(const QString& typeName) { _type = EntityTypes::getEntityTypeFromName(typeName); } + + bool _defaultSettings; + bool _dimensionsInitialized = true; // Only false if creating an entity locally with no dimensions properties + + // NOTE: The following are pseudo client only properties. They are only used in clients which can access + // properties of model geometry. But these properties are not serialized like other properties. + QVariantMap _textureNames; + glm::vec3 _naturalDimensions; + glm::vec3 _naturalPosition; + + size_t _renderInfoVertexCount { 0 }; + int _renderInfoTextureCount { 0 }; + size_t _renderInfoTextureSize { 0 }; + int _renderInfoDrawCalls { 0 }; + bool _renderInfoHasTransparent { false }; + + EntityPropertyFlags _desiredProperties; // if set will narrow scopes of copy/to/from to just these properties +}; + +Q_DECLARE_METATYPE(EntityItemProperties); +ScriptValue EntityItemPropertiesToScriptValue(ScriptEngine* engine, const EntityItemProperties& properties); +ScriptValue EntityItemNonDefaultPropertiesToScriptValue(ScriptEngine* engine, const EntityItemProperties& properties); +bool EntityItemPropertiesFromScriptValueIgnoreReadOnly(const ScriptValue& object, EntityItemProperties& properties); +bool EntityItemPropertiesFromScriptValueHonorReadOnly(const ScriptValue& object, EntityItemProperties& properties); + +Q_DECLARE_METATYPE(EntityPropertyFlags); +ScriptValue EntityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags); +bool EntityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags); + +Q_DECLARE_METATYPE(EntityPropertyInfo); +ScriptValue EntityPropertyInfoToScriptValue(ScriptEngine* engine, const EntityPropertyInfo& propertyInfo); +bool EntityPropertyInfoFromScriptValue(const ScriptValue& object, EntityPropertyInfo& propertyInfo); + +// define these inline here so the macros work +inline void EntityItemProperties::setPosition(const glm::vec3& value) + { _position = glm::clamp(value, (float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); _positionChanged = true; } + +inline void EntityItemProperties::setOwningAvatarID(const QUuid& id) { + _owningAvatarID = id; + if (!_owningAvatarID.isNull()) { + // for AvatarEntities there's no entity-server to tell us we're the simulation owner, + // so always set the simulationOwner to the owningAvatarID and a high priority. + setSimulationOwner(_owningAvatarID, AVATAR_ENTITY_SIMULATION_PRIORITY); + } + _owningAvatarIDChanged = true; +} + +QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f); + +inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { + debug << "EntityItemProperties[" << "\n"; + + debug << " _type:" << properties.getType() << "\n"; + +@ENTITY_ITEM_PROPERTY_DEBUG@ + + debug << " last edited:" << properties.getLastEdited() << "\n"; + debug << " edited ago:" << properties.getEditedAgo() << "\n"; + debug << "]"; + + return debug; +} + +#endif // hifi_EntityItemProperties_h diff --git a/libraries/entities/src/EntityItemProperties.txt b/libraries/entities/src/EntityItemProperties.txt new file mode 100644 index 0000000000..9c631b9fef --- /dev/null +++ b/libraries/entities/src/EntityItemProperties.txt @@ -0,0 +1,268 @@ +Core +enum:SIMULATION_OWNER prop:simulationOwner type:SimulationOwner default:SimulationOwner() noScript readType:QByteArray networkGetter:properties._simulationOwner.toByteArray() variableNetworkGetter:_simulationOwner.toByteArray() noVariableNetworkSetter, +enum:PARENT_ID prop:parentID type:QUuid default:UNKNOWN_ENTITY_ID inherited variableNetworkGetter:actualParentID noVariableNetworkSetter, +enum:PARENT_JOINT_INDEX prop:parentJointIndex type:uint16_t default:ENTITY_ITEM_DEFAULT_PARENT_INDEX inherited noVariableNetworkSetter, +enum:VISIBLE prop:visible type:bool default:ENTITY_ITEM_DEFAULT_VISIBLE renderProp, +enum:NAME prop:name type:QString default:ENTITY_ITEM_DEFAULT_NAME noGetterSetterProp, +enum:LOCKED prop:locked type:bool default:ENTITY_ITEM_DEFAULT_LOCKED, +enum:USER_DATA prop:userData type:QString default:ENTITY_ITEM_DEFAULT_USER_DATA noGetterSetterProp, +enum:PRIVATE_USER_DATA prop:privateUserData type:QString default:ENTITY_ITEM_DEFAULT_PRIVATE_USER_DATA variableNetworkGetter:privateUserData basicProp, +enum:HREF prop:href type:QString default:"", +enum:DESCRIPTION prop:description type:QString default:"" basicProp, +enum:POSITION prop:position type:vec3 default:ENTITY_ITEM_ZERO_VEC3 propertySetter inherited variableCopyGetter:getLocalPosition variableNetworkGetter:getLocalPosition() variableNetworkSetter:customUpdatePositionFromNetwork debugString:"meters" debugGetter:getWorldPosition(), +enum:DIMENSIONS prop:dimensions type:vec3 default:ENTITY_ITEM_DEFAULT_DIMENSIONS min:ENTITY_ITEM_MIN_DIMENSION max:FLT_MAX inherited variableCopyGetter:getScaledDimensions variableCopySetter:setScaledDimensions variableNetworkGetter:getScaledDimensions() variableNetworkSetter:setScaledDimensions debugString:"meters" debugGetter:getScaledDimensions(), +enum:ROTATION prop:rotation type:quat default:ENTITY_ITEM_DEFAULT_ROTATION inherited variableCopyGetter:getLocalOrientation variableNetworkGetter:getLocalOrientation() variableNetworkSetter:customUpdateRotationFromNetwork debugGetter:getWorldOrientation(), +enum:REGISTRATION_POINT prop:registrationPoint type:vec3 default:ENTITY_ITEM_DEFAULT_REGISTRATION_POINT min:ENTITY_ITEM_MIN_REGISTRATION_POINT max:ENTITY_ITEM_MAX_REGISTRATION_POINT inherited, +enum:CREATED prop:created type:quint64 default:UNKNOWN_CREATED_TIME readOnly, +enum:LAST_EDITED_BY prop:lastEditedBy type:QUuid default:ENTITY_ITEM_DEFAULT_LAST_EDITED_BY readOnly basicProp, +enum:ENTITY_HOST_TYPE prop:entityHostType type:entity::HostType default:entity::HostType::DOMAIN enum readOnly noNetwork basicProp, +enum:OWNING_AVATAR_ID prop:owningAvatarID type:QUuid default:UNKNOWN_ENTITY_ID propertySetter variableCopyGetter:getOwningAvatarIDForProperties readOnly noNetwork, +enum:QUERY_AA_CUBE prop:queryAACube type:AACube default:AACube() inherited variableNetworkSetter:customUpdateQueryAACubeFromNetwork, +enum:CAN_CAST_SHADOW prop:canCastShadow type:bool default:ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW renderProp, +enum:VISIBLE_IN_SECONDARY_CAMERA prop:isVisibleInSecondaryCamera type:bool default:ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA noNetwork renderProp, +enum:RENDER_LAYER prop:renderLayer type:RenderLayer default:RenderLayer::WORLD enum renderProp, +enum:PRIMITIVE_MODE prop:primitiveMode type:PrimitiveMode default:PrimitiveMode::SOLID enum renderProp, +enum:IGNORE_PICK_INTERSECTION prop:ignorePickIntersection type:bool default:false basicProp, +enum:RENDER_WITH_ZONES prop:renderWithZones type:qVectorQUuid default:QVector(), +enum:BILLBOARD_MODE prop:billboardMode type:BillboardMode default:BillboardMode::NONE enum renderProp, +enum:TAGS prop:tags type:qSetQString default:QSet() fromScriptType:qVectorQString getter:getTagsAsVector setter:setTagsFromVector basicProp, +group:grab, +enum:MIRROR_MODE prop:mirrorMode type:MirrorMode default:MirrorMode::NONE enum renderProp, +enum:PORTAL_EXIT_ID prop:portalExitID type:QUuid default:UNKNOWN_ENTITY_ID renderProp, +Physics +enum:DENSITY prop:density type:float default:ENTITY_ITEM_DEFAULT_DENSITY min:ENTITY_ITEM_MIN_DENSITY max:ENTITY_ITEM_MIN_DENSITY, +enum:VELOCITY prop:velocity type:vec3 default:ENTITY_ITEM_DEFAULT_VELOCITY inherited variableCopyGetter:getLocalVelocity variableNetworkGetter:getLocalVelocity() variableNetworkSetter:customUpdateVelocityFromNetwork debugString:"m/s" debugGetter:getWorldVelocity(), +enum:ANGULAR_VELOCITY prop:angularVelocity type:vec3 default:ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY inherited variableCopyGetter:getLocalAngularVelocity variableNetworkGetter:getLocalAngularVelocity() variableNetworkSetter:customUpdateAngularVelocityFromNetwork debugGetter:getWorldAngularVelocity(), +enum:GRAVITY prop:gravity type:vec3 default:ENTITY_ITEM_DEFAULT_GRAVITY debugString:"m/s^2", +enum:ACCELERATION prop:acceleration type:vec3 default:ENTITY_ITEM_DEFAULT_ACCELERATION variableNetworkSetter:customSetAcceleration basicProp debugString:"m/s^2", +enum:DAMPING prop:damping type:float default:ENTITY_ITEM_DEFAULT_DAMPING min:ENTITY_ITEM_MIN_DAMPING max:ENTITY_ITEM_MAX_DAMPING, +enum:ANGULAR_DAMPING prop:angularDamping type:float default:ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING min:ENTITY_ITEM_MIN_DAMPING max:ENTITY_ITEM_MAX_DAMPING, +enum:RESTITUTION prop:restitution type:float default:ENTITY_ITEM_DEFAULT_RESTITUTION min:ENTITY_ITEM_MIN_RESTITUTION max:ENTITY_ITEM_MAX_RESTITUTION, +enum:FRICTION prop:friction type:float default:ENTITY_ITEM_DEFAULT_FRICTION min:ENTITY_ITEM_MIN_FRICTION max:ENTITY_ITEM_MAX_FRICTION, +enum:LIFETIME prop:lifetime type:float default:ENTITY_ITEM_DEFAULT_LIFETIME debugString:"seconds", +enum:COLLISIONLESS prop:collisionless type:bool default:ENTITY_ITEM_DEFAULT_COLLISIONLESS, +enum:COLLISIONLESS prop:ignoreForCollisions proxy:collisionless type:bool fromScriptType:bool legacy getter:getCollisionless setter:setCollisionless, +enum:COLLISION_MASK prop:collisionMask type:uint16_t default:ENTITY_COLLISION_MASK_DEFAULT, +enum:COLLISION_MASK prop:collidesWith proxy:collisionMask type:CollisionMask proxyType:uint16_t enum legacy getter:getCollisionMaskAsString, +enum:DYNAMIC prop:dynamic type:bool default:ENTITY_ITEM_DEFAULT_DYNAMIC, +enum:DYNAMIC prop:collisionWillMove proxy:dynamic type:bool fromScriptType:bool legacy getter:getDynamic setter:setDynamic, +enum:COLLISION_SOUND_URL prop:collisionSoundURL type:QString default:ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL urlPermission, +enum:ACTION_DATA prop:actionData type:QByteArray default:QByteArray() readOnly inherited variableCopyGetter:getDynamicData variableCopySetter:setDynamicData variableNetworkGetter:getDynamicData() variableNetworkSetter:setDynamicData debugGetter:getDynamicData(), +Cloning +enum:CLONEABLE prop:cloneable type:bool default:ENTITY_ITEM_DEFAULT_CLONEABLE basicProp, +enum:CLONE_LIFETIME prop:cloneLifetime type:float default:ENTITY_ITEM_DEFAULT_CLONE_LIFETIME basicProp debugString:"seconds", +enum:CLONE_LIMIT prop:cloneLimit type:float default:ENTITY_ITEM_DEFAULT_CLONE_LIMIT basicProp, +enum:CLONE_DYNAMIC prop:cloneDynamic type:bool default:ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC basicProp, +enum:CLONE_AVATAR_ENTITY prop:cloneAvatarEntity type:bool default:ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY basicProp, +enum:CLONE_ORIGIN_ID prop:cloneOriginID type:QUuid default:ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID basicProp, +Scripts +enum:SCRIPT prop:script type:QString default:ENTITY_ITEM_DEFAULT_SCRIPT basicProp, +enum:SCRIPT_TIMESTAMP prop:scriptTimestamp type:quint64 default:ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP, +enum:SERVER_SCRIPTS prop:serverScripts type:QString default:ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS noVariableNetworkSetter, +LocalProps +enum:LOCAL_POSITION prop:localPosition type:vec3 default:ENTITY_ITEM_ZERO_VEC3 noNetwork inherited debugString:"meters", +enum:LOCAL_ROTATION prop:localRotation type:quat default:ENTITY_ITEM_DEFAULT_ROTATION noNetwork inherited variableCopyGetter:getLocalOrientation debugGetter:getLocalOrientation(), +enum:LOCAL_VELOCITY prop:localVelocity type:vec3 default:ENTITY_ITEM_ZERO_VEC3 noNetwork inherited debugString:"m/s", +enum:LOCAL_ANGULAR_VELOCITY prop:localAngularVelocity type:vec3 default:ENTITY_ITEM_ZERO_VEC3 noNetwork inherited, +enum:LOCAL_DIMENSIONS prop:localDimensions type:vec3 default:ENTITY_ITEM_ZERO_VEC3 min:ENTITY_ITEM_MIN_DIMENSION max:FLT_MAX noNetwork inherited variableCopyGetter:getUnscaledDimensions debugString:"meters" debugGetter:getUnscaledDimensions(), +Common +enum:SHAPE_TYPE prop:shapeType type:ShapeType enum default:SHAPE_TYPE_NONE, +enum:COMPOUND_SHAPE_URL prop:compoundShapeURL type:QString default:"" urlPermission, +enum:COLOR prop:color type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR, +enum:ALPHA prop:alpha type:float default:ENTITY_ITEM_DEFAULT_ALPHA min:0.0f max:1.0f, +enum:UNLIT prop:unlit type:bool default:false, +group:pulse, +enum:TEXTURES prop:textures type:QString default:"", +enum:LINE_POINTS prop:linePoints type:qVectorVec3 default:ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC, +ParticleEffect +enum:SHAPE_TYPE prop:shapeType type:ShapeType enum default:SHAPE_TYPE_NONE common noGetterSetterProp, +enum:COMPOUND_SHAPE_URL prop:compoundShapeURL type:QString default:"" urlPermission common renderProp, +enum:COLOR prop:color type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR common, +enum:ALPHA prop:alpha type:float default:ENTITY_ITEM_DEFAULT_ALPHA min:0.0f max:1.0f common, +group:pulse common renderUpdateOnSet, +enum:TEXTURES prop:textures type:QString default:"" common, +enum:MAX_PARTICLES prop:maxParticles type:quint32 default:particle::DEFAULT_MAX_PARTICLES min:particle::MINIMUM_MAX_PARTICLES max:particle::MAXIMUM_MAX_PARTICLES, +enum:LIFESPAN prop:lifespan type:float default:particle::DEFAULT_LIFESPAN min:particle::MINIMUM_LIFESPAN max:particle::MAXIMUM_LIFESPAN, +enum:EMITTING_PARTICLES prop:isEmitting type:bool default:true renderProp, +enum:EMIT_RATE prop:emitRate type:float default:particle::DEFAULT_EMIT_RATE min:particle::MINIMUM_EMIT_RATE max:particle::MAXIMUM_EMIT_RATE, +enum:EMIT_SPEED prop:emitSpeed type:float default:particle::DEFAULT_EMIT_SPEED min:particle::MINIMUM_EMIT_SPEED max:particle::MAXIMUM_EMIT_SPEED, +enum:SPEED_SPREAD prop:speedSpread type:float default:particle::DEFAULT_SPEED_SPREAD min:particle::MINIMUM_EMIT_SPEED max:particle::MAXIMUM_EMIT_SPEED, +enum:EMIT_ORIENTATION prop:emitOrientation type:quat default:particle::DEFAULT_EMIT_ORIENTATION, +enum:EMIT_DIMENSIONS prop:emitDimensions type:vec3 default:particle::DEFAULT_EMIT_DIMENSIONS min:particle::MINIMUM_EMIT_DIMENSION max:particle::MAXIMUM_EMIT_DIMENSION, +enum:EMIT_RADIUS_START prop:emitRadiusStart type:float default:particle::DEFAULT_EMIT_RADIUS_START min:particle::MINIMUM_EMIT_RADIUS_START max:particle::MAXIMUM_EMIT_RADIUS_START, +enum:POLAR_START prop:polarStart type:float default:particle::DEFAULT_POLAR_START min:particle::MINIMUM_POLAR max:particle::MAXIMUM_POLAR, +enum:POLAR_FINISH prop:polarFinish type:float default:particle::DEFAULT_POLAR_FINISH min:particle::MINIMUM_POLAR max:particle::MAXIMUM_POLAR, +enum:AZIMUTH_START prop:azimuthStart type:float default:particle::DEFAULT_AZIMUTH_START min:particle::MINIMUM_AZIMUTH max:particle::MAXIMUM_AZIMUTH, +enum:AZIMUTH_FINISH prop:azimuthFinish type:float default:particle::DEFAULT_AZIMUTH_FINISH min:particle::MINIMUM_AZIMUTH max:particle::MAXIMUM_AZIMUTH, +enum:EMIT_ACCELERATION prop:emitAcceleration type:vec3 default:particle::DEFAULT_EMIT_ACCELERATION min:particle::MINIMUM_EMIT_ACCELERATION max:particle::MAXIMUM_EMIT_ACCELERATION, +enum:ACCELERATION_SPREAD prop:accelerationSpread type:vec3 default:particle::DEFAULT_ACCELERATION_SPREAD min:particle::MINIMUM_ACCELERATION_SPREAD max:particle::MAXIMUM_ACCELERATION_SPREAD, +enum:PARTICLE_RADIUS prop:particleRadius type:float default:particle::DEFAULT_PARTICLE_RADIUS min:particle::MINIMUM_PARTICLE_RADIUS max:particle::MAXIMUM_PARTICLE_RADIUS, +enum:RADIUS_SPREAD prop:radiusSpread type:float default:particle::DEFAULT_RADIUS_SPREAD min:particle::MINIMUM_PARTICLE_RADIUS max:particle::MAXIMUM_PARTICLE_RADIUS, +enum:RADIUS_START prop:radiusStart type:float default:particle::DEFAULT_RADIUS_START min:particle::MINIMUM_PARTICLE_RADIUS max:particle::MAXIMUM_PARTICLE_RADIUS, +enum:RADIUS_FINISH prop:radiusFinish type:float default:particle::DEFAULT_RADIUS_FINISH min:particle::MINIMUM_PARTICLE_RADIUS max:particle::MAXIMUM_PARTICLE_RADIUS, +enum:COLOR_SPREAD prop:colorSpread type:u8vec3Color default:particle::DEFAULT_COLOR_SPREAD, +enum:COLOR_START prop:colorStart type:vec3Color default:particle::DEFAULT_COLOR_UNINITIALIZED, +enum:COLOR_FINISH prop:colorFinish type:vec3Color default:particle::DEFAULT_COLOR_UNINITIALIZED, +enum:ALPHA_SPREAD prop:alphaSpread type:float default:particle::DEFAULT_ALPHA_SPREAD min:particle::MINIMUM_ALPHA max:particle::MAXIMUM_ALPHA, +enum:ALPHA_START prop:alphaStart type:float default:particle::DEFAULT_ALPHA_START min:particle::MINIMUM_ALPHA max:particle::MAXIMUM_ALPHA, +enum:ALPHA_FINISH prop:alphaFinish type:float default:particle::DEFAULT_ALPHA_FINISH min:particle::MINIMUM_ALPHA max:particle::MAXIMUM_ALPHA, +enum:EMITTER_SHOULD_TRAIL prop:emitterShouldTrail type:bool default:particle::DEFAULT_EMITTER_SHOULD_TRAIL, +enum:PARTICLE_SPIN prop:particleSpin type:float default:particle::DEFAULT_PARTICLE_SPIN min:particle::MINIMUM_PARTICLE_SPIN max:particle::MAXIMUM_PARTICLE_SPIN, +enum:SPIN_SPREAD prop:spinSpread type:float default:particle::DEFAULT_SPIN_SPREAD min:particle::MINIMUM_PARTICLE_SPIN max:particle::MAXIMUM_PARTICLE_SPIN, +enum:SPIN_START prop:spinStart type:float default:particle::DEFAULT_SPIN_START min:particle::MINIMUM_PARTICLE_SPIN max:particle::MAXIMUM_PARTICLE_SPIN, +enum:SPIN_FINISH prop:spinFinish type:float default:particle::DEFAULT_SPIN_FINISH min:particle::MINIMUM_PARTICLE_SPIN max:particle::MAXIMUM_PARTICLE_SPIN, +enum:PARTICLE_ROTATE_WITH_ENTITY prop:rotateWithEntity type:bool default:particle::DEFAULT_ROTATE_WITH_ENTITY, +ProceduralParticleEffect +enum:PROCEDURAL_PARTICLE_NUM_PARTICLES prop:numParticles type:uint32_t default:particle::DEFAULT_NUM_PROCEDURAL_PARTICLES min:particle::MINIMUM_MAX_PARTICLES max:particle::MAXIMUM_NUM_PROCEDURAL_PARTICLES renderProp, +enum:PROCEDURAL_PARTICLE_NUM_TRIS_PER prop:numTrianglesPerParticle type:uint8_t default:particle::DEFAULT_NUM_TRIS_PER min:particle::MINIMUM_TRIS_PER max:particle::MAXIMUM_TRIS_PER renderProp, +enum:PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS prop:numUpdateProps type:uint8_t default:particle::DEFAULT_NUM_UPDATE_PROPS min:particle::MINIMUM_NUM_UPDATE_PROPS max:particle::MAXIMUM_NUM_UPDATE_PROPS renderProp, +enum:PROCEDURAL_PARTICLE_TRANSPARENT prop:particleTransparent type:bool default:false renderProp, +enum:PROCEDURAL_PARTCILE_UPDATE_DATA prop:particleUpdateData type:QString default:"" renderProp, +enum:PROCEDURAL_PARTCILE_RENDER_DATA prop:particleRenderData type:QString default:"" renderProp, +Model +enum:SHAPE_TYPE prop:shapeType type:ShapeType enum default:SHAPE_TYPE_NONE common noGetterSetterProp, +enum:COMPOUND_SHAPE_URL prop:compoundShapeURL type:QString default:"" urlPermission common noGetterSetterProp, +enum:COLOR prop:color type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR common renderProp, +enum:TEXTURES prop:textures type:QString default:"" common renderProp, +enum:MODEL_URL prop:modelURL type:QString default:"" urlPermission noGetterSetterProp, +enum:MODEL_SCALE prop:modelScale type:vec3 default:glm::vec3(1.0f) basicProp, +enum:JOINT_ROTATIONS_SET prop:jointRotationsSet type:qVectorBool default:QVector() noGetterSetterProp, +enum:JOINT_ROTATIONS prop:jointRotations type:qVectorQuat default:QVector() noGetterSetterProp, +enum:JOINT_TRANSLATIONS_SET prop:jointTranslationsSet type:qVectorBool default:QVector() noGetterSetterProp, +enum:JOINT_TRANSLATIONS prop:jointTranslations type:qVectorVec3 default:ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC noGetterSetterProp, +enum:RELAY_PARENT_JOINTS prop:relayParentJoints type:bool default:ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS basicProp, +enum:GROUP_CULLED prop:groupCulled type:bool default:false renderProp, +enum:BLENDSHAPE_COEFFICIENTS prop:blendshapeCoefficients type:QString default:"", +enum:USE_ORIGINAL_PIVOT prop:useOriginalPivot type:bool default:false, +enum:LOAD_PRIORITY prop:loadPriority type:float default:0.0f basicProp, +group:animation customVariableSetFrom customVariableRead, +Light +enum:COLOR prop:color type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR common renderProp, +enum:IS_SPOTLIGHT prop:isSpotlight type:bool default:LightEntityItem::DEFAULT_IS_SPOTLIGHT, +enum:INTENSITY prop:intensity type:float default:LightEntityItem::DEFAULT_INTENSITY renderProp, +enum:EXPONENT prop:exponent type:float default:LightEntityItem::DEFAULT_EXPONENT renderProp, +enum:CUTOFF prop:cutoff type:float default:LightEntityItem::DEFAULT_CUTOFF min:LightEntityItem::MIN_CUTOFF max:LightEntityItem::MAX_CUTOFF, +enum:FALLOFF_RADIUS prop:falloffRadius type:float default:LightEntityItem::DEFAULT_FALLOFF_RADIUS, +Text +enum:UNLIT prop:unlit type:bool default:false common renderProp, +group:pulse common renderUpdateOnSet, +enum:TEXT prop:text type:QString default:TextEntityItem::DEFAULT_TEXT renderProp, +enum:LINE_HEIGHT prop:lineHeight type:float default:TextEntityItem::DEFAULT_LINE_HEIGHT renderProp, +enum:TEXT_COLOR prop:textColor type:u8vec3Color default:TextEntityItem::DEFAULT_TEXT_COLOR renderProp, +enum:TEXT_ALPHA prop:textAlpha type:float default:TextEntityItem::DEFAULT_TEXT_ALPHA renderProp, +enum:BACKGROUND_COLOR prop:backgroundColor type:u8vec3Color default:TextEntityItem::DEFAULT_BACKGROUND_COLOR renderProp, +enum:BACKGROUND_ALPHA prop:backgroundAlpha type:float default:TextEntityItem::DEFAULT_TEXT_ALPHA renderProp, +enum:LEFT_MARGIN prop:leftMargin type:float default:TextEntityItem::DEFAULT_MARGIN renderProp, +enum:RIGHT_MARGIN prop:rightMargin type:float default:TextEntityItem::DEFAULT_MARGIN renderProp, +enum:TOP_MARGIN prop:topMargin type:float default:TextEntityItem::DEFAULT_MARGIN renderProp, +enum:BOTTOM_MARGIN prop:bottomMargin type:float default:TextEntityItem::DEFAULT_MARGIN renderProp, +enum:FONT prop:font type:QString default:"Roboto" renderProp, +enum:TEXT_EFFECT prop:textEffect type:TextEffect default:TextEffect::NO_EFFECT enum renderProp, +enum:TEXT_EFFECT_COLOR prop:textEffectColor type:u8vec3Color default:TextEntityItem::DEFAULT_TEXT_COLOR renderProp, +enum:TEXT_EFFECT_THICKNESS prop:textEffectThickness type:float default:TextEntityItem::DEFAULT_TEXT_EFFECT_THICKNESS min:0.0f max:0.5f renderProp, +enum:TEXT_ALIGNMENT prop:alignment type:TextAlignment default:TextAlignment::LEFT enum renderProp, +Zone +enum:SHAPE_TYPE prop:shapeType type:ShapeType enum default:SHAPE_TYPE_NONE common noGetterSetterProp, +enum:COMPOUND_SHAPE_URL prop:compoundShapeURL type:QString default:"" urlPermission common, +group:keyLight recordChange, +group:ambientLight recordChange, +group:skybox recordChange, +group:haze recordChange, +group:bloom recordChange, +group:audio type:ZoneAudio, +group:tonemapping recordChange, +group:ambientOcclusion recordChange, +enum:FLYING_ALLOWED prop:flyingAllowed type:bool default:ZoneEntityItem::DEFAULT_FLYING_ALLOWED basicProp, +enum:GHOSTING_ALLOWED prop:ghostingAllowed type:bool default:ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED basicProp, +enum:FILTER_URL prop:filterURL type:QString default:ZoneEntityItem::DEFAULT_FILTER_URL urlPermission, +enum:KEY_LIGHT_MODE prop:keyLightMode type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum, +enum:AMBIENT_LIGHT_MODE prop:ambientLightMode type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum, +enum:SKYBOX_MODE prop:skyboxMode type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum, +enum:HAZE_MODE prop:hazeMode type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum, +enum:BLOOM_MODE prop:bloomMode type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum, +enum:AVATAR_PRIORITY prop:avatarPriority type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum basicProp, +enum:SCREENSHARE prop:screenshare type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum basicProp, +enum:TONEMAPPING_MODE prop:tonemappingMode type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum, +enum:AMBIENT_OCCLUSION_MODE prop:ambientOcclusionMode type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum, +PolyVox +enum:VOXEL_VOLUME_SIZE prop:voxelVolumeSize type:vec3 default:PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE noGetterSetterProp, +enum:VOXEL_DATA prop:voxelData type:QByteArray default:PolyVoxEntityItem::DEFAULT_VOXEL_DATA noGetterSetterProp, +enum:VOXEL_SURFACE_STYLE prop:voxelSurfaceStyle type:uint16_t default:PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE noGetterSetterProp, +enum:X_TEXTURE_URL prop:xTextureURL type:QString default:"" urlPermission renderProp, +enum:Y_TEXTURE_URL prop:yTextureURL type:QString default:"" urlPermission renderProp, +enum:Z_TEXTURE_URL prop:zTextureURL type:QString default:"" urlPermission renderProp, +enum:X_N_NEIGHBOR_ID prop:xNNeighborID type:EntityItemID default:UNKNOWN_ENTITY_ID noGetterSetterProp, +enum:Y_N_NEIGHBOR_ID prop:yNNeighborID type:EntityItemID default:UNKNOWN_ENTITY_ID noGetterSetterProp, +enum:Z_N_NEIGHBOR_ID prop:zNNeighborID type:EntityItemID default:UNKNOWN_ENTITY_ID noGetterSetterProp, +enum:X_P_NEIGHBOR_ID prop:xPNeighborID type:EntityItemID default:UNKNOWN_ENTITY_ID noGetterSetterProp, +enum:Y_P_NEIGHBOR_ID prop:yPNeighborID type:EntityItemID default:UNKNOWN_ENTITY_ID noGetterSetterProp, +enum:Z_P_NEIGHBOR_ID prop:zPNeighborID type:EntityItemID default:UNKNOWN_ENTITY_ID noGetterSetterProp, +Web +enum:COLOR prop:color type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR common renderProp, +enum:ALPHA prop:alpha type:float default:ENTITY_ITEM_DEFAULT_ALPHA min:0.0f max:1.0f common renderProp, +group:pulse common renderUpdateOnSet, +enum:SOURCE_URL prop:sourceUrl type:QString default:WebEntityItem::DEFAULT_SOURCE_URL urlPermission renderProp, +enum:DPI prop:dpi type:uint16_t default:ENTITY_ITEM_DEFAULT_DPI renderProp, +enum:SCRIPT_URL prop:scriptURL type:QString default:"" urlPermission noGetterSetterProp, +enum:MAX_FPS prop:maxFPS type:uint8_t default:WebEntityItem::DEFAULT_MAX_FPS renderProp, +enum:INPUT_MODE prop:inputMode type:WebInputMode default:WebInputMode::TOUCH enum renderProp, +enum:WANTS_KEYBOARD_FOCUS prop:wantsKeyboardFocus type:bool default:true renderProp, +enum:SHOW_KEYBOARD_FOCUS_HIGHLIGHT prop:showKeyboardFocusHighlight type:bool default:true basicProp, +enum:WEB_USE_BACKGROUND prop:useBackground type:bool default:true renderProp, +enum:USER_AGENT prop:userAgent type:QString default:WebEntityItem::DEFAULT_USER_AGENT renderProp, +Line +enum:COLOR prop:color type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR common renderProp, +enum:LINE_POINTS prop:linePoints type:qVectorVec3 default:ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC common noGetterSetterProp, +PolyLine +enum:COLOR prop:color type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR common, +enum:TEXTURES prop:textures type:QString default:"" common, +enum:LINE_POINTS prop:linePoints type:qVectorVec3 default:ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC common, +enum:STROKE_WIDTHS prop:strokeWidths type:qVectorFloat default:QVector(), +enum:STROKE_NORMALS prop:normals type:qVectorVec3 default:ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC, +enum:STROKE_COLORS prop:strokeColors type:qVectorVec3 default:ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC, +enum:IS_UV_MODE_STRETCH prop:isUVModeStretch type:bool default:true renderProp, +enum:LINE_GLOW prop:glow type:bool default:false renderProp, +enum:LINE_FACE_CAMERA prop:faceCamera type:bool default:false renderProp, +Shape +enum:COLOR prop:color type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR common renderProp, +enum:ALPHA prop:alpha type:float default:ENTITY_ITEM_DEFAULT_ALPHA min:0.0f max:1.0f common renderProp, +enum:UNLIT prop:unlit type:bool default:false common renderProp, +group:pulse common renderUpdateOnSet, +enum:SHAPE prop:shape type:EntityShape default:EntityShape::Sphere enum, +Material +enum:MATERIAL_URL prop:materialURL type:QString default:"" urlPermission renderProp, +enum:MATERIAL_MAPPING_MODE prop:materialMappingMode type:MaterialMappingMode default:UV enum renderProp, +enum:MATERIAL_PRIORITY prop:priority type:quint16 default:0 renderProp, +enum:PARENT_MATERIAL_NAME prop:parentMaterialName type:QString default:"0" renderProp, +enum:MATERIAL_MAPPING_POS prop:materialMappingPos type:vec2 default:glm::vec2(0.0f) renderProp, +enum:MATERIAL_MAPPING_SCALE prop:materialMappingScale type:vec2 default:glm::vec2(1.0f) renderProp, +enum:MATERIAL_MAPPING_ROT prop:materialMappingRot type:float default:0 renderProp, +enum:MATERIAL_DATA prop:materialData type:QString default:"" urlPermission renderProp, +enum:MATERIAL_REPEAT prop:materialRepeat type:bool default:true renderProp, +Image +enum:COLOR prop:color type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR common renderProp, +enum:ALPHA prop:alpha type:float default:ENTITY_ITEM_DEFAULT_ALPHA min:0.0f max:1.0f common renderProp, +group:pulse common renderUpdateOnSet, +enum:IMAGE_URL prop:imageURL type:QString default:"" urlPermission renderProp, +enum:EMISSIVE prop:emissive type:bool default:false renderProp, +enum:KEEP_ASPECT_RATIO prop:keepAspectRatio type:bool default:true renderProp, +enum:SUB_IMAGE prop:subImage type:QRect default:QRect() renderProp, +Grid +enum:COLOR prop:color type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR common renderProp, +enum:ALPHA prop:alpha type:float default:ENTITY_ITEM_DEFAULT_ALPHA min:0.0f max:1.0f common renderProp, +group:pulse common renderUpdateOnSet, +enum:GRID_FOLLOW_CAMERA prop:followCamera type:bool default:true renderProp, +enum:MAJOR_GRID_EVERY prop:majorGridEvery type:uint32_t default:GridEntityItem::DEFAULT_MAJOR_GRID_EVERY, +enum:MINOR_GRID_EVERY prop:minorGridEvery type:float default:GridEntityItem::DEFAULT_MINOR_GRID_EVERY, +Gizmo +enum:GIZMO_TYPE prop:gizmoType type:GizmoType default:GizmoType::RING enum renderProp, +group:ring type:RingGizmo renderUpdateOnSet, +Sound +enum:SOUND_URL prop:soundURL type:QString default:"", +enum:SOUND_VOLUME prop:volume type:float default:1.0f min:0.0f max:1.0f, +enum:SOUND_TIME_OFFSET prop:timeOffset type:float default:0.0f, +enum:SOUND_PITCH, prop:pitch type:float default:1.0f min:1.0f/16.0f max:16.0f, +enum:SOUND_PLAYING prop:playing type:bool default:true, +enum:SOUND_LOOP prop:loop type:bool default:true, +enum:SOUND_POSITIONAL prop:positional type:bool default:true, +enum:SOUND_LOCAL_ONLY prop:localOnly type:bool default:false, \ No newline at end of file diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index ad9de6ac18..b79f011a70 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -89,6 +89,8 @@ const uint16_t ENTITY_ITEM_DEFAULT_DPI = 30; const QUuid ENTITY_ITEM_DEFAULT_LAST_EDITED_BY = QUuid(); +const uint16_t ENTITY_ITEM_DEFAULT_PARENT_INDEX = -1; + const bool ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS = false; const bool ENTITY_ITEM_DEFAULT_CLONEABLE = false; diff --git a/libraries/entities/src/EntityItemPropertiesDocs.cpp b/libraries/entities/src/EntityItemPropertiesDocs.cpp new file mode 100644 index 0000000000..a321327aec --- /dev/null +++ b/libraries/entities/src/EntityItemPropertiesDocs.cpp @@ -0,0 +1,1004 @@ +// +// EntityItemPropertiesDocs.cpp +// libraries/entities/src +// +// Created by HifiExperiments on 7/24/24. +// Copyright 2024 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 +// SPDX-License-Identifier: Apache-2.0 +// + +/*@jsdoc + * Different entity types have different properties: some common to all entities (listed in the table) and some specific to + * each {@link Entities.EntityType|EntityType} (linked to below). + * + * @typedef {object} Entities.EntityProperties + * @property {Uuid} id - The ID of the entity. Read-only. + * @property {string} name="" - A name for the entity. Need not be unique. + * @property {Entities.EntityType} type - The entity's type. You cannot change the type of an entity after it's created. + * However, its value may switch among "Box", "Shape", and "Sphere" depending on + * changes to the shape property set for entities of these types. Read-only. + * + * @property {Entities.EntityHostType} entityHostType="domain" - How the entity is hosted and sent to others for display. + * The value can only be set at entity creation by one of the {@link Entities.addEntity} methods. Read-only. + * @property {boolean} avatarEntity=false - true if the entity is an {@link Entities.EntityHostType|avatar entity}, + * false if it isn't. The value is per the entityHostType property value, set at entity creation + * by one of the {@link Entities.addEntity} methods. Read-only. + * @property {boolean} clientOnly=false - A synonym for avatarEntity. Read-only. + * @property {boolean} localEntity=false - true if the entity is a {@link Entities.EntityHostType|local entity}, + * false if it isn't. The value is per the entityHostType property value, set at entity creation + * by one of the {@link Entities.addEntity} methods. Read-only. + * + * @property {Uuid} owningAvatarID=Uuid.NULL - The session ID of the owning avatar if avatarEntity is + * true, otherwise {@link Uuid(0)|Uuid.NULL}. Read-only. + * + * @property {number} created - When the entity was created, expressed as the number of microseconds since + * 1970-01-01T00:00:00 UTC. Read-only. + * @property {number} age - The age of the entity in seconds since it was created. Read-only. + * @property {string} ageAsText - The age of the entity since it was created, formatted as h hours m minutes s + * seconds. + * @property {number} lifetime=-1 - How long an entity lives for, in seconds, before being automatically deleted. A value of + * -1 means that the entity lives for ever. + * @property {number} lastEdited - When the entity was last edited, expressed as the number of microseconds since + * 1970-01-01T00:00:00 UTC. Read-only. + * @property {Uuid} lastEditedBy - The session ID of the avatar or agent that most recently created or edited the entity. + * Read-only. + * + * @property {boolean} locked=false - true if properties other than locked cannot be changed and the + * entity cannot be deleted, false if all properties can be changed and the entity can be deleted. + * @property {boolean} visible=true - true if the entity is rendered, false if it isn't. + * @property {boolean} canCastShadow=true - true if the entity can cast a shadow, false if it can't. + * Currently applicable only to {@link Entities.EntityProperties-Model|Model} and + * {@link Entities.EntityProperties-Shape|Shape} entities. Shadows are cast if inside a + * {@link Entities.EntityProperties-Zone|Zone} entity with castShadows enabled in its keyLight + * property. + * @property {boolean} isVisibleInSecondaryCamera=true - true if the entity is rendered in the secondary camera, + * false if it isn't. + * @property {Entities.RenderLayer} renderLayer="world" - The layer that the entity renders in. + * @property {Entities.PrimitiveMode} primitiveMode="solid" - How the entity's geometry is rendered. + * @property {boolean} ignorePickIntersection=false - true if {@link Picks} and {@link RayPick} ignore the entity, + * false if they don't. + * + * @property {Vec3} position=0,0,0 - The position of the entity in world coordinates. + * @property {Quat} rotation=0,0,0,1 - The orientation of the entity in world coordinates. + * @property {Vec3} registrationPoint=0.5,0.5,0.5 - The point in the entity that is set to the entity's position and is rotated + * about, range {@link Vec3(0)|Vec3.ZERO} – {@link Vec3(0)|Vec3.ONE}. A value of {@link Vec3(0)|Vec3.ZERO} is the + * entity's minimum x, y, z corner; a value of {@link Vec3(0)|Vec3.ONE} is the entity's maximum x, y, z corner. + * + * @property {Vec3} naturalPosition=0,0,0 - The center of the entity's unscaled mesh model if it has one, otherwise + * {@link Vec3(0)|Vec3.ZERO}. Read-only. + * @property {Vec3} naturalDimensions - The dimensions of the entity's unscaled mesh model or image if it has one, otherwise + * {@link Vec3(0)|Vec3.ONE}. Read-only. + * + * @property {Vec3} velocity=0,0,0 - The linear velocity of the entity in m/s with respect to world coordinates. + * @property {number} damping=0.39347 - How much the linear velocity of an entity slows down over time, range + * 0.01.0. A higher damping value slows down the entity more quickly. The default value + * is for an exponential decay timescale of 2.0s, where it takes 2.0s for the movement to slow to 1/e = 0.368 + * of its initial value. + * @property {Vec3} angularVelocity=0,0,0 - The angular velocity of the entity in rad/s with respect to its axes, about its + * registration point. + * @property {number} angularDamping=0.39347 - How much the angular velocity of an entity slows down over time, range + * 0.01.0. A higher damping value slows down the entity more quickly. The default value + * is for an exponential decay timescale of 2.0s, where it takes 2.0s for the movement to slow to 1/e = 0.368 + * of its initial value. + * + * @property {Vec3} gravity=0,0,0 - The acceleration due to gravity in m/s2 that the entity should move with, in + * world coordinates. Use a value of { x: 0, y: -9.8, z: 0 } to simulate Earth's gravity. Gravity is applied + * to an entity's motion only if its dynamic property is true. + *

    If changing an entity's gravity from {@link Vec3(0)|Vec3.ZERO}, you need to give it a small + * velocity in order to kick off physics simulation.

    + * @property {Vec3} acceleration - The current, measured acceleration of the entity, in m/s2. + *

    Deprecated: This property is deprecated and will be removed.

    + * @property {number} restitution=0.5 - The "bounciness" of an entity when it collides, range 0.0 – + * 0.99. The higher the value, the more bouncy. + * @property {number} friction=0.5 - How much an entity slows down when it's moving against another, range 0.0 + * – 10.0. The higher the value, the more quickly it slows down. Examples: 0.1 for ice, + * 0.9 for sandpaper. + * @property {number} density=1000 - The density of the entity in kg/m3, range 100 – + * 10000. Examples: 100 for balsa wood, 10000 for silver. The density is used in + * conjunction with the entity's bounding box volume to work out its mass in the application of physics. + * + * @property {boolean} collisionless=false - true if the entity shouldn't collide, false if it + * collides with items per its collisionMask property. + * @property {boolean} ignoreForCollisions - Synonym for collisionless. + * @property {CollisionMask} collisionMask=31 - What types of items the entity should collide with. + * @property {string} collidesWith="static,dynamic,kinematic,myAvatar,otherAvatar," - Synonym for collisionMask, + * in text format. + * @property {string} collisionSoundURL="" - The sound that's played when the entity experiences a collision. Valid file + * formats are per {@link SoundObject}. + * @property {boolean} dynamic=false - true if the entity's movement is affected by collisions, false + * if it isn't. + * @property {boolean} collisionsWillMove - A synonym for dynamic. + * + * @property {string} href="" - A "hifi://" directory services address that a user is teleported to when they click on the entity. + * @property {string} description="" - A description of the href property value. + * + * @property {string} userData="" - Used to store extra data about the entity in JSON format. + *

    Warning: Other apps may also use this property, so make sure you handle data stored by other apps: + * edit only your bit and leave the rest of the data intact. You can use JSON.parse() to parse the string into + * a JavaScript object which you can manipulate the properties of, and use JSON.stringify() to convert the + * object into a string to put back in the property.

    + * + * @property {string} privateUserData="" - Like userData, but only accessible by server entity scripts, assignment + * client scripts, and users who have "Can Get and Set Private User Data" permissions in the domain. + * + * @property {string} script="" - The URL of the client entity script, if any, that is attached to the entity. + * @property {number} scriptTimestamp=0 - Used to indicate when the client entity script was loaded. Should be + * an integer number of milliseconds since midnight GMT on January 1, 1970 (e.g., as supplied by Date.now(). + * If you update the property's value, the script is re-downloaded and reloaded. This is how the "reload" + * button beside the "script URL" field in properties tab of the Create app works. + * @property {string} serverScripts="" - The URL of the server entity script, if any, that is attached to the entity. + * + * @property {Uuid} parentID=Uuid.NULL - The ID of the entity or avatar that the entity is parented to. A value of + * {@link Uuid(0)|Uuid.NULL} is used if the entity is not parented. + * @property {number} parentJointIndex=65535 - The joint of the entity or avatar that the entity is parented to. Use + * 65535 or -1 to parent to the entity or avatar's position and orientation rather than a joint. + * @property {Vec3} localPosition=0,0,0 - The position of the entity relative to its parent if the entity is parented, + * otherwise the same value as position. If the entity is parented to an avatar and is an avatar entity + * so that it scales with the avatar, this value remains the original local position value while the avatar scale changes. + * @property {Quat} localRotation=0,0,0,1 - The rotation of the entity relative to its parent if the entity is parented, + * otherwise the same value as rotation. + * @property {Vec3} localVelocity=0,0,0 - The velocity of the entity relative to its parent if the entity is parented, + * otherwise the same value as velocity. + * @property {Vec3} localAngularVelocity=0,0,0 - The angular velocity of the entity relative to its parent if the entity is + * parented, otherwise the same value as angularVelocity. + * @property {Vec3} localDimensions - The dimensions of the entity. If the entity is parented to an avatar and is an + * avatar entity so that it scales with the avatar, this value remains the original dimensions value while the + * avatar scale changes. + * + * @property {Entities.BoundingBox} boundingBox - The axis-aligned bounding box that tightly encloses the entity. + * Read-only. + * @property {AACube} queryAACube - The axis-aligned cube that determines where the entity lives in the entity server's octree. + * The cube may be considerably larger than the entity in some situations, e.g., when the entity is grabbed by an avatar: + * the position of the entity is determined through avatar mixer updates and so the AA cube is expanded in order to reduce + * unnecessary entity server updates. Scripts should not change this property's value. + * + * @property {string} actionData="" - Base-64 encoded compressed dump of the actions associated with the entity. This property + * is typically not used in scripts directly; rather, functions that manipulate an entity's actions update it, e.g., + * {@link Entities.addAction}. The size of this property increases with the number of actions. Because this property value + * has to fit within a Overte datagram packet, there is a limit to the number of actions that an entity can have; + * edits which would result in overflow are rejected. Read-only. + * @property {Entities.RenderInfo} renderInfo - Information on the cost of rendering the entity. Currently information is only + * provided for Model entities. Read-only. + * + * @property {boolean} cloneable=false - true if the domain or avatar entity can be cloned via + * {@link Entities.cloneEntity}, false if it can't be. + * @property {number} cloneLifetime=300 - The entity lifetime for clones created from this entity. + * @property {number} cloneLimit=0 - The total number of clones of this entity that can exist in the domain at any given time. + * @property {boolean} cloneDynamic=false - true if clones created from this entity will have their + * dynamic property set to true, false if they won't. + * @property {boolean} cloneAvatarEntity=false - true if clones created from this entity will be created as + * avatar entities, false if they won't be. + * @property {Uuid} cloneOriginID - The ID of the entity that this entity was cloned from. + * + * @property {Uuid[]} renderWithZones=[] - A list of entity IDs representing with which zones this entity should render. + * If it is empty, this entity will render normally. Otherwise, this entity will only render if your avatar is within + * one of the zones in this list. + * @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera. Use the rotation + * property to control which axis is facing you. + * @property {string[]} tags=[] - A set of tags describing this entity. + * + * @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} + * @see {@link Entities.EntityProperties-Grid|EntityProperties-Grid} + * @see {@link Entities.EntityProperties-Image|EntityProperties-Image} + * @see {@link Entities.EntityProperties-Light|EntityProperties-Light} + * @see {@link Entities.EntityProperties-Line|EntityProperties-Line} + * @see {@link Entities.EntityProperties-Material|EntityProperties-Material} + * @see {@link Entities.EntityProperties-Model|EntityProperties-Model} + * @see {@link Entities.EntityProperties-ParticleEffect|EntityProperties-ParticleEffect} + * @see {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine} + * @see {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox} + * @see {@link Entities.EntityProperties-ProceduralParticleEffect|EntityProperties-ProceduralParticleEffect} + * @see {@link Entities.EntityProperties-Shape|EntityProperties-Shape} + * @see {@link Entities.EntityProperties-Sound|EntityProperties-Sound} + * @see {@link Entities.EntityProperties-Sphere|EntityProperties-Sphere} + * @see {@link Entities.EntityProperties-Text|EntityProperties-Text} + * @see {@link Entities.EntityProperties-Web|EntityProperties-Web} + * @see {@link Entities.EntityProperties-Zone|EntityProperties-Zone} + */ + +/*@jsdoc + * The "Box" {@link Entities.EntityType|EntityType} is the same as the "Shape" + * {@link Entities.EntityType|EntityType} except that its shape value is always set to "Cube" + * when the entity is created. If its shape property value is subsequently changed then the entity's + * type will be reported as "Sphere" if the shape is set to "Sphere", + * otherwise it will be reported as "Shape". + * + * @typedef {object} Entities.EntityProperties-Box + * @see {@link Entities.EntityProperties-Shape|EntityProperties-Shape} + */ + +/*@jsdoc + * The "Light" {@link Entities.EntityType|EntityType} adds local lighting effects. It has properties in addition + * to the common {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-Light + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. Surfaces outside these dimensions are not lit + * by the light. + * @property {Color} color=255,255,255 - The color of the light emitted. + * @property {number} intensity=1 - The brightness of the light. + * @property {number} falloffRadius=0.1 - The distance from the light's center at which intensity is reduced by 25%. + * @property {boolean} isSpotlight=false - true if the light is directional, emitting along the entity's + * local negative z-axis; false if the light is a point light which emanates in all directions. + * @property {number} exponent=0 - Affects the softness of the spotlight beam: the higher the value the softer the beam. + * @property {number} cutoff=1.57 - Affects the size of the spotlight beam: the higher the value the larger the beam. + * @example Create a spotlight pointing at the ground. + * Entities.addEntity({ + * type: "Light", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -4 })), + * rotation: Quat.fromPitchYawRollDegrees(-75, 0, 0), + * dimensions: { x: 5, y: 5, z: 5 }, + * intensity: 100, + * falloffRadius: 0.3, + * isSpotlight: true, + * exponent: 20, + * cutoff: 30, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/*@jsdoc + * The "Line" {@link Entities.EntityType|EntityType} draws thin, straight lines between a sequence of two or more + * points. It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + *

    Deprecated: Use {@link Entities.EntityProperties-PolyLine|PolyLine} entities instead.

    + * + * @typedef {object} Entities.EntityProperties-Line + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. Must be sufficient to contain all the + * linePoints. + * @property {Vec3[]} linePoints=[]] - The sequence of points to draw lines between. The values are relative to the entity's + * position. A maximum of 70 points can be specified. The property's value is set only if all the linePoints + * lie within the entity's dimensions. + * @property {Color} color=255,255,255 - The color of the line. + * @example Draw lines in a "V". + * var entity = Entities.addEntity({ + * type: "Line", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 2, y: 2, z: 1 }, + * linePoints: [ + * { x: -1, y: 1, z: 0 }, + * { x: 0, y: -1, z: 0 }, + * { x: 1, y: 1, z: 0 }, + * ], + * color: { red: 255, green: 0, blue: 0 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/*@jsdoc + * The "Material" {@link Entities.EntityType|EntityType} modifies existing materials on entities and avatars. It + * has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + *

    To apply a material to an entity, set the material entity's parentID property to the entity ID. + * To apply a material to an avatar, set the material entity's parentID property to the avatar's session UUID. + * To apply a material to your avatar such that it persists across domains and log-ins, create the material as an avatar entity + * by setting the entityHostType parameter in {@link Entities.addEntity} to "avatar" and set the + * entity's parentID property to MyAvatar.SELF_ID. + * Material entities render as non-scalable spheres if they don't have their parent set.

    + * + * @typedef {object} Entities.EntityProperties-Material + * @property {Vec3} dimensions=0.1,0.1,0.1 - Used when materialMappingMode == "projected". + * @property {string} materialURL="" - URL to a {@link Entities.MaterialResource|MaterialResource}. Alternatively, set the + * property value to "materialData" to use the materialData property for the + * {@link Entities.MaterialResource|MaterialResource} values. If you append "#name" to the URL, the material + * with that name will be applied to the entity. You can also use the ID of another Material entity as the URL, in which + * case this material will act as a copy of that material, with its own unique material transform, priority, etc. + * @property {string} materialData="" - Used to store {@link Entities.MaterialResource|MaterialResource} data as a JSON string. + * You can use JSON.parse() to parse the string into a JavaScript object which you can manipulate the + * properties of, and use JSON.stringify() to convert the object into a string to put in the property. + * @property {number} priority=0 - The priority for applying the material to its parent. Only the highest priority material is + * applied, with materials of the same priority randomly assigned. Materials that come with the model have a priority of + * 0. + * @property {string} parentMaterialName="0" - Selects the mesh part or parts within the parent to which to apply the material. + * If in the format "mat::string", all mesh parts with material name "string" are replaced. + * If "all", then all mesh parts are replaced. + * Otherwise the property value is parsed as an unsigned integer, specifying the mesh part index to modify. + *

    If the string represents an array (starts with "[" and ends with "]"), the string is split + * at each "," and each element parsed as either a number or a string if it starts with "mat::". + * For example, "[0,1,mat::string,mat::string2]" will replace mesh parts 0 and 1, and any mesh parts with + * material "string" or "string2". Do not put spaces around the commas. Invalid values are parsed + * to 0.

    + * @property {string} materialMappingMode="uv" - How the material is mapped to the entity. Either "uv" or + * "projected". In "uv" mode, the material is evaluated within the UV space of the mesh it is + * applied to. In "projected" mode, the 3D transform (position, rotation, and dimensions) of the Material + * entity is used to evaluate the texture coordinates for the material. + * @property {Vec2} materialMappingPos=0,0 - Offset position in UV-space of the top left of the material, range + * { x: 0, y: 0 }{ x: 1, y: 1 }. + * @property {Vec2} materialMappingScale=1,1 - How much to scale the material within the parent's UV-space. + * @property {number} materialMappingRot=0 - How much to rotate the material within the parent's UV-space, in degrees. + * @property {boolean} materialRepeat=true - true if the material repeats, false if it doesn't. If + * false, fragments outside of texCoord 0 – 1 will be discarded. Works in both "uv" and + * "projected" modes. + * @example Color a sphere using a Material entity. + * var entityID = Entities.addEntity({ + * type: "Sphere", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * dimensions: { x: 1, y: 1, z: 1 }, + * color: { red: 128, green: 128, blue: 128 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * var materialID = Entities.addEntity({ + * type: "Material", + * parentID: entityID, + * materialURL: "materialData", + * priority: 1, + * materialData: JSON.stringify({ + * materialVersion: 1, + * materials: { + * // Value overrides entity's "color" property. + * albedo: [1.0, 1.0, 0] // Yellow + * } + * }) + * }); + */ + +/*@jsdoc + * The "Model" {@link Entities.EntityType|EntityType} displays a glTF, FBX, or OBJ model. When adding an entity, + * if no dimensions value is specified then the model is automatically sized to its + * {@link Entities.EntityProperties|naturalDimensions}. It has properties in addition to the common + * {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-Model + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. When adding an entity, if no dimensions + * value is specified then the model is automatically sized to its + * {@link Entities.EntityProperties|naturalDimensions}. + * @property {string} modelURL="" - The URL of the glTF, FBX, or OBJ model. glTF models may be in JSON or binary format + * (".gltf" or ".glb" URLs respectively). Baked models' URLs have ".baked" before the file type. Model files may also be + * compressed in GZ format, in which case the URL ends in ".gz". + * @property {Vec3} modelScale - The scale factor applied to the model's dimensions. + *

    Deprecated: This property is deprecated and will be removed.

    + * @property {string} blendshapeCoefficients - A JSON string of a map of blendshape names to values. Only stores set values. + * When editing this property, only coefficients that you are editing will change; it will not explicitly reset other + * coefficients. + * @property {boolean} useOriginalPivot=false - If false, the model will be centered based on its content, + * ignoring any offset in the model itself. If true, the model will respect its original offset. Currently, + * only pivots relative to {x: 0, y: 0, z: 0} are supported. + * @property {number} loadPriority=0.0 - If 0, the model download will be prioritized based on distance, size, and + * other factors, and assigned a priority automatically between 0 and PI / 2. Otherwise, the + * download will be ordered based on the set loadPriority. + * @property {string} textures="" - A JSON string of texture name, URL pairs used when rendering the model in place of the + * model's original textures. Use a texture name from the originalTextures property to override that texture. + * Only the texture names and URLs to be overridden need be specified; original textures are used where there are no + * overrides. You can use JSON.stringify() to convert a JavaScript object of name, URL pairs into a JSON + * string. + * @property {string} originalTextures="{}" - A JSON string of texture name, URL pairs used in the model. The property value is + * filled in after the entity has finished rezzing (i.e., textures have loaded). You can use JSON.parse() to + * parse the JSON string into a JavaScript object of name, URL pairs. Read-only. + * @property {Color} color=255,255,255 - Currently not used. + * + * @property {ShapeType} shapeType="none" - The shape of the collision hull used if collisions are enabled. + * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType is + * "compound". + * + * @property {Entities.AnimationProperties} animation - An animation to play on the model. + * + * @property {Quat[]} jointRotations=[]] - Joint rotations applied to the model; [] if none are applied or the + * model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Rotations are relative to + * each joint's parent. + *

    Joint rotations can be set by {@link Entities.setLocalJointRotation|setLocalJointRotation} and similar functions, or + * by setting the value of this property. If you set a joint rotation using this property, you also need to set the + * corresponding jointRotationsSet value to true.

    + * @property {boolean[]} jointRotationsSet=[]] - true values for joints that have had rotations applied, + * false otherwise; [] if none are applied or the model hasn't loaded. The array indexes are per + * {@link Entities.getJointIndex|getJointIndex}. + * @property {Vec3[]} jointTranslations=[]] - Joint translations applied to the model; [] if none are applied or + * the model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Translations are + * relative to each joint's parent. + *

    Joint translations can be set by {@link Entities.setLocalJointTranslation|setLocalJointTranslation} and similar + * functions, or by setting the value of this property. If you set a joint translation using this property you also need to + * set the corresponding jointTranslationsSet value to true.

    + * @property {boolean[]} jointTranslationsSet=[]] - true values for joints that have had translations applied, + * false otherwise; [] if none are applied or the model hasn't loaded. The array indexes are per + * {@link Entities.getJointIndex|getJointIndex}. + * @property {boolean} relayParentJoints=false - true if when the entity is parented to an avatar, the avatar's + * joint rotations are applied to the entity's joints; false if a parent avatar's joint rotations are not + * applied to the entity's joints. + * @property {boolean} groupCulled=false - true if the mesh parts of the model are LOD culled as a group, + * false if separate mesh parts are LOD culled individually. + * + * @example Rez a cowboy hat. + * var entity = Entities.addEntity({ + * type: "Model", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -2 })), + * rotation: MyAvatar.orientation, + * modelURL: "https://apidocs.overte.org/examples/cowboy-hat.fbx", + * dimensions: { x: 0.8569, y: 0.3960, z: 1.0744 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/*@jsdoc + * The "ParticleEffect" {@link Entities.EntityType|EntityType} displays a particle system that can be used to + * simulate things such as fire, smoke, snow, magic spells, etc. The particles emanate from an ellipsoid or part thereof. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-ParticleEffect + * @property {boolean} isEmitting=true - true if particles are being emitted, false if they aren't. + * @property {number} maxParticles=1000 - The maximum number of particles to render at one time. Older particles are deleted if + * necessary when new ones are created. + * @property {number} lifespan=3s - How long, in seconds, each particle lives. + * @property {number} emitRate=15 - The number of particles per second to emit. + * @property {number} emitSpeed=5 - The speed, in m/s, that each particle is emitted at. + * @property {number} speedSpread=1 - The spread in speeds at which particles are emitted at. For example, if + * emitSpeed == 5 and speedSpread == 1, particles will be emitted with speeds in the range + * 46m/s. + * @property {Vec3} emitAcceleration=0,-9.8,0 - The acceleration that is applied to each particle during its lifetime. The + * default is Earth's gravity value. + * @property {Vec3} accelerationSpread=0,0,0 - The spread in accelerations that each particle is given. For example, if + * emitAccelerations == {x: 0, y: -9.8, z: 0} and accelerationSpread == + * {x: 0, y: 1, z: 0}, each particle will have an acceleration in the range {x: 0, y: -10.8, z: 0} + * – {x: 0, y: -8.8, z: 0}. + * @property {Vec3} dimensions - The dimensions of the particle effect, i.e., a bounding box containing all the particles + * during their lifetimes, assuming that emitterShouldTrail == false. Read-only. + * @property {boolean} emitterShouldTrail=false - true if particles are "left behind" as the emitter moves, + * false if they stay within the entity's dimensions. + * + * @property {Quat} emitOrientation=-0.707,0,0,0.707 - The orientation of particle emission relative to the entity's axes. By + * default, particles emit along the entity's local z-axis, and azimuthStart and azimuthFinish + * are relative to the entity's local x-axis. The default value is a rotation of -90 degrees about the local x-axis, i.e., + * the particles emit vertically. + * + * @property {ShapeType} shapeType="ellipsoid" - The shape from which particles are emitted. + * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType == + * "compound". + * @property {Vec3} emitDimensions=0,0,0 - The dimensions of the shape from which particles are emitted. + * @property {number} emitRadiusStart=1 - The starting radius within the shape at which particles start being emitted; + * range 0.01.0 for the center to the surface, respectively. + * Particles are emitted from the portion of the shape that lies between emitRadiusStart and the + * shape's surface. + * @property {number} polarStart=0 - The angle in radians from the entity's local z-axis at which particles start being emitted + * within the shape; range 0Math.PI. Particles are emitted from the portion of the + * shape that lies between polarStart and polarFinish. Only used if shapeType is + * "ellipsoid" or "sphere". + * @property {number} polarFinish=0 - The angle in radians from the entity's local z-axis at which particles stop being emitted + * within the shape; range 0Math.PI. Particles are emitted from the portion of the + * shape that lies between polarStart and polarFinish. Only used if shapeType is + * "ellipsoid" or "sphere". + * @property {number} azimuthStart=-Math.PI - The angle in radians from the entity's local x-axis about the entity's local + * z-axis at which particles start being emitted; range -Math.PIMath.PI. Particles are + * emitted from the portion of the shape that lies between azimuthStart and azimuthFinish. + * Only used if shapeType is "ellipsoid", "sphere", or "circle". + * @property {number} azimuthFinish=Math.PI - The angle in radians from the entity's local x-axis about the entity's local + * z-axis at which particles stop being emitted; range -Math.PIMath.PI. Particles are + * emitted from the portion of the shape that lies between azimuthStart and azimuthFinish. + * Only used if shapeType is "ellipsoid", "sphere", or "circle". + * + * @property {string} textures="" - The URL of a JPG or PNG image file to display for each particle. If you want transparency, + * use PNG format. + * @property {number} particleRadius=0.025 - The radius of each particle at the middle of its life. + * @property {number} radiusStart=null - The radius of each particle at the start of its life. If null, the + * particleRadius value is used. + * @property {number} radiusFinish=null - The radius of each particle at the end of its life. If null, the + * particleRadius value is used. + * @property {number} radiusSpread=0 - The spread in radius that each particle is given. For example, if + * particleRadius == 0.5 and radiusSpread == 0.25, each particle will have a radius in the range + * 0.250.75. + * @property {Color} color=255,255,255 - The color of each particle at the middle of its life. + * @property {ColorFloat} colorStart=null,null,null - The color of each particle at the start of its life. If any of the + * component values are undefined, the color value is used. + * @property {ColorFloat} colorFinish=null,null,null - The color of each particle at the end of its life. If any of the + * component values are undefined, the color value is used. + * @property {Color} colorSpread=0,0,0 - The spread in color that each particle is given. For example, if + * color == {red: 100, green: 100, blue: 100} and colorSpread == + * {red: 10, green: 25, blue: 50}, each particle will have a color in the range + * {red: 90, green: 75, blue: 50}{red: 110, green: 125, blue: 150}. + * @property {number} alpha=1 - The opacity of each particle at the middle of its life. + * @property {number} alphaStart=null - The opacity of each particle at the start of its life. If null, the + * alpha value is used. + * @property {number} alphaFinish=null - The opacity of each particle at the end of its life. If null, the + * alpha value is used. + * @property {number} alphaSpread=0 - The spread in alpha that each particle is given. For example, if + * alpha == 0.5 and alphaSpread == 0.25, each particle will have an alpha in the range + * 0.250.75. + * @property {Entities.Pulse} pulse - Color and alpha pulse. + *

    Deprecated: This property is deprecated and will be removed.

    + * @property {number} particleSpin=0 - The rotation of each particle at the middle of its life, range -2 * Math.PI + * – 2 * Math.PI radians. + * @property {number} spinStart=null - The rotation of each particle at the start of its life, range -2 * Math.PI + * – 2 * Math.PI radians. If null, the particleSpin value is used. + * @property {number} spinFinish=null - The rotation of each particle at the end of its life, range -2 * Math.PI + * – 2 * Math.PI radians. If null, the particleSpin value is used. + * @property {number} spinSpread=0 - The spread in spin that each particle is given, range 0 – + * 2 * Math.PI radians. For example, if particleSpin == Math.PI and + * spinSpread == Math.PI / 2, each particle will have a rotation in the range Math.PI / 2 – + * 3 * Math.PI / 2. + * @property {boolean} rotateWithEntity=false - true if the particles' rotations are relative to the entity's + * instantaneous rotation, false if they're relative to world coordinates. If true with + * particleSpin == 0, the particles keep oriented per the entity's orientation. + * + * @example Create a ball of green smoke. + * particles = Entities.addEntity({ + * type: "ParticleEffect", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -4 })), + * lifespan: 5, + * emitRate: 10, + * emitSpeed: 0.02, + * speedSpread: 0.01, + * emitAcceleration: { x: 0, y: 0.02, z: 0 }, + * polarFinish: Math.PI, + * textures: "https://content.overte.org/Bazaar/Assets/Textures/Defaults/Interface/default_particle.png", + * particleRadius: 0.1, + * color: { red: 0, green: 255, blue: 0 }, + * alphaFinish: 0, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/*@jsdoc + * The "PolyLine" {@link Entities.EntityType|EntityType} draws textured, straight lines between a sequence of + * points. It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-PolyLine + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity, i.e., the size of the bounding box that contains the + * lines drawn. Read-only. + * @property {Vec3[]} linePoints=[]] - The sequence of points to draw lines between. The values are relative to the entity's + * position. A maximum of 70 points can be specified. + * @property {Vec3[]} normals=[]] - The normal vectors for the line's surface at the linePoints. The values are + * relative to the entity's orientation. Must be specified in order for the entity to render. + * @property {number[]} strokeWidths=[]] - The widths, in m, of the line at the linePoints. Must be specified in + * order for the entity to render. + * @property {Vec3[]} strokeColors=[]] - The base colors of each point, with values in the range 0.0,0.0,0.0 + * – 1.0,1.0,1.0. These colors are multiplied with the color of the texture. If there are more line + * points than stroke colors, the color property value is used for the remaining points. + *

    Warning: The ordinate values are in the range 0.01.0.

    + * @property {Color} color=255,255,255 - Used as the color for each point if strokeColors doesn't have a value for + * the point. + * @property {string} textures="" - The URL of a JPG or PNG texture to use for the lines. If you want transparency, use PNG + * format. + * @property {boolean} isUVModeStretch=true - true if the texture is stretched to fill the whole line, + * false if the texture repeats along the line. + * @property {boolean} glow=false - true if the opacity of the strokes drops off away from the line center, + * false if it doesn't. + * @property {boolean} faceCamera=false - true if each line segment rotates to face the camera, false + * if they don't. + * @example Draw a textured "V". + * var entity = Entities.addEntity({ + * type: "PolyLine", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), + * rotation: MyAvatar.orientation, + * linePoints: [ + * { x: -1, y: 0.5, z: 0 }, + * { x: 0, y: 0, z: 0 }, + * { x: 1, y: 0.5, z: 0 } + * ], + * normals: [ + * { x: 0, y: 0, z: 1 }, + * { x: 0, y: 0, z: 1 }, + * { x: 0, y: 0, z: 1 } + * ], + * strokeWidths: [ 0.1, 0.1, 0.1 ], + * color: { red: 255, green: 0, blue: 0 }, // Use just the red channel from the image. + * textures: "https://hifi-content/DomainContent/Toybox/flowArts/trails.png", + * isUVModeStretch: true, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/*@jsdoc + * The "PolyVox" {@link Entities.EntityType|EntityType} displays a set of textured voxels. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * If you have two or more neighboring PolyVox entities of the same size abutting each other, you can display them as joined by + * configuring their voxelSurfaceStyle and various neighbor ID properties. + *

    PolyVox entities uses a library from Volumes of Fun. Their + * library documentation may be useful to read.

    + * + * @typedef {object} Entities.EntityProperties-PolyVox + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. + * @property {Vec3} voxelVolumeSize=32,32,32 - Integer number of voxels along each axis of the entity, in the range + * 1,1,1 to 128,128,128. The dimensions of each voxel is + * dimensions / voxelVolumesize. + * @property {string} voxelData="ABAAEAAQAAAAHgAAEAB42u3BAQ0AAADCoPdPbQ8HFAAAAPBuEAAAAQ==" - Base-64 encoded compressed dump of + * the PolyVox data. This property is typically not used in scripts directly; rather, functions that manipulate a PolyVox + * entity update it. + *

    The size of this property increases with the size and complexity of the PolyVox entity, with the size depending on how + * the particular entity's voxels compress. Because this property value has to fit within a Overte datagram packet, + * there is a limit to the size and complexity of a PolyVox entity; edits which would result in an overflow are rejected.

    + * @property {Entities.PolyVoxSurfaceStyle} voxelSurfaceStyle=2 - The style of rendering the voxels' surface and how + * neighboring PolyVox entities are joined. + * @property {string} xTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local x-axis. + * JPG or PNG format. If no texture is specified the surfaces display white. + * @property {string} yTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local y-axis. + * JPG or PNG format. If no texture is specified the surfaces display white. + * @property {string} zTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local z-axis. + * JPG or PNG format. If no texture is specified the surfaces display white. + * @property {Uuid} xNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local x-axis + * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. + * @property {Uuid} yNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local y-axis + * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. + * @property {Uuid} zNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local z-axis + * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. + * @property {Uuid} xPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local x-axis + * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. + * @property {Uuid} yPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local y-axis + * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. + * @property {Uuid} zPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local z-axis + * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. + * @example Create a textured PolyVox sphere. + * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })); + * var texture = "http://public.highfidelity.com/cozza13/tuscany/Concrete2.jpg"; + * var polyVox = Entities.addEntity({ + * type: "PolyVox", + * position: position, + * dimensions: { x: 2, y: 2, z: 2 }, + * voxelVolumeSize: { x: 16, y: 16, z: 16 }, + * voxelSurfaceStyle: 2, + * xTextureURL: texture, + * yTextureURL: texture, + * zTextureURL: texture, + * lifetime: 300 // Delete after 5 minutes. + * }); + * Entities.setVoxelSphere(polyVox, position, 0.8, 255); + */ + +/*@jsdoc + * The "ProceduralParticleEffect" {@link Entities.EntityType|EntityType} displays a particle system that can be + * used to simulate things such as fire, smoke, snow, magic spells, etc. The particles are fully controlled by the provided + * update and rendering shaders on the GPU. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-ProceduralParticleEffect + * @property {number} numParticles=10000 - The number of particles to render. + * @property {number} numTrianglesPerParticle=1 - The number of triangles to render per particle. By default, these triangles + * still need to be positioned in the particleRenderData vertex shader. + * @property {number} numUpdateProps=0 - The number of persistent Vec4 values stored per particle and updated once per frame. + * These can be modified in the particleUpdateData fragment shader and read in the + * particleRenderData vertex/fragment shaders. + * @property {boolean} particleTransparent=false - Whether the particles should render as transparent (with additive blending) + * or opaque. + * @property {ProceduralData} particleUpdateData="" - Used to store {@link ProceduralData} data as a JSON string to control + * per-particle updates if numUpdateProps > 0. You can use JSON.parse() to parse the string + * into a JavaScript object which you can manipulate the properties of, and use JSON.stringify() to convert + * the object into a string to put in the property. + * @property {ProceduralData} particleRenderData="" - Used to store {@link ProceduralData} data as a JSON string to control + * per-particle rendering. You can use JSON.parse() to parse the string into a JavaScript object which you + * can manipulate the properties of, and use JSON.stringify() to convert the object into a string to put in + * the property. + * + * @example A cube of oscillating, unlit, billboarded triangles, with the oscillation in the update (computed once per particle instead of once per vertex). + * particles = Entities.addEntity({ + * type: "ProceduralParticleEffect", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -4 })), + * dimensions: 3, + * numParticles: 10000, + * numTrianglesPerParticle: 1, + * numUpdateProps: 1, + * particleUpdateData: JSON.stringify({ + * version: 1.0, + * fragmentShaderURL: "https://gist.githubusercontent.com/HifiExperiments/9049fb4a8dcd2c1401ff4321103dce16/raw/4f9474ed82c66c1f94c1055d2724af808cd7aace/proceduralParticleUpdate.fs", + * }), + * particleRenderData: JSON.stringify({ + * version: 1.0, + * vertexShaderURL: "https://gist.github.com/HifiExperiments/5dda24e28e7de1719e3a594d81306343/raw/92e0c5b82a9fa87685064cdbab92ed0c16f49f94/proceduralParticle2.vs", + * fragmentShaderURL: "https://gist.github.com/HifiExperiments/7def54504362c7bc79b5c85cd515b98b/raw/93b3828c2ec66b12b789a625dd141f533c595ede/proceduralParticle.fs", + * uniforms: { + * radius: 0.03 + * } + * }), + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/*@jsdoc + * The "Shape" {@link Entities.EntityType|EntityType} displays an entity of a specified shape. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-Shape + * @property {Entities.Shape} shape="Sphere" - The shape of the entity. + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. + * @property {Color} color=255,255,255 - The color of the entity. + * @property {number} alpha=1 - The opacity of the entity, range 0.01.0. + * @property {boolean} unlit=false - true if the entity is unaffected by lighting, false if it is lit + * by the key light and local lights. + * @property {Entities.Pulse} pulse - Color and alpha pulse. + *

    Deprecated: This property is deprecated and will be removed.

    + * @example Create a cylinder. + * var shape = Entities.addEntity({ + * type: "Shape", + * shape: "Cylinder", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * dimensions: { x: 0.4, y: 0.6, z: 0.4 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/*@jsdoc + * The "Sound" {@link Entities.EntityType|EntityType} plays a sound from a URL. It has properties in addition to + * the common {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-Sound + * @property {string} soundURL="" - The URL of the sound to play, as a wav, mp3, or raw file. Supports stereo and ambisonic. + * Note: ambisonic sounds can only play as localOnly. + * @property {boolean} playing=true - Whether or not the sound should play. + * @property {number} volume=1.0 - The volume of the sound, from 0 to 1. + * @property {number} pitch=1.0 - The relative sample rate at which to resample the sound, within +/- 2 octaves. + * @property {number} timeOffset=0.0 - The time (in seconds) at which to start playback within the sound file. If looping, + * this only affects the first loop. + * @property {boolean} loop=true - Whether or not to loop the sound. + * @property {boolean} positional=true - Whether or not the volume of the sound should decay with distance. + * @property {boolean} localOnly=false - Whether or not the sound should play locally for everyone (unsynced), or synchronously + * for everyone via the Entity Mixer. + * @example Create a Sound entity. + * var entity = Entities.addEntity({ + * type: "Sound", + * soundURL: "https://themushroomkingdom.net/sounds/wav/lm/lm_gold_mouse.wav", + * positional: true, + * volume: 0.75, + * localOnly: true, + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -4 })), + * rotation: MyAvatar.orientation, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/*@jsdoc + * The "Sphere" {@link Entities.EntityType|EntityType} is the same as the "Shape" + * {@link Entities.EntityType|EntityType} except that its shape value is always set to "Sphere" + * when the entity is created. If its shape property value is subsequently changed then the entity's + * type will be reported as "Box" if the shape is set to "Cube", + * otherwise it will be reported as "Shape". + * + * @typedef {object} Entities.EntityProperties-Sphere + * @see {@link Entities.EntityProperties-Shape|EntityProperties-Shape} + */ + +/*@jsdoc + * The "Text" {@link Entities.EntityType|EntityType} displays a 2D rectangle of text in the domain. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-Text + * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. + * @property {string} text="" - The text to display on the face of the entity. Text wraps if necessary to fit. New lines can be + * created using \n. Overflowing lines are not displayed. + * @property {number} lineHeight=0.1 - The height of each line of text (thus determining the font size). + * @property {Color} textColor=255,255,255 - The color of the text. + * @property {number} textAlpha=1.0 - The opacity of the text. + * @property {Color} backgroundColor=0,0,0 - The color of the background rectangle. + * @property {number} backgroundAlpha=1.0 - The opacity of the background. + * @property {Entities.Pulse} pulse - Color and alpha pulse. + *

    Deprecated: This property is deprecated and will be removed.

    + * @property {number} leftMargin=0.0 - The left margin, in meters. + * @property {number} rightMargin=0.0 - The right margin, in meters. + * @property {number} topMargin=0.0 - The top margin, in meters. + * @property {number} bottomMargin=0.0 - The bottom margin, in meters. + * @property {boolean} unlit=false - true if the entity is unaffected by lighting, false if it is lit + * by the key light and local lights. + * @property {string} font="" - The font to render the text with. It can be one of the following: "Courier", + * "Inconsolata", "Roboto", "Timeless", or a path to a PNG MTSDF .arfont file generated + * by the msdf-atlas-gen tool (https://github.com/Chlumsky/msdf-atlas-gen). + * @property {Entities.TextEffect} textEffect="none" - The effect that is applied to the text. + * @property {Color} textEffectColor=255,255,255 - The color of the effect. + * @property {number} textEffectThickness=0.2 - The magnitude of the text effect, range 0.00.5. + * @property {Entities.TextAlignment} alignment="left" - How the text is aligned against its background. + * @property {boolean} faceCamera - true if billboardMode is "yaw", false + * if it isn't. Setting this property to false sets the billboardMode to "none". + *

    Deprecated: This property is deprecated and will be removed.

    + * @property {boolean} isFacingAvatar - true if billboardMode is "full", + * false if it isn't. Setting this property to false sets the billboardMode to + * "none". + *

    Deprecated: This property is deprecated and will be removed.

    + * @example Create a text entity. + * var text = Entities.addEntity({ + * type: "Text", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * dimensions: { x: 0.6, y: 0.3, z: 0.01 }, + * lineHeight: 0.12, + * text: "Hello\nthere!", + * billboardMode: "yaw", + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/*@jsdoc + * The "Web" {@link Entities.EntityType|EntityType} displays a browsable web page. Each user views their own copy + * of the web page: if one user navigates to another page on the entity, other users do not see the change; if a video is being + * played, users don't see it in sync. Internally, a Web entity is rendered as a non-repeating, upside down texture, so additional + * transformations may be necessary if you reference a Web entity texture by UUID. It has properties in addition to the common + * {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-Web + * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. + * @property {string} sourceUrl="" - The URL of the web page to display. This value does not change as you or others navigate + * on the Web entity. + * @property {Color} color=255,255,255 - The color of the web surface. This color tints the web page displayed: the pixel + * colors on the web page are multiplied by the property color. For example, a value of + * { red: 255, green: 0, blue: 0 } lets only the red channel of pixels' colors through. + * @property {number} alpha=1 - The opacity of the web surface. + * @property {Entities.Pulse} pulse - Color and alpha pulse. + *

    Deprecated: This property is deprecated and will be removed.

    + * @property {boolean} faceCamera - true if billboardMode is "yaw", false + * if it isn't. Setting this property to false sets the billboardMode to "none". + *

    Deprecated: This property is deprecated and will be removed.

    + * @property {boolean} isFacingAvatar - true if billboardMode is "full", + * false if it isn't. Setting this property to false sets the billboardMode to + * "none". + *

    Deprecated: This property is deprecated and will be removed.

    + * @property {number} dpi=30 - The resolution to display the page at, in dots per inch. If you convert this to dots per meter + * (multiply by 1 / 0.0254 = 39.3701) then multiply dimensions.x and dimensions.y by that value + * you get the resolution in pixels. + * @property {string} scriptURL="" - The URL of a JavaScript file to inject into the web page. + * @property {number} maxFPS=10 - The maximum update rate for the web content, in frames/second. + * @property {WebInputMode} inputMode="touch" - The user input mode to use. + * @property {boolean} wantsKeyboardFocus=true - true if the entity should capture keyboard focus, false if it + * shouldn't. + * @property {boolean} showKeyboardFocusHighlight=true - true if the entity is highlighted when it has keyboard + * focus, false if it isn't. + * @property {boolean} useBackground=true - true if the web entity should have a background, + * false if the web entity's background should be transparent. The webpage must have CSS properties for transparency set + * on the background-color for this property to have an effect. + * @property {string} userAgent - The user agent for the web entity to use when visiting web pages. + * Default value: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) + * Chrome/69.0.3497.113 Mobile Safari/537.36 + * @example Create a Web entity displaying at 1920 x 1080 resolution. + * var METERS_TO_INCHES = 39.3701; + * var entity = Entities.addEntity({ + * type: "Web", + * sourceUrl: "https://overte.org/", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -4 })), + * rotation: MyAvatar.orientation, + * dimensions: { + * x: 3, + * y: 3 * 1080 / 1920, + * z: 0.01 + * }, + * dpi: 1920 / (3 * METERS_TO_INCHES), + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/*@jsdoc + * The "Zone" {@link Entities.EntityType|EntityType} is a volume of lighting effects and avatar permissions. + * Avatar interaction events such as {@link Entities.enterEntity} are also often used with a Zone entity. It has properties in + * addition to the common {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-Zone + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the volume in which the zone's lighting effects and avatar + * permissions have effect. + * + * @property {ShapeType} shapeType="box" - The shape of the volume in which the zone's lighting effects and avatar + * permissions have effect. Reverts to the default value if set to "none", or set to "compound" + * and compoundShapeURL is "". + * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType is + * "compound". + * + * @property {Entities.ComponentMode} keyLightMode="inherit" - Configures the key light in the zone. + * @property {Entities.KeyLight} keyLight - The key light properties of the zone. + * + * @property {Entities.ComponentMode} ambientLightMode="inherit" - Configures the ambient light in the zone. + * @property {Entities.AmbientLight} ambientLight - The ambient light properties of the zone. + * + * @property {Entities.ComponentMode} skyboxMode="inherit" - Configures the skybox displayed in the zone. + * @property {Entities.Skybox} skybox - The skybox properties of the zone. + * + * @property {Entities.ComponentMode} hazeMode="inherit" - Configures the haze in the zone. + * @property {Entities.Haze} haze - The haze properties of the zone. + * + * @property {Entities.ComponentMode} bloomMode="inherit" - Configures the bloom in the zone. + * @property {Entities.Bloom} bloom - The bloom properties of the zone. + * + * @property {Entities.ZoneAudio} audio - The audio properties of the zone. + * + * @property {Entities.ComponentMode} tonemappingMode="inherit" - Configures the tonemapping in the zone. + * @property {Entities.Tonemapping} tonemapping - The tonemapping properties of the zone. + * + * @property {Entities.ComponentMode} ambientOcclusionMode="inherit" - Configures the ambient occlusion in the zone. + * @property {Entities.AmbientOcclusion} ambientOcclusion - The ambient occlusion properties of the zone. + * + * @property {boolean} flyingAllowed=true - true if visitors can fly in the zone; false if they + * cannot. Only works for domain entities. + * @property {boolean} ghostingAllowed=true - true if visitors with avatar collisions turned off will not + * collide with content in the zone; false if visitors will always collide with content in the zone. Only + * works for domain entities. + * + * @property {string} filterURL="" - The URL of a JavaScript file that filters changes to properties of entities within the + * zone. It is periodically executed for each entity in the zone. It can, for example, be used to not allow changes to + * certain properties: + *
    + * function filter(properties) {
    + *     // Check and edit properties object values,
    + *     // e.g., properties.modelURL, as required.
    + *     return properties;
    + * }
    + * 
    + * + * @property {Entities.AvatarPriorityMode} avatarPriority="inherit" - Configures the priority of updates from avatars in the + * zone to other clients. + * + * @property {Entities.ScreenshareMode} screenshare="inherit" - Configures a zone for screen-sharing. + * + * @example Create a zone that casts a red key light along the x-axis. + * var zone = Entities.addEntity({ + * type: "Zone", + * position: MyAvatar.position, + * dimensions: { x: 100, y: 100, z: 100 }, + * keyLightMode: "enabled", + * keyLight: { + * "color": { "red": 255, "green": 0, "blue": 0 }, + * "direction": { "x": 1, "y": 0, "z": 0 } + * }, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/*@jsdoc + * The "Image" {@link Entities.EntityType|EntityType} displays an image on a 2D rectangle in the domain. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-Image + * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. + * @property {string} imageURL="" - The URL of the image to use. + * @property {boolean} emissive=false - true if the image should be emissive (unlit), false if it + * shouldn't. + * @property {boolean} keepAspectRatio=true - true if the image should maintain its aspect ratio, + * false if it shouldn't. + * @property {Rect} subImage=0,0,0,0 - The portion of the image to display. If width or height are 0, it defaults + * to the full image in that dimension. + * @property {Color} color=255,255,255 - The color of the image. + * @property {number} alpha=1 - The opacity of the image. + * @property {Entities.Pulse} pulse - Color and alpha pulse. + *

    Deprecated: This property is deprecated and will be removed.

    + * @property {boolean} faceCamera - true if billboardMode is "yaw", false + * if it isn't. Setting this property to false sets the billboardMode to "none". + *

    Deprecated: This property is deprecated and will be removed.

    + * @property {boolean} isFacingAvatar - true if billboardMode is "full", + * false if it isn't. Setting this property to false sets the billboardMode to + * "none". + *

    Deprecated: This property is deprecated and will be removed.

    + * @example Create an image entity. + * var image = Entities.addEntity({ + * type: "Image", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * dimensions: { x: 0.6, y: 0.3, z: 0.01 }, + * imageURL: "https://images.pexels.com/photos/1020315/pexels-photo-1020315.jpeg", + * billboardMode: "yaw", + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/*@jsdoc + * The "Grid" {@link Entities.EntityType|EntityType} displays a grid on a 2D plane. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-Grid + * @property {Vec3} dimensions - 0.1,0.1,0.01 - The dimensions of the entity. + * @property {Color} color=255,255,255 - The color of the grid. + * @property {number} alpha=1 - The opacity of the grid. + * @property {Entities.Pulse} pulse - Color and alpha pulse. + *

    Deprecated: This property is deprecated and will be removed.

    + * @property {boolean} followCamera=true - true if the grid is always visible even as the camera moves to another + * position, false if it doesn't follow the camrmea. + * @property {number} majorGridEvery=5 - Integer number of minorGridEvery intervals at which to draw a thick grid + * line. Minimum value = 1. + * @property {number} minorGridEvery=1 - Real number of meters at which to draw thin grid lines. Minimum value = + * 0.001. + * @example Create a grid entity. + * var grid = Entities.addEntity({ + * type: "Grid", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * dimensions: { x: 100.0, y: 100.0, z: 0.01 }, + * followCamera: false, + * majorGridEvery: 4, + * minorGridEvery: 0.5, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/*@jsdoc + * The "Gizmo" {@link Entities.EntityType|EntityType} displays an entity that could be used as UI. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-Gizmo + * @property {Vec3} dimensions=0.1,0.001,0.1 - The dimensions of the entity. + * @property {Entities.GizmoType} gizmoType="ring" - The gizmo type of the entity. + * @property {Entities.RingGizmo} ring - The ring gizmo properties. + */ diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 0fe438459d..71ddb95648 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -16,11 +16,38 @@ #define hifi_EntityItemPropertiesMacros_h #include +#include #include #include #include #include +const quint64 UNKNOWN_CREATED_TIME = 0; + +using vec3Color = glm::vec3; +using u8vec3Color = glm::u8vec3; + +struct EntityPropertyInfo { + EntityPropertyInfo(EntityPropertyList propEnum) : + propertyEnums(propEnum) {} + EntityPropertyInfo(EntityPropertyList propEnum, QVariant min, QVariant max) : + propertyEnums(propEnum), minimum(min), maximum(max) {} + EntityPropertyInfo() = default; + EntityPropertyFlags propertyEnums; + QVariant minimum; + QVariant maximum; +}; + +template +EntityPropertyInfo makePropertyInfo(EntityPropertyList p, typename std::enable_if::value>::type* = 0) { + return EntityPropertyInfo(p); +} + +template +EntityPropertyInfo makePropertyInfo(EntityPropertyList p, typename std::enable_if::value>::type* = 0) { + return EntityPropertyInfo(p, std::numeric_limits::min(), std::numeric_limits::max()); +} + #define APPEND_ENTITY_PROPERTY(P,V) \ if (requestedProperties.getHasProperty(P)) { \ LevelDetails propertyLevel = packetData->startLevel(); \ @@ -201,7 +228,7 @@ inline ScriptValue convertScriptValue(ScriptEngine* e, const AACube& v) { return #define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(p, P, G, T) \ if ((_desiredProperties.isEmpty() || _desiredProperties.getHasProperty(p)) && \ (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ - ScriptValue V = T##_convertScriptValue(engine, G); \ + ScriptValue V = T##_convertScriptValue(engine, G()); \ properties.setProperty(#P, V); \ } @@ -209,7 +236,7 @@ inline ScriptValue convertScriptValue(ScriptEngine* e, const AACube& v) { return #define COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(p, P, X, G) \ if (((!returnNothingOnEmptyPropertyFlags && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(p)) && \ (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ - ScriptValue V = convertScriptValue(engine, G); \ + ScriptValue V = convertScriptValue(engine, G()); \ properties.setProperty(#X, V); \ } @@ -256,6 +283,7 @@ typedef QVector qVectorBool; typedef QVector qVectorFloat; typedef QVector qVectorQUuid; typedef QVector qVectorQString; +typedef QSet qSetQString; inline float float_convertFromScriptValue(const ScriptValue& v, bool& isValid) { return v.toVariant().toFloat(&isValid); } inline quint64 quint64_convertFromScriptValue(const ScriptValue& v, bool& isValid) { return v.toVariant().toULongLong(&isValid); } inline quint32 quint32_convertFromScriptValue(const ScriptValue& v, bool& isValid) { @@ -380,8 +408,6 @@ inline QRect QRect_convertFromScriptValue(const ScriptValue& v, bool& isValid) { } \ } - - #define COPY_PROPERTY_FROM_QSCRIPTVALUE(P, T, S) \ { \ if (namesSet.contains(#P)) { \ @@ -479,21 +505,21 @@ inline QRect QRect_convertFromScriptValue(const ScriptValue& v, bool& isValid) { static T _static##N; -#define ADD_PROPERTY_TO_MAP(P, N, n, T) \ +#define ADD_PROPERTY_TO_MAP(P, n, T) \ { \ EntityPropertyInfo propertyInfo { makePropertyInfo(P) }; \ _propertyInfos[#n] = propertyInfo; \ _enumsToPropertyStrings[P] = #n; \ } -#define ADD_PROPERTY_TO_MAP_WITH_RANGE(P, N, n, T, M, X) \ +#define ADD_PROPERTY_TO_MAP_WITH_RANGE(P, n, M, X) \ { \ EntityPropertyInfo propertyInfo = EntityPropertyInfo(P, M, X); \ _propertyInfos[#n] = propertyInfo; \ _enumsToPropertyStrings[P] = #n; \ } -#define ADD_GROUP_PROPERTY_TO_MAP(P, G, g, N, n) \ +#define ADD_GROUP_PROPERTY_TO_MAP(P, g, n) \ { \ EntityPropertyInfo propertyInfo = EntityPropertyInfo(P); \ _propertyInfos[#g "." #n] = propertyInfo; \ @@ -501,7 +527,7 @@ inline QRect QRect_convertFromScriptValue(const ScriptValue& v, bool& isValid) { _enumsToPropertyStrings[P] = #g "." #n; \ } -#define ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(P, G, g, N, n, M, X) \ +#define ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(P, g, n, M, X) \ { \ EntityPropertyInfo propertyInfo = EntityPropertyInfo(P, M, X); \ _propertyInfos[#g "." #n] = propertyInfo; \ @@ -517,31 +543,31 @@ inline QRect QRect_convertFromScriptValue(const ScriptValue& v, bool& isValid) { T _##n = V; \ bool _##n##Changed { false }; -#define DEFINE_PROPERTY(P, N, n, T, V) \ +#define DEFINE_PROPERTY(N, n, T, V) \ public: \ T get##N() const { return _##n; } \ void set##N(T value) { _##n = value; _##n##Changed = true; } \ DEFINE_CORE(N, n, T, V) -#define DEFINE_PROPERTY_REF(P, N, n, T, V) \ +#define DEFINE_PROPERTY_REF(N, n, T, V) \ public: \ const T& get##N() const { return _##n; } \ void set##N(const T& value) { _##n = value; _##n##Changed = true; } \ DEFINE_CORE(N, n, T, V) -#define DEFINE_PROPERTY_REF_WITH_SETTER(P, N, n, T, V) \ +#define DEFINE_PROPERTY_REF_WITH_SETTER(N, n, T, V) \ public: \ const T& get##N() const { return _##n; } \ void set##N(const T& value); \ DEFINE_CORE(N, n, T, V) -#define DEFINE_PROPERTY_REF_WITH_SETTER_AND_GETTER(P, N, n, T, V) \ +#define DEFINE_PROPERTY_REF_WITH_SETTER_AND_GETTER(N, n, T, V) \ public: \ T get##N() const; \ void set##N(const T& value); \ DEFINE_CORE(N, n, T, V) -#define DEFINE_PROPERTY_REF_ENUM(P, N, n, T, V) \ +#define DEFINE_PROPERTY_REF_ENUM(N, n, T, V) \ public: \ const T& get##N() const { return _##n; } \ void set##N(const T& value) { _##n = value; _##n##Changed = true; } \ @@ -557,4 +583,134 @@ inline QRect QRect_convertFromScriptValue(const ScriptValue& v, bool& isValid) { D << " " << #n << ":" << P.get##N() << x << "\n"; \ } +// EntityItem helpers +#define DEFINE_VARIABLE_NO_GETTER_SETTER(N, n, T, V) \ + protected: \ + T _##n = V; + +#define DEFINE_VARIABLE(N, n, T, V) \ + public: \ + T get##N() const; \ + void set##N(T value); \ + protected: \ + T _##n = V; + +#define DEFINE_VARIABLE_REF(N, n, T, V) \ + public: \ + T get##N() const; \ + void set##N(const T& value); \ + protected: \ + T _##n = V; + +#define DEFINE_VARIABLE_BASIC(N, n, T, V) \ + public: \ + T get##N() const { \ + return resultWithReadLock([&] { \ + return _##n; \ + }); \ + } \ + void set##N(T value) { \ + withWriteLock([&] { \ + _##n = value; \ + }); \ + } \ + protected: \ + T _##n = V; + +#define DEFINE_VARIABLE_BASIC_REF(N, n, T, V) \ + public: \ + T get##N() const { \ + return resultWithReadLock([&] { \ + return _##n; \ + }); \ + } \ + void set##N(const T& value) { \ + withWriteLock([&] { \ + _##n = value; \ + }); \ + } \ + protected: \ + T _##n = V; + +#define DEFINE_VARIABLE_RENDER(N, n, T, V) \ + public: \ + T get##N() const { \ + return resultWithReadLock([&] { \ + return _##n; \ + }); \ + } \ + void set##N(T value) { \ + withWriteLock([&] { \ + _needsRenderUpdate |= _##n != value; \ + _##n = value; \ + }); \ + } \ + protected: \ + T _##n = V; + +#define DEFINE_VARIABLE_RENDER_REF(N, n, T, V) \ + public: \ + T get##N() const { \ + return resultWithReadLock([&] { \ + return _##n; \ + }); \ + } \ + void set##N(const T& value) { \ + withWriteLock([&] { \ + _needsRenderUpdate |= _##n != value; \ + _##n = value; \ + }); \ + } \ + protected: \ + T _##n = V; + +#define ENTITY_PROPERTY_SUBCLASS_METHODS \ + EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, \ + bool allowEmptyDesiredProperties) const override; \ + bool setSubClassProperties(const EntityItemProperties& properties) override; \ + EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; \ + void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, \ + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, \ + EntityPropertyFlags& requestedProperties, \ + EntityPropertyFlags& propertyFlags, \ + EntityPropertyFlags& propertiesDidntFit, \ + int& propertyCount, \ + OctreeElement::AppendState& appendState) const override; \ + int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, \ + ReadBitstreamToTreeParams& args, \ + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, \ + bool& somethingChanged) override; \ + virtual void debugDump() const override; + +#define ENTITY_PROPERTY_GROUP_METHODS(P) \ + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, \ + ScriptEngine* engine, bool skipDefaults, \ + EntityItemProperties& defaultEntityProperties, \ + bool returnNothingOnEmptyPropertyFlags, \ + bool isMyOwnAvatarEntity) const override; \ + virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, \ + bool& _defaultSettings) override; \ + void merge(const P& other); \ + virtual void debugDump() const override; \ + virtual void listChangedProperties(QList& out) override; \ + virtual bool appendToEditPacket(OctreePacketData* packetData, \ + EntityPropertyFlags& requestedProperties, \ + EntityPropertyFlags& propertyFlags, \ + EntityPropertyFlags& propertiesDidntFit, \ + int& propertyCount, \ + OctreeElement::AppendState& appendState) const override; \ + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, \ + const unsigned char*& dataAt, int& processedBytes) override; \ + virtual void markAllChanged() override; \ + virtual EntityPropertyFlags getChangedProperties() const override; \ + virtual void getProperties(EntityItemProperties& propertiesOut) const override; \ + virtual bool setProperties(const EntityItemProperties& properties) override; \ + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; \ + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, \ + ReadBitstreamToTreeParams& args, \ + EntityPropertyFlags& propertyFlags, \ + bool overwriteLocalData, bool& somethingChanged) override; \ + static void addPropertyMap(QHash& _propertyInfos, \ + QHash& _enumsToPropertyStrings); + #endif // hifi_EntityItemPropertiesMacros_h diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index a95feed55b..d5de3b2118 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -95,20 +95,6 @@ enum EntityPropertyList { PROP_SCRIPT_TIMESTAMP, PROP_SERVER_SCRIPTS, - // Certifiable Properties - PROP_ITEM_NAME, - PROP_ITEM_DESCRIPTION, - PROP_ITEM_CATEGORIES, - PROP_ITEM_ARTIST, - PROP_ITEM_LICENSE, - PROP_LIMITED_RUN, - PROP_MARKETPLACE_ID, - PROP_EDITION_NUMBER, - PROP_ENTITY_INSTANCE_NUMBER, - PROP_CERTIFICATE_ID, - PROP_CERTIFICATE_TYPE, - PROP_STATIC_CERTIFICATE_VERSION, - // Used to convert values to and from scripts PROP_LOCAL_POSITION, PROP_LOCAL_ROTATION, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 156023643b..6bb0c0b69f 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1887,7 +1887,7 @@ bool EntityScriptingInterface::setAllPoints(const QUuid& entityID, const QVector if (entityType == EntityTypes::Line) { return setPoints(entityID, [points](LineEntityItem& lineEntity) -> bool { - return (LineEntityItem*)lineEntity.setLinePoints(points); + return lineEntity.setLinePoints(points); }); } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 209240533b..7a70076cf8 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2804,8 +2804,8 @@ bool EntityTree::readFromMap(QVariantMap& map, const bool isImport) { // Use skybox value only if it is not empty, else set ambientMode to inherit (to use default URL) properties.setAmbientLightMode(COMPONENT_MODE_ENABLED); if (properties.getAmbientLight().getAmbientURL() == "") { - if (properties.getSkybox().getURL() != "") { - properties.getAmbientLight().setAmbientURL(properties.getSkybox().getURL()); + if (properties.getSkybox().getUrl() != "") { + properties.getAmbientLight().setAmbientURL(properties.getSkybox().getUrl()); } else { properties.setAmbientLightMode(COMPONENT_MODE_INHERIT); } diff --git a/libraries/entities/src/GizmoEntityItem.cpp b/libraries/entities/src/GizmoEntityItem.cpp.in similarity index 77% rename from libraries/entities/src/GizmoEntityItem.cpp rename to libraries/entities/src/GizmoEntityItem.cpp.in index c53f2293fb..ea48d9bcff 100644 --- a/libraries/entities/src/GizmoEntityItem.cpp +++ b/libraries/entities/src/GizmoEntityItem.cpp.in @@ -34,10 +34,7 @@ void GizmoEntityItem::setUnscaledDimensions(const glm::vec3& value) { EntityItemProperties GizmoEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(gizmoType, getGizmoType); - withReadLock([&] { - _ringProperties.getProperties(properties); - }); +@Gizmo_ENTITY_COPY_TO@ return properties; } @@ -45,16 +42,33 @@ EntityItemProperties GizmoEntityItem::getProperties(const EntityPropertyFlags& d bool GizmoEntityItem::setSubClassProperties(const EntityItemProperties& properties) { bool somethingChanged = false; - SET_ENTITY_PROPERTY_FROM_PROPERTIES(gizmoType, setGizmoType); - withWriteLock([&] { - bool ringPropertiesChanged = _ringProperties.setProperties(properties); - somethingChanged |= ringPropertiesChanged; - _needsRenderUpdate |= ringPropertiesChanged; - }); +@Gizmo_ENTITY_SET_FROM@ return somethingChanged; } +EntityPropertyFlags GizmoEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + +@Gizmo_REQUESTED_PROPS@ + + return requestedProperties; +} + +void GizmoEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Gizmo_ENTITY_APPEND@ + +} + int GizmoEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, @@ -63,42 +77,21 @@ int GizmoEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_GIZMO_TYPE, GizmoType, setGizmoType); - withWriteLock([&] { - int bytesFromRing = _ringProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, - somethingChanged); - bytesRead += bytesFromRing; - dataAt += bytesFromRing; - }); +@Gizmo_ENTITY_READ@ return bytesRead; } -EntityPropertyFlags GizmoEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); +void GizmoEntityItem::debugDump() const { + qCDebug(entities) << "GizmoEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; - requestedProperties += PROP_GIZMO_TYPE; - requestedProperties += _ringProperties.getEntityProperties(params); +@Gizmo_ENTITY_DEBUG@ - return requestedProperties; -} - -void GizmoEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_GIZMO_TYPE, (uint32_t)getGizmoType()); - withReadLock([&] { - _ringProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - }); } bool GizmoEntityItem::supportsDetailedIntersection() const { @@ -181,19 +174,6 @@ bool GizmoEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, return false; } -void GizmoEntityItem::setGizmoType(GizmoType value) { - withWriteLock([&] { - _needsRenderUpdate |= _gizmoType != value; - _gizmoType = value; - }); -} - -GizmoType GizmoEntityItem::getGizmoType() const { - return resultWithReadLock([&] { - return _gizmoType; - }); -} - RingGizmoPropertyGroup GizmoEntityItem::getRingProperties() const { return resultWithReadLock([&] { return _ringProperties; diff --git a/libraries/entities/src/GizmoEntityItem.h b/libraries/entities/src/GizmoEntityItem.h.in similarity index 54% rename from libraries/entities/src/GizmoEntityItem.h rename to libraries/entities/src/GizmoEntityItem.h.in index a05c294523..9396bcc91e 100644 --- a/libraries/entities/src/GizmoEntityItem.h +++ b/libraries/entities/src/GizmoEntityItem.h.in @@ -21,28 +21,10 @@ public: GizmoEntityItem(const EntityItemID& entityItemID); ALLOW_INSTANTIATION // This class can be instantiated + ENTITY_PROPERTY_SUBCLASS_METHODS virtual void setUnscaledDimensions(const glm::vec3& value) override; - // methods for getting/setting all properties of an entity - EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - bool setSubClassProperties(const EntityItemProperties& properties) override; - - EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - bool supportsDetailedIntersection() const override; bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& viewFrustumPos, OctreeElementPointer& element, float& distance, @@ -54,14 +36,11 @@ public: QVariantMap& extraInfo, bool precisionPicking) const override; bool getRotateForPicking() const override { return getBillboardMode() != BillboardMode::NONE; } - GizmoType getGizmoType() const; - void setGizmoType(GizmoType value); - RingGizmoPropertyGroup getRingProperties() const; protected: - GizmoType _gizmoType; - RingGizmoPropertyGroup _ringProperties; + +@Gizmo_ENTITY_PROPS@ }; diff --git a/libraries/entities/src/GrabPropertyGroup.cpp b/libraries/entities/src/GrabPropertyGroup.cpp deleted file mode 100644 index a4037ff98f..0000000000 --- a/libraries/entities/src/GrabPropertyGroup.cpp +++ /dev/null @@ -1,354 +0,0 @@ -// -// GrabPropertyGroup.h -// libraries/entities/src -// -// Created by Seth Alves on 2018-8-8. -// Copyright 2018 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#include "GrabPropertyGroup.h" - -#include - -#include "EntityItemProperties.h" -#include "EntityItemPropertiesMacros.h" - -void GrabPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const { - auto nodeList = DependencyManager::get(); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_GRABBABLE, Grab, grab, Grabbable, grabbable); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_KINEMATIC, Grab, grab, GrabKinematic, grabKinematic); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_FOLLOWS_CONTROLLER, Grab, grab, GrabFollowsController, grabFollowsController); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_TRIGGERABLE, Grab, grab, Triggerable, triggerable); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE, Grab, grab, Equippable, equippable); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_DELEGATE_TO_PARENT, Grab, grab, - GrabDelegateToParent, grabDelegateToParent); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, Grab, grab, - EquippableLeftPosition, equippableLeftPosition); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, - EquippableLeftRotation, equippableLeftRotation); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, Grab, grab, - EquippableRightPosition, equippableRightPosition); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, - EquippableRightRotation, equippableRightRotation); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, Grab, grab, - EquippableIndicatorURL, equippableIndicatorURL); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, Grab, grab, - EquippableIndicatorScale, equippableIndicatorScale); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, Grab, grab, - EquippableIndicatorOffset, equippableIndicatorOffset); - -} - -void GrabPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, grabbable, bool, setGrabbable); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, grabKinematic, bool, setGrabKinematic); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, grabFollowsController, bool, setGrabFollowsController); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, triggerable, bool, setTriggerable); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippable, bool, setEquippable); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, grabDelegateToParent, bool, setGrabDelegateToParent); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableLeftPosition, vec3, setEquippableLeftPosition); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableLeftRotation, quat, setEquippableLeftRotation); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableRightPosition, vec3, setEquippableRightPosition); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableRightRotation, quat, setEquippableRightRotation); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableIndicatorURL, QString, setEquippableIndicatorURL); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableIndicatorScale, vec3, setEquippableIndicatorScale); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableIndicatorOffset, vec3, setEquippableIndicatorOffset); -} - -void GrabPropertyGroup::merge(const GrabPropertyGroup& other) { - COPY_PROPERTY_IF_CHANGED(grabbable); - COPY_PROPERTY_IF_CHANGED(grabKinematic); - COPY_PROPERTY_IF_CHANGED(grabFollowsController); - COPY_PROPERTY_IF_CHANGED(triggerable); - COPY_PROPERTY_IF_CHANGED(equippable); - COPY_PROPERTY_IF_CHANGED(grabDelegateToParent); - COPY_PROPERTY_IF_CHANGED(equippableLeftPosition); - COPY_PROPERTY_IF_CHANGED(equippableLeftRotation); - COPY_PROPERTY_IF_CHANGED(equippableRightPosition); - COPY_PROPERTY_IF_CHANGED(equippableRightRotation); - COPY_PROPERTY_IF_CHANGED(equippableIndicatorURL); - COPY_PROPERTY_IF_CHANGED(equippableIndicatorScale); - COPY_PROPERTY_IF_CHANGED(equippableIndicatorOffset); -} - -void GrabPropertyGroup::debugDump() const { - qCDebug(entities) << " GrabPropertyGroup: ---------------------------------------------"; - - qCDebug(entities) << " _grabbable:" << _grabbable; - qCDebug(entities) << " _grabKinematic:" << _grabKinematic; - qCDebug(entities) << " _grabFollowsController:" << _grabFollowsController; - qCDebug(entities) << " _triggerable:" << _triggerable; - qCDebug(entities) << " _equippable:" << _equippable; - qCDebug(entities) << " _equippableLeftPosition:" << _equippableLeftPosition; - qCDebug(entities) << " _equippableLeftRotation:" << _equippableLeftRotation; - qCDebug(entities) << " _equippableRightPosition:" << _equippableRightPosition; - qCDebug(entities) << " _equippableRightRotation:" << _equippableRightRotation; - qCDebug(entities) << " _equippableIndicatorURL:" << _equippableIndicatorURL; - qCDebug(entities) << " _equippableIndicatorScale:" << _equippableIndicatorScale; - qCDebug(entities) << " _equippableIndicatorOffset:" << _equippableIndicatorOffset; -} - -void GrabPropertyGroup::listChangedProperties(QList& out) { - if (grabbableChanged()) { - out << "grab-grabbable"; - } - if (grabKinematicChanged()) { - out << "grab-grabKinematic"; - } - if (grabFollowsControllerChanged()) { - out << "grab-followsController"; - } - if (triggerableChanged()) { - out << "grab-triggerable"; - } - if (equippableChanged()) { - out << "grab-equippable"; - } - if (grabDelegateToParentChanged()) { - out << "grab-grabDelegateToParent"; - } - if (equippableLeftPositionChanged()) { - out << "grab-equippableLeftPosition"; - } - if (equippableLeftRotationChanged()) { - out << "grab-equippableLeftRotation"; - } - if (equippableRightPositionChanged()) { - out << "grab-equippableRightPosition"; - } - if (equippableRightRotationChanged()) { - out << "grab-equippableRightRotation"; - } - if (equippableIndicatorURLChanged()) { - out << "grab-equippableIndicatorURL"; - } - if (equippableIndicatorScaleChanged()) { - out << "grab-equippableIndicatorScale"; - } - if (equippableIndicatorOffsetChanged()) { - out << "grab-equippableIndicatorOffset"; - } -} - -bool GrabPropertyGroup::appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_GRAB_GRABBABLE, getGrabbable()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_KINEMATIC, getGrabKinematic()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, getGrabFollowsController()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, getTriggerable()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, getEquippable()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, getGrabDelegateToParent()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, getEquippableLeftPosition()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, getEquippableLeftRotation()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, getEquippableRightPosition()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, getEquippableRightRotation()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, getEquippableIndicatorURL()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, getEquippableIndicatorScale()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, getEquippableIndicatorOffset()); - - return true; -} - -bool GrabPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt , int& processedBytes) { - - int bytesRead = 0; - bool overwriteLocalData = true; - bool somethingChanged = false; - - READ_ENTITY_PROPERTY(PROP_GRAB_GRABBABLE, bool, setGrabbable); - READ_ENTITY_PROPERTY(PROP_GRAB_KINEMATIC, bool, setGrabKinematic); - READ_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, bool, setGrabFollowsController); - READ_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, bool, setTriggerable); - READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, bool, setEquippable); - READ_ENTITY_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, bool, setGrabDelegateToParent); - READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableLeftPosition); - READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableLeftRotation); - READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableRightPosition); - READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableRightRotation); - READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, QString, setEquippableIndicatorURL); - READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, glm::vec3, setEquippableIndicatorScale); - READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, glm::vec3, setEquippableIndicatorOffset); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_GRABBABLE, Grabbable); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_KINEMATIC, GrabKinematic); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_FOLLOWS_CONTROLLER, GrabFollowsController); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_TRIGGERABLE, Triggerable); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_EQUIPPABLE, Equippable); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_DELEGATE_TO_PARENT, GrabDelegateToParent); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, EquippableLeftPosition); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, EquippableLeftRotation); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, EquippableRightPosition); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, EquippableRightRotation); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, EquippableIndicatorURL); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, EquippableIndicatorScale); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, EquippableIndicatorOffset); - - processedBytes += bytesRead; - - Q_UNUSED(somethingChanged); - - return true; -} - -void GrabPropertyGroup::markAllChanged() { - _grabbableChanged = true; - _grabKinematicChanged = true; - _grabFollowsControllerChanged = true; - _triggerableChanged = true; - _equippableChanged = true; - _grabDelegateToParentChanged = true; - _equippableLeftPositionChanged = true; - _equippableLeftRotationChanged = true; - _equippableRightPositionChanged = true; - _equippableRightRotationChanged = true; - _equippableIndicatorURLChanged = true; - _equippableIndicatorScaleChanged = true; - _equippableIndicatorOffsetChanged = true; -} - -EntityPropertyFlags GrabPropertyGroup::getChangedProperties() const { - EntityPropertyFlags changedProperties; - - CHECK_PROPERTY_CHANGE(PROP_GRAB_GRABBABLE, grabbable); - CHECK_PROPERTY_CHANGE(PROP_GRAB_KINEMATIC, grabKinematic); - CHECK_PROPERTY_CHANGE(PROP_GRAB_FOLLOWS_CONTROLLER, grabFollowsController); - CHECK_PROPERTY_CHANGE(PROP_GRAB_TRIGGERABLE, triggerable); - CHECK_PROPERTY_CHANGE(PROP_GRAB_EQUIPPABLE, equippable); - CHECK_PROPERTY_CHANGE(PROP_GRAB_DELEGATE_TO_PARENT, grabDelegateToParent); - CHECK_PROPERTY_CHANGE(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, equippableLeftPosition); - CHECK_PROPERTY_CHANGE(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, equippableLeftRotation); - CHECK_PROPERTY_CHANGE(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, equippableRightPosition); - CHECK_PROPERTY_CHANGE(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, equippableRightRotation); - CHECK_PROPERTY_CHANGE(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, equippableIndicatorURL); - CHECK_PROPERTY_CHANGE(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, equippableIndicatorScale); - CHECK_PROPERTY_CHANGE(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, equippableIndicatorOffset); - - return changedProperties; -} - -void GrabPropertyGroup::getProperties(EntityItemProperties& properties) const { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, Grabbable, getGrabbable); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, GrabKinematic, getGrabKinematic); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, GrabFollowsController, getGrabFollowsController); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, Triggerable, getTriggerable); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, Equippable, getEquippable); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, GrabDelegateToParent, getGrabDelegateToParent); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableLeftPosition, getEquippableLeftPosition); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableLeftRotation, getEquippableLeftRotation); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableRightPosition, getEquippableRightPosition); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableRightRotation, getEquippableRightRotation); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableIndicatorURL, getEquippableIndicatorURL); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableIndicatorScale, getEquippableIndicatorScale); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableIndicatorOffset, getEquippableIndicatorOffset); -} - -bool GrabPropertyGroup::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, Grabbable, grabbable, setGrabbable); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, GrabKinematic, grabKinematic, setGrabKinematic); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, GrabFollowsController, grabFollowsController, setGrabFollowsController); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, Triggerable, triggerable, setTriggerable); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, Equippable, equippable, setEquippable); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, GrabDelegateToParent, grabDelegateToParent, setGrabDelegateToParent); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableLeftPosition, equippableLeftPosition, setEquippableLeftPosition); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableLeftRotation, equippableLeftRotation, setEquippableLeftRotation); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableRightPosition, equippableRightPosition, - setEquippableRightPosition); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableRightRotation, equippableRightRotation, - setEquippableRightRotation); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableIndicatorURL, equippableIndicatorURL, - setEquippableIndicatorURL); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableIndicatorScale, equippableIndicatorScale, - setEquippableIndicatorScale); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableIndicatorOffset, equippableIndicatorOffset, - setEquippableIndicatorOffset); - - return somethingChanged; -} - -EntityPropertyFlags GrabPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties; - - requestedProperties += PROP_GRAB_GRABBABLE; - requestedProperties += PROP_GRAB_KINEMATIC; - requestedProperties += PROP_GRAB_FOLLOWS_CONTROLLER; - requestedProperties += PROP_GRAB_TRIGGERABLE; - requestedProperties += PROP_GRAB_EQUIPPABLE; - requestedProperties += PROP_GRAB_DELEGATE_TO_PARENT; - requestedProperties += PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET; - requestedProperties += PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET; - requestedProperties += PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET; - requestedProperties += PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET; - requestedProperties += PROP_GRAB_EQUIPPABLE_INDICATOR_URL; - requestedProperties += PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE; - requestedProperties += PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET; - - return requestedProperties; -} - -void GrabPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_GRAB_GRABBABLE, getGrabbable()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_KINEMATIC, getGrabKinematic()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, getGrabFollowsController()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, getTriggerable()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, getEquippable()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, getGrabDelegateToParent()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, getEquippableLeftPosition()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, getEquippableLeftRotation()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, getEquippableRightPosition()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, getEquippableRightRotation()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, getEquippableIndicatorURL()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, getEquippableIndicatorScale()); - APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, getEquippableIndicatorOffset()); -} - -int GrabPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_GRAB_GRABBABLE, bool, setGrabbable); - READ_ENTITY_PROPERTY(PROP_GRAB_KINEMATIC, bool, setGrabKinematic); - READ_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, bool, setGrabFollowsController); - READ_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, bool, setTriggerable); - READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, bool, setEquippable); - READ_ENTITY_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, bool, setGrabDelegateToParent); - READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableLeftPosition); - READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableLeftRotation); - READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableRightPosition); - READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableRightRotation); - READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, QString, setEquippableIndicatorURL); - READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, glm::vec3, setEquippableIndicatorScale); - READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, glm::vec3, setEquippableIndicatorOffset); - - return bytesRead; -} diff --git a/libraries/entities/src/GrabPropertyGroup.cpp.in b/libraries/entities/src/GrabPropertyGroup.cpp.in new file mode 100644 index 0000000000..290e70cd14 --- /dev/null +++ b/libraries/entities/src/GrabPropertyGroup.cpp.in @@ -0,0 +1,140 @@ +// +// GrabPropertyGroup.h +// libraries/entities/src +// +// Created by Seth Alves on 2018-8-8. +// Copyright 2018 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "GrabPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" +#include "EntityItemPropertiesMacros.h" + +void GrabPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, + ScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const { + auto nodeList = DependencyManager::get(); + +@Grab_GROUP_COPY_TO_SCRIPT@ + +} + +void GrabPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet& namesSet, bool& _defaultSettings) { + +@Grab_GROUP_COPY_FROM_SCRIPT@ + +} + +void GrabPropertyGroup::merge(const GrabPropertyGroup& other) { + +@Grab_GROUP_MERGE@ + +} + +void GrabPropertyGroup::debugDump() const { + +@Grab_GROUP_DEBUG_DUMP@ + +} + +void GrabPropertyGroup::listChangedProperties(QList& out) { + +@Grab_GROUP_LIST_CHANGED@ + +} + +bool GrabPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Grab_GROUP_APPEND@ + + return successPropertyFits; +} + +bool GrabPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + +@Grab_GROUP_READ@ + +@Grab_GROUP_DECODE_CHANGED@ + + processedBytes += bytesRead; + + return somethingChanged; +} + +void GrabPropertyGroup::markAllChanged() { + +@Grab_GROUP_MARK_CHANGED@ + +} + +EntityPropertyFlags GrabPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + +@Grab_GROUP_CHANGED_PROPERTIES@ + + return changedProperties; +} + +void GrabPropertyGroup::getProperties(EntityItemProperties& properties) const { + +@Grab_GROUP_COPY_TO@ + +} + +bool GrabPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Grab_GROUP_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags GrabPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + +@Grab_REQUESTED_PROPS@ + + return requestedProperties; +} + +int GrabPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Grab_GROUP_READ@ + + return bytesRead; +} + + +void GrabPropertyGroup::addPropertyMap(QHash& _propertyInfos, + QHash& _enumsToPropertyStrings) { + +@Grab_GROUP_ADD_TO_MAP@ + +} diff --git a/libraries/entities/src/GrabPropertyGroup.h b/libraries/entities/src/GrabPropertyGroup.h deleted file mode 100644 index 23211bde21..0000000000 --- a/libraries/entities/src/GrabPropertyGroup.h +++ /dev/null @@ -1,145 +0,0 @@ -// -// GrabPropertyGroup.h -// libraries/entities/src -// -// Created by Seth Alves on 2018-8-8. -// Copyright 2018 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#ifndef hifi_GrabPropertyGroup_h -#define hifi_GrabPropertyGroup_h - -#include - -#include - -#include "PropertyGroup.h" -#include "EntityItemPropertiesMacros.h" - -class EntityItemProperties; -class EncodeBitstreamParams; -class OctreePacketData; -class ReadBitstreamToTreeParams; -class ScriptValue; - -static const bool INITIAL_GRABBABLE { true }; -static const bool INITIAL_KINEMATIC { true }; -static const bool INITIAL_FOLLOWS_CONTROLLER { true }; -static const bool INITIAL_TRIGGERABLE { false }; -static const bool INITIAL_EQUIPPABLE { false }; -static const bool INITIAL_GRAB_DELEGATE_TO_PARENT { true }; -static const glm::vec3 INITIAL_LEFT_EQUIPPABLE_POSITION { glm::vec3(0.0f) }; -static const glm::quat INITIAL_LEFT_EQUIPPABLE_ROTATION { glm::quat() }; -static const glm::vec3 INITIAL_RIGHT_EQUIPPABLE_POSITION { glm::vec3(0.0f) }; -static const glm::quat INITIAL_RIGHT_EQUIPPABLE_ROTATION { glm::quat() }; -static const glm::vec3 INITIAL_EQUIPPABLE_INDICATOR_SCALE { glm::vec3(1.0f) }; -static const glm::vec3 INITIAL_EQUIPPABLE_INDICATOR_OFFSET { glm::vec3(0.0f) }; - - -/*@jsdoc - * Grabbing behavior is defined by the following properties: - * - * @typedef {object} Entities.Grab - * @property {boolean} grabbable=true - true if the entity can be grabbed, false if it can't be. - * @property {boolean} grabKinematic=true - true if the entity will be updated in a kinematic manner when - * grabbed; false if it will be grabbed using a tractor action. A kinematic grab will make the item appear - * more tightly held but will cause it to behave poorly when interacting with dynamic entities. - * @property {boolean} grabFollowsController=true - true if the entity will follow the motions of the hand - * controller even if the avatar's hand can't get to the implied position, false if it will follow the motions - * of the avatar's hand. This should be set true for tools, pens, etc. and false for things meant -* to decorate the hand. - * @property {boolean} triggerable=false - true if the entity will receive calls to trigger - * {@link Controller|Controller entity methods}, false if it won't. - * @property {boolean} grabDelegateToParent=true - true if when the entity is grabbed, the grab will be - * transferred to its parent entity if there is one; false if the grab won't be transferred, so a child entity - * can be grabbed and moved relative to its parent. - * @property {boolean} equippable=true - true if the entity can be equipped, false if it cannot. - * @property {Vec3} equippableLeftPosition=0,0,0 - Positional offset from the left hand, when equipped. - * @property {Quat} equippableLeftRotation=0,0,0,1 - Rotational offset from the left hand, when equipped. - * @property {Vec3} equippableRightPosition=0,0,0 - Positional offset from the right hand, when equipped. - * @property {Quat} equippableRightRotation=0,0,0,1 - Rotational offset from the right hand, when equipped. - * @property {string} equippableIndicatorURL="" - If non-empty, this model will be used to indicate that an - * entity is equippable, rather than the default. - * @property {Vec3} equippableIndicatorScale=1,1,1 - If equippableIndicatorURL is non-empty, this controls the - scale of the displayed indicator. - * @property {Vec3} equippableIndicatorOffset=0,0,0 - If equippableIndicatorURL is non-empty, this controls the - relative offset of the displayed object from the equippable entity. - */ -class GrabPropertyGroup : public PropertyGroup { -public: - // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const override; - virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; - - void merge(const GrabPropertyGroup& other); - - virtual void debugDump() const override; - virtual void listChangedProperties(QList& out) override; - - virtual bool appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt, int& processedBytes) override; - virtual void markAllChanged() override; - virtual EntityPropertyFlags getChangedProperties() const override; - - // EntityItem related helpers - // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const override; - - // returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - // grab properties - DEFINE_PROPERTY(PROP_GRAB_GRABBABLE, Grabbable, grabbable, bool, INITIAL_GRABBABLE); - DEFINE_PROPERTY(PROP_GRAB_KINEMATIC, GrabKinematic, grabKinematic, bool, INITIAL_KINEMATIC); - DEFINE_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, GrabFollowsController, grabFollowsController, bool, - INITIAL_FOLLOWS_CONTROLLER); - DEFINE_PROPERTY(PROP_GRAB_TRIGGERABLE, Triggerable, triggerable, bool, INITIAL_TRIGGERABLE); - DEFINE_PROPERTY(PROP_GRAB_EQUIPPABLE, Equippable, equippable, bool, INITIAL_EQUIPPABLE); - DEFINE_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, GrabDelegateToParent, grabDelegateToParent, bool, - INITIAL_GRAB_DELEGATE_TO_PARENT); - DEFINE_PROPERTY_REF(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, EquippableLeftPosition, equippableLeftPosition, - glm::vec3, INITIAL_LEFT_EQUIPPABLE_POSITION); - DEFINE_PROPERTY_REF(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, EquippableLeftRotation, equippableLeftRotation, - glm::quat, INITIAL_LEFT_EQUIPPABLE_ROTATION); - DEFINE_PROPERTY_REF(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, EquippableRightPosition, equippableRightPosition, - glm::vec3, INITIAL_RIGHT_EQUIPPABLE_POSITION); - DEFINE_PROPERTY_REF(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, EquippableRightRotation, equippableRightRotation, - glm::quat, INITIAL_RIGHT_EQUIPPABLE_ROTATION); - DEFINE_PROPERTY_REF(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, EquippableIndicatorURL, equippableIndicatorURL, QString, ""); - DEFINE_PROPERTY_REF(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, EquippableIndicatorScale, equippableIndicatorScale, - glm::vec3, INITIAL_EQUIPPABLE_INDICATOR_SCALE); - DEFINE_PROPERTY_REF(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, EquippableIndicatorOffset, equippableIndicatorOffset, - glm::vec3, INITIAL_EQUIPPABLE_INDICATOR_OFFSET); -}; - -#endif // hifi_GrabPropertyGroup_h diff --git a/libraries/entities/src/GrabPropertyGroup.h.in b/libraries/entities/src/GrabPropertyGroup.h.in new file mode 100644 index 0000000000..1a0382da26 --- /dev/null +++ b/libraries/entities/src/GrabPropertyGroup.h.in @@ -0,0 +1,83 @@ +// +// GrabPropertyGroup.h +// libraries/entities/src +// +// Created by Seth Alves on 2018-8-8. +// Copyright 2018 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_GrabPropertyGroup_h +#define hifi_GrabPropertyGroup_h + +#include + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class ReadBitstreamToTreeParams; +class ScriptValue; + +static const bool INITIAL_GRABBABLE { true }; +static const bool INITIAL_KINEMATIC { true }; +static const bool INITIAL_FOLLOWS_CONTROLLER { true }; +static const bool INITIAL_TRIGGERABLE { false }; +static const bool INITIAL_EQUIPPABLE { false }; +static const bool INITIAL_GRAB_DELEGATE_TO_PARENT { true }; +static const glm::vec3 INITIAL_LEFT_EQUIPPABLE_POSITION { glm::vec3(0.0f) }; +static const glm::quat INITIAL_LEFT_EQUIPPABLE_ROTATION { glm::quat() }; +static const glm::vec3 INITIAL_RIGHT_EQUIPPABLE_POSITION { glm::vec3(0.0f) }; +static const glm::quat INITIAL_RIGHT_EQUIPPABLE_ROTATION { glm::quat() }; +static const glm::vec3 INITIAL_EQUIPPABLE_INDICATOR_SCALE { glm::vec3(1.0f) }; +static const glm::vec3 INITIAL_EQUIPPABLE_INDICATOR_OFFSET { glm::vec3(0.0f) }; + + +/*@jsdoc + * Grabbing behavior is defined by the following properties: + * + * @typedef {object} Entities.Grab + * @property {boolean} grabbable=true - true if the entity can be grabbed, false if it can't be. + * @property {boolean} grabKinematic=true - true if the entity will be updated in a kinematic manner when + * grabbed; false if it will be grabbed using a tractor action. A kinematic grab will make the item appear + * more tightly held but will cause it to behave poorly when interacting with dynamic entities. + * @property {boolean} grabFollowsController=true - true if the entity will follow the motions of the hand + * controller even if the avatar's hand can't get to the implied position, false if it will follow the motions + * of the avatar's hand. This should be set true for tools, pens, etc. and false for things meant +* to decorate the hand. + * @property {boolean} triggerable=false - true if the entity will receive calls to trigger + * {@link Controller|Controller entity methods}, false if it won't. + * @property {boolean} grabDelegateToParent=true - true if when the entity is grabbed, the grab will be + * transferred to its parent entity if there is one; false if the grab won't be transferred, so a child entity + * can be grabbed and moved relative to its parent. + * @property {boolean} equippable=true - true if the entity can be equipped, false if it cannot. + * @property {Vec3} equippableLeftPosition=0,0,0 - Positional offset from the left hand, when equipped. + * @property {Quat} equippableLeftRotation=0,0,0,1 - Rotational offset from the left hand, when equipped. + * @property {Vec3} equippableRightPosition=0,0,0 - Positional offset from the right hand, when equipped. + * @property {Quat} equippableRightRotation=0,0,0,1 - Rotational offset from the right hand, when equipped. + * @property {string} equippableIndicatorURL="" - If non-empty, this model will be used to indicate that an + * entity is equippable, rather than the default. + * @property {Vec3} equippableIndicatorScale=1,1,1 - If equippableIndicatorURL is non-empty, this controls the + scale of the displayed indicator. + * @property {Vec3} equippableIndicatorOffset=0,0,0 - If equippableIndicatorURL is non-empty, this controls the + relative offset of the displayed object from the equippable entity. + */ +class GrabPropertyGroup : public PropertyGroup { +public: + ENTITY_PROPERTY_GROUP_METHODS(GrabPropertyGroup) + +protected: + +@Grab_GROUP_PROPS@ + +}; + +#endif // hifi_GrabPropertyGroup_h diff --git a/libraries/entities/src/GridEntityItem.cpp b/libraries/entities/src/GridEntityItem.cpp deleted file mode 100644 index 5e749652cc..0000000000 --- a/libraries/entities/src/GridEntityItem.cpp +++ /dev/null @@ -1,203 +0,0 @@ -// -// Created by Sam Gondelman on 11/29/18 -// Copyright 2018 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "GridEntityItem.h" - -#include "EntityItemProperties.h" - -const uint32_t GridEntityItem::DEFAULT_MAJOR_GRID_EVERY = 5; -const float GridEntityItem::DEFAULT_MINOR_GRID_EVERY = 1.0f; - -EntityItemPointer GridEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - Pointer entity(new GridEntityItem(entityID), [](GridEntityItem* ptr) { ptr->deleteLater(); }); - entity->setProperties(properties); - return entity; -} - -// our non-pure virtual subclass for now... -GridEntityItem::GridEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { - _type = EntityTypes::Grid; -} - -void GridEntityItem::setUnscaledDimensions(const glm::vec3& value) { - const float GRID_ENTITY_ITEM_FIXED_DEPTH = 0.01f; - // NOTE: Grid Entities always have a "depth" of 1cm. - EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, GRID_ENTITY_ITEM_FIXED_DEPTH)); -} - -EntityItemProperties GridEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { - EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); - withReadLock([&] { - _pulseProperties.getProperties(properties); - }); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(followCamera, getFollowCamera); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(majorGridEvery, getMajorGridEvery); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(minorGridEvery, getMinorGridEvery); - - return properties; -} - -bool GridEntityItem::setSubClassProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha); - withWriteLock([&] { - bool pulsePropertiesChanged = _pulseProperties.setProperties(properties); - somethingChanged |= pulsePropertiesChanged; - _needsRenderUpdate |= pulsePropertiesChanged; - }); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(followCamera, setFollowCamera); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(majorGridEvery, setMajorGridEvery); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(minorGridEvery, setMinorGridEvery); - - return somethingChanged; -} - -int GridEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); - withWriteLock([&] { - int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, - somethingChanged); - bytesRead += bytesFromPulse; - dataAt += bytesFromPulse; - }); - - READ_ENTITY_PROPERTY(PROP_GRID_FOLLOW_CAMERA, bool, setFollowCamera); - READ_ENTITY_PROPERTY(PROP_MAJOR_GRID_EVERY, uint32_t, setMajorGridEvery); - READ_ENTITY_PROPERTY(PROP_MINOR_GRID_EVERY, float, setMinorGridEvery); - - return bytesRead; -} - -EntityPropertyFlags GridEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - - requestedProperties += PROP_COLOR; - requestedProperties += PROP_ALPHA; - requestedProperties += _pulseProperties.getEntityProperties(params); - - requestedProperties += PROP_GRID_FOLLOW_CAMERA; - requestedProperties += PROP_MAJOR_GRID_EVERY; - requestedProperties += PROP_MINOR_GRID_EVERY; - - return requestedProperties; -} - -void GridEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); - withReadLock([&] { - _pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - }); - - APPEND_ENTITY_PROPERTY(PROP_GRID_FOLLOW_CAMERA, getFollowCamera()); - APPEND_ENTITY_PROPERTY(PROP_MAJOR_GRID_EVERY, getMajorGridEvery()); - APPEND_ENTITY_PROPERTY(PROP_MINOR_GRID_EVERY, getMinorGridEvery()); -} - -void GridEntityItem::setColor(const glm::u8vec3& color) { - withWriteLock([&] { - _needsRenderUpdate |= _color != color; - _color = color; - }); -} - -glm::u8vec3 GridEntityItem::getColor() const { - return resultWithReadLock([&] { - return _color; - }); -} - -void GridEntityItem::setAlpha(float alpha) { - withWriteLock([&] { - _needsRenderUpdate |= _alpha != alpha; - _alpha = alpha; - }); -} - -float GridEntityItem::getAlpha() const { - return resultWithReadLock([&] { - return _alpha; - }); -} - -void GridEntityItem::setFollowCamera(bool followCamera) { - withWriteLock([&] { - _needsRenderUpdate |= _followCamera != followCamera; - _followCamera = followCamera; - }); -} - -bool GridEntityItem::getFollowCamera() const { - return resultWithReadLock([&] { - return _followCamera; - }); -} - -void GridEntityItem::setMajorGridEvery(uint32_t majorGridEvery) { - const uint32_t MAJOR_GRID_EVERY_MIN = 1; - majorGridEvery = std::max(majorGridEvery, MAJOR_GRID_EVERY_MIN); - - withWriteLock([&] { - _needsRenderUpdate |= _majorGridEvery != majorGridEvery; - _majorGridEvery = majorGridEvery; - }); -} - -uint32_t GridEntityItem::getMajorGridEvery() const { - return resultWithReadLock([&] { - return _majorGridEvery; - }); -} - -void GridEntityItem::setMinorGridEvery(float minorGridEvery) { - const float MINOR_GRID_EVERY_MIN = 0.01f; - minorGridEvery = std::max(minorGridEvery, MINOR_GRID_EVERY_MIN); - - withWriteLock([&] { - _needsRenderUpdate |= _minorGridEvery != minorGridEvery; - _minorGridEvery = minorGridEvery; - }); -} - -float GridEntityItem::getMinorGridEvery() const { - return resultWithReadLock([&] { - return _minorGridEvery; - }); -} - -PulsePropertyGroup GridEntityItem::getPulseProperties() const { - return resultWithReadLock([&] { - return _pulseProperties; - }); -} \ No newline at end of file diff --git a/libraries/entities/src/GridEntityItem.cpp.in b/libraries/entities/src/GridEntityItem.cpp.in new file mode 100644 index 0000000000..a1e83092fa --- /dev/null +++ b/libraries/entities/src/GridEntityItem.cpp.in @@ -0,0 +1,132 @@ +// +// Created by Sam Gondelman on 11/29/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "GridEntityItem.h" + +#include "EntityItemProperties.h" + +const uint32_t GridEntityItem::DEFAULT_MAJOR_GRID_EVERY = 5; +const float GridEntityItem::DEFAULT_MINOR_GRID_EVERY = 1.0f; + +EntityItemPointer GridEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + Pointer entity(new GridEntityItem(entityID), [](GridEntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +// our non-pure virtual subclass for now... +GridEntityItem::GridEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { + _type = EntityTypes::Grid; +} + +void GridEntityItem::setUnscaledDimensions(const glm::vec3& value) { + const float GRID_ENTITY_ITEM_FIXED_DEPTH = 0.01f; + // NOTE: Grid Entities always have a "depth" of 1cm. + EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, GRID_ENTITY_ITEM_FIXED_DEPTH)); +} + +EntityItemProperties GridEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + +@Grid_ENTITY_COPY_TO@ + + return properties; +} + +bool GridEntityItem::setSubClassProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Grid_ENTITY_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags GridEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + +@Grid_REQUESTED_PROPS@ + + return requestedProperties; +} + +void GridEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Grid_ENTITY_APPEND@ + +} + +int GridEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Grid_ENTITY_READ@ + + return bytesRead; +} + +void GridEntityItem::debugDump() const { + qCDebug(entities) << "GridEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@Grid_ENTITY_DEBUG@ + +} + +void GridEntityItem::setMajorGridEvery(uint32_t majorGridEvery) { + const uint32_t MAJOR_GRID_EVERY_MIN = 1; + majorGridEvery = std::max(majorGridEvery, MAJOR_GRID_EVERY_MIN); + + withWriteLock([&] { + _needsRenderUpdate |= _majorGridEvery != majorGridEvery; + _majorGridEvery = majorGridEvery; + }); +} + +uint32_t GridEntityItem::getMajorGridEvery() const { + return resultWithReadLock([&] { + return _majorGridEvery; + }); +} + +void GridEntityItem::setMinorGridEvery(float minorGridEvery) { + const float MINOR_GRID_EVERY_MIN = 0.01f; + minorGridEvery = std::max(minorGridEvery, MINOR_GRID_EVERY_MIN); + + withWriteLock([&] { + _needsRenderUpdate |= _minorGridEvery != minorGridEvery; + _minorGridEvery = minorGridEvery; + }); +} + +float GridEntityItem::getMinorGridEvery() const { + return resultWithReadLock([&] { + return _minorGridEvery; + }); +} + +PulsePropertyGroup GridEntityItem::getPulseProperties() const { + return resultWithReadLock([&] { + return _pulseProperties; + }); +} \ No newline at end of file diff --git a/libraries/entities/src/GridEntityItem.h b/libraries/entities/src/GridEntityItem.h deleted file mode 100644 index 7dc7a475b2..0000000000 --- a/libraries/entities/src/GridEntityItem.h +++ /dev/null @@ -1,77 +0,0 @@ -// -// Created by Sam Gondelman on 11/29/18 -// Copyright 2018 High Fidelity, Inc. -// -// 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_GridEntityItem_h -#define hifi_GridEntityItem_h - -#include "EntityItem.h" - -#include "PulsePropertyGroup.h" - -class GridEntityItem : public EntityItem { - using Pointer = std::shared_ptr; -public: - static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - - GridEntityItem(const EntityItemID& entityItemID); - - ALLOW_INSTANTIATION // This class can be instantiated - - virtual void setUnscaledDimensions(const glm::vec3& value) override; - - // methods for getting/setting all properties of an entity - EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - bool setSubClassProperties(const EntityItemProperties& properties) override; - - EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - static const uint32_t DEFAULT_MAJOR_GRID_EVERY; - static const float DEFAULT_MINOR_GRID_EVERY; - - void setColor(const glm::u8vec3& color); - glm::u8vec3 getColor() const; - - void setAlpha(float alpha); - float getAlpha() const; - - void setFollowCamera(bool followCamera); - bool getFollowCamera() const; - - void setMajorGridEvery(uint32_t majorGridEvery); - uint32_t getMajorGridEvery() const; - - void setMinorGridEvery(float minorGridEvery); - float getMinorGridEvery() const; - - PulsePropertyGroup getPulseProperties() const; - -protected: - glm::u8vec3 _color; - float _alpha; - PulsePropertyGroup _pulseProperties; - - bool _followCamera { true }; - uint32_t _majorGridEvery { DEFAULT_MAJOR_GRID_EVERY }; - float _minorGridEvery { DEFAULT_MINOR_GRID_EVERY }; - -}; - -#endif // hifi_GridEntityItem_h diff --git a/libraries/entities/src/GridEntityItem.h.in b/libraries/entities/src/GridEntityItem.h.in new file mode 100644 index 0000000000..bbd7b2a6ff --- /dev/null +++ b/libraries/entities/src/GridEntityItem.h.in @@ -0,0 +1,39 @@ +// +// Created by Sam Gondelman on 11/29/18 +// Copyright 2018 High Fidelity, Inc. +// +// 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_GridEntityItem_h +#define hifi_GridEntityItem_h + +#include "EntityItem.h" + +#include "PulsePropertyGroup.h" + +class GridEntityItem : public EntityItem { + using Pointer = std::shared_ptr; +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + GridEntityItem(const EntityItemID& entityItemID); + + ALLOW_INSTANTIATION // This class can be instantiated + ENTITY_PROPERTY_SUBCLASS_METHODS + + virtual void setUnscaledDimensions(const glm::vec3& value) override; + + static const uint32_t DEFAULT_MAJOR_GRID_EVERY; + static const float DEFAULT_MINOR_GRID_EVERY; + + PulsePropertyGroup getPulseProperties() const; + +protected: + +@Grid_ENTITY_PROPS@ + +}; + +#endif // hifi_GridEntityItem_h diff --git a/libraries/entities/src/HazePropertyGroup.cpp b/libraries/entities/src/HazePropertyGroup.cpp deleted file mode 100644 index fd091de8ac..0000000000 --- a/libraries/entities/src/HazePropertyGroup.cpp +++ /dev/null @@ -1,366 +0,0 @@ -// -// HazePropertyGroup.h -// libraries/entities/src -// -// Created by Nissim hadar on 9/21/17. -// Copyright 2013 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#include "HazePropertyGroup.h" - -#include - -#include "EntityItemProperties.h" -#include "EntityItemPropertiesMacros.h" - -void HazePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, - bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_RANGE, Haze, haze, HazeRange, hazeRange); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_HAZE_COLOR, Haze, haze, HazeColor, hazeColor, u8vec3Color); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_HAZE_GLARE_COLOR, Haze, haze, HazeGlareColor, hazeGlareColor, u8vec3Color); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_ENABLE_GLARE, Haze, haze, HazeEnableGlare, hazeEnableGlare); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_GLARE_ANGLE, Haze, haze, HazeGlareAngle, hazeGlareAngle); - - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_ALTITUDE_EFFECT, Haze, haze, HazeAltitudeEffect, hazeAltitudeEffect); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_CEILING, Haze, haze, HazeCeiling, hazeCeiling); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_BASE_REF, Haze, haze, HazeBaseRef, hazeBaseRef); - - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_BACKGROUND_BLEND, Haze, haze, HazeBackgroundBlend, hazeBackgroundBlend); - - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_ATTENUATE_KEYLIGHT, Haze, haze, HazeAttenuateKeyLight, hazeAttenuateKeyLight); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_KEYLIGHT_RANGE, Haze, haze, HazeKeyLightRange, hazeKeyLightRange); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_KEYLIGHT_ALTITUDE, Haze, haze, HazeKeyLightAltitude, hazeKeyLightAltitude); -} - -void HazePropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeRange, float, setHazeRange); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeColor, u8vec3Color, setHazeColor); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeGlareColor, u8vec3Color, setHazeGlareColor); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeEnableGlare, bool, setHazeEnableGlare); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeGlareAngle, float, setHazeGlareAngle); - - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeAltitudeEffect, bool, setHazeAltitudeEffect); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeCeiling, float, setHazeCeiling); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeBaseRef, float, setHazeBaseRef); - - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeBackgroundBlend, float, setHazeBackgroundBlend); - - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeAttenuateKeyLight, bool, setHazeAttenuateKeyLight); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeKeyLightRange, float, setHazeKeyLightRange); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeKeyLightAltitude, float, setHazeKeyLightAltitude); -} - -void HazePropertyGroup::merge(const HazePropertyGroup& other) { - COPY_PROPERTY_IF_CHANGED(hazeRange); - COPY_PROPERTY_IF_CHANGED(hazeColor); - COPY_PROPERTY_IF_CHANGED(hazeGlareColor); - COPY_PROPERTY_IF_CHANGED(hazeEnableGlare); - COPY_PROPERTY_IF_CHANGED(hazeGlareAngle); - - COPY_PROPERTY_IF_CHANGED(hazeAltitudeEffect); - COPY_PROPERTY_IF_CHANGED(hazeCeiling); - COPY_PROPERTY_IF_CHANGED(hazeBaseRef); - - COPY_PROPERTY_IF_CHANGED(hazeBackgroundBlend); - - COPY_PROPERTY_IF_CHANGED(hazeAttenuateKeyLight); - COPY_PROPERTY_IF_CHANGED(hazeKeyLightRange); - COPY_PROPERTY_IF_CHANGED(hazeKeyLightAltitude); -} - -void HazePropertyGroup::debugDump() const { - qCDebug(entities) << " HazePropertyGroup: ---------------------------------------------"; - - qCDebug(entities) << " _hazeRange:" << _hazeRange; - qCDebug(entities) << " _hazeColor:" << _hazeColor; - qCDebug(entities) << " _hazeGlareColor:" << _hazeGlareColor; - qCDebug(entities) << " _hazeEnableGlare:" << _hazeEnableGlare; - qCDebug(entities) << " _hazeGlareAngle:" << _hazeGlareAngle; - - qCDebug(entities) << " _hazeAltitudeEffect:" << _hazeAltitudeEffect; - qCDebug(entities) << " _hazeCeiling:" << _hazeCeiling; - qCDebug(entities) << " _hazeBaseRef:" << _hazeBaseRef; - - qCDebug(entities) << " _hazeBackgroundBlend:" << _hazeBackgroundBlend; - - qCDebug(entities) << " _hazeAttenuateKeyLight:" << _hazeAttenuateKeyLight; - qCDebug(entities) << " _hazeKeyLightRange:" << _hazeKeyLightRange; - qCDebug(entities) << " _hazeKeyLightAltitude:" << _hazeKeyLightAltitude; -} - -void HazePropertyGroup::listChangedProperties(QList& out) { - if (hazeRangeChanged()) { - out << "haze-hazeRange"; - } - if (hazeColorChanged()) { - out << "haze-hazeColor"; - } - if (hazeGlareColorChanged()) { - out << "haze-hazeGlareColor"; - } - if (hazeEnableGlareChanged()) { - out << "haze-hazeEnableGlare"; - } - if (hazeGlareAngleChanged()) { - out << "haze-hazeGlareAngle"; - } - - if (hazeAltitudeEffectChanged()) { - out << "haze-hazeAltitudeEffect"; - } - if (hazeCeilingChanged()) { - out << "haze-hazeCeiling"; - } - if (hazeBaseRefChanged()) { - out << "haze-hazeBaseRef"; - } - - if (hazeBackgroundBlendChanged()) { - out << "haze-hazeBackgroundBlend"; - } - - if (hazeAttenuateKeyLightChanged()) { - out << "haze-hazeAttenuateKeyLight"; - } - if (hazeKeyLightRangeChanged()) { - out << "haze-hazeKeyLightRange"; - } - if (hazeKeyLightAltitudeChanged()) { - out << "haze-hazeKeyLightAltitude"; - } -} - -bool HazePropertyGroup::appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_HAZE_RANGE, getHazeRange()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_COLOR, getHazeColor()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_GLARE_COLOR, getHazeGlareColor()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_ENABLE_GLARE, getHazeEnableGlare()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_GLARE_ANGLE, getHazeGlareAngle()); - - APPEND_ENTITY_PROPERTY(PROP_HAZE_ALTITUDE_EFFECT, getHazeAltitudeEffect()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_CEILING, getHazeCeiling()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_BASE_REF, getHazeBaseRef()); - - APPEND_ENTITY_PROPERTY(PROP_HAZE_BACKGROUND_BLEND, getHazeBackgroundBlend()); - - APPEND_ENTITY_PROPERTY(PROP_HAZE_ATTENUATE_KEYLIGHT, getHazeAttenuateKeyLight()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_KEYLIGHT_RANGE, getHazeKeyLightRange()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_KEYLIGHT_ALTITUDE, getHazeKeyLightAltitude()); - - return true; -} - -bool HazePropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { - - int bytesRead = 0; - bool overwriteLocalData = true; - bool somethingChanged = false; - - READ_ENTITY_PROPERTY(PROP_HAZE_RANGE, float, setHazeRange); - READ_ENTITY_PROPERTY(PROP_HAZE_COLOR, u8vec3Color, setHazeColor); - READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_COLOR, u8vec3Color, setHazeGlareColor); - READ_ENTITY_PROPERTY(PROP_HAZE_ENABLE_GLARE, bool, setHazeEnableGlare); - READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_ANGLE, float, setHazeGlareAngle); - - READ_ENTITY_PROPERTY(PROP_HAZE_ALTITUDE_EFFECT, bool, setHazeAltitudeEffect); - READ_ENTITY_PROPERTY(PROP_HAZE_CEILING, float, setHazeCeiling); - READ_ENTITY_PROPERTY(PROP_HAZE_BASE_REF, float, setHazeBaseRef); - - READ_ENTITY_PROPERTY(PROP_HAZE_BACKGROUND_BLEND, float, setHazeBackgroundBlend); - - READ_ENTITY_PROPERTY(PROP_HAZE_ATTENUATE_KEYLIGHT, bool, setHazeAttenuateKeyLight); - READ_ENTITY_PROPERTY(PROP_HAZE_KEYLIGHT_RANGE, float, setHazeKeyLightRange); - READ_ENTITY_PROPERTY(PROP_HAZE_KEYLIGHT_ALTITUDE, float, setHazeKeyLightAltitude); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HAZE_RANGE, HazeRange); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HAZE_COLOR, HazeColor); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HAZE_GLARE_COLOR, HazeGlareColor); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HAZE_ENABLE_GLARE, HazeEnableGlare); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HAZE_GLARE_ANGLE, HazeGlareAngle); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HAZE_ALTITUDE_EFFECT, HazeAltitudeEffect); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HAZE_CEILING, HazeCeiling); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HAZE_BASE_REF, HazeBaseRef); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HAZE_BACKGROUND_BLEND, HazeBackgroundBlend); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HAZE_ATTENUATE_KEYLIGHT, HazeAttenuateKeyLight); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HAZE_KEYLIGHT_RANGE, HazeKeyLightRange); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HAZE_KEYLIGHT_ALTITUDE, HazeKeyLightAltitude); - - processedBytes += bytesRead; - - Q_UNUSED(somethingChanged); - - return true; -} - -void HazePropertyGroup::markAllChanged() { - _hazeRangeChanged = true; - _hazeColorChanged = true; - _hazeGlareColorChanged = true; - _hazeEnableGlareChanged = true; - _hazeGlareAngleChanged = true; - - _hazeAltitudeEffectChanged = true; - _hazeCeilingChanged = true; - _hazeBaseRefChanged = true; - - _hazeBackgroundBlendChanged = true; - - _hazeAttenuateKeyLightChanged = true; - _hazeKeyLightRangeChanged = true; - _hazeKeyLightAltitudeChanged = true; -} - -EntityPropertyFlags HazePropertyGroup::getChangedProperties() const { - EntityPropertyFlags changedProperties; - - CHECK_PROPERTY_CHANGE(PROP_HAZE_RANGE, hazeRange); - CHECK_PROPERTY_CHANGE(PROP_HAZE_COLOR, hazeColor); - CHECK_PROPERTY_CHANGE(PROP_HAZE_GLARE_COLOR, hazeGlareColor); - CHECK_PROPERTY_CHANGE(PROP_HAZE_ENABLE_GLARE, hazeEnableGlare); - CHECK_PROPERTY_CHANGE(PROP_HAZE_GLARE_ANGLE, hazeGlareAngle); - - CHECK_PROPERTY_CHANGE(PROP_HAZE_ALTITUDE_EFFECT, hazeAltitudeEffect); - CHECK_PROPERTY_CHANGE(PROP_HAZE_CEILING, hazeCeiling); - CHECK_PROPERTY_CHANGE(PROP_HAZE_BASE_REF, hazeBaseRef); - - CHECK_PROPERTY_CHANGE(PROP_HAZE_BACKGROUND_BLEND, hazeBackgroundBlend); - - CHECK_PROPERTY_CHANGE(PROP_HAZE_ATTENUATE_KEYLIGHT, hazeAttenuateKeyLight); - CHECK_PROPERTY_CHANGE(PROP_HAZE_KEYLIGHT_RANGE, hazeKeyLightRange); - CHECK_PROPERTY_CHANGE(PROP_HAZE_KEYLIGHT_ALTITUDE, hazeKeyLightAltitude); - - return changedProperties; -} - -void HazePropertyGroup::getProperties(EntityItemProperties& properties) const { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Haze, HazeRange, getHazeRange); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Haze, HazeColor, getHazeColor); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Haze, HazeGlareColor, getHazeGlareColor); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Haze, HazeEnableGlare, getHazeEnableGlare); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Haze, HazeGlareAngle, getHazeGlareAngle); - - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Haze, HazeAltitudeEffect, getHazeAltitudeEffect); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Haze, HazeCeiling, getHazeCeiling); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Haze, HazeBaseRef, getHazeBaseRef); - - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Haze, HazeBackgroundBlend, getHazeBackgroundBlend); - - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Haze, HazeAttenuateKeyLight, getHazeAttenuateKeyLight); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Haze, HazeKeyLightRange, getHazeKeyLightRange); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Haze, HazeKeyLightAltitude, getHazeKeyLightAltitude); -} - -bool HazePropertyGroup::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Haze, HazeRange, hazeRange, setHazeRange); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Haze, HazeColor, hazeColor, setHazeColor); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Haze, HazeGlareColor, hazeGlareColor, setHazeGlareColor); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Haze, HazeEnableGlare, hazeEnableGlare, setHazeEnableGlare); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Haze, HazeGlareAngle, hazeGlareAngle, setHazeGlareAngle); - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Haze, HazeAltitudeEffect, hazeAltitudeEffect, setHazeAltitudeEffect); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Haze, HazeCeiling, hazeCeiling, setHazeCeiling); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Haze, HazeBaseRef, hazeBaseRef, setHazeBaseRef); - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Haze, HazeBackgroundBlend, hazeBackgroundBlend, setHazeBackgroundBlend); - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Haze, HazeAttenuateKeyLight, hazeAttenuateKeyLight, setHazeAttenuateKeyLight); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Haze, HazeKeyLightRange, hazeKeyLightRange, setHazeKeyLightRange); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Haze, HazeKeyLightAltitude, hazeKeyLightAltitude, setHazeKeyLightAltitude); - - return somethingChanged; -} - -EntityPropertyFlags HazePropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties; - - requestedProperties += PROP_HAZE_RANGE; - requestedProperties += PROP_HAZE_COLOR; - requestedProperties += PROP_HAZE_GLARE_COLOR; - requestedProperties += PROP_HAZE_ENABLE_GLARE; - requestedProperties += PROP_HAZE_GLARE_ANGLE; - - requestedProperties += PROP_HAZE_ALTITUDE_EFFECT; - requestedProperties += PROP_HAZE_CEILING; - requestedProperties += PROP_HAZE_BASE_REF; - - requestedProperties += PROP_HAZE_BACKGROUND_BLEND; - - requestedProperties += PROP_HAZE_ATTENUATE_KEYLIGHT; - requestedProperties += PROP_HAZE_KEYLIGHT_RANGE; - requestedProperties += PROP_HAZE_KEYLIGHT_ALTITUDE; - - return requestedProperties; -} - -void HazePropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_HAZE_RANGE, getHazeRange()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_COLOR, getHazeColor()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_GLARE_COLOR, getHazeGlareColor()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_ENABLE_GLARE, getHazeEnableGlare()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_GLARE_ANGLE, getHazeGlareAngle()); - - APPEND_ENTITY_PROPERTY(PROP_HAZE_ALTITUDE_EFFECT, getHazeAltitudeEffect()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_CEILING, getHazeCeiling()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_BASE_REF, getHazeBaseRef()); - - APPEND_ENTITY_PROPERTY(PROP_HAZE_BACKGROUND_BLEND, getHazeBackgroundBlend()); - - APPEND_ENTITY_PROPERTY(PROP_HAZE_ATTENUATE_KEYLIGHT, getHazeAttenuateKeyLight()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_KEYLIGHT_RANGE, getHazeKeyLightRange()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_KEYLIGHT_ALTITUDE, getHazeKeyLightAltitude()); -} - -int HazePropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_HAZE_RANGE, float, setHazeRange); - READ_ENTITY_PROPERTY(PROP_HAZE_COLOR, u8vec3Color, setHazeColor); - READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_COLOR, u8vec3Color, setHazeGlareColor); - READ_ENTITY_PROPERTY(PROP_HAZE_ENABLE_GLARE, bool, setHazeEnableGlare); - READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_ANGLE, float, setHazeGlareAngle); - - READ_ENTITY_PROPERTY(PROP_HAZE_ALTITUDE_EFFECT, bool, setHazeAltitudeEffect); - READ_ENTITY_PROPERTY(PROP_HAZE_CEILING, float, setHazeCeiling); - READ_ENTITY_PROPERTY(PROP_HAZE_BASE_REF, float, setHazeBaseRef); - - READ_ENTITY_PROPERTY(PROP_HAZE_BACKGROUND_BLEND, float, setHazeBackgroundBlend); - - READ_ENTITY_PROPERTY(PROP_HAZE_ATTENUATE_KEYLIGHT, bool, setHazeAttenuateKeyLight); - READ_ENTITY_PROPERTY(PROP_HAZE_KEYLIGHT_RANGE, float, setHazeKeyLightRange); - READ_ENTITY_PROPERTY(PROP_HAZE_KEYLIGHT_ALTITUDE, float, setHazeKeyLightAltitude); - - return bytesRead; -} diff --git a/libraries/entities/src/HazePropertyGroup.cpp.in b/libraries/entities/src/HazePropertyGroup.cpp.in new file mode 100644 index 0000000000..882af32a2e --- /dev/null +++ b/libraries/entities/src/HazePropertyGroup.cpp.in @@ -0,0 +1,134 @@ +// +// HazePropertyGroup.h +// libraries/entities/src +// +// Created by Nissim hadar on 9/21/17. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "HazePropertyGroup.h" + +#include + +#include "EntityItemProperties.h" + +void HazePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + +@Haze_GROUP_COPY_TO_SCRIPT@ + +} + +void HazePropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + +@Haze_GROUP_COPY_FROM_SCRIPT@ + +} + +void HazePropertyGroup::merge(const HazePropertyGroup& other) { + +@Haze_GROUP_MERGE@ + +} + +void HazePropertyGroup::debugDump() const { + +@Haze_GROUP_DEBUG_DUMP@ + +} + +void HazePropertyGroup::listChangedProperties(QList& out) { + +@Haze_GROUP_LIST_CHANGED@ + +} + +bool HazePropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Haze_GROUP_APPEND@ + + return successPropertyFits; +} + +bool HazePropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + +@Haze_GROUP_READ@ + +@Haze_GROUP_DECODE_CHANGED@ + + processedBytes += bytesRead; + + return somethingChanged; +} + +void HazePropertyGroup::markAllChanged() { + +@Haze_GROUP_MARK_CHANGED@ + +} + +EntityPropertyFlags HazePropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + +@Haze_GROUP_CHANGED_PROPERTIES@ + + return changedProperties; +} + +void HazePropertyGroup::getProperties(EntityItemProperties& properties) const { + +@Haze_GROUP_COPY_TO@ + +} + +bool HazePropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Haze_GROUP_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags HazePropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + +@Haze_REQUESTED_PROPS@ + + return requestedProperties; +} + +int HazePropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Haze_GROUP_READ@ + + return bytesRead; +} + +void HazePropertyGroup::addPropertyMap(QHash& _propertyInfos, + QHash& _enumsToPropertyStrings) { + +@Haze_GROUP_ADD_TO_MAP@ + +} diff --git a/libraries/entities/src/HazePropertyGroup.h b/libraries/entities/src/HazePropertyGroup.h deleted file mode 100644 index a84ec20713..0000000000 --- a/libraries/entities/src/HazePropertyGroup.h +++ /dev/null @@ -1,147 +0,0 @@ -// -// HazePropertyGroup.h -// libraries/entities/src -// -// Created by Nissim hadar on 9/21/17. -// Copyright 2013 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#ifndef hifi_HazePropertyGroup_h -#define hifi_HazePropertyGroup_h - -#include - -#include - -#include "PropertyGroup.h" -#include "EntityItemPropertiesMacros.h" - -class EntityItemProperties; -class EncodeBitstreamParams; -class OctreePacketData; -class EntityTreeElementExtraEncodeData; -class ReadBitstreamToTreeParams; -class ScriptEngine; -class ScriptValue; - -static const float INITIAL_HAZE_RANGE{ 1000.0f }; -static const glm::u8vec3 initialHazeGlareColor { 255, 229, 179 }; -static const glm::u8vec3 initialHazeColor { 128, 154, 179 }; -static const float INITIAL_HAZE_GLARE_ANGLE{ 20.0f }; - -static const float INITIAL_HAZE_BASE_REFERENCE{ 0.0f }; -static const float INITIAL_HAZE_HEIGHT{ 200.0f }; - -static const float INITIAL_HAZE_BACKGROUND_BLEND{ 0.0f }; - -static const float INITIAL_KEY_LIGHT_RANGE{ 1000.0f }; -static const float INITIAL_KEY_LIGHT_ALTITUDE{ 200.0f }; - -// FIXME: Document hazeAttenuationKeyLight, hazeKeyLightRange, and hazeKeyLightAltitude once they're working and are provided -// in the Create app's UI. -/*@jsdoc - * Haze is defined by the following properties: - * @typedef {object} Entities.Haze - * - * @property {number} hazeRange=1000 - The horizontal distance at which visibility is reduced to 95%; i.e., 95% of each pixel's - * color is haze. - * @property {Color} hazeColor=128,154,179 - The color of the haze when looking away from the key light. - * @property {boolean} hazeEnableGlare=false - true if the haze is colored with glare from the key light, - * false if it isn't. If true, then hazeGlareColor and hazeGlareAngle - * are used. - * @property {Color} hazeGlareColor=255,299,179 - The color of the haze when looking towards the key light. - * @property {number} hazeGlareAngle=20 - The angle in degrees across the circle around the key light that the glare color and - * haze color are blended 50/50. - * - * @property {boolean} hazeAltitudeEffect=false - true if haze decreases with altitude as defined by the - * entity's local coordinate system, false if it doesn't. If true, then hazeBaseRef - * and hazeCeiling are used. - * @property {number} hazeBaseRef=0 - The y-axis value in the entity's local coordinate system at which the haze density starts - * reducing with altitude. - * @property {number} hazeCeiling=200 - The y-axis value in the entity's local coordinate system at which the haze density has - * reduced to 5%. - * - * @property {number} hazeBackgroundBlend=0 - The proportion of the skybox image to show through the haze: 0.0 - * displays no skybox image; 1.0 displays no haze. - * - * @property {boolean} hazeAttenuateKeyLight=false - true if the haze attenuates the key light, false - * if it doesn't. If true, then hazeKeyLightRange and hazeKeyLightAltitude are used. - * @property {number} hazeKeyLightRange=1000 - The distance at which the haze attenuates the key light by 95%. - * @property {number} hazeKeyLightAltitude=200 - The altitude at which the haze starts attenuating the key light (i.e., the - * altitude at which the distance starts being calculated). - */ -class HazePropertyGroup : public PropertyGroup { -public: - // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const override; - virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; - - void merge(const HazePropertyGroup& other); - - virtual void debugDump() const override; - virtual void listChangedProperties(QList& out) override; - - virtual bool appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt, int& processedBytes) override; - virtual void markAllChanged() override; - virtual EntityPropertyFlags getChangedProperties() const override; - - // EntityItem related helpers - // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const override; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - // Range only parameters - DEFINE_PROPERTY(PROP_HAZE_RANGE, HazeRange, hazeRange, float, INITIAL_HAZE_RANGE); - DEFINE_PROPERTY_REF(PROP_HAZE_COLOR, HazeColor, hazeColor, glm::u8vec3, initialHazeColor); - DEFINE_PROPERTY_REF(PROP_HAZE_GLARE_COLOR, HazeGlareColor, hazeGlareColor, glm::u8vec3, initialHazeGlareColor); - DEFINE_PROPERTY(PROP_HAZE_ENABLE_GLARE, HazeEnableGlare, hazeEnableGlare, bool, false); - DEFINE_PROPERTY_REF(PROP_HAZE_GLARE_ANGLE, HazeGlareAngle, hazeGlareAngle, float, INITIAL_HAZE_GLARE_ANGLE); - - // Altitude parameters - DEFINE_PROPERTY(PROP_HAZE_ALTITUDE_EFFECT, HazeAltitudeEffect, hazeAltitudeEffect, bool, false); - DEFINE_PROPERTY_REF(PROP_HAZE_CEILING, HazeCeiling, hazeCeiling, float, INITIAL_HAZE_BASE_REFERENCE + INITIAL_HAZE_HEIGHT); - DEFINE_PROPERTY_REF(PROP_HAZE_BASE_REF, HazeBaseRef, hazeBaseRef, float, INITIAL_HAZE_BASE_REFERENCE); - - // Background (skybox) blend value - DEFINE_PROPERTY_REF(PROP_HAZE_BACKGROUND_BLEND, HazeBackgroundBlend, hazeBackgroundBlend, float, INITIAL_HAZE_BACKGROUND_BLEND); - - // hazeDirectional light attenuation - DEFINE_PROPERTY(PROP_HAZE_ATTENUATE_KEYLIGHT, HazeAttenuateKeyLight, hazeAttenuateKeyLight, bool, false); - DEFINE_PROPERTY_REF(PROP_HAZE_KEYLIGHT_RANGE, HazeKeyLightRange, hazeKeyLightRange, float, INITIAL_KEY_LIGHT_RANGE); - DEFINE_PROPERTY_REF(PROP_HAZE_KEYLIGHT_ALTITUDE, HazeKeyLightAltitude, hazeKeyLightAltitude, float, INITIAL_KEY_LIGHT_ALTITUDE); -}; - -#endif // hifi_HazePropertyGroup_h diff --git a/libraries/entities/src/HazePropertyGroup.h.in b/libraries/entities/src/HazePropertyGroup.h.in new file mode 100644 index 0000000000..88496ba655 --- /dev/null +++ b/libraries/entities/src/HazePropertyGroup.h.in @@ -0,0 +1,88 @@ +// +// HazePropertyGroup.h +// libraries/entities/src +// +// Created by Nissim hadar on 9/21/17. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_HazePropertyGroup_h +#define hifi_HazePropertyGroup_h + +#include + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +static const float INITIAL_HAZE_RANGE{ 1000.0f }; +static const glm::u8vec3 initialHazeGlareColor { 255, 229, 179 }; +static const glm::u8vec3 initialHazeColor { 128, 154, 179 }; +static const float INITIAL_HAZE_GLARE_ANGLE{ 20.0f }; + +static const float INITIAL_HAZE_BASE_REFERENCE{ 0.0f }; +static const float INITIAL_HAZE_HEIGHT{ 200.0f }; + +static const float INITIAL_HAZE_BACKGROUND_BLEND{ 0.0f }; + +static const float INITIAL_KEY_LIGHT_RANGE{ 1000.0f }; +static const float INITIAL_KEY_LIGHT_ALTITUDE{ 200.0f }; + +// FIXME: Document hazeAttenuationKeyLight, hazeKeyLightRange, and hazeKeyLightAltitude once they're working and are provided +// in the Create app's UI. +/*@jsdoc + * Haze is defined by the following properties: + * @typedef {object} Entities.Haze + * + * @property {number} hazeRange=1000 - The horizontal distance at which visibility is reduced to 95%; i.e., 95% of each pixel's + * color is haze. + * @property {Color} hazeColor=128,154,179 - The color of the haze when looking away from the key light. + * @property {boolean} hazeEnableGlare=false - true if the haze is colored with glare from the key light, + * false if it isn't. If true, then hazeGlareColor and hazeGlareAngle + * are used. + * @property {Color} hazeGlareColor=255,299,179 - The color of the haze when looking towards the key light. + * @property {number} hazeGlareAngle=20 - The angle in degrees across the circle around the key light that the glare color and + * haze color are blended 50/50. + * + * @property {boolean} hazeAltitudeEffect=false - true if haze decreases with altitude as defined by the + * entity's local coordinate system, false if it doesn't. If true, then hazeBaseRef + * and hazeCeiling are used. + * @property {number} hazeBaseRef=0 - The y-axis value in the entity's local coordinate system at which the haze density starts + * reducing with altitude. + * @property {number} hazeCeiling=200 - The y-axis value in the entity's local coordinate system at which the haze density has + * reduced to 5%. + * + * @property {number} hazeBackgroundBlend=0 - The proportion of the skybox image to show through the haze: 0.0 + * displays no skybox image; 1.0 displays no haze. + * + * @property {boolean} hazeAttenuateKeyLight=false - true if the haze attenuates the key light, false + * if it doesn't. If true, then hazeKeyLightRange and hazeKeyLightAltitude are used. + * @property {number} hazeKeyLightRange=1000 - The distance at which the haze attenuates the key light by 95%. + * @property {number} hazeKeyLightAltitude=200 - The altitude at which the haze starts attenuating the key light (i.e., the + * altitude at which the distance starts being calculated). + */ +class HazePropertyGroup : public PropertyGroup { +public: + ENTITY_PROPERTY_GROUP_METHODS(HazePropertyGroup) + +protected: + +@Haze_GROUP_PROPS@ + +}; + +#endif // hifi_HazePropertyGroup_h diff --git a/libraries/entities/src/ImageEntityItem.cpp b/libraries/entities/src/ImageEntityItem.cpp deleted file mode 100644 index 3a796160c8..0000000000 --- a/libraries/entities/src/ImageEntityItem.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// -// Created by Sam Gondelman on 11/29/18 -// Copyright 2018 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "ImageEntityItem.h" - -#include "EntityItemProperties.h" - -EntityItemPointer ImageEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - Pointer entity(new ImageEntityItem(entityID), [](ImageEntityItem* ptr) { ptr->deleteLater(); }); - entity->setProperties(properties); - return entity; -} - -// our non-pure virtual subclass for now... -ImageEntityItem::ImageEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { - _type = EntityTypes::Image; -} - -void ImageEntityItem::setUnscaledDimensions(const glm::vec3& value) { - const float IMAGE_ENTITY_ITEM_FIXED_DEPTH = 0.01f; - // NOTE: Image Entities always have a "depth" of 1cm. - EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, IMAGE_ENTITY_ITEM_FIXED_DEPTH)); -} - -EntityItemProperties ImageEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { - EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); - withReadLock([&] { - _pulseProperties.getProperties(properties); - properties.setNaturalDimensions(_naturalDimensions); - }); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getImageURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(emissive, getEmissive); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(keepAspectRatio, getKeepAspectRatio); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(subImage, getSubImage); - - return properties; -} - -bool ImageEntityItem::setSubClassProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha); - withWriteLock([&] { - bool pulsePropertiesChanged = _pulseProperties.setProperties(properties); - somethingChanged |= pulsePropertiesChanged; - _needsRenderUpdate |= pulsePropertiesChanged; - }); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(imageURL, setImageURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(emissive, setEmissive); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(keepAspectRatio, setKeepAspectRatio); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(subImage, setSubImage); - - return somethingChanged; -} - -int ImageEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); - withWriteLock([&] { - int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, - somethingChanged); - bytesRead += bytesFromPulse; - dataAt += bytesFromPulse; - }); - - READ_ENTITY_PROPERTY(PROP_IMAGE_URL, QString, setImageURL); - READ_ENTITY_PROPERTY(PROP_EMISSIVE, bool, setEmissive); - READ_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, bool, setKeepAspectRatio); - READ_ENTITY_PROPERTY(PROP_SUB_IMAGE, QRect, setSubImage); - - return bytesRead; -} - -EntityPropertyFlags ImageEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - - requestedProperties += PROP_COLOR; - requestedProperties += PROP_ALPHA; - requestedProperties += _pulseProperties.getEntityProperties(params); - - requestedProperties += PROP_IMAGE_URL; - requestedProperties += PROP_EMISSIVE; - requestedProperties += PROP_KEEP_ASPECT_RATIO; - requestedProperties += PROP_SUB_IMAGE; - - return requestedProperties; -} - -void ImageEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); - withReadLock([&] { - _pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - }); - - APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, getImageURL()); - APPEND_ENTITY_PROPERTY(PROP_EMISSIVE, getEmissive()); - APPEND_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, getKeepAspectRatio()); - APPEND_ENTITY_PROPERTY(PROP_SUB_IMAGE, getSubImage()); -} - -QString ImageEntityItem::getImageURL() const { - QString result; - withReadLock([&] { - result = _imageURL; - }); - return result; -} - -void ImageEntityItem::setImageURL(const QString& url) { - withWriteLock([&] { - _needsRenderUpdate |= _imageURL != url; - _imageURL = url; - }); -} - -bool ImageEntityItem::getEmissive() const { - bool result; - withReadLock([&] { - result = _emissive; - }); - return result; -} - -void ImageEntityItem::setEmissive(bool emissive) { - withWriteLock([&] { - _needsRenderUpdate |= _emissive != emissive; - _emissive = emissive; - }); -} - -bool ImageEntityItem::getKeepAspectRatio() const { - bool result; - withReadLock([&] { - result = _keepAspectRatio; - }); - return result; -} - -void ImageEntityItem::setKeepAspectRatio(bool keepAspectRatio) { - withWriteLock([&] { - _needsRenderUpdate |= _keepAspectRatio != keepAspectRatio; - _keepAspectRatio = keepAspectRatio; - }); -} - -QRect ImageEntityItem::getSubImage() const { - QRect result; - withReadLock([&] { - result = _subImage; - }); - return result; -} - -void ImageEntityItem::setSubImage(const QRect& subImage) { - withWriteLock([&] { - _needsRenderUpdate |= _subImage != subImage; - _subImage = subImage; - }); -} - -void ImageEntityItem::setColor(const glm::u8vec3& color) { - withWriteLock([&] { - _needsRenderUpdate |= _color != color; - _color = color; - }); -} - -glm::u8vec3 ImageEntityItem::getColor() const { - return resultWithReadLock([&] { - return _color; - }); -} - -void ImageEntityItem::setAlpha(float alpha) { - withWriteLock([&] { - _needsRenderUpdate |= _alpha != alpha; - _alpha = alpha; - }); -} - -float ImageEntityItem::getAlpha() const { - return resultWithReadLock([&] { - return _alpha; - }); -} - -PulsePropertyGroup ImageEntityItem::getPulseProperties() const { - return resultWithReadLock([&] { - return _pulseProperties; - }); -} - -void ImageEntityItem::setNaturalDimension(const glm::vec3& naturalDimensions) const { - withWriteLock([&] { - _naturalDimensions = naturalDimensions; - }); -} diff --git a/libraries/entities/src/ImageEntityItem.cpp.in b/libraries/entities/src/ImageEntityItem.cpp.in new file mode 100644 index 0000000000..fcbd102284 --- /dev/null +++ b/libraries/entities/src/ImageEntityItem.cpp.in @@ -0,0 +1,107 @@ +// +// Created by Sam Gondelman on 11/29/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ImageEntityItem.h" + +#include "EntityItemProperties.h" + +EntityItemPointer ImageEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + Pointer entity(new ImageEntityItem(entityID), [](ImageEntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +// our non-pure virtual subclass for now... +ImageEntityItem::ImageEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { + _type = EntityTypes::Image; +} + +void ImageEntityItem::setUnscaledDimensions(const glm::vec3& value) { + const float IMAGE_ENTITY_ITEM_FIXED_DEPTH = 0.01f; + // NOTE: Image Entities always have a "depth" of 1cm. + EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, IMAGE_ENTITY_ITEM_FIXED_DEPTH)); +} + +EntityItemProperties ImageEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + +@Image_ENTITY_COPY_TO@ + + withReadLock([&] { + properties.setNaturalDimensions(_naturalDimensions); + }); + + return properties; +} + +bool ImageEntityItem::setSubClassProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Image_ENTITY_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags ImageEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + +@Image_REQUESTED_PROPS@ + + return requestedProperties; +} + +void ImageEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Image_ENTITY_APPEND@ + +} + +int ImageEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Image_ENTITY_READ@ + + return bytesRead; +} + +void ImageEntityItem::debugDump() const { + qCDebug(entities) << "ImageEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@Image_ENTITY_DEBUG@ + +} + +PulsePropertyGroup ImageEntityItem::getPulseProperties() const { + return resultWithReadLock([&] { + return _pulseProperties; + }); +} + +void ImageEntityItem::setNaturalDimension(const glm::vec3& naturalDimensions) const { + withWriteLock([&] { + _naturalDimensions = naturalDimensions; + }); +} diff --git a/libraries/entities/src/ImageEntityItem.h b/libraries/entities/src/ImageEntityItem.h deleted file mode 100644 index 0cc4eae05a..0000000000 --- a/libraries/entities/src/ImageEntityItem.h +++ /dev/null @@ -1,81 +0,0 @@ -// -// Created by Sam Gondelman on 11/29/18 -// Copyright 2018 High Fidelity, Inc. -// -// 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_ImageEntityItem_h -#define hifi_ImageEntityItem_h - -#include "EntityItem.h" - -#include "PulsePropertyGroup.h" - -class ImageEntityItem : public EntityItem { - using Pointer = std::shared_ptr; -public: - static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - - ImageEntityItem(const EntityItemID& entityItemID); - - ALLOW_INSTANTIATION // This class can be instantiated - - virtual void setUnscaledDimensions(const glm::vec3& value) override; - - // methods for getting/setting all properties of an entity - EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - bool setSubClassProperties(const EntityItemProperties& properties) override; - - EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - void setImageURL(const QString& imageUrl); - QString getImageURL() const; - - void setEmissive(bool emissive); - bool getEmissive() const; - - void setKeepAspectRatio(bool keepAspectRatio); - bool getKeepAspectRatio() const; - - void setSubImage(const QRect& subImage); - QRect getSubImage() const; - - void setColor(const glm::u8vec3& color); - glm::u8vec3 getColor() const; - - void setAlpha(float alpha); - float getAlpha() const; - - PulsePropertyGroup getPulseProperties() const; - - void setNaturalDimension(const glm::vec3& naturalDimensions) const; - -protected: - glm::u8vec3 _color; - float _alpha; - PulsePropertyGroup _pulseProperties; - - QString _imageURL; - bool _emissive { false }; - bool _keepAspectRatio { true }; - QRect _subImage; - - mutable glm::vec3 _naturalDimensions; -}; - -#endif // hifi_ImageEntityItem_h diff --git a/libraries/entities/src/ImageEntityItem.h.in b/libraries/entities/src/ImageEntityItem.h.in new file mode 100644 index 0000000000..13989306f5 --- /dev/null +++ b/libraries/entities/src/ImageEntityItem.h.in @@ -0,0 +1,38 @@ +// +// Created by Sam Gondelman on 11/29/18 +// Copyright 2018 High Fidelity, Inc. +// +// 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_ImageEntityItem_h +#define hifi_ImageEntityItem_h + +#include "EntityItem.h" + +#include "PulsePropertyGroup.h" + +class ImageEntityItem : public EntityItem { + using Pointer = std::shared_ptr; +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + ImageEntityItem(const EntityItemID& entityItemID); + + ALLOW_INSTANTIATION // This class can be instantiated + ENTITY_PROPERTY_SUBCLASS_METHODS + + void setNaturalDimension(const glm::vec3& naturalDimensions) const; + virtual void setUnscaledDimensions(const glm::vec3& value) override; + + PulsePropertyGroup getPulseProperties() const; + +protected: + +@Image_ENTITY_PROPS@ + + mutable glm::vec3 _naturalDimensions; +}; + +#endif // hifi_ImageEntityItem_h diff --git a/libraries/entities/src/KeyLightPropertyGroup.cpp b/libraries/entities/src/KeyLightPropertyGroup.cpp deleted file mode 100644 index f431aa55cc..0000000000 --- a/libraries/entities/src/KeyLightPropertyGroup.cpp +++ /dev/null @@ -1,235 +0,0 @@ -// -// KeyLightPropertyGroup.cpp -// libraries/entities/src -// -// Created by Sam Gateau on 2015/10/23. -// Copyright 2013 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#include "KeyLightPropertyGroup.h" - -#include -#include - -#include "EntityItemProperties.h" -#include "EntityItemPropertiesMacros.h" - -const glm::u8vec3 KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR = { 255, 255, 255 }; -const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_INTENSITY = 1.0f; -const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY = 0.5f; -const glm::vec3 KeyLightPropertyGroup::DEFAULT_KEYLIGHT_DIRECTION = { 0.0f, -1.0f, 0.0f }; -const bool KeyLightPropertyGroup::DEFAULT_KEYLIGHT_CAST_SHADOWS { false }; -const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_SHADOW_BIAS { 0.5f }; -const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_SHADOW_MAX_DISTANCE { 40.0f }; - -void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const { - - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_KEYLIGHT_COLOR, KeyLight, keyLight, Color, color, u8vec3Color); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_INTENSITY, KeyLight, keyLight, Intensity, intensity); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_DIRECTION, KeyLight, keyLight, Direction, direction); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_CAST_SHADOW, KeyLight, keyLight, CastShadows, castShadows); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_SHADOW_BIAS, KeyLight, keyLight, ShadowBias, shadowBias); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_SHADOW_MAX_DISTANCE, KeyLight, keyLight, ShadowMaxDistance, shadowMaxDistance); -} - -void KeyLightPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, color, u8vec3Color, setColor); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, intensity, float, setIntensity); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, direction, vec3, setDirection); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, castShadows, bool, setCastShadows); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, shadowBias, float, setShadowBias); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, shadowMaxDistance, float, setShadowMaxDistance); - - // legacy property support - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightColor, u8vec3Color, setColor, getColor); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightIntensity, float, setIntensity, getIntensity); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightDirection, vec3, setDirection, getDirection); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightCastShadows, bool, setCastShadows, getCastShadows); -} - -void KeyLightPropertyGroup::merge(const KeyLightPropertyGroup& other) { - COPY_PROPERTY_IF_CHANGED(color); - COPY_PROPERTY_IF_CHANGED(intensity); - COPY_PROPERTY_IF_CHANGED(direction); - COPY_PROPERTY_IF_CHANGED(castShadows); - COPY_PROPERTY_IF_CHANGED(shadowBias); - COPY_PROPERTY_IF_CHANGED(shadowMaxDistance); -} - -void KeyLightPropertyGroup::debugDump() const { - qCDebug(entities) << " KeyLightPropertyGroup: ---------------------------------------------"; - qCDebug(entities) << " color:" << getColor(); - qCDebug(entities) << " intensity:" << getIntensity(); - qCDebug(entities) << " direction:" << getDirection(); - qCDebug(entities) << " castShadows:" << getCastShadows(); - qCDebug(entities) << " shadowBias:" << getShadowBias(); - qCDebug(entities) << " shadowMaxDistance:" << getShadowMaxDistance(); -} - -void KeyLightPropertyGroup::listChangedProperties(QList& out) { - if (colorChanged()) { - out << "keyLight-color"; - } - if (intensityChanged()) { - out << "keyLight-intensity"; - } - if (directionChanged()) { - out << "keyLight-direction"; - } - if (castShadowsChanged()) { - out << "keyLight-castShadows"; - } - if (shadowBiasChanged()) { - out << "keyLight-shadowBias"; - } - if (shadowMaxDistanceChanged()) { - out << "keyLight-shadowMaxDistance"; - } -} - -bool KeyLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, getColor()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, getIntensity()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, getDirection()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, getCastShadows()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_SHADOW_BIAS, getShadowBias()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_SHADOW_MAX_DISTANCE, getShadowMaxDistance()); - - return true; -} - -bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt, - int& processedBytes) { - - int bytesRead = 0; - bool overwriteLocalData = true; - bool somethingChanged = false; - - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, bool, setCastShadows); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_SHADOW_BIAS, float, setShadowBias); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_SHADOW_MAX_DISTANCE, float, setShadowMaxDistance); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_COLOR, Color); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_INTENSITY, Intensity); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_DIRECTION, Direction); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_CAST_SHADOW, CastShadows); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_SHADOW_BIAS, ShadowBias); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_SHADOW_MAX_DISTANCE, ShadowMaxDistance); - - processedBytes += bytesRead; - - Q_UNUSED(somethingChanged); - - return true; -} - -void KeyLightPropertyGroup::markAllChanged() { - _colorChanged = true; - _intensityChanged = true; - _directionChanged = true; - _castShadowsChanged = true; - _shadowBiasChanged = true; - _shadowMaxDistanceChanged = true; -} - -EntityPropertyFlags KeyLightPropertyGroup::getChangedProperties() const { - EntityPropertyFlags changedProperties; - - CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_COLOR, color); - CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_INTENSITY, intensity); - CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_DIRECTION, direction); - CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_CAST_SHADOW, castShadows); - CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_SHADOW_BIAS, shadowBias); - CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_SHADOW_MAX_DISTANCE, shadowMaxDistance); - - return changedProperties; -} - -void KeyLightPropertyGroup::getProperties(EntityItemProperties& properties) const { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Color, getColor); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Intensity, getIntensity); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Direction, getDirection); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, CastShadows, getCastShadows); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, ShadowBias, getShadowBias); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, ShadowMaxDistance, getShadowMaxDistance); -} - -bool KeyLightPropertyGroup::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Color, color, setColor); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Intensity, intensity, setIntensity); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Direction, direction, setDirection); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, CastShadows, castShadows, setCastShadows); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, ShadowBias, shadowBias, setShadowBias); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, ShadowMaxDistance, shadowMaxDistance, setShadowMaxDistance); - - return somethingChanged; -} - -EntityPropertyFlags KeyLightPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties; - - requestedProperties += PROP_KEYLIGHT_COLOR; - requestedProperties += PROP_KEYLIGHT_INTENSITY; - requestedProperties += PROP_KEYLIGHT_DIRECTION; - requestedProperties += PROP_KEYLIGHT_CAST_SHADOW; - requestedProperties += PROP_KEYLIGHT_SHADOW_BIAS; - requestedProperties += PROP_KEYLIGHT_SHADOW_MAX_DISTANCE; - - return requestedProperties; -} - -void KeyLightPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, getColor()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, getIntensity()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, getDirection()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, getCastShadows()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_SHADOW_BIAS, getShadowBias()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_SHADOW_MAX_DISTANCE, getShadowMaxDistance()); -} - -int KeyLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, bool, setCastShadows); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_SHADOW_BIAS, float, setShadowBias); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_SHADOW_MAX_DISTANCE, float, setShadowMaxDistance); - - return bytesRead; -} diff --git a/libraries/entities/src/KeyLightPropertyGroup.cpp.in b/libraries/entities/src/KeyLightPropertyGroup.cpp.in new file mode 100644 index 0000000000..54f3d67172 --- /dev/null +++ b/libraries/entities/src/KeyLightPropertyGroup.cpp.in @@ -0,0 +1,143 @@ +// +// KeyLightPropertyGroup.cpp +// libraries/entities/src +// +// Created by Sam Gateau on 2015/10/23. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "KeyLightPropertyGroup.h" + +#include +#include + +#include "EntityItemProperties.h" + +const glm::u8vec3 KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR = { 255, 255, 255 }; +const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_INTENSITY = 1.0f; +const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY = 0.5f; +const glm::vec3 KeyLightPropertyGroup::DEFAULT_KEYLIGHT_DIRECTION = { 0.0f, -1.0f, 0.0f }; +const bool KeyLightPropertyGroup::DEFAULT_KEYLIGHT_CAST_SHADOWS { false }; +const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_SHADOW_BIAS { 0.5f }; +const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_SHADOW_MAX_DISTANCE { 40.0f }; + +void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + +@KeyLight_GROUP_COPY_TO_SCRIPT@ + +} + +void KeyLightPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + +@KeyLight_GROUP_COPY_FROM_SCRIPT@ + +} + +void KeyLightPropertyGroup::merge(const KeyLightPropertyGroup& other) { + +@KeyLight_GROUP_MERGE@ + +} + +void KeyLightPropertyGroup::debugDump() const { + +@KeyLight_GROUP_DEBUG_DUMP@ + +} + +void KeyLightPropertyGroup::listChangedProperties(QList& out) { + +@KeyLight_GROUP_LIST_CHANGED@ + +} + +bool KeyLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@KeyLight_GROUP_APPEND@ + + return successPropertyFits; +} + +bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + +@KeyLight_GROUP_READ@ + +@KeyLight_GROUP_DECODE_CHANGED@ + + processedBytes += bytesRead; + + return somethingChanged; +} + +void KeyLightPropertyGroup::markAllChanged() { + +@KeyLight_GROUP_MARK_CHANGED@ + +} + +EntityPropertyFlags KeyLightPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + +@KeyLight_GROUP_CHANGED_PROPERTIES@ + + return changedProperties; +} + +void KeyLightPropertyGroup::getProperties(EntityItemProperties& properties) const { + +@KeyLight_GROUP_COPY_TO@ + +} + +bool KeyLightPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@KeyLight_GROUP_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags KeyLightPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + +@KeyLight_REQUESTED_PROPS@ + + return requestedProperties; +} + +int KeyLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@KeyLight_GROUP_READ@ + + return bytesRead; +} + +void KeyLightPropertyGroup::addPropertyMap(QHash& _propertyInfos, + QHash& _enumsToPropertyStrings) { + +@KeyLight_GROUP_ADD_TO_MAP@ + +} diff --git a/libraries/entities/src/KeyLightPropertyGroup.h b/libraries/entities/src/KeyLightPropertyGroup.h deleted file mode 100644 index 4a412f9802..0000000000 --- a/libraries/entities/src/KeyLightPropertyGroup.h +++ /dev/null @@ -1,112 +0,0 @@ -// -// KeyLightPropertyGroup.h -// libraries/entities/src -// -// Created by Sam Gateau on 2015/10/23. -// Copyright 2013 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - - -#ifndef hifi_KeyLightPropertyGroup_h -#define hifi_KeyLightPropertyGroup_h - -#include - -#include - -#include "EntityItemPropertiesMacros.h" -#include "PropertyGroup.h" - -class EntityItemProperties; -class EncodeBitstreamParams; -class OctreePacketData; -class EntityTreeElementExtraEncodeData; -class ReadBitstreamToTreeParams; -class ScriptEngine; -class ScriptValue; - -/*@jsdoc - * A key light is defined by the following properties: - * @typedef {object} Entities.KeyLight - * @property {Color} color=255,255,255 - The color of the light. - * @property {number} intensity=1 - The intensity of the light. - * @property {Vec3} direction=0,-1,0 - The direction the light is shining. - * @property {boolean} castShadows=false - true if shadows are cast, false if they aren't. Shadows - * are cast by avatars, plus {@link Entities.EntityProperties-Model|Model} and - * {@link Entities.EntityProperties-Shape|Shape} entities that have their - * {@link Entities.EntityProperties|canCastShadow} property set to true. - * @property {number} shadowBias=0.5 - The bias of the shadows cast by the light, range 0.0 – - * 1.0. This fine-tunes shadows cast by the light, to prevent shadow acne and peter panning. - * @property {number} shadowMaxDistance=40.0 - The maximum distance from the camera position at which shadows will be computed, - * range 1.0250.0. Higher values cover more of the scene but with less precision. - */ -class KeyLightPropertyGroup : public PropertyGroup { -public: - // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const override; - virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; - - void merge(const KeyLightPropertyGroup& other); - - virtual void debugDump() const override; - virtual void listChangedProperties(QList& out) override; - - virtual bool appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt, int& processedBytes) override; - virtual void markAllChanged() override; - virtual EntityPropertyFlags getChangedProperties() const override; - - // EntityItem related helpers - // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const override; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - static const glm::u8vec3 DEFAULT_KEYLIGHT_COLOR; - static const float DEFAULT_KEYLIGHT_INTENSITY; - static const float DEFAULT_KEYLIGHT_AMBIENT_INTENSITY; - static const glm::vec3 DEFAULT_KEYLIGHT_DIRECTION; - static const bool DEFAULT_KEYLIGHT_CAST_SHADOWS; - static const float DEFAULT_KEYLIGHT_SHADOW_BIAS; - static const float DEFAULT_KEYLIGHT_SHADOW_MAX_DISTANCE; - - DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, Color, color, glm::u8vec3, DEFAULT_KEYLIGHT_COLOR); - DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, Intensity, intensity, float, DEFAULT_KEYLIGHT_INTENSITY); - DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, Direction, direction, glm::vec3, DEFAULT_KEYLIGHT_DIRECTION); - DEFINE_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, CastShadows, castShadows, bool, DEFAULT_KEYLIGHT_CAST_SHADOWS); - DEFINE_PROPERTY(PROP_KEYLIGHT_SHADOW_BIAS, ShadowBias, shadowBias, float, DEFAULT_KEYLIGHT_SHADOW_BIAS); - DEFINE_PROPERTY(PROP_KEYLIGHT_SHADOW_MAX_DISTANCE, ShadowMaxDistance, shadowMaxDistance, float, DEFAULT_KEYLIGHT_SHADOW_MAX_DISTANCE); -}; - -#endif // hifi_KeyLightPropertyGroup_h diff --git a/libraries/entities/src/KeyLightPropertyGroup.h.in b/libraries/entities/src/KeyLightPropertyGroup.h.in new file mode 100644 index 0000000000..03adb66a93 --- /dev/null +++ b/libraries/entities/src/KeyLightPropertyGroup.h.in @@ -0,0 +1,66 @@ +// +// KeyLightPropertyGroup.h +// libraries/entities/src +// +// Created by Sam Gateau on 2015/10/23. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + + +#ifndef hifi_KeyLightPropertyGroup_h +#define hifi_KeyLightPropertyGroup_h + +#include + +#include + +#include "EntityItemPropertiesMacros.h" +#include "PropertyGroup.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +/*@jsdoc + * A key light is defined by the following properties: + * @typedef {object} Entities.KeyLight + * @property {Color} color=255,255,255 - The color of the light. + * @property {number} intensity=1 - The intensity of the light. + * @property {Vec3} direction=0,-1,0 - The direction the light is shining. + * @property {boolean} castShadows=false - true if shadows are cast, false if they aren't. Shadows + * are cast by avatars, plus {@link Entities.EntityProperties-Model|Model} and + * {@link Entities.EntityProperties-Shape|Shape} entities that have their + * {@link Entities.EntityProperties|canCastShadow} property set to true. + * @property {number} shadowBias=0.5 - The bias of the shadows cast by the light, range 0.0 – + * 1.0. This fine-tunes shadows cast by the light, to prevent shadow acne and peter panning. + * @property {number} shadowMaxDistance=40.0 - The maximum distance from the camera position at which shadows will be computed, + * range 1.0250.0. Higher values cover more of the scene but with less precision. + */ +class KeyLightPropertyGroup : public PropertyGroup { +public: + ENTITY_PROPERTY_GROUP_METHODS(KeyLightPropertyGroup) + + static const glm::u8vec3 DEFAULT_KEYLIGHT_COLOR; + static const float DEFAULT_KEYLIGHT_INTENSITY; + static const float DEFAULT_KEYLIGHT_AMBIENT_INTENSITY; + static const glm::vec3 DEFAULT_KEYLIGHT_DIRECTION; + static const bool DEFAULT_KEYLIGHT_CAST_SHADOWS; + static const float DEFAULT_KEYLIGHT_SHADOW_BIAS; + static const float DEFAULT_KEYLIGHT_SHADOW_MAX_DISTANCE; + +protected: + +@KeyLight_GROUP_PROPS@ + +}; + +#endif // hifi_KeyLightPropertyGroup_h diff --git a/libraries/entities/src/LightEntityItem.cpp b/libraries/entities/src/LightEntityItem.cpp.in similarity index 65% rename from libraries/entities/src/LightEntityItem.cpp rename to libraries/entities/src/LightEntityItem.cpp.in index 4bd9eda5ac..43dfc784b1 100644 --- a/libraries/entities/src/LightEntityItem.cpp +++ b/libraries/entities/src/LightEntityItem.cpp.in @@ -58,16 +58,17 @@ void LightEntityItem::setUnscaledDimensions(const glm::vec3& value) { EntityItemProperties LightEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(isSpotlight, getIsSpotlight); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(intensity, getIntensity); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(exponent, getExponent); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(cutoff, getCutoff); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(falloffRadius, getFalloffRadius); +@Light_ENTITY_COPY_TO@ return properties; } +float LightEntityItem::getFalloffRadius() const { + return resultWithReadLock([&] { + return _falloffRadius; + }); +} + void LightEntityItem::setFalloffRadius(float value) { value = glm::max(value, 0.0f); @@ -77,6 +78,12 @@ void LightEntityItem::setFalloffRadius(float value) { }); } +bool LightEntityItem::getIsSpotlight() const { + return resultWithReadLock([&] { + return _isSpotlight; + }); +} + void LightEntityItem::setIsSpotlight(bool value) { bool needsRenderUpdate; withWriteLock([&] { @@ -102,6 +109,12 @@ void LightEntityItem::setIsSpotlight(bool value) { setScaledDimensions(newDimensions); } +float LightEntityItem::getCutoff() const { + return resultWithReadLock([&] { + return _cutoff; + }); +} + void LightEntityItem::setCutoff(float value) { value = glm::clamp(value, MIN_CUTOFF, MAX_CUTOFF); bool needsRenderUpdate; @@ -129,17 +142,20 @@ void LightEntityItem::setCutoff(float value) { bool LightEntityItem::setSubClassProperties(const EntityItemProperties& properties) { bool somethingChanged = false; - SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(isSpotlight, setIsSpotlight); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(intensity, setIntensity); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(exponent, setExponent); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(cutoff, setCutoff); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(falloffRadius, setFalloffRadius); +@Light_ENTITY_SET_FROM@ return somethingChanged; } -int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, +EntityPropertyFlags LightEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + +@Light_REQUESTED_PROPS@ + + return requestedProperties; +} + +int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) { @@ -147,110 +163,35 @@ int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); - READ_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, bool, setIsSpotlight); - READ_ENTITY_PROPERTY(PROP_INTENSITY, float, setIntensity); - READ_ENTITY_PROPERTY(PROP_EXPONENT, float, setExponent); - READ_ENTITY_PROPERTY(PROP_CUTOFF, float, setCutoff); - READ_ENTITY_PROPERTY(PROP_FALLOFF_RADIUS, float, setFalloffRadius); +@Light_ENTITY_READ@ return bytesRead; } - -EntityPropertyFlags LightEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - requestedProperties += PROP_COLOR; - requestedProperties += PROP_IS_SPOTLIGHT; - requestedProperties += PROP_INTENSITY; - requestedProperties += PROP_EXPONENT; - requestedProperties += PROP_CUTOFF; - requestedProperties += PROP_FALLOFF_RADIUS; - return requestedProperties; -} - -void LightEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { +void LightEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { bool successPropertyFits = true; - APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); - APPEND_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, getIsSpotlight()); - APPEND_ENTITY_PROPERTY(PROP_INTENSITY, getIntensity()); - APPEND_ENTITY_PROPERTY(PROP_EXPONENT, getExponent()); - APPEND_ENTITY_PROPERTY(PROP_CUTOFF, getCutoff()); - APPEND_ENTITY_PROPERTY(PROP_FALLOFF_RADIUS, getFalloffRadius()); + +@Light_ENTITY_APPEND@ + } -glm::u8vec3 LightEntityItem::getColor() const { - return resultWithReadLock([&] { - return _color; - }); -} +void LightEntityItem::debugDump() const { + qCDebug(entities) << "LightEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; -void LightEntityItem::setColor(const glm::u8vec3& value) { - withWriteLock([&] { - _needsRenderUpdate |= _color != value; - _color = value; - }); -} +@Light_ENTITY_DEBUG@ -bool LightEntityItem::getIsSpotlight() const { - bool result; - withReadLock([&] { - result = _isSpotlight; - }); - return result; -} - -float LightEntityItem::getIntensity() const { - float result; - withReadLock([&] { - result = _intensity; - }); - return result; -} - -void LightEntityItem::setIntensity(float value) { - withWriteLock([&] { - _needsRenderUpdate |= _intensity != value; - _intensity = value; - }); -} - -float LightEntityItem::getFalloffRadius() const { - float result; - withReadLock([&] { - result = _falloffRadius; - }); - return result; -} - -float LightEntityItem::getExponent() const { - float result; - withReadLock([&] { - result = _exponent; - }); - return result; -} - -void LightEntityItem::setExponent(float value) { - withWriteLock([&] { - _needsRenderUpdate |= _exponent != value; - _exponent = value; - }); -} - -float LightEntityItem::getCutoff() const { - float result; - withReadLock([&] { - result = _cutoff; - }); - return result; } bool LightEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, diff --git a/libraries/entities/src/LightEntityItem.h b/libraries/entities/src/LightEntityItem.h.in similarity index 51% rename from libraries/entities/src/LightEntityItem.h rename to libraries/entities/src/LightEntityItem.h.in index 0f21c6acd9..2ce56b9b3b 100644 --- a/libraries/entities/src/LightEntityItem.h +++ b/libraries/entities/src/LightEntityItem.h.in @@ -29,46 +29,11 @@ public: LightEntityItem(const EntityItemID& entityItemID); ALLOW_INSTANTIATION // This class can be instantiated + ENTITY_PROPERTY_SUBCLASS_METHODS /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately virtual void setUnscaledDimensions(const glm::vec3& value) override; - // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - virtual bool setSubClassProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - glm::u8vec3 getColor() const; - void setColor(const glm::u8vec3& value); - - bool getIsSpotlight() const; - void setIsSpotlight(bool value); - - float getIntensity() const; - void setIntensity(float value); - float getFalloffRadius() const; - void setFalloffRadius(float value); - - float getExponent() const; - void setExponent(float value); - - float getCutoff() const; - void setCutoff(float value); - static bool getLightsArePickable() { return _lightsArePickable; } static void setLightsArePickable(bool value) { _lightsArePickable = value; } @@ -83,13 +48,8 @@ public: QVariantMap& extraInfo, bool precisionPicking) const override; private: - // properties of a light - glm::u8vec3 _color; - bool _isSpotlight { DEFAULT_IS_SPOTLIGHT }; - float _intensity { DEFAULT_INTENSITY }; - float _falloffRadius { DEFAULT_FALLOFF_RADIUS }; - float _exponent { DEFAULT_EXPONENT }; - float _cutoff { DEFAULT_CUTOFF }; + +@Light_ENTITY_PROPS@ static bool _lightsArePickable; }; diff --git a/libraries/entities/src/LineEntityItem.cpp b/libraries/entities/src/LineEntityItem.cpp.in similarity index 68% rename from libraries/entities/src/LineEntityItem.cpp rename to libraries/entities/src/LineEntityItem.cpp.in index efb21b881e..d29729ded2 100644 --- a/libraries/entities/src/LineEntityItem.cpp +++ b/libraries/entities/src/LineEntityItem.cpp.in @@ -36,11 +36,10 @@ LineEntityItem::LineEntityItem(const EntityItemID& entityItemID) : } EntityItemProperties LineEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { - + EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(linePoints, getLinePoints); +@Line_ENTITY_COPY_TO@ return properties; } @@ -48,14 +47,60 @@ EntityItemProperties LineEntityItem::getProperties(const EntityPropertyFlags& de bool LineEntityItem::setSubClassProperties(const EntityItemProperties& properties) { bool somethingChanged = false; - SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(linePoints, setLinePoints); +@Line_ENTITY_SET_FROM@ return somethingChanged; } +EntityPropertyFlags LineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + +@Line_REQUESTED_PROPS@ + + return requestedProperties; +} + +void LineEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Line_ENTITY_APPEND@ + +} + +int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Line_ENTITY_READ@ + + return bytesRead; +} + +void LineEntityItem::debugDump() const { + qCDebug(entities) << "LineEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@Line_ENTITY_DEBUG@ + +} + bool LineEntityItem::appendPoint(const glm::vec3& point) { - if (_points.size() > MAX_POINTS_PER_LINE - 1) { + if (_linePoints.size() > MAX_POINTS_PER_LINE - 1) { qCDebug(entities) << "MAX POINTS REACHED!"; return false; } @@ -66,12 +111,18 @@ bool LineEntityItem::appendPoint(const glm::vec3& point) { } withWriteLock([&] { _needsRenderUpdate = true; - _points << point; + _linePoints << point; }); return true; } +QVector LineEntityItem::getLinePoints() const { + return resultWithReadLock>([&] { + return _linePoints; + }); +} + bool LineEntityItem::setLinePoints(const QVector& points) { if (points.size() > MAX_POINTS_PER_LINE) { return false; @@ -87,74 +138,8 @@ bool LineEntityItem::setLinePoints(const QVector& points) { withWriteLock([&] { _needsRenderUpdate = true; - _points = points; + _linePoints = points; }); return true; } - -int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); - READ_ENTITY_PROPERTY(PROP_LINE_POINTS, QVector, setLinePoints); - - return bytesRead; -} - - -EntityPropertyFlags LineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - requestedProperties += PROP_COLOR; - requestedProperties += PROP_LINE_POINTS; - return requestedProperties; -} - -void LineEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); - APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, getLinePoints()); -} - -void LineEntityItem::debugDump() const { - quint64 now = usecTimestampNow(); - qCDebug(entities) << " LINE EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " color:" << _color; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); -} - -glm::u8vec3 LineEntityItem::getColor() const { - return resultWithReadLock([&] { - return _color; - }); -} - -void LineEntityItem::setColor(const glm::u8vec3& value) { - withWriteLock([&] { - _needsRenderUpdate |= _color != value; - _color = value; - }); -} - -QVector LineEntityItem::getLinePoints() const { - QVector result; - withReadLock([&] { - result = _points; - }); - return result; -} \ No newline at end of file diff --git a/libraries/entities/src/LineEntityItem.h b/libraries/entities/src/LineEntityItem.h.in similarity index 57% rename from libraries/entities/src/LineEntityItem.h rename to libraries/entities/src/LineEntityItem.h.in index 505291cdda..63ea27d98b 100644 --- a/libraries/entities/src/LineEntityItem.h +++ b/libraries/entities/src/LineEntityItem.h.in @@ -21,34 +21,10 @@ class LineEntityItem : public EntityItem { LineEntityItem(const EntityItemID& entityItemID); ALLOW_INSTANTIATION // This class can be instantiated + ENTITY_PROPERTY_SUBCLASS_METHODS - // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - virtual bool setSubClassProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - glm::u8vec3 getColor() const; - void setColor(const glm::u8vec3& value); - - bool setLinePoints(const QVector& points); bool appendPoint(const glm::vec3& point); - QVector getLinePoints() const; - // never have a ray intersection pick a LineEntityItem. virtual bool supportsDetailedIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -62,12 +38,15 @@ class LineEntityItem : public EntityItem { QVariantMap& extraInfo, bool precisionPicking) const override { return false; } - virtual void debugDump() const override; static const int MAX_POINTS_PER_LINE; + QVector getLinePoints() const; + bool setLinePoints(const QVector& points); + private: - glm::u8vec3 _color; - QVector _points; + +@Line_ENTITY_PROPS@ + }; #endif // hifi_LineEntityItem_h diff --git a/libraries/entities/src/MaterialEntityItem.cpp b/libraries/entities/src/MaterialEntityItem.cpp deleted file mode 100644 index 2ee7ad4949..0000000000 --- a/libraries/entities/src/MaterialEntityItem.cpp +++ /dev/null @@ -1,290 +0,0 @@ -// -// Created by Sam Gondelman on 1/12/18 -// Copyright 2018 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "MaterialEntityItem.h" - -#include "EntityItemProperties.h" - -#include "QJsonDocument" -#include "QJsonArray" - -EntityItemPointer MaterialEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - Pointer entity(new MaterialEntityItem(entityID), [](MaterialEntityItem* ptr) { ptr->deleteLater(); }); - entity->setProperties(properties); - return entity; -} - -// our non-pure virtual subclass for now... -MaterialEntityItem::MaterialEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { - _type = EntityTypes::Material; -} - -EntityItemProperties MaterialEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { - EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialURL, getMaterialURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialMappingMode, getMaterialMappingMode); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(priority, getPriority); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentMaterialName, getParentMaterialName); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialMappingPos, getMaterialMappingPos); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialMappingScale, getMaterialMappingScale); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialMappingRot, getMaterialMappingRot); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialData, getMaterialData); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialRepeat, getMaterialRepeat); - return properties; -} - -bool MaterialEntityItem::setSubClassProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialURL, setMaterialURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialMappingMode, setMaterialMappingMode); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(priority, setPriority); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentMaterialName, setParentMaterialName); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialMappingPos, setMaterialMappingPos); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialMappingScale, setMaterialMappingScale); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialMappingRot, setMaterialMappingRot); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialData, setMaterialData); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialRepeat, setMaterialRepeat); - - return somethingChanged; -} - -int MaterialEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_MATERIAL_URL, QString, setMaterialURL); - READ_ENTITY_PROPERTY(PROP_MATERIAL_MAPPING_MODE, MaterialMappingMode, setMaterialMappingMode); - READ_ENTITY_PROPERTY(PROP_MATERIAL_PRIORITY, quint16, setPriority); - READ_ENTITY_PROPERTY(PROP_PARENT_MATERIAL_NAME, QString, setParentMaterialName); - READ_ENTITY_PROPERTY(PROP_MATERIAL_MAPPING_POS, glm::vec2, setMaterialMappingPos); - READ_ENTITY_PROPERTY(PROP_MATERIAL_MAPPING_SCALE, glm::vec2, setMaterialMappingScale); - READ_ENTITY_PROPERTY(PROP_MATERIAL_MAPPING_ROT, float, setMaterialMappingRot); - READ_ENTITY_PROPERTY(PROP_MATERIAL_DATA, QString, setMaterialData); - READ_ENTITY_PROPERTY(PROP_MATERIAL_REPEAT, bool, setMaterialRepeat); - - return bytesRead; -} - -EntityPropertyFlags MaterialEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - requestedProperties += PROP_MATERIAL_URL; - requestedProperties += PROP_MATERIAL_MAPPING_MODE; - requestedProperties += PROP_MATERIAL_PRIORITY; - requestedProperties += PROP_PARENT_MATERIAL_NAME; - requestedProperties += PROP_MATERIAL_MAPPING_POS; - requestedProperties += PROP_MATERIAL_MAPPING_SCALE; - requestedProperties += PROP_MATERIAL_MAPPING_ROT; - requestedProperties += PROP_MATERIAL_DATA; - requestedProperties += PROP_MATERIAL_REPEAT; - return requestedProperties; -} - -void MaterialEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_URL, getMaterialURL()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_MAPPING_MODE, (uint32_t)getMaterialMappingMode()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_PRIORITY, getPriority()); - APPEND_ENTITY_PROPERTY(PROP_PARENT_MATERIAL_NAME, getParentMaterialName()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_MAPPING_POS, getMaterialMappingPos()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_MAPPING_SCALE, getMaterialMappingScale()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_MAPPING_ROT, getMaterialMappingRot()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_DATA, getMaterialData()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_REPEAT, getMaterialRepeat()); -} - -void MaterialEntityItem::debugDump() const { - quint64 now = usecTimestampNow(); - qCDebug(entities) << " MATERIAL EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " name:" << _name; - qCDebug(entities) << " material url:" << _materialURL; - qCDebug(entities) << " material mapping mode:" << _materialMappingMode; - qCDebug(entities) << " material repeat:" << _materialRepeat; - qCDebug(entities) << " priority:" << _priority; - qCDebug(entities) << " parent material name:" << _parentMaterialName; - qCDebug(entities) << " material mapping pos:" << _materialMappingPos; - qCDebug(entities) << " material mapping scale:" << _materialMappingRot; - qCDebug(entities) << " material mapping rot:" << _materialMappingScale; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << "MATERIAL EntityItem Ptr:" << this; -} - -void MaterialEntityItem::setUnscaledDimensions(const glm::vec3& value) { - _desiredDimensions = value; - if (_hasVertexShader || _materialMappingMode == MaterialMappingMode::PROJECTED) { - EntityItem::setUnscaledDimensions(value); - } else if (_materialMappingMode == MaterialMappingMode::UV) { - EntityItem::setUnscaledDimensions(ENTITY_ITEM_DEFAULT_DIMENSIONS); - } -} - -QString MaterialEntityItem::getMaterialURL() const { - return resultWithReadLock([&] { - return _materialURL; - }); -} - -void MaterialEntityItem::setMaterialURL(const QString& materialURL) { - withWriteLock([&] { - _needsRenderUpdate |= _materialURL != materialURL; - _materialURL = materialURL; - }); -} - -QString MaterialEntityItem::getMaterialData() const { - return resultWithReadLock([&] { - return _materialData; - }); -} - -void MaterialEntityItem::setMaterialData(const QString& materialData) { - withWriteLock([&] { - _needsRenderUpdate |= _materialData != materialData; - _materialData = materialData; - }); -} - -MaterialMappingMode MaterialEntityItem::getMaterialMappingMode() const { - return resultWithReadLock([&] { - return _materialMappingMode; - }); -} - -void MaterialEntityItem::setMaterialMappingMode(MaterialMappingMode mode) { - withWriteLock([&] { - _needsRenderUpdate |= _materialMappingMode != mode; - _materialMappingMode = mode; - }); - setUnscaledDimensions(_desiredDimensions); -} - -quint16 MaterialEntityItem::getPriority() const { - return resultWithReadLock([&] { - return _priority; - }); -} - -void MaterialEntityItem::setPriority(quint16 priority) { - withWriteLock([&] { - _needsRenderUpdate |= _priority != priority; - _priority = priority; - }); -} - -QString MaterialEntityItem::getParentMaterialName() const { - return resultWithReadLock([&] { - return _parentMaterialName; - }); -} - -void MaterialEntityItem::setParentMaterialName(const QString& parentMaterialName) { - withWriteLock([&] { - _needsRenderUpdate |= _parentMaterialName != parentMaterialName; - _parentMaterialName = parentMaterialName; - }); -} - -glm::vec2 MaterialEntityItem::getMaterialMappingPos() const { - return resultWithReadLock([&] { - return _materialMappingPos; - }); -} - -void MaterialEntityItem::setMaterialMappingPos(const glm::vec2& materialMappingPos) { - withWriteLock([&] { - _needsRenderUpdate |= _materialMappingPos != materialMappingPos; - _materialMappingPos = materialMappingPos; - }); -} - -glm::vec2 MaterialEntityItem::getMaterialMappingScale() const { - return resultWithReadLock([&] { - return _materialMappingScale; - }); -} - -void MaterialEntityItem::setMaterialMappingScale(const glm::vec2& materialMappingScale) { - withWriteLock([&] { - _needsRenderUpdate |= _materialMappingScale != materialMappingScale; - _materialMappingScale = materialMappingScale; - }); -} - -float MaterialEntityItem::getMaterialMappingRot() const { - return resultWithReadLock([&] { - return _materialMappingRot; - }); -} - -void MaterialEntityItem::setMaterialMappingRot(float materialMappingRot) { - withWriteLock([&] { - _needsRenderUpdate |= _materialMappingRot != materialMappingRot; - _materialMappingRot = materialMappingRot; - }); -} - -bool MaterialEntityItem::getMaterialRepeat() const { - return resultWithReadLock([&] { - return _materialRepeat; - }); -} - -void MaterialEntityItem::setMaterialRepeat(bool value) { - withWriteLock([&] { - _needsRenderUpdate |= _materialRepeat != value; - _materialRepeat = value; - }); -} - -void MaterialEntityItem::setParentID(const QUuid& parentID) { - if (parentID != getParentID()) { - EntityItem::setParentID(parentID); - _hasVertexShader = false; - } -} - -AACube MaterialEntityItem::calculateInitialQueryAACube(bool& success) { - AACube aaCube = EntityItem::calculateInitialQueryAACube(success); - // A Material entity's queryAACube contains its parent's queryAACube - auto parent = getParentPointer(success); - if (success && parent) { - success = false; - AACube parentQueryAACube = parent->calculateInitialQueryAACube(success); - if (success) { - aaCube += parentQueryAACube.getMinimumPoint(); - aaCube += parentQueryAACube.getMaximumPoint(); - } - } - return aaCube; -} - -void MaterialEntityItem::setHasVertexShader(bool hasVertexShader) { - bool prevHasVertexShader = _hasVertexShader; - _hasVertexShader = hasVertexShader; - - if (hasVertexShader && !prevHasVertexShader) { - setLocalPosition(glm::vec3(0.0f)); - setLocalOrientation(glm::quat()); - setUnscaledDimensions(EntityTree::getUnscaledDimensionsForID(getParentID())); - } else if (!hasVertexShader && prevHasVertexShader) { - setUnscaledDimensions(_desiredDimensions); - } -} \ No newline at end of file diff --git a/libraries/entities/src/MaterialEntityItem.cpp.in b/libraries/entities/src/MaterialEntityItem.cpp.in new file mode 100644 index 0000000000..045c155084 --- /dev/null +++ b/libraries/entities/src/MaterialEntityItem.cpp.in @@ -0,0 +1,132 @@ +// +// Created by Sam Gondelman on 1/12/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "MaterialEntityItem.h" + +#include "EntityItemProperties.h" + +#include "QJsonDocument" +#include "QJsonArray" + +EntityItemPointer MaterialEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + Pointer entity(new MaterialEntityItem(entityID), [](MaterialEntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +// our non-pure virtual subclass for now... +MaterialEntityItem::MaterialEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { + _type = EntityTypes::Material; +} + +EntityItemProperties MaterialEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + +@Material_ENTITY_COPY_TO@ + + return properties; +} + +bool MaterialEntityItem::setSubClassProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Material_ENTITY_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags MaterialEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + +@Material_REQUESTED_PROPS@ + + return requestedProperties; +} + +void MaterialEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Material_ENTITY_APPEND@ + +} + +int MaterialEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Material_ENTITY_READ@ + + return bytesRead; +} + +void MaterialEntityItem::debugDump() const { + qCDebug(entities) << "MaterialEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@Material_ENTITY_DEBUG@ + +} + +void MaterialEntityItem::setUnscaledDimensions(const glm::vec3& value) { + _desiredDimensions = value; + if (_hasVertexShader || _materialMappingMode == MaterialMappingMode::PROJECTED) { + EntityItem::setUnscaledDimensions(value); + } else if (_materialMappingMode == MaterialMappingMode::UV) { + EntityItem::setUnscaledDimensions(ENTITY_ITEM_DEFAULT_DIMENSIONS); + } +} + +void MaterialEntityItem::setParentID(const QUuid& parentID) { + if (parentID != getParentID()) { + EntityItem::setParentID(parentID); + _hasVertexShader = false; + } +} + +AACube MaterialEntityItem::calculateInitialQueryAACube(bool& success) { + AACube aaCube = EntityItem::calculateInitialQueryAACube(success); + // A Material entity's queryAACube contains its parent's queryAACube + auto parent = getParentPointer(success); + if (success && parent) { + success = false; + AACube parentQueryAACube = parent->calculateInitialQueryAACube(success); + if (success) { + aaCube += parentQueryAACube.getMinimumPoint(); + aaCube += parentQueryAACube.getMaximumPoint(); + } + } + return aaCube; +} + +void MaterialEntityItem::setHasVertexShader(bool hasVertexShader) { + bool prevHasVertexShader = _hasVertexShader; + _hasVertexShader = hasVertexShader; + + if (hasVertexShader && !prevHasVertexShader) { + setLocalPosition(glm::vec3(0.0f)); + setLocalOrientation(glm::quat()); + setUnscaledDimensions(EntityTree::getUnscaledDimensionsForID(getParentID())); + } else if (!hasVertexShader && prevHasVertexShader) { + setUnscaledDimensions(_desiredDimensions); + } +} diff --git a/libraries/entities/src/MaterialEntityItem.h b/libraries/entities/src/MaterialEntityItem.h deleted file mode 100644 index 76cfbfa9dc..0000000000 --- a/libraries/entities/src/MaterialEntityItem.h +++ /dev/null @@ -1,119 +0,0 @@ -// -// Created by Sam Gondelman on 1/12/18 -// Copyright 2018 High Fidelity, Inc. -// -// 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_MaterialEntityItem_h -#define hifi_MaterialEntityItem_h - -#include "EntityItem.h" - -#include "MaterialMappingMode.h" - -class MaterialEntityItem : public EntityItem { - using Pointer = std::shared_ptr; -public: - static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - - MaterialEntityItem(const EntityItemID& entityItemID); - - ALLOW_INSTANTIATION // This class can be instantiated - - // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - virtual bool setSubClassProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - void debugDump() const override; - - virtual void setUnscaledDimensions(const glm::vec3& value) override; - - QString getMaterialURL() const; - void setMaterialURL(const QString& materialURL); - - QString getMaterialData() const; - void setMaterialData(const QString& materialData); - - MaterialMappingMode getMaterialMappingMode() const; - void setMaterialMappingMode(MaterialMappingMode mode); - - bool getMaterialRepeat() const; - void setMaterialRepeat(bool repeat); - - quint16 getPriority() const; - void setPriority(quint16 priority); - - QString getParentMaterialName() const; - void setParentMaterialName(const QString& parentMaterialName); - - void setParentID(const QUuid& parentID) override; - - glm::vec2 getMaterialMappingPos() const; - void setMaterialMappingPos(const glm::vec2& materialMappingPos); - glm::vec2 getMaterialMappingScale() const; - void setMaterialMappingScale(const glm::vec2& materialMappingScale); - float getMaterialMappingRot() const; - void setMaterialMappingRot(float materialMappingRot); - - AACube calculateInitialQueryAACube(bool& success) override; - - void setHasVertexShader(bool hasVertexShader); - -private: - // URL for this material. Currently, only JSON format is supported. Set to "materialData" to use the material data to live edit a material. - // The following fields are supported in the JSON: - // materialVersion: a uint for the version of this network material (currently, only 1 is supported) - // materials, which is either an object or an array of objects, each with the following properties: - // strings: - // name (NOT YET USED), model (NOT YET USED, should use "hifi_pbr") - // floats: - // opacity, roughness, metallic, scattering - // bool: - // unlit - // colors (arrays of 3 floats 0-1. Optional fourth value in array can be a boolean isSRGB): - // emissive, albedo - // urls to textures: - // emissiveMap, albedoMap (set opacityMap = albedoMap for transparency), metallicMap or specularMap, roughnessMap or glossMap, - // normalMap or bumpMap, occlusionMap, lightMap (broken, FIXME), scatteringMap (only works if normal mapped) - QString _materialURL; - // Type of material. "uv" or "projected". - MaterialMappingMode _materialMappingMode { UV }; - bool _materialRepeat { true }; - glm::vec3 _desiredDimensions; - // Priority for this material when applying it to its parent. Only the highest priority material will be used. Materials with the same priority are (essentially) randomly sorted. - // Base materials that come with models always have priority 0. - quint16 _priority { 0 }; - // An identifier for choosing a submesh or submeshes within a parent. If in the format "mat::", all submeshes with material name "" will be replaced. Otherwise, - // parentMaterialName will be parsed as an unsigned int (strings not starting with "mat::" will parse to 0), representing the mesh index to modify. - QString _parentMaterialName { "0" }; - // Offset position in UV-space of top left of material, (0, 0) to (1, 1) - glm::vec2 _materialMappingPos { 0, 0 }; - // How much to scale this material within its parent's UV-space - glm::vec2 _materialMappingScale { 1, 1 }; - // How much to rotate this material within its parent's UV-space (degrees) - float _materialMappingRot { 0 }; - QString _materialData; - - bool _hasVertexShader { false }; - -}; - -#endif // hifi_MaterialEntityItem_h diff --git a/libraries/entities/src/MaterialEntityItem.h.in b/libraries/entities/src/MaterialEntityItem.h.in new file mode 100644 index 0000000000..8575936e69 --- /dev/null +++ b/libraries/entities/src/MaterialEntityItem.h.in @@ -0,0 +1,42 @@ +// +// Created by Sam Gondelman on 1/12/18 +// Copyright 2018 High Fidelity, Inc. +// +// 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_MaterialEntityItem_h +#define hifi_MaterialEntityItem_h + +#include "EntityItem.h" + +#include "MaterialMappingMode.h" + +class MaterialEntityItem : public EntityItem { + using Pointer = std::shared_ptr; +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + MaterialEntityItem(const EntityItemID& entityItemID); + + ALLOW_INSTANTIATION // This class can be instantiated + ENTITY_PROPERTY_SUBCLASS_METHODS + + virtual void setUnscaledDimensions(const glm::vec3& value) override; + void setParentID(const QUuid& parentID) override; + + AACube calculateInitialQueryAACube(bool& success) override; + + void setHasVertexShader(bool hasVertexShader); + +private: + +@Material_ENTITY_PROPS@ + + glm::vec3 _desiredDimensions; + bool _hasVertexShader { false }; + +}; + +#endif // hifi_MaterialEntityItem_h diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp.in similarity index 69% rename from libraries/entities/src/ModelEntityItem.cpp rename to libraries/entities/src/ModelEntityItem.cpp.in index 5306613925..dd28b21818 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp.in @@ -41,63 +41,18 @@ ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem( _visuallyReady = false; } -const QString ModelEntityItem::getTextures() const { - return resultWithReadLock([&] { - return _textures; - }); -} - -void ModelEntityItem::setTextures(const QString& textures) { - withWriteLock([&] { - _needsRenderUpdate |= _textures != textures; - _textures = textures; - }); -} - EntityItemProperties ModelEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); +@Model_ENTITY_COPY_TO@ - COPY_ENTITY_PROPERTY_TO_PROPERTIES(modelURL, getModelURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(modelScale, getModelScale); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointRotationsSet, getJointRotationsSet); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointRotations, getJointRotations); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointTranslationsSet, getJointTranslationsSet); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointTranslations, getJointTranslations); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(relayParentJoints, getRelayParentJoints); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(groupCulled, getGroupCulled); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(blendshapeCoefficients, getBlendshapeCoefficients); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(useOriginalPivot, getUseOriginalPivot); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(loadPriority, getLoadPriority); - withReadLock([&] { - _animationProperties.getProperties(properties); - }); return properties; } bool ModelEntityItem::setSubClassProperties(const EntityItemProperties& properties) { bool somethingChanged = false; - SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(modelURL, setModelURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(modelScale, setModelScale); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointRotationsSet, setJointRotationsSet); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointRotations, setJointRotations); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslationsSet, setJointTranslationsSet); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslations, setJointTranslations); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(relayParentJoints, setRelayParentJoints); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(groupCulled, setGroupCulled); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(blendshapeCoefficients, setBlendshapeCoefficients); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(useOriginalPivot, setUseOriginalPivot); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(loadPriority, setLoadPriority); +@Model_ENTITY_SET_FROM@ withWriteLock([&] { AnimationPropertyGroup animationProperties = _animationProperties; @@ -109,6 +64,28 @@ bool ModelEntityItem::setSubClassProperties(const EntityItemProperties& properti return somethingChanged; } +EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + +@Model_REQUESTED_PROPS@ + + return requestedProperties; +} + +void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Model_ENTITY_APPEND@ + +} + int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, @@ -118,22 +95,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, const unsigned char* dataAt = data; bool animationPropertiesChanged = false; - READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); - READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); - READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); - READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); - - READ_ENTITY_PROPERTY(PROP_MODEL_URL, QString, setModelURL); - READ_ENTITY_PROPERTY(PROP_MODEL_SCALE, glm::vec3, setModelScale); - READ_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS_SET, QVector, setJointRotationsSet); - READ_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, QVector, setJointRotations); - READ_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, QVector, setJointTranslationsSet); - READ_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, QVector, setJointTranslations); - READ_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints); - READ_ENTITY_PROPERTY(PROP_GROUP_CULLED, bool, setGroupCulled); - READ_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, QString, setBlendshapeCoefficients); - READ_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, bool, setUseOriginalPivot); - READ_ENTITY_PROPERTY(PROP_LOAD_PRIORITY, float, setLoadPriority); +@Model_ENTITY_READ@ // grab a local copy of _animationProperties to avoid multiple locks int bytesFromAnimation; @@ -156,65 +118,6 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, return bytesRead; } -EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - - requestedProperties += PROP_SHAPE_TYPE; - requestedProperties += PROP_COMPOUND_SHAPE_URL; - requestedProperties += PROP_COLOR; - requestedProperties += PROP_TEXTURES; - - requestedProperties += PROP_MODEL_URL; - requestedProperties += PROP_MODEL_SCALE; - requestedProperties += PROP_JOINT_ROTATIONS_SET; - requestedProperties += PROP_JOINT_ROTATIONS; - requestedProperties += PROP_JOINT_TRANSLATIONS_SET; - requestedProperties += PROP_JOINT_TRANSLATIONS; - requestedProperties += PROP_RELAY_PARENT_JOINTS; - requestedProperties += PROP_GROUP_CULLED; - requestedProperties += PROP_BLENDSHAPE_COEFFICIENTS; - requestedProperties += PROP_USE_ORIGINAL_PIVOT; - requestedProperties += PROP_LOAD_PRIORITY; - requestedProperties += _animationProperties.getEntityProperties(params); - - return requestedProperties; -} - - -void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); - APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL()); - APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); - APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures()); - - APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, getModelURL()); - APPEND_ENTITY_PROPERTY(PROP_MODEL_SCALE, getModelScale()); - APPEND_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS_SET, getJointRotationsSet()); - APPEND_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, getJointRotations()); - APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, getJointTranslationsSet()); - APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, getJointTranslations()); - APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, getRelayParentJoints()); - APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, getGroupCulled()); - APPEND_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, getBlendshapeCoefficients()); - APPEND_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, getUseOriginalPivot()); - APPEND_ENTITY_PROPERTY(PROP_LOAD_PRIORITY, getLoadPriority()); - - withReadLock([&] { - _animationProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - }); -} - - - // added update function back for property fix void ModelEntityItem::update(const quint64& now) { assert(_lastAnimated > 0); @@ -232,7 +135,7 @@ void ModelEntityItem::update(const quint64& now) { } // increment animation frame - _currentFrame += (animationProperties.getFPS() * ((float)interval) / (float)USECS_PER_SECOND); + _currentFrame += (animationProperties.getFps() * ((float)interval) / (float)USECS_PER_SECOND); if (_currentFrame > animationProperties.getLastFrame() + 1.0f) { if (animationProperties.getLoop()) { _currentFrame = animationProperties.computeLoopedFrame(_currentFrame); @@ -252,15 +155,15 @@ void ModelEntityItem::update(const quint64& now) { } void ModelEntityItem::debugDump() const { - qCDebug(entities) << "ModelEntityItem id:" << getEntityItemID(); - qCDebug(entities) << " edited ago:" << getEditedAgo(); - qCDebug(entities) << " position:" << getWorldPosition(); - qCDebug(entities) << " dimensions:" << getScaledDimensions(); - qCDebug(entities) << " model URL:" << getModelURL(); - qCDebug(entities) << " compound shape URL:" << getCompoundShapeURL(); - qCDebug(entities) << " blendshapeCoefficients:" << getBlendshapeCoefficients(); - qCDebug(entities) << " useOrigialPivot:" << getUseOriginalPivot(); - qCDebug(entities) << " loadPriority:" << getLoadPriority(); + qCDebug(entities) << "ModelEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@Model_ENTITY_DEBUG@ + } void ModelEntityItem::setShapeType(ShapeType type) { @@ -300,15 +203,6 @@ ShapeType ModelEntityItem::getShapeType() const { return type; } -void ModelEntityItem::setModelURL(const QString& url) { - withWriteLock([&] { - if (_modelURL != url) { - _modelURL = url; - _needsRenderUpdate = true; - } - }); -} - glm::vec3 ModelEntityItem::getScaledDimensions() const { glm::vec3 parentScale = getTransform().getScale(); return _unscaledDimensions * parentScale; @@ -347,14 +241,6 @@ const Transform ModelEntityItem::getTransformWithOnlyLocalRotation(bool& success return worldTransform; } -void ModelEntityItem::setCompoundShapeURL(const QString& url) { - withWriteLock([&] { - if (_compoundShapeURL.get() != url) { - _compoundShapeURL.set(url); - } - }); -} - void ModelEntityItem::setAnimationSettings(const QString& value) { // NOTE: this method only called for old bitstream format @@ -371,7 +257,7 @@ void ModelEntityItem::setAnimationSettings(const QString& value) { QVariantMap settingsMap = settingsAsJsonObject.toVariantMap(); if (settingsMap.contains("fps")) { float fps = settingsMap["fps"].toFloat(); - animationProperties.setFPS(fps); + animationProperties.setFps(fps); } // old settings used frameIndex @@ -417,6 +303,19 @@ void ModelEntityItem::setAnimationSettings(const QString& value) { }); } +QString ModelEntityItem::getModelURL() const { + return resultWithReadLock([&] { + return _modelURL; + }); +} + +void ModelEntityItem::setModelURL(const QString& value) { + withWriteLock([&] { + _needsRenderUpdate |= _modelURL != value; + _modelURL = value; + }); +} + void ModelEntityItem::resizeJointArrays(int newSize) { if (newSize < 0) { return; @@ -548,78 +447,45 @@ QVector ModelEntityItem::getJointTranslationsSet() const { return result; } -bool ModelEntityItem::hasModel() const { +bool ModelEntityItem::hasModel() const { return resultWithReadLock([&] { return !_modelURL.isEmpty(); }); } bool ModelEntityItem::hasCompoundShapeURL() const { - return !_compoundShapeURL.get().isEmpty(); -} - -QString ModelEntityItem::getModelURL() const { - return resultWithReadLock([&] { - return _modelURL; - }); -} - -void ModelEntityItem::setRelayParentJoints(bool relayJoints) { - withWriteLock([&] { - _relayParentJoints = relayJoints; - }); -} - -bool ModelEntityItem::getRelayParentJoints() const { return resultWithReadLock([&] { - return _relayParentJoints; - }); -} - -void ModelEntityItem::setGroupCulled(bool value) { - withWriteLock([&] { - _needsRenderUpdate |= _groupCulled != value; - _groupCulled = value; - }); -} - -bool ModelEntityItem::getGroupCulled() const { - return resultWithReadLock([&] { - return _groupCulled; + return !_compoundShapeURL.isEmpty(); }); } QString ModelEntityItem::getCompoundShapeURL() const { - return _compoundShapeURL.get(); -} - -void ModelEntityItem::setColor(const glm::u8vec3& value) { - withWriteLock([&] { - _color = value; + return resultWithReadLock([&] { + return _compoundShapeURL; }); } -glm::u8vec3 ModelEntityItem::getColor() const { - return resultWithReadLock([&] { - return _color; +void ModelEntityItem::setCompoundShapeURL(const QString& url) { + withWriteLock([&] { + _compoundShapeURL = url; }); } // Animation related items... -AnimationPropertyGroup ModelEntityItem::getAnimationProperties() const { +AnimationPropertyGroup ModelEntityItem::getAnimationProperties() const { return resultWithReadLock([&] { return _animationProperties; }); } -bool ModelEntityItem::hasAnimation() const { - return resultWithReadLock([&] { - return !_animationProperties.getURL().isEmpty(); +bool ModelEntityItem::hasAnimation() const { + return resultWithReadLock([&] { + return !_animationProperties.getUrl().isEmpty(); }); } -QString ModelEntityItem::getAnimationURL() const { +QString ModelEntityItem::getAnimationURL() const { return resultWithReadLock([&] { - return _animationProperties.getURL(); + return _animationProperties.getUrl(); }); } @@ -635,7 +501,7 @@ bool ModelEntityItem::getAnimationAllowTranslation() const { }); } -float ModelEntityItem::getAnimationCurrentFrame() const { +float ModelEntityItem::getAnimationCurrentFrame() const { return resultWithReadLock([&] { return _animationProperties.getCurrentFrame(); }); @@ -701,18 +567,6 @@ bool ModelEntityItem::applyNewAnimationProperties(AnimationPropertyGroup newProp return somethingChanged; } -glm::vec3 ModelEntityItem::getModelScale() const { - return resultWithReadLock([&] { - return _modelScale; - }); -} - -void ModelEntityItem::setModelScale(const glm::vec3& modelScale) { - withWriteLock([&] { - _modelScale = modelScale; - }); -} - QString ModelEntityItem::getBlendshapeCoefficients() const { return resultWithReadLock([&] { return QJsonDocument::fromVariant(_blendshapeCoefficientsMap).toJson(); @@ -774,15 +628,3 @@ bool ModelEntityItem::getUseOriginalPivot() const { return _useOriginalPivot; }); } - -float ModelEntityItem::getLoadPriority() const { - return resultWithReadLock([&] { - return _loadPriority; - }); -} - -void ModelEntityItem::setLoadPriority(float loadPriority) { - withWriteLock([&] { - _loadPriority = loadPriority; - }); -} diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h.in similarity index 61% rename from libraries/entities/src/ModelEntityItem.h rename to libraries/entities/src/ModelEntityItem.h.in index f6c3e82073..7aa527f986 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h.in @@ -26,44 +26,24 @@ public: ModelEntityItem(const EntityItemID& entityItemID); ALLOW_INSTANTIATION // This class can be instantiated - - // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - virtual bool setSubClassProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - + ENTITY_PROPERTY_SUBCLASS_METHODS virtual void update(const quint64& now) override; bool needsToCallUpdate() const override { return isAnimatingSomething(); } - virtual void debugDump() const override; - void setShapeType(ShapeType type) override; virtual ShapeType getShapeType() const override; - glm::u8vec3 getColor() const; - void setColor(const glm::u8vec3& value); + virtual void setModelURL(const QString& value); + QString getModelURL() const; bool hasModel() const; virtual bool hasCompoundShapeURL() const; + virtual void setCompoundShapeURL(const QString& value); + QString getCompoundShapeURL() const; static const QString DEFAULT_MODEL_URL; - QString getModelURL() const; + static const QString DEFAULT_COMPOUND_SHAPE_URL; virtual glm::vec3 getScaledDimensions() const override; virtual void setScaledDimensions(const glm::vec3& value) override; @@ -72,13 +52,6 @@ public: virtual const Transform getTransformWithOnlyLocalRotation(bool& success, int depth = 0) const override; virtual const Transform getTransform() const override; - static const QString DEFAULT_COMPOUND_SHAPE_URL; - QString getCompoundShapeURL() const; - - // model related properties - virtual void setModelURL(const QString& url); - virtual void setCompoundShapeURL(const QString& url); - // Animation related items... AnimationPropertyGroup getAnimationProperties() const; bool hasAnimation() const; @@ -90,47 +63,29 @@ public: bool getAnimationSmoothFrames() const; int getAnimationNextFrame(int currentFrame, int frameCount) const; - void setRelayParentJoints(bool relayJoints); - bool getRelayParentJoints() const; - - void setGroupCulled(bool value); - bool getGroupCulled() const; - static const QString DEFAULT_TEXTURES; - const QString getTextures() const; - void setTextures(const QString& textures); - - virtual void setJointRotations(const QVector& rotations); - virtual void setJointRotationsSet(const QVector& rotationsSet); - virtual void setJointTranslations(const QVector& translations); - virtual void setJointTranslationsSet(const QVector& translationsSet); - - virtual void setAnimationJointsData(const QVector& jointsData); QVector getJointRotations() const; + virtual void setJointRotations(const QVector& rotations); QVector getJointRotationsSet() const; + virtual void setJointRotationsSet(const QVector& rotationsSet); QVector getJointTranslations() const; + virtual void setJointTranslations(const QVector& translations); QVector getJointTranslationsSet() const; + virtual void setJointTranslationsSet(const QVector& translationsSet); + virtual void setAnimationJointsData(const QVector& jointsData); - glm::vec3 getModelScale() const; - void setModelScale(const glm::vec3& modelScale); - - QString getBlendshapeCoefficients() const; - void setBlendshapeCoefficients(const QString& blendshapeCoefficients); bool blendshapesChanged() const { return _blendshapesChanged; } QVector getBlendshapeCoefficientVector(); - bool getUseOriginalPivot() const; - void setUseOriginalPivot(bool useOriginalPivot); - - float getLoadPriority() const; - void setLoadPriority(float loadPriority); - private: void setAnimationSettings(const QString& value); // only called for old bitstream format bool applyNewAnimationProperties(AnimationPropertyGroup newProperties); protected: + +@Model_ENTITY_PROPS@ + void resizeJointArrays(int newSize); // these are used: @@ -139,11 +94,6 @@ protected: // - to relay between network and model/rig // they aren't currently updated from data in the model/rig, and they don't have a direct effect // on what's rendered. - ReadWriteLockable _jointDataLock; - - bool _jointRotationsExplicitlySet { false }; // were the joints set as a property or just side effect of animations - bool _jointTranslationsExplicitlySet{ false }; // were the joints set as a property or just side effect of animations - struct ModelJointData { EntityJointData joint; bool rotationDirty { false }; @@ -151,23 +101,12 @@ protected: }; QVector _localJointData; + ReadWriteLockable _jointDataLock; + + bool _jointRotationsExplicitlySet { false }; // were the joints set as a property or just side effect of animations + bool _jointTranslationsExplicitlySet{ false }; // were the joints set as a property or just side effect of animations - glm::u8vec3 _color; - glm::vec3 _modelScale { 1.0f }; - QString _modelURL; - float _loadPriority { 0.0f }; - bool _relayParentJoints; - bool _groupCulled { false }; QVariantMap _blendshapeCoefficientsMap; - bool _useOriginalPivot { false }; - - ThreadSafeValueCache _compoundShapeURL; - - AnimationPropertyGroup _animationProperties; - - QString _textures; - - ShapeType _shapeType { SHAPE_TYPE_NONE }; private: uint64_t _lastAnimated { 0 }; diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp.in similarity index 68% rename from libraries/entities/src/ParticleEffectEntityItem.cpp rename to libraries/entities/src/ParticleEffectEntityItem.cpp.in index b0cd6efc9d..184ffee8cf 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp.in @@ -73,7 +73,7 @@ bool operator!=(const RangeGradient& a, const RangeGradient& b) { } bool operator==(const EmitProperties& a, const EmitProperties& b) { - return + return (a.rate == b.rate) && (a.speed == b.speed) && (a.acceleration == b.acceleration) && @@ -142,7 +142,7 @@ bool Properties::valid() const { bool Properties::emitting() const { return emission.rate > 0.0f && lifespan > 0.0f && polar.start <= polar.finish; - + } uint64_t Properties::emitIntervalUsecs() const { @@ -644,54 +644,7 @@ void ParticleEffectEntityItem::computeAndUpdateDimensions() { EntityItemProperties ParticleEffectEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); - withReadLock([&] { - _pulseProperties.getProperties(properties); - }); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxParticles, getMaxParticles); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifespan, getLifespan); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(isEmitting, getIsEmitting); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitRate, getEmitRate); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitSpeed, getEmitSpeed); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(speedSpread, getSpeedSpread); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitOrientation, getEmitOrientation); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitDimensions, getEmitDimensions); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitRadiusStart, getEmitRadiusStart); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(polarStart, getPolarStart); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(polarFinish, getPolarFinish); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(azimuthStart, getAzimuthStart); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(azimuthFinish, getAzimuthFinish); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitAcceleration, getEmitAcceleration); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(accelerationSpread, getAccelerationSpread); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleRadius, getParticleRadius); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(radiusSpread, getRadiusSpread); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(radiusStart, getRadiusStart); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(radiusFinish, getRadiusFinish); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(colorSpread, getColorSpread); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(colorStart, getColorStart); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(colorFinish, getColorFinish); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaSpread, getAlphaSpread); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaStart, getAlphaStart); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaFinish, getAlphaFinish); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitterShouldTrail, getEmitterShouldTrail); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleSpin, getParticleSpin); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(spinSpread, getSpinSpread); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(spinStart, getSpinStart); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(spinFinish, getSpinFinish); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotateWithEntity, getRotateWithEntity); +@ParticleEffect_ENTITY_COPY_TO@ return properties; } @@ -699,186 +652,15 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(const EntityPropert bool ParticleEffectEntityItem::setSubClassProperties(const EntityItemProperties& properties) { bool somethingChanged = false; - SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha); - withWriteLock([&] { - bool pulsePropertiesChanged = _pulseProperties.setProperties(properties); - somethingChanged |= pulsePropertiesChanged; - _needsRenderUpdate |= pulsePropertiesChanged; - }); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxParticles, setMaxParticles); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifespan, setLifespan); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(isEmitting, setIsEmitting); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitRate, setEmitRate); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitSpeed, setEmitSpeed); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(speedSpread, setSpeedSpread); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitOrientation, setEmitOrientation); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitDimensions, setEmitDimensions); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitRadiusStart, setEmitRadiusStart); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(polarStart, setPolarStart); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(polarFinish, setPolarFinish); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(azimuthStart, setAzimuthStart); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(azimuthFinish, setAzimuthFinish); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitAcceleration, setEmitAcceleration); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(accelerationSpread, setAccelerationSpread); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleRadius, setParticleRadius); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(radiusSpread, setRadiusSpread); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(radiusStart, setRadiusStart); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(radiusFinish, setRadiusFinish); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(colorSpread, setColorSpread); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(colorStart, setColorStart); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(colorFinish, setColorFinish); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaSpread, setAlphaSpread); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaStart, setAlphaStart); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaFinish, setAlphaFinish); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitterShouldTrail, setEmitterShouldTrail); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleSpin, setParticleSpin); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(spinSpread, setSpinSpread); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(spinStart, setSpinStart); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(spinFinish, setSpinFinish); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotateWithEntity, setRotateWithEntity); +@ParticleEffect_ENTITY_SET_FROM@ return somethingChanged; } -void ParticleEffectEntityItem::setColor(const glm::u8vec3& value) { - withWriteLock([&] { - _needsRenderUpdate |= _particleProperties.color.gradient.target != glm::vec3(value); - _particleProperties.color.gradient.target = value; - }); -} - -glm::u8vec3 ParticleEffectEntityItem::getColor() const { - return resultWithReadLock([&] { - return _particleProperties.color.gradient.target; - }); -} - -int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); - READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); - READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); - withWriteLock([&] { - int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, - somethingChanged); - bytesRead += bytesFromPulse; - dataAt += bytesFromPulse; - }); - READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); - - READ_ENTITY_PROPERTY(PROP_MAX_PARTICLES, quint32, setMaxParticles); - READ_ENTITY_PROPERTY(PROP_LIFESPAN, float, setLifespan); - - READ_ENTITY_PROPERTY(PROP_EMITTING_PARTICLES, bool, setIsEmitting); - READ_ENTITY_PROPERTY(PROP_EMIT_RATE, float, setEmitRate); - READ_ENTITY_PROPERTY(PROP_EMIT_SPEED, float, setEmitSpeed); - READ_ENTITY_PROPERTY(PROP_SPEED_SPREAD, float, setSpeedSpread); - READ_ENTITY_PROPERTY(PROP_EMIT_ORIENTATION, quat, setEmitOrientation); - READ_ENTITY_PROPERTY(PROP_EMIT_DIMENSIONS, glm::vec3, setEmitDimensions); - READ_ENTITY_PROPERTY(PROP_EMIT_RADIUS_START, float, setEmitRadiusStart); - - READ_ENTITY_PROPERTY(PROP_POLAR_START, float, setPolarStart); - READ_ENTITY_PROPERTY(PROP_POLAR_FINISH, float, setPolarFinish); - READ_ENTITY_PROPERTY(PROP_AZIMUTH_START, float, setAzimuthStart); - READ_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, float, setAzimuthFinish); - - READ_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, glm::vec3, setEmitAcceleration); - READ_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, glm::vec3, setAccelerationSpread); - - READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius); - READ_ENTITY_PROPERTY(PROP_RADIUS_SPREAD, float, setRadiusSpread); - READ_ENTITY_PROPERTY(PROP_RADIUS_START, float, setRadiusStart); - READ_ENTITY_PROPERTY(PROP_RADIUS_FINISH, float, setRadiusFinish); - - READ_ENTITY_PROPERTY(PROP_COLOR_SPREAD, u8vec3Color, setColorSpread); - READ_ENTITY_PROPERTY(PROP_COLOR_START, vec3Color, setColorStart); - READ_ENTITY_PROPERTY(PROP_COLOR_FINISH, vec3Color, setColorFinish); - - READ_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, float, setAlphaSpread); - READ_ENTITY_PROPERTY(PROP_ALPHA_START, float, setAlphaStart); - READ_ENTITY_PROPERTY(PROP_ALPHA_FINISH, float, setAlphaFinish); - - READ_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail); - - READ_ENTITY_PROPERTY(PROP_PARTICLE_SPIN, float, setParticleSpin); - READ_ENTITY_PROPERTY(PROP_SPIN_SPREAD, float, setSpinSpread); - READ_ENTITY_PROPERTY(PROP_SPIN_START, float, setSpinStart); - READ_ENTITY_PROPERTY(PROP_SPIN_FINISH, float, setSpinFinish); - READ_ENTITY_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, bool, setRotateWithEntity); - - return bytesRead; -} - EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - requestedProperties += PROP_SHAPE_TYPE; - requestedProperties += PROP_COMPOUND_SHAPE_URL; - requestedProperties += PROP_COLOR; - requestedProperties += PROP_ALPHA; - requestedProperties += _pulseProperties.getEntityProperties(params); - requestedProperties += PROP_TEXTURES; - - requestedProperties += PROP_MAX_PARTICLES; - requestedProperties += PROP_LIFESPAN; - - requestedProperties += PROP_EMITTING_PARTICLES; - requestedProperties += PROP_EMIT_RATE; - requestedProperties += PROP_EMIT_SPEED; - requestedProperties += PROP_SPEED_SPREAD; - requestedProperties += PROP_EMIT_ORIENTATION; - requestedProperties += PROP_EMIT_DIMENSIONS; - requestedProperties += PROP_EMIT_RADIUS_START; - - requestedProperties += PROP_POLAR_START; - requestedProperties += PROP_POLAR_FINISH; - requestedProperties += PROP_AZIMUTH_START; - requestedProperties += PROP_AZIMUTH_FINISH; - - requestedProperties += PROP_EMIT_ACCELERATION; - requestedProperties += PROP_ACCELERATION_SPREAD; - - requestedProperties += PROP_PARTICLE_RADIUS; - requestedProperties += PROP_RADIUS_SPREAD; - requestedProperties += PROP_RADIUS_START; - requestedProperties += PROP_RADIUS_FINISH; - - requestedProperties += PROP_COLOR_SPREAD; - requestedProperties += PROP_COLOR_START; - requestedProperties += PROP_COLOR_FINISH; - - requestedProperties += PROP_ALPHA_SPREAD; - requestedProperties += PROP_ALPHA_START; - requestedProperties += PROP_ALPHA_FINISH; - - requestedProperties += PROP_EMITTER_SHOULD_TRAIL; - - requestedProperties += PROP_PARTICLE_SPIN; - requestedProperties += PROP_SPIN_SPREAD; - requestedProperties += PROP_SPIN_START; - requestedProperties += PROP_SPIN_FINISH; - requestedProperties += PROP_PARTICLE_ROTATE_WITH_ENTITY; +@ParticleEffect_REQUESTED_PROPS@ return requestedProperties; } @@ -892,67 +674,47 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, OctreeElement::AppendState& appendState) const { bool successPropertyFits = true; - APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); - APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL()); - APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); - withReadLock([&] { - _pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - }); - APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures()); - APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, getMaxParticles()); - APPEND_ENTITY_PROPERTY(PROP_LIFESPAN, getLifespan()); +@ParticleEffect_ENTITY_APPEND@ - APPEND_ENTITY_PROPERTY(PROP_EMITTING_PARTICLES, getIsEmitting()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_RATE, getEmitRate()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_SPEED, getEmitSpeed()); - APPEND_ENTITY_PROPERTY(PROP_SPEED_SPREAD, getSpeedSpread()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_ORIENTATION, getEmitOrientation()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_DIMENSIONS, getEmitDimensions()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_RADIUS_START, getEmitRadiusStart()); +} - APPEND_ENTITY_PROPERTY(PROP_POLAR_START, getPolarStart()); - APPEND_ENTITY_PROPERTY(PROP_POLAR_FINISH, getPolarFinish()); - APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, getAzimuthStart()); - APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, getAzimuthFinish()); +int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { - APPEND_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, getEmitAcceleration()); - APPEND_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, getAccelerationSpread()); + int bytesRead = 0; + const unsigned char* dataAt = data; - APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, getParticleRadius()); - APPEND_ENTITY_PROPERTY(PROP_RADIUS_SPREAD, getRadiusSpread()); - APPEND_ENTITY_PROPERTY(PROP_RADIUS_START, getRadiusStart()); - APPEND_ENTITY_PROPERTY(PROP_RADIUS_FINISH, getRadiusFinish()); +@ParticleEffect_ENTITY_READ@ - APPEND_ENTITY_PROPERTY(PROP_COLOR_SPREAD, getColorSpread()); - APPEND_ENTITY_PROPERTY(PROP_COLOR_START, getColorStart()); - APPEND_ENTITY_PROPERTY(PROP_COLOR_FINISH, getColorFinish()); - - APPEND_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, getAlphaSpread()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, getAlphaStart()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, getAlphaFinish()); - - APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, getEmitterShouldTrail()); - - APPEND_ENTITY_PROPERTY(PROP_PARTICLE_SPIN, getParticleSpin()); - APPEND_ENTITY_PROPERTY(PROP_SPIN_SPREAD, getSpinSpread()); - APPEND_ENTITY_PROPERTY(PROP_SPIN_START, getSpinStart()); - APPEND_ENTITY_PROPERTY(PROP_SPIN_FINISH, getSpinFinish()); - APPEND_ENTITY_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, getRotateWithEntity()); + return bytesRead; } void ParticleEffectEntityItem::debugDump() const { - quint64 now = usecTimestampNow(); - qCDebug(entities) << "PA EFFECT EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " color:" << - _particleProperties.color.gradient.target.r << "," << - _particleProperties.color.gradient.target.g << "," << - _particleProperties.color.gradient.target.b; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << "ParticleEffectEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@ParticleEffect_ENTITY_DEBUG@ + +} + +void ParticleEffectEntityItem::setColor(const glm::u8vec3& value) { + withWriteLock([&] { + _needsRenderUpdate |= _particleProperties.color.gradient.target != glm::vec3(value); + _particleProperties.color.gradient.target = value; + }); +} + +glm::u8vec3 ParticleEffectEntityItem::getColor() const { + return resultWithReadLock([&] { + return _particleProperties.color.gradient.target; + }); } void ParticleEffectEntityItem::setShapeType(ShapeType type) { @@ -984,26 +746,6 @@ ShapeType ParticleEffectEntityItem::getShapeType() const { }); } -void ParticleEffectEntityItem::setCompoundShapeURL(const QString& compoundShapeURL) { - withWriteLock([&] { - _needsRenderUpdate |= _compoundShapeURL != compoundShapeURL; - _compoundShapeURL = compoundShapeURL; - }); -} - -QString ParticleEffectEntityItem::getCompoundShapeURL() const { - return resultWithReadLock([&] { - return _compoundShapeURL; - }); -} - -void ParticleEffectEntityItem::setIsEmitting(bool isEmitting) { - withWriteLock([&] { - _needsRenderUpdate |= _isEmitting != isEmitting; - _isEmitting = isEmitting; - }); -} - void ParticleEffectEntityItem::setMaxParticles(quint32 maxParticles) { maxParticles = glm::clamp(maxParticles, MINIMUM_MAX_PARTICLES, MAXIMUM_MAX_PARTICLES); @@ -1078,6 +820,14 @@ void ParticleEffectEntityItem::setEmitterShouldTrail(bool emitterShouldTrail) { }); } +bool ParticleEffectEntityItem::getEmitterShouldTrail() const { + return _particleProperties.emission.shouldTrail; +} + +bool ParticleEffectEntityItem::getRotateWithEntity() const { + return _particleProperties.rotateWithEntity; +} + void ParticleEffectEntityItem::setRotateWithEntity(bool rotateWithEntity) { withWriteLock([&] { _needsRenderUpdate |= _particleProperties.rotateWithEntity != rotateWithEntity; @@ -1086,7 +836,7 @@ void ParticleEffectEntityItem::setRotateWithEntity(bool rotateWithEntity) { } particle::Properties ParticleEffectEntityItem::getParticleProperties() const { - particle::Properties result; + particle::Properties result; withReadLock([&] { result = _particleProperties; }); @@ -1121,7 +871,7 @@ particle::Properties ParticleEffectEntityItem::getParticleProperties() const { qCWarning(entities) << "failed validation"; } - return result; + return result; } PulsePropertyGroup ParticleEffectEntityItem::getPulseProperties() const { diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h.in similarity index 67% rename from libraries/entities/src/ParticleEffectEntityItem.h rename to libraries/entities/src/ParticleEffectEntityItem.h.in index c96323fc9a..093d059132 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h.in @@ -175,7 +175,7 @@ namespace particle { bool valid() const; bool emitting() const; uint64_t emitIntervalUsecs() const; - + Properties& operator =(const Properties& other) { color = other.color; alpha = other.alpha; @@ -206,157 +206,30 @@ bool operator!=(const particle::Properties& a, const particle::Properties& b); class ParticleEffectEntityItem : public EntityItem { public: - ALLOW_INSTANTIATION // This class can be instantiated - static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); ParticleEffectEntityItem(const EntityItemID& entityItemID); - // methods for getting/setting all properties of this entity - virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - virtual bool setSubClassProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; + ALLOW_INSTANTIATION // This class can be instantiated + ENTITY_PROPERTY_SUBCLASS_METHODS bool shouldBePhysical() const override { return false; } - virtual void debugDump() const override; - - void setColor(const glm::u8vec3& value); - glm::u8vec3 getColor() const; - - void setColorStart(const vec3& colorStart); - vec3 getColorStart() const; - - void setColorFinish(const vec3& colorFinish); - vec3 getColorFinish() const; - - void setColorSpread(const glm::u8vec3& colorSpread); - glm::u8vec3 getColorSpread() const; - - void setAlpha(float alpha); - float getAlpha() const; - - void setAlphaStart(float alphaStart); - float getAlphaStart() const; - - void setAlphaFinish(float alphaFinish); - float getAlphaFinish() const; - - void setAlphaSpread(float alphaSpread); - float getAlphaSpread() const; - - void setShapeType(ShapeType type) override; - virtual ShapeType getShapeType() const override; - - QString getCompoundShapeURL() const; - virtual void setCompoundShapeURL(const QString& url); - - bool getIsEmitting() const { return _isEmitting; } - void setIsEmitting(bool isEmitting); - - void setMaxParticles(quint32 maxParticles); - quint32 getMaxParticles() const; - - void setLifespan(float lifespan); - float getLifespan() const; - - void setEmitRate(float emitRate); - float getEmitRate() const; - - void setEmitSpeed(float emitSpeed); - float getEmitSpeed() const; - - void setSpeedSpread(float speedSpread); - float getSpeedSpread() const; - - void setEmitOrientation(const glm::quat& emitOrientation); - glm::quat getEmitOrientation() const; - - void setEmitDimensions(const glm::vec3& emitDimensions); - glm::vec3 getEmitDimensions() const; - - void setEmitRadiusStart(float emitRadiusStart); - float getEmitRadiusStart() const; - - void setPolarStart(float polarStart); - float getPolarStart() const; - - void setPolarFinish(float polarFinish); - float getPolarFinish() const; - - void setAzimuthStart(float azimuthStart); - float getAzimuthStart() const; - - void setAzimuthFinish(float azimuthFinish); - float getAzimuthFinish() const; - - void setEmitAcceleration(const glm::vec3& emitAcceleration); - glm::vec3 getEmitAcceleration() const; - - void setAccelerationSpread(const glm::vec3& accelerationSpread); - glm::vec3 getAccelerationSpread() const; - - void setParticleRadius(float particleRadius); - float getParticleRadius() const; - - void setRadiusStart(float radiusStart); - float getRadiusStart() const; - - void setRadiusFinish(float radiusFinish); - float getRadiusFinish() const; - - void setRadiusSpread(float radiusSpread); - float getRadiusSpread() const; - - void setParticleSpin(float particleSpin); - float getParticleSpin() const; - - void setSpinStart(float spinStart); - float getSpinStart() const; - - void setSpinFinish(float spinFinish); - float getSpinFinish() const; - - void setSpinSpread(float spinSpread); - float getSpinSpread() const; - - void setRotateWithEntity(bool rotateWithEntity); - bool getRotateWithEntity() const { return _particleProperties.rotateWithEntity; } - void computeAndUpdateDimensions(); - void setTextures(const QString& textures); - QString getTextures() const; - - void setEmitterShouldTrail(bool emitterShouldTrail); - bool getEmitterShouldTrail() const { return _particleProperties.emission.shouldTrail; } - virtual bool supportsDetailedIntersection() const override { return false; } + ShapeType getShapeType() const override; + void setShapeType(ShapeType type) override; + particle::Properties getParticleProperties() const; PulsePropertyGroup getPulseProperties() const; protected: - particle::Properties _particleProperties; - PulsePropertyGroup _pulseProperties; - bool _isEmitting { true }; - ShapeType _shapeType{ particle::DEFAULT_SHAPE_TYPE }; - QString _compoundShapeURL { "" }; +@ParticleEffect_ENTITY_PROPS@ + + particle::Properties _particleProperties; }; #endif // hifi_ParticleEffectEntityItem_h diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp.in similarity index 59% rename from libraries/entities/src/PolyLineEntityItem.cpp rename to libraries/entities/src/PolyLineEntityItem.cpp.in index 605a171c7c..e4670bc35a 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp.in @@ -38,17 +38,8 @@ PolyLineEntityItem::PolyLineEntityItem(const EntityItemID& entityItemID) : Entit EntityItemProperties PolyLineEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(linePoints, getLinePoints); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(strokeWidths, getStrokeWidths); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(normals, getNormals); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(strokeColors, getStrokeColors); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(isUVModeStretch, getIsUVModeStretch); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(glow, getGlow); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(faceCamera, getFaceCamera); +@PolyLine_ENTITY_COPY_TO@ return properties; } @@ -56,23 +47,60 @@ EntityItemProperties PolyLineEntityItem::getProperties(const EntityPropertyFlags bool PolyLineEntityItem::setSubClassProperties(const EntityItemProperties& properties) { bool somethingChanged = false; - SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(linePoints, setLinePoints); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(strokeWidths, setStrokeWidths); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(normals, setNormals); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(strokeColors, setStrokeColors); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(isUVModeStretch, setIsUVModeStretch); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(glow, setGlow); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(faceCamera, setFaceCamera); +@PolyLine_ENTITY_SET_FROM@ return somethingChanged; } +EntityPropertyFlags PolyLineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + +@PolyLine_REQUESTED_PROP@ + + return requestedProperties; +} + +void PolyLineEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@PolyLine_ENTITY_APPEND@ + +} + +int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + int bytesRead = 0; + const unsigned char* dataAt = data; + +@PolyLine_ENTITY_READ@ + + return bytesRead; +} + +void PolyLineEntityItem::debugDump() const { + qCDebug(entities) << "PolyLineEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@PolyLine_ENTITY_DEBUG@ + +} + void PolyLineEntityItem::setLinePoints(const QVector& points) { withWriteLock([&] { - _points = points; + _linePoints = points; _pointsChanged = true; }); computeAndUpdateDimensions(); @@ -80,7 +108,7 @@ void PolyLineEntityItem::setLinePoints(const QVector& points) { void PolyLineEntityItem::setStrokeWidths(const QVector& strokeWidths) { withWriteLock([&] { - _widths = strokeWidths; + _strokeWidths = strokeWidths; _widthsChanged = true; }); computeAndUpdateDimensions(); @@ -95,7 +123,7 @@ void PolyLineEntityItem::setNormals(const QVector& normals) { void PolyLineEntityItem::setStrokeColors(const QVector& strokeColors) { withWriteLock([&] { - _colors = strokeColors; + _strokeColors = strokeColors; _colorsChanged = true; }); } @@ -105,8 +133,8 @@ void PolyLineEntityItem::computeAndUpdateDimensions() { QVector widths; withReadLock([&] { - points = _points; - widths = _widths; + points = _linePoints; + widths = _strokeWidths; }); glm::vec3 maxHalfDim(0.5f * ENTITY_ITEM_DEFAULT_WIDTH); @@ -123,8 +151,8 @@ void PolyLineEntityItem::computeTightLocalBoundingBox(AABox& localBox) const { QVector points; QVector widths; withReadLock([&] { - points = _points; - widths = _widths; + points = _linePoints; + widths = _strokeWidths; }); if (points.size() > 0) { @@ -145,76 +173,9 @@ void PolyLineEntityItem::computeTightLocalBoundingBox(AABox& localBox) const { } } -int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); - READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); - - READ_ENTITY_PROPERTY(PROP_LINE_POINTS, QVector, setLinePoints); - READ_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, QVector, setStrokeWidths); - READ_ENTITY_PROPERTY(PROP_STROKE_NORMALS, QVector, setNormals); - READ_ENTITY_PROPERTY(PROP_STROKE_COLORS, QVector, setStrokeColors); - READ_ENTITY_PROPERTY(PROP_IS_UV_MODE_STRETCH, bool, setIsUVModeStretch); - READ_ENTITY_PROPERTY(PROP_LINE_GLOW, bool, setGlow); - READ_ENTITY_PROPERTY(PROP_LINE_FACE_CAMERA, bool, setFaceCamera); - - return bytesRead; -} - -EntityPropertyFlags PolyLineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - requestedProperties += PROP_COLOR; - requestedProperties += PROP_TEXTURES; - - requestedProperties += PROP_LINE_POINTS; - requestedProperties += PROP_STROKE_WIDTHS; - requestedProperties += PROP_STROKE_NORMALS; - requestedProperties += PROP_STROKE_COLORS; - requestedProperties += PROP_IS_UV_MODE_STRETCH; - requestedProperties += PROP_LINE_GLOW; - requestedProperties += PROP_LINE_FACE_CAMERA; - return requestedProperties; -} - -void PolyLineEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); - APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures()); - - APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, getLinePoints()); - APPEND_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, getStrokeWidths()); - APPEND_ENTITY_PROPERTY(PROP_STROKE_NORMALS, getNormals()); - APPEND_ENTITY_PROPERTY(PROP_STROKE_COLORS, getStrokeColors()); - APPEND_ENTITY_PROPERTY(PROP_IS_UV_MODE_STRETCH, getIsUVModeStretch()); - APPEND_ENTITY_PROPERTY(PROP_LINE_GLOW, getGlow()); - APPEND_ENTITY_PROPERTY(PROP_LINE_FACE_CAMERA, getFaceCamera()); -} - -void PolyLineEntityItem::debugDump() const { - quint64 now = usecTimestampNow(); - qCDebug(entities) << " QUAD EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " color:" << _color; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); -} - QVector PolyLineEntityItem::getLinePoints() const { return resultWithReadLock>([&] { - return _points; + return _linePoints; }); } @@ -226,17 +187,17 @@ QVector PolyLineEntityItem::getNormals() const { QVector PolyLineEntityItem::getStrokeColors() const { return resultWithReadLock>([&] { - return _colors; + return _strokeColors; }); } -QVector PolyLineEntityItem::getStrokeWidths() const { +QVector PolyLineEntityItem::getStrokeWidths() const { return resultWithReadLock>([&] { - return _widths; + return _strokeWidths; }); } -QString PolyLineEntityItem::getTextures() const { +QString PolyLineEntityItem::getTextures() const { return resultWithReadLock([&] { return _textures; }); @@ -263,24 +224,3 @@ glm::u8vec3 PolyLineEntityItem::getColor() const { return _color; }); } - -void PolyLineEntityItem::setIsUVModeStretch(bool isUVModeStretch) { - withWriteLock([&] { - _needsRenderUpdate |= _isUVModeStretch != isUVModeStretch; - _isUVModeStretch = isUVModeStretch; - }); -} - -void PolyLineEntityItem::setGlow(bool glow) { - withWriteLock([&] { - _needsRenderUpdate |= _glow != glow; - _glow = glow; - }); -} - -void PolyLineEntityItem::setFaceCamera(bool faceCamera) { - withWriteLock([&] { - _needsRenderUpdate |= _faceCamera != faceCamera; - _faceCamera = faceCamera; - }); -} diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h.in similarity index 50% rename from libraries/entities/src/PolyLineEntityItem.h rename to libraries/entities/src/PolyLineEntityItem.h.in index 4f3810b900..dd1fa87e1f 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h.in @@ -23,56 +23,12 @@ public: PolyLineEntityItem(const EntityItemID& entityItemID); ALLOW_INSTANTIATION // This class can be instantiated - - // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - virtual bool setSubClassProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - glm::u8vec3 getColor() const; - void setColor(const glm::u8vec3& value); + ENTITY_PROPERTY_SUBCLASS_METHODS static const int MAX_POINTS_PER_LINE; - void setLinePoints(const QVector& points); - QVector getLinePoints() const; - static const float DEFAULT_LINE_WIDTH; - void setStrokeWidths(const QVector& strokeWidths); - QVector getStrokeWidths() const; - void setNormals(const QVector& normals); - QVector getNormals() const; - - void setStrokeColors(const QVector& strokeColors); - QVector getStrokeColors() const; - - void setIsUVModeStretch(bool isUVModeStretch); - bool getIsUVModeStretch() const{ return _isUVModeStretch; } - - QString getTextures() const; - void setTextures(const QString& textures); - - void setGlow(bool glow); - bool getGlow() const { return _glow; } - - void setFaceCamera(bool faceCamera); - bool getFaceCamera() const { return _faceCamera; } - - bool pointsChanged() const { return _pointsChanged; } + bool pointsChanged() const { return _pointsChanged; } bool normalsChanged() const { return _normalsChanged; } bool colorsChanged() const { return _colorsChanged; } bool widthsChanged() const { return _widthsChanged; } @@ -93,21 +49,12 @@ public: QVariantMap& extraInfo, bool precisionPicking) const override { return false; } void computeTightLocalBoundingBox(AABox& box) const; - - virtual void debugDump() const override; private: void computeAndUpdateDimensions(); - protected: - glm::u8vec3 _color; - QVector _points; - QVector _normals; - QVector _colors; - QVector _widths; - QString _textures; - bool _isUVModeStretch { false }; - bool _glow { false }; - bool _faceCamera { false }; +protected: + +@PolyLine_ENTITY_PROPS@ bool _pointsChanged { false }; bool _normalsChanged { false }; diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp.in similarity index 61% rename from libraries/entities/src/PolyVoxEntityItem.cpp rename to libraries/entities/src/PolyVoxEntityItem.cpp.in index dcbcd72d1f..d35111d35c 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp.in @@ -80,28 +80,16 @@ void PolyVoxEntityItem::setVoxelVolumeSize(const glm::vec3& voxelVolumeSize_) { } glm::vec3 PolyVoxEntityItem::getVoxelVolumeSize() const { - glm::vec3 voxelVolumeSize; - withReadLock([&] { - voxelVolumeSize = _voxelVolumeSize; + return resultWithReadLock([&] { + return _voxelVolumeSize; }); - return voxelVolumeSize; } EntityItemProperties PolyVoxEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelVolumeSize, getVoxelVolumeSize); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelData, getVoxelData); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelSurfaceStyle, getVoxelSurfaceStyle); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(xTextureURL, getXTextureURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(yTextureURL, getYTextureURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(zTextureURL, getZTextureURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(xNNeighborID, getXNNeighborID); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(yNNeighborID, getYNNeighborID); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(zNNeighborID, getZNNeighborID); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(xPNeighborID, getXPNeighborID); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(yPNeighborID, getYPNeighborID); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(zPNeighborID, getZPNeighborID); + +@PolyVox_ENTITY_COPY_TO@ return properties; } @@ -109,23 +97,34 @@ EntityItemProperties PolyVoxEntityItem::getProperties(const EntityPropertyFlags& bool PolyVoxEntityItem::setSubClassProperties(const EntityItemProperties& properties) { bool somethingChanged = false; - SET_ENTITY_PROPERTY_FROM_PROPERTIES(voxelVolumeSize, setVoxelVolumeSize); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(voxelData, setVoxelData); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(voxelSurfaceStyle, setVoxelSurfaceStyle); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(xTextureURL, setXTextureURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(yTextureURL, setYTextureURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(zTextureURL, setZTextureURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(xNNeighborID, setXNNeighborID); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(yNNeighborID, setYNNeighborID); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(zNNeighborID, setZNNeighborID); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(xPNeighborID, setXPNeighborID); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(yPNeighborID, setYPNeighborID); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(zPNeighborID, setZPNeighborID); +@PolyVox_ENTITY_SET_FROM@ return somethingChanged; } -int PolyVoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, +EntityPropertyFlags PolyVoxEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + +@PolyVox_REQUESTED_PROPS@ + + return requestedProperties; +} + +void PolyVoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@PolyVox_ENTITY_APPEND@ + +} + +int PolyVoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) { @@ -133,68 +132,21 @@ int PolyVoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* dat int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_VOXEL_VOLUME_SIZE, glm::vec3, setVoxelVolumeSize); - READ_ENTITY_PROPERTY(PROP_VOXEL_DATA, QByteArray, setVoxelData); - READ_ENTITY_PROPERTY(PROP_VOXEL_SURFACE_STYLE, uint16_t, setVoxelSurfaceStyle); - READ_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, QString, setXTextureURL); - READ_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, QString, setYTextureURL); - READ_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, QString, setZTextureURL); - READ_ENTITY_PROPERTY(PROP_X_N_NEIGHBOR_ID, EntityItemID, setXNNeighborID); - READ_ENTITY_PROPERTY(PROP_Y_N_NEIGHBOR_ID, EntityItemID, setYNNeighborID); - READ_ENTITY_PROPERTY(PROP_Z_N_NEIGHBOR_ID, EntityItemID, setZNNeighborID); - READ_ENTITY_PROPERTY(PROP_X_P_NEIGHBOR_ID, EntityItemID, setXPNeighborID); - READ_ENTITY_PROPERTY(PROP_Y_P_NEIGHBOR_ID, EntityItemID, setYPNeighborID); - READ_ENTITY_PROPERTY(PROP_Z_P_NEIGHBOR_ID, EntityItemID, setZPNeighborID); +@PolyVox_ENTITY_READ@ return bytesRead; } -EntityPropertyFlags PolyVoxEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - requestedProperties += PROP_VOXEL_VOLUME_SIZE; - requestedProperties += PROP_VOXEL_DATA; - requestedProperties += PROP_VOXEL_SURFACE_STYLE; - requestedProperties += PROP_X_TEXTURE_URL; - requestedProperties += PROP_Y_TEXTURE_URL; - requestedProperties += PROP_Z_TEXTURE_URL; - requestedProperties += PROP_X_N_NEIGHBOR_ID; - requestedProperties += PROP_Y_N_NEIGHBOR_ID; - requestedProperties += PROP_Z_N_NEIGHBOR_ID; - requestedProperties += PROP_X_P_NEIGHBOR_ID; - requestedProperties += PROP_Y_P_NEIGHBOR_ID; - requestedProperties += PROP_Z_P_NEIGHBOR_ID; - return requestedProperties; -} - -void PolyVoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_VOXEL_VOLUME_SIZE, getVoxelVolumeSize()); - APPEND_ENTITY_PROPERTY(PROP_VOXEL_DATA, getVoxelData()); - APPEND_ENTITY_PROPERTY(PROP_VOXEL_SURFACE_STYLE, (uint16_t) getVoxelSurfaceStyle()); - APPEND_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, getXTextureURL()); - APPEND_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, getYTextureURL()); - APPEND_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, getZTextureURL()); - APPEND_ENTITY_PROPERTY(PROP_X_N_NEIGHBOR_ID, getXNNeighborID()); - APPEND_ENTITY_PROPERTY(PROP_Y_N_NEIGHBOR_ID, getYNNeighborID()); - APPEND_ENTITY_PROPERTY(PROP_Z_N_NEIGHBOR_ID, getZNNeighborID()); - APPEND_ENTITY_PROPERTY(PROP_X_P_NEIGHBOR_ID, getXPNeighborID()); - APPEND_ENTITY_PROPERTY(PROP_Y_P_NEIGHBOR_ID, getYPNeighborID()); - APPEND_ENTITY_PROPERTY(PROP_Z_P_NEIGHBOR_ID, getZPNeighborID()); -} - void PolyVoxEntityItem::debugDump() const { - quint64 now = usecTimestampNow(); - qCDebug(entities) << " POLYVOX EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << "PolyVoxEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@PolyVox_ENTITY_DEBUG@ + } void PolyVoxEntityItem::setVoxelData(const QByteArray& voxelData) { @@ -205,56 +157,21 @@ void PolyVoxEntityItem::setVoxelData(const QByteArray& voxelData) { } QByteArray PolyVoxEntityItem::getVoxelData() const { - QByteArray voxelDataCopy; - withReadLock([&] { - voxelDataCopy = _voxelData; + return resultWithReadLock([&] { + return _voxelData; }); - return voxelDataCopy; } - -void PolyVoxEntityItem::setXTextureURL(const QString& xTextureURL) { +void PolyVoxEntityItem::setVoxelSurfaceStyle(uint16_t voxelSurfaceStyle) { withWriteLock([&] { - _needsRenderUpdate |= _xTextureURL != xTextureURL; - _xTextureURL = xTextureURL; + _voxelSurfaceStyle = voxelSurfaceStyle; }); } -QString PolyVoxEntityItem::getXTextureURL() const { - QString result; - withReadLock([&] { - result = _xTextureURL; +uint16_t PolyVoxEntityItem::getVoxelSurfaceStyle() const { + return resultWithReadLock([&] { + return _voxelSurfaceStyle; }); - return result; -} - -void PolyVoxEntityItem::setYTextureURL(const QString& yTextureURL) { - withWriteLock([&] { - _needsRenderUpdate |= _yTextureURL != yTextureURL; - _yTextureURL = yTextureURL; - }); -} - -QString PolyVoxEntityItem::getYTextureURL() const { - QString result; - withReadLock([&] { - result = _yTextureURL; - }); - return result; -} - -void PolyVoxEntityItem::setZTextureURL(const QString& zTextureURL) { - withWriteLock([&] { - _needsRenderUpdate |= _zTextureURL != zTextureURL; - _zTextureURL = zTextureURL; - }); -} -QString PolyVoxEntityItem::getZTextureURL() const { - QString result; - withReadLock([&] { - result = _zTextureURL; - }); - return result; } void PolyVoxEntityItem::setXNNeighborID(const EntityItemID& xNNeighborID) { @@ -264,11 +181,9 @@ void PolyVoxEntityItem::setXNNeighborID(const EntityItemID& xNNeighborID) { } EntityItemID PolyVoxEntityItem::getXNNeighborID() const { - EntityItemID result; - withReadLock([&] { - result = _xNNeighborID; + return resultWithReadLock([&] { + return _xNNeighborID; }); - return result; } void PolyVoxEntityItem::setYNNeighborID(const EntityItemID& yNNeighborID) { @@ -278,11 +193,9 @@ void PolyVoxEntityItem::setYNNeighborID(const EntityItemID& yNNeighborID) { } EntityItemID PolyVoxEntityItem::getYNNeighborID() const { - EntityItemID result; - withReadLock([&] { - result = _yNNeighborID; + return resultWithReadLock([&] { + return _yNNeighborID; }); - return result; } void PolyVoxEntityItem::setZNNeighborID(const EntityItemID& zNNeighborID) { @@ -292,11 +205,9 @@ void PolyVoxEntityItem::setZNNeighborID(const EntityItemID& zNNeighborID) { } EntityItemID PolyVoxEntityItem::getZNNeighborID() const { - EntityItemID result; - withReadLock([&] { - result = _zNNeighborID; + return resultWithReadLock([&] { + return _zNNeighborID; }); - return result; } void PolyVoxEntityItem::setXPNeighborID(const EntityItemID& xPNeighborID) { @@ -306,11 +217,9 @@ void PolyVoxEntityItem::setXPNeighborID(const EntityItemID& xPNeighborID) { } EntityItemID PolyVoxEntityItem::getXPNeighborID() const { - EntityItemID result; - withReadLock([&] { - result = _xPNeighborID; + return resultWithReadLock([&] { + return _xPNeighborID; }); - return result; } void PolyVoxEntityItem::setYPNeighborID(const EntityItemID& yPNeighborID) { @@ -320,11 +229,9 @@ void PolyVoxEntityItem::setYPNeighborID(const EntityItemID& yPNeighborID) { } EntityItemID PolyVoxEntityItem::getYPNeighborID() const { - EntityItemID result; - withReadLock([&] { - result = _yPNeighborID; + return resultWithReadLock([&] { + return _yPNeighborID; }); - return result; } void PolyVoxEntityItem::setZPNeighborID(const EntityItemID& zPNeighborID) { @@ -334,11 +241,9 @@ void PolyVoxEntityItem::setZPNeighborID(const EntityItemID& zPNeighborID) { } EntityItemID PolyVoxEntityItem::getZPNeighborID() const { - EntityItemID result; - withReadLock([&] { - result = _zPNeighborID; + return resultWithReadLock([&] { + return _zPNeighborID; }); - return result; } glm::vec3 PolyVoxEntityItem::getSurfacePositionAdjustment() const { @@ -429,7 +334,7 @@ ShapeType PolyVoxEntityItem::getShapeType() const { } bool PolyVoxEntityItem::isEdged() const { - return isEdged(_voxelSurfaceStyle); + return isEdged((PolyVoxSurfaceStyle)_voxelSurfaceStyle); } std::array PolyVoxEntityItem::getNNeigborIDs() const { diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h.in similarity index 51% rename from libraries/entities/src/PolyVoxEntityItem.h rename to libraries/entities/src/PolyVoxEntityItem.h.in index 163e03cd55..15bfceaec2 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h.in @@ -15,31 +15,13 @@ #include "EntityItem.h" class PolyVoxEntityItem : public EntityItem { - public: +public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); PolyVoxEntityItem(const EntityItemID& entityItemID); ALLOW_INSTANTIATION // This class can be instantiated - - // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - virtual bool setSubClassProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; + ENTITY_PROPERTY_SUBCLASS_METHODS // never have a ray intersection pick a PolyVoxEntityItem. virtual bool supportsDetailedIntersection() const override { return true; } @@ -52,55 +34,62 @@ class PolyVoxEntityItem : public EntityItem { float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) const override { return false; } - virtual void debugDump() const override; - - virtual void setVoxelVolumeSize(const glm::vec3& voxelVolumeSize); - virtual glm::vec3 getVoxelVolumeSize() const; - - virtual void setVoxelData(const QByteArray& voxelData); - virtual QByteArray getVoxelData() const; - virtual int getOnCount() const { return 0; } /*@jsdoc - *

    The surface of a {@link Entities.EntityProperties-PolyVox|PolyVox} entity may be one of the following styles:

    - * - * - * - * - * - * - * - * - * - * - *
    ValueTypeDescription
    0Marching cubes.Chamfered edges. Open volume. - * Joins neighboring PolyVox entities reasonably well.
    1Cubic.Square edges. Open volume. - * Joins neighboring PolyVox entities cleanly.
    2Edged cubic.Square edges. Enclosed volume. - * Joins neighboring PolyVox entities cleanly.
    3Edged marching cubes.Chamfered edges. Enclosed volume. - * Doesn't join neighboring PolyVox entities.
    - * @typedef {number} Entities.PolyVoxSurfaceStyle - */ + *

    The surface of a {@link Entities.EntityProperties-PolyVox|PolyVox} entity may be one of the following styles:

    + * + * + * + * + * + * + * + * + * + * + *
    ValueTypeDescription
    0Marching cubes.Chamfered edges. Open volume. + * Joins neighboring PolyVox entities reasonably well.
    1Cubic.Square edges. Open volume. + * Joins neighboring PolyVox entities cleanly.
    2Edged cubic.Square edges. Enclosed volume. + * Joins neighboring PolyVox entities cleanly.
    3Edged marching cubes.Chamfered edges. Enclosed volume. + * Doesn't join neighboring PolyVox entities.
    + * @typedef {number} Entities.PolyVoxSurfaceStyle + */ enum PolyVoxSurfaceStyle { SURFACE_MARCHING_CUBES, SURFACE_CUBIC, SURFACE_EDGED_CUBIC, SURFACE_EDGED_MARCHING_CUBES }; + static bool isEdged(PolyVoxSurfaceStyle surfaceStyle); - - virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { _voxelSurfaceStyle = voxelSurfaceStyle; } - // this other version of setVoxelSurfaceStyle is needed for SET_ENTITY_PROPERTY_FROM_PROPERTIES - void setVoxelSurfaceStyle(uint16_t voxelSurfaceStyle) { setVoxelSurfaceStyle((PolyVoxSurfaceStyle) voxelSurfaceStyle); } - virtual PolyVoxSurfaceStyle getVoxelSurfaceStyle() const { return _voxelSurfaceStyle; } - static const glm::vec3 DEFAULT_VOXEL_VOLUME_SIZE; static const float MAX_VOXEL_DIMENSION; static const QByteArray DEFAULT_VOXEL_DATA; static const PolyVoxSurfaceStyle DEFAULT_VOXEL_SURFACE_STYLE; + QByteArray getVoxelData() const; + virtual void setVoxelData(const QByteArray& voxelData); + glm::vec3 getVoxelVolumeSize() const; + virtual void setVoxelVolumeSize(const glm::vec3& voxelVolumeSize); + uint16_t getVoxelSurfaceStyle() const; + virtual void setVoxelSurfaceStyle(uint16_t voxelSurfaceStyle); + + EntityItemID getXNNeighborID() const; + virtual void setXNNeighborID(const EntityItemID& value); + EntityItemID getYNNeighborID() const; + virtual void setYNNeighborID(const EntityItemID& value); + EntityItemID getZNNeighborID() const; + virtual void setZNNeighborID(const EntityItemID& value); + EntityItemID getXPNeighborID() const; + virtual void setXPNeighborID(const EntityItemID& value); + EntityItemID getYPNeighborID() const; + virtual void setYPNeighborID(const EntityItemID& value); + EntityItemID getZPNeighborID() const; + virtual void setZPNeighborID(const EntityItemID& value); + glm::vec3 voxelCoordsToWorldCoords(const glm::vec3& voxelCoords) const; glm::vec3 worldCoordsToVoxelCoords(const glm::vec3& worldCoords) const; glm::vec3 voxelCoordsToLocalCoords(const glm::vec3& voxelCoords) const; @@ -125,40 +114,10 @@ class PolyVoxEntityItem : public EntityItem { static QByteArray makeEmptyVoxelData(quint16 voxelXSize = 16, quint16 voxelYSize = 16, quint16 voxelZSize = 16); static const QString DEFAULT_X_TEXTURE_URL; - void setXTextureURL(const QString& xTextureURL); - QString getXTextureURL() const; - static const QString DEFAULT_Y_TEXTURE_URL; - void setYTextureURL(const QString& yTextureURL); - QString getYTextureURL() const; - static const QString DEFAULT_Z_TEXTURE_URL; - void setZTextureURL(const QString& zTextureURL); - QString getZTextureURL() const; - - virtual void setXNNeighborID(const EntityItemID& xNNeighborID); - void setXNNeighborID(const QString& xNNeighborID); - EntityItemID getXNNeighborID() const; - virtual void setYNNeighborID(const EntityItemID& yNNeighborID); - void setYNNeighborID(const QString& yNNeighborID); - EntityItemID getYNNeighborID() const; - virtual void setZNNeighborID(const EntityItemID& zNNeighborID); - void setZNNeighborID(const QString& zNNeighborID); - EntityItemID getZNNeighborID() const; std::array getNNeigborIDs() const; - - - virtual void setXPNeighborID(const EntityItemID& xPNeighborID); - void setXPNeighborID(const QString& xPNeighborID); - EntityItemID getXPNeighborID() const; - virtual void setYPNeighborID(const EntityItemID& yPNeighborID); - void setYPNeighborID(const QString& yPNeighborID); - EntityItemID getYPNeighborID() const; - virtual void setZPNeighborID(const EntityItemID& zPNeighborID); - void setZPNeighborID(const QString& zPNeighborID); - EntityItemID getZPNeighborID() const; - std::array getPNeigborIDs() const; glm::vec3 getSurfacePositionAdjustment() const; @@ -173,27 +132,11 @@ class PolyVoxEntityItem : public EntityItem { glm::mat4 localToVoxelMatrix() const; protected: + +@PolyVox_ENTITY_PROPS@ + void setVoxelDataDirty(bool value) { withWriteLock([&] { _voxelDataDirty = value; }); } - - glm::vec3 _voxelVolumeSize { DEFAULT_VOXEL_VOLUME_SIZE }; // this is always 3 bytes - - QByteArray _voxelData { DEFAULT_VOXEL_DATA }; bool _voxelDataDirty { true }; // _voxelData has changed, things that depend on it should be updated - - PolyVoxSurfaceStyle _voxelSurfaceStyle { DEFAULT_VOXEL_SURFACE_STYLE }; - - QString _xTextureURL { DEFAULT_X_TEXTURE_URL }; - QString _yTextureURL { DEFAULT_Y_TEXTURE_URL }; - QString _zTextureURL { DEFAULT_Z_TEXTURE_URL }; - - // for non-edged surface styles, these are used to compute the high-axis edges - EntityItemID _xNNeighborID{UNKNOWN_ENTITY_ID}; - EntityItemID _yNNeighborID{UNKNOWN_ENTITY_ID}; - EntityItemID _zNNeighborID{UNKNOWN_ENTITY_ID}; - - EntityItemID _xPNeighborID{UNKNOWN_ENTITY_ID}; - EntityItemID _yPNeighborID{UNKNOWN_ENTITY_ID}; - EntityItemID _zPNeighborID{UNKNOWN_ENTITY_ID}; }; #endif // hifi_PolyVoxEntityItem_h diff --git a/libraries/entities/src/ProceduralParticleEffectEntityItem.cpp b/libraries/entities/src/ProceduralParticleEffectEntityItem.cpp deleted file mode 100644 index a8a601d6f6..0000000000 --- a/libraries/entities/src/ProceduralParticleEffectEntityItem.cpp +++ /dev/null @@ -1,174 +0,0 @@ -// -// ProceduralParticleEffectEntityItem.cpp -// libraries/entities/src -// -// Created by HifiExperiements on 11/19/23 -// Copyright 2023 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 "ProceduralParticleEffectEntityItem.h" - -#include "EntityTree.h" -#include "EntityTreeElement.h" -#include "EntitiesLogging.h" -#include "EntityScriptingInterface.h" - -EntityItemPointer ProceduralParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - std::shared_ptr entity(new ProceduralParticleEffectEntityItem(entityID), [](ProceduralParticleEffectEntityItem* ptr) { ptr->deleteLater(); }); - entity->setProperties(properties); - return entity; -} - -// our non-pure virtual subclass for now... -ProceduralParticleEffectEntityItem::ProceduralParticleEffectEntityItem(const EntityItemID& entityItemID) : - EntityItem(entityItemID) -{ - _type = EntityTypes::ProceduralParticleEffect; -} - -EntityItemProperties ProceduralParticleEffectEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { - EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(numParticles, getNumParticles); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(numTrianglesPerParticle, getNumTrianglesPerParticle); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(numUpdateProps, getNumUpdateProps); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleTransparent, getParticleTransparent); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleUpdateData, getParticleUpdateData); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleRenderData, getParticleRenderData); - - return properties; -} - -bool ProceduralParticleEffectEntityItem::setSubClassProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(numParticles, setNumParticles); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(numTrianglesPerParticle, setNumTrianglesPerParticle); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(numUpdateProps, setNumUpdateProps); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleTransparent, setParticleTransparent); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleUpdateData, setParticleUpdateData); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleRenderData, setParticleRenderData); - - return somethingChanged; -} - -int ProceduralParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, uint32_t, setNumParticles); - READ_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, uint8_t, setNumTrianglesPerParticle); - READ_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, uint8_t, setNumUpdateProps); - READ_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, bool, setParticleTransparent); - READ_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, QString, setParticleUpdateData); - READ_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, QString, setParticleRenderData); - - return bytesRead; -} - -EntityPropertyFlags ProceduralParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - - requestedProperties += PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES; - requestedProperties += PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER; - requestedProperties += PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS; - requestedProperties += PROP_PROCEDURAL_PARTICLE_TRANSPARENT; - requestedProperties += PROP_PROCEDURAL_PARTCILE_UPDATE_DATA; - requestedProperties += PROP_PROCEDURAL_PARTCILE_RENDER_DATA; - - return requestedProperties; -} - -void ProceduralParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES, getNumParticles()); - APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER, getNumTrianglesPerParticle()); - APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS, getNumUpdateProps()); - APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTICLE_TRANSPARENT, getParticleTransparent()); - APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTCILE_UPDATE_DATA, getParticleUpdateData()); - APPEND_ENTITY_PROPERTY(PROP_PROCEDURAL_PARTCILE_RENDER_DATA, getParticleRenderData()); -} - -void ProceduralParticleEffectEntityItem::debugDump() const { - quint64 now = usecTimestampNow(); - qCDebug(entities) << "PROC PA EFFECT EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); -} - -uint32_t ProceduralParticleEffectEntityItem::getNumParticles() const { - return resultWithReadLock([&] { return _numParticles; }); -} - -void ProceduralParticleEffectEntityItem::setNumParticles(uint32_t numParticles) { - withWriteLock([&] { - _needsRenderUpdate |= _numParticles != numParticles; - _numParticles = numParticles; - }); -} - -uint8_t ProceduralParticleEffectEntityItem::getNumTrianglesPerParticle() const { - return resultWithReadLock([&] { return _numTrianglesPerParticle; }); -} - -void ProceduralParticleEffectEntityItem::setNumTrianglesPerParticle(uint8_t numTrianglesPerParticle) { - withWriteLock([&] { - _needsRenderUpdate |= _numTrianglesPerParticle != numTrianglesPerParticle; - _numTrianglesPerParticle = numTrianglesPerParticle; - }); -} - -uint8_t ProceduralParticleEffectEntityItem::getNumUpdateProps() const { - return resultWithReadLock([&] { return _numUpdateProps; }); -} - -void ProceduralParticleEffectEntityItem::setNumUpdateProps(uint8_t numUpdateProps) { - withWriteLock([&] { - _needsRenderUpdate |= _numUpdateProps != numUpdateProps; - _numUpdateProps = numUpdateProps; - }); -} - -void ProceduralParticleEffectEntityItem::setParticleTransparent(bool particleTransparent) { - withWriteLock([&] { - _needsRenderUpdate |= _particleTransparent != particleTransparent; - _particleTransparent = particleTransparent; - }); -} - -QString ProceduralParticleEffectEntityItem::getParticleUpdateData() const { - return resultWithReadLock([&] { return _particleUpdateData; }); -} - -void ProceduralParticleEffectEntityItem::setParticleUpdateData(const QString& particleUpdateData) { - withWriteLock([&] { - _needsRenderUpdate |= _particleUpdateData != particleUpdateData; - _particleUpdateData = particleUpdateData; - }); -} - -QString ProceduralParticleEffectEntityItem::getParticleRenderData() const { - return resultWithReadLock([&] { return _particleRenderData; }); -} - -void ProceduralParticleEffectEntityItem::setParticleRenderData(const QString& particleRenderData) { - withWriteLock([&] { - _needsRenderUpdate |= _particleRenderData != particleRenderData; - _particleRenderData = particleRenderData; - }); -} \ No newline at end of file diff --git a/libraries/entities/src/ProceduralParticleEffectEntityItem.cpp.in b/libraries/entities/src/ProceduralParticleEffectEntityItem.cpp.in new file mode 100644 index 0000000000..d6bbbf4b68 --- /dev/null +++ b/libraries/entities/src/ProceduralParticleEffectEntityItem.cpp.in @@ -0,0 +1,93 @@ +// +// ProceduralParticleEffectEntityItem.cpp +// libraries/entities/src +// +// Created by HifiExperiements on 11/19/23 +// Copyright 2023 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 "ProceduralParticleEffectEntityItem.h" + +#include "EntityTree.h" +#include "EntityTreeElement.h" +#include "EntitiesLogging.h" +#include "EntityScriptingInterface.h" + +EntityItemPointer ProceduralParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + std::shared_ptr entity(new ProceduralParticleEffectEntityItem(entityID), [](ProceduralParticleEffectEntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +// our non-pure virtual subclass for now... +ProceduralParticleEffectEntityItem::ProceduralParticleEffectEntityItem(const EntityItemID& entityItemID) : + EntityItem(entityItemID) +{ + _type = EntityTypes::ProceduralParticleEffect; +} + +EntityItemProperties ProceduralParticleEffectEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + +@ProceduralParticleEffect_ENTITY_COPY_TO@ + + return properties; +} + +bool ProceduralParticleEffectEntityItem::setSubClassProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@ProceduralParticleEffect_ENTITY_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags ProceduralParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + +@ProceduralParticleEffect_REQUESTED_PROPS@ + + return requestedProperties; +} + +void ProceduralParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@ProceduralParticleEffect_ENTITY_APPEND@ + +} + +int ProceduralParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@ProceduralParticleEffect_ENTITY_READ@ + + return bytesRead; +} + +void ProceduralParticleEffectEntityItem::debugDump() const { + qCDebug(entities) << "ProceduralParticleEffectEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@ProceduralParticleEffect_ENTITY_DEBUG@ + +} diff --git a/libraries/entities/src/ProceduralParticleEffectEntityItem.h b/libraries/entities/src/ProceduralParticleEffectEntityItem.h deleted file mode 100644 index 98ae8b53df..0000000000 --- a/libraries/entities/src/ProceduralParticleEffectEntityItem.h +++ /dev/null @@ -1,88 +0,0 @@ -// -// ProceduralParticleEffectEntityItem.h -// libraries/entities/src -// -// Created by HifiExperiements on 11/19/23 -// Copyright 2023 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_ProceduralParticleEffectEntityItem_h -#define hifi_ProceduralParticleEffectEntityItem_h - -#include "EntityItem.h" - -namespace particle { - static const uint32_t DEFAULT_NUM_PROCEDURAL_PARTICLES = 10000; - static const uint32_t MAXIMUM_NUM_PROCEDURAL_PARTICLES = 1024 * 1024; - static const uint8_t DEFAULT_NUM_TRIS_PER = 1; - static const uint8_t MINIMUM_TRIS_PER = 1; - static const uint8_t MAXIMUM_TRIS_PER = 15; - static const uint8_t DEFAULT_NUM_UPDATE_PROPS = 0; - static const uint8_t MINIMUM_NUM_UPDATE_PROPS = 0; - static const uint8_t MAXIMUM_NUM_UPDATE_PROPS = 5; -} - -class ProceduralParticleEffectEntityItem : public EntityItem { -public: - ALLOW_INSTANTIATION // This class can be instantiated - - static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - - ProceduralParticleEffectEntityItem(const EntityItemID& entityItemID); - - // methods for getting/setting all properties of this entity - virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - virtual bool setSubClassProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - bool shouldBePhysical() const override { return false; } - - virtual void debugDump() const override; - - virtual bool supportsDetailedIntersection() const override { return false; } - - uint32_t getNumParticles() const; - void setNumParticles(uint32_t numParticles); - - uint8_t getNumTrianglesPerParticle() const; - void setNumTrianglesPerParticle(uint8_t numTrianglesPerParticle); - - uint8_t getNumUpdateProps() const; - void setNumUpdateProps(uint8_t numUpdateProps); - - bool getParticleTransparent() const { return _particleTransparent; } - void setParticleTransparent(bool particleTransparent); - - QString getParticleUpdateData() const; - void setParticleUpdateData(const QString& particleUpdateData); - - QString getParticleRenderData() const; - void setParticleRenderData(const QString& particleRenderData); - -protected: - uint32_t _numParticles; - uint8_t _numTrianglesPerParticle; - uint8_t _numUpdateProps; - bool _particleTransparent; - QString _particleUpdateData; - QString _particleRenderData; -}; - -#endif // hifi_ProceduralParticleEffectEntityItem_h diff --git a/libraries/entities/src/ProceduralParticleEffectEntityItem.h.in b/libraries/entities/src/ProceduralParticleEffectEntityItem.h.in new file mode 100644 index 0000000000..996bc83233 --- /dev/null +++ b/libraries/entities/src/ProceduralParticleEffectEntityItem.h.in @@ -0,0 +1,47 @@ +// +// ProceduralParticleEffectEntityItem.h +// libraries/entities/src +// +// Created by HifiExperiements on 11/19/23 +// Copyright 2023 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_ProceduralParticleEffectEntityItem_h +#define hifi_ProceduralParticleEffectEntityItem_h + +#include "EntityItem.h" + +namespace particle { + static const uint32_t DEFAULT_NUM_PROCEDURAL_PARTICLES = 10000; + static const uint32_t MAXIMUM_NUM_PROCEDURAL_PARTICLES = 1024 * 1024; + static const uint8_t DEFAULT_NUM_TRIS_PER = 1; + static const uint8_t MINIMUM_TRIS_PER = 1; + static const uint8_t MAXIMUM_TRIS_PER = 15; + static const uint8_t DEFAULT_NUM_UPDATE_PROPS = 0; + static const uint8_t MINIMUM_NUM_UPDATE_PROPS = 0; + static const uint8_t MAXIMUM_NUM_UPDATE_PROPS = 5; +} + +class ProceduralParticleEffectEntityItem : public EntityItem { +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + ProceduralParticleEffectEntityItem(const EntityItemID& entityItemID);\ + + ALLOW_INSTANTIATION // This class can be instantiated + ENTITY_PROPERTY_SUBCLASS_METHODS + + bool shouldBePhysical() const override { return false; } + + virtual bool supportsDetailedIntersection() const override { return false; } + +protected: + +@ProceduralParticleEffect_ENTITY_PROPS@ + +}; + +#endif // hifi_ProceduralParticleEffectEntityItem_h diff --git a/libraries/entities/src/PropertyGroup.h b/libraries/entities/src/PropertyGroup.h index b73c2dad2a..57f6ef567c 100644 --- a/libraries/entities/src/PropertyGroup.h +++ b/libraries/entities/src/PropertyGroup.h @@ -59,14 +59,6 @@ public: virtual bool setProperties(const EntityItemProperties& properties) = 0; virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const = 0; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const = 0; virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, diff --git a/libraries/entities/src/PulsePropertyGroup.cpp b/libraries/entities/src/PulsePropertyGroup.cpp deleted file mode 100644 index ab61a1f8ad..0000000000 --- a/libraries/entities/src/PulsePropertyGroup.cpp +++ /dev/null @@ -1,252 +0,0 @@ -// -// PulsePropertyGroup.cpp -// -// Created by Sam Gondelman on 1/15/19 -// Copyright 2019 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#include "PulsePropertyGroup.h" - -#include - -#include "EntityItemProperties.h" -#include "EntityItemPropertiesMacros.h" - -QHash stringToPulseModeLookup; - -void addPulseMode(PulseMode mode) { - stringToPulseModeLookup[PulseModeHelpers::getNameForPulseMode(mode)] = mode; -} - -void buildStringToPulseModeLookup() { - addPulseMode(PulseMode::NONE); - addPulseMode(PulseMode::IN_PHASE); - addPulseMode(PulseMode::OUT_PHASE); -} - -QString PulsePropertyGroup::getColorModeAsString() const { - return PulseModeHelpers::getNameForPulseMode(_colorMode); -} - -void PulsePropertyGroup::setColorModeFromString(const QString& pulseMode) { - if (stringToPulseModeLookup.empty()) { - buildStringToPulseModeLookup(); - } - auto pulseModeItr = stringToPulseModeLookup.find(pulseMode.toLower()); - if (pulseModeItr != stringToPulseModeLookup.end()) { - _colorMode = pulseModeItr.value(); - _colorModeChanged = true; - } -} - -QString PulsePropertyGroup::getAlphaModeAsString() const { - return PulseModeHelpers::getNameForPulseMode(_alphaMode); -} - -void PulsePropertyGroup::setAlphaModeFromString(const QString& pulseMode) { - if (stringToPulseModeLookup.empty()) { - buildStringToPulseModeLookup(); - } - auto pulseModeItr = stringToPulseModeLookup.find(pulseMode.toLower()); - if (pulseModeItr != stringToPulseModeLookup.end()) { - _alphaMode = pulseModeItr.value(); - _alphaModeChanged = true; - } -} - -void PulsePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_PULSE_MIN, Pulse, pulse, Min, min); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_PULSE_MAX, Pulse, pulse, Max, max); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_PULSE_PERIOD, Pulse, pulse, Period, period); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_PULSE_COLOR_MODE, Pulse, pulse, ColorMode, colorMode, getColorModeAsString); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_PULSE_ALPHA_MODE, Pulse, pulse, AlphaMode, alphaMode, getAlphaModeAsString); -} - -void PulsePropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(pulse, min, float, setMin); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(pulse, max, float, setMax); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(pulse, period, float, setPeriod); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(pulse, colorMode, ColorMode); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(pulse, alphaMode, AlphaMode); -} - -void PulsePropertyGroup::merge(const PulsePropertyGroup& other) { - COPY_PROPERTY_IF_CHANGED(min); - COPY_PROPERTY_IF_CHANGED(max); - COPY_PROPERTY_IF_CHANGED(period); - COPY_PROPERTY_IF_CHANGED(colorMode); - COPY_PROPERTY_IF_CHANGED(alphaMode); -} - -void PulsePropertyGroup::debugDump() const { - qCDebug(entities) << " PulsePropertyGroup: ---------------------------------------------"; - qCDebug(entities) << " _min:" << _min; - qCDebug(entities) << " _max:" << _max; - qCDebug(entities) << " _period:" << _period; - qCDebug(entities) << " _colorMode:" << getColorModeAsString(); - qCDebug(entities) << " _alphaMode:" << getAlphaModeAsString(); -} - -void PulsePropertyGroup::listChangedProperties(QList& out) { - if (minChanged()) { - out << "pulse-min"; - } - if (maxChanged()) { - out << "pulse-max"; - } - if (periodChanged()) { - out << "pulse-period"; - } - if (colorModeChanged()) { - out << "pulse-colorMode"; - } - if (alphaModeChanged()) { - out << "pulse-alphaMode"; - } -} - -bool PulsePropertyGroup::appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_PULSE_MIN, getMin()); - APPEND_ENTITY_PROPERTY(PROP_PULSE_MAX, getMax()); - APPEND_ENTITY_PROPERTY(PROP_PULSE_PERIOD, getPeriod()); - APPEND_ENTITY_PROPERTY(PROP_PULSE_COLOR_MODE, (uint32_t)getColorMode()); - APPEND_ENTITY_PROPERTY(PROP_PULSE_ALPHA_MODE, (uint32_t)getAlphaMode()); - - return true; -} - -bool PulsePropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt , int& processedBytes) { - - int bytesRead = 0; - bool overwriteLocalData = true; - bool somethingChanged = false; - - READ_ENTITY_PROPERTY(PROP_PULSE_MIN, float, setMin); - READ_ENTITY_PROPERTY(PROP_PULSE_MAX, float, setMax); - READ_ENTITY_PROPERTY(PROP_PULSE_PERIOD, float, setPeriod); - READ_ENTITY_PROPERTY(PROP_PULSE_COLOR_MODE, PulseMode, setColorMode); - READ_ENTITY_PROPERTY(PROP_PULSE_ALPHA_MODE, PulseMode, setAlphaMode); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_MIN, Min); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_MAX, Max); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_PERIOD, Period); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_COLOR_MODE, ColorMode); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_ALPHA_MODE, AlphaMode); - - processedBytes += bytesRead; - - Q_UNUSED(somethingChanged); - - return true; -} - -void PulsePropertyGroup::markAllChanged() { - _minChanged = true; - _maxChanged = true; - _periodChanged = true; - _colorModeChanged = true; - _alphaModeChanged = true; -} - -EntityPropertyFlags PulsePropertyGroup::getChangedProperties() const { - EntityPropertyFlags changedProperties; - - CHECK_PROPERTY_CHANGE(PROP_PULSE_MIN, min); - CHECK_PROPERTY_CHANGE(PROP_PULSE_MAX, max); - CHECK_PROPERTY_CHANGE(PROP_PULSE_PERIOD, period); - CHECK_PROPERTY_CHANGE(PROP_PULSE_COLOR_MODE, colorMode); - CHECK_PROPERTY_CHANGE(PROP_PULSE_ALPHA_MODE, alphaMode); - - return changedProperties; -} - -void PulsePropertyGroup::getProperties(EntityItemProperties& properties) const { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, Min, getMin); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, Max, getMax); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, Period, getPeriod); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, ColorMode, getColorMode); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, AlphaMode, getAlphaMode); -} - -bool PulsePropertyGroup::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, Min, min, setMin); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, Max, max, setMax); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, Period, period, setPeriod); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, ColorMode, colorMode, setColorMode); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, AlphaMode, alphaMode, setAlphaMode); - - return somethingChanged; -} - -EntityPropertyFlags PulsePropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties; - - requestedProperties += PROP_PULSE_MIN; - requestedProperties += PROP_PULSE_MAX; - requestedProperties += PROP_PULSE_PERIOD; - requestedProperties += PROP_PULSE_COLOR_MODE; - requestedProperties += PROP_PULSE_ALPHA_MODE; - - return requestedProperties; -} - -void PulsePropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_PULSE_MIN, getMin()); - APPEND_ENTITY_PROPERTY(PROP_PULSE_MAX, getMax()); - APPEND_ENTITY_PROPERTY(PROP_PULSE_PERIOD, getPeriod()); - APPEND_ENTITY_PROPERTY(PROP_PULSE_COLOR_MODE, (uint32_t)getColorMode()); - APPEND_ENTITY_PROPERTY(PROP_PULSE_ALPHA_MODE, (uint32_t)getAlphaMode()); -} - -int PulsePropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_PULSE_MIN, float, setMin); - READ_ENTITY_PROPERTY(PROP_PULSE_MAX, float, setMax); - READ_ENTITY_PROPERTY(PROP_PULSE_PERIOD, float, setPeriod); - READ_ENTITY_PROPERTY(PROP_PULSE_COLOR_MODE, PulseMode, setColorMode); - READ_ENTITY_PROPERTY(PROP_PULSE_ALPHA_MODE, PulseMode, setAlphaMode); - - return bytesRead; -} - -bool PulsePropertyGroup::operator==(const PulsePropertyGroup& a) const { - return (a._min == _min) && - (a._max == _max) && - (a._period == _period) && - (a._colorMode == _colorMode) && - (a._alphaMode == _alphaMode); -} \ No newline at end of file diff --git a/libraries/entities/src/PulsePropertyGroup.cpp.in b/libraries/entities/src/PulsePropertyGroup.cpp.in new file mode 100644 index 0000000000..ddd4cb71d0 --- /dev/null +++ b/libraries/entities/src/PulsePropertyGroup.cpp.in @@ -0,0 +1,183 @@ +// +// PulsePropertyGroup.cpp +// +// Created by Sam Gondelman on 1/15/19 +// Copyright 2019 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "PulsePropertyGroup.h" + +#include + +#include "EntityItemProperties.h" + +QHash stringToPulseModeLookup; + +void addPulseMode(PulseMode mode) { + stringToPulseModeLookup[PulseModeHelpers::getNameForPulseMode(mode)] = mode; +} + +void buildStringToPulseModeLookup() { + addPulseMode(PulseMode::NONE); + addPulseMode(PulseMode::IN_PHASE); + addPulseMode(PulseMode::OUT_PHASE); +} + +QString PulsePropertyGroup::getColorModeAsString() const { + return PulseModeHelpers::getNameForPulseMode(_colorMode); +} + +void PulsePropertyGroup::setColorModeFromString(const QString& pulseMode) { + if (stringToPulseModeLookup.empty()) { + buildStringToPulseModeLookup(); + } + auto pulseModeItr = stringToPulseModeLookup.find(pulseMode.toLower()); + if (pulseModeItr != stringToPulseModeLookup.end()) { + _colorMode = pulseModeItr.value(); + _colorModeChanged = true; + } +} + +QString PulsePropertyGroup::getAlphaModeAsString() const { + return PulseModeHelpers::getNameForPulseMode(_alphaMode); +} + +void PulsePropertyGroup::setAlphaModeFromString(const QString& pulseMode) { + if (stringToPulseModeLookup.empty()) { + buildStringToPulseModeLookup(); + } + auto pulseModeItr = stringToPulseModeLookup.find(pulseMode.toLower()); + if (pulseModeItr != stringToPulseModeLookup.end()) { + _alphaMode = pulseModeItr.value(); + _alphaModeChanged = true; + } +} + +void PulsePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + +@Pulse_GROUP_COPY_TO_SCRIPT@ + +} + +void PulsePropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + +@Pulse_GROUP_COPY_FROM_SCRIPT@ + +} + +void PulsePropertyGroup::merge(const PulsePropertyGroup& other) { + +@Pulse_GROUP_MERGE@ + +} + +void PulsePropertyGroup::debugDump() const { + +@Pulse_GROUP_DEBUG_DUMP@ + +} + +void PulsePropertyGroup::listChangedProperties(QList& out) { + +@Pulse_GROUP_LIST_CHANGED@ + +} + +bool PulsePropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Pulse_GROUP_APPEND@ + + return successPropertyFits; +} + +bool PulsePropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + +@Pulse_GROUP_READ@ + +@Pulse_GROUP_DECODE_CHANGED@ + + processedBytes += bytesRead; + + return somethingChanged; +} + +void PulsePropertyGroup::markAllChanged() { + +@Pulse_GROUP_MARK_CHANGED@ + +} + +EntityPropertyFlags PulsePropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + +@Pulse_GROUP_CHANGED_PROPERTIES@ + + return changedProperties; +} + +void PulsePropertyGroup::getProperties(EntityItemProperties& properties) const { + +@Pulse_GROUP_COPY_TO@ + +} + +bool PulsePropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Pulse_GROUP_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags PulsePropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + +@Pulse_REQUESTED_PROPS@ + + return requestedProperties; +} + +int PulsePropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Pulse_GROUP_READ@ + + return bytesRead; +} + +void PulsePropertyGroup::addPropertyMap(QHash& _propertyInfos, + QHash& _enumsToPropertyStrings) { + +@Pulse_GROUP_ADD_TO_MAP@ + +} + +bool PulsePropertyGroup::operator==(const PulsePropertyGroup& a) const { + return (a._min == _min) && + (a._max == _max) && + (a._period == _period) && + (a._colorMode == _colorMode) && + (a._alphaMode == _alphaMode); +} diff --git a/libraries/entities/src/PulsePropertyGroup.h b/libraries/entities/src/PulsePropertyGroup.h deleted file mode 100644 index 649005b970..0000000000 --- a/libraries/entities/src/PulsePropertyGroup.h +++ /dev/null @@ -1,100 +0,0 @@ -// -// PulsePropertyGroup.h -// -// Created by Sam Gondelman on 1/15/19 -// Copyright 2019 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#ifndef hifi_PulsePropertyGroup_h -#define hifi_PulsePropertyGroup_h - -#include - -#include - -#include "PropertyGroup.h" -#include "EntityItemPropertiesMacros.h" - -class EntityItemProperties; -class EncodeBitstreamParams; -class OctreePacketData; -class ReadBitstreamToTreeParams; -class ScriptEngine; -class ScriptValue; - -/*@jsdoc - * A color and alpha pulse that an entity may have. - * @typedef {object} Entities.Pulse - * @property {number} min=0 - The minimum value of the pulse multiplier. - * @property {number} max=1 - The maximum value of the pulse multiplier. - * @property {number} period=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from - * min to max, then max to min in one period. - * @property {Entities.PulseMode} colorMode="none" - If "in", the color is pulsed in phase with the pulse period; if "out" - * the color is pulsed out of phase with the pulse period. - * @property {Entities.PulseMode} alphaMode="none" - If "in", the alpha is pulsed in phase with the pulse period; if "out" - * the alpha is pulsed out of phase with the pulse period. - */ -class PulsePropertyGroup : public PropertyGroup { -public: - // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const override; - virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; - - void merge(const PulsePropertyGroup& other); - - virtual void debugDump() const override; - virtual void listChangedProperties(QList& out) override; - - virtual bool appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt, int& processedBytes) override; - virtual void markAllChanged() override; - virtual EntityPropertyFlags getChangedProperties() const override; - - // EntityItem related helpers - // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const override; - - // returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - bool operator==(const PulsePropertyGroup& a) const; - bool operator!=(const PulsePropertyGroup& a) const { return !(*this == a); } - - DEFINE_PROPERTY(PROP_PULSE_MIN, Min, min, float, 0.0f); - DEFINE_PROPERTY(PROP_PULSE_MAX, Max, max, float, 1.0f); - DEFINE_PROPERTY(PROP_PULSE_PERIOD, Period, period, float, 1.0f); - DEFINE_PROPERTY_REF_ENUM(PROP_PULSE_COLOR_MODE, ColorMode, colorMode, PulseMode, PulseMode::NONE); - DEFINE_PROPERTY_REF_ENUM(PROP_PULSE_ALPHA_MODE, AlphaMode, alphaMode, PulseMode, PulseMode::NONE); -}; - -#endif // hifi_PulsePropertyGroup_h diff --git a/libraries/entities/src/PulsePropertyGroup.h.in b/libraries/entities/src/PulsePropertyGroup.h.in new file mode 100644 index 0000000000..59fc84143f --- /dev/null +++ b/libraries/entities/src/PulsePropertyGroup.h.in @@ -0,0 +1,55 @@ +// +// PulsePropertyGroup.h +// +// Created by Sam Gondelman on 1/15/19 +// Copyright 2019 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_PulsePropertyGroup_h +#define hifi_PulsePropertyGroup_h + +#include + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +/*@jsdoc + * A color and alpha pulse that an entity may have. + * @typedef {object} Entities.Pulse + * @property {number} min=0 - The minimum value of the pulse multiplier. + * @property {number} max=1 - The maximum value of the pulse multiplier. + * @property {number} period=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from + * min to max, then max to min in one period. + * @property {Entities.PulseMode} colorMode="none" - If "in", the color is pulsed in phase with the pulse period; if "out" + * the color is pulsed out of phase with the pulse period. + * @property {Entities.PulseMode} alphaMode="none" - If "in", the alpha is pulsed in phase with the pulse period; if "out" + * the alpha is pulsed out of phase with the pulse period. + */ +class PulsePropertyGroup : public PropertyGroup { +public: + ENTITY_PROPERTY_GROUP_METHODS(PulsePropertyGroup) + + bool operator==(const PulsePropertyGroup& a) const; + bool operator!=(const PulsePropertyGroup& a) const { return !(*this == a); } + +protected: + +@Pulse_GROUP_PROPS@ + +}; + +#endif // hifi_PulsePropertyGroup_h diff --git a/libraries/entities/src/RingGizmoPropertyGroup.cpp b/libraries/entities/src/RingGizmoPropertyGroup.cpp deleted file mode 100644 index 68021f44a2..0000000000 --- a/libraries/entities/src/RingGizmoPropertyGroup.cpp +++ /dev/null @@ -1,492 +0,0 @@ -// -// Created by Sam Gondelman on 1/22/19 -// Copyright 2019 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#include "RingGizmoPropertyGroup.h" - -#include - -#include "EntityItemProperties.h" -#include "EntityItemPropertiesMacros.h" - -const float RingGizmoPropertyGroup::MIN_ANGLE = 0.0f; -const float RingGizmoPropertyGroup::MAX_ANGLE = 360.0f; -const float RingGizmoPropertyGroup::MIN_ALPHA = 0.0f; -const float RingGizmoPropertyGroup::MAX_ALPHA = 1.0f; -const float RingGizmoPropertyGroup::MIN_RADIUS = 0.0f; -const float RingGizmoPropertyGroup::MAX_RADIUS = 0.5f; - -void RingGizmoPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_START_ANGLE, Ring, ring, StartAngle, startAngle); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_END_ANGLE, Ring, ring, EndAngle, endAngle); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_INNER_RADIUS, Ring, ring, InnerRadius, innerRadius); - - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_INNER_START_COLOR, Ring, ring, InnerStartColor, innerStartColor, u8vec3Color); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_INNER_END_COLOR, Ring, ring, InnerEndColor, innerEndColor, u8vec3Color); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_OUTER_START_COLOR, Ring, ring, OuterStartColor, outerStartColor, u8vec3Color); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_OUTER_END_COLOR, Ring, ring, OuterEndColor, outerEndColor, u8vec3Color); - - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_INNER_START_ALPHA, Ring, ring, InnerStartAlpha, innerStartAlpha); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_INNER_END_ALPHA, Ring, ring, InnerEndAlpha, innerEndAlpha); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_OUTER_START_ALPHA, Ring, ring, OuterStartAlpha, outerStartAlpha); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_OUTER_END_ALPHA, Ring, ring, OuterEndAlpha, outerEndAlpha); - - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAS_TICK_MARKS, Ring, ring, HasTickMarks, hasTickMarks); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_MAJOR_TICK_MARKS_ANGLE, Ring, ring, MajorTickMarksAngle, majorTickMarksAngle); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_MINOR_TICK_MARKS_ANGLE, Ring, ring, MinorTickMarksAngle, minorTickMarksAngle); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_MAJOR_TICK_MARKS_LENGTH, Ring, ring, MajorTickMarksLength, majorTickMarksLength); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_MINOR_TICK_MARKS_LENGTH, Ring, ring, MinorTickMarksLength, minorTickMarksLength); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_MAJOR_TICK_MARKS_COLOR, Ring, ring, MajorTickMarksColor, majorTickMarksColor, u8vec3Color); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_MINOR_TICK_MARKS_COLOR, Ring, ring, MinorTickMarksColor, minorTickMarksColor, u8vec3Color); -} - -void RingGizmoPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, startAngle, float, setStartAngle); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, endAngle, float, setEndAngle); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, innerRadius, float, setInnerRadius); - - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, innerStartColor, u8vec3Color, setInnerStartColor); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, innerEndColor, u8vec3Color, setInnerEndColor); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, outerStartColor, u8vec3Color, setOuterStartColor); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, outerEndColor, u8vec3Color, setOuterEndColor); - - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, innerStartAlpha, float, setInnerStartAlpha); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, innerEndAlpha, float, setInnerEndAlpha); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, outerStartAlpha, float, setOuterStartAlpha); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, outerEndAlpha, float, setOuterEndAlpha); - - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, hasTickMarks, bool, setHasTickMarks); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, majorTickMarksAngle, float, setMajorTickMarksAngle); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, minorTickMarksAngle, float, setMinorTickMarksAngle); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, majorTickMarksLength, float, setMajorTickMarksLength); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, minorTickMarksLength, float, setMinorTickMarksLength); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, majorTickMarksColor, u8vec3Color, setMajorTickMarksColor); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, minorTickMarksColor, u8vec3Color, setMinorTickMarksColor); -} - -void RingGizmoPropertyGroup::merge(const RingGizmoPropertyGroup& other) { - COPY_PROPERTY_IF_CHANGED(startAngle); - COPY_PROPERTY_IF_CHANGED(endAngle); - COPY_PROPERTY_IF_CHANGED(innerRadius); - - COPY_PROPERTY_IF_CHANGED(innerStartColor); - COPY_PROPERTY_IF_CHANGED(innerEndColor); - COPY_PROPERTY_IF_CHANGED(outerStartColor); - COPY_PROPERTY_IF_CHANGED(outerEndColor); - - COPY_PROPERTY_IF_CHANGED(innerStartAlpha); - COPY_PROPERTY_IF_CHANGED(innerEndAlpha); - COPY_PROPERTY_IF_CHANGED(outerStartAlpha); - COPY_PROPERTY_IF_CHANGED(outerEndAlpha); - - COPY_PROPERTY_IF_CHANGED(hasTickMarks); - COPY_PROPERTY_IF_CHANGED(majorTickMarksAngle); - COPY_PROPERTY_IF_CHANGED(minorTickMarksAngle); - COPY_PROPERTY_IF_CHANGED(majorTickMarksLength); - COPY_PROPERTY_IF_CHANGED(minorTickMarksLength); - COPY_PROPERTY_IF_CHANGED(majorTickMarksColor); - COPY_PROPERTY_IF_CHANGED(minorTickMarksColor); -} - -void RingGizmoPropertyGroup::debugDump() const { - qCDebug(entities) << " RingGizmoPropertyGroup: ---------------------------------------------"; - qCDebug(entities) << " _startAngle:" << _startAngle; - qCDebug(entities) << " _endAngle:" << _endAngle; - qCDebug(entities) << " _innerRadius:" << _innerRadius; - qCDebug(entities) << " _innerStartColor:" << _innerStartColor; - qCDebug(entities) << " _innerEndColor:" << _innerEndColor; - qCDebug(entities) << " _outerStartColor:" << _outerStartColor; - qCDebug(entities) << " _outerEndColor:" << _outerEndColor; - qCDebug(entities) << " _innerStartAlpha:" << _innerStartAlpha; - qCDebug(entities) << " _innerEndAlpha:" << _innerEndAlpha; - qCDebug(entities) << " _outerStartAlpha:" << _outerStartAlpha; - qCDebug(entities) << " _outerEndAlpha:" << _outerEndAlpha; - qCDebug(entities) << " _hasTickMarks:" << _hasTickMarks; - qCDebug(entities) << " _majorTickMarksAngle:" << _majorTickMarksAngle; - qCDebug(entities) << " _minorTickMarksAngle:" << _minorTickMarksAngle; - qCDebug(entities) << " _majorTickMarksLength:" << _majorTickMarksLength; - qCDebug(entities) << " _minorTickMarksLength:" << _minorTickMarksLength; - qCDebug(entities) << " _majorTickMarksColor:" << _majorTickMarksColor; - qCDebug(entities) << " _minorTickMarksColor:" << _minorTickMarksColor; -} - -void RingGizmoPropertyGroup::listChangedProperties(QList& out) { - if (startAngleChanged()) { - out << "ring-startAngle"; - } - if (endAngleChanged()) { - out << "ring-endAngle"; - } - if (innerRadiusChanged()) { - out << "ring-innerRadius"; - } - - if (innerStartColorChanged()) { - out << "ring-innerStartColor"; - } - if (innerEndColorChanged()) { - out << "ring-innerEndColor"; - } - if (outerStartColorChanged()) { - out << "ring-outerStartColor"; - } - if (outerEndColorChanged()) { - out << "ring-outerEndColor"; - } - - if (innerStartAlphaChanged()) { - out << "ring-innerStartAlpha"; - } - if (innerEndAlphaChanged()) { - out << "ring-innerEndAlpha"; - } - if (outerStartAlphaChanged()) { - out << "ring-outerStartAlpha"; - } - if (outerEndAlphaChanged()) { - out << "ring-outerEndAlpha"; - } - - if (hasTickMarksChanged()) { - out << "ring-hasTickMarks"; - } - if (majorTickMarksAngleChanged()) { - out << "ring-majorTickMarksAngle"; - } - if (minorTickMarksAngleChanged()) { - out << "ring-minorTickMarksAngle"; - } - if (majorTickMarksLengthChanged()) { - out << "ring-majorTickMarksLength"; - } - if (minorTickMarksLengthChanged()) { - out << "ring-minorTickMarksLength"; - } - if (majorTickMarksColorChanged()) { - out << "ring-majorTickMarksColor"; - } - if (minorTickMarksColorChanged()) { - out << "ring-minorTickMarksColor"; - } -} - -bool RingGizmoPropertyGroup::appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_START_ANGLE, getStartAngle()); - APPEND_ENTITY_PROPERTY(PROP_END_ANGLE, getEndAngle()); - APPEND_ENTITY_PROPERTY(PROP_INNER_RADIUS, getInnerRadius()); - - APPEND_ENTITY_PROPERTY(PROP_INNER_START_COLOR, getInnerStartColor()); - APPEND_ENTITY_PROPERTY(PROP_INNER_END_COLOR, getInnerEndColor()); - APPEND_ENTITY_PROPERTY(PROP_OUTER_START_COLOR, getOuterStartColor()); - APPEND_ENTITY_PROPERTY(PROP_OUTER_END_COLOR, getOuterEndColor()); - - APPEND_ENTITY_PROPERTY(PROP_INNER_START_ALPHA, getInnerStartAlpha()); - APPEND_ENTITY_PROPERTY(PROP_INNER_END_ALPHA, getInnerEndAlpha()); - APPEND_ENTITY_PROPERTY(PROP_OUTER_START_ALPHA, getOuterStartAlpha()); - APPEND_ENTITY_PROPERTY(PROP_OUTER_END_ALPHA, getOuterEndAlpha()); - - APPEND_ENTITY_PROPERTY(PROP_HAS_TICK_MARKS, getHasTickMarks()); - APPEND_ENTITY_PROPERTY(PROP_MAJOR_TICK_MARKS_ANGLE, getMajorTickMarksAngle()); - APPEND_ENTITY_PROPERTY(PROP_MINOR_TICK_MARKS_ANGLE, getMinorTickMarksAngle()); - APPEND_ENTITY_PROPERTY(PROP_MAJOR_TICK_MARKS_LENGTH, getMajorTickMarksLength()); - APPEND_ENTITY_PROPERTY(PROP_MINOR_TICK_MARKS_LENGTH, getMinorTickMarksLength()); - APPEND_ENTITY_PROPERTY(PROP_MAJOR_TICK_MARKS_COLOR, getMajorTickMarksColor()); - APPEND_ENTITY_PROPERTY(PROP_MINOR_TICK_MARKS_COLOR, getMinorTickMarksColor()); - - return true; -} - -bool RingGizmoPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt , int& processedBytes) { - - int bytesRead = 0; - bool overwriteLocalData = true; - bool somethingChanged = false; - - READ_ENTITY_PROPERTY(PROP_START_ANGLE, float, setStartAngle); - READ_ENTITY_PROPERTY(PROP_END_ANGLE, float, setEndAngle); - READ_ENTITY_PROPERTY(PROP_INNER_RADIUS, float, setInnerRadius); - - READ_ENTITY_PROPERTY(PROP_INNER_START_COLOR, u8vec3Color, setInnerStartColor); - READ_ENTITY_PROPERTY(PROP_INNER_END_COLOR, u8vec3Color, setInnerEndColor); - READ_ENTITY_PROPERTY(PROP_OUTER_START_COLOR, u8vec3Color, setOuterStartColor); - READ_ENTITY_PROPERTY(PROP_OUTER_END_COLOR, u8vec3Color, setOuterEndColor); - - READ_ENTITY_PROPERTY(PROP_INNER_START_ALPHA, float, setInnerStartAlpha); - READ_ENTITY_PROPERTY(PROP_INNER_END_ALPHA, float, setInnerEndAlpha); - READ_ENTITY_PROPERTY(PROP_OUTER_START_ALPHA, float, setOuterStartAlpha); - READ_ENTITY_PROPERTY(PROP_OUTER_END_ALPHA, float, setOuterEndAlpha); - - READ_ENTITY_PROPERTY(PROP_HAS_TICK_MARKS, bool, setHasTickMarks); - READ_ENTITY_PROPERTY(PROP_MAJOR_TICK_MARKS_ANGLE, float, setMajorTickMarksAngle); - READ_ENTITY_PROPERTY(PROP_MINOR_TICK_MARKS_ANGLE, float, setMinorTickMarksAngle); - READ_ENTITY_PROPERTY(PROP_MAJOR_TICK_MARKS_LENGTH, float, setMajorTickMarksLength); - READ_ENTITY_PROPERTY(PROP_MINOR_TICK_MARKS_LENGTH, float, setMinorTickMarksLength); - READ_ENTITY_PROPERTY(PROP_MAJOR_TICK_MARKS_COLOR, u8vec3Color, setMajorTickMarksColor); - READ_ENTITY_PROPERTY(PROP_MINOR_TICK_MARKS_COLOR, u8vec3Color, setMinorTickMarksColor); - - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_START_ANGLE, StartAngle); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_END_ANGLE, EndAngle); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_INNER_RADIUS, InnerRadius); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_INNER_START_COLOR, InnerStartColor); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_INNER_END_COLOR, InnerEndColor); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_OUTER_START_COLOR, OuterStartColor); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_OUTER_END_COLOR, OuterEndColor); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_INNER_START_ALPHA, InnerStartAlpha); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_INNER_END_ALPHA, InnerEndAlpha); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_OUTER_START_ALPHA, OuterStartAlpha); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_OUTER_END_ALPHA, OuterEndAlpha); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HAS_TICK_MARKS, HasTickMarks); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_MAJOR_TICK_MARKS_ANGLE, MajorTickMarksAngle); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_MINOR_TICK_MARKS_ANGLE, MinorTickMarksAngle); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_MAJOR_TICK_MARKS_LENGTH, MajorTickMarksLength); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_MINOR_TICK_MARKS_LENGTH, MinorTickMarksLength); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_MAJOR_TICK_MARKS_COLOR, MajorTickMarksColor); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_MINOR_TICK_MARKS_COLOR, MinorTickMarksColor); - - processedBytes += bytesRead; - - Q_UNUSED(somethingChanged); - - return true; -} - -void RingGizmoPropertyGroup::markAllChanged() { - _startAngleChanged = true; - _endAngleChanged = true; - _innerRadiusChanged = true; - - _innerStartColorChanged = true; - _innerEndColorChanged = true; - _outerStartColorChanged = true; - _outerEndColorChanged = true; - - _innerStartAlphaChanged = true; - _innerEndAlphaChanged = true; - _outerStartAlphaChanged = true; - _outerEndAlphaChanged = true; - - _hasTickMarksChanged = true; - _majorTickMarksAngleChanged = true; - _minorTickMarksAngleChanged = true; - _majorTickMarksLengthChanged = true; - _minorTickMarksLengthChanged = true; - _majorTickMarksColorChanged = true; - _minorTickMarksColorChanged = true; -} - -EntityPropertyFlags RingGizmoPropertyGroup::getChangedProperties() const { - EntityPropertyFlags changedProperties; - - CHECK_PROPERTY_CHANGE(PROP_START_ANGLE, startAngle); - CHECK_PROPERTY_CHANGE(PROP_END_ANGLE, endAngle); - CHECK_PROPERTY_CHANGE(PROP_INNER_RADIUS, innerRadius); - - CHECK_PROPERTY_CHANGE(PROP_INNER_START_COLOR, innerStartColor); - CHECK_PROPERTY_CHANGE(PROP_INNER_END_COLOR, innerEndColor); - CHECK_PROPERTY_CHANGE(PROP_OUTER_START_COLOR, outerStartColor); - CHECK_PROPERTY_CHANGE(PROP_OUTER_END_COLOR, outerEndColor); - - CHECK_PROPERTY_CHANGE(PROP_INNER_START_ALPHA, innerStartAlpha); - CHECK_PROPERTY_CHANGE(PROP_INNER_END_ALPHA, innerEndAlpha); - CHECK_PROPERTY_CHANGE(PROP_OUTER_START_ALPHA, outerStartAlpha); - CHECK_PROPERTY_CHANGE(PROP_OUTER_END_ALPHA, outerEndAlpha); - - CHECK_PROPERTY_CHANGE(PROP_HAS_TICK_MARKS, hasTickMarks); - CHECK_PROPERTY_CHANGE(PROP_MAJOR_TICK_MARKS_ANGLE, majorTickMarksAngle); - CHECK_PROPERTY_CHANGE(PROP_MINOR_TICK_MARKS_ANGLE, minorTickMarksAngle); - CHECK_PROPERTY_CHANGE(PROP_MAJOR_TICK_MARKS_LENGTH, majorTickMarksLength); - CHECK_PROPERTY_CHANGE(PROP_MINOR_TICK_MARKS_LENGTH, minorTickMarksLength); - CHECK_PROPERTY_CHANGE(PROP_MAJOR_TICK_MARKS_COLOR, majorTickMarksColor); - CHECK_PROPERTY_CHANGE(PROP_MINOR_TICK_MARKS_COLOR, minorTickMarksColor); - - return changedProperties; -} - -void RingGizmoPropertyGroup::getProperties(EntityItemProperties& properties) const { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, StartAngle, getStartAngle); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, EndAngle, getEndAngle); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, InnerRadius, getInnerRadius); - - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, InnerStartColor, getInnerStartColor); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, InnerEndColor, getInnerEndColor); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, OuterStartColor, getOuterStartColor); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, OuterEndColor, getOuterEndColor); - - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, InnerStartAlpha, getInnerStartAlpha); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, InnerEndAlpha, getInnerEndAlpha); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, OuterStartAlpha, getOuterStartAlpha); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, OuterEndAlpha, getOuterEndAlpha); - - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, HasTickMarks, getHasTickMarks); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, MajorTickMarksAngle, getMajorTickMarksAngle); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, MinorTickMarksAngle, getMinorTickMarksAngle); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, MajorTickMarksLength, getMajorTickMarksLength); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, MinorTickMarksLength, getMinorTickMarksLength); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, MajorTickMarksColor, getMajorTickMarksColor); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Ring, MinorTickMarksColor, getMinorTickMarksColor); -} - -bool RingGizmoPropertyGroup::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, StartAngle, startAngle, setStartAngle); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, EndAngle, endAngle, setEndAngle); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, InnerRadius, innerRadius, setInnerRadius); - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, InnerStartColor, innerStartColor, setInnerStartColor); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, InnerEndColor, innerEndColor, setInnerEndColor); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, OuterStartColor, outerStartColor, setOuterStartColor); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, OuterEndColor, outerEndColor, setOuterEndColor); - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, InnerStartAlpha, innerStartAlpha, setInnerStartAlpha); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, InnerEndAlpha, innerEndAlpha, setInnerEndAlpha); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, OuterStartAlpha, outerStartAlpha, setOuterStartAlpha); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, OuterEndAlpha, outerEndAlpha, setOuterEndAlpha); - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, HasTickMarks, hasTickMarks, setHasTickMarks); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, MajorTickMarksAngle, majorTickMarksAngle, setMajorTickMarksAngle); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, MinorTickMarksAngle, minorTickMarksAngle, setMinorTickMarksAngle); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, MajorTickMarksLength, majorTickMarksLength, setMajorTickMarksLength); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, MinorTickMarksLength, minorTickMarksLength, setMinorTickMarksLength); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, MajorTickMarksColor, majorTickMarksColor, setMajorTickMarksColor); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Ring, MinorTickMarksColor, minorTickMarksColor, setMinorTickMarksColor); - - return somethingChanged; -} - -EntityPropertyFlags RingGizmoPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties; - - requestedProperties += PROP_START_ANGLE; - requestedProperties += PROP_END_ANGLE; - requestedProperties += PROP_INNER_RADIUS; - - requestedProperties += PROP_INNER_START_COLOR; - requestedProperties += PROP_INNER_END_COLOR; - requestedProperties += PROP_OUTER_START_COLOR; - requestedProperties += PROP_OUTER_END_COLOR; - - requestedProperties += PROP_INNER_START_ALPHA; - requestedProperties += PROP_INNER_END_ALPHA; - requestedProperties += PROP_OUTER_START_ALPHA; - requestedProperties += PROP_OUTER_END_ALPHA; - - requestedProperties += PROP_HAS_TICK_MARKS; - requestedProperties += PROP_MAJOR_TICK_MARKS_ANGLE; - requestedProperties += PROP_MINOR_TICK_MARKS_ANGLE; - requestedProperties += PROP_MAJOR_TICK_MARKS_LENGTH; - requestedProperties += PROP_MINOR_TICK_MARKS_LENGTH; - requestedProperties += PROP_MAJOR_TICK_MARKS_COLOR; - requestedProperties += PROP_MINOR_TICK_MARKS_COLOR; - - return requestedProperties; -} - -void RingGizmoPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_START_ANGLE, getStartAngle()); - APPEND_ENTITY_PROPERTY(PROP_END_ANGLE, getEndAngle()); - APPEND_ENTITY_PROPERTY(PROP_INNER_RADIUS, getInnerRadius()); - - APPEND_ENTITY_PROPERTY(PROP_INNER_START_COLOR, getInnerStartColor()); - APPEND_ENTITY_PROPERTY(PROP_INNER_END_COLOR, getInnerEndColor()); - APPEND_ENTITY_PROPERTY(PROP_OUTER_START_COLOR, getOuterStartColor()); - APPEND_ENTITY_PROPERTY(PROP_OUTER_END_COLOR, getOuterEndColor()); - - APPEND_ENTITY_PROPERTY(PROP_INNER_START_ALPHA, getInnerStartAlpha()); - APPEND_ENTITY_PROPERTY(PROP_INNER_END_ALPHA, getInnerEndAlpha()); - APPEND_ENTITY_PROPERTY(PROP_OUTER_START_ALPHA, getOuterStartAlpha()); - APPEND_ENTITY_PROPERTY(PROP_OUTER_END_ALPHA, getOuterEndAlpha()); - - APPEND_ENTITY_PROPERTY(PROP_HAS_TICK_MARKS, getHasTickMarks()); - APPEND_ENTITY_PROPERTY(PROP_MAJOR_TICK_MARKS_ANGLE, getMajorTickMarksAngle()); - APPEND_ENTITY_PROPERTY(PROP_MINOR_TICK_MARKS_ANGLE, getMinorTickMarksAngle()); - APPEND_ENTITY_PROPERTY(PROP_MAJOR_TICK_MARKS_LENGTH, getMajorTickMarksLength()); - APPEND_ENTITY_PROPERTY(PROP_MINOR_TICK_MARKS_LENGTH, getMinorTickMarksLength()); - APPEND_ENTITY_PROPERTY(PROP_MAJOR_TICK_MARKS_COLOR, getMajorTickMarksColor()); - APPEND_ENTITY_PROPERTY(PROP_MINOR_TICK_MARKS_COLOR, getMinorTickMarksColor()); -} - -int RingGizmoPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_START_ANGLE, float, setStartAngle); - READ_ENTITY_PROPERTY(PROP_END_ANGLE, float, setEndAngle); - READ_ENTITY_PROPERTY(PROP_INNER_RADIUS, float, setInnerRadius); - - READ_ENTITY_PROPERTY(PROP_INNER_START_COLOR, u8vec3Color, setInnerStartColor); - READ_ENTITY_PROPERTY(PROP_INNER_END_COLOR, u8vec3Color, setInnerEndColor); - READ_ENTITY_PROPERTY(PROP_OUTER_START_COLOR, u8vec3Color, setOuterStartColor); - READ_ENTITY_PROPERTY(PROP_OUTER_END_COLOR, u8vec3Color, setOuterEndColor); - - READ_ENTITY_PROPERTY(PROP_INNER_START_ALPHA, float, setInnerStartAlpha); - READ_ENTITY_PROPERTY(PROP_INNER_END_ALPHA, float, setInnerEndAlpha); - READ_ENTITY_PROPERTY(PROP_OUTER_START_ALPHA, float, setOuterStartAlpha); - READ_ENTITY_PROPERTY(PROP_OUTER_END_ALPHA, float, setOuterEndAlpha); - - READ_ENTITY_PROPERTY(PROP_HAS_TICK_MARKS, bool, setHasTickMarks); - READ_ENTITY_PROPERTY(PROP_MAJOR_TICK_MARKS_ANGLE, float, setMajorTickMarksAngle); - READ_ENTITY_PROPERTY(PROP_MINOR_TICK_MARKS_ANGLE, float, setMinorTickMarksAngle); - READ_ENTITY_PROPERTY(PROP_MAJOR_TICK_MARKS_LENGTH, float, setMajorTickMarksLength); - READ_ENTITY_PROPERTY(PROP_MINOR_TICK_MARKS_LENGTH, float, setMinorTickMarksLength); - READ_ENTITY_PROPERTY(PROP_MAJOR_TICK_MARKS_COLOR, u8vec3Color, setMajorTickMarksColor); - READ_ENTITY_PROPERTY(PROP_MINOR_TICK_MARKS_COLOR, u8vec3Color, setMinorTickMarksColor); - - return bytesRead; -} - -bool RingGizmoPropertyGroup::operator==(const RingGizmoPropertyGroup& a) const { - return (a._startAngle == _startAngle) && - (a._endAngle == _endAngle) && - (a._innerRadius == _innerRadius) && - (a._innerStartColor == _innerStartColor) && - (a._innerEndColor == _innerEndColor) && - (a._outerStartColor == _outerStartColor) && - (a._outerEndColor == _outerEndColor) && - (a._innerStartAlpha == _innerStartAlpha) && - (a._innerEndAlpha == _innerEndAlpha) && - (a._outerStartAlpha == _outerStartAlpha) && - (a._outerEndAlpha == _outerEndAlpha) && - (a._hasTickMarks == _hasTickMarks) && - (a._majorTickMarksAngle == _majorTickMarksAngle) && - (a._minorTickMarksAngle == _minorTickMarksAngle) && - (a._majorTickMarksLength == _majorTickMarksLength) && - (a._minorTickMarksLength == _minorTickMarksLength) && - (a._majorTickMarksColor == _majorTickMarksColor) && - (a._minorTickMarksColor == _minorTickMarksColor); -} \ No newline at end of file diff --git a/libraries/entities/src/RingGizmoPropertyGroup.cpp.in b/libraries/entities/src/RingGizmoPropertyGroup.cpp.in new file mode 100644 index 0000000000..b7e22c8187 --- /dev/null +++ b/libraries/entities/src/RingGizmoPropertyGroup.cpp.in @@ -0,0 +1,159 @@ +// +// Created by Sam Gondelman on 1/22/19 +// Copyright 2019 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "RingGizmoPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" + +const float RingGizmoPropertyGroup::MIN_ANGLE = 0.0f; +const float RingGizmoPropertyGroup::MAX_ANGLE = 360.0f; +const float RingGizmoPropertyGroup::MIN_ALPHA = 0.0f; +const float RingGizmoPropertyGroup::MAX_ALPHA = 1.0f; +const float RingGizmoPropertyGroup::MIN_RADIUS = 0.0f; +const float RingGizmoPropertyGroup::MAX_RADIUS = 0.5f; + +void RingGizmoPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + +@Ring_GROUP_COPY_TO_SCRIPT@ + +} + +void RingGizmoPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + +@Ring_GROUP_COPY_FROM_SCRIPT@ + +} + +void RingGizmoPropertyGroup::merge(const RingGizmoPropertyGroup& other) { + +@Ring_GROUP_MERGE@ + +} + +void RingGizmoPropertyGroup::debugDump() const { + +@Ring_GROUP_DEBUG_DUMP@ + +} + +void RingGizmoPropertyGroup::listChangedProperties(QList& out) { + +@Ring_GROUP_LIST_CHANGED@ + +} + +bool RingGizmoPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Ring_GROUP_APPEND@ + + return successPropertyFits; +} + +bool RingGizmoPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + +@Ring_GROUP_READ@ + +@Ring_GROUP_DECODE_CHANGED@ + + processedBytes += bytesRead; + + return somethingChanged; +} + +void RingGizmoPropertyGroup::markAllChanged() { + +@Ring_GROUP_MARK_CHANGED@ + +} + +EntityPropertyFlags RingGizmoPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + +@Ring_GROUP_CHANGED_PROPERTIES@ + + return changedProperties; +} + +void RingGizmoPropertyGroup::getProperties(EntityItemProperties& properties) const { + +@Ring_GROUP_COPY_TO@ + +} + +bool RingGizmoPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Ring_GROUP_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags RingGizmoPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + +@Ring_REQUESTED_PROPS@ + + return requestedProperties; +} + +int RingGizmoPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Ring_GROUP_READ@ + + return bytesRead; +} + +void RingGizmoPropertyGroup::addPropertyMap(QHash& _propertyInfos, + QHash& _enumsToPropertyStrings) { + +@Ring_GROUP_ADD_TO_MAP@ + +} + +bool RingGizmoPropertyGroup::operator==(const RingGizmoPropertyGroup& a) const { + return (a._startAngle == _startAngle) && + (a._endAngle == _endAngle) && + (a._innerRadius == _innerRadius) && + (a._innerStartColor == _innerStartColor) && + (a._innerEndColor == _innerEndColor) && + (a._outerStartColor == _outerStartColor) && + (a._outerEndColor == _outerEndColor) && + (a._innerStartAlpha == _innerStartAlpha) && + (a._innerEndAlpha == _innerEndAlpha) && + (a._outerStartAlpha == _outerStartAlpha) && + (a._outerEndAlpha == _outerEndAlpha) && + (a._hasTickMarks == _hasTickMarks) && + (a._majorTickMarksAngle == _majorTickMarksAngle) && + (a._minorTickMarksAngle == _minorTickMarksAngle) && + (a._majorTickMarksLength == _majorTickMarksLength) && + (a._minorTickMarksLength == _minorTickMarksLength) && + (a._majorTickMarksColor == _majorTickMarksColor) && + (a._minorTickMarksColor == _minorTickMarksColor); +} diff --git a/libraries/entities/src/RingGizmoPropertyGroup.h b/libraries/entities/src/RingGizmoPropertyGroup.h deleted file mode 100644 index 51ef709f5b..0000000000 --- a/libraries/entities/src/RingGizmoPropertyGroup.h +++ /dev/null @@ -1,139 +0,0 @@ -// -// Created by Sam Gondelman on 1/22/19 -// Copyright 2019 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#ifndef hifi_RingGizmoPropertyGroup_h -#define hifi_RingGizmoPropertyGroup_h - -#include - -#include "PropertyGroup.h" -#include "EntityItemPropertiesMacros.h" -#include "EntityItemPropertiesDefaults.h" - -class EntityItemProperties; -class EncodeBitstreamParams; -class OctreePacketData; -class ReadBitstreamToTreeParams; -class ScriptEngine; -class ScriptValue; - -using u8vec3Color = glm::u8vec3; - -/*@jsdoc - * A {@link Entities.EntityProperties-Gizmo|ring Gizmo} entity is defined by the following properties: - * @typedef {object} Entities.RingGizmo - * - * @property {number} startAngle=0 - The angle at which the ring starts, in degrees. - * @property {number} endAngle=360 - The angle at which the ring ends, in degrees. - * @property {number} innerRadius=0 - The inner radius of the ring as a fraction of the total radius, range 0.0 - * — 1.0. - - * @property {Color} innerStartColor=255,255,255 - The color at the inner start point of the ring. - * @property {Color} innerEndColor=255,255,255 - The color at the inner end point of the ring. - * @property {Color} outerStartColor=255,255,255 - The color at the outer start point of the ring. - * @property {Color} outerEndColor=255,255,255 - The color at the outer end point of the ring. - * @property {number} innerStartAlpha=1 - The opacity at the inner start point of the ring. - * @property {number} innerEndAlpha=1 - The opacity at the inner end point of the ring. - * @property {number} outerStartAlpha=1 - The opacity at the outer start point of the ring. - * @property {number} outerEndAlpha=1 - The opacity at the outer end point of the ring. - - * @property {boolean} hasTickMarks=false - true to render tick marks, otherwise false. - * @property {number} majorTickMarksAngle=0 - The angle between major tick marks, in degrees. - * @property {number} minorTickMarksAngle=0 - The angle between minor tick marks, in degrees. - * @property {number} majorTickMarksLength=0 - The length of the major tick marks as a fraction of the radius. A positive value - * draws tick marks outwards from the inner radius; a negative value draws tick marks inwards from the outer radius. - * @property {number} minorTickMarksLength=0 - The length of the minor tick marks, as a fraction of the radius. A positive - * value draws tick marks outwards from the inner radius; a negative value draws tick marks inwards from the outer radius. - * @property {Color} majorTickMarksColor=255,255,255 - The color of the major tick marks. - * @property {Color} minorTickMarksColor=255,255,255 - The color of the minor tick marks. - */ - -class RingGizmoPropertyGroup : public PropertyGroup { -public: - // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const override; - virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; - - void merge(const RingGizmoPropertyGroup& other); - - virtual void debugDump() const override; - virtual void listChangedProperties(QList& out) override; - - virtual bool appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt, int& processedBytes) override; - virtual void markAllChanged() override; - virtual EntityPropertyFlags getChangedProperties() const override; - - // EntityItem related helpers - // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const override; - - // returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - bool operator==(const RingGizmoPropertyGroup& a) const; - bool operator!=(const RingGizmoPropertyGroup& a) const { return !(*this == a); } - - static const float MIN_ANGLE; - static const float MAX_ANGLE; - static const float MIN_ALPHA; - static const float MAX_ALPHA; - static const float MIN_RADIUS; - static const float MAX_RADIUS; - - DEFINE_PROPERTY(PROP_START_ANGLE, StartAngle, startAngle, float, 0.0f); - DEFINE_PROPERTY(PROP_END_ANGLE, EndAngle, endAngle, float, 360.0f); - DEFINE_PROPERTY(PROP_INNER_RADIUS, InnerRadius, innerRadius, float, 0.0f); - - DEFINE_PROPERTY_REF(PROP_INNER_START_COLOR, InnerStartColor, innerStartColor, u8vec3Color, ENTITY_ITEM_DEFAULT_COLOR); - DEFINE_PROPERTY_REF(PROP_INNER_END_COLOR, InnerEndColor, innerEndColor, u8vec3Color, ENTITY_ITEM_DEFAULT_COLOR); - DEFINE_PROPERTY_REF(PROP_OUTER_START_COLOR, OuterStartColor, outerStartColor, u8vec3Color, ENTITY_ITEM_DEFAULT_COLOR); - DEFINE_PROPERTY_REF(PROP_OUTER_END_COLOR, OuterEndColor, outerEndColor, u8vec3Color, ENTITY_ITEM_DEFAULT_COLOR); - - DEFINE_PROPERTY(PROP_INNER_START_ALPHA, InnerStartAlpha, innerStartAlpha, float, ENTITY_ITEM_DEFAULT_ALPHA); - DEFINE_PROPERTY(PROP_INNER_END_ALPHA, InnerEndAlpha, innerEndAlpha, float, ENTITY_ITEM_DEFAULT_ALPHA); - DEFINE_PROPERTY(PROP_OUTER_START_ALPHA, OuterStartAlpha, outerStartAlpha, float, ENTITY_ITEM_DEFAULT_ALPHA); - DEFINE_PROPERTY(PROP_OUTER_END_ALPHA, OuterEndAlpha, outerEndAlpha, float, ENTITY_ITEM_DEFAULT_ALPHA); - - DEFINE_PROPERTY(PROP_HAS_TICK_MARKS, HasTickMarks, hasTickMarks, bool, false); - DEFINE_PROPERTY(PROP_MAJOR_TICK_MARKS_ANGLE, MajorTickMarksAngle, majorTickMarksAngle, float, 0.0f); - DEFINE_PROPERTY(PROP_MINOR_TICK_MARKS_ANGLE, MinorTickMarksAngle, minorTickMarksAngle, float, 0.0f); - DEFINE_PROPERTY(PROP_MAJOR_TICK_MARKS_LENGTH, MajorTickMarksLength, majorTickMarksLength, float, 0.0f); - DEFINE_PROPERTY(PROP_MINOR_TICK_MARKS_LENGTH, MinorTickMarksLength, minorTickMarksLength, float, 0.0f); - DEFINE_PROPERTY_REF(PROP_MAJOR_TICK_MARKS_COLOR, MajorTickMarksColor, majorTickMarksColor, u8vec3Color, ENTITY_ITEM_DEFAULT_COLOR); - DEFINE_PROPERTY_REF(PROP_MINOR_TICK_MARKS_COLOR, MinorTickMarksColor, minorTickMarksColor, u8vec3Color, ENTITY_ITEM_DEFAULT_COLOR); -}; - -#endif // hifi_RingGizmoPropertyGroup_h diff --git a/libraries/entities/src/RingGizmoPropertyGroup.h.in b/libraries/entities/src/RingGizmoPropertyGroup.h.in new file mode 100644 index 0000000000..ea42225851 --- /dev/null +++ b/libraries/entities/src/RingGizmoPropertyGroup.h.in @@ -0,0 +1,76 @@ +// +// Created by Sam Gondelman on 1/22/19 +// Copyright 2019 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_RingGizmoPropertyGroup_h +#define hifi_RingGizmoPropertyGroup_h + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" +#include "EntityItemPropertiesDefaults.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +/*@jsdoc + * A {@link Entities.EntityProperties-Gizmo|ring Gizmo} entity is defined by the following properties: + * @typedef {object} Entities.RingGizmo + * + * @property {number} startAngle=0 - The angle at which the ring starts, in degrees. + * @property {number} endAngle=360 - The angle at which the ring ends, in degrees. + * @property {number} innerRadius=0 - The inner radius of the ring as a fraction of the total radius, range 0.0 + * — 1.0. + + * @property {Color} innerStartColor=255,255,255 - The color at the inner start point of the ring. + * @property {Color} innerEndColor=255,255,255 - The color at the inner end point of the ring. + * @property {Color} outerStartColor=255,255,255 - The color at the outer start point of the ring. + * @property {Color} outerEndColor=255,255,255 - The color at the outer end point of the ring. + * @property {number} innerStartAlpha=1 - The opacity at the inner start point of the ring. + * @property {number} innerEndAlpha=1 - The opacity at the inner end point of the ring. + * @property {number} outerStartAlpha=1 - The opacity at the outer start point of the ring. + * @property {number} outerEndAlpha=1 - The opacity at the outer end point of the ring. + + * @property {boolean} hasTickMarks=false - true to render tick marks, otherwise false. + * @property {number} majorTickMarksAngle=0 - The angle between major tick marks, in degrees. + * @property {number} minorTickMarksAngle=0 - The angle between minor tick marks, in degrees. + * @property {number} majorTickMarksLength=0 - The length of the major tick marks as a fraction of the radius. A positive value + * draws tick marks outwards from the inner radius; a negative value draws tick marks inwards from the outer radius. + * @property {number} minorTickMarksLength=0 - The length of the minor tick marks, as a fraction of the radius. A positive + * value draws tick marks outwards from the inner radius; a negative value draws tick marks inwards from the outer radius. + * @property {Color} majorTickMarksColor=255,255,255 - The color of the major tick marks. + * @property {Color} minorTickMarksColor=255,255,255 - The color of the minor tick marks. + */ + +class RingGizmoPropertyGroup : public PropertyGroup { +public: + ENTITY_PROPERTY_GROUP_METHODS(RingGizmoPropertyGroup) + + bool operator==(const RingGizmoPropertyGroup& a) const; + bool operator!=(const RingGizmoPropertyGroup& a) const { return !(*this == a); } + + static const float MIN_ANGLE; + static const float MAX_ANGLE; + static const float MIN_ALPHA; + static const float MAX_ALPHA; + static const float MIN_RADIUS; + static const float MAX_RADIUS; + +protected: + +@Ring_GROUP_PROPS@ + +}; + +#endif // hifi_RingGizmoPropertyGroup_h diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp.in similarity index 61% rename from libraries/entities/src/ShapeEntityItem.cpp rename to libraries/entities/src/ShapeEntityItem.cpp.in index 054d7f96be..cababe7f03 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp.in @@ -19,67 +19,8 @@ #include "EntityTree.h" #include "EntityTreeElement.h" -namespace entity { - - /*@jsdoc - *

    A "Shape", "Box", or "Sphere" {@link Entities.EntityType|EntityType} may - * display as one of the following geometrical shapes:

    - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    ValueDimensionsNotes
    "Circle"2DA circle oriented in 3D.
    "Cone"3D
    "Cube"3D
    "Cylinder"3D
    "Dodecahedron"3D
    "Hexagon"3DA hexagonal prism.
    "Icosahedron"3D
    "Octagon"3DAn octagonal prism.
    "Octahedron"3D
    "Quad"2DA square oriented in 3D.
    "Sphere"3D
    "Tetrahedron"3D
    "Torus"3DNot implemented.
    "Triangle"3DA triangular prism.
    - * @typedef {string} Entities.Shape - */ - static const std::array shapeStrings { { - "Triangle", - "Quad", - "Hexagon", - "Octagon", - "Circle", - "Cube", - "Sphere", - "Tetrahedron", - "Octahedron", - "Dodecahedron", - "Icosahedron", - "Torus", // Not implemented yet. - "Cone", - "Cylinder" - } }; - - Shape shapeFromString(const ::QString& shapeString) { - for (size_t i = 0; i < shapeStrings.size(); ++i) { - if (shapeString.toLower() == shapeStrings[i].toLower()) { - return static_cast(i); - } - } - return Shape::Sphere; - } - - QString stringFromShape(Shape shape) { - return shapeStrings[shape]; - } -} - // hullShapeCalculator is a hook for external code that knows how to configure a ShapeInfo -// for given entity::Shape and dimensions +// for given EntityShape and dimensions ShapeEntityItem::ShapeInfoCalculator hullShapeCalculator = nullptr; void ShapeEntityItem::setShapeInfoCalulator(ShapeEntityItem::ShapeInfoCalculator callback) { @@ -98,13 +39,13 @@ EntityItemPointer ShapeEntityItem::factory(const EntityItemID& entityID, const E EntityItemPointer ShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { auto result = baseFactory(entityID, properties); - result->setShape(entity::Shape::Cube); + result->setShape(EntityShape::Cube); return result; } EntityItemPointer ShapeEntityItem::sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { auto result = baseFactory(entityID, properties); - result->setShape(entity::Shape::Sphere); + result->setShape(EntityShape::Sphere); return result; } @@ -117,31 +58,24 @@ ShapeEntityItem::ShapeEntityItem(const EntityItemID& entityItemID) : EntityItem( EntityItemProperties ShapeEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(unlit, getUnlit); - withReadLock([&] { - _pulseProperties.getProperties(properties); - }); - properties.setShape(entity::stringFromShape(getShape())); - properties._shapeChanged = false; +@Shape_ENTITY_COPY_TO@ return properties; } -void ShapeEntityItem::setShape(const entity::Shape& shape) { +void ShapeEntityItem::setShape(EntityShape shape) { switch (shape) { - case entity::Shape::Cube: + case EntityShape::Cube: _type = EntityTypes::Box; break; - case entity::Shape::Sphere: + case EntityShape::Sphere: _type = EntityTypes::Sphere; break; - case entity::Shape::Circle: + case EntityShape::Circle: // Circle is implicitly flat so we enforce flat dimensions setUnscaledDimensions(getUnscaledDimensions()); break; - case entity::Shape::Quad: + case EntityShape::Quad: // Quad is implicitly flat so we enforce flat dimensions setUnscaledDimensions(getUnscaledDimensions()); break; @@ -160,8 +94,8 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) { } } -entity::Shape ShapeEntityItem::getShape() const { - return resultWithReadLock([&] { +EntityShape ShapeEntityItem::getShape() const { + return resultWithReadLock([&] { return _shape; }); } @@ -169,19 +103,33 @@ entity::Shape ShapeEntityItem::getShape() const { bool ShapeEntityItem::setSubClassProperties(const EntityItemProperties& properties) { bool somethingChanged = false; - SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(unlit, setUnlit); - withWriteLock([&] { - bool pulsePropertiesChanged = _pulseProperties.setProperties(properties); - somethingChanged |= pulsePropertiesChanged; - _needsRenderUpdate |= pulsePropertiesChanged; - }); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(shape, setShape); +@Shape_ENTITY_SET_FROM@ return somethingChanged; } +EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + +@Shape_REQUESTED_PROPS@ + + return requestedProperties; +} + +void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Shape_ENTITY_APPEND@ + +} + int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, @@ -190,93 +138,15 @@ int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); - READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); - READ_ENTITY_PROPERTY(PROP_UNLIT, bool, setUnlit); - withWriteLock([&] { - int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, - somethingChanged); - bytesRead += bytesFromPulse; - dataAt += bytesFromPulse; - }); - READ_ENTITY_PROPERTY(PROP_SHAPE, QString, setShape); +@Shape_ENTITY_READ@ return bytesRead; } -EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - requestedProperties += PROP_COLOR; - requestedProperties += PROP_ALPHA; - requestedProperties += PROP_UNLIT; - requestedProperties += _pulseProperties.getEntityProperties(params); - requestedProperties += PROP_SHAPE; - return requestedProperties; -} - -void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); - APPEND_ENTITY_PROPERTY(PROP_UNLIT, getUnlit()); - withReadLock([&] { - _pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - }); - APPEND_ENTITY_PROPERTY(PROP_SHAPE, entity::stringFromShape(getShape())); -} - -void ShapeEntityItem::setColor(const glm::u8vec3& value) { - withWriteLock([&] { - _needsRenderUpdate |= _color != value; - _color = value; - }); -} - -glm::u8vec3 ShapeEntityItem::getColor() const { - return resultWithReadLock([&] { - return _color; - }); -} - -void ShapeEntityItem::setAlpha(float alpha) { - withWriteLock([&] { - _needsRenderUpdate |= _alpha != alpha; - _alpha = alpha; - }); -} - -float ShapeEntityItem::getAlpha() const { - return resultWithReadLock([&] { - return _alpha; - }); -} - -void ShapeEntityItem::setUnlit(bool unlit) { - withWriteLock([&] { - _needsRenderUpdate |= _unlit != unlit; - _unlit = unlit; - }); -} - -bool ShapeEntityItem::getUnlit() const { - return resultWithReadLock([&] { - return _unlit; - }); -} - void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) { const float MAX_FLAT_DIMENSION = 0.0001f; const auto shape = getShape(); - if ((shape == entity::Shape::Circle || shape == entity::Shape::Quad) && value.y > MAX_FLAT_DIMENSION) { + if ((shape == EntityShape::Circle || shape == EntityShape::Quad) && value.y > MAX_FLAT_DIMENSION) { // enforce flatness in Y glm::vec3 newDimensions = value; newDimensions.y = MAX_FLAT_DIMENSION; @@ -287,7 +157,7 @@ void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) { } bool ShapeEntityItem::supportsDetailedIntersection() const { - return getShape() == entity::Sphere; + return getShape() == EntityShape::Sphere; } bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -357,20 +227,19 @@ bool ShapeEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, bool ShapeEntityItem::getRotateForPicking() const { auto shape = getShape(); - return getBillboardMode() != BillboardMode::NONE && (shape < entity::Shape::Cube || shape > entity::Shape::Icosahedron); + return getBillboardMode() != BillboardMode::NONE && (shape < EntityShape::Cube || shape > EntityShape::Icosahedron); } void ShapeEntityItem::debugDump() const { - quint64 now = usecTimestampNow(); - qCDebug(entities) << "SHAPE EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " name:" << _name; - qCDebug(entities) << " shape:" << stringFromShape(_shape) << " (EnumId: " << _shape << " )"; - qCDebug(entities) << " collisionShapeType:" << ShapeInfo::getNameForShapeType(getShapeType()); - qCDebug(entities) << " color:" << _color; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << "SHAPE EntityItem Ptr:" << this; + qCDebug(entities) << "ShapeEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@Shape_ENTITY_DEBUG@ + } void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { @@ -382,13 +251,13 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { const auto shape = getShape(); switch (shape){ - case entity::Shape::Quad: + case EntityShape::Quad: // Quads collide like flat Cubes - case entity::Shape::Cube: { + case EntityShape::Cube: { _collisionShapeType = SHAPE_TYPE_BOX; } break; - case entity::Shape::Sphere: { + case EntityShape::Sphere: { float diameter = entityDimensions.x; const float MIN_DIAMETER = 0.001f; const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; @@ -402,9 +271,9 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { } } break; - case entity::Shape::Circle: + case EntityShape::Circle: // Circles collide like flat Cylinders - case entity::Shape::Cylinder: { + case EntityShape::Cylinder: { float diameter = entityDimensions.x; const float MIN_DIAMETER = 0.001f; const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; @@ -421,7 +290,7 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { } } break; - case entity::Shape::Cone: { + case EntityShape::Cone: { if (hullShapeCalculator) { hullShapeCalculator(this, info); _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; @@ -431,9 +300,9 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { } break; // gons, ones, & angles built via GeometryCache::extrudePolygon - case entity::Shape::Triangle: - case entity::Shape::Hexagon: - case entity::Shape::Octagon: { + case EntityShape::Triangle: + case EntityShape::Hexagon: + case EntityShape::Octagon: { if (hullShapeCalculator) { hullShapeCalculator(this, info); _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; @@ -443,10 +312,10 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { } break; // hedrons built via GeometryCache::setUpFlatShapes - case entity::Shape::Tetrahedron: - case entity::Shape::Octahedron: - case entity::Shape::Dodecahedron: - case entity::Shape::Icosahedron: { + case EntityShape::Tetrahedron: + case EntityShape::Octahedron: + case EntityShape::Dodecahedron: + case EntityShape::Icosahedron: { if ( hullShapeCalculator ) { hullShapeCalculator(this, info); _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; @@ -455,7 +324,7 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { } } break; - case entity::Shape::Torus: { + case EntityShape::Torus: { // Not in GeometryCache::buildShapes, unsupported. _collisionShapeType = SHAPE_TYPE_ELLIPSOID; //TODO handle this shape more correctly diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h.in similarity index 54% rename from libraries/entities/src/ShapeEntityItem.h rename to libraries/entities/src/ShapeEntityItem.h.in index 1559949c8c..151e844f59 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h.in @@ -13,29 +13,6 @@ #include "PulsePropertyGroup.h" -namespace entity { - enum Shape { - Triangle, - Quad, - Hexagon, - Octagon, - Circle, - Cube, - Sphere, - Tetrahedron, - Octahedron, - Dodecahedron, - Icosahedron, - Torus, - Cone, - Cylinder, - NUM_SHAPES, - }; - - Shape shapeFromString(const ::QString& shapeString); - QString stringFromShape(Shape shape); -} - class ShapeEntityItem : public EntityItem { using Pointer = std::shared_ptr; static Pointer baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -51,39 +28,9 @@ public: void pureVirtualFunctionPlaceHolder() override { }; // Triggers warnings on OSX - //ALLOW_INSTANTIATION - - // methods for getting/setting all properties of an entity - EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - bool setSubClassProperties(const EntityItemProperties& properties) override; + //ALLOW_INSTANTIATION - EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - entity::Shape getShape() const; - void setShape(const entity::Shape& shape); - void setShape(const QString& shape) { setShape(entity::shapeFromString(shape)); } - - float getAlpha() const; - void setAlpha(float alpha); - - glm::u8vec3 getColor() const; - void setColor(const glm::u8vec3& value); - - bool getUnlit() const; - void setUnlit(bool unlit); + ENTITY_PROPERTY_SUBCLASS_METHODS void setUnscaledDimensions(const glm::vec3& value) override; @@ -98,8 +45,6 @@ public: QVariantMap& extraInfo, bool precisionPicking) const override; bool getRotateForPicking() const override; - void debugDump() const override; - virtual void computeShapeInfo(ShapeInfo& info) override; virtual ShapeType getShapeType() const override; @@ -108,11 +53,8 @@ public: void setUserData(const QString& value) override; protected: - glm::u8vec3 _color; - float _alpha { 1.0f }; - bool _unlit { false }; - PulsePropertyGroup _pulseProperties; - entity::Shape _shape { entity::Shape::Sphere }; + +@Shape_ENTITY_PROPS@ //! This is SHAPE_TYPE_ELLIPSOID rather than SHAPE_TYPE_NONE to maintain //! prior functionality where new or unsupported shapes are treated as diff --git a/libraries/entities/src/SkyboxPropertyGroup.cpp b/libraries/entities/src/SkyboxPropertyGroup.cpp deleted file mode 100644 index 9c3ad46fce..0000000000 --- a/libraries/entities/src/SkyboxPropertyGroup.cpp +++ /dev/null @@ -1,154 +0,0 @@ -// -// SkyboxPropertyGroup.cpp -// libraries/entities/src -// -// Created by Brad Hefta-Gaub on 12/4/13. -// Copyright 2013 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#include "SkyboxPropertyGroup.h" - -#include - -#include "EntityItemProperties.h" -#include "EntityItemPropertiesMacros.h" - -const glm::u8vec3 SkyboxPropertyGroup::DEFAULT_COLOR = { 0, 0, 0 }; - -void SkyboxPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, - bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { - auto nodeList = DependencyManager::get(); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color, u8vec3Color); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_IF_URL_PERMISSION(PROP_SKYBOX_URL, Skybox, skybox, URL, url); -} - -void SkyboxPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(skybox, color, u8vec3Color, setColor); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(skybox, url, QString, setURL); -} - -void SkyboxPropertyGroup::merge(const SkyboxPropertyGroup& other) { - COPY_PROPERTY_IF_CHANGED(color); - COPY_PROPERTY_IF_CHANGED(url); -} - - -void SkyboxPropertyGroup::debugDump() const { - qCDebug(entities) << " SkyboxPropertyGroup: ---------------------------------------------"; - qCDebug(entities) << " Color:" << getColor() << " has changed:" << colorChanged(); - qCDebug(entities) << " URL:" << getURL() << " has changed:" << urlChanged(); -} - -void SkyboxPropertyGroup::listChangedProperties(QList& out) { - if (colorChanged()) { - out << "skybox-color"; - } - if (urlChanged()) { - out << "skybox-url"; - } -} - -bool SkyboxPropertyGroup::appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_SKYBOX_COLOR, getColor()); - APPEND_ENTITY_PROPERTY(PROP_SKYBOX_URL, getURL()); - - return true; -} - - -bool SkyboxPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { - - int bytesRead = 0; - bool overwriteLocalData = true; - bool somethingChanged = false; - - READ_ENTITY_PROPERTY(PROP_SKYBOX_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY(PROP_SKYBOX_URL, QString, setURL); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_SKYBOX_COLOR, Color); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_SKYBOX_URL, URL); - - processedBytes += bytesRead; - - Q_UNUSED(somethingChanged); - - return true; -} - -void SkyboxPropertyGroup::markAllChanged() { - _colorChanged = true; - _urlChanged = true; -} - -EntityPropertyFlags SkyboxPropertyGroup::getChangedProperties() const { - EntityPropertyFlags changedProperties; - - CHECK_PROPERTY_CHANGE(PROP_SKYBOX_COLOR, color); - CHECK_PROPERTY_CHANGE(PROP_SKYBOX_URL, url); - - return changedProperties; -} - -void SkyboxPropertyGroup::getProperties(EntityItemProperties& properties) const { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Skybox, Color, getColor); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Skybox, URL, getURL); -} - -bool SkyboxPropertyGroup::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Skybox, Color, color, setColor); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Skybox, URL, url, setURL); - - return somethingChanged; -} - -EntityPropertyFlags SkyboxPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties; - - requestedProperties += PROP_SKYBOX_COLOR; - requestedProperties += PROP_SKYBOX_URL; - - return requestedProperties; -} - -void SkyboxPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_SKYBOX_COLOR, getColor()); - APPEND_ENTITY_PROPERTY(PROP_SKYBOX_URL, getURL()); -} - -int SkyboxPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_SKYBOX_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY(PROP_SKYBOX_URL, QString, setURL); - - return bytesRead; -} diff --git a/libraries/entities/src/SkyboxPropertyGroup.cpp.in b/libraries/entities/src/SkyboxPropertyGroup.cpp.in new file mode 100644 index 0000000000..6b43ac514b --- /dev/null +++ b/libraries/entities/src/SkyboxPropertyGroup.cpp.in @@ -0,0 +1,138 @@ +// +// SkyboxPropertyGroup.cpp +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "SkyboxPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" + +const glm::u8vec3 SkyboxPropertyGroup::DEFAULT_COLOR = { 0, 0, 0 }; + +void SkyboxPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + + auto nodeList = DependencyManager::get(); + +@Skybox_GROUP_COPY_TO_SCRIPT@ + +} + +void SkyboxPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + +@Skybox_GROUP_COPY_FROM_SCRIPT@ + +} + +void SkyboxPropertyGroup::merge(const SkyboxPropertyGroup& other) { + +@Skybox_GROUP_MERGE@ + +} + +void SkyboxPropertyGroup::debugDump() const { + +@Skybox_GROUP_DEBUG_DUMP@ + +} + +void SkyboxPropertyGroup::listChangedProperties(QList& out) { + +@Skybox_GROUP_LIST_CHANGED@ + +} + +bool SkyboxPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Skybox_GROUP_APPEND@ + + return successPropertyFits; +} + +bool SkyboxPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + +@Skybox_GROUP_READ@ + +@Skybox_GROUP_DECODE_CHANGED@ + + processedBytes += bytesRead; + + return somethingChanged; +} + +void SkyboxPropertyGroup::markAllChanged() { + +@Skybox_GROUP_MARK_CHANGED@ + +} + +EntityPropertyFlags SkyboxPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + +@Skybox_GROUP_CHANGED_PROPERTIES@ + + return changedProperties; +} + +void SkyboxPropertyGroup::getProperties(EntityItemProperties& properties) const { + +@Skybox_GROUP_COPY_TO@ + +} + +bool SkyboxPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Skybox_GROUP_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags SkyboxPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + +@Skybox_REQUESTED_PROPS@ + + return requestedProperties; +} + +int SkyboxPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Skybox_GROUP_READ@ + + return bytesRead; +} + +void SkyboxPropertyGroup::addPropertyMap(QHash& _propertyInfos, + QHash& _enumsToPropertyStrings) { + +@Skybox_GROUP_ADD_TO_MAP@ + +} diff --git a/libraries/entities/src/SkyboxPropertyGroup.h b/libraries/entities/src/SkyboxPropertyGroup.h deleted file mode 100644 index 30c9ef1d3a..0000000000 --- a/libraries/entities/src/SkyboxPropertyGroup.h +++ /dev/null @@ -1,94 +0,0 @@ -// -// SkyboxPropertyGroup.h -// libraries/entities/src -// -// Created by Brad Hefta-Gaub on 12/4/13. -// Copyright 2013 High Fidelity, Inc. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#ifndef hifi_SkyboxPropertyGroup_h -#define hifi_SkyboxPropertyGroup_h - -#include - -#include - -#include - -#include "PropertyGroup.h" -#include "EntityItemPropertiesMacros.h" - -class EntityItemProperties; -class EncodeBitstreamParams; -class OctreePacketData; -class EntityTreeElementExtraEncodeData; -class ReadBitstreamToTreeParams; -class ScriptEngine; -class ScriptValue; - -/*@jsdoc - * A skybox is defined by the following properties: - * @typedef {object} Entities.Skybox - * @property {Color} color=0,0,0 - Sets the color of the sky if url is "", otherwise modifies the - * color of the cube map image. - * @property {string} url="" - A cube map image that is used to render the sky. - */ -class SkyboxPropertyGroup : public PropertyGroup { -public: - // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const override; - virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; - - void merge(const SkyboxPropertyGroup& other); - - virtual void debugDump() const override; - virtual void listChangedProperties(QList& out) override; - - virtual bool appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt, int& processedBytes) override; - virtual void markAllChanged() override; - virtual EntityPropertyFlags getChangedProperties() const override; - - // EntityItem related helpers - // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const override; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - static const glm::u8vec3 DEFAULT_COLOR; - DEFINE_PROPERTY_REF(PROP_SKYBOX_COLOR, Color, color, glm::u8vec3, DEFAULT_COLOR); - DEFINE_PROPERTY_REF(PROP_SKYBOX_URL, URL, url, QString, ""); -}; - -#endif // hifi_SkyboxPropertyGroup_h diff --git a/libraries/entities/src/SkyboxPropertyGroup.h.in b/libraries/entities/src/SkyboxPropertyGroup.h.in new file mode 100644 index 0000000000..3961f25bad --- /dev/null +++ b/libraries/entities/src/SkyboxPropertyGroup.h.in @@ -0,0 +1,53 @@ +// +// SkyboxPropertyGroup.h +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_SkyboxPropertyGroup_h +#define hifi_SkyboxPropertyGroup_h + +#include + +#include + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +/*@jsdoc + * A skybox is defined by the following properties: + * @typedef {object} Entities.Skybox + * @property {Color} color=0,0,0 - Sets the color of the sky if url is "", otherwise modifies the + * color of the cube map image. + * @property {string} url="" - A cube map image that is used to render the sky. + */ +class SkyboxPropertyGroup : public PropertyGroup { +public: + ENTITY_PROPERTY_GROUP_METHODS(SkyboxPropertyGroup) + + static const glm::u8vec3 DEFAULT_COLOR; + +protected: + +@Skybox_GROUP_PROPS@ + +}; + +#endif // hifi_SkyboxPropertyGroup_h diff --git a/libraries/entities/src/SoundEntityItem.cpp b/libraries/entities/src/SoundEntityItem.cpp.in similarity index 76% rename from libraries/entities/src/SoundEntityItem.cpp rename to libraries/entities/src/SoundEntityItem.cpp.in index d8c648a61a..a308747bff 100644 --- a/libraries/entities/src/SoundEntityItem.cpp +++ b/libraries/entities/src/SoundEntityItem.cpp.in @@ -36,14 +36,7 @@ SoundEntityItem::~SoundEntityItem() { EntityItemProperties SoundEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(soundURL, getURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(volume, getVolume); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(timeOffset, getTimeOffset); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(pitch, getPitch); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(playing, getPlaying); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(loop, getLoop); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(positional, getPositional); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(localOnly, getLocalOnly); +@Sound_ENTITY_COPY_TO@ return properties; } @@ -51,49 +44,15 @@ EntityItemProperties SoundEntityItem::getProperties(const EntityPropertyFlags& d bool SoundEntityItem::setSubClassProperties(const EntityItemProperties& properties) { bool somethingChanged = false; - SET_ENTITY_PROPERTY_FROM_PROPERTIES(soundURL, setURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(volume, setVolume); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(timeOffset, setTimeOffset); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(pitch, setPitch); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(playing, setPlaying); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(loop, setLoop); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(positional, setPositional); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(localOnly, setLocalOnly); +@Sound_ENTITY_SET_FROM@ return somethingChanged; } -int SoundEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_SOUND_URL, QString, setURL); - READ_ENTITY_PROPERTY(PROP_SOUND_VOLUME, float, setVolume); - READ_ENTITY_PROPERTY(PROP_SOUND_TIME_OFFSET, float, setTimeOffset); - READ_ENTITY_PROPERTY(PROP_SOUND_PITCH, float, setPitch); - READ_ENTITY_PROPERTY(PROP_SOUND_PLAYING, bool, setPlaying); - READ_ENTITY_PROPERTY(PROP_SOUND_LOOP, bool, setLoop); - READ_ENTITY_PROPERTY(PROP_SOUND_POSITIONAL, bool, setPositional); - READ_ENTITY_PROPERTY(PROP_SOUND_LOCAL_ONLY, bool, setLocalOnly); - - return bytesRead; -} - EntityPropertyFlags SoundEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - requestedProperties += PROP_SOUND_URL; - requestedProperties += PROP_SOUND_VOLUME; - requestedProperties += PROP_SOUND_TIME_OFFSET; - requestedProperties += PROP_SOUND_PITCH; - requestedProperties += PROP_SOUND_PLAYING; - requestedProperties += PROP_SOUND_LOOP; - requestedProperties += PROP_SOUND_POSITIONAL; - requestedProperties += PROP_SOUND_LOCAL_ONLY; +@Sound_REQUESTED_PROPS@ return requestedProperties; } @@ -108,25 +67,33 @@ void SoundEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit bool successPropertyFits = true; - APPEND_ENTITY_PROPERTY(PROP_SOUND_URL, getURL()); - APPEND_ENTITY_PROPERTY(PROP_SOUND_VOLUME, getVolume()); - APPEND_ENTITY_PROPERTY(PROP_SOUND_TIME_OFFSET, getTimeOffset()); - APPEND_ENTITY_PROPERTY(PROP_SOUND_PITCH, getPitch()); - APPEND_ENTITY_PROPERTY(PROP_SOUND_PLAYING, getPlaying()); - APPEND_ENTITY_PROPERTY(PROP_SOUND_LOOP, getLoop()); - APPEND_ENTITY_PROPERTY(PROP_SOUND_POSITIONAL, getPositional()); - APPEND_ENTITY_PROPERTY(PROP_SOUND_LOCAL_ONLY, getLocalOnly()); +@Sound_ENTITY_APPEND@ + +} + +int SoundEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Sound_ENTITY_READ@ + + return bytesRead; } void SoundEntityItem::debugDump() const { - quint64 now = usecTimestampNow(); - qCDebug(entities) << "SOUND EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " name:" << _name; - qCDebug(entities) << " url:" << _url; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << "SOUND EntityItem Ptr:" << this; + qCDebug(entities) << "SoundEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@Sound_ENTITY_DEBUG@ + } bool SoundEntityItem::shouldCreateSound(const EntityTreePointer& tree) const { @@ -144,7 +111,7 @@ void SoundEntityItem::update(const quint64& now) { withReadLock([&] { if (shouldCreateSound(tree)) { - _sound = DependencyManager::get()->getSound(_url); + _sound = DependencyManager::get()->getSound(_soundURL); } }); @@ -168,11 +135,11 @@ void SoundEntityItem::dimensionsChanged() { updateSound(); } -void SoundEntityItem::setURL(const QString& value) { +void SoundEntityItem::setSoundURL(const QString& value) { bool changed = false; withWriteLock([&] { - if (value != _url) { - _url = value; + if (value != _soundURL) { + _soundURL = value; changed = true; } }); @@ -188,7 +155,7 @@ void SoundEntityItem::setURL(const QString& value) { withReadLock([&] { if (shouldCreateSound(tree)) { - _sound = DependencyManager::get()->getSound(_url); + _sound = DependencyManager::get()->getSound(_soundURL); } }); @@ -202,9 +169,9 @@ void SoundEntityItem::setURL(const QString& value) { } } -QString SoundEntityItem::getURL() const { +QString SoundEntityItem::getSoundURL() const { return resultWithReadLock([&] { - return _url; + return _soundURL; }); } @@ -349,7 +316,7 @@ void SoundEntityItem::setLocalOnly(bool value) { bool createdSound = false; withReadLock([&] { if (shouldCreateSound(tree)) { - _sound = DependencyManager::get()->getSound(_url); + _sound = DependencyManager::get()->getSound(_soundURL); createdSound = true; } }); diff --git a/libraries/entities/src/SoundEntityItem.h b/libraries/entities/src/SoundEntityItem.h deleted file mode 100644 index bd590d29d2..0000000000 --- a/libraries/entities/src/SoundEntityItem.h +++ /dev/null @@ -1,102 +0,0 @@ -// -// Created by HifiExperiments on 12/30/2023 -// Copyright 2023 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_SoundEntityItem_h -#define hifi_SoundEntityItem_h - -#include "EntityItem.h" - -#include -#include - -class SoundEntityItem : public EntityItem { -public: - static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - - SoundEntityItem(const EntityItemID& entityItemID); - ~SoundEntityItem(); - - ALLOW_INSTANTIATION // This class can be instantiated - - // methods for getting/setting all properties of an entity - EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - bool setSubClassProperties(const EntityItemProperties& properties) override; - - EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - bool shouldBePhysical() const override { return false; } - - virtual void debugDump() const override; - - virtual bool supportsDetailedIntersection() const override { return false; } - - virtual void update(const quint64& now) override; - bool needsToCallUpdate() const override { return _updateNeeded; } - - void locationChanged(bool tellPhysics = true, bool tellChildren = true) override; - void dimensionsChanged() override; - - void setURL(const QString& value); - QString getURL() const; - - void setVolume(float value); - float getVolume() const; - - void setTimeOffset(float value); - float getTimeOffset() const; - - void setPitch(float value); - float getPitch() const; - - void setPlaying(bool value); - bool getPlaying() const; - - void setLoop(bool value); - bool getLoop() const; - - void setPositional(bool value); - bool getPositional() const; - - void setLocalOnly(bool value); - bool getLocalOnly() const; - - bool restartSound(bool lock = false); - -protected: - bool shouldCreateSound(const EntityTreePointer& tree) const; - void updateSound(bool restart = false); - - QString _url { "" }; - float _volume { 1.0f }; - float _timeOffset { 0.0f }; - float _pitch { 1.0f }; - bool _playing { true }; - bool _loop { true }; - bool _positional { true }; - bool _localOnly { false }; - - std::recursive_mutex _soundLock; - SharedSoundPointer _sound; - AudioInjectorPointer _injector; - bool _updateNeeded { false }; -}; - -#endif // hifi_SoundEntityItem_h diff --git a/libraries/entities/src/SoundEntityItem.h.in b/libraries/entities/src/SoundEntityItem.h.in new file mode 100644 index 0000000000..c88345640a --- /dev/null +++ b/libraries/entities/src/SoundEntityItem.h.in @@ -0,0 +1,52 @@ +// +// Created by HifiExperiments on 12/30/2023 +// Copyright 2023 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_SoundEntityItem_h +#define hifi_SoundEntityItem_h + +#include "EntityItem.h" + +#include +#include + +class SoundEntityItem : public EntityItem { +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + SoundEntityItem(const EntityItemID& entityItemID); + ~SoundEntityItem(); + + ALLOW_INSTANTIATION // This class can be instantiated + ENTITY_PROPERTY_SUBCLASS_METHODS + + bool shouldBePhysical() const override { return false; } + + virtual bool supportsDetailedIntersection() const override { return false; } + + virtual void update(const quint64& now) override; + bool needsToCallUpdate() const override { return _updateNeeded; } + + void locationChanged(bool tellPhysics = true, bool tellChildren = true) override; + void dimensionsChanged() override; + + bool restartSound(bool lock = false); + +protected: + +@Sound_ENTITY_PROPS@ + + bool shouldCreateSound(const EntityTreePointer& tree) const; + void updateSound(bool restart = false); + + std::recursive_mutex _soundLock; + SharedSoundPointer _sound; + AudioInjectorPointer _injector; + bool _updateNeeded { false }; +}; + +#endif // hifi_SoundEntityItem_h diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp deleted file mode 100644 index 160a608c23..0000000000 --- a/libraries/entities/src/TextEntityItem.cpp +++ /dev/null @@ -1,413 +0,0 @@ -// -// TextEntityItem.cpp -// libraries/entities/src -// -// Created by Brad Hefta-Gaub on 12/4/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "TextEntityItem.h" - -#include - -#include - -#include -#include - -#include "EntityItemProperties.h" -#include "EntitiesLogging.h" -#include "EntityTree.h" -#include "EntityTreeElement.h" - -const QString TextEntityItem::DEFAULT_TEXT(""); -const float TextEntityItem::DEFAULT_LINE_HEIGHT = 0.1f; -const glm::u8vec3 TextEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 }; -const float TextEntityItem::DEFAULT_TEXT_ALPHA = 1.0f; -const glm::u8vec3 TextEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0}; -const float TextEntityItem::DEFAULT_MARGIN = 0.0f; -const float TextEntityItem::DEFAULT_TEXT_EFFECT_THICKNESS = 0.2f; - -EntityItemPointer TextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - std::shared_ptr entity(new TextEntityItem(entityID), [](TextEntityItem* ptr) { ptr->deleteLater(); }); - entity->setProperties(properties); - return entity; -} - -TextEntityItem::TextEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { - _type = EntityTypes::Text; -} - -void TextEntityItem::setUnscaledDimensions(const glm::vec3& value) { - const float TEXT_ENTITY_ITEM_FIXED_DEPTH = 0.01f; - // NOTE: Text Entities always have a "depth" of 1cm. - EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH)); -} - -EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { - EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - - withReadLock([&] { - _pulseProperties.getProperties(properties); - }); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(text, getText); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(textColor, getTextColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(textAlpha, getTextAlpha); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundColor, getBackgroundColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundAlpha, getBackgroundAlpha); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(leftMargin, getLeftMargin); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(rightMargin, getRightMargin); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(topMargin, getTopMargin); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(bottomMargin, getBottomMargin); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(unlit, getUnlit); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(font, getFont); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(textEffect, getTextEffect); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(textEffectColor, getTextEffectColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(textEffectThickness, getTextEffectThickness); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(alignment, getAlignment); - return properties; -} - -bool TextEntityItem::setSubClassProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - withWriteLock([&] { - bool pulsePropertiesChanged = _pulseProperties.setProperties(properties); - somethingChanged |= pulsePropertiesChanged; - _needsRenderUpdate |= pulsePropertiesChanged; - }); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(text, setText); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineHeight, setLineHeight); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(textColor, setTextColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(textAlpha, setTextAlpha); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundColor, setBackgroundColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundAlpha, setBackgroundAlpha); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(leftMargin, setLeftMargin); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(rightMargin, setRightMargin); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(topMargin, setTopMargin); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(bottomMargin, setBottomMargin); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(unlit, setUnlit); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(font, setFont); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(textEffect, setTextEffect); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(textEffectColor, setTextEffectColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(textEffectThickness, setTextEffectThickness); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(alignment, setAlignment); - - return somethingChanged; -} - -int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - withWriteLock([&] { - int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, - somethingChanged); - bytesRead += bytesFromPulse; - dataAt += bytesFromPulse; - }); - - READ_ENTITY_PROPERTY(PROP_TEXT, QString, setText); - READ_ENTITY_PROPERTY(PROP_LINE_HEIGHT, float, setLineHeight); - READ_ENTITY_PROPERTY(PROP_TEXT_COLOR, glm::u8vec3, setTextColor); - READ_ENTITY_PROPERTY(PROP_TEXT_ALPHA, float, setTextAlpha); - READ_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, glm::u8vec3, setBackgroundColor); - READ_ENTITY_PROPERTY(PROP_BACKGROUND_ALPHA, float, setBackgroundAlpha); - READ_ENTITY_PROPERTY(PROP_LEFT_MARGIN, float, setLeftMargin); - READ_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, float, setRightMargin); - READ_ENTITY_PROPERTY(PROP_TOP_MARGIN, float, setTopMargin); - READ_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, float, setBottomMargin); - READ_ENTITY_PROPERTY(PROP_UNLIT, bool, setUnlit); - READ_ENTITY_PROPERTY(PROP_FONT, QString, setFont); - READ_ENTITY_PROPERTY(PROP_TEXT_EFFECT, TextEffect, setTextEffect); - READ_ENTITY_PROPERTY(PROP_TEXT_EFFECT_COLOR, glm::u8vec3, setTextEffectColor); - READ_ENTITY_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, float, setTextEffectThickness); - READ_ENTITY_PROPERTY(PROP_TEXT_ALIGNMENT, TextAlignment, setAlignment); - - return bytesRead; -} - -EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - - requestedProperties += _pulseProperties.getEntityProperties(params); - - requestedProperties += PROP_TEXT; - requestedProperties += PROP_LINE_HEIGHT; - requestedProperties += PROP_TEXT_COLOR; - requestedProperties += PROP_TEXT_ALPHA; - requestedProperties += PROP_BACKGROUND_COLOR; - requestedProperties += PROP_BACKGROUND_ALPHA; - requestedProperties += PROP_LEFT_MARGIN; - requestedProperties += PROP_RIGHT_MARGIN; - requestedProperties += PROP_TOP_MARGIN; - requestedProperties += PROP_BOTTOM_MARGIN; - requestedProperties += PROP_UNLIT; - requestedProperties += PROP_FONT; - requestedProperties += PROP_TEXT_EFFECT; - requestedProperties += PROP_TEXT_EFFECT_COLOR; - requestedProperties += PROP_TEXT_EFFECT_THICKNESS; - requestedProperties += PROP_TEXT_ALIGNMENT; - - return requestedProperties; -} - -void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - withReadLock([&] { - _pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - }); - - APPEND_ENTITY_PROPERTY(PROP_TEXT, getText()); - APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, getLineHeight()); - APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, getTextColor()); - APPEND_ENTITY_PROPERTY(PROP_TEXT_ALPHA, getTextAlpha()); - APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, getBackgroundColor()); - APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_ALPHA, getBackgroundAlpha()); - APPEND_ENTITY_PROPERTY(PROP_LEFT_MARGIN, getLeftMargin()); - APPEND_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, getRightMargin()); - APPEND_ENTITY_PROPERTY(PROP_TOP_MARGIN, getTopMargin()); - APPEND_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, getBottomMargin()); - APPEND_ENTITY_PROPERTY(PROP_UNLIT, getUnlit()); - APPEND_ENTITY_PROPERTY(PROP_FONT, getFont()); - APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT, (uint32_t)getTextEffect()); - APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_COLOR, getTextEffectColor()); - APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, getTextEffectThickness()); - APPEND_ENTITY_PROPERTY(PROP_TEXT_ALIGNMENT, (uint32_t)getAlignment()); -} - -void TextEntityItem::setText(const QString& value) { - withWriteLock([&] { - _needsRenderUpdate |= _text != value; - _text = value; - }); -} - -QString TextEntityItem::getText() const { - return resultWithReadLock([&] { - return _text; - }); -} - -void TextEntityItem::setLineHeight(float value) { - withWriteLock([&] { - _needsRenderUpdate |= _lineHeight != value; - _lineHeight = value; - }); -} - -float TextEntityItem::getLineHeight() const { - float result; - withReadLock([&] { - result = _lineHeight; - }); - return result; -} - -void TextEntityItem::setTextColor(const glm::u8vec3& value) { - withWriteLock([&] { - _needsRenderUpdate |= _textColor != value; - _textColor = value; - }); -} - -glm::u8vec3 TextEntityItem::getTextColor() const { - return resultWithReadLock([&] { - return _textColor; - }); -} - -void TextEntityItem::setTextAlpha(float value) { - withWriteLock([&] { - _needsRenderUpdate |= _textAlpha != value; - _textAlpha = value; - }); -} - -float TextEntityItem::getTextAlpha() const { - return resultWithReadLock([&] { - return _textAlpha; - }); -} - -void TextEntityItem::setBackgroundColor(const glm::u8vec3& value) { - withWriteLock([&] { - _needsRenderUpdate |= _backgroundColor != value; - _backgroundColor = value; - }); -} - -glm::u8vec3 TextEntityItem::getBackgroundColor() const { - return resultWithReadLock([&] { - return _backgroundColor; - }); -} - -void TextEntityItem::setBackgroundAlpha(float value) { - withWriteLock([&] { - _needsRenderUpdate |= _backgroundAlpha != value; - _backgroundAlpha = value; - }); -} - -float TextEntityItem::getBackgroundAlpha() const { - return resultWithReadLock([&] { - return _backgroundAlpha; - }); -} - -void TextEntityItem::setLeftMargin(float value) { - withWriteLock([&] { - _needsRenderUpdate |= _leftMargin != value; - _leftMargin = value; - }); -} - -float TextEntityItem::getLeftMargin() const { - return resultWithReadLock([&] { - return _leftMargin; - }); -} - -void TextEntityItem::setRightMargin(float value) { - withWriteLock([&] { - _needsRenderUpdate |= _rightMargin != value; - _rightMargin = value; - }); -} - -float TextEntityItem::getRightMargin() const { - return resultWithReadLock([&] { - return _rightMargin; - }); -} - -void TextEntityItem::setTopMargin(float value) { - withWriteLock([&] { - _needsRenderUpdate |= _topMargin != value; - _topMargin = value; - }); -} - -float TextEntityItem::getTopMargin() const { - return resultWithReadLock([&] { - return _topMargin; - }); -} - -void TextEntityItem::setBottomMargin(float value) { - withWriteLock([&] { - _needsRenderUpdate |= _bottomMargin != value; - _bottomMargin = value; - }); -} - -float TextEntityItem::getBottomMargin() const { - return resultWithReadLock([&] { - return _bottomMargin; - }); -} - -void TextEntityItem::setUnlit(bool value) { - withWriteLock([&] { - _needsRenderUpdate |= _unlit != value; - _unlit = value; - }); -} - -bool TextEntityItem::getUnlit() const { - return resultWithReadLock([&] { - return _unlit; - }); -} - -void TextEntityItem::setFont(const QString& value) { - withWriteLock([&] { - _needsRenderUpdate |= _font != value; - _font = value; - }); -} - -QString TextEntityItem::getFont() const { - return resultWithReadLock([&] { - return _font; - }); -} - -void TextEntityItem::setTextEffect(TextEffect value) { - withWriteLock([&] { - _needsRenderUpdate |= _effect != value; - _effect = value; - }); -} - -TextEffect TextEntityItem::getTextEffect() const { - return resultWithReadLock([&] { - return _effect; - }); -} - -void TextEntityItem::setTextEffectColor(const glm::u8vec3& value) { - withWriteLock([&] { - _needsRenderUpdate |= _effectColor != value; - _effectColor = value; - }); -} - -glm::u8vec3 TextEntityItem::getTextEffectColor() const { - return resultWithReadLock([&] { - return _effectColor; - }); -} - -void TextEntityItem::setTextEffectThickness(float value) { - withWriteLock([&] { - _needsRenderUpdate |= _effectThickness != value; - _effectThickness = value; - }); -} - -float TextEntityItem::getTextEffectThickness() const { - return resultWithReadLock([&] { - return _effectThickness; - }); -} - -void TextEntityItem::setAlignment(TextAlignment value) { - withWriteLock([&] { - _needsRenderUpdate |= _alignment != value; - _alignment = value; - }); -} - -TextAlignment TextEntityItem::getAlignment() const { - return resultWithReadLock([&] { - return _alignment; - }); -} - -PulsePropertyGroup TextEntityItem::getPulseProperties() const { - return resultWithReadLock([&] { - return _pulseProperties; - }); -} diff --git a/libraries/entities/src/TextEntityItem.cpp.in b/libraries/entities/src/TextEntityItem.cpp.in new file mode 100644 index 0000000000..b5927e350a --- /dev/null +++ b/libraries/entities/src/TextEntityItem.cpp.in @@ -0,0 +1,117 @@ +// +// TextEntityItem.cpp +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "TextEntityItem.h" + +#include + +#include + +#include +#include + +#include "EntityItemProperties.h" +#include "EntitiesLogging.h" +#include "EntityTree.h" +#include "EntityTreeElement.h" + +const QString TextEntityItem::DEFAULT_TEXT(""); +const float TextEntityItem::DEFAULT_LINE_HEIGHT = 0.1f; +const glm::u8vec3 TextEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 }; +const float TextEntityItem::DEFAULT_TEXT_ALPHA = 1.0f; +const glm::u8vec3 TextEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0}; +const float TextEntityItem::DEFAULT_MARGIN = 0.0f; +const float TextEntityItem::DEFAULT_TEXT_EFFECT_THICKNESS = 0.2f; + +EntityItemPointer TextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + std::shared_ptr entity(new TextEntityItem(entityID), [](TextEntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +TextEntityItem::TextEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { + _type = EntityTypes::Text; +} + +void TextEntityItem::setUnscaledDimensions(const glm::vec3& value) { + const float TEXT_ENTITY_ITEM_FIXED_DEPTH = 0.01f; + // NOTE: Text Entities always have a "depth" of 1cm. + EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH)); +} + +EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + +@Text_ENTITY_COPY_TO@ + + return properties; +} + +bool TextEntityItem::setSubClassProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Text_ENTITY_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + +@Text_REQUESTED_PROPS@ + + return requestedProperties; +} + +void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Text_ENTITY_APPEND@ + +} + +int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Text_ENTITY_READ@ + + return bytesRead; +} + +void TextEntityItem::debugDump() const { + qCDebug(entities) << "TextEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@Text_ENTITY_DEBUG@ + +} + +PulsePropertyGroup TextEntityItem::getPulseProperties() const { + return resultWithReadLock([&] { + return _pulseProperties; + }); +} diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h deleted file mode 100644 index e39625060a..0000000000 --- a/libraries/entities/src/TextEntityItem.h +++ /dev/null @@ -1,128 +0,0 @@ -// -// TextEntityItem.h -// libraries/entities/src -// -// Created by Brad Hefta-Gaub on 12/4/13. -// Copyright 2013 High Fidelity, Inc. -// -// 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_TextEntityItem_h -#define hifi_TextEntityItem_h - -#include "EntityItem.h" - -#include "PulsePropertyGroup.h" - -class TextEntityItem : public EntityItem { -public: - static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - - TextEntityItem(const EntityItemID& entityItemID); - - ALLOW_INSTANTIATION // This class can be instantiated - - /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately - virtual void setUnscaledDimensions(const glm::vec3& value) override; - virtual ShapeType getShapeType() const override { return SHAPE_TYPE_BOX; } - - // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - virtual bool setSubClassProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - static const QString DEFAULT_TEXT; - void setText(const QString& value); - QString getText() const; - - static const float DEFAULT_LINE_HEIGHT; - void setLineHeight(float value); - float getLineHeight() const; - - static const glm::u8vec3 DEFAULT_TEXT_COLOR; - glm::u8vec3 getTextColor() const; - void setTextColor(const glm::u8vec3& value); - - static const float DEFAULT_TEXT_ALPHA; - float getTextAlpha() const; - void setTextAlpha(float value); - - static const glm::u8vec3 DEFAULT_BACKGROUND_COLOR; - glm::u8vec3 getBackgroundColor() const; - void setBackgroundColor(const glm::u8vec3& value); - - float getBackgroundAlpha() const; - void setBackgroundAlpha(float value); - - static const float DEFAULT_MARGIN; - float getLeftMargin() const; - void setLeftMargin(float value); - - float getRightMargin() const; - void setRightMargin(float value); - - float getTopMargin() const; - void setTopMargin(float value); - - float getBottomMargin() const; - void setBottomMargin(float value); - - bool getUnlit() const; - void setUnlit(bool value); - - void setFont(const QString& value); - QString getFont() const; - - TextEffect getTextEffect() const; - void setTextEffect(TextEffect value); - - glm::u8vec3 getTextEffectColor() const; - void setTextEffectColor(const glm::u8vec3& value); - - static const float DEFAULT_TEXT_EFFECT_THICKNESS; - float getTextEffectThickness() const; - void setTextEffectThickness(float value); - - TextAlignment getAlignment() const; - void setAlignment(TextAlignment value); - - PulsePropertyGroup getPulseProperties() const; - -private: - QString _text; - float _lineHeight; - glm::u8vec3 _textColor; - float _textAlpha; - glm::u8vec3 _backgroundColor; - float _backgroundAlpha; - PulsePropertyGroup _pulseProperties; - float _leftMargin; - float _rightMargin; - float _topMargin; - float _bottomMargin; - bool _unlit { false }; - - QString _font; - TextAlignment _alignment; - TextEffect _effect; - glm::u8vec3 _effectColor; - float _effectThickness; -}; - -#endif // hifi_TextEntityItem_h diff --git a/libraries/entities/src/TextEntityItem.h.in b/libraries/entities/src/TextEntityItem.h.in new file mode 100644 index 0000000000..ec236449f5 --- /dev/null +++ b/libraries/entities/src/TextEntityItem.h.in @@ -0,0 +1,48 @@ +// +// TextEntityItem.h +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 High Fidelity, Inc. +// +// 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_TextEntityItem_h +#define hifi_TextEntityItem_h + +#include "EntityItem.h" + +#include "PulsePropertyGroup.h" + +class TextEntityItem : public EntityItem { +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + TextEntityItem(const EntityItemID& entityItemID); + + ALLOW_INSTANTIATION // This class can be instantiated + ENTITY_PROPERTY_SUBCLASS_METHODS + + /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately + virtual void setUnscaledDimensions(const glm::vec3& value) override; + virtual ShapeType getShapeType() const override { return SHAPE_TYPE_BOX; } + + static const QString DEFAULT_TEXT; + static const float DEFAULT_LINE_HEIGHT; + static const glm::u8vec3 DEFAULT_TEXT_COLOR; + static const float DEFAULT_TEXT_ALPHA; + static const glm::u8vec3 DEFAULT_BACKGROUND_COLOR; + static const float DEFAULT_MARGIN; + static const float DEFAULT_TEXT_EFFECT_THICKNESS; + + PulsePropertyGroup getPulseProperties() const; + +private: + +@Text_ENTITY_PROPS@ + +}; + +#endif // hifi_TextEntityItem_h diff --git a/libraries/entities/src/TonemappingPropertyGroup.cpp b/libraries/entities/src/TonemappingPropertyGroup.cpp deleted file mode 100644 index 2613e3144d..0000000000 --- a/libraries/entities/src/TonemappingPropertyGroup.cpp +++ /dev/null @@ -1,167 +0,0 @@ -// -// TonemappingPropertyGroup.cpp -// libraries/entities/src -// -// Created by HifiExperiments on 6/23/24 -// Copyright 2024 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#include "TonemappingPropertyGroup.h" - -#include - -#include "EntityItemProperties.h" -#include "EntityItemPropertiesMacros.h" - -inline void addTonemappingCurve(QHash& lookup, TonemappingCurve curve) { lookup[TonemappingCurveHelpers::getNameForTonemappingCurve(curve)] = curve; } -const QHash stringToTonemappingCurveLookup = [] { - QHash toReturn; - addTonemappingCurve(toReturn, TonemappingCurve::RGB); - addTonemappingCurve(toReturn, TonemappingCurve::SRGB); - addTonemappingCurve(toReturn, TonemappingCurve::FILMIC); - addTonemappingCurve(toReturn, TonemappingCurve::REINHARD); - return toReturn; -}(); -QString TonemappingPropertyGroup::getCurveAsString() const { return TonemappingCurveHelpers::getNameForTonemappingCurve(_curve); } -void TonemappingPropertyGroup::setCurveFromString(const QString& curve) { - auto curveItr = stringToTonemappingCurveLookup.find(curve.toLower()); - if (curveItr != stringToTonemappingCurveLookup.end()) { - _curve = curveItr.value(); - _curveChanged = true; - } -} - -void TonemappingPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, - bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TONEMAPPING_CURVE, Tonemapping, tonemapping, Curve, curve, getCurveAsString); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_TONEMAPPING_EXPOSURE, Tonemapping, tonemapping, Exposure, exposure); -} - -void TonemappingPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(tonemapping, curve, Curve); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(tonemapping, exposure, float, setExposure); -} - -void TonemappingPropertyGroup::merge(const TonemappingPropertyGroup& other) { - COPY_PROPERTY_IF_CHANGED(curve); - COPY_PROPERTY_IF_CHANGED(exposure); -} - -void TonemappingPropertyGroup::debugDump() const { - qCDebug(entities) << " TonemappingPropertyGroup: ---------------------------------------------"; - qCDebug(entities) << " Curve:" << getCurveAsString(); - qCDebug(entities) << " Exposure:" << getExposure(); -} - -void TonemappingPropertyGroup::listChangedProperties(QList& out) { - if (curveChanged()) { - out << "tonemapping-curve"; - } - if (exposureChanged()) { - out << "tonemapping-exposure"; - } -} - -bool TonemappingPropertyGroup::appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_CURVE, (uint32_t)getCurve()); - APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_EXPOSURE, getExposure()); - - return true; -} - - -bool TonemappingPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { - - int bytesRead = 0; - bool overwriteLocalData = true; - bool somethingChanged = false; - - READ_ENTITY_PROPERTY(PROP_TONEMAPPING_CURVE, TonemappingCurve, setCurve); - READ_ENTITY_PROPERTY(PROP_TONEMAPPING_EXPOSURE, float, setExposure); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_TONEMAPPING_CURVE, Curve); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_TONEMAPPING_EXPOSURE, Exposure); - - processedBytes += bytesRead; - - Q_UNUSED(somethingChanged); - - return true; -} - -void TonemappingPropertyGroup::markAllChanged() { - _curveChanged = true; - _exposureChanged = true; -} - -EntityPropertyFlags TonemappingPropertyGroup::getChangedProperties() const { - EntityPropertyFlags changedProperties; - - CHECK_PROPERTY_CHANGE(PROP_TONEMAPPING_CURVE, curve); - CHECK_PROPERTY_CHANGE(PROP_TONEMAPPING_EXPOSURE, exposure); - - return changedProperties; -} - -void TonemappingPropertyGroup::getProperties(EntityItemProperties& properties) const { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Tonemapping, Curve, getCurve); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Tonemapping, Exposure, getExposure); -} - -bool TonemappingPropertyGroup::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Tonemapping, Curve, curve, setCurve); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Tonemapping, Exposure, exposure, setExposure); - - return somethingChanged; -} - -EntityPropertyFlags TonemappingPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties; - - requestedProperties += PROP_TONEMAPPING_CURVE; - requestedProperties += PROP_TONEMAPPING_EXPOSURE; - - return requestedProperties; -} - -void TonemappingPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_CURVE, (uint32_t)getCurve()); - APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_EXPOSURE, getExposure()); -} - -int TonemappingPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_TONEMAPPING_CURVE, TonemappingCurve, setCurve); - READ_ENTITY_PROPERTY(PROP_TONEMAPPING_EXPOSURE, float, setExposure); - - return bytesRead; -} diff --git a/libraries/entities/src/TonemappingPropertyGroup.cpp.in b/libraries/entities/src/TonemappingPropertyGroup.cpp.in new file mode 100644 index 0000000000..95319ae87a --- /dev/null +++ b/libraries/entities/src/TonemappingPropertyGroup.cpp.in @@ -0,0 +1,151 @@ +// +// TonemappingPropertyGroup.cpp +// libraries/entities/src +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "TonemappingPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" + +inline void addTonemappingCurve(QHash& lookup, TonemappingCurve curve) { lookup[TonemappingCurveHelpers::getNameForTonemappingCurve(curve)] = curve; } +const QHash stringToTonemappingCurveLookup = [] { + QHash toReturn; + addTonemappingCurve(toReturn, TonemappingCurve::RGB); + addTonemappingCurve(toReturn, TonemappingCurve::SRGB); + addTonemappingCurve(toReturn, TonemappingCurve::FILMIC); + addTonemappingCurve(toReturn, TonemappingCurve::REINHARD); + return toReturn; +}(); +QString TonemappingPropertyGroup::getCurveAsString() const { return TonemappingCurveHelpers::getNameForTonemappingCurve(_curve); } +void TonemappingPropertyGroup::setCurveFromString(const QString& curve) { + auto curveItr = stringToTonemappingCurveLookup.find(curve.toLower()); + if (curveItr != stringToTonemappingCurveLookup.end()) { + _curve = curveItr.value(); + _curveChanged = true; + } +} + +void TonemappingPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + +@Tonemapping_GROUP_COPY_TO_SCRIPT@ + +} + +void TonemappingPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + +@Tonemapping_GROUP_COPY_FROM_SCRIPT@ + +} + +void TonemappingPropertyGroup::merge(const TonemappingPropertyGroup& other) { + +@Tonemapping_GROUP_MERGE@ + +} + +void TonemappingPropertyGroup::debugDump() const { + +@Tonemapping_GROUP_DEBUG_DUMP@ + +} + +void TonemappingPropertyGroup::listChangedProperties(QList& out) { + +@Tonemapping_GROUP_LIST_CHANGED@ + +} + +bool TonemappingPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Tonemapping_GROUP_APPEND@ + + return successPropertyFits; +} + +bool TonemappingPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + +@Tonemapping_GROUP_READ@ + +@Tonemapping_GROUP_DECODE_CHANGED@ + + processedBytes += bytesRead; + + return somethingChanged; +} + +void TonemappingPropertyGroup::markAllChanged() { + +@Tonemapping_GROUP_MARK_CHANGED@ + +} + +EntityPropertyFlags TonemappingPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + +@Tonemapping_GROUP_CHANGED_PROPERTIES@ + + return changedProperties; +} + +void TonemappingPropertyGroup::getProperties(EntityItemProperties& properties) const { + +@Tonemapping_GROUP_COPY_TO@ + +} + +bool TonemappingPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Tonemapping_GROUP_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags TonemappingPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + +@Tonemapping_REQUESTED_PROPS@ + + return requestedProperties; +} + +int TonemappingPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Tonemapping_GROUP_READ@ + + return bytesRead; +} + +void TonemappingPropertyGroup::addPropertyMap(QHash& _propertyInfos, + QHash& _enumsToPropertyStrings) { + +@Tonemapping_GROUP_ADD_TO_MAP@ + +} diff --git a/libraries/entities/src/TonemappingPropertyGroup.h b/libraries/entities/src/TonemappingPropertyGroup.h deleted file mode 100644 index 70db875d8c..0000000000 --- a/libraries/entities/src/TonemappingPropertyGroup.h +++ /dev/null @@ -1,87 +0,0 @@ -// -// TonemappingPropertyGroup.h -// libraries/entities/src -// -// Created by HifiExperiments on 6/23/24 -// Copyright 2024 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#ifndef hifi_TonemappingPropertyGroup_h -#define hifi_TonemappingPropertyGroup_h - -#include - -#include "PropertyGroup.h" -#include "EntityItemPropertiesMacros.h" - -class EntityItemProperties; -class EncodeBitstreamParams; -class OctreePacketData; -class EntityTreeElementExtraEncodeData; -class ReadBitstreamToTreeParams; -class ScriptEngine; -class ScriptValue; - -/*@jsdoc - * Tonemapping is defined by the following properties: - * @typedef {object} Entities.Tonemapping - * @property {TonemappingCurve} curve="srgb" - The tonemapping curve used. - * @property {number} exposure=0.0 - The applied exposure. - */ -class TonemappingPropertyGroup : public PropertyGroup { -public: - // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const override; - virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; - - void merge(const TonemappingPropertyGroup& other); - - virtual void debugDump() const override; - virtual void listChangedProperties(QList& out) override; - - virtual bool appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt, int& processedBytes) override; - virtual void markAllChanged() override; - virtual EntityPropertyFlags getChangedProperties() const override; - - // EntityItem related helpers - // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const override; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - DEFINE_PROPERTY_REF_ENUM(PROP_TONEMAPPING_CURVE, Curve, curve, TonemappingCurve, TonemappingCurve::SRGB); - DEFINE_PROPERTY(PROP_TONEMAPPING_EXPOSURE, Exposure, exposure, float, 0.0); -}; - -#endif // hifi_TonemappingPropertyGroup_h diff --git a/libraries/entities/src/TonemappingPropertyGroup.h.in b/libraries/entities/src/TonemappingPropertyGroup.h.in new file mode 100644 index 0000000000..5b3c437dae --- /dev/null +++ b/libraries/entities/src/TonemappingPropertyGroup.h.in @@ -0,0 +1,45 @@ +// +// TonemappingPropertyGroup.h +// libraries/entities/src +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_TonemappingPropertyGroup_h +#define hifi_TonemappingPropertyGroup_h + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +/*@jsdoc + * Tonemapping is defined by the following properties: + * @typedef {object} Entities.Tonemapping + * @property {TonemappingCurve} curve="srgb" - The tonemapping curve used. + * @property {number} exposure=0.0 - The applied exposure. + */ +class TonemappingPropertyGroup : public PropertyGroup { +public: + ENTITY_PROPERTY_GROUP_METHODS(TonemappingPropertyGroup) + +protected: + +@Tonemapping_GROUP_PROPS@ + +}; + +#endif // hifi_TonemappingPropertyGroup_h diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp deleted file mode 100644 index 579a656069..0000000000 --- a/libraries/entities/src/WebEntityItem.cpp +++ /dev/null @@ -1,322 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015/05/12 -// Copyright 2013 High Fidelity, Inc. -// Copyright 2020 Vircadia contributors. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "WebEntityItem.h" - -#include - -#include -#include - -#include -#include -#include -#include - -#include "EntitiesLogging.h" -#include "EntityItemProperties.h" -#include "EntityTree.h" -#include "EntityTreeElement.h" - -const QString WebEntityItem::DEFAULT_SOURCE_URL = NetworkingConstants::WEB_ENTITY_DEFAULT_SOURCE_URL; -const QString WebEntityItem::DEFAULT_USER_AGENT = NetworkingConstants::WEB_ENTITY_DEFAULT_USER_AGENT; -const uint8_t WebEntityItem::DEFAULT_MAX_FPS = 10; - -EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - std::shared_ptr entity(new WebEntityItem(entityID), [](WebEntityItem* ptr) { ptr->deleteLater(); }); - entity->setProperties(properties); - return entity; -} - -WebEntityItem::WebEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { - // this initialzation of localSafeContext is reading a thread-local variable and that is depends on - // the ctor being executed on the same thread as the script, assuming it's being create by a script - _localSafeContext = hifi::scripting::isLocalAccessSafeThread(); - _type = EntityTypes::Web; -} - -void WebEntityItem::setUnscaledDimensions(const glm::vec3& value) { - // NOTE: Web Entities always have a "depth" of 1cm. - const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f; - EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, WEB_ENTITY_ITEM_FIXED_DEPTH)); -} - -EntityItemProperties WebEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { - EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); - withReadLock([&] { - _pulseProperties.getProperties(properties); - }); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(sourceUrl, getSourceUrl); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(dpi, getDPI); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(scriptURL, getScriptURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxFPS, getMaxFPS); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(inputMode, getInputMode); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(wantsKeyboardFocus, wantsKeyboardFocus); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(showKeyboardFocusHighlight, getShowKeyboardFocusHighlight); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(useBackground, getUseBackground); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(userAgent, getUserAgent); - return properties; -} - -bool WebEntityItem::setSubClassProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha); - withWriteLock([&] { - bool pulsePropertiesChanged = _pulseProperties.setProperties(properties); - somethingChanged |= pulsePropertiesChanged; - _needsRenderUpdate |= pulsePropertiesChanged; - }); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(sourceUrl, setSourceUrl); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(dpi, setDPI); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(scriptURL, setScriptURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxFPS, setMaxFPS); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(inputMode, setInputMode); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(wantsKeyboardFocus, setWantsKeyboardFocus); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(showKeyboardFocusHighlight, setShowKeyboardFocusHighlight); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(useBackground, setUseBackground); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(userAgent, setUserAgent); - - return somethingChanged; -} - -int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); - READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); - withWriteLock([&] { - int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, - somethingChanged); - bytesRead += bytesFromPulse; - dataAt += bytesFromPulse; - }); - - READ_ENTITY_PROPERTY(PROP_SOURCE_URL, QString, setSourceUrl); - READ_ENTITY_PROPERTY(PROP_DPI, uint16_t, setDPI); - READ_ENTITY_PROPERTY(PROP_SCRIPT_URL, QString, setScriptURL); - READ_ENTITY_PROPERTY(PROP_MAX_FPS, uint8_t, setMaxFPS); - READ_ENTITY_PROPERTY(PROP_INPUT_MODE, WebInputMode, setInputMode); - READ_ENTITY_PROPERTY(PROP_WANTS_KEYBOARD_FOCUS, bool, setWantsKeyboardFocus); - READ_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, bool, setShowKeyboardFocusHighlight); - READ_ENTITY_PROPERTY(PROP_WEB_USE_BACKGROUND, bool, setUseBackground); - READ_ENTITY_PROPERTY(PROP_USER_AGENT, QString, setUserAgent); - - return bytesRead; -} - -EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - requestedProperties += PROP_COLOR; - requestedProperties += PROP_ALPHA; - requestedProperties += _pulseProperties.getEntityProperties(params); - - requestedProperties += PROP_SOURCE_URL; - requestedProperties += PROP_DPI; - requestedProperties += PROP_SCRIPT_URL; - requestedProperties += PROP_MAX_FPS; - requestedProperties += PROP_INPUT_MODE; - requestedProperties += PROP_WANTS_KEYBOARD_FOCUS; - requestedProperties += PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT; - requestedProperties += PROP_WEB_USE_BACKGROUND; - requestedProperties += PROP_USER_AGENT; - return requestedProperties; -} - -void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); - withReadLock([&] { - _pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - }); - - APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, getSourceUrl()); - APPEND_ENTITY_PROPERTY(PROP_DPI, getDPI()); - APPEND_ENTITY_PROPERTY(PROP_SCRIPT_URL, getScriptURL()); - APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, getMaxFPS()); - APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)getInputMode()); - APPEND_ENTITY_PROPERTY(PROP_WANTS_KEYBOARD_FOCUS, wantsKeyboardFocus()); - APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, getShowKeyboardFocusHighlight()); - APPEND_ENTITY_PROPERTY(PROP_WEB_USE_BACKGROUND, getUseBackground()); - APPEND_ENTITY_PROPERTY(PROP_USER_AGENT, getUserAgent()); -} - -void WebEntityItem::setColor(const glm::u8vec3& value) { - withWriteLock([&] { - _needsRenderUpdate |= _color != value; - _color = value; - }); -} - -glm::u8vec3 WebEntityItem::getColor() const { - return resultWithReadLock([&] { - return _color; - }); -} - -bool WebEntityItem::getLocalSafeContext() const { - return resultWithReadLock([&] { - return _localSafeContext; - }); -} - -void WebEntityItem::setAlpha(float alpha) { - withWriteLock([&] { - _needsRenderUpdate |= _alpha != alpha; - _alpha = alpha; - }); -} - -float WebEntityItem::getAlpha() const { - return resultWithReadLock([&] { - return _alpha; - }); -} - -void WebEntityItem::setSourceUrl(const QString& value) { - withWriteLock([&] { - _needsRenderUpdate |= _sourceUrl != value; - _sourceUrl = value; - }); -} - -QString WebEntityItem::getSourceUrl() const { - return resultWithReadLock([&] { - return _sourceUrl; - }); -} - -void WebEntityItem::setDPI(uint16_t value) { - withWriteLock([&] { - _needsRenderUpdate |= _dpi != value; - _dpi = value; - }); -} - -uint16_t WebEntityItem::getDPI() const { - return resultWithReadLock([&] { - return _dpi; - }); -} - -void WebEntityItem::setScriptURL(const QString& value) { - auto newURL = QUrl::fromUserInput(value); - - if (!newURL.isValid()) { - qCDebug(entities) << "Not setting web entity script URL since" << value << "cannot be parsed to a valid URL."; - return; - } - - auto urlString = newURL.toDisplayString(); - - withWriteLock([&] { - _needsRenderUpdate |= _scriptURL != urlString; - _scriptURL = urlString; - }); -} - -QString WebEntityItem::getScriptURL() const { - return resultWithReadLock([&] { - return _scriptURL; - }); -} - -void WebEntityItem::setMaxFPS(uint8_t value) { - withWriteLock([&] { - _needsRenderUpdate |= _maxFPS != value; - _maxFPS = value; - }); -} - -uint8_t WebEntityItem::getMaxFPS() const { - return resultWithReadLock([&] { - return _maxFPS; - }); -} - -void WebEntityItem::setInputMode(const WebInputMode& value) { - withWriteLock([&] { - _needsRenderUpdate |= _inputMode != value; - _inputMode = value; - }); -} - -WebInputMode WebEntityItem::getInputMode() const { - return resultWithReadLock([&] { - return _inputMode; - }); -} - -void WebEntityItem::setWantsKeyboardFocus(bool value) { - withWriteLock([&] { - _needsRenderUpdate |= _wantsKeyboardFocus != value; - _wantsKeyboardFocus = value; - }); -} - -bool WebEntityItem::wantsKeyboardFocus() const { - return _wantsKeyboardFocus; -} - -void WebEntityItem::setShowKeyboardFocusHighlight(bool value) { - _showKeyboardFocusHighlight = value; -} - -bool WebEntityItem::getShowKeyboardFocusHighlight() const { - return _showKeyboardFocusHighlight; -} - -void WebEntityItem::setUseBackground(bool value) { - withWriteLock([&] { - _needsRenderUpdate |= _useBackground != value; - _useBackground = value; - }); -} - -bool WebEntityItem::getUseBackground() const { - return resultWithReadLock([&] { return _useBackground; }); -} - -void WebEntityItem::setUserAgent(const QString& value) { - withWriteLock([&] { - _needsRenderUpdate |= _userAgent != value; - _userAgent = value; - }); -} - -QString WebEntityItem::getUserAgent() const { - return resultWithReadLock([&] { return _userAgent; }); -} - -PulsePropertyGroup WebEntityItem::getPulseProperties() const { - return resultWithReadLock([&] { - return _pulseProperties; - }); -} diff --git a/libraries/entities/src/WebEntityItem.cpp.in b/libraries/entities/src/WebEntityItem.cpp.in new file mode 100644 index 0000000000..c6e1129dcd --- /dev/null +++ b/libraries/entities/src/WebEntityItem.cpp.in @@ -0,0 +1,145 @@ +// +// Created by Bradley Austin Davis on 2015/05/12 +// Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "WebEntityItem.h" + +#include + +#include +#include + +#include +#include +#include +#include + +#include "EntitiesLogging.h" +#include "EntityItemProperties.h" +#include "EntityTree.h" +#include "EntityTreeElement.h" + +const QString WebEntityItem::DEFAULT_SOURCE_URL = NetworkingConstants::WEB_ENTITY_DEFAULT_SOURCE_URL; +const QString WebEntityItem::DEFAULT_USER_AGENT = NetworkingConstants::WEB_ENTITY_DEFAULT_USER_AGENT; +const uint8_t WebEntityItem::DEFAULT_MAX_FPS = 10; + +EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + std::shared_ptr entity(new WebEntityItem(entityID), [](WebEntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +WebEntityItem::WebEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { + // this initialzation of localSafeContext is reading a thread-local variable and that is depends on + // the ctor being executed on the same thread as the script, assuming it's being create by a script + _localSafeContext = hifi::scripting::isLocalAccessSafeThread(); + _type = EntityTypes::Web; +} + +void WebEntityItem::setUnscaledDimensions(const glm::vec3& value) { + // NOTE: Web Entities always have a "depth" of 1cm. + const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f; + EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, WEB_ENTITY_ITEM_FIXED_DEPTH)); +} + +EntityItemProperties WebEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + +@Web_ENTITY_COPY_TO@ + + return properties; +} + +bool WebEntityItem::setSubClassProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Web_ENTITY_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + +@Web_REQUESTED_PROPS@ + + return requestedProperties; +} + +void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Web_ENTITY_APPEND@ + +} + +int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Web_ENTITY_READ@ + + return bytesRead; +} + +void WebEntityItem::debugDump() const { + qCDebug(entities) << "WebEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@Web_ENTITY_DEBUG@ + +} + +bool WebEntityItem::getLocalSafeContext() const { + return resultWithReadLock([&] { + return _localSafeContext; + }); +} + +void WebEntityItem::setScriptURL(const QString& value) { + auto newURL = QUrl::fromUserInput(value); + + if (!newURL.isValid()) { + qCDebug(entities) << "Not setting web entity script URL since" << value << "cannot be parsed to a valid URL."; + return; + } + + auto urlString = newURL.toDisplayString(); + + withWriteLock([&] { + _needsRenderUpdate |= _scriptURL != urlString; + _scriptURL = urlString; + }); +} + +QString WebEntityItem::getScriptURL() const { + return resultWithReadLock([&] { + return _scriptURL; + }); +} + +PulsePropertyGroup WebEntityItem::getPulseProperties() const { + return resultWithReadLock([&] { + return _pulseProperties; + }); +} diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h deleted file mode 100644 index 86a9717862..0000000000 --- a/libraries/entities/src/WebEntityItem.h +++ /dev/null @@ -1,107 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015/05/12 -// Copyright 2013 High Fidelity, Inc. -// Copyright 2020 Vircadia contributors. -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#ifndef hifi_WebEntityItem_h -#define hifi_WebEntityItem_h - -#include "EntityItem.h" - -#include "PulsePropertyGroup.h" - -class WebEntityItem : public EntityItem { -public: - static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - - WebEntityItem(const EntityItemID& entityItemID); - - ALLOW_INSTANTIATION // This class can be instantiated - - /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately - virtual void setUnscaledDimensions(const glm::vec3& value) override; - virtual ShapeType getShapeType() const override { return SHAPE_TYPE_BOX; } - - // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - virtual bool setSubClassProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - glm::u8vec3 getColor() const; - void setColor(const glm::u8vec3& value); - - float getAlpha() const; - void setAlpha(float alpha); - - static const QString DEFAULT_SOURCE_URL; - void setSourceUrl(const QString& value); - QString getSourceUrl() const; - - void setDPI(uint16_t value); - uint16_t getDPI() const; - - void setScriptURL(const QString& value); - QString getScriptURL() const; - - bool getLocalSafeContext() const; - - static const uint8_t DEFAULT_MAX_FPS; - void setMaxFPS(uint8_t value); - uint8_t getMaxFPS() const; - - void setInputMode(const WebInputMode& value); - WebInputMode getInputMode() const; - - bool wantsKeyboardFocus() const; - void setWantsKeyboardFocus(bool value); - - bool getShowKeyboardFocusHighlight() const; - void setShowKeyboardFocusHighlight(bool value); - - bool getUseBackground() const; - void setUseBackground(bool value); - - static const QString DEFAULT_USER_AGENT; - QString getUserAgent() const; - void setUserAgent(const QString& value); - - PulsePropertyGroup getPulseProperties() const; - -protected: - glm::u8vec3 _color; - float _alpha { 1.0f }; - PulsePropertyGroup _pulseProperties; - - QString _sourceUrl; - uint16_t _dpi; - QString _scriptURL; - uint8_t _maxFPS; - WebInputMode _inputMode; - bool _wantsKeyboardFocus { false }; - bool _showKeyboardFocusHighlight { false }; - bool _useBackground { false }; - QString _userAgent; - bool _localSafeContext { false }; -}; - -#endif // hifi_WebEntityItem_h diff --git a/libraries/entities/src/WebEntityItem.h.in b/libraries/entities/src/WebEntityItem.h.in new file mode 100644 index 0000000000..df28f893f4 --- /dev/null +++ b/libraries/entities/src/WebEntityItem.h.in @@ -0,0 +1,50 @@ +// +// Created by Bradley Austin Davis on 2015/05/12 +// Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_WebEntityItem_h +#define hifi_WebEntityItem_h + +#include "EntityItem.h" + +#include "PulsePropertyGroup.h" + +class WebEntityItem : public EntityItem { +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + WebEntityItem(const EntityItemID& entityItemID); + + ALLOW_INSTANTIATION // This class can be instantiated + ENTITY_PROPERTY_SUBCLASS_METHODS + + /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately + virtual void setUnscaledDimensions(const glm::vec3& value) override; + virtual ShapeType getShapeType() const override { return SHAPE_TYPE_BOX; } + + void setScriptURL(const QString& value); + QString getScriptURL() const; + + bool getLocalSafeContext() const; + + static const QString DEFAULT_SOURCE_URL; + static const uint8_t DEFAULT_MAX_FPS; + static const QString DEFAULT_USER_AGENT; + + PulsePropertyGroup getPulseProperties() const; + +protected: + +@Web_ENTITY_PROPS@ + + bool _localSafeContext { false }; +}; + +#endif // hifi_WebEntityItem_h diff --git a/libraries/entities/src/ZoneAudioPropertyGroup.cpp b/libraries/entities/src/ZoneAudioPropertyGroup.cpp deleted file mode 100644 index 34bde41e1d..0000000000 --- a/libraries/entities/src/ZoneAudioPropertyGroup.cpp +++ /dev/null @@ -1,195 +0,0 @@ -// -// ZoneAudioPropertyGroup.cpp -// libraries/entities/src -// -// Created by HifiExperiments on 11/28/23 -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#include "ZoneAudioPropertyGroup.h" - -#include - -#include "EntityItemProperties.h" -#include "EntityItemPropertiesMacros.h" - -void ZoneAudioPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_REVERB_ENABLED, Audio, audio, ReverbEnabled, reverbEnabled); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_REVERB_TIME, Audio, audio, ReverbTime, reverbTime); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_REVERB_WET_LEVEL, Audio, audio, ReverbWetLevel, reverbWetLevel); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_LISTENER_ZONES, Audio, audio, ListenerZones, listenerZones); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_LISTENER_ATTENUATION_COEFFICIENTS, Audio, audio, ListenerAttenuationCoefficients, listenerAttenuationCoefficients); -} - -void ZoneAudioPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, reverbEnabled, bool, setReverbEnabled); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, reverbTime, float, setReverbTime); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, reverbWetLevel, float, setReverbWetLevel); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, listenerZones, qVectorQUuid, setListenerZones); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, listenerAttenuationCoefficients, qVectorFloat, setListenerAttenuationCoefficients); -} - -void ZoneAudioPropertyGroup::merge(const ZoneAudioPropertyGroup& other) { - COPY_PROPERTY_IF_CHANGED(reverbEnabled); - COPY_PROPERTY_IF_CHANGED(reverbTime); - COPY_PROPERTY_IF_CHANGED(reverbWetLevel); - COPY_PROPERTY_IF_CHANGED(listenerZones); - COPY_PROPERTY_IF_CHANGED(listenerAttenuationCoefficients); -} - -void ZoneAudioPropertyGroup::debugDump() const { - qCDebug(entities) << " ZoneAudioPropertyGroup: ---------------------------------------------"; - qCDebug(entities) << " _reverbEnabled:" << _reverbEnabled; - qCDebug(entities) << " _reverbTime:" << _reverbTime; - qCDebug(entities) << " _reverbWetLevel:" << _reverbWetLevel; - qCDebug(entities) << " _listenerZones:" << _listenerZones; - qCDebug(entities) << " _listenerAttenuationCoefficients:" << _listenerAttenuationCoefficients; -} - -void ZoneAudioPropertyGroup::listChangedProperties(QList& out) { - if (reverbEnabledChanged()) { - out << "reverbEnabled"; - } - if (reverbTimeChanged()) { - out << "reverbTime"; - } - if (reverbWetLevelChanged()) { - out << "reverbWetLevel"; - } - if (listenerZonesChanged()) { - out << "listenerZones"; - } - if (listenerAttenuationCoefficientsChanged()) { - out << "listenerAttenuationCoefficients"; - } -} - -bool ZoneAudioPropertyGroup::appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_REVERB_ENABLED, getReverbEnabled()); - APPEND_ENTITY_PROPERTY(PROP_REVERB_TIME, getReverbTime()); - APPEND_ENTITY_PROPERTY(PROP_REVERB_WET_LEVEL, getReverbWetLevel()); - APPEND_ENTITY_PROPERTY(PROP_LISTENER_ZONES, getListenerZones()); - APPEND_ENTITY_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, getListenerAttenuationCoefficients()); - - return true; -} - -bool ZoneAudioPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { - int bytesRead = 0; - bool overwriteLocalData = true; - bool somethingChanged = false; - - READ_ENTITY_PROPERTY(PROP_REVERB_ENABLED, bool, setReverbEnabled); - READ_ENTITY_PROPERTY(PROP_REVERB_TIME, float, setReverbTime); - READ_ENTITY_PROPERTY(PROP_REVERB_WET_LEVEL, float, setReverbWetLevel); - READ_ENTITY_PROPERTY(PROP_LISTENER_ZONES, QVector, setListenerZones); - READ_ENTITY_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, QVector, setListenerAttenuationCoefficients); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_REVERB_ENABLED, ReverbEnabled); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_REVERB_TIME, ReverbTime); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_REVERB_WET_LEVEL, ReverbWetLevel); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_LISTENER_ZONES, ListenerZones); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_LISTENER_ATTENUATION_COEFFICIENTS, ListenerAttenuationCoefficients); - - processedBytes += bytesRead; - - Q_UNUSED(somethingChanged); - - return true; -} - -void ZoneAudioPropertyGroup::markAllChanged() { - _reverbEnabledChanged = true; - _reverbTimeChanged = true; - _reverbWetLevelChanged = true; - _listenerZonesChanged = true; - _listenerAttenuationCoefficientsChanged = true; -} - -EntityPropertyFlags ZoneAudioPropertyGroup::getChangedProperties() const { - EntityPropertyFlags changedProperties; - - CHECK_PROPERTY_CHANGE(PROP_REVERB_ENABLED, reverbEnabled); - CHECK_PROPERTY_CHANGE(PROP_REVERB_TIME, reverbTime); - CHECK_PROPERTY_CHANGE(PROP_REVERB_WET_LEVEL, reverbWetLevel); - CHECK_PROPERTY_CHANGE(PROP_LISTENER_ZONES, listenerZones); - CHECK_PROPERTY_CHANGE(PROP_LISTENER_ATTENUATION_COEFFICIENTS, listenerAttenuationCoefficients); - - return changedProperties; -} - -void ZoneAudioPropertyGroup::getProperties(EntityItemProperties& properties) const { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ReverbEnabled, getReverbEnabled); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ReverbTime, getReverbTime); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ReverbWetLevel, getReverbWetLevel); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ListenerZones, getListenerZones); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ListenerAttenuationCoefficients, getListenerAttenuationCoefficients); -} - -bool ZoneAudioPropertyGroup::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ReverbEnabled, reverbEnabled, setReverbEnabled); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ReverbTime, reverbTime, setReverbTime); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ReverbWetLevel, reverbWetLevel, setReverbWetLevel); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ListenerZones, listenerZones, setListenerZones); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ListenerAttenuationCoefficients, listenerAttenuationCoefficients, setListenerAttenuationCoefficients); - - return somethingChanged; -} - -EntityPropertyFlags ZoneAudioPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties; - - requestedProperties += PROP_REVERB_ENABLED; - requestedProperties += PROP_REVERB_TIME; - requestedProperties += PROP_REVERB_WET_LEVEL; - requestedProperties += PROP_LISTENER_ZONES; - requestedProperties += PROP_LISTENER_ATTENUATION_COEFFICIENTS; - - return requestedProperties; -} - -void ZoneAudioPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_REVERB_ENABLED, getReverbEnabled()); - APPEND_ENTITY_PROPERTY(PROP_REVERB_TIME, getReverbTime()); - APPEND_ENTITY_PROPERTY(PROP_REVERB_WET_LEVEL, getReverbWetLevel()); - APPEND_ENTITY_PROPERTY(PROP_LISTENER_ZONES, getListenerZones()); - APPEND_ENTITY_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, getListenerAttenuationCoefficients()); -} - -int ZoneAudioPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_REVERB_ENABLED, bool, setReverbEnabled); - READ_ENTITY_PROPERTY(PROP_REVERB_TIME, float, setReverbTime); - READ_ENTITY_PROPERTY(PROP_REVERB_WET_LEVEL, float, setReverbWetLevel); - READ_ENTITY_PROPERTY(PROP_LISTENER_ZONES, QVector, setListenerZones); - READ_ENTITY_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, QVector, setListenerAttenuationCoefficients); - - return bytesRead; -} diff --git a/libraries/entities/src/ZoneAudioPropertyGroup.cpp.in b/libraries/entities/src/ZoneAudioPropertyGroup.cpp.in new file mode 100644 index 0000000000..9d9226eff4 --- /dev/null +++ b/libraries/entities/src/ZoneAudioPropertyGroup.cpp.in @@ -0,0 +1,132 @@ +// +// ZoneAudioPropertyGroup.cpp +// libraries/entities/src +// +// Created by HifiExperiments on 11/28/23 +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ZoneAudioPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" +void ZoneAudioPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + +@Audio_GROUP_COPY_TO_SCRIPT@ + +} + +void ZoneAudioPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + +@Audio_GROUP_COPY_FROM_SCRIPT@ + +} + +void ZoneAudioPropertyGroup::merge(const ZoneAudioPropertyGroup& other) { + +@Audio_GROUP_MERGE@ + +} + +void ZoneAudioPropertyGroup::debugDump() const { + +@Audio_GROUP_DEBUG_DUMP@ + +} + +void ZoneAudioPropertyGroup::listChangedProperties(QList& out) { + +@Audio_GROUP_LIST_CHANGED@ + +} + +bool ZoneAudioPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Audio_GROUP_APPEND@ + + return successPropertyFits; +} + +bool ZoneAudioPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + +@Audio_GROUP_READ@ + +@Audio_GROUP_DECODE_CHANGED@ + + processedBytes += bytesRead; + + return somethingChanged; +} + +void ZoneAudioPropertyGroup::markAllChanged() { + +@Audio_GROUP_MARK_CHANGED@ + +} + +EntityPropertyFlags ZoneAudioPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + +@Audio_GROUP_CHANGED_PROPERTIES@ + + return changedProperties; +} + +void ZoneAudioPropertyGroup::getProperties(EntityItemProperties& properties) const { + +@Audio_GROUP_COPY_TO@ + +} + +bool ZoneAudioPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Audio_GROUP_SET_FROM@ + + return somethingChanged; +} + +EntityPropertyFlags ZoneAudioPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + +@Audio_REQUESTED_PROPS@ + + return requestedProperties; +} + +int ZoneAudioPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Audio_GROUP_READ@ + + return bytesRead; +} + +void ZoneAudioPropertyGroup::addPropertyMap(QHash& _propertyInfos, + QHash& _enumsToPropertyStrings) { + +@Audio_GROUP_ADD_TO_MAP@ + +} diff --git a/libraries/entities/src/ZoneAudioPropertyGroup.h b/libraries/entities/src/ZoneAudioPropertyGroup.h deleted file mode 100644 index bbe32b1549..0000000000 --- a/libraries/entities/src/ZoneAudioPropertyGroup.h +++ /dev/null @@ -1,98 +0,0 @@ -// -// ZoneAudioPropertyGroup.h -// libraries/entities/src -// -// Created by HifiExperiments on 11/28/23 -// Copyright 2023 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 -// SPDX-License-Identifier: Apache-2.0 -// - -#ifndef hifi_ZoneAudioPropertyGroup_h -#define hifi_ZoneAudioPropertyGroup_h - -#include -#include - -#include "PropertyGroup.h" -#include "EntityItemPropertiesMacros.h" - -class EntityItemProperties; -class EncodeBitstreamParams; -class OctreePacketData; -class EntityTreeElementExtraEncodeData; -class ReadBitstreamToTreeParams; -class ScriptEngine; -class ScriptValue; - -/*@jsdoc - * Zone audio is defined by the following properties: - * @typedef {object} Entities.ZoneAudio - * @property {boolean} reverbEnabled=false - If reverb should be enabled for listeners in this zone. - * @property {number} reverbTime=1.0 - The time (seconds) for the reverb tail to decay by 60dB, also known as RT60. - * @property {number} reverbWetLevel=50 - Adjusts the wet/dry percentage, from completely dry (0%) to completely wet (100%). - * @property {Uuid[]} listenerZones=[] - A list of entity IDs representing listener zones with this zone as a source. - * Sounds from this zone being heard by a listener in a listener zone will be attenuated by the corresponding - * listenerAttenuationCoefficient. - * @property {number[]} listenerAttenuationCoefficients=[] - A list of attenuation coefficients. Each coefficient will be - * applied to sounds coming from this zone and being heard by a listener in the corresponding listenerZone. - */ -class ZoneAudioPropertyGroup : public PropertyGroup { -public: - // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, - ScriptEngine* engine, bool skipDefaults, - EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, - bool isMyOwnAvatarEntity) const override; - virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; - - void merge(const ZoneAudioPropertyGroup& other); - - virtual void debugDump() const override; - virtual void listChangedProperties(QList& out) override; - - virtual bool appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, - const unsigned char*& dataAt, int& processedBytes) override; - virtual void markAllChanged() override; - virtual EntityPropertyFlags getChangedProperties() const override; - - // EntityItem related helpers - // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const override; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - DEFINE_PROPERTY(PROP_REVERB_ENABLED, ReverbEnabled, reverbEnabled, bool, false); - DEFINE_PROPERTY(PROP_REVERB_TIME, ReverbTime, reverbTime, float, 1.0f); - DEFINE_PROPERTY(PROP_REVERB_WET_LEVEL, ReverbWetLevel, reverbWetLevel, float, 50.0f); - DEFINE_PROPERTY(PROP_LISTENER_ZONES, ListenerZones, listenerZones, QVector, QVector()); - DEFINE_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, ListenerAttenuationCoefficients, listenerAttenuationCoefficients, QVector, QVector()); - -}; - -#endif // hifi_ZoneAudioPropertyGroup_h diff --git a/libraries/entities/src/ZoneAudioPropertyGroup.h.in b/libraries/entities/src/ZoneAudioPropertyGroup.h.in new file mode 100644 index 0000000000..62067b9a25 --- /dev/null +++ b/libraries/entities/src/ZoneAudioPropertyGroup.h.in @@ -0,0 +1,52 @@ +// +// ZoneAudioPropertyGroup.h +// libraries/entities/src +// +// Created by HifiExperiments on 11/28/23 +// Copyright 2023 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_ZoneAudioPropertyGroup_h +#define hifi_ZoneAudioPropertyGroup_h + +#include +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +/*@jsdoc + * Zone audio is defined by the following properties: + * @typedef {object} Entities.ZoneAudio + * @property {boolean} reverbEnabled=false - If reverb should be enabled for listeners in this zone. + * @property {number} reverbTime=1.0 - The time (seconds) for the reverb tail to decay by 60dB, also known as RT60. + * @property {number} reverbWetLevel=50 - Adjusts the wet/dry percentage, from completely dry (0%) to completely wet (100%). + * @property {Uuid[]} listenerZones=[] - A list of entity IDs representing listener zones with this zone as a source. + * Sounds from this zone being heard by a listener in a listener zone will be attenuated by the corresponding + * listenerAttenuationCoefficient. + * @property {number[]} listenerAttenuationCoefficients=[] - A list of attenuation coefficients. Each coefficient will be + * applied to sounds coming from this zone and being heard by a listener in the corresponding listenerZone. + */ +class ZoneAudioPropertyGroup : public PropertyGroup { +public: + ENTITY_PROPERTY_GROUP_METHODS(ZoneAudioPropertyGroup) + +protected: + +@Audio_GROUP_PROPS@ + +}; + +#endif // hifi_ZoneAudioPropertyGroup_h diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp deleted file mode 100644 index a8561f49d9..0000000000 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ /dev/null @@ -1,533 +0,0 @@ -// -// ZoneEntityItem.cpp -// libraries/entities/src -// -// Created by Brad Hefta-Gaub on 12/4/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "ZoneEntityItem.h" - -#include -#include - -#include - -#include "EntitiesLogging.h" -#include "EntityItemProperties.h" -#include "EntityTree.h" -#include "EntityTreeElement.h" -#include "EntityEditFilters.h" - -bool ZoneEntityItem::_zonesArePickable = false; -bool ZoneEntityItem::_drawZoneBoundaries = false; - - -const ShapeType ZoneEntityItem::DEFAULT_SHAPE_TYPE = SHAPE_TYPE_BOX; -const QString ZoneEntityItem::DEFAULT_COMPOUND_SHAPE_URL = ""; -const bool ZoneEntityItem::DEFAULT_FLYING_ALLOWED = true; -const bool ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED = true; -const QString ZoneEntityItem::DEFAULT_FILTER_URL = ""; - -EntityItemPointer ZoneEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - std::shared_ptr entity(new ZoneEntityItem(entityID), [](ZoneEntityItem* ptr) { ptr->deleteLater(); }); - entity->setProperties(properties); - return entity; -} - -ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { - _type = EntityTypes::Zone; - - _shapeType = DEFAULT_SHAPE_TYPE; - _compoundShapeURL = DEFAULT_COMPOUND_SHAPE_URL; - _visuallyReady = false; -} - -EntityItemProperties ZoneEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { - EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); - - // Contain QString properties, must be synchronized - withReadLock([&] { - _keyLightProperties.getProperties(properties); - _ambientLightProperties.getProperties(properties); - _skyboxProperties.getProperties(properties); - }); - _hazeProperties.getProperties(properties); - _bloomProperties.getProperties(properties); - _audioProperties.getProperties(properties); - _tonemappingProperties.getProperties(properties); - _ambientOcclusionProperties.getProperties(properties); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(flyingAllowed, getFlyingAllowed); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(ghostingAllowed, getGhostingAllowed); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(filterURL, getFilterURL); - - COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightMode, getKeyLightMode); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientLightMode, getAmbientLightMode); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(skyboxMode, getSkyboxMode); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(hazeMode, getHazeMode); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(bloomMode, getBloomMode); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(avatarPriority, getAvatarPriority); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(screenshare, getScreenshare); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(tonemappingMode, getTonemappingMode); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientOcclusionMode, getAmbientOcclusionMode); - - return properties; -} - -bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL); - - // Contains a QString property, must be synchronized - withWriteLock([&] { - _keyLightPropertiesChanged |= _keyLightProperties.setProperties(properties); - _ambientLightPropertiesChanged |= _ambientLightProperties.setProperties(properties); - _skyboxPropertiesChanged |= _skyboxProperties.setProperties(properties); - }); - _hazePropertiesChanged |= _hazeProperties.setProperties(properties); - _bloomPropertiesChanged |= _bloomProperties.setProperties(properties); - bool audioPropertiesChanged = _audioProperties.setProperties(properties); - _tonemappingPropertiesChanged |= _tonemappingProperties.setProperties(properties); - _ambientOcclusionPropertiesChanged |= _ambientOcclusionProperties.setProperties(properties); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(filterURL, setFilterURL); - - SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightMode, setKeyLightMode); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientLightMode, setAmbientLightMode); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(skyboxMode, setSkyboxMode); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(hazeMode, setHazeMode); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(avatarPriority, setAvatarPriority); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(screenshare, setScreenshare); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(tonemappingMode, setTonemappingMode); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientOcclusionMode, setAmbientOcclusionMode); - - somethingChanged |= _keyLightPropertiesChanged || _ambientLightPropertiesChanged || _skyboxPropertiesChanged || - _hazePropertiesChanged || _bloomPropertiesChanged || audioPropertiesChanged || - _tonemappingPropertiesChanged || _ambientOcclusionPropertiesChanged; - - return somethingChanged; -} - -int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); - READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); - - { - int bytesFromKeylight; - withWriteLock([&] { - bytesFromKeylight = _keyLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _keyLightPropertiesChanged); - }); - somethingChanged |= _keyLightPropertiesChanged; - bytesRead += bytesFromKeylight; - dataAt += bytesFromKeylight; - } - - { - int bytesFromAmbientlight; - withWriteLock([&] { - bytesFromAmbientlight = _ambientLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _ambientLightPropertiesChanged); - }); - somethingChanged |= _ambientLightPropertiesChanged; - bytesRead += bytesFromAmbientlight; - dataAt += bytesFromAmbientlight; - } - - { - int bytesFromSkybox; - withWriteLock([&] { - bytesFromSkybox = _skyboxProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _skyboxPropertiesChanged); - }); - somethingChanged |= _skyboxPropertiesChanged; - bytesRead += bytesFromSkybox; - dataAt += bytesFromSkybox; - } - - { - int bytesFromHaze = _hazeProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _hazePropertiesChanged); - somethingChanged |= _hazePropertiesChanged; - bytesRead += bytesFromHaze; - dataAt += bytesFromHaze; - } - - { - int bytesFromBloom = _bloomProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _bloomPropertiesChanged); - somethingChanged |= _bloomPropertiesChanged; - bytesRead += bytesFromBloom; - dataAt += bytesFromBloom; - } - - { - int bytesFromAudio = _audioProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, somethingChanged); - bytesRead += bytesFromAudio; - dataAt += bytesFromAudio; - } - - { - int bytesFromTonemapping = _tonemappingProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, somethingChanged); - bytesRead += bytesFromTonemapping; - dataAt += bytesFromTonemapping; - } - - { - int bytesFromAmbientOcclusion = _ambientOcclusionProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, somethingChanged); - bytesRead += bytesFromAmbientOcclusion; - dataAt += bytesFromAmbientOcclusion; - } - - READ_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); - READ_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); - READ_ENTITY_PROPERTY(PROP_FILTER_URL, QString, setFilterURL); - - READ_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode); - READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode); - READ_ENTITY_PROPERTY(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); - READ_ENTITY_PROPERTY(PROP_HAZE_MODE, uint32_t, setHazeMode); - READ_ENTITY_PROPERTY(PROP_BLOOM_MODE, uint32_t, setBloomMode); - READ_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, uint32_t, setAvatarPriority); - READ_ENTITY_PROPERTY(PROP_SCREENSHARE, uint32_t, setScreenshare); - READ_ENTITY_PROPERTY(PROP_TONEMAPPING_MODE, uint32_t, setTonemappingMode); - READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_MODE, uint32_t, setAmbientOcclusionMode); - - return bytesRead; -} - -EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - - requestedProperties += PROP_SHAPE_TYPE; - requestedProperties += PROP_COMPOUND_SHAPE_URL; - - requestedProperties += _keyLightProperties.getEntityProperties(params); - requestedProperties += _ambientLightProperties.getEntityProperties(params); - requestedProperties += _skyboxProperties.getEntityProperties(params); - requestedProperties += _hazeProperties.getEntityProperties(params); - requestedProperties += _bloomProperties.getEntityProperties(params); - requestedProperties += _audioProperties.getEntityProperties(params); - requestedProperties += _tonemappingProperties.getEntityProperties(params); - requestedProperties += _ambientOcclusionProperties.getEntityProperties(params); - - requestedProperties += PROP_FLYING_ALLOWED; - requestedProperties += PROP_GHOSTING_ALLOWED; - requestedProperties += PROP_FILTER_URL; - requestedProperties += PROP_AVATAR_PRIORITY; - requestedProperties += PROP_SCREENSHARE; - - requestedProperties += PROP_KEY_LIGHT_MODE; - requestedProperties += PROP_AMBIENT_LIGHT_MODE; - requestedProperties += PROP_SKYBOX_MODE; - requestedProperties += PROP_HAZE_MODE; - requestedProperties += PROP_BLOOM_MODE; - requestedProperties += PROP_TONEMAPPING_MODE; - requestedProperties += PROP_AMBIENT_OCCLUSION_MODE; - - return requestedProperties; -} - -void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); - APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL()); - - withReadLock([&] { - _keyLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - _ambientLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - _skyboxProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - }); - _hazeProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - _bloomProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - _audioProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - _tonemappingProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - _ambientOcclusionProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - - APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, getFlyingAllowed()); - APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, getGhostingAllowed()); - APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, getFilterURL()); - - APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, getKeyLightMode()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, getAmbientLightMode()); - APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, getSkyboxMode()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, getHazeMode()); - APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, getBloomMode()); - APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, getAvatarPriority()); - APPEND_ENTITY_PROPERTY(PROP_SCREENSHARE, getScreenshare()); - APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_MODE, getTonemappingMode()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_MODE, getAmbientOcclusionMode()); -} - -void ZoneEntityItem::debugDump() const { - quint64 now = usecTimestampNow(); - qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeAsString(_hazeMode); - qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeAsString(_keyLightMode); - qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeAsString(_ambientLightMode); - qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeAsString(_skyboxMode); - qCDebug(entities) << " _bloomMode:" << EntityItemProperties::getComponentModeAsString(_bloomMode); - qCDebug(entities) << " _avatarPriority:" << getAvatarPriority(); - qCDebug(entities) << " _tonemappingMode:" << EntityItemProperties::getComponentModeAsString(_tonemappingMode); - qCDebug(entities) << " _ambientOcclusionMode:" << EntityItemProperties::getComponentModeAsString(_ambientOcclusionMode); - - _keyLightProperties.debugDump(); - _ambientLightProperties.debugDump(); - _skyboxProperties.debugDump(); - _hazeProperties.debugDump(); - _bloomProperties.debugDump(); - _audioProperties.debugDump(); - _tonemappingProperties.debugDump(); - _ambientOcclusionProperties.debugDump(); -} - -void ZoneEntityItem::setShapeType(ShapeType type) { - switch(type) { - case SHAPE_TYPE_NONE: - case SHAPE_TYPE_CAPSULE_X: - case SHAPE_TYPE_CAPSULE_Y: - case SHAPE_TYPE_CAPSULE_Z: - case SHAPE_TYPE_HULL: - case SHAPE_TYPE_PLANE: - case SHAPE_TYPE_SIMPLE_HULL: - case SHAPE_TYPE_SIMPLE_COMPOUND: - case SHAPE_TYPE_STATIC_MESH: - case SHAPE_TYPE_CIRCLE: - // these types are unsupported for ZoneEntity - type = DEFAULT_SHAPE_TYPE; - break; - default: - break; - } - - ShapeType oldShapeType; - withWriteLock([&] { - oldShapeType = _shapeType; - _shapeType = type; - }); - - if (type == SHAPE_TYPE_COMPOUND) { - if (type != oldShapeType) { - fetchCollisionGeometryResource(); - } - } else { - _shapeResource.reset(); - } -} - -ShapeType ZoneEntityItem::getShapeType() const { - return resultWithReadLock([&] { - return _shapeType; - }); -} - -void ZoneEntityItem::setCompoundShapeURL(const QString& url) { - QString oldCompoundShapeURL; - ShapeType shapeType; - withWriteLock([&] { - oldCompoundShapeURL = _compoundShapeURL; - _compoundShapeURL = url; - shapeType = _shapeType; - }); - if (oldCompoundShapeURL != url) { - if (shapeType == SHAPE_TYPE_COMPOUND) { - fetchCollisionGeometryResource(); - } else { - _shapeResource.reset(); - } - } -} - -bool ZoneEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& viewFrustumPos, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, - QVariantMap& extraInfo, bool precisionPicking) const { - return _zonesArePickable; -} - -bool ZoneEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, - const glm::vec3& acceleration, const glm::vec3& viewFrustumPos, OctreeElementPointer& element, - float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, - QVariantMap& extraInfo, bool precisionPicking) const { - return _zonesArePickable; -} - -bool ZoneEntityItem::contains(const glm::vec3& point) const { - GeometryResource::Pointer resource = _shapeResource; - if (getShapeType() == SHAPE_TYPE_COMPOUND && resource) { - if (resource->isLoaded()) { - const HFMModel& hfmModel = resource->getHFMModel(); - - Extents meshExtents = hfmModel.getMeshExtents(); - glm::vec3 meshExtentsDiagonal = meshExtents.maximum - meshExtents.minimum; - glm::vec3 offset = -meshExtents.minimum - (meshExtentsDiagonal * getRegistrationPoint()); - glm::vec3 scale(getScaledDimensions() / meshExtentsDiagonal); - - glm::mat4 hfmToEntityMatrix = glm::scale(scale) * glm::translate(offset); - glm::mat4 entityToWorldMatrix = getTransform().getMatrix(); - glm::mat4 worldToHFMMatrix = glm::inverse(entityToWorldMatrix * hfmToEntityMatrix); - - return hfmModel.convexHullContains(glm::vec3(worldToHFMMatrix * glm::vec4(point, 1.0f))); - } - } - return EntityItem::contains(point); -} - -void ZoneEntityItem::setFilterURL(QString url) { - withWriteLock([&] { - _filterURL = url; - }); - if (DependencyManager::isSet()) { - auto entityEditFilters = DependencyManager::get(); - qCDebug(entities) << "adding filter " << url << "for zone" << getEntityItemID(); - entityEditFilters->addFilter(getEntityItemID(), url); - } -} - -QString ZoneEntityItem::getFilterURL() const { - QString result; - withReadLock([&] { - result = _filterURL; - }); - return result; -} - -QString ZoneEntityItem::getCompoundShapeURL() const { - QString result; - withReadLock([&] { - result = _compoundShapeURL; - }); - return result; -} - -void ZoneEntityItem::resetRenderingPropertiesChanged() { - withWriteLock([&] { - _keyLightPropertiesChanged = false; - _ambientLightPropertiesChanged = false; - _skyboxPropertiesChanged = false; - _hazePropertiesChanged = false; - _bloomPropertiesChanged = false; - _tonemappingPropertiesChanged = false; - _ambientOcclusionPropertiesChanged = false; - }); -} - -void ZoneEntityItem::setSkyboxMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _skyboxMode) { - _skyboxMode = value; - _skyboxPropertiesChanged = true; - } -} - -void ZoneEntityItem::setKeyLightMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) { - _keyLightMode = value; - _keyLightPropertiesChanged = true; - } -} - -void ZoneEntityItem::setAmbientLightMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientLightMode) { - _ambientLightMode = value; - _ambientLightPropertiesChanged = true; - } -} - -void ZoneEntityItem::setHazeMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _hazeMode) { - _hazeMode = value; - _hazePropertiesChanged = true; - } -} - -void ZoneEntityItem::setBloomMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _bloomMode) { - _bloomMode = value; - _bloomPropertiesChanged = true; - } -} - -void ZoneEntityItem::setTonemappingMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _tonemappingMode) { - _tonemappingMode = value; - _tonemappingPropertiesChanged = true; - } -} - -void ZoneEntityItem::setAmbientOcclusionMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientOcclusionMode) { - _ambientOcclusionMode = value; - _ambientOcclusionPropertiesChanged = true; - } -} - -void ZoneEntityItem::setUserData(const QString& value) { - withWriteLock([&] { - _needsRenderUpdate |= _userData != value; - _userData = value; - }); -} - -void ZoneEntityItem::fetchCollisionGeometryResource() { - QUrl hullURL(getCompoundShapeURL()); - if (hullURL.isEmpty()) { - _shapeResource.reset(); - } else { - _shapeResource = DependencyManager::get()->getCollisionGeometryResource(hullURL); - } -} - -bool ZoneEntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const { - // currently the only property filter we handle in ZoneEntityItem is value of avatarPriority - - static const QString AVATAR_PRIORITY_PROPERTY = "avatarPriority"; - - // If set match zones of interest to avatar mixer: - if (jsonFilters.contains(AVATAR_PRIORITY_PROPERTY) && jsonFilters[AVATAR_PRIORITY_PROPERTY].toBool() - && (_avatarPriority != COMPONENT_MODE_INHERIT || _screenshare != COMPONENT_MODE_INHERIT)) { - return true; - } - - // Chain to base: - return EntityItem::matchesJSONFilters(jsonFilters); -} diff --git a/libraries/entities/src/ZoneEntityItem.cpp.in b/libraries/entities/src/ZoneEntityItem.cpp.in new file mode 100644 index 0000000000..4b3689be02 --- /dev/null +++ b/libraries/entities/src/ZoneEntityItem.cpp.in @@ -0,0 +1,348 @@ +// +// ZoneEntityItem.cpp +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ZoneEntityItem.h" + +#include +#include + +#include + +#include "EntitiesLogging.h" +#include "EntityItemProperties.h" +#include "EntityTree.h" +#include "EntityTreeElement.h" +#include "EntityEditFilters.h" + +bool ZoneEntityItem::_zonesArePickable = false; +bool ZoneEntityItem::_drawZoneBoundaries = false; + + +const ShapeType ZoneEntityItem::DEFAULT_SHAPE_TYPE = SHAPE_TYPE_BOX; +const QString ZoneEntityItem::DEFAULT_COMPOUND_SHAPE_URL = ""; +const bool ZoneEntityItem::DEFAULT_FLYING_ALLOWED = true; +const bool ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED = true; +const QString ZoneEntityItem::DEFAULT_FILTER_URL = ""; + +EntityItemPointer ZoneEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + std::shared_ptr entity(new ZoneEntityItem(entityID), [](ZoneEntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { + _type = EntityTypes::Zone; + + _shapeType = DEFAULT_SHAPE_TYPE; + _compoundShapeURL = DEFAULT_COMPOUND_SHAPE_URL; + _visuallyReady = false; +} + +EntityItemProperties ZoneEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + +@Zone_ENTITY_COPY_TO@ + + return properties; +} + +bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + +@Zone_ENTITY_SET_FROM@ + + somethingChanged |= _keyLightPropertiesChanged || _ambientLightPropertiesChanged || _skyboxPropertiesChanged || + _hazePropertiesChanged || _bloomPropertiesChanged || _tonemappingPropertiesChanged || + _ambientOcclusionPropertiesChanged; + + return somethingChanged; +} + +EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + +@Zone_REQUESTED_PROPS@ + + return requestedProperties; +} + +void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + +@Zone_ENTITY_APPEND@ + +} + +int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + int bytesRead = 0; + const unsigned char* dataAt = data; + +@Zone_ENTITY_READ@ + + return bytesRead; +} + +void ZoneEntityItem::debugDump() const { + qCDebug(entities) << "ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); + qCDebug(entities) << " pointer:" << this; + +@Zone_ENTITY_DEBUG@ + +} + +void ZoneEntityItem::setShapeType(ShapeType type) { + switch(type) { + case SHAPE_TYPE_NONE: + case SHAPE_TYPE_CAPSULE_X: + case SHAPE_TYPE_CAPSULE_Y: + case SHAPE_TYPE_CAPSULE_Z: + case SHAPE_TYPE_HULL: + case SHAPE_TYPE_PLANE: + case SHAPE_TYPE_SIMPLE_HULL: + case SHAPE_TYPE_SIMPLE_COMPOUND: + case SHAPE_TYPE_STATIC_MESH: + case SHAPE_TYPE_CIRCLE: + // these types are unsupported for ZoneEntity + type = DEFAULT_SHAPE_TYPE; + break; + default: + break; + } + + ShapeType oldShapeType; + withWriteLock([&] { + oldShapeType = _shapeType; + _shapeType = type; + }); + + if (type == SHAPE_TYPE_COMPOUND) { + if (type != oldShapeType) { + fetchCollisionGeometryResource(); + } + } else { + _shapeResource.reset(); + } +} + +ShapeType ZoneEntityItem::getShapeType() const { + return resultWithReadLock([&] { + return _shapeType; + }); +} + +void ZoneEntityItem::setCompoundShapeURL(const QString& url) { + QString oldCompoundShapeURL; + ShapeType shapeType; + withWriteLock([&] { + oldCompoundShapeURL = _compoundShapeURL; + _compoundShapeURL = url; + shapeType = _shapeType; + }); + if (oldCompoundShapeURL != url) { + if (shapeType == SHAPE_TYPE_COMPOUND) { + fetchCollisionGeometryResource(); + } else { + _shapeResource.reset(); + } + } +} + +bool ZoneEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& viewFrustumPos, OctreeElementPointer& element, float& distance, + BoxFace& face, glm::vec3& surfaceNormal, + QVariantMap& extraInfo, bool precisionPicking) const { + return _zonesArePickable; +} + +bool ZoneEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, + const glm::vec3& acceleration, const glm::vec3& viewFrustumPos, OctreeElementPointer& element, + float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, + QVariantMap& extraInfo, bool precisionPicking) const { + return _zonesArePickable; +} + +bool ZoneEntityItem::contains(const glm::vec3& point) const { + GeometryResource::Pointer resource = _shapeResource; + if (getShapeType() == SHAPE_TYPE_COMPOUND && resource) { + if (resource->isLoaded()) { + const HFMModel& hfmModel = resource->getHFMModel(); + + Extents meshExtents = hfmModel.getMeshExtents(); + glm::vec3 meshExtentsDiagonal = meshExtents.maximum - meshExtents.minimum; + glm::vec3 offset = -meshExtents.minimum - (meshExtentsDiagonal * getRegistrationPoint()); + glm::vec3 scale(getScaledDimensions() / meshExtentsDiagonal); + + glm::mat4 hfmToEntityMatrix = glm::scale(scale) * glm::translate(offset); + glm::mat4 entityToWorldMatrix = getTransform().getMatrix(); + glm::mat4 worldToHFMMatrix = glm::inverse(entityToWorldMatrix * hfmToEntityMatrix); + + return hfmModel.convexHullContains(glm::vec3(worldToHFMMatrix * glm::vec4(point, 1.0f))); + } + } + return EntityItem::contains(point); +} + +void ZoneEntityItem::setFilterURL(const QString& url) { + withWriteLock([&] { + _filterURL = url; + }); + if (DependencyManager::isSet()) { + auto entityEditFilters = DependencyManager::get(); + qCDebug(entities) << "adding filter " << url << "for zone" << getEntityItemID(); + entityEditFilters->addFilter(getEntityItemID(), url); + } +} + +QString ZoneEntityItem::getFilterURL() const { + return resultWithReadLock([&] { + return _filterURL; + }); +} + +QString ZoneEntityItem::getCompoundShapeURL() const { + return resultWithReadLock([&] { + return _compoundShapeURL; + }); +} + +void ZoneEntityItem::resetRenderingPropertiesChanged() { + withWriteLock([&] { + _keyLightPropertiesChanged = false; + _ambientLightPropertiesChanged = false; + _skyboxPropertiesChanged = false; + _hazePropertiesChanged = false; + _bloomPropertiesChanged = false; + _tonemappingPropertiesChanged = false; + _ambientOcclusionPropertiesChanged = false; + }); +} + +void ZoneEntityItem::setSkyboxMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _skyboxMode) { + _skyboxMode = value; + _skyboxPropertiesChanged = true; + } +} + +uint32_t ZoneEntityItem::getSkyboxMode() const { + return _skyboxMode; +} + +void ZoneEntityItem::setKeyLightMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) { + _keyLightMode = value; + _keyLightPropertiesChanged = true; + } +} + +uint32_t ZoneEntityItem::getKeyLightMode() const { + return _keyLightMode; +} + +void ZoneEntityItem::setAmbientLightMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientLightMode) { + _ambientLightMode = value; + _ambientLightPropertiesChanged = true; + } +} + +uint32_t ZoneEntityItem::getAmbientLightMode() const { + return _ambientLightMode; +} + +void ZoneEntityItem::setHazeMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _hazeMode) { + _hazeMode = value; + _hazePropertiesChanged = true; + } +} + +uint32_t ZoneEntityItem::getHazeMode() const { + return _hazeMode; +} + +void ZoneEntityItem::setBloomMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _bloomMode) { + _bloomMode = value; + _bloomPropertiesChanged = true; + } +} + +uint32_t ZoneEntityItem::getBloomMode() const { + return _bloomMode; +} + +void ZoneEntityItem::setTonemappingMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _tonemappingMode) { + _tonemappingMode = value; + _tonemappingPropertiesChanged = true; + } +} + +uint32_t ZoneEntityItem::getTonemappingMode() const { + return _tonemappingMode; +} + +void ZoneEntityItem::setAmbientOcclusionMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientOcclusionMode) { + _ambientOcclusionMode = value; + _ambientOcclusionPropertiesChanged = true; + } +} + +uint32_t ZoneEntityItem::getAmbientOcclusionMode() const { + return _ambientOcclusionMode; +} + +void ZoneEntityItem::setUserData(const QString& value) { + withWriteLock([&] { + _needsRenderUpdate |= _userData != value; + _userData = value; + }); +} + +void ZoneEntityItem::fetchCollisionGeometryResource() { + QUrl hullURL(getCompoundShapeURL()); + if (hullURL.isEmpty()) { + _shapeResource.reset(); + } else { + _shapeResource = DependencyManager::get()->getCollisionGeometryResource(hullURL); + } +} + +bool ZoneEntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const { + // currently the only property filter we handle in ZoneEntityItem is value of avatarPriority + + static const QString AVATAR_PRIORITY_PROPERTY = "avatarPriority"; + + // If set match zones of interest to avatar mixer: + if (jsonFilters.contains(AVATAR_PRIORITY_PROPERTY) && jsonFilters[AVATAR_PRIORITY_PROPERTY].toBool() + && (_avatarPriority != COMPONENT_MODE_INHERIT || _screenshare != COMPONENT_MODE_INHERIT)) { + return true; + } + + // Chain to base: + return EntityItem::matchesJSONFilters(jsonFilters); +} diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h.in similarity index 55% rename from libraries/entities/src/ZoneEntityItem.h rename to libraries/entities/src/ZoneEntityItem.h.in index 6fb0145df7..7da10c3c2a 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h.in @@ -33,26 +33,7 @@ public: ZoneEntityItem(const EntityItemID& entityItemID); ALLOW_INSTANTIATION // This class can be instantiated - - // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - virtual bool setSubClassProperties(const EntityItemProperties& properties) override; - - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - + ENTITY_PROPERTY_SUBCLASS_METHODS static bool getZonesArePickable() { return _zonesArePickable; } static void setZonesArePickable(bool value) { _zonesArePickable = value; } @@ -65,38 +46,8 @@ public: virtual ShapeType getShapeType() const override; bool shouldBePhysical() const override { return false; } - QString getCompoundShapeURL() const; - virtual void setCompoundShapeURL(const QString& url); - virtual bool matchesJSONFilters(const QJsonObject& jsonFilters) const override; - void setSkyboxMode(uint32_t value); - uint32_t getSkyboxMode() const { return _skyboxMode; } - - void setKeyLightMode(uint32_t value); - uint32_t getKeyLightMode() const { return _keyLightMode; } - - void setAmbientLightMode(uint32_t value); - uint32_t getAmbientLightMode() const { return _ambientLightMode; } - - void setHazeMode(const uint32_t value); - uint32_t getHazeMode() const { return _hazeMode; } - - void setBloomMode(const uint32_t value); - uint32_t getBloomMode() const { return _bloomMode; } - - uint32_t getAvatarPriority() const { return _avatarPriority; } - void setAvatarPriority(uint32_t value) { _avatarPriority = value; } - - uint32_t getScreenshare() const { return _screenshare; } - void setScreenshare(uint32_t value) { _screenshare = value; } - - void setTonemappingMode(uint32_t value); - uint32_t getTonemappingMode() const { return _tonemappingMode; } - - void setAmbientOcclusionMode(const uint32_t value); - uint32_t getAmbientOcclusionMode() const { return _ambientOcclusionMode; } - SkyboxPropertyGroup getSkyboxProperties() const { return resultWithReadLock([&] { return _skyboxProperties; }); } KeyLightPropertyGroup getKeyLightProperties() const { return resultWithReadLock([&] { return _keyLightProperties; }); } AmbientLightPropertyGroup getAmbientLightProperties() const { return resultWithReadLock([&] { return _ambientLightProperties; }); } @@ -106,13 +57,6 @@ public: const TonemappingPropertyGroup& getTonemappingProperties() const { return _tonemappingProperties; } const AmbientOcclusionPropertyGroup& getAmbientOcclusionProperties() const { return _ambientOcclusionProperties; } - bool getFlyingAllowed() const { return _flyingAllowed; } - void setFlyingAllowed(bool value) { _flyingAllowed = value; } - bool getGhostingAllowed() const { return _ghostingAllowed; } - void setGhostingAllowed(bool value) { _ghostingAllowed = value; } - QString getFilterURL() const; - void setFilterURL(const QString url); - void setUserData(const QString& value) override; bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; } @@ -137,8 +81,6 @@ public: bool contains(const glm::vec3& point) const override; - virtual void debugDump() const override; - static const ShapeType DEFAULT_SHAPE_TYPE; static const QString DEFAULT_COMPOUND_SHAPE_URL; static const bool DEFAULT_FLYING_ALLOWED; @@ -146,32 +88,8 @@ public: static const QString DEFAULT_FILTER_URL; protected: - KeyLightPropertyGroup _keyLightProperties; - AmbientLightPropertyGroup _ambientLightProperties; - ShapeType _shapeType { DEFAULT_SHAPE_TYPE }; - QString _compoundShapeURL; - - uint32_t _keyLightMode { COMPONENT_MODE_INHERIT }; - uint32_t _skyboxMode { COMPONENT_MODE_INHERIT }; - uint32_t _ambientLightMode { COMPONENT_MODE_INHERIT }; - uint32_t _hazeMode { COMPONENT_MODE_INHERIT }; - uint32_t _bloomMode { COMPONENT_MODE_INHERIT }; - uint32_t _avatarPriority { COMPONENT_MODE_INHERIT }; - uint32_t _screenshare { COMPONENT_MODE_INHERIT }; - uint32_t _tonemappingMode { COMPONENT_MODE_INHERIT }; - uint32_t _ambientOcclusionMode { COMPONENT_MODE_INHERIT }; - - SkyboxPropertyGroup _skyboxProperties; - HazePropertyGroup _hazeProperties; - BloomPropertyGroup _bloomProperties; - ZoneAudioPropertyGroup _audioProperties; - TonemappingPropertyGroup _tonemappingProperties; - AmbientOcclusionPropertyGroup _ambientOcclusionProperties; - - bool _flyingAllowed { DEFAULT_FLYING_ALLOWED }; - bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED }; - QString _filterURL { DEFAULT_FILTER_URL }; +@Zone_ENTITY_PROPS@ // Dirty flags turn true when either keylight properties is changing values. bool _keyLightPropertiesChanged { false }; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 960345ffaa..ff3bfaaa9b 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -360,6 +360,7 @@ enum class EntityVersion : PacketVersion { SoundEntities, TonemappingAndAmbientOcclusion, ModelLoadPriority, + PropertyCleanup, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 6d736fe8cf..eaffc4bdb4 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -33,6 +33,7 @@ #include #include +#include "EntityShape.h" #include "MaterialMappingMode.h" #include "BillboardMode.h" #include "RenderLayer.h" @@ -276,6 +277,7 @@ public: static int unpackDataFromBytes(const unsigned char* dataBytes, uint16_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, uint8_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, glm::quat& result) { int bytes = unpackOrientationQuatFromBytes(dataBytes, result); return bytes; } + static int unpackDataFromBytes(const unsigned char* dataBytes, EntityShape& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, ShapeType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, MaterialMappingMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, BillboardMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } diff --git a/libraries/physics/CMakeLists.txt b/libraries/physics/CMakeLists.txt index ad6d5292b9..001c992320 100644 --- a/libraries/physics/CMakeLists.txt +++ b/libraries/physics/CMakeLists.txt @@ -20,5 +20,6 @@ include_hifi_library_headers(hfm) include_hifi_library_headers(model-serializers) include_hifi_library_headers(graphics) include_hifi_library_headers(script-engine) +include_hifi_library_headers(entities) target_bullet() diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 3f06a6b1a3..0265b5e35b 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -147,7 +147,7 @@ public: NUM_SHAPES, }; - /// @param entityShapeEnum: The entity::Shape enumeration for the shape + /// @param entityShapeEnum: The EntityShape enumeration for the shape /// whose GeometryCache::Shape is desired. /// @return GeometryCache::NUM_SHAPES in the event of an error; otherwise, /// the GeometryCache::Shape enum which aligns with the diff --git a/libraries/shared/src/EntityShape.cpp b/libraries/shared/src/EntityShape.cpp new file mode 100644 index 0000000000..a9c266904b --- /dev/null +++ b/libraries/shared/src/EntityShape.cpp @@ -0,0 +1,36 @@ +// +// Created by HifiExperiments on 8/4/24 +// Copyright 2024 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 "EntityShape.h" + +const char* shapeNames[] = { + "Triangle", + "Quad", + "Hexagon", + "Octagon", + "Circle", + "Cube", + "Sphere", + "Tetrahedron", + "Octahedron", + "Dodecahedron", + "Icosahedron", + "Torus", // Not implemented yet. + "Cone", + "Cylinder" +}; + +static const size_t ENTITY_SHAPE_NAMES = (sizeof(shapeNames) / sizeof(shapeNames[0])); + +QString EntityShapeHelpers::getNameForEntityShape(EntityShape shape) { + if (((int)shape <= 0) || ((int)shape >= (int)ENTITY_SHAPE_NAMES)) { + shape = (EntityShape)0; + } + + return shapeNames[(int)shape]; +} diff --git a/libraries/shared/src/EntityShape.h b/libraries/shared/src/EntityShape.h new file mode 100644 index 0000000000..8c1500d4cb --- /dev/null +++ b/libraries/shared/src/EntityShape.h @@ -0,0 +1,63 @@ +// +// Created by HifiExperiments on 8/4/24 +// Copyright 2024 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_EntityShape_h +#define hifi_EntityShape_h + +#include + +/*@jsdoc + *

    A "Shape", "Box", or "Sphere" {@link Entities.EntityType|EntityType} may + * display as one of the following geometrical shapes:

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    ValueDimensionsNotes
    "Circle"2DA circle oriented in 3D.
    "Cone"3D
    "Cube"3D
    "Cylinder"3D
    "Dodecahedron"3D
    "Hexagon"3DA hexagonal prism.
    "Icosahedron"3D
    "Octagon"3DAn octagonal prism.
    "Octahedron"3D
    "Quad"2DA square oriented in 3D.
    "Sphere"3D
    "Tetrahedron"3D
    "Torus"3DNot implemented.
    "Triangle"3DA triangular prism.
    + * @typedef {string} Entities.Shape + */ +enum class EntityShape { + Triangle, + Quad, + Hexagon, + Octagon, + Circle, + Cube, + Sphere, + Tetrahedron, + Octahedron, + Dodecahedron, + Icosahedron, + Torus, + Cone, + Cylinder, + NUM_SHAPES, +}; + +class EntityShapeHelpers { +public: + static QString getNameForEntityShape(EntityShape shape); +}; + +#endif // hifi_EntityShape_h From 926cbef606d7249a3a0d95bed4298bf6bb515b8a Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Wed, 7 Aug 2024 16:33:23 -0700 Subject: [PATCH 075/109] cleanup + automate EntityPropertyFlags --- cmake/macros/GenerateEntityProperties.cmake | 58 ++- .../src/EntityItemGroupProperties.txt | 6 +- .../entities/src/EntityItemProperties.txt | 4 +- libraries/entities/src/EntityPropertyFlags.h | 448 ------------------ .../entities/src/EntityPropertyFlags.h.in | 107 +++++ 5 files changed, 166 insertions(+), 457 deletions(-) delete mode 100644 libraries/entities/src/EntityPropertyFlags.h create mode 100644 libraries/entities/src/EntityPropertyFlags.h.in diff --git a/cmake/macros/GenerateEntityProperties.cmake b/cmake/macros/GenerateEntityProperties.cmake index 55fef26b3c..11bc3ff0cb 100644 --- a/cmake/macros/GenerateEntityProperties.cmake +++ b/cmake/macros/GenerateEntityProperties.cmake @@ -34,6 +34,7 @@ macro(GENERATE_ENTITY_PROPERTIES) set(ENTITY_ITEM_PROPERTY_DEBUG "") set(CURRENT_TYPE "Base") list(APPEND ENTITY_TYPES ${CURRENT_TYPE}) + set(ENTITY_PROPERTY_FLAGS "") set(NON_REF_TYPES "bool" "int" "float" "uint8_t" "uint16_t" "quint16" "uint32_t" "quint32" "quint64") set(BASE_ENTITY_TYPES "Core" "Physics" "Cloning" "Scripts" "LocalProps" "Common") @@ -56,6 +57,7 @@ macro(GENERATE_ENTITY_PROPERTIES) else() set(LOCAL_PROPS false) endif() + list(APPEND ENTITY_PROPERTY_FLAGS ${LINE}) string(CONCAT ENTITY_ITEM_PROPERTY_DEFINES "${ENTITY_ITEM_PROPERTY_DEFINES}" "\t// ${LINE}\n") string(CONCAT ENTITY_ITEM_PROPERTY_CHANGED_PROPERTIES "${ENTITY_ITEM_PROPERTY_CHANGED_PROPERTIES}" "\t// ${LINE}\n") if(NEEDS_CLOSING_BRACE) @@ -118,6 +120,7 @@ macro(GENERATE_ENTITY_PROPERTIES) endif() if(NOT LINE MATCHES ".*common( |,).*") + list(APPEND ENTITY_PROPERTY_FLAGS "group:${ENTITY_PROPERTY_GROUP}") string(CONCAT ENTITY_ITEM_PROPERTY_DEFINES "${ENTITY_ITEM_PROPERTY_DEFINES}" "\tDEFINE_PROPERTY_GROUP(${ENTITY_PROPERTY_GROUP_CAPS}, ${ENTITY_PROPERTY_GROUP}, ${ENTITY_PROPERTY_GROUP_TYPE});\n") string(CONCAT ENTITY_ITEM_PROPERTY_INCLUDES "${ENTITY_ITEM_PROPERTY_INCLUDES}" "#include \"${ENTITY_PROPERTY_GROUP_TYPE}.h\"\n") string(CONCAT ENTITY_ITEM_PROPERTY_STATIC_GROUPS "${ENTITY_ITEM_PROPERTY_STATIC_GROUPS}" "${ENTITY_PROPERTY_GROUP_TYPE} EntityItemProperties::_static${ENTITY_PROPERTY_GROUP_CAPS};\n") @@ -177,12 +180,13 @@ macro(GENERATE_ENTITY_PROPERTIES) set(ENTITY_PROPERTY_MAX ${CMAKE_MATCH_1}) string(REGEX MATCH ".*fromScriptType:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_FROM_SCRIPT_TYPE ${LINE}) set(ENTITY_PROPERTY_FROM_SCRIPT_TYPE ${CMAKE_MATCH_1}) + if (NOT ENTITY_PROPERTY_FROM_SCRIPT_TYPE) + set(ENTITY_PROPERTY_FROM_SCRIPT_TYPE ${ENTITY_PROPERTY_TYPE}) + endif() string(REGEX MATCH ".*getter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_GETTER ${LINE}) set(ENTITY_PROPERTY_GETTER ${CMAKE_MATCH_1}) string(REGEX MATCH ".*setter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_SETTER ${LINE}) set(ENTITY_PROPERTY_SETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*types:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_TYPES ${LINE}) - set(ENTITY_PROPERTY_TYPES ${CMAKE_MATCH_1}) string(REGEX MATCH ".*networkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_NETWORK_GETTER ${LINE}) set(ENTITY_PROPERTY_NETWORK_GETTER ${CMAKE_MATCH_1}) string(REGEX MATCH ".*variableNetworkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_VARIABLE_NETWORK_GETTER ${LINE}) @@ -208,6 +212,7 @@ macro(GENERATE_ENTITY_PROPERTIES) if(NOT LINE MATCHES ".*legacy( |,).*") if(NOT LINE MATCHES ".*common( |,).*") + list(APPEND ENTITY_PROPERTY_FLAGS ${ENTITY_PROPERTY_ENUM}) set(DEFINE_FUNC "DEFINE_PROPERTY_REF") if(LINE MATCHES ".*enum( |,).*") set(DEFINE_FUNC "DEFINE_PROPERTY_REF_ENUM") @@ -423,6 +428,7 @@ macro(GENERATE_ENTITY_PROPERTIES) else() string(REGEX MATCH ".*enum:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_ENUM ${LINE}) string(CONCAT GROUP_PROPERTY_ENUM "PROP_" ${CMAKE_MATCH_1}) + list(APPEND ${CURRENT_TYPE}_PROPERTY_FLAGS ${GROUP_PROPERTY_ENUM}) string(REGEX MATCH ".*prop:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_NAME ${LINE}) set(GROUP_PROPERTY_NAME ${CMAKE_MATCH_1}) CAPITALIZE_FIRST_LETTER(${GROUP_PROPERTY_NAME}) @@ -439,8 +445,6 @@ macro(GENERATE_ENTITY_PROPERTIES) set(GROUP_PROPERTY_GETTER ${CMAKE_MATCH_1}) string(REGEX MATCH ".*setter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_SETTER ${LINE}) set(GROUP_PROPERTY_SETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*types:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_TYPES ${LINE}) - set(GROUP_PROPERTY_TYPES ${CMAKE_MATCH_1}) string(REGEX MATCH ".*networkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_NETWORK_GETTER ${LINE}) set(GROUP_PROPERTY_NETWORK_GETTER ${CMAKE_MATCH_1}) string(REGEX MATCH ".*variableNetworkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_VARIABLE_NETWORK_GETTER ${LINE}) @@ -544,5 +548,51 @@ macro(GENERATE_ENTITY_PROPERTIES) list(APPEND GENERATE_ENTITIES_LIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/src/${TYPE}PropertyGroup.h ${CMAKE_CURRENT_BINARY_DIR}/src/${TYPE}PropertyGroup.cpp) endforeach() + set(HAS_REACHED_COMMON_PROPS false) + set(DERIVED_PROP false) + set(DERIVED_PROP_INDEX 0) + foreach(FLAG_LINE IN LISTS ENTITY_PROPERTY_FLAGS) + string(REGEX MATCH "group:+([A-Za-z]+)" FLAG_GROUP ${FLAG_LINE}) + set(FLAG_GROUP ${CMAKE_MATCH_1}) + + if (FLAG_LINE STREQUAL "Common") + set(HAS_REACHED_COMMON_PROPS true) + elseif(NOT FLAG_GROUP AND NOT FLAG_LINE MATCHES "PROP_.*" AND HAS_REACHED_COMMON_PROPS) + set(DERIVED_PROP true) + endif() + + if (DERIVED_PROP) + if (FLAG_GROUP) + string(CONCAT ENTITY_PROPERTY_FLAGS_DERIVED "${ENTITY_PROPERTY_FLAGS_DERIVED}" "\t// ${FLAG_GROUP}\n") + foreach(GROUP_FLAG IN LISTS ${FLAG_GROUP}_PROPERTY_FLAGS) + string(CONCAT ENTITY_PROPERTY_FLAGS_DERIVED "${ENTITY_PROPERTY_FLAGS_DERIVED}" "\t${GROUP_FLAG} = PROP_DERIVED_${DERIVED_PROP_INDEX},\n") + MATH(EXPR DERIVED_PROP_INDEX "${DERIVED_PROP_INDEX}+1") + endforeach() + elseif(FLAG_LINE MATCHES "PROP_.*") + string(CONCAT ENTITY_PROPERTY_FLAGS_DERIVED "${ENTITY_PROPERTY_FLAGS_DERIVED}" "\t${FLAG_LINE} = PROP_DERIVED_${DERIVED_PROP_INDEX},\n") + MATH(EXPR DERIVED_PROP_INDEX "${DERIVED_PROP_INDEX}+1") + else() + string(CONCAT ENTITY_PROPERTY_FLAGS_DERIVED "${ENTITY_PROPERTY_FLAGS_DERIVED}" "\t// ${FLAG_LINE}\n") + set(DERIVED_PROP_INDEX 0) + endif() + else() + if (FLAG_GROUP) + string(CONCAT ENTITY_PROPERTY_FLAGS_COMMON "${ENTITY_PROPERTY_FLAGS_COMMON}" "\t// ${FLAG_GROUP}\n") + foreach(GROUP_FLAG IN LISTS ${FLAG_GROUP}_PROPERTY_FLAGS) + string(CONCAT ENTITY_PROPERTY_FLAGS_COMMON "${ENTITY_PROPERTY_FLAGS_COMMON}" "\t${GROUP_FLAG},\n") + endforeach() + elseif(FLAG_LINE MATCHES "PROP_.*") + string(CONCAT ENTITY_PROPERTY_FLAGS_COMMON "${ENTITY_PROPERTY_FLAGS_COMMON}" "\t${FLAG_LINE},\n") + else() + string(CONCAT ENTITY_PROPERTY_FLAGS_COMMON "${ENTITY_PROPERTY_FLAGS_COMMON}" "\t// ${FLAG_LINE}\n") + endif() + endif() + endforeach() + + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/src/EntityPropertyFlags.h.in + ${CMAKE_CURRENT_BINARY_DIR}/src/EntityPropertyFlags.h) + list(APPEND GENERATE_ENTITIES_LIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/src/EntityPropertyFlags.h) + message(STATUS "Entity property processing end") endmacro() diff --git a/libraries/entities/src/EntityItemGroupProperties.txt b/libraries/entities/src/EntityItemGroupProperties.txt index 46051c76cb..12ad010285 100644 --- a/libraries/entities/src/EntityItemGroupProperties.txt +++ b/libraries/entities/src/EntityItemGroupProperties.txt @@ -53,9 +53,9 @@ enum:HAZE_ALTITUDE_EFFECT prop:hazeAltitudeEffect type:bool default:false, enum:HAZE_CEILING prop:hazeCeiling type:float default:INITIAL_HAZE_BASE_REFERENCE+INITIAL_HAZE_HEIGHT, enum:HAZE_BASE_REF prop:hazeBaseRef type:float default:INITIAL_HAZE_BASE_REFERENCE, enum:HAZE_BACKGROUND_BLEND prop:hazeBackgroundBlend type:float default:INITIAL_HAZE_BACKGROUND_BLEND, -enum:HAZE_ALTITUDE_EFFECT prop:hazeAttenuateKeyLight type:bool default:false, -enum:HAZE_CEILING prop:hazeKeyLightRange type:float default:INITIAL_KEY_LIGHT_RANGE, -enum:HAZE_BASE_REF prop:hazeKeyLightAltitude type:float default:INITIAL_KEY_LIGHT_ALTITUDE, +enum:PROP_HAZE_ATTENUATE_KEYLIGHT prop:hazeAttenuateKeyLight type:bool default:false, +enum:PROP_HAZE_KEYLIGHT_RANGE prop:hazeKeyLightRange type:float default:INITIAL_KEY_LIGHT_RANGE, +enum:PROP_HAZE_KEYLIGHT_ALTITUDE prop:hazeKeyLightAltitude type:float default:INITIAL_KEY_LIGHT_ALTITUDE, bloom enum:BLOOM_INTENSITY prop:bloomIntensity type:float default:INITIAL_BLOOM_INTENSITY, enum:BLOOM_THRESHOLD prop:bloomThreshold type:float default:INITIAL_BLOOM_THRESHOLD, diff --git a/libraries/entities/src/EntityItemProperties.txt b/libraries/entities/src/EntityItemProperties.txt index 9c631b9fef..c4c9d5f0a3 100644 --- a/libraries/entities/src/EntityItemProperties.txt +++ b/libraries/entities/src/EntityItemProperties.txt @@ -41,11 +41,11 @@ enum:RESTITUTION prop:restitution type:float default:ENTITY_ITEM_DEFAULT_RESTITU enum:FRICTION prop:friction type:float default:ENTITY_ITEM_DEFAULT_FRICTION min:ENTITY_ITEM_MIN_FRICTION max:ENTITY_ITEM_MAX_FRICTION, enum:LIFETIME prop:lifetime type:float default:ENTITY_ITEM_DEFAULT_LIFETIME debugString:"seconds", enum:COLLISIONLESS prop:collisionless type:bool default:ENTITY_ITEM_DEFAULT_COLLISIONLESS, -enum:COLLISIONLESS prop:ignoreForCollisions proxy:collisionless type:bool fromScriptType:bool legacy getter:getCollisionless setter:setCollisionless, +enum:COLLISIONLESS prop:ignoreForCollisions proxy:collisionless type:bool legacy getter:getCollisionless setter:setCollisionless, enum:COLLISION_MASK prop:collisionMask type:uint16_t default:ENTITY_COLLISION_MASK_DEFAULT, enum:COLLISION_MASK prop:collidesWith proxy:collisionMask type:CollisionMask proxyType:uint16_t enum legacy getter:getCollisionMaskAsString, enum:DYNAMIC prop:dynamic type:bool default:ENTITY_ITEM_DEFAULT_DYNAMIC, -enum:DYNAMIC prop:collisionWillMove proxy:dynamic type:bool fromScriptType:bool legacy getter:getDynamic setter:setDynamic, +enum:DYNAMIC prop:collisionWillMove proxy:dynamic type:bool legacy getter:getDynamic setter:setDynamic, enum:COLLISION_SOUND_URL prop:collisionSoundURL type:QString default:ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL urlPermission, enum:ACTION_DATA prop:actionData type:QByteArray default:QByteArray() readOnly inherited variableCopyGetter:getDynamicData variableCopySetter:setDynamicData variableNetworkGetter:getDynamicData() variableNetworkSetter:setDynamicData debugGetter:getDynamicData(), Cloning diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h deleted file mode 100644 index d5de3b2118..0000000000 --- a/libraries/entities/src/EntityPropertyFlags.h +++ /dev/null @@ -1,448 +0,0 @@ -// -// EntityPropertyFlags.h -// libraries/entities/src -// -// Created by Brad Hefta-Gaub on 12/4/13. -// Copyright 2013 High Fidelity, Inc. -// Copyright 2020 Vircadia contributors. -// Copyright 2023 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_EntityPropertyFlags_h -#define hifi_EntityPropertyFlags_h - -#include - -enum EntityPropertyList { - PROP_PAGED_PROPERTY, - PROP_CUSTOM_PROPERTIES_INCLUDED, - - // Core properties - PROP_SIMULATION_OWNER, - PROP_PARENT_ID, - PROP_PARENT_JOINT_INDEX, - PROP_VISIBLE, - PROP_NAME, - PROP_LOCKED, - PROP_USER_DATA, - PROP_PRIVATE_USER_DATA, - PROP_HREF, - PROP_DESCRIPTION, - PROP_POSITION, - PROP_DIMENSIONS, - PROP_ROTATION, - PROP_REGISTRATION_POINT, - PROP_CREATED, - PROP_LAST_EDITED_BY, - PROP_ENTITY_HOST_TYPE, // not sent over the wire - PROP_OWNING_AVATAR_ID, // not sent over the wire - PROP_QUERY_AA_CUBE, - PROP_CAN_CAST_SHADOW, - PROP_VISIBLE_IN_SECONDARY_CAMERA, // not sent over the wire - PROP_RENDER_LAYER, - PROP_PRIMITIVE_MODE, - PROP_IGNORE_PICK_INTERSECTION, - PROP_RENDER_WITH_ZONES, - PROP_BILLBOARD_MODE, - PROP_TAGS, - // Grab - PROP_GRAB_GRABBABLE, - PROP_GRAB_KINEMATIC, - PROP_GRAB_FOLLOWS_CONTROLLER, - PROP_GRAB_TRIGGERABLE, - PROP_GRAB_EQUIPPABLE, - PROP_GRAB_DELEGATE_TO_PARENT, - PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, - PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, - PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, - PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, - 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, - PROP_VELOCITY, - PROP_ANGULAR_VELOCITY, - PROP_GRAVITY, - PROP_ACCELERATION, - PROP_DAMPING, - PROP_ANGULAR_DAMPING, - PROP_RESTITUTION, - PROP_FRICTION, - PROP_LIFETIME, - PROP_COLLISIONLESS, - PROP_COLLISION_MASK, - PROP_DYNAMIC, - PROP_COLLISION_SOUND_URL, - PROP_ACTION_DATA, - - // Cloning - PROP_CLONEABLE, - PROP_CLONE_LIFETIME, - PROP_CLONE_LIMIT, - PROP_CLONE_DYNAMIC, - PROP_CLONE_AVATAR_ENTITY, - PROP_CLONE_ORIGIN_ID, - - // Scripts - PROP_SCRIPT, - PROP_SCRIPT_TIMESTAMP, - PROP_SERVER_SCRIPTS, - - // Used to convert values to and from scripts - PROP_LOCAL_POSITION, - PROP_LOCAL_ROTATION, - PROP_LOCAL_VELOCITY, - PROP_LOCAL_ANGULAR_VELOCITY, - PROP_LOCAL_DIMENSIONS, - - // These properties are used by multiple subtypes but aren't in the base EntityItem - PROP_SHAPE_TYPE, - PROP_COMPOUND_SHAPE_URL, - PROP_COLOR, - PROP_ALPHA, - PROP_UNLIT, - PROP_PULSE_MIN, - PROP_PULSE_MAX, - PROP_PULSE_PERIOD, - PROP_PULSE_COLOR_MODE, - PROP_PULSE_ALPHA_MODE, - PROP_TEXTURES, - - //////////////////////////////////////////////////////////////////////////////////////////////////// - // ATTENTION: add new shared EntityItem properties to the list ABOVE this line - //////////////////////////////////////////////////////////////////////////////////////////////////// - - // We need as many of these as the number of unique properties of a derived EntityItem class - PROP_DERIVED_0, - PROP_DERIVED_1, - PROP_DERIVED_2, - PROP_DERIVED_3, - PROP_DERIVED_4, - PROP_DERIVED_5, - PROP_DERIVED_6, - PROP_DERIVED_7, - PROP_DERIVED_8, - PROP_DERIVED_9, - PROP_DERIVED_10, - PROP_DERIVED_11, - PROP_DERIVED_12, - PROP_DERIVED_13, - PROP_DERIVED_14, - PROP_DERIVED_15, - PROP_DERIVED_16, - PROP_DERIVED_17, - PROP_DERIVED_18, - PROP_DERIVED_19, - PROP_DERIVED_20, - PROP_DERIVED_21, - PROP_DERIVED_22, - PROP_DERIVED_23, - PROP_DERIVED_24, - PROP_DERIVED_25, - PROP_DERIVED_26, - PROP_DERIVED_27, - PROP_DERIVED_28, - PROP_DERIVED_29, - PROP_DERIVED_30, - PROP_DERIVED_31, - PROP_DERIVED_32, - PROP_DERIVED_33, - PROP_DERIVED_34, - PROP_DERIVED_35, - PROP_DERIVED_36, - PROP_DERIVED_37, - PROP_DERIVED_38, - PROP_DERIVED_39, - PROP_DERIVED_40, - PROP_DERIVED_41, - PROP_DERIVED_42, - PROP_DERIVED_43, - PROP_DERIVED_44, - PROP_DERIVED_45, - PROP_DERIVED_46, - PROP_DERIVED_47, - PROP_DERIVED_48, - PROP_DERIVED_49, - PROP_DERIVED_50, - PROP_DERIVED_51, - PROP_DERIVED_52, - PROP_DERIVED_53, - PROP_DERIVED_54, - PROP_DERIVED_55, - PROP_DERIVED_56, - PROP_DERIVED_57, - - PROP_AFTER_LAST_ITEM, - - //////////////////////////////////////////////////////////////////////////////////////////////////// - // WARNING! Do not add props here unless you intentionally mean to reuse PROP_DERIVED_X indexes - // - // These properties intentionally reuse the enum values for other properties which will never overlap with each other. We do this so that we don't have to expand - // the size of the properties bitflags mask - // - // Only add properties here that are only used by one subclass. Otherwise, they should go above to prevent collisions - - // Particles - PROP_MAX_PARTICLES = PROP_DERIVED_0, - PROP_LIFESPAN = PROP_DERIVED_1, - PROP_EMITTING_PARTICLES = PROP_DERIVED_2, - PROP_EMIT_RATE = PROP_DERIVED_3, - PROP_EMIT_SPEED = PROP_DERIVED_4, - PROP_SPEED_SPREAD = PROP_DERIVED_5, - PROP_EMIT_ORIENTATION = PROP_DERIVED_6, - PROP_EMIT_DIMENSIONS = PROP_DERIVED_7, - PROP_ACCELERATION_SPREAD = PROP_DERIVED_8, - PROP_POLAR_START = PROP_DERIVED_9, - PROP_POLAR_FINISH = PROP_DERIVED_10, - PROP_AZIMUTH_START = PROP_DERIVED_11, - PROP_AZIMUTH_FINISH = PROP_DERIVED_12, - PROP_EMIT_RADIUS_START = PROP_DERIVED_13, - PROP_EMIT_ACCELERATION = PROP_DERIVED_14, - PROP_PARTICLE_RADIUS = PROP_DERIVED_15, - PROP_RADIUS_SPREAD = PROP_DERIVED_16, - PROP_RADIUS_START = PROP_DERIVED_17, - PROP_RADIUS_FINISH = PROP_DERIVED_18, - PROP_COLOR_SPREAD = PROP_DERIVED_19, - PROP_COLOR_START = PROP_DERIVED_20, - PROP_COLOR_FINISH = PROP_DERIVED_21, - PROP_ALPHA_SPREAD = PROP_DERIVED_22, - PROP_ALPHA_START = PROP_DERIVED_23, - PROP_ALPHA_FINISH = PROP_DERIVED_24, - PROP_EMITTER_SHOULD_TRAIL = PROP_DERIVED_25, - PROP_PARTICLE_SPIN = PROP_DERIVED_26, - PROP_SPIN_START = PROP_DERIVED_27, - PROP_SPIN_FINISH = PROP_DERIVED_28, - PROP_SPIN_SPREAD = PROP_DERIVED_29, - PROP_PARTICLE_ROTATE_WITH_ENTITY = PROP_DERIVED_30, - - // Procedural Particles - PROP_PROCEDURAL_PARTICLE_NUM_PARTICLES = PROP_DERIVED_0, - PROP_PROCEDURAL_PARTICLE_NUM_TRIS_PER = PROP_DERIVED_1, - PROP_PROCEDURAL_PARTICLE_NUM_UPDATE_PROPS = PROP_DERIVED_2, - PROP_PROCEDURAL_PARTICLE_TRANSPARENT = PROP_DERIVED_3, - PROP_PROCEDURAL_PARTCILE_UPDATE_DATA = PROP_DERIVED_4, - PROP_PROCEDURAL_PARTCILE_RENDER_DATA = PROP_DERIVED_5, - - // Model - PROP_MODEL_URL = PROP_DERIVED_0, - PROP_MODEL_SCALE = PROP_DERIVED_1, - PROP_JOINT_ROTATIONS_SET = PROP_DERIVED_2, - PROP_JOINT_ROTATIONS = PROP_DERIVED_3, - PROP_JOINT_TRANSLATIONS_SET = PROP_DERIVED_4, - PROP_JOINT_TRANSLATIONS = PROP_DERIVED_5, - PROP_RELAY_PARENT_JOINTS = PROP_DERIVED_6, - PROP_GROUP_CULLED = PROP_DERIVED_7, - PROP_BLENDSHAPE_COEFFICIENTS = PROP_DERIVED_8, - PROP_USE_ORIGINAL_PIVOT = PROP_DERIVED_9, - PROP_LOAD_PRIORITY = PROP_DERIVED_10, - // Animation - PROP_ANIMATION_URL = PROP_DERIVED_11, - PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_12, - PROP_ANIMATION_FPS = PROP_DERIVED_13, - PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_14, - PROP_ANIMATION_PLAYING = PROP_DERIVED_15, - PROP_ANIMATION_LOOP = PROP_DERIVED_16, - PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_17, - PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_18, - PROP_ANIMATION_HOLD = PROP_DERIVED_19, - PROP_ANIMATION_SMOOTH_FRAMES = PROP_DERIVED_20, - - // Light - PROP_IS_SPOTLIGHT = PROP_DERIVED_0, - PROP_INTENSITY = PROP_DERIVED_1, - PROP_EXPONENT = PROP_DERIVED_2, - PROP_CUTOFF = PROP_DERIVED_3, - PROP_FALLOFF_RADIUS = PROP_DERIVED_4, - - // Text - PROP_TEXT = PROP_DERIVED_0, - PROP_LINE_HEIGHT = PROP_DERIVED_1, - PROP_TEXT_COLOR = PROP_DERIVED_2, - PROP_TEXT_ALPHA = PROP_DERIVED_3, - PROP_BACKGROUND_COLOR = PROP_DERIVED_4, - PROP_BACKGROUND_ALPHA = PROP_DERIVED_5, - PROP_LEFT_MARGIN = PROP_DERIVED_6, - PROP_RIGHT_MARGIN = PROP_DERIVED_7, - PROP_TOP_MARGIN = PROP_DERIVED_8, - PROP_BOTTOM_MARGIN = PROP_DERIVED_9, - PROP_FONT = PROP_DERIVED_10, - PROP_TEXT_EFFECT = PROP_DERIVED_11, - PROP_TEXT_EFFECT_COLOR = PROP_DERIVED_12, - PROP_TEXT_EFFECT_THICKNESS = PROP_DERIVED_13, - PROP_TEXT_ALIGNMENT = PROP_DERIVED_14, - - // Zone - // Keylight - PROP_KEY_LIGHT_MODE = PROP_DERIVED_0, - PROP_KEYLIGHT_COLOR = PROP_DERIVED_1, - PROP_KEYLIGHT_INTENSITY = PROP_DERIVED_2, - PROP_KEYLIGHT_DIRECTION = PROP_DERIVED_3, - PROP_KEYLIGHT_CAST_SHADOW = PROP_DERIVED_4, - PROP_KEYLIGHT_SHADOW_BIAS = PROP_DERIVED_5, - PROP_KEYLIGHT_SHADOW_MAX_DISTANCE = PROP_DERIVED_6, - // Ambient light - PROP_AMBIENT_LIGHT_MODE = PROP_DERIVED_7, - PROP_AMBIENT_LIGHT_INTENSITY = PROP_DERIVED_8, - PROP_AMBIENT_LIGHT_URL = PROP_DERIVED_9, - PROP_AMBIENT_LIGHT_COLOR = PROP_DERIVED_10, - // Skybox - PROP_SKYBOX_MODE = PROP_DERIVED_11, - PROP_SKYBOX_COLOR = PROP_DERIVED_12, - PROP_SKYBOX_URL = PROP_DERIVED_13, - // Haze - PROP_HAZE_MODE = PROP_DERIVED_14, - PROP_HAZE_RANGE = PROP_DERIVED_15, - PROP_HAZE_COLOR = PROP_DERIVED_16, - PROP_HAZE_GLARE_COLOR = PROP_DERIVED_17, - PROP_HAZE_ENABLE_GLARE = PROP_DERIVED_18, - PROP_HAZE_GLARE_ANGLE = PROP_DERIVED_19, - PROP_HAZE_ALTITUDE_EFFECT = PROP_DERIVED_20, - PROP_HAZE_CEILING = PROP_DERIVED_21, - PROP_HAZE_BASE_REF = PROP_DERIVED_22, - PROP_HAZE_BACKGROUND_BLEND = PROP_DERIVED_23, - PROP_HAZE_ATTENUATE_KEYLIGHT = PROP_DERIVED_24, - PROP_HAZE_KEYLIGHT_RANGE = PROP_DERIVED_25, - PROP_HAZE_KEYLIGHT_ALTITUDE = PROP_DERIVED_26, - // Bloom - PROP_BLOOM_MODE = PROP_DERIVED_27, - PROP_BLOOM_INTENSITY = PROP_DERIVED_28, - PROP_BLOOM_THRESHOLD = PROP_DERIVED_29, - PROP_BLOOM_SIZE = PROP_DERIVED_30, - PROP_FLYING_ALLOWED = PROP_DERIVED_31, - PROP_GHOSTING_ALLOWED = PROP_DERIVED_32, - PROP_FILTER_URL = PROP_DERIVED_33, - // Avatar priority - PROP_AVATAR_PRIORITY = PROP_DERIVED_34, - // Screen-sharing - PROP_SCREENSHARE = PROP_DERIVED_35, - // Audio - PROP_REVERB_ENABLED = PROP_DERIVED_36, - PROP_REVERB_TIME = PROP_DERIVED_37, - PROP_REVERB_WET_LEVEL = PROP_DERIVED_38, - PROP_LISTENER_ZONES = PROP_DERIVED_39, - PROP_LISTENER_ATTENUATION_COEFFICIENTS = PROP_DERIVED_40, - // Tonemapping - PROP_TONEMAPPING_MODE = PROP_DERIVED_41, - PROP_TONEMAPPING_CURVE = PROP_DERIVED_42, - PROP_TONEMAPPING_EXPOSURE = PROP_DERIVED_43, - // Ambient Occlusion - PROP_AMBIENT_OCCLUSION_MODE = PROP_DERIVED_44, - PROP_AMBIENT_OCCLUSION_TECHNIQUE = PROP_DERIVED_45, - PROP_AMBIENT_OCCLUSION_JITTER = PROP_DERIVED_46, - PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL = PROP_DERIVED_47, - PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS = PROP_DERIVED_48, - PROP_AMBIENT_OCCLUSION_BLUR_RADIUS = PROP_DERIVED_49, - PROP_AMBIENT_OCCLUSION_AO_RADIUS = PROP_DERIVED_50, - PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL = PROP_DERIVED_51, - PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE = PROP_DERIVED_52, - PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT = PROP_DERIVED_53, - PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS = PROP_DERIVED_54, - - // Polyvox - PROP_VOXEL_VOLUME_SIZE = PROP_DERIVED_0, - PROP_VOXEL_DATA = PROP_DERIVED_1, - PROP_VOXEL_SURFACE_STYLE = PROP_DERIVED_2, - PROP_X_TEXTURE_URL = PROP_DERIVED_3, - PROP_Y_TEXTURE_URL = PROP_DERIVED_4, - PROP_Z_TEXTURE_URL = PROP_DERIVED_5, - PROP_X_N_NEIGHBOR_ID = PROP_DERIVED_6, - PROP_Y_N_NEIGHBOR_ID = PROP_DERIVED_7, - PROP_Z_N_NEIGHBOR_ID = PROP_DERIVED_8, - PROP_X_P_NEIGHBOR_ID = PROP_DERIVED_9, - PROP_Y_P_NEIGHBOR_ID = PROP_DERIVED_10, - PROP_Z_P_NEIGHBOR_ID = PROP_DERIVED_11, - - // Web - PROP_SOURCE_URL = PROP_DERIVED_0, - PROP_DPI = PROP_DERIVED_1, - PROP_SCRIPT_URL = PROP_DERIVED_2, - PROP_MAX_FPS = PROP_DERIVED_3, - PROP_INPUT_MODE = PROP_DERIVED_4, - PROP_WANTS_KEYBOARD_FOCUS = PROP_DERIVED_5, - PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT = PROP_DERIVED_6, - PROP_WEB_USE_BACKGROUND = PROP_DERIVED_7, - PROP_USER_AGENT = PROP_DERIVED_8, - - // Polyline - PROP_LINE_POINTS = PROP_DERIVED_0, - PROP_STROKE_WIDTHS = PROP_DERIVED_1, - PROP_STROKE_NORMALS = PROP_DERIVED_2, - PROP_STROKE_COLORS = PROP_DERIVED_3, - PROP_IS_UV_MODE_STRETCH = PROP_DERIVED_4, - PROP_LINE_GLOW = PROP_DERIVED_5, - PROP_LINE_FACE_CAMERA = PROP_DERIVED_6, - - // Shape - PROP_SHAPE = PROP_DERIVED_0, - - // Material - PROP_MATERIAL_URL = PROP_DERIVED_0, - PROP_MATERIAL_MAPPING_MODE = PROP_DERIVED_1, - PROP_MATERIAL_PRIORITY = PROP_DERIVED_2, - PROP_PARENT_MATERIAL_NAME = PROP_DERIVED_3, - PROP_MATERIAL_MAPPING_POS = PROP_DERIVED_4, - PROP_MATERIAL_MAPPING_SCALE = PROP_DERIVED_5, - PROP_MATERIAL_MAPPING_ROT = PROP_DERIVED_6, - PROP_MATERIAL_DATA = PROP_DERIVED_7, - PROP_MATERIAL_REPEAT = PROP_DERIVED_8, - - // Image - PROP_IMAGE_URL = PROP_DERIVED_0, - PROP_EMISSIVE = PROP_DERIVED_1, - PROP_KEEP_ASPECT_RATIO = PROP_DERIVED_2, - PROP_SUB_IMAGE = PROP_DERIVED_3, - - // Grid - PROP_GRID_FOLLOW_CAMERA = PROP_DERIVED_0, - PROP_MAJOR_GRID_EVERY = PROP_DERIVED_1, - PROP_MINOR_GRID_EVERY = PROP_DERIVED_2, - - // Gizmo - PROP_GIZMO_TYPE = PROP_DERIVED_0, - // Ring - PROP_START_ANGLE = PROP_DERIVED_1, - PROP_END_ANGLE = PROP_DERIVED_2, - PROP_INNER_RADIUS = PROP_DERIVED_3, - PROP_INNER_START_COLOR = PROP_DERIVED_4, - PROP_INNER_END_COLOR = PROP_DERIVED_5, - PROP_OUTER_START_COLOR = PROP_DERIVED_6, - PROP_OUTER_END_COLOR = PROP_DERIVED_7, - PROP_INNER_START_ALPHA = PROP_DERIVED_8, - PROP_INNER_END_ALPHA = PROP_DERIVED_9, - PROP_OUTER_START_ALPHA = PROP_DERIVED_10, - PROP_OUTER_END_ALPHA = PROP_DERIVED_11, - PROP_HAS_TICK_MARKS = PROP_DERIVED_12, - PROP_MAJOR_TICK_MARKS_ANGLE = PROP_DERIVED_13, - PROP_MINOR_TICK_MARKS_ANGLE = PROP_DERIVED_14, - PROP_MAJOR_TICK_MARKS_LENGTH = PROP_DERIVED_15, - PROP_MINOR_TICK_MARKS_LENGTH = PROP_DERIVED_16, - PROP_MAJOR_TICK_MARKS_COLOR = PROP_DERIVED_17, - PROP_MINOR_TICK_MARKS_COLOR = PROP_DERIVED_18, - - // Sound - PROP_SOUND_URL = PROP_DERIVED_0, - PROP_SOUND_VOLUME = PROP_DERIVED_1, - PROP_SOUND_TIME_OFFSET = PROP_DERIVED_2, - PROP_SOUND_PITCH = PROP_DERIVED_3, - PROP_SOUND_PLAYING = PROP_DERIVED_4, - PROP_SOUND_POSITIONAL = PROP_DERIVED_5, - PROP_SOUND_LOOP = PROP_DERIVED_6, - PROP_SOUND_LOCAL_ONLY = PROP_DERIVED_7, - - // WARNING!!! DO NOT ADD PROPS_xxx here unless you really really meant to.... Add them UP above -}; - -typedef PropertyFlags EntityPropertyFlags; - -// this is set at the top of EntityItemProperties.cpp to PROP_AFTER_LAST_ITEM - 1. PROP_AFTER_LAST_ITEM is always -// one greater than the last item property due to the enum's auto-incrementing. -extern EntityPropertyList PROP_LAST_ITEM; - -#endif // hifi_EntityPropertyFlags_h diff --git a/libraries/entities/src/EntityPropertyFlags.h.in b/libraries/entities/src/EntityPropertyFlags.h.in new file mode 100644 index 0000000000..f689845478 --- /dev/null +++ b/libraries/entities/src/EntityPropertyFlags.h.in @@ -0,0 +1,107 @@ +// +// EntityPropertyFlags.h +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. +// Copyright 2023 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_EntityPropertyFlags_h +#define hifi_EntityPropertyFlags_h + +#include + +enum EntityPropertyList { + PROP_PAGED_PROPERTY, + PROP_CUSTOM_PROPERTIES_INCLUDED, + +@ENTITY_PROPERTY_FLAGS_COMMON@ + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // ATTENTION: add new shared EntityItem properties to the list ABOVE this line + //////////////////////////////////////////////////////////////////////////////////////////////////// + + // We need as many of these as the number of unique properties of a derived EntityItem class + PROP_DERIVED_0, + PROP_DERIVED_1, + PROP_DERIVED_2, + PROP_DERIVED_3, + PROP_DERIVED_4, + PROP_DERIVED_5, + PROP_DERIVED_6, + PROP_DERIVED_7, + PROP_DERIVED_8, + PROP_DERIVED_9, + PROP_DERIVED_10, + PROP_DERIVED_11, + PROP_DERIVED_12, + PROP_DERIVED_13, + PROP_DERIVED_14, + PROP_DERIVED_15, + PROP_DERIVED_16, + PROP_DERIVED_17, + PROP_DERIVED_18, + PROP_DERIVED_19, + PROP_DERIVED_20, + PROP_DERIVED_21, + PROP_DERIVED_22, + PROP_DERIVED_23, + PROP_DERIVED_24, + PROP_DERIVED_25, + PROP_DERIVED_26, + PROP_DERIVED_27, + PROP_DERIVED_28, + PROP_DERIVED_29, + PROP_DERIVED_30, + PROP_DERIVED_31, + PROP_DERIVED_32, + PROP_DERIVED_33, + PROP_DERIVED_34, + PROP_DERIVED_35, + PROP_DERIVED_36, + PROP_DERIVED_37, + PROP_DERIVED_38, + PROP_DERIVED_39, + PROP_DERIVED_40, + PROP_DERIVED_41, + PROP_DERIVED_42, + PROP_DERIVED_43, + PROP_DERIVED_44, + PROP_DERIVED_45, + PROP_DERIVED_46, + PROP_DERIVED_47, + PROP_DERIVED_48, + PROP_DERIVED_49, + PROP_DERIVED_50, + PROP_DERIVED_51, + PROP_DERIVED_52, + PROP_DERIVED_53, + PROP_DERIVED_54, + + PROP_AFTER_LAST_ITEM, + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // WARNING! Do not add props here unless you intentionally mean to reuse PROP_DERIVED_X indexes + // + // These properties intentionally reuse the enum values for other properties which will never overlap with each other. We do this so that we don't have to expand + // the size of the properties bitflags mask + // + // Only add properties here that are only used by one subclass. Otherwise, they should go above to prevent collisions + +@ENTITY_PROPERTY_FLAGS_DERIVED@ + + // WARNING!!! DO NOT ADD PROPS_xxx here unless you really really meant to.... Add them UP above +}; + +typedef PropertyFlags EntityPropertyFlags; + +// this is set at the top of EntityItemProperties.cpp to PROP_AFTER_LAST_ITEM - 1. PROP_AFTER_LAST_ITEM is always +// one greater than the last item property due to the enum's auto-incrementing. +extern EntityPropertyList PROP_LAST_ITEM; + +#endif // hifi_EntityPropertyFlags_h From 74fa1d12917de0270587c1f9ab9bbc6b7e9e4b7e Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 18 Aug 2024 20:35:57 -0700 Subject: [PATCH 076/109] text vertical alignment, use uint8_t for entity property enums, fix text recalculating too often --- cmake/macros/GenerateEntityProperties.cmake | 6 +- .../src/RenderableTextEntityItem.cpp | 3 +- .../src/RenderableTextEntityItem.h | 1 + .../entities/src/EntityItemProperties.cpp.in | 17 ++++++ .../entities/src/EntityItemProperties.h.in | 1 + .../entities/src/EntityItemProperties.txt | 1 + .../entities/src/EntityItemPropertiesDocs.cpp | 3 +- libraries/networking/src/udt/PacketHeaders.h | 1 + libraries/octree/src/OctreePacketData.h | 2 + libraries/render-utils/src/text/Font.cpp | 61 ++++++++++++++----- libraries/render-utils/src/text/Font.h | 16 +++-- .../shared/src/AmbientOcclusionTechnique.h | 2 +- libraries/shared/src/BillboardMode.h | 2 +- libraries/shared/src/EntityShape.h | 2 +- libraries/shared/src/GizmoType.h | 2 +- libraries/shared/src/MaterialMappingMode.h | 2 +- libraries/shared/src/MirrorMode.h | 2 +- libraries/shared/src/PrimitiveMode.h | 2 +- libraries/shared/src/PulseMode.h | 2 +- libraries/shared/src/RenderLayer.h | 2 +- libraries/shared/src/ShapeInfo.h | 2 +- libraries/shared/src/TextAlignment.h | 6 +- libraries/shared/src/TextEffect.h | 2 +- .../shared/src/TextVerticalAlignment.cpp | 25 ++++++++ libraries/shared/src/TextVerticalAlignment.h | 40 ++++++++++++ libraries/shared/src/TonemappingCurve.h | 2 +- libraries/shared/src/WebInputMode.h | 2 +- .../create/assets/data/createAppTooltips.json | 3 + .../html/js/entityProperties.js | 11 ++++ 29 files changed, 180 insertions(+), 43 deletions(-) create mode 100644 libraries/shared/src/TextVerticalAlignment.cpp create mode 100644 libraries/shared/src/TextVerticalAlignment.h diff --git a/cmake/macros/GenerateEntityProperties.cmake b/cmake/macros/GenerateEntityProperties.cmake index 11bc3ff0cb..b8980a9c25 100644 --- a/cmake/macros/GenerateEntityProperties.cmake +++ b/cmake/macros/GenerateEntityProperties.cmake @@ -277,7 +277,7 @@ macro(GENERATE_ENTITY_PROPERTIES) endif() if(NOT COMMON_PROPS) if(LINE MATCHES ".*enum( |,).*") - string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t\t\tAPPEND_ENTITY_PROPERTY(${ENTITY_PROPERTY_ENUM}, (uint32_t)properties.get${ENTITY_PROPERTY_NAME_CAPS}());\n") + string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t\t\tAPPEND_ENTITY_PROPERTY(${ENTITY_PROPERTY_ENUM}, (uint8_t)properties.get${ENTITY_PROPERTY_NAME_CAPS}());\n") elseif(ENTITY_PROPERTY_NETWORK_GETTER) string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t\t\tAPPEND_ENTITY_PROPERTY(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NETWORK_GETTER});\n") else() @@ -286,7 +286,7 @@ macro(GENERATE_ENTITY_PROPERTIES) string(CONCAT ENTITY_ITEM_PROPERTY_READ "${ENTITY_ITEM_PROPERTY_READ}" "\tREAD_ENTITY_PROPERTY_TO_PROPERTIES(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_READ_TYPE}, set${ENTITY_PROPERTY_NAME_CAPS});\n") string(CONCAT ${CURRENT_TYPE}_REQUESTED_PROPS "${${CURRENT_TYPE}_REQUESTED_PROPS}" "\trequestedProperties += ${ENTITY_PROPERTY_ENUM};\n") if(LINE MATCHES ".*enum( |,).*") - string(CONCAT ${CURRENT_TYPE}_ENTITY_APPEND "${${CURRENT_TYPE}_ENTITY_APPEND}" "\tAPPEND_ENTITY_PROPERTY(${ENTITY_PROPERTY_ENUM}, (uint32_t)get${ENTITY_PROPERTY_NAME_CAPS}());\n") + string(CONCAT ${CURRENT_TYPE}_ENTITY_APPEND "${${CURRENT_TYPE}_ENTITY_APPEND}" "\tAPPEND_ENTITY_PROPERTY(${ENTITY_PROPERTY_ENUM}, (uint8_t)get${ENTITY_PROPERTY_NAME_CAPS}());\n") elseif(ENTITY_VARIABLE_NETWORK_GETTER) string(CONCAT ${CURRENT_TYPE}_ENTITY_APPEND "${${CURRENT_TYPE}_ENTITY_APPEND}" "\tAPPEND_ENTITY_PROPERTY(${ENTITY_PROPERTY_ENUM}, ${ENTITY_VARIABLE_NETWORK_GETTER});\n") else() @@ -492,7 +492,7 @@ macro(GENERATE_ENTITY_PROPERTIES) string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_LIST_CHANGED "${${CURRENT_TYPE_CAPS}_GROUP_LIST_CHANGED}" "\tif (${GROUP_PROPERTY_NAME}Changed()) {\n\t\tout += \"${GROUP_PROPERTY_NAME}\";\n\t}\n") string(CONCAT ${CURRENT_TYPE_CAPS}_REQUESTED_PROPS "${${CURRENT_TYPE_CAPS}_REQUESTED_PROPS}" "\trequestedProperties += ${GROUP_PROPERTY_ENUM};\n") if(LINE MATCHES ".*enum( |,).*") - string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_APPEND "${${CURRENT_TYPE_CAPS}_GROUP_APPEND}" "\tAPPEND_ENTITY_PROPERTY(${GROUP_PROPERTY_ENUM}, (uint32_t)get${GROUP_PROPERTY_NAME_CAPS}());\n") + string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_APPEND "${${CURRENT_TYPE_CAPS}_GROUP_APPEND}" "\tAPPEND_ENTITY_PROPERTY(${GROUP_PROPERTY_ENUM}, (uint8_t)get${GROUP_PROPERTY_NAME_CAPS}());\n") elseif(GROUP_VARIABLE_NETWORK_GETTER) string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_APPEND "${${CURRENT_TYPE_CAPS}_GROUP_APPEND}" "\tAPPEND_ENTITY_PROPERTY(${GROUP_PROPERTY_ENUM}, ${GROUP_VARIABLE_NETWORK_GETTER});\n") else() diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 1cfab07986..e7167b8c78 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -77,6 +77,7 @@ void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe _effectColor = toGlm(entity->getTextEffectColor()); _effectThickness = entity->getTextEffectThickness(); _alignment = entity->getAlignment(); + _verticalAlignment = entity->getVerticalAlignment(); bool materialChanged = false; glm::vec3 color = toGlm(entity->getBackgroundColor()); @@ -381,7 +382,7 @@ void entities::TextPayload::render(RenderArgs* args) { glm::vec2 bounds = glm::vec2(dimensions.x - (textRenderable->_leftMargin + textRenderable->_rightMargin), dimensions.y - (textRenderable->_topMargin + textRenderable->_bottomMargin)); textRenderer->draw(batch, textRenderable->_font, { textRenderable->_text, textColor, effectColor, { textRenderable->_leftMargin / scale, -textRenderable->_topMargin / scale }, - bounds / scale, scale, textRenderable->_effectThickness, textRenderable->_effect, textRenderable->_alignment, textRenderable->_unlit, forward, mirror }); + bounds / scale, scale, textRenderable->_effectThickness, textRenderable->_effect, textRenderable->_alignment, textRenderable->_verticalAlignment, textRenderable->_unlit, forward, mirror }); } namespace render { diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index f48bb8085f..782b4d4f34 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -74,6 +74,7 @@ private: QString _font { "" }; TextAlignment _alignment { TextAlignment::LEFT }; + TextVerticalAlignment _verticalAlignment { TextVerticalAlignment::TOP }; TextEffect _effect { TextEffect::NO_EFFECT }; glm::vec3 _effectColor { 0 }; float _effectThickness { 0.0f }; diff --git a/libraries/entities/src/EntityItemProperties.cpp.in b/libraries/entities/src/EntityItemProperties.cpp.in index bc8804c396..31272b954f 100644 --- a/libraries/entities/src/EntityItemProperties.cpp.in +++ b/libraries/entities/src/EntityItemProperties.cpp.in @@ -376,6 +376,23 @@ void EntityItemProperties::setAlignmentFromString(const QString& alignment) { } } +inline void addTextVerticalAlignment(QHash& lookup, TextVerticalAlignment verticalAlignment) { lookup[TextVerticalAlignmentHelpers::getNameForTextVerticalAlignment(verticalAlignment)] = verticalAlignment; } +const QHash stringToTextVerticalAlignmentLookup = [] { + QHash toReturn; + addTextVerticalAlignment(toReturn, TextVerticalAlignment::TOP); + addTextVerticalAlignment(toReturn, TextVerticalAlignment::BOTTOM); + addTextVerticalAlignment(toReturn, TextVerticalAlignment::CENTER); + return toReturn; +}(); +QString EntityItemProperties::getVerticalAlignmentAsString() const { return TextVerticalAlignmentHelpers::getNameForTextVerticalAlignment(_verticalAlignment); } +void EntityItemProperties::setVerticalAlignmentFromString(const QString& verticalAlignment) { + auto textVerticalAlignmentItr = stringToTextVerticalAlignmentLookup.find(verticalAlignment.toLower()); + if (textVerticalAlignmentItr != stringToTextVerticalAlignmentLookup.end()) { + _verticalAlignment = textVerticalAlignmentItr.value(); + _verticalAlignmentChanged = true; + } +} + QString getCollisionGroupAsString(uint16_t group) { switch (group) { case USER_COLLISION_GROUP_DYNAMIC: diff --git a/libraries/entities/src/EntityItemProperties.h.in b/libraries/entities/src/EntityItemProperties.h.in index 9c904837df..6565d650c9 100644 --- a/libraries/entities/src/EntityItemProperties.h.in +++ b/libraries/entities/src/EntityItemProperties.h.in @@ -55,6 +55,7 @@ #include "GizmoType.h" #include "TextEffect.h" #include "TextAlignment.h" +#include "TextVerticalAlignment.h" #include "MirrorMode.h" #include "EntityShape.h" diff --git a/libraries/entities/src/EntityItemProperties.txt b/libraries/entities/src/EntityItemProperties.txt index c4c9d5f0a3..e03142afdd 100644 --- a/libraries/entities/src/EntityItemProperties.txt +++ b/libraries/entities/src/EntityItemProperties.txt @@ -161,6 +161,7 @@ enum:TEXT_EFFECT prop:textEffect type:TextEffect default:TextEffect::NO_EFFECT e enum:TEXT_EFFECT_COLOR prop:textEffectColor type:u8vec3Color default:TextEntityItem::DEFAULT_TEXT_COLOR renderProp, enum:TEXT_EFFECT_THICKNESS prop:textEffectThickness type:float default:TextEntityItem::DEFAULT_TEXT_EFFECT_THICKNESS min:0.0f max:0.5f renderProp, enum:TEXT_ALIGNMENT prop:alignment type:TextAlignment default:TextAlignment::LEFT enum renderProp, +enum:TEXT_VERTICAL_ALIGNMENT prop:verticalAlignment type:TextVerticalAlignment default:TextVerticalAlignment::TOP enum renderProp, Zone enum:SHAPE_TYPE prop:shapeType type:ShapeType enum default:SHAPE_TYPE_NONE common noGetterSetterProp, enum:COMPOUND_SHAPE_URL prop:compoundShapeURL type:QString default:"" urlPermission common, diff --git a/libraries/entities/src/EntityItemPropertiesDocs.cpp b/libraries/entities/src/EntityItemPropertiesDocs.cpp index a321327aec..2e1c0013e3 100644 --- a/libraries/entities/src/EntityItemPropertiesDocs.cpp +++ b/libraries/entities/src/EntityItemPropertiesDocs.cpp @@ -779,7 +779,8 @@ * @property {Entities.TextEffect} textEffect="none" - The effect that is applied to the text. * @property {Color} textEffectColor=255,255,255 - The color of the effect. * @property {number} textEffectThickness=0.2 - The magnitude of the text effect, range 0.00.5. - * @property {Entities.TextAlignment} alignment="left" - How the text is aligned against its background. + * @property {Entities.TextAlignment} alignment="left" - How the text is horizontally aligned against its background. + * @property {Entities.TextVerticalAlignment} verticalAlignment="top" - How the text is vertically aligned against its background. * @property {boolean} faceCamera - true if billboardMode is "yaw", false * if it isn't. Setting this property to false sets the billboardMode to "none". *

    Deprecated: This property is deprecated and will be removed.

    diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index ff3bfaaa9b..923913e896 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -361,6 +361,7 @@ enum class EntityVersion : PacketVersion { TonemappingAndAmbientOcclusion, ModelLoadPriority, PropertyCleanup, + TextVerticalAlignment, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index eaffc4bdb4..cf4d259a4c 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -43,6 +43,7 @@ #include "GizmoType.h" #include "TextEffect.h" #include "TextAlignment.h" +#include "TextVerticalAlignment.h" #include "MirrorMode.h" #include "TonemappingCurve.h" #include "AmbientOcclusionTechnique.h" @@ -288,6 +289,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, TextVerticalAlignment& 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, TonemappingCurve& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, AmbientOcclusionTechnique& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 3019b8f1c3..d8b5deed96 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -373,17 +373,22 @@ void Font::setupGPU() { } inline QuadBuilder adjustedQuadBuilderForAlignmentMode(const Glyph& glyph, glm::vec2 advance, float scale, float enlargeForShadows, - TextAlignment alignment, float rightSpacing) { + TextAlignment alignment, float rightSpacing, TextVerticalAlignment verticalAlignment, float bottomSpacing) { if (alignment == TextAlignment::RIGHT) { advance.x += rightSpacing; } else if (alignment == TextAlignment::CENTER) { advance.x += 0.5f * rightSpacing; } + if (verticalAlignment == TextVerticalAlignment::BOTTOM) { + advance.y += bottomSpacing; + } else if (verticalAlignment == TextVerticalAlignment::CENTER) { + advance.y += 0.5f * bottomSpacing; + } return QuadBuilder(glyph, advance, scale, enlargeForShadows); } void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds, float scale, bool enlargeForShadows, - TextAlignment alignment) { + TextAlignment alignment, TextVerticalAlignment verticalAlignment) { drawInfo.verticesBuffer = std::make_shared(); drawInfo.indicesBuffer = std::make_shared(); drawInfo.indexCount = 0; @@ -394,6 +399,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm drawInfo.origin = origin; float rightEdge = origin.x + bounds.x; + float bottomEdge = origin.y - bounds.y; // Top left of text bool firstTokenOfLine = true; @@ -403,7 +409,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm for (int i = 0; i < tokens.length(); i++) { const QString& token = tokens[i]; - if ((bounds.y != -1) && (advance.y < origin.y - bounds.y)) { + if ((bounds.y != -1) && (advance.y < bottomEdge)) { // We are out of the y bound, stop drawing break; } @@ -459,25 +465,47 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm std::vector quadBuilders; quadBuilders.reserve(glyphsAndCorners.size()); { + float bottomSpacing = -FLT_MAX; + bool foundBottomSpacing = false; + if (verticalAlignment != TextVerticalAlignment::TOP) { + int i = (int)glyphsAndCorners.size() - 1; + while (!foundBottomSpacing && i >= 0) { + auto* nextGlyphAndCorner = &glyphsAndCorners[i]; + bottomSpacing = std::max(bottomSpacing, bottomEdge - (nextGlyphAndCorner->second.y + (nextGlyphAndCorner->first.offset.y - nextGlyphAndCorner->first.size.y))); + i--; + while (i >= 0) { + auto& prevGlyphAndCorner = glyphsAndCorners[i]; + // We're to the right of the last character we checked, which means we're on a previous line, so we can stop + if (prevGlyphAndCorner.second.x >= nextGlyphAndCorner->second.x) { + foundBottomSpacing = true; + break; + } + nextGlyphAndCorner = &prevGlyphAndCorner; + bottomSpacing = std::max(bottomSpacing, bottomEdge - (nextGlyphAndCorner->second.y + (nextGlyphAndCorner->first.offset.y - nextGlyphAndCorner->first.size.y))); + i--; + } + } + } + int i = (int)glyphsAndCorners.size() - 1; while (i >= 0) { - auto nextGlyphAndCorner = glyphsAndCorners[i]; - float rightSpacing = rightEdge - (nextGlyphAndCorner.second.x + nextGlyphAndCorner.first.d); - quadBuilders.push_back(adjustedQuadBuilderForAlignmentMode(nextGlyphAndCorner.first, nextGlyphAndCorner.second, scale, enlargeForShadows, - alignment, rightSpacing)); + auto* nextGlyphAndCorner = &glyphsAndCorners[i]; + float rightSpacing = rightEdge - (nextGlyphAndCorner->second.x + nextGlyphAndCorner->first.d); + quadBuilders.push_back(adjustedQuadBuilderForAlignmentMode(nextGlyphAndCorner->first, nextGlyphAndCorner->second, scale, enlargeForShadows, + alignment, rightSpacing, verticalAlignment, bottomSpacing)); i--; while (i >= 0) { - const auto& prevGlyphAndCorner = glyphsAndCorners[i]; + auto& prevGlyphAndCorner = glyphsAndCorners[i]; // We're to the right of the last character we checked, which means we're on a previous line, so we need to // recalculate the spacing, so we exit this loop - if (prevGlyphAndCorner.second.x >= nextGlyphAndCorner.second.x) { + if (prevGlyphAndCorner.second.x >= nextGlyphAndCorner->second.x) { break; } quadBuilders.push_back(adjustedQuadBuilderForAlignmentMode(prevGlyphAndCorner.first, prevGlyphAndCorner.second, scale, enlargeForShadows, - alignment, rightSpacing)); + alignment, rightSpacing, verticalAlignment, bottomSpacing)); - nextGlyphAndCorner = prevGlyphAndCorner; + nextGlyphAndCorner = &prevGlyphAndCorner; i--; } } @@ -529,12 +557,13 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const DrawPro const bool boundsChanged = props.bounds != drawInfo.bounds || props.origin != drawInfo.origin; // If we're switching to or from shadow effect mode, we need to rebuild the vertices - if (props.str != drawInfo.string || boundsChanged || props.alignment != _alignment || + if (props.str != drawInfo.string || boundsChanged || props.alignment != drawInfo.alignment || props.verticalAlignment != drawInfo.verticalAlignment || (drawInfo.params.effect != textEffect && (textEffect == SHADOW_EFFECT || drawInfo.params.effect == SHADOW_EFFECT)) || - (textEffect == SHADOW_EFFECT && props.scale != _scale)) { - _scale = props.scale; - _alignment = props.alignment; - buildVertices(drawInfo, props.str, props.origin, props.bounds, props.scale, textEffect == SHADOW_EFFECT, props.alignment); + (textEffect == SHADOW_EFFECT && props.scale != drawInfo.scale)) { + drawInfo.scale = props.scale; + drawInfo.alignment = props.alignment; + drawInfo.verticalAlignment = props.verticalAlignment; + buildVertices(drawInfo, props.str, props.origin, props.bounds, props.scale, textEffect == SHADOW_EFFECT, drawInfo.alignment, drawInfo.verticalAlignment); } setupGPU(); diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h index c9d96bc6f6..d9e2570e7e 100644 --- a/libraries/render-utils/src/text/Font.h +++ b/libraries/render-utils/src/text/Font.h @@ -15,6 +15,7 @@ #include "Glyph.h" #include "TextEffect.h" #include "TextAlignment.h" +#include "TextVerticalAlignment.h" #include #include @@ -55,6 +56,10 @@ public: glm::vec2 origin; glm::vec2 bounds; DrawParams params; + + float scale { 0.0f }; + TextAlignment alignment { TextAlignment::LEFT }; + TextVerticalAlignment verticalAlignment { TextVerticalAlignment::TOP }; }; glm::vec2 computeExtent(const QString& str) const; @@ -62,9 +67,10 @@ public: 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) : + float scale, float effectThickness, TextEffect effect, TextAlignment alignment, TextVerticalAlignment verticalAlignment, 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) {} + effect(effect), alignment(alignment), verticalAlignment(verticalAlignment), 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) {} @@ -77,6 +83,7 @@ public: float effectThickness { 0.0f }; TextEffect effect { TextEffect::NO_EFFECT }; TextAlignment alignment { TextAlignment::LEFT }; + TextVerticalAlignment verticalAlignment { TextVerticalAlignment::TOP }; bool unlit = true; bool forward; bool mirror = false; @@ -100,7 +107,7 @@ private: const Glyph& getGlyph(const QChar& c) const; void buildVertices(DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds, float scale, bool enlargeForShadows, - TextAlignment alignment); + TextAlignment alignment, TextVerticalAlignment verticalAlignment); void setupGPU(); @@ -118,9 +125,6 @@ private: float _leading { 0.0f }; float _spaceWidth { 0.0f }; - float _scale { 0.0f }; - TextAlignment _alignment { TextAlignment::LEFT }; - bool _loaded { false }; bool _needsParamsUpdate { false }; diff --git a/libraries/shared/src/AmbientOcclusionTechnique.h b/libraries/shared/src/AmbientOcclusionTechnique.h index 15ce034606..3295149e4d 100644 --- a/libraries/shared/src/AmbientOcclusionTechnique.h +++ b/libraries/shared/src/AmbientOcclusionTechnique.h @@ -25,7 +25,7 @@ * @typedef {string} AmbientOcclusionTechnique */ -enum class AmbientOcclusionTechnique { +enum class AmbientOcclusionTechnique : uint8_t { SSAO = 0, HBAO, }; diff --git a/libraries/shared/src/BillboardMode.h b/libraries/shared/src/BillboardMode.h index dd377cab31..7c7ab981fb 100644 --- a/libraries/shared/src/BillboardMode.h +++ b/libraries/shared/src/BillboardMode.h @@ -33,7 +33,7 @@ * @typedef {string} BillboardMode */ -enum class BillboardMode { +enum class BillboardMode : uint8_t { NONE = 0, YAW, FULL diff --git a/libraries/shared/src/EntityShape.h b/libraries/shared/src/EntityShape.h index 8c1500d4cb..baa3eb7738 100644 --- a/libraries/shared/src/EntityShape.h +++ b/libraries/shared/src/EntityShape.h @@ -37,7 +37,7 @@ * * @typedef {string} Entities.Shape */ -enum class EntityShape { +enum class EntityShape : uint8_t { Triangle, Quad, Hexagon, diff --git a/libraries/shared/src/GizmoType.h b/libraries/shared/src/GizmoType.h index ca091e63fe..edee96698a 100644 --- a/libraries/shared/src/GizmoType.h +++ b/libraries/shared/src/GizmoType.h @@ -24,7 +24,7 @@ * @typedef {string} Entities.GizmoType */ -enum GizmoType { +enum GizmoType : uint8_t { RING = 0, // put new gizmo-types before this line. UNSET_GIZMO_TYPE diff --git a/libraries/shared/src/MaterialMappingMode.h b/libraries/shared/src/MaterialMappingMode.h index d95fbb339e..d48739562a 100644 --- a/libraries/shared/src/MaterialMappingMode.h +++ b/libraries/shared/src/MaterialMappingMode.h @@ -11,7 +11,7 @@ #include "QString" -enum MaterialMappingMode { +enum MaterialMappingMode : uint8_t { UV = 0, PROJECTED, // put new mapping-modes before this line. diff --git a/libraries/shared/src/MirrorMode.h b/libraries/shared/src/MirrorMode.h index e48e564df0..cfcef790f2 100644 --- a/libraries/shared/src/MirrorMode.h +++ b/libraries/shared/src/MirrorMode.h @@ -30,7 +30,7 @@ * @typedef {string} MirrorMode */ -enum class MirrorMode { +enum class MirrorMode : uint8_t { NONE = 0, MIRROR, PORTAL diff --git a/libraries/shared/src/PrimitiveMode.h b/libraries/shared/src/PrimitiveMode.h index 6dd65ec0c7..f52d21ca59 100644 --- a/libraries/shared/src/PrimitiveMode.h +++ b/libraries/shared/src/PrimitiveMode.h @@ -25,7 +25,7 @@ * @typedef {string} Entities.PrimitiveMode */ -enum class PrimitiveMode { +enum class PrimitiveMode : uint8_t { SOLID = 0, LINES }; diff --git a/libraries/shared/src/PulseMode.h b/libraries/shared/src/PulseMode.h index 8d4c24b4be..fb6bfc434f 100644 --- a/libraries/shared/src/PulseMode.h +++ b/libraries/shared/src/PulseMode.h @@ -26,7 +26,7 @@ * @typedef {string} Entities.PulseMode */ -enum class PulseMode { +enum class PulseMode : uint8_t { NONE = 0, IN_PHASE, OUT_PHASE diff --git a/libraries/shared/src/RenderLayer.h b/libraries/shared/src/RenderLayer.h index e0c249a001..d3fb97a256 100644 --- a/libraries/shared/src/RenderLayer.h +++ b/libraries/shared/src/RenderLayer.h @@ -26,7 +26,7 @@ * @typedef {string} Entities.RenderLayer */ -enum class RenderLayer { +enum class RenderLayer : uint8_t { WORLD = 0, FRONT, HUD diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index 6b0f981b24..7af28e8684 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -28,7 +28,7 @@ const int MAX_HULL_POINTS = 42; const int32_t END_OF_MESH_PART = -1; // bogus vertex index at end of mesh part const int32_t END_OF_MESH = -2; // bogus vertex index at end of mesh -enum ShapeType { +enum ShapeType : uint8_t { SHAPE_TYPE_NONE, SHAPE_TYPE_BOX, SHAPE_TYPE_SPHERE, diff --git a/libraries/shared/src/TextAlignment.h b/libraries/shared/src/TextAlignment.h index b82d8e8c57..643b501b31 100644 --- a/libraries/shared/src/TextAlignment.h +++ b/libraries/shared/src/TextAlignment.h @@ -12,21 +12,21 @@ #include "QString" /*@jsdoc - *

    A {@link Entities.EntityProperties-Text|Text} entity may use one of the following alignments:

    + *

    A {@link Entities.EntityProperties-Text|Text} entity may use one of the following horizontal alignments:

    * * * * * * - * + * * * *
    ValueDescription
    "left"Text is aligned to the left side.
    "center"Text is centered.
    "center"Text is centered horizontally.
    "right"Text is aligned to the right side.
    * @typedef {string} Entities.TextAlignment */ -enum class TextAlignment { +enum class TextAlignment : uint8_t { LEFT = 0, CENTER, RIGHT diff --git a/libraries/shared/src/TextEffect.h b/libraries/shared/src/TextEffect.h index 09affc1f4e..f0bbf22209 100644 --- a/libraries/shared/src/TextEffect.h +++ b/libraries/shared/src/TextEffect.h @@ -27,7 +27,7 @@ * @typedef {string} Entities.TextEffect */ -enum class TextEffect { +enum class TextEffect : uint8_t { NO_EFFECT = 0, OUTLINE_EFFECT, OUTLINE_WITH_FILL_EFFECT, diff --git a/libraries/shared/src/TextVerticalAlignment.cpp b/libraries/shared/src/TextVerticalAlignment.cpp new file mode 100644 index 0000000000..fa9381cba0 --- /dev/null +++ b/libraries/shared/src/TextVerticalAlignment.cpp @@ -0,0 +1,25 @@ +// +// Created by HifiExperiments on 2/9/21 +// Copyright 2021 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "TextVerticalAlignment.h" + +const char* textVerticalAlignmentNames[] = { + "top", + "bottom", + "center" +}; + +static const size_t TEXT_VERTICAL_ALIGNMENT_NAMES = (sizeof(textVerticalAlignmentNames) / sizeof(textVerticalAlignmentNames[0])); + +QString TextVerticalAlignmentHelpers::getNameForTextVerticalAlignment(TextVerticalAlignment verticalAlignment) { + if (((int)verticalAlignment <= 0) || ((int)verticalAlignment >= (int)TEXT_VERTICAL_ALIGNMENT_NAMES)) { + verticalAlignment = (TextVerticalAlignment)0; + } + + return textVerticalAlignmentNames[(int)verticalAlignment]; +} \ No newline at end of file diff --git a/libraries/shared/src/TextVerticalAlignment.h b/libraries/shared/src/TextVerticalAlignment.h new file mode 100644 index 0000000000..67dc5162b4 --- /dev/null +++ b/libraries/shared/src/TextVerticalAlignment.h @@ -0,0 +1,40 @@ +// +// Created by HifiExperiments on 8/17/24 +// Copyright 2021 Vircadia contributors. +// +// 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_TextVerticalAlignment_h +#define hifi_TextVerticalAlignment_h + +#include "QString" + +/*@jsdoc + *

    A {@link Entities.EntityProperties-Text|Text} entity may use one of the following vertical alignments:

    + * + * + * + * + * + * + * + * + * + *
    ValueDescription
    "top"Text is aligned to the top.
    "bottom"Text is aligned to the bottom.
    "center"Text is centered vertically.
    + * @typedef {string} Entities.TextVerticalAlignment + */ + +enum class TextVerticalAlignment { + TOP = 0, + BOTTOM, + CENTER, +}; + +class TextVerticalAlignmentHelpers { +public: + static QString getNameForTextVerticalAlignment(TextVerticalAlignment alignment); +}; + +#endif // hifi_TextVerticalAlignment_h \ No newline at end of file diff --git a/libraries/shared/src/TonemappingCurve.h b/libraries/shared/src/TonemappingCurve.h index f13cb3d437..f5f4d3fbb5 100644 --- a/libraries/shared/src/TonemappingCurve.h +++ b/libraries/shared/src/TonemappingCurve.h @@ -27,7 +27,7 @@ * @typedef {string} TonemappingCurve */ -enum class TonemappingCurve { +enum class TonemappingCurve : uint8_t { RGB = 0, SRGB, REINHARD, diff --git a/libraries/shared/src/WebInputMode.h b/libraries/shared/src/WebInputMode.h index a65ae1341c..e1bd7b7984 100644 --- a/libraries/shared/src/WebInputMode.h +++ b/libraries/shared/src/WebInputMode.h @@ -25,7 +25,7 @@ * @typedef {string} WebInputMode */ -enum class WebInputMode { +enum class WebInputMode : uint8_t { TOUCH = 0, MOUSE, }; diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index a6feb44b99..fcbad7b320 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -45,6 +45,9 @@ "textAlignment": { "tooltip": "How the text is aligned within its left and right bounds." }, + "textVerticalAlignment": { + "tooltip": "How the text is aligned within its top and bottom bounds." + }, "topMargin": { "tooltip": "The top margin, in meters." }, diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index dd97620eea..20f34ba8b8 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -273,6 +273,17 @@ const GROUPS = [ propertyID: "textAlignment", propertyName: "alignment", // actual entity property name }, + { + label: "Vertical Alignment", + type: "dropdown", + options: { + top: "Top", + center: "Center", + bottom: "Bottom" + }, + propertyID: "textVerticalAlignment", + propertyName: "verticalAlignment", // actual entity property name + }, { label: "Top Margin", type: "number-draggable", From 356ccc8b15bdc0a1160b9ac7db20e00aab0d281f Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 6 Sep 2024 21:41:46 -0700 Subject: [PATCH 077/109] fix text size --- .../src/avatars-renderer/Avatar.cpp | 2 +- .../src/RenderableTextEntityItem.cpp | 14 +++----------- libraries/render-utils/src/TextRenderer3D.cpp | 4 ++-- libraries/render-utils/src/TextRenderer3D.h | 2 +- libraries/render-utils/src/text/Font.cpp | 14 +++++++------- libraries/render-utils/src/text/Font.h | 6 +++--- 6 files changed, 17 insertions(+), 25 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 6b27a31f8e..4b63dcb932 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1040,7 +1040,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit(); // Render text slightly in front to avoid z-fighting - textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * displayNameRenderer->getFontSize())); + textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT)); batch.setModelTransform(textTransform); { PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderText"); diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index e7167b8c78..e6b42db0db 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -25,10 +25,6 @@ using namespace render; using namespace render::entities; -static const int FIXED_FONT_POINT_SIZE = 40; -const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 92.0f; // Determined through experimentation to fit font to line height. -const float LINE_SCALE_RATIO = 1.2f; - TextEntityRenderer::TextEntityRenderer(const EntityItemPointer& entity) : Parent(entity), _textRenderer(TextRenderer3D::getInstance(ROBOTO_FONT_FAMILY)) { @@ -193,12 +189,8 @@ void TextEntityRenderer::doRender(RenderArgs* args) { QSizeF TextEntityRenderer::textSize(const QString& text) const { auto extents = _textRenderer->computeExtent(text); - extents.y *= 2.0f; - - float maxHeight = (float)_textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO; - float pointToWorldScale = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; - - return QSizeF(extents.x, extents.y) * pointToWorldScale; + float scale = _lineHeight / _textRenderer->getFontHeight(); + return scale * QSizeF(extents.x, extents.y); } void TextEntityRenderer::onAddToSceneTyped(const TypedEntityPointer& entity) { @@ -375,7 +367,7 @@ void entities::TextPayload::render(RenderArgs* args) { transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), textRenderable->_billboardMode, usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); - float scale = textRenderable->_lineHeight / textRenderer->getFontSize(); + float scale = textRenderable->_lineHeight / textRenderer->getFontHeight(); transform.postTranslate(glm::vec3(-0.5, 0.5, 1.0f + EPSILON / dimensions.z)); transform.setScale(scale); batch.setModelTransform(transform); diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index 8ab1b8e0e9..a4680805e6 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -33,9 +33,9 @@ glm::vec2 TextRenderer3D::computeExtent(const QString& str) const { return glm::vec2(0.0f, 0.0f); } -float TextRenderer3D::getFontSize() const { +float TextRenderer3D::getFontHeight() const { if (_font) { - return _font->getFontSize(); + return _font->getFontHeight(); } return 0.0f; } diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h index 9db93e9dcc..1a0a1343f8 100644 --- a/libraries/render-utils/src/TextRenderer3D.h +++ b/libraries/render-utils/src/TextRenderer3D.h @@ -25,7 +25,7 @@ public: static TextRenderer3D* getInstance(const char* family); glm::vec2 computeExtent(const QString& str) const; - float getFontSize() const; // Pixel size + float getFontHeight() const; void draw(gpu::Batch& batch, const Font::DrawProps& props); void draw(gpu::Batch& batch, const QString& font, const Font::DrawProps& props); diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index d8b5deed96..81badb8440 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -129,7 +129,7 @@ void Font::read(QIODevice& in) { } _distanceRange = glm::vec2(arteryFont.variants[0].metrics.distanceRange); - _fontSize = arteryFont.variants[0].metrics.ascender + fabs(arteryFont.variants[0].metrics.descender); + _fontHeight = arteryFont.variants[0].metrics.ascender + fabs(arteryFont.variants[0].metrics.descender); _leading = arteryFont.variants[0].metrics.lineHeight; _spaceWidth = 0.5f * arteryFont.variants[0].metrics.emSize; // We use half the emSize as a first guess for _spaceWidth @@ -303,11 +303,11 @@ QStringList Font::tokenizeForWrapping(const QString& str) const { return tokens; } -glm::vec2 Font::computeTokenExtent(const QString& token) const { - glm::vec2 advance(0, _fontSize); +float Font::computeTokenWidth(const QString& token) const { + float advance = 0.0f; foreach(QChar c, token) { Q_ASSERT(c != '\n'); - advance.x += (c == ' ') ? _spaceWidth : getGlyph(c).d; + advance += (c == ' ') ? _spaceWidth : getGlyph(c).d; } return advance; } @@ -318,10 +318,10 @@ glm::vec2 Font::computeExtent(const QString& str) const { QStringList lines = splitLines(str); if (!lines.empty()) { for(const auto& line : lines) { - glm::vec2 tokenExtent = computeTokenExtent(line); - extent.x = std::max(tokenExtent.x, extent.x); + float tokenWidth = computeTokenWidth(line); + extent.x = std::max(tokenWidth, extent.x); } - extent.y = lines.count() * _fontSize; + extent.y = lines.count() * _fontHeight; } return extent; } diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h index d9e2570e7e..8112e279e5 100644 --- a/libraries/render-utils/src/text/Font.h +++ b/libraries/render-utils/src/text/Font.h @@ -63,7 +63,7 @@ public: }; glm::vec2 computeExtent(const QString& str) const; - float getFontSize() const { return _fontSize; } + float getFontHeight() const { return _fontHeight; } struct DrawProps { DrawProps(const QString& str, const glm::vec4& color, const glm::vec3& effectColor, const glm::vec2& origin, const glm::vec2& bounds, @@ -103,7 +103,7 @@ private: static Pointer load(const QString& family, QIODevice& fontFile); QStringList tokenizeForWrapping(const QString& str) const; QStringList splitLines(const QString& str) const; - glm::vec2 computeTokenExtent(const QString& str) const; + float computeTokenWidth(const QString& str) const; const Glyph& getGlyph(const QChar& c) const; void buildVertices(DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds, float scale, bool enlargeForShadows, @@ -121,7 +121,7 @@ private: // Font characteristics QString _family; glm::vec2 _distanceRange { 1.0f }; - float _fontSize { 0.0f }; + float _fontHeight { 0.0f }; float _leading { 0.0f }; float _spaceWidth { 0.0f }; From 78a8cccd8375dd9caf6afb379458b6902f8cc54c Mon Sep 17 00:00:00 2001 From: Armored-Dragon Date: Mon, 9 Sep 2024 06:01:56 +0000 Subject: [PATCH 078/109] Update interface/resources/controllers/keyboardMouse.json Co-authored-by: HifiExperiments <53453710+HifiExperiments@users.noreply.github.com> --- interface/resources/controllers/keyboardMouse.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index a935f65a26..e0034a94db 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -2,7 +2,7 @@ "name": "Keyboard/Mouse to Actions", "channels": [ { "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton", "Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeRight" }, - { "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton","!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeLeft" }, + { "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton", "!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeLeft" }, { "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeRight" }, { "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.StrafeLeft" }, { "from": "Keyboard.Q", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_LEFT" }, From 729b85062d7fa21fbc549fec92bad796e906a633 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Mon, 9 Sep 2024 12:37:35 -0700 Subject: [PATCH 079/109] fix component mode serialization --- .../entities/src/EntityItemProperties.cpp.in | 2 +- .../entities/src/EntityItemProperties.h.in | 2 +- .../entities/src/EntityItemProperties.txt | 18 ++++++------ .../entities/src/EntityPropertyFlags.h.in | 2 +- libraries/entities/src/ZoneEntityItem.cpp.in | 28 +++++++++---------- libraries/shared/src/ComponentMode.h | 4 +-- 6 files changed, 28 insertions(+), 28 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp.in b/libraries/entities/src/EntityItemProperties.cpp.in index 31272b954f..e5d16aaf94 100644 --- a/libraries/entities/src/EntityItemProperties.cpp.in +++ b/libraries/entities/src/EntityItemProperties.cpp.in @@ -257,7 +257,7 @@ const QHash stringToComponentMode = [] { addComponentMode(toReturn, ComponentMode::COMPONENT_MODE_ENABLED); return toReturn; }(); -QString EntityItemProperties::getComponentModeAsString(uint32_t mode) { return ComponentModeHelpers::getNameForComponentMode((ComponentMode)mode); } +QString EntityItemProperties::getComponentModeAsString(uint8_t mode) { return ComponentModeHelpers::getNameForComponentMode((ComponentMode)mode); } QString EntityItemProperties::getSkyboxModeAsString() const { return getComponentModeAsString(_skyboxMode); } QString EntityItemProperties::getKeyLightModeAsString() const { return getComponentModeAsString(_keyLightMode); } QString EntityItemProperties::getAmbientLightModeAsString() const { return getComponentModeAsString(_ambientLightMode); } diff --git a/libraries/entities/src/EntityItemProperties.h.in b/libraries/entities/src/EntityItemProperties.h.in index 6565d650c9..c838e1d0d7 100644 --- a/libraries/entities/src/EntityItemProperties.h.in +++ b/libraries/entities/src/EntityItemProperties.h.in @@ -117,7 +117,7 @@ public: @ENTITY_ITEM_PROPERTY_DEFINES@ - static QString getComponentModeAsString(uint32_t mode); + static QString getComponentModeAsString(uint8_t mode); public: float getMaxDimension() const { return glm::compMax(_dimensions); } diff --git a/libraries/entities/src/EntityItemProperties.txt b/libraries/entities/src/EntityItemProperties.txt index e03142afdd..251c9766d1 100644 --- a/libraries/entities/src/EntityItemProperties.txt +++ b/libraries/entities/src/EntityItemProperties.txt @@ -176,15 +176,15 @@ group:ambientOcclusion recordChange, enum:FLYING_ALLOWED prop:flyingAllowed type:bool default:ZoneEntityItem::DEFAULT_FLYING_ALLOWED basicProp, enum:GHOSTING_ALLOWED prop:ghostingAllowed type:bool default:ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED basicProp, enum:FILTER_URL prop:filterURL type:QString default:ZoneEntityItem::DEFAULT_FILTER_URL urlPermission, -enum:KEY_LIGHT_MODE prop:keyLightMode type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum, -enum:AMBIENT_LIGHT_MODE prop:ambientLightMode type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum, -enum:SKYBOX_MODE prop:skyboxMode type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum, -enum:HAZE_MODE prop:hazeMode type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum, -enum:BLOOM_MODE prop:bloomMode type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum, -enum:AVATAR_PRIORITY prop:avatarPriority type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum basicProp, -enum:SCREENSHARE prop:screenshare type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum basicProp, -enum:TONEMAPPING_MODE prop:tonemappingMode type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum, -enum:AMBIENT_OCCLUSION_MODE prop:ambientOcclusionMode type:uint32_t default:(uint32_t)COMPONENT_MODE_INHERIT enum, +enum:KEY_LIGHT_MODE prop:keyLightMode type:uint8_t default:(uint8_t)COMPONENT_MODE_INHERIT enum, +enum:AMBIENT_LIGHT_MODE prop:ambientLightMode type:uint8_t default:(uint8_t)COMPONENT_MODE_INHERIT enum, +enum:SKYBOX_MODE prop:skyboxMode type:uint8_t default:(uint8_t)COMPONENT_MODE_INHERIT enum, +enum:HAZE_MODE prop:hazeMode type:uint8_t default:(uint8_t)COMPONENT_MODE_INHERIT enum, +enum:BLOOM_MODE prop:bloomMode type:uint8_t default:(uint8_t)COMPONENT_MODE_INHERIT enum, +enum:AVATAR_PRIORITY prop:avatarPriority type:uint8_t default:(uint8_t)COMPONENT_MODE_INHERIT enum basicProp, +enum:SCREENSHARE prop:screenshare type:uint8_t default:(uint8_t)COMPONENT_MODE_INHERIT enum basicProp, +enum:TONEMAPPING_MODE prop:tonemappingMode type:uint8_t default:(uint8_t)COMPONENT_MODE_INHERIT enum, +enum:AMBIENT_OCCLUSION_MODE prop:ambientOcclusionMode type:uint8_t default:(uint8_t)COMPONENT_MODE_INHERIT enum, PolyVox enum:VOXEL_VOLUME_SIZE prop:voxelVolumeSize type:vec3 default:PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE noGetterSetterProp, enum:VOXEL_DATA prop:voxelData type:QByteArray default:PolyVoxEntityItem::DEFAULT_VOXEL_DATA noGetterSetterProp, diff --git a/libraries/entities/src/EntityPropertyFlags.h.in b/libraries/entities/src/EntityPropertyFlags.h.in index f689845478..c087ae5c57 100644 --- a/libraries/entities/src/EntityPropertyFlags.h.in +++ b/libraries/entities/src/EntityPropertyFlags.h.in @@ -16,7 +16,7 @@ #include -enum EntityPropertyList { +enum EntityPropertyList : uint16_t { PROP_PAGED_PROPERTY, PROP_CUSTOM_PROPERTIES_INCLUDED, diff --git a/libraries/entities/src/ZoneEntityItem.cpp.in b/libraries/entities/src/ZoneEntityItem.cpp.in index 4b3689be02..e926f678ce 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp.in +++ b/libraries/entities/src/ZoneEntityItem.cpp.in @@ -239,80 +239,80 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() { }); } -void ZoneEntityItem::setSkyboxMode(const uint32_t value) { +void ZoneEntityItem::setSkyboxMode(const uint8_t value) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _skyboxMode) { _skyboxMode = value; _skyboxPropertiesChanged = true; } } -uint32_t ZoneEntityItem::getSkyboxMode() const { +uint8_t ZoneEntityItem::getSkyboxMode() const { return _skyboxMode; } -void ZoneEntityItem::setKeyLightMode(const uint32_t value) { +void ZoneEntityItem::setKeyLightMode(const uint8_t value) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) { _keyLightMode = value; _keyLightPropertiesChanged = true; } } -uint32_t ZoneEntityItem::getKeyLightMode() const { +uint8_t ZoneEntityItem::getKeyLightMode() const { return _keyLightMode; } -void ZoneEntityItem::setAmbientLightMode(const uint32_t value) { +void ZoneEntityItem::setAmbientLightMode(const uint8_t value) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientLightMode) { _ambientLightMode = value; _ambientLightPropertiesChanged = true; } } -uint32_t ZoneEntityItem::getAmbientLightMode() const { +uint8_t ZoneEntityItem::getAmbientLightMode() const { return _ambientLightMode; } -void ZoneEntityItem::setHazeMode(const uint32_t value) { +void ZoneEntityItem::setHazeMode(const uint8_t value) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _hazeMode) { _hazeMode = value; _hazePropertiesChanged = true; } } -uint32_t ZoneEntityItem::getHazeMode() const { +uint8_t ZoneEntityItem::getHazeMode() const { return _hazeMode; } -void ZoneEntityItem::setBloomMode(const uint32_t value) { +void ZoneEntityItem::setBloomMode(const uint8_t value) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _bloomMode) { _bloomMode = value; _bloomPropertiesChanged = true; } } -uint32_t ZoneEntityItem::getBloomMode() const { +uint8_t ZoneEntityItem::getBloomMode() const { return _bloomMode; } -void ZoneEntityItem::setTonemappingMode(const uint32_t value) { +void ZoneEntityItem::setTonemappingMode(const uint8_t value) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _tonemappingMode) { _tonemappingMode = value; _tonemappingPropertiesChanged = true; } } -uint32_t ZoneEntityItem::getTonemappingMode() const { +uint8_t ZoneEntityItem::getTonemappingMode() const { return _tonemappingMode; } -void ZoneEntityItem::setAmbientOcclusionMode(const uint32_t value) { +void ZoneEntityItem::setAmbientOcclusionMode(const uint8_t value) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientOcclusionMode) { _ambientOcclusionMode = value; _ambientOcclusionPropertiesChanged = true; } } -uint32_t ZoneEntityItem::getAmbientOcclusionMode() const { +uint8_t ZoneEntityItem::getAmbientOcclusionMode() const { return _ambientOcclusionMode; } diff --git a/libraries/shared/src/ComponentMode.h b/libraries/shared/src/ComponentMode.h index f1d030cefd..9cf773e997 100644 --- a/libraries/shared/src/ComponentMode.h +++ b/libraries/shared/src/ComponentMode.h @@ -14,7 +14,7 @@ #include -enum ComponentMode { +enum ComponentMode : uint8_t { COMPONENT_MODE_INHERIT, COMPONENT_MODE_DISABLED, COMPONENT_MODE_ENABLED, @@ -22,7 +22,7 @@ enum ComponentMode { COMPONENT_MODE_ITEM_COUNT }; -enum AvatarPriorityMode { +enum AvatarPriorityMode : uint8_t { AVATAR_PRIORITY_INHERIT, AVATAR_PRIORITY_CROWD, AVATAR_PRIORITY_HERO, From 661ca01c098bd9f14a04e46a81a31555cda71bbc Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Mon, 9 Sep 2024 15:23:25 -0500 Subject: [PATCH 080/109] Fixed mouse look in selfie mode. --- interface/resources/controllers/keyboardMouse.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index e0034a94db..47f9b97380 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -147,13 +147,22 @@ { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, "to": "Actions.DeltaPitch", - "when": "Application.CaptureMouse", + "when": ["Application.CaptureMouse", "!Application.CameraSelfie"], "filters": [ { "type": "scale", "scale": 0.2 } ] }, + { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, + "to": "Actions.DeltaPitch", + "when": ["Application.CaptureMouse", "Application.CameraSelfie"], + "filters": + [ + { "type": "scale", "scale": -0.2 } + ] + }, + { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, "when": ["Application.CameraLookAt", "Keyboard.RightMouseButton"], "to": "Actions.DeltaPitch", From 0dd0d07466aa2c88018f2929ab8a338bb4f1d378 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Mon, 9 Sep 2024 13:38:55 -0700 Subject: [PATCH 081/109] fix text debug assert on invalid or unloaded font --- .../entities-renderer/src/RenderableTextEntityItem.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index e6b42db0db..9aec4c47f1 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -367,7 +367,11 @@ void entities::TextPayload::render(RenderArgs* args) { transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), textRenderable->_billboardMode, usePrimaryFrustum ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); - float scale = textRenderable->_lineHeight / textRenderer->getFontHeight(); + float scale = 1.0f; + float fontHeight = textRenderer->getFontHeight(); + if (fontHeight > 0.0f) { + scale = textRenderable->_lineHeight / fontHeight; + } transform.postTranslate(glm::vec3(-0.5, 0.5, 1.0f + EPSILON / dimensions.z)); transform.setScale(scale); batch.setModelTransform(transform); From 1533f6a26842adb9cdcccc0f096c24677cee3b83 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Thu, 12 Sep 2024 19:33:31 -0700 Subject: [PATCH 082/109] missed some enums --- libraries/entities/src/EntityItem.h.in | 2 +- libraries/shared/src/TextVerticalAlignment.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityItem.h.in b/libraries/entities/src/EntityItem.h.in index d1d4c49352..837c84714c 100644 --- a/libraries/entities/src/EntityItem.h.in +++ b/libraries/entities/src/EntityItem.h.in @@ -69,7 +69,7 @@ class MeshProxyList; #endif namespace entity { -enum class HostType { +enum class HostType : uint8_t { DOMAIN = 0, AVATAR, LOCAL diff --git a/libraries/shared/src/TextVerticalAlignment.h b/libraries/shared/src/TextVerticalAlignment.h index 67dc5162b4..889171b97d 100644 --- a/libraries/shared/src/TextVerticalAlignment.h +++ b/libraries/shared/src/TextVerticalAlignment.h @@ -26,7 +26,7 @@ * @typedef {string} Entities.TextVerticalAlignment */ -enum class TextVerticalAlignment { +enum class TextVerticalAlignment : uint8_t { TOP = 0, BOTTOM, CENTER, From 6a3a47bfc4f8ace1858e79af94bed39ef7a3ef51 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 29 Sep 2024 14:50:46 -0700 Subject: [PATCH 083/109] fix ADD_GROUP_PROPERTY_TO_MAP --- cmake/macros/GenerateEntityProperties.cmake | 80 ++++++++++----------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/cmake/macros/GenerateEntityProperties.cmake b/cmake/macros/GenerateEntityProperties.cmake index 11bc3ff0cb..ef37b24149 100644 --- a/cmake/macros/GenerateEntityProperties.cmake +++ b/cmake/macros/GenerateEntityProperties.cmake @@ -108,11 +108,11 @@ macro(GENERATE_ENTITY_PROPERTIES) endif() endif() elseif(LINE MATCHES "group:.*,") - string(REGEX MATCH ".*group:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_GROUP ${LINE}) + string(REGEX MATCH ".*group:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_GROUP ${LINE}) set(ENTITY_PROPERTY_GROUP ${CMAKE_MATCH_1}) CAPITALIZE_FIRST_LETTER(${ENTITY_PROPERTY_GROUP}) set(ENTITY_PROPERTY_GROUP_CAPS ${CAPITALIZE_RESULT}) - string(REGEX MATCH ".*type:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_GROUP_TYPE ${LINE}) + string(REGEX MATCH ".*type:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_GROUP_TYPE ${LINE}) if (CMAKE_MATCH_1) set(ENTITY_PROPERTY_GROUP_TYPE "${CMAKE_MATCH_1}PropertyGroup") else() @@ -164,50 +164,50 @@ macro(GENERATE_ENTITY_PROPERTIES) string(CONCAT ${CURRENT_TYPE}_ENTITY_DEBUG "${${CURRENT_TYPE}_ENTITY_DEBUG}" "\t_${ENTITY_PROPERTY_GROUP}Properties.debugDump();\n") endif() else() - string(REGEX MATCH ".*enum:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_ENUM ${LINE}) + string(REGEX MATCH ".*enum:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_ENUM ${LINE}) string(CONCAT ENTITY_PROPERTY_ENUM "PROP_" ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*prop:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_NAME ${LINE}) + string(REGEX MATCH ".*prop:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_NAME ${LINE}) set(ENTITY_PROPERTY_NAME ${CMAKE_MATCH_1}) CAPITALIZE_FIRST_LETTER(${ENTITY_PROPERTY_NAME}) set(ENTITY_PROPERTY_NAME_CAPS ${CAPITALIZE_RESULT}) - string(REGEX MATCH ".*type:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_TYPE ${LINE}) + string(REGEX MATCH ".*type:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_TYPE ${LINE}) set(ENTITY_PROPERTY_TYPE ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*default:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_DEFAULT ${LINE}) + string(REGEX MATCH ".*default:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_DEFAULT ${LINE}) set(ENTITY_PROPERTY_DEFAULT ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*min:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_MIN ${LINE}) + string(REGEX MATCH ".*min:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_MIN ${LINE}) set(ENTITY_PROPERTY_MIN ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*max:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_MAX ${LINE}) + string(REGEX MATCH ".*max:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_MAX ${LINE}) set(ENTITY_PROPERTY_MAX ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*fromScriptType:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_FROM_SCRIPT_TYPE ${LINE}) + string(REGEX MATCH ".*fromScriptType:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_FROM_SCRIPT_TYPE ${LINE}) set(ENTITY_PROPERTY_FROM_SCRIPT_TYPE ${CMAKE_MATCH_1}) if (NOT ENTITY_PROPERTY_FROM_SCRIPT_TYPE) set(ENTITY_PROPERTY_FROM_SCRIPT_TYPE ${ENTITY_PROPERTY_TYPE}) endif() - string(REGEX MATCH ".*getter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_GETTER ${LINE}) + string(REGEX MATCH ".*getter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_GETTER ${LINE}) set(ENTITY_PROPERTY_GETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*setter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_SETTER ${LINE}) + string(REGEX MATCH ".*setter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_SETTER ${LINE}) set(ENTITY_PROPERTY_SETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*networkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_NETWORK_GETTER ${LINE}) + string(REGEX MATCH ".*networkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_NETWORK_GETTER ${LINE}) set(ENTITY_PROPERTY_NETWORK_GETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*variableNetworkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_VARIABLE_NETWORK_GETTER ${LINE}) + string(REGEX MATCH ".*variableNetworkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_VARIABLE_NETWORK_GETTER ${LINE}) set(ENTITY_VARIABLE_NETWORK_GETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*variableNetworkSetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_VARIABLE_NETWORK_SETTER ${LINE}) + string(REGEX MATCH ".*variableNetworkSetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_VARIABLE_NETWORK_SETTER ${LINE}) set(ENTITY_VARIABLE_NETWORK_SETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*variableCopyGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_VARIABLE_COPY_GETTER ${LINE}) + string(REGEX MATCH ".*variableCopyGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_VARIABLE_COPY_GETTER ${LINE}) set(ENTITY_VARIABLE_COPY_GETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*variableCopySetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_VARIABLE_COPY_SETTER ${LINE}) + string(REGEX MATCH ".*variableCopySetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_VARIABLE_COPY_SETTER ${LINE}) set(ENTITY_VARIABLE_COPY_SETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*readType:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_READ_TYPE ${LINE}) + string(REGEX MATCH ".*readType:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_READ_TYPE ${LINE}) set(ENTITY_PROPERTY_READ_TYPE ${CMAKE_MATCH_1}) if (NOT ENTITY_PROPERTY_READ_TYPE) set(ENTITY_PROPERTY_READ_TYPE ${ENTITY_PROPERTY_TYPE}) endif() - string(REGEX MATCH ".*debugString:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_DEBUG_STRING ${LINE}) + string(REGEX MATCH ".*debugString:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_DEBUG_STRING ${LINE}) set(ENTITY_PROPERTY_DEBUG_STRING ${CMAKE_MATCH_1}) if (NOT ENTITY_PROPERTY_DEBUG_STRING) set(ENTITY_PROPERTY_DEBUG_STRING "\"\"") endif() - string(REGEX MATCH ".*debugGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_DEBUG_GETTER ${LINE}) + string(REGEX MATCH ".*debugGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_DEBUG_GETTER ${LINE}) set(ENTITY_PROPERTY_DEBUG_GETTER ${CMAKE_MATCH_1}) if(NOT LINE MATCHES ".*legacy( |,).*") @@ -239,7 +239,7 @@ macro(GENERATE_ENTITY_PROPERTIES) endif() endif() string(CONCAT ENTITY_ITEM_PROPERTY_MERGE "${ENTITY_ITEM_PROPERTY_MERGE}" "\tCOPY_PROPERTY_IF_CHANGED(${ENTITY_PROPERTY_NAME});\n") - if(ENTITY_PROPERTY_MIN AND ENTITY_PROPERTY_MAX) + if(DEFINED ENTITY_PROPERTY_MIN AND DEFINED ENTITY_PROPERTY_MAX) string(CONCAT ENTITY_ITEM_PROPERTY_ADD_TO_MAP "${ENTITY_ITEM_PROPERTY_ADD_TO_MAP}" "\t\tADD_PROPERTY_TO_MAP_WITH_RANGE(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_MIN}, ${ENTITY_PROPERTY_MAX});\n") else() string(CONCAT ENTITY_ITEM_PROPERTY_ADD_TO_MAP "${ENTITY_ITEM_PROPERTY_ADD_TO_MAP}" "\t\tADD_PROPERTY_TO_MAP(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_TYPE});\n") @@ -342,9 +342,9 @@ macro(GENERATE_ENTITY_PROPERTIES) endif() else() if(NOT LINE MATCHES ".*noScript( |,).*") - string(REGEX MATCH ".*proxy:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_PROXY ${LINE}) + string(REGEX MATCH ".*proxy:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_PROXY ${LINE}) set(ENTITY_PROPERTY_PROXY ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*proxyType:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" ENTITY_PROPERTY_PROXY_MAP_TYPE ${LINE}) + string(REGEX MATCH ".*proxyType:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_PROXY_MAP_TYPE ${LINE}) set(ENTITY_PROPERTY_PROXY_MAP_TYPE ${CMAKE_MATCH_1}) if (NOT ENTITY_PROPERTY_PROXY_MAP_TYPE) set(ENTITY_PROPERTY_PROXY_MAP_TYPE ${ENTITY_PROPERTY_TYPE}) @@ -367,7 +367,7 @@ macro(GENERATE_ENTITY_PROPERTIES) string(CONCAT ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_FROM_SCRIPT}" "\t}\n") endif() endif() - if(ENTITY_PROPERTY_MIN AND ENTITY_PROPERTY_MAX) + if(DEFINED ENTITY_PROPERTY_MIN AND DEFINED ENTITY_PROPERTY_MAX) string(CONCAT ENTITY_ITEM_PROPERTY_ADD_TO_MAP "${ENTITY_ITEM_PROPERTY_ADD_TO_MAP}" "\t\tADD_PROPERTY_TO_MAP_WITH_RANGE(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_PROXY_MAP_TYPE}, ${ENTITY_PROPERTY_MIN}, ${ENTITY_PROPERTY_MAX}); // legacy support\n") else() string(CONCAT ENTITY_ITEM_PROPERTY_ADD_TO_MAP "${ENTITY_ITEM_PROPERTY_ADD_TO_MAP}" "\t\tADD_PROPERTY_TO_MAP(${ENTITY_PROPERTY_ENUM}, ${ENTITY_PROPERTY_NAME}, ${ENTITY_PROPERTY_PROXY_MAP_TYPE}); // legacy support\n") @@ -408,11 +408,11 @@ macro(GENERATE_ENTITY_PROPERTIES) while(GROUP_PROPERTIES_FILE) list(POP_FRONT GROUP_PROPERTIES_FILE LINE) if(NOT LINE MATCHES ".*,") - string(REGEX MATCH ".*type:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+).*" GROUP_PROPERTY_TYPE ${LINE}) + string(REGEX MATCH ".*type:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+).*" GROUP_PROPERTY_TYPE ${LINE}) set(GROUP_PROPERTY_TYPE ${CMAKE_MATCH_1}) if (GROUP_PROPERTY_TYPE) - string(REGEX MATCH "([A-Z0-9a-z_<>::\/\.\"\(\)\+]+) type:.*" CURRENT_TYPE ${LINE}) + string(REGEX MATCH "([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+) type:.*" CURRENT_TYPE ${LINE}) set(CURRENT_TYPE ${CMAKE_MATCH_1}) CAPITALIZE_FIRST_LETTER(${CURRENT_TYPE}) set(CURRENT_TYPE_CAPS ${CAPITALIZE_RESULT}) @@ -426,36 +426,36 @@ macro(GENERATE_ENTITY_PROPERTIES) list(APPEND GROUP_TYPES ${CURRENT_TYPE_CAPS}) endif() else() - string(REGEX MATCH ".*enum:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_ENUM ${LINE}) + string(REGEX MATCH ".*enum:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_PROPERTY_ENUM ${LINE}) string(CONCAT GROUP_PROPERTY_ENUM "PROP_" ${CMAKE_MATCH_1}) list(APPEND ${CURRENT_TYPE}_PROPERTY_FLAGS ${GROUP_PROPERTY_ENUM}) - string(REGEX MATCH ".*prop:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_NAME ${LINE}) + string(REGEX MATCH ".*prop:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_PROPERTY_NAME ${LINE}) set(GROUP_PROPERTY_NAME ${CMAKE_MATCH_1}) CAPITALIZE_FIRST_LETTER(${GROUP_PROPERTY_NAME}) set(GROUP_PROPERTY_NAME_CAPS ${CAPITALIZE_RESULT}) - string(REGEX MATCH ".*type:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_TYPE ${LINE}) + string(REGEX MATCH ".*type:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_PROPERTY_TYPE ${LINE}) set(GROUP_PROPERTY_TYPE ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*default:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_DEFAULT ${LINE}) + string(REGEX MATCH ".*default:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_PROPERTY_DEFAULT ${LINE}) set(GROUP_PROPERTY_DEFAULT ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*min:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_MIN ${LINE}) + string(REGEX MATCH ".*min:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_PROPERTY_MIN ${LINE}) set(GROUP_PROPERTY_MIN ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*max:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_MAX ${LINE}) + string(REGEX MATCH ".*max:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_PROPERTY_MAX ${LINE}) set(GROUP_PROPERTY_MAX ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*getter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_GETTER ${LINE}) + string(REGEX MATCH ".*getter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_PROPERTY_GETTER ${LINE}) set(GROUP_PROPERTY_GETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*setter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_SETTER ${LINE}) + string(REGEX MATCH ".*setter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_PROPERTY_SETTER ${LINE}) set(GROUP_PROPERTY_SETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*networkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_PROPERTY_NETWORK_GETTER ${LINE}) + string(REGEX MATCH ".*networkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_PROPERTY_NETWORK_GETTER ${LINE}) set(GROUP_PROPERTY_NETWORK_GETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*variableNetworkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_VARIABLE_NETWORK_GETTER ${LINE}) + string(REGEX MATCH ".*variableNetworkGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_VARIABLE_NETWORK_GETTER ${LINE}) set(GROUP_VARIABLE_NETWORK_GETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*variableNetworkSetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_VARIABLE_NETWORK_SETTER ${LINE}) + string(REGEX MATCH ".*variableNetworkSetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_VARIABLE_NETWORK_SETTER ${LINE}) set(GROUP_VARIABLE_NETWORK_SETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*variableCopyGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_VARIABLE_COPY_GETTER ${LINE}) + string(REGEX MATCH ".*variableCopyGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_VARIABLE_COPY_GETTER ${LINE}) set(GROUP_VARIABLE_COPY_GETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*variableCopySetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_VARIABLE_COPY_SETTER ${LINE}) + string(REGEX MATCH ".*variableCopySetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_VARIABLE_COPY_SETTER ${LINE}) set(GROUP_VARIABLE_COPY_SETTER ${CMAKE_MATCH_1}) - string(REGEX MATCH ".*debugGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+]+)( |,)" GROUP_VARIABLE_DEBUG_GETTER ${LINE}) + string(REGEX MATCH ".*debugGetter:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_VARIABLE_DEBUG_GETTER ${LINE}) set(GROUP_VARIABLE_DEBUG_GETTER ${CMAKE_MATCH_1}) set(DEFINE_FUNC "DEFINE_PROPERTY_REF") @@ -522,7 +522,7 @@ macro(GENERATE_ENTITY_PROPERTIES) string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_SET_FROM "${${CURRENT_TYPE_CAPS}_GROUP_SET_FROM}" "\tSET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(${CURRENT_TYPE_CAPS}, ${GROUP_PROPERTY_NAME_CAPS}, ${GROUP_PROPERTY_NAME}, set${GROUP_PROPERTY_NAME_CAPS});\n") endif() - if(ENTITY_PROPERTY_MIN AND ENTITY_PROPERTY_MAX) + if(DEFINED GROUP_PROPERTY_MIN AND DEFINED GROUP_PROPERTY_MAX) string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_ADD_TO_MAP "${${CURRENT_TYPE_CAPS}_GROUP_ADD_TO_MAP}" "\tADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(${GROUP_PROPERTY_ENUM}, ${CURRENT_TYPE}, ${GROUP_PROPERTY_NAME}, ${GROUP_PROPERTY_MIN}, ${GROUP_PROPERTY_MAX});\n") else() string(CONCAT ${CURRENT_TYPE_CAPS}_GROUP_ADD_TO_MAP "${${CURRENT_TYPE_CAPS}_GROUP_ADD_TO_MAP}" "\tADD_GROUP_PROPERTY_TO_MAP(${GROUP_PROPERTY_ENUM}, ${CURRENT_TYPE}, ${GROUP_PROPERTY_NAME});\n") From ed4c5f23cf5b31915972038fa6441f58dbfe166b Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 29 Sep 2024 14:56:35 -0700 Subject: [PATCH 084/109] fix PROP_GRAB_EQUIPPABLE_INDICATOR_URL missing urlPermission --- libraries/entities/src/EntityItemGroupProperties.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItemGroupProperties.txt b/libraries/entities/src/EntityItemGroupProperties.txt index 12ad010285..3877f88bd2 100644 --- a/libraries/entities/src/EntityItemGroupProperties.txt +++ b/libraries/entities/src/EntityItemGroupProperties.txt @@ -9,7 +9,7 @@ enum:GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET prop:equippableLeftPosition type:vec3 enum:GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET prop:equippableLeftRotation type:quat default:INITIAL_LEFT_EQUIPPABLE_ROTATION, enum:GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET prop:equippableRightPosition type:vec3 default:INITIAL_RIGHT_EQUIPPABLE_POSITION, enum:GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET prop:equippableRightRotation type:quat default:INITIAL_RIGHT_EQUIPPABLE_ROTATION, -enum:GRAB_EQUIPPABLE_INDICATOR_URL prop:equippableIndicatorURL type:QString default:"", +enum:GRAB_EQUIPPABLE_INDICATOR_URL prop:equippableIndicatorURL type:QString default:"" urlPermission, enum:GRAB_EQUIPPABLE_INDICATOR_SCALE prop:equippableIndicatorScale type:vec3 default:INITIAL_EQUIPPABLE_INDICATOR_SCALE, enum:GRAB_EQUIPPABLE_INDICATOR_OFFSET prop:equippableIndicatorOffset type:vec3 default:INITIAL_EQUIPPABLE_INDICATOR_OFFSET, pulse From b88b187c07f3cf9a0152de0cde438b39d7ec8db8 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 29 Sep 2024 15:01:49 -0700 Subject: [PATCH 085/109] fix KeyLightPropertyGroup legacy properties --- libraries/entities/src/KeyLightPropertyGroup.cpp.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/entities/src/KeyLightPropertyGroup.cpp.in b/libraries/entities/src/KeyLightPropertyGroup.cpp.in index 54f3d67172..782fe9be81 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.cpp.in +++ b/libraries/entities/src/KeyLightPropertyGroup.cpp.in @@ -37,6 +37,11 @@ void KeyLightPropertyGroup::copyFromScriptValue(const ScriptValue& object, const @KeyLight_GROUP_COPY_FROM_SCRIPT@ + // legacy property support + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightColor, u8vec3Color, setColor, getColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightIntensity, float, setIntensity, getIntensity); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightDirection, vec3, setDirection, getDirection); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightCastShadows, bool, setCastShadows, getCastShadows); } void KeyLightPropertyGroup::merge(const KeyLightPropertyGroup& other) { From 474026b9e134551d0c2de4b722f58beff771ac82 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 29 Sep 2024 15:04:07 -0700 Subject: [PATCH 086/109] fix PolyLineEntityItem::getEntityProperties --- libraries/entities/src/PolyLineEntityItem.cpp.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/PolyLineEntityItem.cpp.in b/libraries/entities/src/PolyLineEntityItem.cpp.in index e4670bc35a..b8db81d7dc 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp.in +++ b/libraries/entities/src/PolyLineEntityItem.cpp.in @@ -55,7 +55,7 @@ bool PolyLineEntityItem::setSubClassProperties(const EntityItemProperties& prope EntityPropertyFlags PolyLineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); -@PolyLine_REQUESTED_PROP@ +@PolyLine_REQUESTED_PROPS@ return requestedProperties; } From 4e97090f965351a68259ea3bcccd0db6ba6865ab Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 29 Sep 2024 15:38:59 -0700 Subject: [PATCH 087/109] comment cmake script --- cmake/macros/GenerateEntityProperties.cmake | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/cmake/macros/GenerateEntityProperties.cmake b/cmake/macros/GenerateEntityProperties.cmake index ef37b24149..fd267f289e 100644 --- a/cmake/macros/GenerateEntityProperties.cmake +++ b/cmake/macros/GenerateEntityProperties.cmake @@ -17,6 +17,7 @@ endmacro() macro(GENERATE_ENTITY_PROPERTIES) message(STATUS "Entity property processing start") + # Define most of our variables. Some are initialized only in the loops below. set(ENTITY_ITEM_PROPERTY_DEFINES "") set(ENTITY_ITEM_PROPERTY_INCLUDES "#include \"EntityItem.h\"\n") set(ENTITY_ITEM_PROPERTY_FRIENDS "\tfriend class EntityItem;\n") @@ -36,16 +37,24 @@ macro(GENERATE_ENTITY_PROPERTIES) list(APPEND ENTITY_TYPES ${CURRENT_TYPE}) set(ENTITY_PROPERTY_FLAGS "") + # These types are passed by value everywhere, anything else is passed by reference. set(NON_REF_TYPES "bool" "int" "float" "uint8_t" "uint16_t" "quint16" "uint32_t" "quint32" "quint64") + # These property sections are shared by all types. "Common" must be at the end. set(BASE_ENTITY_TYPES "Core" "Physics" "Cloning" "Scripts" "LocalProps" "Common") + # These types have special methods for converting to/from scripts. set(TYPED_SCRIPT_CONVERT "u8vec3Color" "vec3Color" "qVectorVec3Color") set(COMMON_PROPS false) set(LOCAL_PROPS false) set(NEEDS_CLOSING_BRACE false) + # All (non-group) properties are defined in EntityItemProperties.txt. We loop over them in order. file(STRINGS src/EntityItemProperties.txt ENTITY_PROPERTIES_FILE) while(ENTITY_PROPERTIES_FILE) list(POP_FRONT ENTITY_PROPERTIES_FILE LINE) + + # Single-word lines represent sections of properties, which can be base properties like "Physics" or + # Entity types like "Model". We use the section names to generate comments, and also handle indentation + # and opening/closing braces. if(NOT LINE MATCHES ".*:.*") if(LINE STREQUAL "Common") set(COMMON_PROPS true) @@ -108,10 +117,13 @@ macro(GENERATE_ENTITY_PROPERTIES) endif() endif() elseif(LINE MATCHES "group:.*,") + # Lines that start with "group:" represent property groups like "grab". string(REGEX MATCH ".*group:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_GROUP ${LINE}) set(ENTITY_PROPERTY_GROUP ${CMAKE_MATCH_1}) CAPITALIZE_FIRST_LETTER(${ENTITY_PROPERTY_GROUP}) set(ENTITY_PROPERTY_GROUP_CAPS ${CAPITALIZE_RESULT}) + # Most property groups have the same filenames, e.g. "pulse" to "PulsePropertyGroup", but some are different, + # e.g. "ring" is "RingGizmoPropertyGroup" string(REGEX MATCH ".*type:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_GROUP_TYPE ${LINE}) if (CMAKE_MATCH_1) set(ENTITY_PROPERTY_GROUP_TYPE "${CMAKE_MATCH_1}PropertyGroup") @@ -164,6 +176,8 @@ macro(GENERATE_ENTITY_PROPERTIES) string(CONCAT ${CURRENT_TYPE}_ENTITY_DEBUG "${${CURRENT_TYPE}_ENTITY_DEBUG}" "\t_${ENTITY_PROPERTY_GROUP}Properties.debugDump();\n") endif() else() + # Everything else is just a normal, non-group property. Properties support a wide variety of settings, + # which control things like default/min/max, script conversions, and networking behavior. string(REGEX MATCH ".*enum:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_ENUM ${LINE}) string(CONCAT ENTITY_PROPERTY_ENUM "PROP_" ${CMAKE_MATCH_1}) string(REGEX MATCH ".*prop:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_NAME ${LINE}) @@ -341,6 +355,8 @@ macro(GENERATE_ENTITY_PROPERTIES) endif() endif() else() + # We have some "legacy" properties, which are mostly aliases of existing properties with old names or different types. + # They only need to worry about script conversions. if(NOT LINE MATCHES ".*noScript( |,).*") string(REGEX MATCH ".*proxy:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" ENTITY_PROPERTY_PROXY ${LINE}) set(ENTITY_PROPERTY_PROXY ${CMAKE_MATCH_1}) @@ -376,10 +392,12 @@ macro(GENERATE_ENTITY_PROPERTIES) endif() endwhile() + # Final closing parentheses string(CONCAT ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT "${ENTITY_ITEM_PROPERTY_COPY_TO_SCRIPT}" "\t}") string(CONCAT ENTITY_ITEM_PROPERTY_APPEND "${ENTITY_ITEM_PROPERTY_APPEND}" "\t\t\t}") string(CONCAT ENTITY_ITEM_PROPERTY_READ "${ENTITY_ITEM_PROPERTY_READ}" "\t}") + # Generate the real code! configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/src/EntityItemProperties.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/src/EntityItemProperties.cpp) @@ -404,9 +422,12 @@ macro(GENERATE_ENTITY_PROPERTIES) # Group Properties set(GROUP_TYPES "") + # All group properties are defined in EntityItemGroupProperties.txt. We loop over them in order. file(STRINGS src/EntityItemGroupProperties.txt GROUP_PROPERTIES_FILE) while(GROUP_PROPERTIES_FILE) list(POP_FRONT GROUP_PROPERTIES_FILE LINE) + + # Lines that don't end in a comma represent the start of a new property group. if(NOT LINE MATCHES ".*,") string(REGEX MATCH ".*type:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+).*" GROUP_PROPERTY_TYPE ${LINE}) set(GROUP_PROPERTY_TYPE ${CMAKE_MATCH_1}) @@ -426,6 +447,8 @@ macro(GENERATE_ENTITY_PROPERTIES) list(APPEND GROUP_TYPES ${CURRENT_TYPE_CAPS}) endif() else() + # Everything else is just a normal group property. Group properties support almost all of the same + # settings as non-group properties. string(REGEX MATCH ".*enum:+([A-Z0-9a-z_<>::\/\.\"\(\)\+\-]+)( |,)" GROUP_PROPERTY_ENUM ${LINE}) string(CONCAT GROUP_PROPERTY_ENUM "PROP_" ${CMAKE_MATCH_1}) list(APPEND ${CURRENT_TYPE}_PROPERTY_FLAGS ${GROUP_PROPERTY_ENUM}) @@ -538,6 +561,7 @@ macro(GENERATE_ENTITY_PROPERTIES) endif() endwhile() + # Generate the real code! foreach(TYPE IN LISTS GROUP_TYPES) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/src/${TYPE}PropertyGroup.cpp.in @@ -548,6 +572,8 @@ macro(GENERATE_ENTITY_PROPERTIES) list(APPEND GENERATE_ENTITIES_LIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/src/${TYPE}PropertyGroup.h ${CMAKE_CURRENT_BINARY_DIR}/src/${TYPE}PropertyGroup.cpp) endforeach() + # Lastly, now that we have a big list of all of our properties in order, we build our EntityPropertyList enum. + # Shared properties are defined first, and then subclass properties are defined using PROP_DERIVED_XXXX. set(HAS_REACHED_COMMON_PROPS false) set(DERIVED_PROP false) set(DERIVED_PROP_INDEX 0) @@ -589,6 +615,7 @@ macro(GENERATE_ENTITY_PROPERTIES) endif() endforeach() + # Generate the real code! configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/src/EntityPropertyFlags.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/EntityPropertyFlags.h) From 470cd776cc47fafe723fd0c9ad10ce069980a843 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 29 Sep 2024 16:10:44 -0700 Subject: [PATCH 088/109] fix copyright --- libraries/shared/src/TextVerticalAlignment.cpp | 4 ++-- libraries/shared/src/TextVerticalAlignment.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/TextVerticalAlignment.cpp b/libraries/shared/src/TextVerticalAlignment.cpp index fa9381cba0..a42b4c7e32 100644 --- a/libraries/shared/src/TextVerticalAlignment.cpp +++ b/libraries/shared/src/TextVerticalAlignment.cpp @@ -1,6 +1,6 @@ // -// Created by HifiExperiments on 2/9/21 -// Copyright 2021 Vircadia contributors. +// Created by HifiExperiments on 8/17/24 +// Copyright 2024 Overte e.V contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/shared/src/TextVerticalAlignment.h b/libraries/shared/src/TextVerticalAlignment.h index 889171b97d..938bb93abe 100644 --- a/libraries/shared/src/TextVerticalAlignment.h +++ b/libraries/shared/src/TextVerticalAlignment.h @@ -1,6 +1,6 @@ // // Created by HifiExperiments on 8/17/24 -// Copyright 2021 Vircadia contributors. +// Copyright 2024 Overte e.V contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html From 1ede7ed3e9ee72be055cd93b6ec57c3f247de08c Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Mon, 30 Sep 2024 08:02:44 -0500 Subject: [PATCH 089/109] Replaced key value with key text. Added additional comment about the specific delete key used. --- scripts/system/create/edit.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index c9f167e54b..624886e454 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -2133,19 +2133,19 @@ // Hacks to get the menu bar buttons to work // Copy - if (event.key === 67 && event.isControl && !event.isShifted) { + if (event.text.toLowerCase() === "c" && event.isControl && !event.isShifted) { selectionManager.copySelectedEntities(); } // Paste - if (event.key === 86 && event.isControl && !event.isShifted) { + if (event.text.toLowerCase() === "v" && event.isControl && !event.isShifted) { selectionManager.pasteEntities(); } // Cut - if (event.key === 88 && event.isControl && !event.isShifted) { + if (event.text.toLowerCase() === "x" && event.isControl && !event.isShifted) { selectionManager.cutSelectedEntities(); } - // Delete - if (event.key === 16777223 && !event.isControl && !event.isShifted) { + // Delete - This uses the physical 'delete' key on a keyboard. + if (event.text.toLowerCase() === "delete" && !event.isControl && !event.isShifted) { createApp.deleteSelectedEntities(); } }; From 8526f09a9d83955f25002102bf97506ff94f50ad Mon Sep 17 00:00:00 2001 From: Alezia Kurdis <60075796+AleziaKurdis@users.noreply.github.com> Date: Mon, 30 Sep 2024 20:27:09 -0400 Subject: [PATCH 090/109] weekly promoted place Highlight the first place in the list as the weekly promoted place --- scripts/system/places/places.css | 12 ++++++++++++ scripts/system/places/places.html | 26 ++++++++++++++++++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/scripts/system/places/places.css b/scripts/system/places/places.css index 6b52dc2239..37eac2d002 100644 --- a/scripts/system/places/places.css +++ b/scripts/system/places/places.css @@ -487,6 +487,10 @@ div.placeEntry { text-align: center; } +div.placeEntryLargeSize { + height: 220px; +} + div.addMsEntry { width: 85%; height: 38px; @@ -625,6 +629,10 @@ div.placeDetail-event { border: 0px; } +.promotedblur { + backdrop-filter: blur(0px); +} + .placeTitle { color: #ffffff; font-size: 21px; @@ -633,6 +641,10 @@ div.placeDetail-event { text-shadow: 1px 1px 3px #000000; } +.placeTitlePromoted { + font-size: 28px; + text-shadow: 1px 1px 4px #000000; +} .placeTable { width: 100%; } diff --git a/scripts/system/places/places.html b/scripts/system/places/places.html index 5dbb11b907..980d8f0f1f 100644 --- a/scripts/system/places/places.html +++ b/scripts/system/places/places.html @@ -415,8 +415,12 @@ } else if (placeRecords[i].category === "Z") { moreStyle = " style='background-color: #364a6e;'"; } - - var shortenedDescription = placeRecords[i].description.substr(0, 55); + + var maxCharNumber = 55; + if (currentListFormat === "PLACES" && i === 0) { + maxCharNumber = 180; + } + var shortenedDescription = placeRecords[i].description.substr(0, maxCharNumber); if (shortenedDescription.slice(-1) !== ".") { shortenedDescription = shortenedDescription + "..."; } @@ -429,21 +433,31 @@ placeUrl = "file:///~/serverless/tutorial.json"; } + var promotedblur = ""; + var promoted = ""; + var placeTitlePromoted = ""; + var prompotionDescHeight = ""; + if (currentListFormat === "PLACES" && i === 0) { + promotedblur = " promotedblur"; + promoted = " placeEntryLargeSize"; + placeTitlePromoted = " placeTitlePromoted"; + prompotionDescHeight = " style='height: 175px; '"; + } //Add the place to the list - formatedList = formatedList + "
    "; + formatedList = formatedList + "

    PolyVox entities uses a library from Volumes of Fun. Their - * library documentation may be useful to read.

    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 J`wJ< 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

  2. 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�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%0A*AscBex@j7_Y& z)2}vp?mI0i&&nQGBnX_%JgiT|?iZ4Q{GSalTE-X6AhNlN^UE5%mnrIB$0Twqox-cJ zQbFz4Z=M-4cVC@Ow!$J?WWik^%~%-)S8kU16potKypBFGCZEhm)blsrkY}S z?rdVBd{t(2R&qW^?S2?=JIou(T4b=ls8V;t1?>OT14bo%6 z!2IiFH5?_SnSMjH^eFggWMW`zBRy3>AOeU%PQYRJ-Gqlr=|~L zho|4a(UZq2H{8OuXTO{Z+PYqM!91E}^lC-9c$%h+!~!`J+*fggnZ;-5SzETc)ew~F%K&fpVl9X3-+)B**5XMB`*G=eQF zjuQI0wE<*lC8F=}8tudr`yq-#SN9r&Jy z-vK+ZhQa0+_Tt-7`mqJfMU_WQtNl%KQRc`ReKGl!<_L5@CTRXw)d~$!Vrz*uCVD0lYBWtB>pM0I#8=(fIM(hj>~Ry(NKBANzm%|N3L``AIZOtv2@2myPYTa!S0Z!d3^JNZ3)kW7d%w zSV0&ilT=+BPWZmHC{ZBXxoUhOz8x?})Ku*D0cWQEU1^JFtGB7BnPZg_w2cZ98A<3( z8zxN~40*hp^H0qO%u>W|6_=71ZuD{XC^`W_(<&#TK-r95TwHmdO}3Dutg1u+Aoysf zxF0&7qa;T&_~f2UmtOw>aavm2#VKi^Vw4m$B!D=P@+SusprslaxwzDA1GBBw!m9J9 z$JU^M@&MN*5xQ!gljmz+-v z^VWhI+Rj;oQpK?mNgmstl6h#ly#4O>-{%34b3+G4b^>K2J4&UY?Vh%2{2 z(2tf0bsqHri+)=)w;JWG&Ze}tk)8%(1rW%z4fFTO!+K>tPiFE>tlyU(m=RlQcO&#_ z$s)$(s*@{<4nv_*clGKeudK_@{fM0L~_wO)E&$-s+Lf5-*_JFla{}V0aWO^Eh&SOAX#>X?|`k zE-n3FQ2cM|R2Qk(4`J5hfh&OWVrtg^t{g3Jn|4|T~)eVFjsV}4rm3u!RP z454L;RCC5f4e^o0Q{Qc{5}8HyFXb$8`K!xEN4n*2EsAZ(>57^BHS zRv-lx@ad8U&7_21mpsd-c~@4rwu&`!Ua`$iOSfPTk4&6J?8JgXC(RyCzHLq^Ebl@k z&Ad}f7=NJPOtA{1#s?`J+l)#CF10GqVyc)eNP{!wXp9H;G6nh<+?oSrkz zJ>iVai(|i<8Wfs#vw5d0spRc4vhGVNim3kpDxC*@z4DL@d9$~P+g@rGH*0MZN|CI; zx>Y8etGNUG`{ah^``L;q&zXG9bF19R1j?}vh>?$lSZqih2Xpf{0p@`ouUztfoozIA zvhylYwLLbG$O!vzP%6{>thb0BUgoI_YxBd)7J77+-e$JA4F;>pqp#Ww4Oo5cm2j9! zyqUs8{2oE1Y4>qS9n(N$fi|yDU%%noC445=iqXDc)@0Q6L3M9#w(A*2P@fjbKnd~b zk&egY$}zTRHN7{_ny=M0coxn}l8YR+`zpwQD%CtFH~Hfo!x=|}CjS7PI-TX0=>GsC zTmoRz?Gdfq01G6G@RmKurZPNIOm_(emF2xt^1D^C)%7Fj6L0mjla~A(RMd4XMx{D* z$pgTC3QrJ0TluhS-gARg)RaSF*P>TN6bd9GpHE@a-yCALDdDK7{%UB}ns=Bl^q8eF z#RFCYO4J^MZ`pD2S0i?7%`r-~Pb6OXa_MxfNl&QIqmsMvSyUCTw)^4cX&xgPxQXp9 zCbUjQj8%DmzWN5QzDJ2KpuaM-J6#j<{{YL{dWHQXa4s%x2&Gi9_z?X7oR&|igiXGG zpGwqi;I}t%zt!ee5E|8J4(IP1WO)`=s%M&8cq~f#ZwoVnLJJ1mSHHd+;|bxS^qHd5 zJg1@Qeqyi%b)_TSsf6Ol+TWS7fa?&cv zaK5wJ#&?gG+8zK7qsJn7ki<@cQ*EZ%c|!7Qd*>onw*LS`<@Q6C6{zwQ-1`$V+Bjb?1cgqz|QN7T;IYU40o}tz$KWIc80$4&+8RZv?;6=4*R@F;7EpJf&ma zv>^38`u@DEZL=^DdfdL3p=g2PqSeC+6Cosx{3u2cNGkOU@63DE@_b%iu$R%6In1+$ zP)mTcUMI(`d}=%~$_*^L?uR;g6wnSf6!7C|MoJRIkR&FHFd|Q*q&|!~OVpJa6 zH<)y&yoq__`_NNVTcIR$CWQ|ZPzUV-C_Rb6GjRjCtPRwBo#*>+EI}5YyrhbId2Q|> zJD_5p45&I*wZxsNOgN3P7(DGYv=5|d@d_l&@k&p{z>l-{<9F~!hiT`RWx4ZhuBoO( z!XUw}pyHnn`ybB#fKy#Erel8)N~n*~l@Kc`qlC-M-Tn z+MRX;{XOtRQf`?}v!%T%5&G?hJ3$Zz$8Pn*Qe6#Y;%8 z!WimkMSWei9lYuJa(K~;fXR3&BO{CiuL=BAaqb|PD$1a zRF)*{7KH2$#=lST!a1OGdQasxynL}W%#+RO*v}6cjGNG{NEG>!2ewZb+KU+mIp;qh z$>rGghdjLxb`l{q@RzlFNyPNpSauDD>reC<^r_<6~3HZ^Px2PZe`a zZ0%m!E0uqO<~Y`<-?kfK2fcgZQcbBzIg3h!%_B(B$wMCe5~Ha1G{lam44=z7>>35+ z+M3#ZY6p$uXyxhg5zeQ+-7;4}k1+I~$`322n(cL%ZCQoP@P@aAIuNhL`t+q#{D#=@ z*uOML?igQ@JgE!nXG*>J@BLYQIKZflpeOBDu@!Hko$?WlpMuU*kbY!d3(NbTRQiex zD6)gdg#i2`<&g}5rYe?_d470}z~xm}Qbxpd9eev^uC`5Br=j_G%{TfzzNw+u5))U6 zNi^L;64d*;WWljsY-j4+^bb?~=F+sUE@-}7vw8`Z#x%H&gPAUQD6eV`zS#_;*F)UK z2E)eu_ov-xT1B>ltf7xlfrEuwar7A!$3i-`=a+qkG8A|GZkTUnh}i1TTIn-c&2GND z(njkm4~VD*Pah5N9#qW3l4a!WM#{?S-VJO#+F+d?czZZg-{dQk29RR6UxqzTT9W%n zRgTf+Vu%eWy|x`HJ7r)f**Wrcqs6UR{bDi?s*H+j#MF0ks$#(PN9b{x;eUzJAP>6T>P zsi;eLg~hB}=@(++{{V=)Hq8(WC^=G`%|B?@Z@U9!BK(!Ae~LAEJf)~JJ)P8SkyCT( zpR>15w0@C>08L1(-Q+JJo4+w$U0%B_zME|=$=m>CilI;w;zrpR%7vmwYGA%_^F8mJ zbqVhESLgOwSO5S;UMsh5oA{hS+L?frX*TWarL!Y5HCKT&A4VQ!NqTfzOfdP54I9jn zA3<4R;D(hZi`T?s6TLC%Z*lzbGkH_WdS{UngG0zk8^*LeS8%d;zVzjhnX^nFA3bQ^ zP*l)n^M&omj$66j1&W=cp=Pg#Po(7MIskXFk=^N6UU0GU)|EDyqN%sHodXu2tC>&< z2P3@+6a-)(njOijELu44^*u0a`rCa`CMfn!!|kLhLmGA`_DTL2Wz7CLqWV0$&NuHr zro2iajW?(~&q98m!y;KkCthCC?zDRr^KGeLPAON4Hr$weOHu49Ym(%dNK0mY`8DQ9 zZv4k_cjhNA4yOxSyC2yE0tGwtrV+@oka^Fayp=YDV%9*UcXBZw7i#Ro*!KBk4$Pu# z78zz#idwUPUjf1wDwtT?yuG3R!B-BkfO97v`>kpaFld<-{j)9o#)FVqM*95 zx45q|IEEZQYp@5z{IbY?hBM^HKRfQU?MuuO&kUP87i0#sYSEN?4%toJl^b$mdVZ^K zb)*k3OscXxqD7LM{g5~Nu=5E3o!GrTr!&L96>o%|>%9&hE7_e>+W!D4yvcFo{cq~5 zjOS2|ZRS!cLb&klPC(bg2$g~wraG~GFI0!g{(Jp1c+PDt?j)q0TZpQHqu3Gp@;J>i z0@(imoS;jsZ}h-Eovq}ibO&;%$7=gm^<-hm0N6@yBcD*7TY|Mzbp&!C6{o|$jWTS5 zAv)Ei){#DedkYAR@ktbld2n?UVeT8L@Wy7)B9d9g&WIP6q@aP{W;EaKz%JC6&@yRF z6y_ z-sR1Th+Rx(K06PId3^H8a|kJQos_zz$JOCxkQSgLde^4jJwBX|RNV)tCDy6t=+Bk) zXw?m!xrQH9(4iZG$FLnv6YAZE2#(>A`94hp%NMJm+stn-;)ux{w4#aBkKL%~N#2Jf zWGa?K$oC;XG2c{IJYT(yeYjFzvPozVUXPwy8zR< zMFf+`pfOdUUXM+Q-`j7VCz~RkK?GOHEEiG)iQ-ep8u4I21Lt12YnlpUrD{{Hjnzr^JT8DbEjH)zU`gtuX(ROwZ+wlqaPA$^kq>MUg7ts9T@kR{HJl{)Pm%)%N^Vv zv~niGf`I1dSYA18>D0 z_U2DuqPX&nrd{td(j!}&<`+DqV-c5lA8E`cIgb$u_#Lm#%p`YDk7T zB#3$X!0QGQ4|nJX>y^m$f`HAnuTMM>5>#9$>YxZm>-miGbLO zf)BpieyljBy#ybmqrW|_njbdZSzE|VO36aI0tyO`Y7@Rf$u?O-kYF%f-M^=H(-nVJ zXionCW}HqklOQb9*Dw6UWz%iE^QHj0wWP#OD0s4!#hO(uSI(JgiB zOHVfF`TxSxq~d|9a7ZWS=-%2T`r_&NuVoE3>bx>G^k)dUQW|=&#W|C zZ7a$ep0lV{JHBv7ZW^GGB}1AY0YmiRmrr{Vc#9qKR;O*GX?oYLIZtzHEn;pwg8V^^ zJPy?PU?*ftnwOBg-+Sfba@I})-6JH?)`El(dhJ?ohl^yG-FZDHUh{fsb$IN|3y1RIrc*czsdX1i^aQsAR!cHI# zO}Sd{y$2zL{VdJBcga3ST^;n#I4uC?BzPqiWa8g#N3iK$^vMZPKISu(@=A4|I{u9F zJicSKXH_yksUPh(Gg|%CAY=z6+^TCnQU3rIXcPIu(jyY2>{V&m--{9S;3I@=h@IX) z{vx?cSaj6|Vz-;sRAWjo6(oB$+hHQZWiq`cAtD7GqT{o$tUBanwR8)o!TCZS9JbkiI z99-JSATZry$*=i)sNdOY%u;Ae{Z2q>Lfdkue}`?naw1eWXT@(Hia|BNp5%JGgAzUZ zF8%Aa8AgpWXSTN#iKTL>3MGsN&pe$BR&d?h z$yp6ZKz9`K3^v5*U7D_+8ZkwBK_#%u4Srx{>u_D0AmuF8xIS+N&f&zNCgOKc}HEy75FPp zZ=);ZEwiI0kUfT+7!m7F%)D$qAWak&@Z1nR@^UoCt1|qs{U4#-{{VvmIU+Ju^A z$5ehYf0WhSf_!@9V=5Nd85oXjBT03twY~UXP;?`MeV^5m$=r*w#Pp)E)Vmc?MI1+3 zp1BUGN5ros#8E_O&r|?xc~+D^b}-u1jh@GQtJ+26o3AX{(A^42boHpLBv1-=A&=59 z&(#pZ0*gBQg_hiTe^#4NA(0_ioG=AIdXB!-srJj|TsyVMQGD^K!F%P)_0&;H`oUVO z_Qih54em}-Hf;jcbvXo2ZKy2H^W3X6hq{wfw%w~wd>|&%?=4!}*j&SVcZz&z&S-p^W|UoC0TtXBaQE!DqknFWWj z196R7ows5)9$C~hNgB@T6B%{@vHg)lzTbb#AQ^*cZQbCoxF*g|R*VRK-M1baZH!83 zxaE@F;?yN2L6nYFuH&vC{iy?^+`l>OmrU}1m+Yk+W{NIIJVeEuf;>Z1e6o4|q3&wN zZv5UopI(vnV1Y{o)kA7GUf9vqKzjOEoaG&g$ppY3e#e79k!>Pn+oTUGkS zY06k-P*K~!j+s1yi(Coh`Re}wPep=Jr?)m%jicpCs+Hv3J@&|Zn_^9)bR8E|^DO9X zVg=%;-1TEpHue}tctMpi=h68v)T4z&nth~P;bFVVu6am6+$aZsqaL329B)d~Y!>s& z8g8|CN7f{{`og~S_)UF3pvDOv2PKv^VuE^kFbmx>&fi$P^ELkfBX7*`MW+)hHp^b1 zaO?8O$6}!EOiRsIcYl~(W=pAMiACG|9YTRhD5qgTLGr-?&75VznP|FJm8MATt6d*b z2~b88VIr#%#19>XFvJ8IThiyc*EJt7T0MdXxRxK?$QOe=WY7-V4Y3iun299bXVUEN z)(bKTyCK?Tufn0;iCXmAd=sQUX|}tUzSF?vxwR0 zpO$Z8AXv2mBbs|@;zLeA%JL9|4;m3ulZ~7Be%7gkuTJv(^7*C>D^@~%OOHq^H32Ml zsq9Bw0$csby_wILv>P~ecev9hKU@k@2-=HATB@q})Czp^P&91y*HS@n$RiPgD^neKpd)%3^vFkX=E_j&l>Y#T?!3LK8J;CSSDhL-n`&-I z6z{g!VrrRi+A~-4mFJjm;ngH@0>IYEnlvksoLsq8qU1bm}n0$4$6Cc z@Ubzlfb*5zzOk*{YMNvpRfa|dpa#4T5z3!WY--jw6t$@=UXY@OsAJ#X0lJAz|UwhtM~`8D9%G^BGHBJ5Il{ zm7d~HSV&6}D6RtWuT8ci!v^5r=Z8Z7buWzM!qv~SP8;6eF z9ySEL+f|7Dk;wiyYt{EKj=|zya?oec^=q3M<7SB^Q|h3tIRHn|;p3FVmSG2LOPOwD zAjiNdDhFjC5=UW^y^?o&x8}{?o?jzB`kLEs_tD3`d*DEj4@l zYbdPXkgdNzP%Sp)UccFcpW4}X&7Ap9%`sbDao23t>E?{aj3^lcHBCELb4)zI_%W0= zW;WhYwDOj-rg^EZ5%kdQG>XJk8aAj&2VPX|-w`eXz{rr9`Gd?Ff9DpVs44}tk~qrWL5#>V9R>UghNyN2f|)o-%2{+v|sIS?mT{YW_Q^jAn$LrKQ*WL-(4`rac2w` z{G*H~tHBDbK%guF0bc$0$xd7GEH)IIU1)H5i%+uEZsl87OKIg~;sG)zV5hSZ32y=2L~^nn<&onJ@l$h3b@$0n)koxkeO?o!UPWgmXLjHV zw>1EQ#cGxDJi6pY#5Nt#m;PwfZ?!?@Jx)_C@G6W+`%0pKAD9$4B52w~ZI*ezQ678N zwA-1G+rw~?uMmC}Ec9Tf=-Rz7Fx%m-T3p`PjZqY$kt3ao41weTH2Sum1Oes9$6wXIG~FUzjuM@HtW$nkUb zdDEpx>w@@=sM%l4cESw_Y}BlhTU-F#2ifMm2hb~DYy*|65WCdo)om@U<`t3_sF<3U zuh|59ZZVLs%9Umh6*&+>p*5z*dROQ;CETf|opE(-X+5>L{{Tjv6=b3Kxbm$Y@6x<9`olu<&YS0dB*kZ~GD`NTD#XzdSeh^jD-rg23WK)vt{lFhe)gfUe2=MU zFQZ6OQr@CDJ~W{CM}|T@-c6i$vPmQ}-H9)mfuIh;r)6pZ_Y+JtN}Xv@Kv1pI6=P7o znCQTG{P4)6?oly8^=XCW6vXFJGJG2*o1{{H97YO!f5(OpMYl0YaxeZJ}v zE0QCJZzK)Y+UN`V9&LK@gf81##z*(=O)8iK)#DN;IQahlw-8}mSU znLfR#i>ujvM{tij_YyzEN6>P(_oh6XzttqV(d_g^NY!KJ!aykIuTTm0si)5g76-X$ zD6od|{&myfQ!B_yEMWDZp{LQ3j_D_>+&qncKDw9wQl7BJ)-%THy-UXzz}x$;Uf6iK zwM%W@Go|@z_gI$YG{+OQoJMy_ZbFK;5#NsW$itPk6maFLy`6&6JH5kiKuAAe@$91#`>olBA^K7oMup*Q@s@`cx$ zwF`YPF9enO@?r3@b?eZzDggd$oFRPu>?UdHzn$9DI{aEziKVS=?IRZOhuO;@^yvQQ zwpBP>xRyIS3(NN=&(+f%fWB2J6A|z@3U(*N8dD~BNmMgSc+4dwZEGMEK!TBBL z%a1MU^4LVYW1ToKZlExs73_K+qX5`b9D}MQHM=h;dFuZFRgyO`J(bd1fw?a#{ju^j zBLq)B$5kT2F6YRieGb#ky08dmnnpIJoZr$FQ%=a_ZS%=pBZ-Y%0RKM#z*^h+pH7nV zQ{xdAQWyemX~dQ%$9{tc4Wx5GStgsHS&42*mg08{8n@dCN`NWRk59u5(PJqYS`+iL zQNPwTx4P68hDl=d>Chlh5`(os85o`!{8Ba#OaB0zgVN)LbQz>}p5W>6i6k_s41fXd zL8$w(c@45(WZXv_zho;n?$(F7+MV{vMqDa)6!ecY&!c&ITkkVz?z3KMB`$4h1w~nd zl0RgDLJl(9Z!*9ec!rVYDfRt#O0vJ6L2c`yn2pM+ov1qRO5u*>ol`0N(b6<)i;I0n zMxT|c%5BO6@u37V@~-~?8Df39cF2xsjF)~%-Rb%kzbBY=0ub3cn4x+C%1uw*N&+{o zPae^>IX3~9d0p@TuGp)YR@3UQV#-wg!Qa;h~uW{R_d=ob#FFr^DqVRod zNt?{jHnn9DhRf?u3?+>goA>NbhC*9!_~yuLmtXmkTm3TINmW$z5Tou#A_<{B^dw}7 z#60+IZv4jYWXdfW>CEm2 z#6@fE-z$V!N0MJXjF#7{V;S`$k8sJ*_v`eHu!{ke_p)CxTG`uaP_=~O;^uS>B8`kx z0FBSS)bR%ljWCSKJG~!5Bgmds9(lWAF0FnzTB9vYbfsiH=vUzxWQZFNdO4TRNVgi9 zY1@>XYU}|lKx5x34Y52W=pJ&mAE0!A2l`A&>A9#Zej-qP2!8wo(Q5&P$03m#ILifk z2CZsHHT%a;mQAsdTTBI&2ulH^zh7v?=$OSstHKorRQ?dkl4 zfkyTNrdJ`$zS)K-*0&mT2Tq+>%_joAcH%ly4epqbH+qMaug{S#to1Ej5ZTG;iDCod zXvtUP4`bnz2X+LKp84J(t;XJD&@NR)@Cov3JXaXcYRc!uv|(>&3o`GV>6Z#zK`=~aZrl7$6a zsH!PF0U&kpIC>TaByvl<>*cLZ+f%dhg{{b>ks`oJ0b>o~dy&xKm-AB|g-i1TTT34< zX}(3CCXVk-946(|ky5pSofKhJ6(QXEota| z7ILVq*O8$rK9ND;k%sJgCRScsp3>#5FW@mrB#4Sxwv2C9Aa(iSfwan1v)lasnqQSJ zYzCgH+Ncr8Ze2wrRvk#`Pm6DmIQ6C=+sM4lcXe}rD`^Y$dui0iBE{3wsjAR+{i^%q z0Hu!4D|;sT`$DB@a*u?HoUS+hF<4{X$u1%&z9Cq-rrpf@Q{{UK@ zkA+S(zUYqcEVj3?ZD}DA6|sAeDPM?0%tv0G2Gz*Jn;>X2JyXe1E%m_A9i_Fljc1vd zjp1#@DZel1aR-@ElfCF(SM#2W29I-Vc;??zK`J^mc)IcU03GN+IVlw@w=6&p`kRdL$=u%mda*c zSMtyJtLt(JKYwd(_Ys0B3nf2y?X^x4Hehy0^vGP@$!!`__kn9(lmycRciE9;nk|Rv zTd`pRs>p!^g_!j%P9*s6w}wtTmL*rr*Oz~%TIG=@7GYrwgJ$5TeY*GitC4}+uS$9c zm;AVz)5quEHb^6|R`qusQB(yB?1b%3)x%sO{me-xXNdXZU!Ppoze`;OTT^2yBpn09I!kxG(JkA3h{D@N#AYfTi- zdcPN-q5`Ii%$kApjm8nW-*YJZvV!bucJ~wh=ju=EG?b>?g+M(GK*k4c*yQAz`S$MD zMDud_)-%e{o9l>*ekU^SC{KQs@WYJo8%Y4-r~zzDtK;XfmXi&;DHiSMthS z@&0aGA?~NR@yasSIP-SVmhU3+fwRzIlILAYPFzJA9)yZBG4=w3=ZO|J__L9|NUt$@ z<5Ib~n~7DoH8Fq)*_btc;fPp<8?Ian`-IfuPu>zalhLTO9dW8;IX-}c^BNsNwC^p? z2)D3#8E=3;2NqtaJ8=Z@_umPKk=?I=^J4yO{Ij-}_UHUFAeJ!7c52i$IH*26e^y6? zo_*;OVjgO+OD#UnP13@P>vXNg+tpr*f99-DdLb)gdKKd*rwpTm%EE`C01;3yhEf*K zG+FI*U0hx2YgouWnkh7QdU8;Ee{LA;#PZqb{&1RK&8TP9u3cc&EFl)D4?`eS1i#Ew zlkH3r7LFnxJ!!T}V|uY`z{3PZsCzL((S3m%Vh;A%%{JC`sI`4oYt1?_3=@dr0-tM* zJ}tYFH^u{^IVMr*P^PCBG<&Lw?tcx&OBe*v??F2=Px;8*0X(;^L!{|e z>XuU5$0Tf&p$uvZd-clXH>Bg0hmq-;S(Y{q&HK7BXI;XtFNFJ4(=3L@(oaXw{{Zum?PIIzR$8r$%NpF-!|IW+B}Y&@k~)lzOEM*| zA0WYhE#T9&T?19}ls4-6t-K#z7eCAw;o=GC03xHm(T#BiQrx_T`%J#RxSHY<$VP!A zRW#ghUvLf@WDP0X+uiB5Gd!wN84PUHG~8|}^TjuBTl}=K8t0faeL55A+dQ%Q&W3>y zd_{@zAFSl@?pRFFVZL@(*L>kNip&219}N^`K;^}oYLDg~J{e2}{L3I{C$QzbR?|09p6*Ch}_MnE2gs(DjIv$~T-(k}gbbb*Aj77NM9_(OYjH1Avn z7Sv{%>3qDo<;_RS>ls+Ch<{p}_V3|C(+&>3;?>v6V;T|13}RL04VA) zmi&<(Obb>QF{Pcw(vLwRRb8o0TQIME_pU~p0Y})Rx{5?0_~Z*wPOD9-Y0`(LKJ_7= zeq7w$T z85qbm18l$Y&qJHcx}#bmt4d**kNBuY2`x`*S1gQnYm*uC^y2Sad8OmcD5S^VVMnLF zRLU*tx6{nY=Dj{8g`j2l<1_)*f~V(`5+Je_EuP!?OR0a3wQK8dG9t7$F-Yb^K#-m^ zVzuc}LG6-=;s)C1wOOe6Rp#szM5B1*9b2B zd|W{-R#W0=RZbw=36NO+yK8%_U;dHM=8#D!D9T=r(T|3rpGYA4V7{H{s%1WO^OSyB z^7ftOEh>MlO$EWbGPvB5HcAeQL%72^dWVx34bZxlw`X_d2#vFWsCw-z>&AeVuOGQ~ z#$;{oO*~^tlH&3iXJ#Q$M-Dp`AnZql4gtxPC%1`pc|3O$P!-;)e$ZyEY3<+Th>aUb z0_mT>=02qN@!#F?a?yxY6i}jrQ9wFS)SsEeo&D@gLni#w^9pFrx^zf|riE@*xY5+j zxTAHTp`h@lM1jk>gaSeNV|x|d)wYAID$JMFjfw7&_ z3tyYlHOT-dXtn@-<9}?Js|UF9Vj#@?^`+TsR#IR2UgaaP2+J}t;5ml)LXX<5e(V`L z`|g^?&kwcorm3J!`dK06^+_s7H3CH;ii&$;0f}xi9Fng;YR|3fQfl)?sU^V@GAQB$ zH&IUk+atri$`DrK86l1_yzb(qx)IZCQ^>LnKTi7Iu$n^5tdy^)zho~~r|QUrR@Sr4 zesR?N)jhqou^@HRBNotrD55yc05tjSld*QnAOv~yCEc`EmeN11090`cl2ip^NH~M) zVZiz`^DD~j=PxVS{$uG2`_Dtuo+NFM2BMxLps!4pO^)m#w$GbvG<%fLZZED{iLiwV zbtU(q>}%KCAbB$+0+D%tOTE-?{O2A_Q#VeZqrY>)UW1&SUu4!?0N!! zYxSIf-{hiH$uvzgp`TaO6+>GEhtgrW=9J}5=7%L)ZpLgCjb-$0nY{%imzO|EsULPA zG6I>LqfCcSGpoqj*l&dsaHFUv#)hAM6S3K3O{V^C^DWnz^qm(%TKal&I!J|y0hohc zUBx;M*(uD(f_i=I1RkfT`J+tpr<2zDK6($h})d zn$ybuNz^sg3T|%4)gm2ObE!~AU_CY`#-lCBYKMO$4^gMjR?)Q56DRkK)jst&&@xlG#2vCr);NFm6OxCY68Jr0nGmZ zk{fT9PVSnx2uwWTr&-uZYh|Gz{U@l9uMuCi0V~G71nv)TF_d_Z2x>m=oTC{DJQ`IX8vg*SYEDZOZ12ploNXff z-?7xKHQTu!ZgF!&Qlp4t#8JYHfHglCe5;quF-kOlHOZyuH~IzKamx+Lhd_3cIjcow zJ+>yJzF95yWLRyEp>w8RjYigHOO;~6MKm-g+vm1IJ12*|>Rwgzo!z{XYI>A!Iz))h zUw?!(O%HFDS#%gm4_@@{8(EY(XC)z8t+xP zTAN`XrT)2XhtezeVVt-_E=H6a)zmB}oO)q+SApw5KGI>~-pWX#nCTil)~#UIHt0%4 z71SkWv~`{QeMSR--h^D?NlHVK2_f(AdVnT%&IGK=DF;2>nH?o8Y-4vgodCe!lIk| zV^_a&K9*Pck@am#(hFTQQfqrSt?p~QjlkBwD%PhN6Lzr!tIBS*Y47Z!(dPsG8<(W3 z&;}!x{k(Eg-n^NC)n(00P}wR1h}9$=i0Z5TaS^hkW=nIV9SX+RQ;@6ADKtPg;^KfX z@iZqRTa)Av_irg`wz_ZS@0cgJm?Ov|WdQt3%n`s3eYs_sg+LFt-p8xw{a^C#_2IPD z^w5$;eHS};{{U!?;E(19Bfvoy)sU5QL2BMnlEz=r_D}16>^nP-KOcpA0a7V|fMs;f zGaVUMo33@@B}m26=IHSQs$#?59ap68>(X?vnbZ@=JJ1;C%Axe{5)6exsL%j#F4>M2TB0Tco6G|!8FgVsFr=A-2Q0555NUGnm*cgEf+F62(B zMcsLJH9rT!q_C3MKUiLb%9G^Jr z-`jxCJWdlL0M^0Q+@f?(K3V+Exea;z5EetS%Tt<@ zwQI<1JJ+T**kGJ_vLk(`Swp8X%RF{u&b+B1so4nbN4G(domeBeh~~jOt9^gvyU6wZ zF*#pbh81<>7nmw)K1XBQ2zboaI8B+>^I`#idX_wS5FsEr0;=6+fP!W}yN zT@nG8;n=Y|f%5(s-3s$#Pfh%^^IZN;miJH8tvx2xEsCQMLbH$^Ht}vlh3o-w~{2WNQx;`VMZSQ*-$+h#92=5ujLv%dK8eX_Um)%NkdaYC}?_u zXcHe)a9wH4a4TrRTVEIzlM*P09zt4$+yb3YVr@P6e8VJhGW;SY)dO-$zyi~oHxl6dM;ao z4uqQQJ@Adl_>=>`!Rfk6U20mEnPcXqY2aq`(RQs$nky67><%#Bb|;eu^N*Wa3#ZZa zibtmNc1RcOhUic6$jU;fGp(3qq!f@f{{ZgorjdCS6=hIpNcW+x5J@|&cQf5bO3~NM zZ>CvH6H2Qyy{JCiKib;EWJh4jVo3JD!5As13ra2F! z4<@*aG+!QS^ISZpcw;fMv7kLi9<};vax~mFRnYY5b!|c$C;^i0Srh`t`P=OuZvOyA zNCtdWyWTv@sM~&1=vQ7=((V@LSiMARnPdA3gfJ`kgHIex`VL!P#54hB-!^FWa_V!U zJ90JMpP;D~E$!oi5vKBZUtD>ILDPROG=_qUaXs`3u*~3mEx}CezepTE$>oyBXjjFj zi-^3(PV(KByP%&nf)Z%U{{W+@_?=4yA2JP3y-qTTjTDJe56&K7n&!^S%lA>5+8}}i zBcUdMe76{w@f3m8H_#%}{EMVtX+C3xpw(|w`X-er7z6ih;7RL^?MiI!^Y1WuQuan$ zyEa)Zxro`+lB&dVH1?-|Y=FK>B*yg{S+uh7$n}VV71!+7sQOMysP4!VZi(gv(q@t( zqGY&11cmwqVk*PH?Sr4#%ZAgawCh=88kaKYhOxomPT?!=cQ}ARwz6p#=T9)h=4+cf z9X1D!8*#yqn$w5?@%F7q!SxWB#+mPybcFoE)--q~w-H-dL?Dse&=DTx*n1PcTn`kC zx)uj*ldsKvYe&BGRptKxm;C@OR7F}-)CdiH~#2!?*W=T(4P*5LW22}=8*%Ru|qm|Ut znlqsIf!d^0ACab57!hb`V~|`fvo!&g(AKmy0=+${kvyx-vZzx#^H-bhb;+%xWKlE_ zqNIvHcz9HvUpA&po0AyFWXt^@&h}C-m>fr|qdOb6*gsVW-WyZ{>@Xiw5Nlz{;*(gu zmwGL(YPB>RhTtBbuK;YlnXZF%Wo4;b+-eG5-YGpvs6NvQ5U0degXMsCF{I|(&3cSC zccRcN7B?fDmhZaPk)s9d2Q9FPZA-&c_a7|zcGB0&qs{iq&sdJ;rQdH(QaUn^9>c?Y zl>Gn^&X3;K4@b{BmYF>MVUqIlMNg{BAd$z~X8S;o9sA?~pi4eVdcT?PF0XCjwvn1! zbc}O79DSfqy-pkIwQ0|tJkKt&@HV7)-6APzl}&2fly2Psr7+K8lNu?f`HtA>y7bXP35YY5rpMB$EAl5H1G773K3ud- zXP%dEzJ>k4Q)zI15R9ObE8=zo@59q?Kd}X^JI+nz*)MdxLdhn)f87V@6fHvnJDs*U zBP-?PfGoW`^7~GIGU#4l)3uL=djQ&u0sh(+pew&(8+;Nzo9YN*RCavj3=3nbi6>Jf zwnVm`3c|$vl^*=(@$rN9JlnC=&2vvXZMv>*LClZ)%)lTqLt3+b{BTuzgEW^2L)X zPcx$huf$9zn{jXDBBS1zn=#wo!dGFAd1J~Knw^%t=4)95Nv2C56eDBE(2e^oNBN8! z0rO`gMv|W?>9W1^*hJt0RfDnWR1;D3^}sC#L6z!O658q(x^zWk7p`M_nADYasUm~r zvG4~FHz?_8{{ZIv(CAtePp&(R^Ukr@9~-u)YNQzK3L50ACh8Tf3A5VDD-VB zTUl9eXNbo$dlnD#<0gE*N`7J)2Rx$-E zn(F@m%(pLjb_DZtGqpy*1)=Ns`{IxSA5XVyxAc-WE-OS$&m;C}_tyh1C(yUBzm^P$|D;A9FZttxAKv{&m|+j2x*V2cZb^C&uc4K=*#K6%5CU6_2kMAhi|6< z-LV;NWVg4v(=`rBI8RYUX~mS(Y&-`~V}P0ns(ld?>2Ks4YpY5&sRuNkU`0eu%t9J_ zAIBw_&>rTivibH4y*JJA8(m4-+*_-NS-vIZ!=d)5G&q*gab@09xU}+ZrNyPIM*5WD z)7K5j7ToN5`}C$p`XW|&u+KXAcH>OAw!eSwCPj6SHA9E1tB-0LdtnYqiGy+MK?nJktBoTRiUm^$XmGD>5WUL-1*r~v0)I2A&n!uB!Hqo zekMQb)L@XU!eg;x9%Z;uEILeV9xGrYSq8+F6sNsUe2+AMsiyi+7LMcw;5P?#_aAml zDMwyfM{6h6ExEnDK@8nV=zpd}s4so|hDdIPv3YO|k7;W6ah{<{0u4urCv2Y=r2E?B zEYs)sJl4}|hT~8it)wou058PLF6>8#h`|aQ9_GUy=b&p|OVjmBWb@XMBSoe?*?=Q# z$b+RvdiAafBD~mxTQP4p$8#<07Z5{Op$f{XDaty0CvP64;I6EL)BgZ1G?cl#x0?Bj zTN%qU5_u9x9egN2%Q-NX?>+qExQkNMA5YRyn|l^yj)P)=l1I>M^BG?~*#SvcmwcNy znskfJPfVCC?U{~5wx`-h+mJrTug@f6dL)`q&@8U*?qQD9G?T{7$*Kqfv_C+pt`4?| z5O#a-=UvUFxg~{+4zG7_;N@Yk~`1^2b+Fdy`PgVFE2de zrLE?-c+tqEHUnULfOa@JUz**byuGWzro*XOX|hJzeeVg(WbSwZRa?hz#|YXqQC3)m z&zlFBm`8CF%JFfDC^lG-oanx!{wh(>lUpN80Hi| zykWvEa0&#rC3KTDv?| zmNX6adTqCf9zHlNnF5}@=C3rL@MXW%=(8-WyNZ!rn2PqLHu1wdwz!ShrJ>WJ{cI$B7n}+@z{!ERyWxu z`ZD`ZB3tO!_cwY9o82I^mhnkb%bAQ2DgMf%@4#c;$B(@Q`5|s}zdOSYtr=&s4>Vk< zv4u+|M@`KteX>~3Gvu@6iSxu3{(kd)@J3d0zPkXx@igYae10vmIOAf|bP>Oo8rpd# z3+SUOCaH8Lfmi`mT7tlMgZ3DE;Rn-9Zr&qtc<^h}$*e?V9Ql~(Kr2$C<>! z>XCkm)GX%;A-VWsW)%uP(+o#B$G4g~eBuIv&*POdM1xCL^oaKPXvsSnqUQR3aoO*D^>?WoA43 zZ`ZcNB@hgFN#(l?o|9pX*;w1^FRI&VPHq()B#KrZ)G9|}I-eYk?P<4^-!`RTBh7fs zwz4^M%eW2U;ZcMdO)F~}`d(gJUfRM6#TZ6%e%~NSBz7XbeTF`3Nu}4EZ>uG?m<%>| zYNFy0J_91B62N@Fj!T){JR9GVY5CKmn+q)p@xrW<+ATs7SgL@aw}Vs__X4>Y>{#RE z!0&IT)->x>o{RNPN{4#O%#Dx84*ju_t=Ysqqw;^vk-@1SEbFYzrC!7$di72OQ25Z) zkEbP%lTJXCd9v!;%6iSh>1-Mh&k|H_u1N#}I{*Rs0$Z=U!#Ih4j!%~#3MSosFx`eh?ohU<%>nu8RJR5j_^w97MA-xe2T<9ZGJc8209 zg&m6>#=?q4e)EtmrIjS*qmJ1~jnpt^I|f>R9F&V1z14J^A$@4aO+zipa*+HqAwZ{n zslNCvcT)3?vvKF0Ni|7|i9||;7ocdAC?6_dj_YEXhJ&W0P)V%Z{6@%gMN$NZ;VcH< zvr)P2glxm=*eh!KM4Gpju0J#`+v@K3Z*K&2r~wkB50iepG8MmjD9d+>X>c2*cX=5f ztw&=~N*^8Y2FpyWAvX8zE$=3YmCrL;x58_`z5-B7wL7a@i@#S-{n`{j%h(X2w6AUO z4r&6N7V$GAlWh?-CbcWy?lj4hMKT*&A?%A>wY$gy&`|X1zU>Y9}3~fY=Ax<_P7H@E%FYds4bSIb*D1RX$6U%_8C^I z#QyBC@Ng;blP05Nti_ZLr{v2|Gx;`eJ87F-Y16XYH$YNGCM~{o6z~`%IAg`o?66e7az|8?rkh?OCF5>+uCb9;>whp%_GwEymAn@^9u1-;;DpxE)2t zpK}u*MceI=z;EH!gYUyBf;VeC+VTbEubXv8@@>3+m7Vjy)vDF>Bxb6r*RO%`p~(pG zjk;SI#*MN+&_l>xXuG`g0-ved+TB{oz;Ej^aZr7ZlSo+NHUF1LC0jHcT4rnNPZU%uY^djnC74FxY?^Zx*uExh-0Wp>u# zW&$xhl=z9+K%nbStufzyLnrCm*c)nu+Moe>|?NJjti&o@cTg+T@g;LMm0G>c1gc zV9C#6&5Q*CA3f?(^Rwq}%C?ABJ{9B1wq~!7h(AU|vH^+S*~@J(wYUt76&Rhz!u=23 zz9cgnqu_X;2?HQydXRb=jg;X9X;$9aL#Uu;q>53hYd}H$Uf9MwhjR3K-{xkWCWUvT z+s33_M{JJFtH!*P9$w~z3}k4~U=4?rO|M;E+zXvfX1Iy?g)h+hW`I+$2LX+t{9QPf+(yMpSx-tM#j*P%WR)rm+Cvk8p~4miW<;%0DaZO)OO~8Z7A}#uRe{b4SPxkG3u*F9cnSRYp)RF3z zzIo8Eb<4YG*)+IROAtQ^GQD|t*N`B8t%EFRTLH0r!49*gMWn}k0XOd~s_F+-+ll`G zWq|jxqRc$03rd=*=J{doCaUv|pH>)K| zk>T%=m_X*4o56Rd4H_7Ue^#p?6ky7fBk8Y9c(|45H#6Gl9#%SPF}2r0B1q{@N_i2n z{CsLMQ=fO^k_1YJn!LF+rh}#VgIJl`_Tgg!7p;9Y*$D@wMmaquls(hQ-fW9R&@6nP z9A;aq=p?8kbq+zLI+dks@&hN9$pME68@#Vx^0Yoy^AuKEoh~fxCH1``ei>E1AR3*C zAFC^XqDOLhu?Ftg8BZ`oAm-k*@o@>o6J|fNHX9MCq zMMXYU{dq1yd4xu`ujVf?3oRrtSD}&OJ8>(`zUq>GSjHwtZuUl_yEgpU^5wpV=6G~H z5fVp;TiPqc-wdqEC<*D`uY4a=jUqCj^T_lIy-!&-Gyd)cb@ge{06W*{$qkDlc4ykA zfRgLh&}ve1wT|L3#y{Fhh9G?DOo(G6QBHLo7Ry@GbqmcL`Z`sjxI#%houuRym%l&- zahMz+@6jO9q^1usLv5woLosIk9Q>n@jmZ=RSLP@Oh~Ezxk>c`SsVBGdf_Z~gmj3`y zwlUf?az`s7)l!k3CY=G~0j>z`WTeMtNu~K(9aqY7`NvVAzPGvbh>&j3xFR9GgF*z({J8Wbv_&KlK@2Dy#{NpWYJEmEU@yG6_OaD0BvB8c# zd&&v4`^}zZx1Y=fby%WTfVEI4D<3M6^F2P3gKIwY2$K-O*UX2^8mFZ5NU)-hNtbcO zHw!`7cx0e=_}eZD7R^61>~Hi$7ciQ^sI)gEB}nAUuwpt9UA}n|L`e6n4X|B1T!!1q z*SdB5BkNImV$_lG^7Rj?^8{~(V#3z!t3~rQoKcggLnIn?^`ghfkB8L{UTSo=Qq`M<{B4YU?4 zv7p#100MG8!@q2ivSi1W!AHt^Hk&@Db*X7!Tup5gqQXe48XhEeuEvGa>*Y} zF(BQrzbWZ3>bjk*T8T13jWxy7MtKkyZr=U9@WYyu=E~I;-P%Q4n?xkI0$#*TOI~`ie^mA9(v~t#*epKJ@$nfk6cDi`|v|#U z6XDq7ZKT^UcZ@7|Fo?vev8@5@EK?mq+S=mrnp69+z!E=do*(NRM#Ui0G5n>| zueFae+r?-=TS?MD%)2ia45g3Gl^)qVTd>M@r@DF9Td>l+?tJfkNeT)ju;1e$kkoeg zz9H;#Aki!+)5rd_-eJ_WZC6fphSnI2l#EaqZCMie+&-DUT9= z`A_8CH_W=H>2!re*0$#1E!cvuGXueX{{V@GN->~TMBO}x%vyAox5G)fGA5#DV9Qz* znTg)M6+JvM5f(?eovB~nOQQ>j?dp@vzKzP0RpcLw6g}!d{BX--OAv~K$`?Q3&+)V} zsD&*eH?%A+7lDDqD< zR*UNvjeWA*O&E3plu-0Le6n%f5)VZD$nwRVhN#*fmrGn-pp~Q`{6T{TJaRWk z4DMY*KR13`yq;jI>ehb{2kkUVL(AqlQHN}^F%~2|kpe`YH$@3+SuN~1%@C8XR$}1o zPPH8lLa%0I+a>b-#m|;+EZ@xt?O|hbj?pQor(eE@rXcsNSVw+kYu2MzT~9)gG&Aui zR-%PI8q=sEls@?qQ5e(6^m_)>u97)r3SLmE&UR47q>^$%EaYjEPo;=8sQkAq>&K9Z zq#KIXfrqzDVs}NFuz~Ythj2y8ZVjdbi0T`M3-(FI*sDkg{bf2^yN@CSpj>p ztuEfrPqw?(bb_mSCBmh%&p`2^j;+LZ2BwGGCnHD)cawR;LBG@8bq#f-GEMO(D$uFu zN6dX04)giigeGB4| zys8Jnvd2oFr(^izj(jq|HHg_Te?8mzi%~kJwE-kX3vd+%YEioOV19#u%C}w;Hc!7S zFLdkg(|T?5fi=yYEY?=z8Xdz@y;uqiHE})cOiiV}U(_`F9Xk6=^D1)z)IoK}kwpN3 zLEfWm5||)b(YKY|t)sf}0l#<&WCRLQl&^E&AtTS;8vr5b{!jB&hLY+gwh}#~8PE|> zz-((@4!ii}p^TzPM0sLceS6GM4=O`hrV>^;0PDo^p*vIO-xzFBFhEZ~GPSFpF5mu$ z&~5_h^V_7a96$z1D1AU%uIGAXe2sMIn2R4b&z9m%QtcXUf}ntU`{D%)WDN#Eq}alM z-Ceit$ZjiNiJQO63FYupBus)RV7F*mrY0qpl>o8$NUp>#_$ufi9p1_1yG=SduC-VY zI$P-zA5Dcpl>7yUzi>Nb$h$qvO}sPLT`N)-N>*2m%E*LlM}59WAsKATn54m=zSpLb zJ2Ycw$j*^hWdeegJ(j0zA~v?je2=9jv9DO^bNq37ypJw}hua@;-`gdOUo=SUzt7JW zjpkx)2Pk=(#zx4jYN$MTgG&2la3=Rr$zq_28D(I`HbrVN>%RX0=l5ll#^^mi^9RW> zdFIOB&VEuL+*`pKPiVW8DwT4_wMecq8OPW*!qN^9ny&3jEQ-VHy>JH*giyu+%s z!Y?B4)8bYnV4SYl#sr<1_mMuY=INftP>8+y!!M?vvoVp0V0;RLRPgY_00^|qQytel zp#E6bXScnPC6Wdt&{1FEsQd`Xj&z^Y7{$-6dn}K786beFx`DW-73+r61K#&v%ZsZ` z68>FI)D1%3J2|*2jw~JGV5r@|>R9CLN+pEBCY~j&Gwa4*F*6TdH&r9MZCVc00)s0I z&duEGSAJ!-wzrRr31XbXC&J8BQnm5Pbp;Xhl%jYVIU>~_Z&v1KV#>y?RU>x!oQ=_E z)#z*HT~;Y?wGk@Yz16gET#8V#h2v4}w)rEtBfW=n)BgZ2{Iy}`Zz^iqUGk&ez?0nD zPM>YWLviCyR3g4OW(N+;c(;7Zb!>FYnJw;ikJhS{?kU@~H^>jx*|~ckl(k4a!KqsR z0ElBnwzq<@Fce=>Aywt?QNA1Mz{Z$m^z-?HT9Wd`SX;n%pxTu+Y7Ug&7=IBpZm8i0}1{bDs)6PDYu@*Ib_>OJx! zV@V*Fpy;~hv*z7iwieOQA(rCWHKca|svO7$-D$`9aEj{0#=PD=sN6oAt~{2G>hYE| z2YvdVVbdWJP1_&^J!A6wQ-{lUdWGki+1BO@N0!|ssoq9faj$yqQIjHpVt1#U+WCd; zr#DWb<_QC@qadvq(v9O@Rj0PtB53@XN;{PES@Q+W>n5k^7~0`#004zvOr(zkN`2UC zbO^83^d!?DwuUG5W4VP|RvU$pcc%N$j-w()AZ(Uu7gE?-Tcylv9OKoFlpMq^XgLw$ zNv29f6^I9tmzQ;D^m*4)F%@Ky1U25f?_Z;*hBr*4FK4Z3G0Ui24>xPX!=%9C-Zwip z2UGC?cQxV#dtlga-ovBCCA@%ZjjKeAu)@mg3}>efls-EOoH(Uhp7v(Qr>Xv8rG})7 z=WQgbda;9YJXnPc039fP{<#7;ckg2oM#bm<0GF+Ecd88`mRTT~gUfD7p+)Oo9k&@_ zjk7$MibWh+g!Ztp5ShRVM|#ko?8?BTv&~b>_O^aewAZcMo}o3}%o2cU#3%tl{{UE$ zY2KJl$Kb~8lH6(u=AR)hi0qB1THGm(i1@uAfbH~xDes12=#J!aXCL#A`mLqZcbZ1H z!K5CV*+lz=eq5RN?LqkBK9pqGux&F-lJCxEPl`hg(p=5OzysiAHLr%$pgn7dAcT`o zHPm#`ajs0v=hOAQK9nro2_7JBZ~%~KGc9{jI!qDV+5sKR5|>HSa_<_@5#TFdryHg= z{{a8d`I&p?JwAB!yL%`IiqF$okad&c{l5|p-z(6#Ij}wi~tN`U}cx-gzwR9X9JuRXuutp7>9w0D3W`{{TtzD(Sjq#+5kw#FrMvC|%l* z4;QY@;6~Xw8y_SX?`*sBXHk$}+()gwETe{L%OTxLjezaehy9KqZDowbC7+gemO9s% zZ=mxHKSRB>!#nj6l@$_i-oMb~VtpplK9qsg+HWjHHU9vYnWeD0Vd%>hD#)9HM`OrP z_=ArTs#!4q04;gGYk9t_9AX1Ig?0yxBXR=@_){C)23{H$&eO+x<~i-O`QA$=lmsO8 z9f_y0`Y=_RO;EOL=gnR#Z8Fo#my9kaNR~x*>I8rmTKFjjp2rN1%xfAL6F%!L>Hxo^)$u+~vUBkFFtMWV7#Dj?B%ITcwce3e_ z&1+y1MwlY0ZOGrK@Sxx4gM@}RZ1c?*iF4&`KF3zN3#qh@Wp0jPLOAm8$dmVE<+UF1 zY$6mq%XZqgl&r2a^jKcPA4+FdrAv|(hsaP7^y4#T(-T`fi^#f-&YgZ|gs3WvtQ_ph zH)TKT3h$M`vpks&m!u}S=9RRuR+7=&D@KjgH#GkMf&E+L@>3{rm6?nH2VkK0oMX!DoC|;mdf7KMv}bU5<+XTa<9OB`d~YnKpj*606kmXXm*y~ zThtei60@R{T9(~SdxJ_G2bl{L!$h)^Sn~F%Wo(4W6e#A0;Z%*oRF37+ zBshaD^2e8(UpnWR>}KI{VF;Od8WR&S1hMhxM{n7|!|AZbLy&kDiL6bdYIj!*00lt$ zzeCE=O94F%91&66FxcRUu-h%uzb4q1zLx6hRsG#%N0OyM%tKi-SnK{KM4m z^$7SuEO5N!6GFl<(x<6CGBJtfgDPSgidK#0Y2Spjo!#5=#by0iR5`TtuV0Gu4HH? za%xN@02k%RlfQ;J4S;+0Nawn1`3}ize>B>wksYiLER0B_JAx`})QbKXc>)nI@qJ%i zePdFdD~2s;8EC)MP!);}O+)C)#{VW{CQEXk-z}p9%YJrmMHht{T|ak^#57{!iZ9YSvd8f0;yU zVGM6CtNijt+jZKMJAA2@8sP=5T;3C`c}muOep^d9P>YHEILdCdbMTL&E8@Mnvn5~5 zUQ@l%rZGCQHKOq~sVvI1H1@+*n8}Cf+NH&WtLD?nN1tz{>XI5OK_4?T|NYf z*PE|56f{3!kL@3RmCHe(ie+~fF)pJF2*g7)NRytb%}^LqxTZvXL?f3cv3_oSTSU;I z@;08)n@Lhji*)ZGk;thXjYm%Z07e5FHQX4+CGw4xl(ud(*~v-41-Wbhpu{#kV^NaG z`9n*(f4%PgX9na;xmz%EJmZ2u0JaKL|jS)ow@W&>`KITz2UKePtiNc1e#Oyke(39YD zBVyTUo{ez~32|`YM2e)S0;ioQ2jlo*D@{lxNRlZ~hA0Ovl^uT^EQZfdzSEmT@=SL+ zg3^6X;bpfbl`zH%pQDi;_$SMSsymhgpix13+hH$H6P>EGC_?IeTYLXp#%KgvJ=k>43cfj(`0Y4XmK7O$nxJUUZ6Yc)_B z(PRMbM%#*?qbmbu%LVGw>$W#KEu{Lwthbhzq35Wf5t;|s^;6VSp(iFbqvXW-JnGxa z^7*&SNuv2?Ppr#u3c^Okj{H>oymBLT2ZSYB){j4z?j*IlqR$JoZkzTXDIY$yzzwnz z;(N#C)xM|aKQUTrdezS}q}h6?=ctTOar=%=G5Vxh=gOWhVQ+D9<_n8!TXb8QrTAlh zftd9AcgV$+?4IZeHJ+wgV0l`|vK6Ixh|d22iKTGEzs)Ci(m$5=^8RURa!IL5Q$(;= zyMYe%c+d_jUutdPzkHV=#n*ajBA+&DK3_Wj0Gcm($RxIj+F6PUm4q7nBe^1*c<+_s z-=Dm;9F!@D>eg2F8obuFK(&~9AuKA&M^|&a>NZq8cTUtVKP`lJ5X@lJByfEID#9-O$3ajD%M9ygt{$&o z^LC2_UzWC7y8#8=?b|$z4M0y;pddTf$J+)&R04xtzdXC~7*D?3bFleOlJsr>iw8Q*KHRxiqFq z1CjTvrAepeSDly7TD`P3p<{{^hT1t~G$iakFK?DZi-_7{HhE5=rnZ-MG`RqBkLWNR z+pR~^a7NC_3EUUlH0dmFq+=XRvaCuz)Qj++l@|%!Qg@0?J<{^Vf)=M8>O*m6j%;TdNG)ZP&idT5Kf<%fW#hLL z40#ZJfUX>3#r`>@NQDh^RJhk>TRB_QwYOBagjD=Kokd4_ZgK;C*3<0)Cc4!vv?C<3 zK*}SOG1zn5ZSw3oNEWHp3ys@?>b1TlrV!nS8N-$f7;G#y?$TAS)=Y#=o<_OqMxb%w-GY_20|QIe#-| z)e}$%w*}rvRNfV;Iydbgz?Ki_ElbAZt?HTiZ|dO9M zp2{YTOfD($%2-!#FrOC{{{Uh>{IVnXlPHv%&z^8?N6d4}ppiTh zOw=qZRAE8%dd@4UOL zYRjzK{c6S%(Yaa>3z`S_*d4nzB2+g)Hpe`l=9nMM(L<(1Naed|!EK8n1wlUMyY?7k zx!YvU8!>5o)p2XD33V1X5!*{g1NNFTl|OJ2NN z!o6{buv1CE)SFwm)8x_5)#ZqjGdZG<*$-a<`tnu2$gUP&`IF{YJf)`D`7=UQ7cUT& z{EFtxqNi}AIF^epFK-Kgn(_zzf_6>C8x2mV?&}=F91Avjqct}dLPc|-! z`lpxD-Z5!*#dkZaNA`F8Dc>Sa$>DkjkZy;aB~2Re`avr%0BEO*ZOC{7+ruRh#4ovo zj{;w3 zGyp3DLEs1mqu*>e<+>#uo~QZw=CNU^!=?Fk;$)K93E%?)CQ#gYf1h7$gvCsYR9Ek)AL6vOstLbOdu8roP&Tb`XRI_f}mXs$6BHL{frCF)Cg>9zbWGoiD4n*|% zbjf2V;-|WKYt1)v>T!8rQbB!hB>w=ujrZro1)?6+9~a@0g-FqABDY9CHGGCN4SeZ3 zb8I3mK_ZccHR65Nrz(Nx@BJ*ewnO><0L(f)-=U)E?t<3BCWJJ5tCnKsKq=9P10|G$ z$uL|004rKsEgml{>1sZr*6+)X%#Kk}iSZk4jzec;Ml5qe*QUL^X4hmfML6Qq0a8@Z zQ2S70c9R~eoTUU@T8h4hQAO(aU;*P%z=oA$054iaesK!gRRnSF5c-9yZk_@!k%nDr;;iSK4) z0A{*YwD+D|)NC)KNtvZr57CsME4l8z-z*Y7pNj%FPyEs4T|Y}4- zoRK@hU++K#f}b7K51GOX6C=n3BKi7HD{9vR$#DR;59;r~#40EcalcGslZRy0{Yl1w zc=B0l+UJ_7>PHb+QR~c1QlNqI@AP4mXfV1UJon1-YTD1&?7X@Aq`6O4Sb_U$olSk} zaRAY6mwH2`O(u^!iNH~oq;gk)OOH=pI3ixG{6)ITz-r4zG=9_;h z==z3_s3En~WVMpwHf_B^6abIoO~^htZ;Rmo zb^ic5CLM7fcm$VuE`t?)C0YJ=td;aS)qKV=atj7TaPsY(p<-F zCe6J>qk*k@^{5!Y_o9JAzU(#1vT1DxYvLfp&s8SVxqEc6x+w zbj}tDOp+C*=|-W(9;+#l+3z-&ns&2dG;Lt|(%oEBk00KV9CZx8>pg$WWTi*#Vn~>iWiHGL~oh9YeK6f?$0CLOgH!Zz6 zK_SBUijlrUhl6BBSF%}WX?69@H78nk65p}_79DUutSVnu?@e>!~CXRd2a z<>Wxt_C??f4(xiM6xao!84hOB1w7MJ^55tfZR3tbpX(HFgzmh6;wj>JZ-!f>8)J9Y zb67~$_shmuz8)uP8UiX0d?aoY67_quj$g~-3Me+$&=DdnPSLYbzm!yup2H+%xISm@ zWBf!g4={aeS`qoO z9EkooJYvr5iJ|dMv`8$pFErj;Yh?o226P)0c@%sqeUC$thQg6O!x(}&bqz`9;ld(< ziU-07T5XY}w!gf1?X757ihMml3TyY_^IClg<;@55Pm}Nb%c&6{lG}~3O~@>F`g}%C zVxzZvST3TiJsj5Y$g)e(^Fj&uH$O@l&R2+mwnrtS8y;D;1UGJ9Qxma$AyoH{q z0R*>o745m-zD)9BWqTyPP;1MX5XV^V;Zh4Ir@+h!srMD%1QBT;o@BdwsFP4p&KV0C z&iuEkf_y2F48XQ=<^KRLUU{0A6SCS{#|lj(l5{GKLC4?XulpR4J1{#vKl0Penpc&z zNu;*96G11aBZIcG@V`M|OE$S5j(2Fao^2{4zYgOn{`bPt&88Bu%}3C!W=*6`QuUl$yC|RSB00{&Vdi@m1MDU-39Gi-IXZ+-bpKlJV+OC@`m6>5;oj4VY z0>AMR2fp~^6+1>Vw(kV`<;BEr&B-nl9rrcg@uoy~vm{gN?<8qfulZf5d9Ou?CbOko zdQRW9Bu)XPJAqncrfMLLtt6f&&UT(&)U^n%FEs0L);8Ag38Yq{b2JpGrzdUpUY&4q z8>CLnBRsbf`Av?kt;a}2PF5Kg?Xe$ck6!(JaMypqoV{Blcko-Pi?}1+PymgN;MD%1 zN*^4EHL>WUWlQ_1bw5k%oMZG>IKqnknr~C|Mjhuq+;kPVA(%h&SxZqj=*ZW`lKjh5xyQ<5p<2l*tP{{Tik zDK~20lKMr2dbP;&X=p@roe}n0FT_QDL{lt;Y!#HLPT~>yl`Z_y2$E|wR*8GbPlO<+ zU4?#(uZM0WGNPP$Bg{75LV4caQu8znOH#E~9As1euTkzWz+I`_u^IHMGjaN6QUSQ& z=ZOV}E}I{5zDJUtRF(O|R84zRir(f>tU`%r+^Pi}PtT=kgq|cu&>y2M^vI=>X_i++ z=NOD`c$(Dw@d246mO-oBzNc=N2!SjuO1p*qlOTWV!144hUg^Fw3R&2nhF!&t{IMtU?MFvscvtr zG{`u~5M?tk`!Ta_c@wtaVh-T^6Df0K{{WgEc865C{R^UkntB9|C9m04LbL#T@*}v! zqA{^=BDd4ckuqH?0xQACMjbyKi0*6!l-UU*nIMWzR?-n9ksW|lAkv;au!&LKi8smY z*E(Icq2@b1Yse%lD{hmjN7ZVDWDV>HBjrpyBwGtFoi1%IC)VYL2&*t+!aju1a-}>+ zY=i|C*p$av)+N+EJvAsHgob#Az%y(}9zG`}kAJm@6v}jIjl5G`ENwKv>;T({QO3U2 z#wTVH=+A#C?Ctd3I`7Vx?5%Q>rK~Dw5erq^zk+}$GFNc{GoJqd1}`+aubJQJCdeL6Ub(~qez_yg29$s%jK#pg+U->h2QYEDFU?4f|z23iIncmv0`lY+0 zNC4a;4k}3Zrso89-{1D68rg{G&bLI#AqtU`$-Gyv1K%WAl&nA2e4BC9+{YpEZT(CIHI8sa`yoQIE(2fQwVjD7vysTWO$C z%5<4ZOo!lf=}LY+*h*u~^>3G>hs-~e8kL>T!9AtwwYcT>)PAjGK48>43mEIr`RCV$EnH2f+iSb^Fih< zVr!;iVdlgFX+=_Kzevj93Fpuz>6`gxUs5&lle@GR&`Cr)dUA)L-{d};*9btyC(qRVs}jHmfnb{^Cq*WV)z_GJ@j-8WS_EUhdI$vP>aQYuespW~8{PXI4e z{Im09T2yk`>ot2p4Z}J$3YL<*4G-;Bls&h@dr)9T>CFB{wew||^G~h9HRtwCYamLX zr@cTJka_!3$;^ID6(ZrODY!4AmH1-?Q2RlA%4T2NM@?YOeHkxY! zf~_-bIL4>q1b3|f#;uVcF}+LC(RDPRPkw40+>$;NtN#FZPULj10C!Mrlxf-x$Cz*C zZ%@&-w`6;fK}h{sH)H<*1^Tc?D7~J?UkdB zh{+SNA0!jSyX9?HMY*w=^%+#O3`>_EXNl-*-@>^XA-9`7Z5rc7(QS3<&nMMVXnj7x z7mEiT#*`lQ$)4u8%F7<8y=csc%+Pv+r~0IKt^wr8hqeCzhxF|(@?R)k%9ESe z)$Qabk!cK7;v4;y2g?jWPI0oEQFpX+#kawSGRK*p!~5N z+;^}WnM`)`$>|N~tgS%6pTp&b9%+*q^F6E)YnQRvClyPX2;;KxKVXk)WCLd~n_22g z!ZWBTl~xCW5XQ9R$KnIWeX?d&ShG(uc^*h~DE!f($?MoNkt)`$6L0~Eru&+jcE(Yp zK&g4oIU(}KkD);rd2aszetkNU1t{Td!$sPP# z+YK?REOth?y}X}Tk|ME7GdCp^0Cf3x$beZZY34hDGh6UQr4$~m!*johIVfxjqpW8) zP_)+S@=ym282ciXYM&8GWQQdpo}Kx99p1W^{#CRJw$od*ti(_w1gh?Q4{qe(;jvxJ zdc3yE+D$g?wO?EbtfWSbTVg;oEVb}BBang_UF`SoIqUjgny<9aI%)2P(4=tP!b#p$ zin1~LzZ{fKl8)01Wh;qK{{ZJBv}30E zq7;hZKz59ymiQDf2fsk9#+|YTH!{i`lMgd_PT$TRP_+EQVpj6nPtnpz68r`@7QDUr z0vngO89alL9Et|pEwj_1)-)R}8W{acIpdhcBd+0+SL^K6Bz*%TS8FYv52^WbOL>`X zfmy1=2m5slM*TaFqaloKY=N#x#)0K;)8hA1)MfN;tsrUm5y-Ja_G$i6+vv$eaElz) zQmvVXlMohHNzsHRu@LY&%ZSsXmCm8O%6iZR7vW`1%4%wz7~D^upe>CiK5Co z?((lK+TBTSCDqI-*0%&e&cp0ip$GiNJlPc~Jd-Vkq_($}Xc^<=#5HDfmNKPbMPrCq~w<_7g+^tmD-q2xCM=>rT)WVd=}maMfuGwB+fnnb)TQr)49 zlkwByegJJ%BDKLi;(g(P=F2>(e`)>|j>}s`H++WPOtyUb$Rui0|37)d#d_-c+&DEaFc(lD8Mu{=JOA*M)eBKlvx9 z`JA&L;q$g!aaJn^7L**T@ZTs_0GS%b8XS%R=`S$F#L@Sy-Kg zcONQvWTtko#0}m}=FLKD>x-*vW%R6t%M@DmU}#7qusK+bbNDv^vqsf#v{{5Ub1;8V zNdWE1dNn>-A+puIzf|++(LAm+$_cf-Lg1M>kFtt^L2n+r9>WZ}0nw;N%P9Q0@=lHC z=q9w0g|@c@Z70J(jsXQ-LFqz8ag=u1db}r6(r)cRe8~*{cCfqiE`)_G$DMks;&0PGYGhsPr^z4@|^#+bL3E++EPON*#c zt;C8X6yAW;)5l|yiL3ae(Me=6O>;H&n0+gBtrVJqYD)blA;>Kvbc zoUl8EU5O{ZT4Q8J!y~YI&*e4Fsjm5w((V@$y`)8)OnIqL4<$A2zfQgxb}>Yj64^QY z%+t`=-+zZbIBhSCS4JUJ=Bu#;ZN$`5<-SyR8+WiA-XEdMd!cE!7c^xT@x8wECvK#7 z_O2(BX$(V8Q!ANNRJA}S_+kdh38LJh$LOL_9YrwH=8$StcCYIIPpcXVfOtWnDju{ZL6G zh}d`BpQEiuj!2H3!IW#8-+bNX-9O3NE|a49PJVK|vdec6-91hu65jks{dp{g_6<>q zJG`dDUbDW@C9}9w7NW!{a1X^2uYZ1DuPdJ+O!!z$j7GuYmf=ql$L&;ANZWqiIW8@k z0khISGGTeN%PW{&n!s_62+8D7G6rMxu{jz2Ofj*GxGXI+D-mt+YL^p8&Q$KP0YFc^ zdV6Ad?7d0>RqYCDAq5}et1UWpBiMAqcF&_Fyw)F3mK!*u;I^c!d`{t-gt$G4ps(SJ z5*a_0e9nBo<|%D2+t;w0kF2tiRk)s1Z*k$`azOD|K?=W{zm_ZH8=EgOX!9_(zK}?f zsiQ4N+OKN(WFc@X8F2z0KWXXS-z+ahR$y7b;zan=q+iCE;eTlq(I`a{l|O~lfO;U3u z2poERRqyRwmJ_}AvB}9D`Sa%3Ji+G6%|}Z36vY1k2QT+%VurN;08+VBc@yTyM`wWA zY3}itoHsD#DN2z+Qfc4@8FXegPLkd$Sz67MFjhPFV8XTeVFK8M!Zj%)TYZvZ6xyKh z-)s@&Qh>C#d8hP|fObMEe~=2A{D#0_8&|nmv~%5gTTi#tbjf`wC*-bCzBL;GUv2gt z`6Dh85mv}QICSLGbWIlBNHW~V>s&}h9;aa&cO*A2o-&UNtH>uDzE2yHS?^`~wDso# zvQYH^3Yz@`VSsJPG$J-;S|6A$G`%+MVvm|isEqB|6^TEI-we4Ri+fMyzmaV`+4*;^ zY8KV{-IUaoNuYjJWo7U*HSnm(WQ9Z!z{;lcf6N_G-^=<}l(`mQgPal@m z$V+p6Ork2@XQEsBex$D%^bjv9?90l4b@3-2jIV1>be#iG^99A{=uT2Cs;sWcX+g@L zPsM=8(`rp@nRz!#wV5?NPgj(rx_SnVIvTsNG$Z{}Mk6Q4TAjr!YFAhOcx%mU6}N&Y zV={%M9ESAOfbk@K*%Aph#_1~Z$CzaDw6Qjpqt%XKB&6;NFWFO0)va{DDpG}7$s z{NbowYFhliR%Z@l5slhGLMl86CnH-T_;yO~r@GQD4v#uF)!Js2WaGJNao@&+wj|OY zL}_XDFCkuemsF63eKJccV<4wSRUB*L4_uyOQQNBlyq_oePgkSOdrcKr8&MUA|cn3X1}1g=;!~r31%% zsa2j|-jmcG+`Z~6Ts$<)0A7RnVWwF5qg#Vd^Bl~I@eu(hZeePO0r&0nVTik7KTjl= z^D|JomsXnO#L&s=G-axO%Fqe}+L-`p5gRgH3P?PYHk0RTP%e^CL5pvOslkcwQC6Yx z$rDJs*)5Mp8vVYl<~zw5ZSCPkgab}ew1G-}1Al$-t+($>1+n#KTZRZ(P}ATWiZ|Lc z>}iCXWKFY;DYbj>b=@_LBFZ*|%M}y_ak8HhLHsd^rV?cT0GG1p_cj`}#qd_WnLRaT zATreQpdJTt>@bnT8VHhCWvJikclOq+7`MA=7Tzg1(SoVu0IzzJzlI2Uw(l&Z9g`2w z+raYJT3$7EXhTCduGEN-yLgk^BVfwI7(AO!mgTJDw~1*2Ge6ngi)=ypcJRb94|e>o z^M0A4Xo)CdTXsWjUWEv^EgXy(Rot&NYJV>p4ZGm3r2v4Qc{1hoZ!BtlNWV`?vHf-zAGTv- z`?mINso-$PTt~n3ETRFh@yi`Xc&w$-u9S$GmOzv{6a;-8cgKpuGATH@j^f`-mUxFd zXmN$J6HEX^E`)GT1I>D28}k;W)|*qLmD{NeL7+UBJ8@S$64K=6P^#^SUt z$F)yE*kn%SW8T1gy(Ois#1}KhX5%6=Lfsex(4TRV9O;>Q{{ZIalOoY>Eque}^Ibaa zD;Yont8oOV{{U1TKQV$riwGB=`7s|;)NQS7iv4Bgjd%EZvhVG`T($s{vSfC)Vfo>p z!J+w`;)TbnD#7X+?dm*ZnKkS2RQ)+$QV|(gN<4>qZw9jzT8jGC<<)6kA%%GM=ml%^ zWTGu4?7x*BO0~b%_1#ZeRZTWDW=Gp+JgBMe1w}jKjL;|G!bK#p){c#%YF<&)0s4%W zo}?t5)qviE+|s?e;6F$nX*)2z2t(!FO3%)4RIQc7Qpg*6s34z~)gMMnAEep|u}@C( zSD9^QzqQphn!GZ`NR?O-Do60 z_TS;A3*p&A64iXirg@(5Nb!}52$C+KnF1&*$K0HN1OvM=6Wx2vzh2Vp>~wk2V3Zir z+-z306l#3yT%JQ*mRc9NMz2FkcpR7=RPJ7@=486_kCH7eCgnZ-oy!9= zHAsb&nlSO$d`GaRGZ#gKc6k!$x0*XI*WqZFia$DXUx{ir6&(*sS&BM{+@_q5l9Yu*u3-8tz9AR&k^GUr^WdkMZ0Saj?b6npGeBX5I7HJ@io^p`^a^5jMN{3s zEZ_Vo<&95QyKmm!Kx49uu&Or9>^@mJkRwB@1I_b2=C^mF>hRqcq6vib0*fN@C#Omg z+s7-3A)Pu+cH>uXM%)^;04&^(6a*k11TNn^BFI{&p?SZ~mUsGIoFPy(ArM=nK}tAq zD1GR)$x1-(#xY0v>q0T;_Wo$nT(zCw?&RWX$`wa`)$qYDC67JcFXbDlwCkjv+D3|A zM683LJAi&$;fZZLnT4Z%kGs6GcJ)lkA!jX6Ly&4bMQ}u2kAnxK`Nzufc^}Ibnxn95 znb(vO9nS_1lq=W{H6Z^0Fu{^E#s`SlcqWRGT}BPnO~F@D+_Mj|K4jqmu{(v4VMwA< z(nt($)!cx75C@NrBX$>0Z2ZWE%Rut|t%sKo$dXvwz1ofXk_+$d2j7v9-HcTH&tnbO zk+jWAmpgQ6V1@=epkNrEq@TYfkZ*qEK$~N_h1~akP}BaoEibLb)DcGAL}TI~NCbg{ zDP(4J;XnL`-*9W5=NL3UhSEUVpRr~M} zMv!N0-^hMv{{Remg;pcZ#^KV|YJ4Cgu&5tM-wq;qF_5szzcoCsZKP`vd6P{7DDPfI z0BlDxYQ8iCYzz{0IV734DHAbdLEve|y}_kn}2(#Povr(lH`e8*SL*K5pKuTP zIYgzfF5v7-4D)}PCi7bTW@1`dBvQx6hyWk>5l^EdEzPn5p1Qx7zohK6&o5~Zm_iGC zYoL`yVHqO3c!OG+(B-7D;ds8gJlZb1evnJ>i-A;K!5p~};ZN6)iKcH}(={Z6OS&N<3Y3h$G^PNQYhe-D_Jfr&ef6Q`$V5{+s6!f z62@CMq1iu_?lr4cKDDjgtHCF+3RsXnVwCNGNEngk%)BPz+I=$c*v1?NqoCjRg=jwf zdNR9ZnmwJyuWvS)V+%4NQzGYwPBr^wdQc~3{3ZWj6|o^ry4Hme}* z)YA!<5Ty~fjLU8;ZbPv&a2|wHe)RSurT}>}x@9rl@>E7zl~NGzQ`3!ofT%et-4yzh z%B^j!`ELIJ^77~7D6!o~8QbG;iJ4FNE4R4DXN*|svsWVdk6DHr9W8V)!6!E*h@~ss z5_~w+eDGe)jB86%Xwt=U%?ikm>Gd3#{hy-;He^~d!%=Zzc>!`JlhRIA@ITp1GE>ge z{WkJ1sErEZsO-U(Qg%nJPtZo#j%-Ql^nWqxOXS@MXqK`&+TN&^HQW@CsIM<@Nw3j{ zW`ZI(nI4(tyDe+-$+hdX2UOFibdE#zr4P79_CbR%pbHL0pE_G!TrAJ%IYlJG>YXS) z*8Viejhho|J9zDFZb@H`s#JsVa5W^4e%ME)iD#ZhKc@A4A{hP&1l(kww-7-kylGs! z7~kN;ot1y*BPX`D^R4S?zz10tE_`SRjxel!`5p3rZJ6Zpm^8z25WkKi>5oku^&IGH zzrTEuEQwCFK?+)1#c1T*10p*gvcR`bm_LR*ix6Jv`8bkV>Nb8})EbLCjLRIqV~UFN zJ``d6GCz}PmKje8>HJc@*kNW z%vaj3op_0F0I_8XeiHtguMpleBarcGV|yQZ2&a|b`NQj%YioGErMvOhNrzSlFr_bPa-%jC7 zm0nDT?16-A$H|s{Y)v1VeA8>LT!N5@R7{LOsYjq9pUkw#4rvAFADGwjYj;N9Pp{P? zWmr#tOn|CBfwnzXJ0wEV`%!}5MKJ>Blu(oQ_=YFQ??^=Ld2zhThq*!R_k=F z9lc3u4$1$i%A9B7(#yKeM-7qV}~97;O&T`_#F5&|Dl@~E=3K{FF1Q3AY03xiby+>_G?2;@h4^C-JR)roU7!0N+`&;yRvj=H`qW;w zm#9XwEGkJytrdx{%9X@|4Hh?Q7QRQ*Ji{{G=+Z5)GOxiQ;v1PKh$n4;B%d5SCekvH znfY(a!q3bQ%d4xwq{87^Mm)aKP5=NwAfEa7!CBv<*k*XS5YusoD+xJ7!>r6KMCTtk_Je>Zc%M%~ys@ij`gfT$ zKz_EivD;k1LJuGUvv>njwo5NPxf)>@r<#wKFSUOmTPKy~H;EW8Jbhj^ZHlEj?kP+; zLLzAH@P90MIrV6+(@}q_PYg_BEnVMgk7G=L+mmMy4_@+Crw5Y!uX!Y`>fX(AsL+G( zBJm&-;&}4IBeu8}GskB+Rh^Ksbyg~CwSDq2k!=wNny;GeX0*71TZJnl zB}3D8s1-Z-90c!30?HbDb+mL_kV{B`*KkV$dYn72N2XY=ud4ZLQ95g+R(6dQ!_}!E zDx?lQ`1|~@&yx@WU-@h1$I^VoWv$-4l9iKi#Iq27()~Db%mn2eLuaBv=A9Q<^LOc8 zI*Dg??zfOi2NsT>@h-slp)hE5^*S=e}K1!F&*5#$Ritt70k@h119#!kR5THi^b-C511-a6a@ zyRN`3cONfooDZll=gsfGoLYvDdvB*`wz5Yqvn+P)Gld*jNI)ZQlp`ZEQ`@5m$I|nU zH~v_?irMQH%4M>e6qaJUxb*-AwCqmR`QXXvvF>LQ^S?0bSC@JwU zXRjtaM(HVv%UiOyXzZ=tKT=kaBth{7VYnV6Y>BWx8?)PydN+_YxqQ2)>e`~j%WE_X z0GD9tB|{QEjz+#T%Q9>>?+jD#$;1=+FU`MO{qHElD3H(+%~Id%4UR^8WB9R<-Q$)T zj2CwBXd374r&$WeF*^ldYs=-g&nIHH{A|iV^mf1bb$z5m9=#le^y`4$gu|h6)F}4I z9l0b;f?2e2-6CBQ6u0kLefA`H-~!I!WFGZo&d0kp)?nm0i0KI{0G5 z6zj^GYk8knu=4Jp=091zE&3vzsy^Y8jCh$_=;(aoX<_8gFn(WHg`VQj8C8eG%^9!7 zPv$|@MhX7#TT$4)Qq;9L(sWB_;r-GYb)zT5N4YpFh@?i4I+dKdb^Ap#a`g|wSnPQo zzlAW6%pHPgY(a&gwN6Djb8l+X?yf7ui_&4Z)^07dCXfi-xK^JMv(R??vP1(QgURY& zp7tN%6KCZsR8ebdYi_EdfUJ!jBsCpLder1)E-6wmhl5x{%5{cU0hvfX0MnrTc*kZ% zKh|#jRe>3UMQ<5|gI|ij6>0N0IV!omg=bsvp+m%gs6VvkgqnK~zY+RSL1lWNw$wb! z8PHddmN;F28+x<_hW^9pIASVDr@HCaaQWc9v2XxsVcmB0yRnb5d*H36VSZ zF^J)Ox942|=o;nblMw+Tl0)!{&;js}SE(bfVUWF>0&!vFtLG_kcA*5(#VUtt298n! z65T1^Es(vu!x>HJT8x?>mZG(=(FAulsTi!~t64!i{{Y6L+aP|en1gRN^KPUrp^c}N zn@}RR5=paeNL2ZOxyaw>WD@e}vZ<6{2D0MS|@X>jVhG<0Ff$L&zJT(Q`GmuO(e{6S)=Lq9%a3X z%F-s1=2DP_C)$F)c1}I)j>4XotiyTcJyO@o`b=J&Ho`!vIvxy5vv%0m=%xW=+Y)Pb zGj(qzwenb5O3^WyQP_e))QTUF@xT*LpnR=)ZF}Yje50(m)U2-5K_Z>ROcb{;-4qi~ zzkHDO64VaX^KU%p=gMATy3w>qc$f@r9Ev_JE(YZIfl6g@HL^k5b}MI~+S%!Pp0ld6 z&wflx1YKE`{w8DZ)YB(hSDPa(-eGflGu}1iY{E8hss`MFp!`PIE4`RcZDoWx)Ff~X zu?{1By+#eV`BQ8}sROxmI)V~!Vf$q9iF)-lr^w_1BV|WumMy5xQZxRMBDmZV6aihS z;g*qHl$WG_TlqKqWN-BZla;2F1g{$I&A(!MZCpU1WrU^CkN$B>appa6-q|;+52X;I zY!srqT18T&o8)Uun3w{ZXX=%_ zGhWg!Ed0vtchB@<-G-wY(j;w_fu z2Hda&4XEwPhxi|1fS!$lJFe5T+xZFnW8;R%On^!B*%Bt; zvQT_PxgOio$0J~b(E&QNnFPw}+&~N~&;kW&JN3yit7Nx2w4QFewbAAHp)#cy4gNM9 zx_h1Q!W>cxO$&Q({N%QeccP0wB~N5g`DQEPz#^G1m+mWe!r zR10ucFtMWU3%`&0z3~TT$$FG7uKu?;HlCtDM-Y`Fv?Nn*;<*x<0qD0H)L-E?&^2h4 zbt_3f(d7N0jA{S{K>EMJr>^9FIb@~8M%`(N7FnN_yo9lNZp+USN_kqtaVbE4Gbbfj z=sYUbt|mklJ?}02(bg|K!K=07qO5G9BqFq+Pys&l84em`2&Sy%k4s%VT+z}v&#Z^I zsbDGcI7wQ129=@950-EB5NU0FI(jO4RVu{w*o8e0U9wpy02uLZ^1TinLdI*IGfb^+ zqx9G{E5ZID`mqOUWnnhcrq!$x0;*W0ip&YwcEtftfH(NY|^CH=` z%Zo{)AnFspt2c>xH>F2mwGWMQsvQ}{6`*-3?Bh4O#O-eakBlzo?SRAswjNutCYS5l zua|FQ7Pb-nRU&s*YWz${s3dr7DdC9#q*-OYM%B|q(i>W~wubr#m^Y(0#lwC-5{~uh zlkN;>7B}bZVIaG@(;8+-?dK*qIc}=mkB3}_Pf4zd2FyhH&gW6QvemsHi)Fe4`$)AQ z93oX^p9rU{%X_5xwl=<;h;=6`C}gE1W~p_jVt;22z*y?U*enY3P16RUYi$DpCoGD4 z6628MI}l9|wm^3#k2Fr0viUkojc0(m7Lz&^E%2$KG(O$O-H0{K2i46aZ#U|i+~Vfl z#1ML(q>&Hq$!6GBpdfU{G5~JNgdcn8Upi=(*1lfTeCOqUG08Kfyo$6uz^a=60FuOg z=O>fu70IqNUJLnxf5Te+%G)7ZaVRQTK?bW^r<;H5C!+K;ogrYn`nBA+{rrXC+iJ(t{Go*Rv_}I?mt#Ud9n{i z@^|Hs^8{0BdQ7M4c13=$uhbK;q3}C^dt|_^`LH<%*rq?2dX)P64KBnyI%b~=qAhlH z4p*Ml`%To-Av49g#RV3ZVddM+J6q6;JaM>YKeq6MI*dWv2aP>i6yD>*wp1Q zHW{&a5^1D;GR5RJ(z4PfJxKBI-)tjcMVJhp*O|9%9`VGSS!A{Ys2G(>RUE?Ws+MxmX2Z(F4zeOoodljZFy!-cQbnm`KcuXo968v3;U_s4QeMA=z571P@TI0&}D~q z08wS$K)lqyK;QW}?f$a1lSeNR(9{q?`bahL#11XI*mbv;er@?qEh|(5LzCBq<4GA) ztY(04ZvrT5^U8_l&OEnym94-8)}^`*`66kUPGy^fB~y@-)K7z@1v>rtAa_XIz2ov? z*>x{7eulaxYYjrySmz-8Ca8WQI&5l3xS+{n77eh-Ym>@+#c8SeGt4)dZMDRL3wf&z zPb4F;Dr!1)%3^FN1FA_yy0w;gNNN5KE-DB-$*3Rawn&R+UsQfdOzER}i%jzZOZ9VR zlbBA$!tBlTd?eGhS(A8tnupX6lgisvOK4)#U_4UHqr({7F_lQKwK)#EeIqL}v_w-9 z)wGLgXJ|>?BIi*~LD{HjUCjvmak^z?m1|yhg5t&vCf!6qN7bi7{eLDM~k0rXzk#xO$?sjW#_((X1x}>voZuE#zM5AmvJU?lI!o z9&AfUnrY^X>J$^GW}pffI}V7AoZ(=_W)vR+u*al2$2 z83+|pI&DqCc7WhEly7$0X$l6hR<`JOVU0GeyQ!~dLiC?g)r;iiry;!u7A?wZ?Y7YDpVhG1xJSYEP{^h zaW9-~^!T*>M(WE`jn+W`F!9=kp@nPMgVU}<$(^g;XxDLD$Epgwtq28AblCp91n`Bn zVdj4=`qq{@uAUer-L!mFs2iG5PTu4ZTr=41l!XRMhwZU&A6u;JO<^j;@~}H1H4mrkYq7?-WRb};^Y4}|^=(dlU&+z0=v0wbH%-6+6qhzC)*uj|<0 zkV=2#ooO^pLM!Q`SxW{+R~hSi>qh=%l0sIcIf5DK(~jdM0qLf>^GLriw21tnt~l&<14%KmZQ4JAOH7BZ|;vN&dCwD?cmC9;p(tJ=mM-(?<0D(+$BL z`mYn)B7Be|#mVNE+EhMjRM%pe5F?^aD?%B`08+ic>*J3mKo(EuA1YX1X@|_(GCCr1 ziQDZdbwJBb!k{0!8SFy{NSBrAnl<&pI>v{PN{~e>O~C~CgOQHy(U$b}A1L`^JJ>E3 z_Sj2v1d1L~-wGff5T|lM$wY0$!biE6`Ihp_UeeP|nG#uH5=hf8Y8Gl#bf6->;9!#h zVFBd3-A7qVpCng?lO4RbBWf`|pBh)WBc@O53)bENV;q)OPiaPFQfNP!sk!_z1Dj>N z9>T)9dDW*=$Jg}e+c5+k>ETdogq?x!v{Dnvy3_f8QHxTyL2yyasxUlFD^dJ%F($q1 zD7_!fUR-}P`F3wL+{j;0is1(%LS$FmFg^#M@ZttaW0J-|8hJLEbS^a6VD%a_g~9ZV z2QF0=BfSBr#EU3q-dncQEUtymnv;}rMJJ>?RhR%sAIf(=IK)=SESt|d&Bl$bz0n?# zlF@D4}&!4WfDQ+YPk}*Y91*%BowV}vMYG+S#{JYoebh`~l zM%7o#ZGX7Spi?l_TbNO}F0v$L~F(U{PaURCaJk!j*)D;#=mpzzwv zUE`0CG4v8Jr7PHXKP*7qd5zxF=3AM(!R3fFWOb8LvbS$Z)`XOe%Mwq1LWFp6-xC{Z zj?Wg-e9dzOtk#mJs_X$I(6X|c3U}Cc!FJeU_d9(@Nz}ZuH70*T>H@Lx*tDaFGW>`l zqsF;89h!jMk66OG!dsge8sQ^li4@exp!`gxzk$F5Fvc&n2yB)OK0r7L!n0R?M1Vo| zuK34f5o>(UrCj-9_RGwTa3-}Z-kDSTDD(ooss$qFGE{{YM_SuF0;(q;&e zOVNNPg0$}21Cz!#2#`p>Gh?*!&ap0*@c#gDAS!EKD)eFc&3*E`eQ&*(DbACl$>;0V z^4^YB;!8r&vWf-e*nznu(7A6Eyg`U2_few2b`o2gnIxF#C&OypC(ni1<|c0y=T=g&#L2! zs^@yc{v&V;$;2+vSoH0?A_{uYL7zGiz*b z})e09K5^jO)md0aXLJprr?nNSl|z95jydozaiX14`5& z^fgIk^y8akc-fkwgXVhtvRJJ0MB}xkUTDc zE*Go*S3{zC^1|=S+QKSo!X|rjzwe$fO09e=(<}nU+}KFd$F;lm^8URxpR840AOPX^ zs;X$G!?!P^D-yl?*^z7~L6&*uzMRTYX9OMo)lGhnY)Pd3ET78FqNsaaa5KXkF6rs* zNd-a!<5~*jc+ zzRSt?S3}a9eVl5_D24;A=+}N&)J2Z83rdKq5j3R=*Yy#A4s5xqTx%B=Q2kEdlxE^H z9N;$2t3oOVg)4yWv5E>=l)b!m(ShkkE5y*$bT#~OC3d@?QCkEt2C;V6j|B$KY7WRUacz3GvCq?Y~wL7q^mk`HT5~XXVW* z$Icp9GuvK((bHrGO0eQQC~5S2Wpf+{;ITIF{VQ0V@7v6mF>)5f7NFa1fctjCbjzAq zeQ^4|p>?QSfQ>VmPWuv1Ax{q2A{kvLdRlrn;_mF0Ri`pVIMaIl6v@PkwlX%)JhSF8 zr|ZqE>4DP44OTbYFd%xLAbR9tctw;KuKsCxD#rf+$@BT&Mpd`4xR>h?+wDl?`z$N| zS5P~gW-5n$A|#Uew#&-*Iy0H2;;fWaSp@;u6JH7fF_5B@A<2x}MfHtI;ZPPfkbqdw z3biZrSLn!rY|OTMrPi%y+6Rt5qQQ*RQe3WIR9*kHl;~#yws$ z`W|arXtneOtsT@cAF@jtp&m3DDo}bSo@3J6Po6P1_fCl@a(>r8ij?c^kddOIO8$b} z8x*ipJl7BLa0Kz8*?eoh5zQY^EZ_4lO0)8(nk=p$mVx4iX=-^CdR4_cQMMQf^&g4^ ztTP*bJ`GpNgH5tyZL8T^BL4u)N{88N@5qz*WTq~@3`A*D%sx}nuO+iOLZF>=)V;VB zs`{*;fww|F&m5LdNj1?iJ6U;#pL;KywHrStNaiUU;I{;P3d%{PK7H~r97&=qu@5xs z*TQ);mp+q4C{nBT7$6{@n4ti8Wn*y8kX~8yOqy7^wz`dOZ7xFkyoCt>?7;RSrkOa% zRV2ldS$XpI#{N+d^{ri62vSYVYQmw1xLv-S1kj?1LeSQ}a1V7cp4~>5a2b}|9iu0u zK3!>&-*w@quOCA}{JfJ<{{VRIt|Bpr$NRMp5W=0<*XS7;fB^UX>>|Lme;`F@G~Ghx zRcmY5)F(NIR(M);9x8m(3{|jXFgle=#-XUc;^lMum6R+@B^pDJbTli_`!|e*XY0mF&pH=zo+N ztU3m<7n*Nv#mo?ME{qBLK!ShZhyyHyz})+oOj)4KJlmkRm^`B=o+p$WjY9HPMUe9J z;{>W8uJs#z`3VjPfz+^xr-|D6aM&N;m6l&j^Fh~<8+|10gf;`RJ45tD{<9Bp58RBOo|q~eG}~+v|lZ1 zdUm611k+52;1f^?+y)(uFvD^|Vo6QBx#rmXxi+`u`zKhh7zq%8Rbk46SGl1F+L;KO zi2(q0Np%ep>q(62+Q1@7cyJE2XJFK2B=iTz4C*Yc<(^^lE!(Qt%|7mNor9K*{uNO)`O=0*n@o1WoWC2OTsO#4<)LK4uhbsPL?uW@kcr(p$sQbR*Sty zU%ZS!B(deXYog5#gR8;%N)qb>ag}?8VbX`ET4Z6#mD=n0A$GjEiXv8E9asZVL%8s* zI^sDoJkYimMfB5S4=VGoA=nM}9kN!Lb9zVRcbgBAb(n702Dg>CTn`vtipqZexI}{q ziwvvHx-5GA?e3N2KlG@rjQ36ud@NampHIVrjs9648y0{7=ES^(t=aij*HpT@j=H*- zn%##|<*C!PJK^I3G!l0#hskTGXxFxWa@9zWOLn;6O*tP{s>&8olwtUhF!`P@W_38?sJpf2_H@xVLN!YS!6d4|JZp4INH#k_FAt{S%_A8_S782U;DD+(GIpPWVpz zkO;eb=jAk!ep~4J(Z6Jl?&=1GV{P}N0r&5>woJ;SOb+JWFKvHqYj3K=2wVH0fHa3; z9XSvOi39j$08sd{DNE`X+I5oadUSlEG**#%({fawr+=d?mfIu*lgplGk{v~LOPJ%6 z(FMRGuJy0ZxQXzjz_O1$dHFT3t0t0YpH}_ipV@QpvX7QV#jJ7D(0tFR#i{u!&&

dU*L>Wsg8q@JUQi%|jRHAH=$OmcmO7TJ+I`)Sepwgx>~-$v8mX(8LN@9uHuBLz=Kp{i){PL=V={^4b%Ik3IlK;|6+t zxHq2GW3NOlqWoB*3K&Ifa&*nl*=zZW0wNw zmeKm@un0SRFVT*NdRSo&;m5B8F+%KhiCt3igU@!S_v{}!-@{wV@# zA)&OzgJWv)AQO= z8$EJBaHw6yD9_PM`F8HeKE9SY%TGCLtZCt2ULXXtCs!z(7#|D|W@c$!i3$ch|FCmM zqD(3{@UpUbDd?Xr<8@BMpke>(oYN}0nY>ws!BMuPB*oM&vELuNZgBADBZoly!~FsS z=s8HAgty8p)!ukk!ma(%Tg1a3zn>-u%!&cd!VUrnj+T7J<5pAr8%9{@#P1||YmN{7 z8|BsK)i~phw@~@?Zr8)i!DtVzNZkh9e#*VeT#vqKiLdjd?ht`*G*%CDusgTiVL{X6 z{T2t0L++`7?dmQ8W71QR=PDsj{eZ`hV|QWYNcf)@X7sjT6`By%u$Ak*pPH88sCCxn zmPuFeA;IwujY^pwK*H#Sjg%MZPq}8Fr?yv5xVsXsFLVtHU1{xdYXGh}{6kMN0juXj zMvG}n51WmkCZ0i$@kL$_b|mO`o+E8f3rs8r$3XWvxp7I3#5voaK{oauFvq_pt$VZu zMQQSLGvh$S0B(xmiEFFH?k$qspDlSSpWfBKlW?_~eYOmAKyb|9x2FtM6d=%!|CNi; zzjH8>sgL{1+01lLu}tReJQsUdDe3-4Ql58LVFQKtpifrM(;)qS7iWJCqX*s#*n@|6 zZsw*YhZ58$RV7fh!?J7Fc&k;+CeEmi$Yds(h`s(FN$29vg#Z2VsZdFZ79WxdVMyeb z+uX0Yn~7MFVs23`bJ?UqbHCs3cf)d->sYttGWTn_7B;tp&223EJ^KFsi1+(_-se2e z^Tok-K{X+!3q&&~ZQXMh9@Kckzz=)6j_oO;CBL=${PFha2!4tjWwV$#b`1Fk)GO50 z7NQ2N?BP&%g~3>Kik>g=t>R8Wgx;0vkzNZ%%dF4^$Z?8)q7Jf1QtCu(*-&lIte2z zmv+$tO6W_05=#6>v)>-1SrOk_^?nT0_xt3rV{v*6h)jihPnS+z9quMxF@6)wYfudc zVOs{tLMi5qWgzx1hY!Udg`gZ-An=6;R@>aE078}_M8wy2edR0kvGJh4KGdO{pEO*D zhrRBI$>SiCYn30P-cS7kn`#lAyx8-aP^vI^K8)HC(=nhyzB&HnqK>c}j=Sr2!D}Ln zIli}$16K6$R{P~u`Y^p=Bzyi@NLJ!IGS4Yuf~NBL8+60MT|^71>oK*HA!q7UHKXGp zg9Wy6b0_;BPg_b0!9e%ucPa)4ryeu2;M`5^cqhz$k_MEXmM(r4W$vfZED|mKx!9IA! zEYkIwE9!mm#`b3OE)f!XKzBjW>2#NuB`43;PV!6q3hW~%I3M^GGdL_;xA&`v51%s@ z=Np>mAstj#`cW6AZ0@fW<&uVYU%X-0x;3g|0r=eMWqW~f)Y$7Y$(KGFSP;0Z^vsr} z-BfcS^ewuyhgddPwrNWV9;`}7nT<<-ycbqbV=D5S34kKyF}J!c7MhHzZp1>4d{&?l zLuN;5MRBcf?JQq|?5yGO;a07fZ}=ScRK@{?utQ1mZ16ZjW+~)2mhsg{yudHQ1MlD? zm;h^Z*T3hbf(gj=A7}hs;SlOy5N%4&jxPXy5i3k}5#GM`ddGspx)A#S@|&M`1-(9< zDwHmi$tlHA^<7W46)L{1!u_@Jw4QSink$P4yMr1GUFALTZlmUATPUR;Oi#3z;ZAFc z{yeQV*CRU_nO3zB3OSM`&qyjW%G}9wZ0}#HvGr%MK#e2CiJFHxJ^hCCgdgRgLl9%MaAHu_qHK(33QOjDUl{#(SAZ)t#y6Q`qit9t9QTAZP%!!ZX#cVa2HL zoWc)z$&aoLh}N*MiexLK$J)dVNPZ~ebFL}ra|)aQE0#|rq+D=LJu~bXK!kZ9o#}1(3cBTykA8zv#(r{> zwi1i|?WEY7?^RiBSu&BIT+n#ZiSIF$-Hv)Ys?yP;rA41YrI_@#%4}VISZvY5_Xomv z>FoDf33Oc}Bf2GGVpAbc%WJ7!zTK|6rYYCx8peJ+LVN*y0kUyo#%P!d?eMCagHKmT zYtzXP3GF2NznO{W_ba}t`9dib3`$&c9)(MTZbCgUM}SANRb%3e@7mi<18TT66|MSp zQjTw&cg7Bb`#1_@5iy2g9;Boi>E}WvwI&`LHX<13E8hmjxuJK zJ05vNE+STtBHvSuE3t2QG}UCG-SZEbfUEQ=8nz#n@yw{4>Xhfo|6WWV;(*is682i{ z4Y;*%3thFQ@-2@ds7>n8@O1B89RK8RdTqK z;QBnsCBg_At2NFyskeA48+~3{&%nypb{8{NJ9Onkf5UXEy{qlD(1gcvDE3LlnnxqfFV3;!OSH zLYzmZ!Ao?fOMd`#{u%8ej_l!PrOvtgiM6xmRF$95*dZK#3hUcew?~9&(EG;vVJXvm zw5U|EkO7!M8;|+<(?+9R1;uBf<22RR=p%rqfEUAT~*WUkd zKCMVL1#-M&Yrq63b5hhz`k8=q+k@**66fF?zQ_nlxT+NN0{R+{efU}SFaJ~HCE5la zZDlix3f(Q$ClkC7{)iBMX&W`gL6e=33de;K> zK`iAUMjI25oASZDpU@x2K}v6r4Vc{b6=~V8?LFPC77a--j+Y#(s^|jcAU0_5DcFQ7 z^+Gz@XLt9>(-j1=>K?zWmC`9qrRm%NB1~`fsE5%;?aa-8(Sp2(G1>cgjy3H~%FQe1 z&py^#b$~l#rN-5=YCVuIa)!J%dW(K^%kS5fy?z4W(Vb^+Jx~z52--foXTazlZQ7D? zdL4+BQv7qf-C7{+-uajMf1ICH?MUmOyKszlT0jyLpuBT2!HcpZ=dO~PqT}^~=kJ_k zab=kvcPZ%aA!d*XSP}IXG#}tSlA*B1V!g<2GR|q`K_uZm>9WhBY~-hD(a1vI>&lql zogPC{`4;U{VlAjz_w>i|;vk{XSf$T;a7E#-zh$9Oj!eLymK~ynWJ)n79X#|YkF9ws z@f0eY!)84${(Vnj;{n8g_GVDg{vb8OzCKwVeKKfw!4qeO%yM#5NtpLEhdX2cwK79i z?FkvL#9zg%X5R$9>)=x!wd<8?NK~!qP&c2xQYH)ify&O;Y^NckWG1m9k&E*Ntu4D5 zuWne~Gh4QpIHhd)HgcH_F$_%!qA+K?@D5x!zM;hxKW#<_l|E@HWd)k;>8KJ=)b><5pUO!)-_!c+{dr9HKC zX)X9E7^b;i8hF!a1{mB;DahDTd%eO0+<^y7S`LsUH<$p-`qfVnoecv-XEzo}2xTIe z`*W?PFSOP}Wf~DhGuLCZjHm4$OLCl3IAarB+glYgBCsQBz`HixS#Rs%RceROg&XKL zphxCi)W_?KUyD91>FrAs8S_0c4gHsW%2@u>QE9XS6Q`T#G@!RM@tx`GAwhP*Cmp)y zAEgRxbKjGFHdEkun2gy|HE;BQNj!A&h}r8{ZhXhDf6eKk;T109#4CGZ*iZwN~qn+KP5%$lYKXtl7SFmdlUjZs?+NIJ4x7<9?T?$M*CVK78?R3?INBFl4shubbJ>-{*2A+_k6ZxSLrBS#d{;7M38!6G;3S@G~*>y1r zXw0KC;i>lFjdjME2xZQ#*g5|El@v7I-%l|}gFXC(~r{5|GeF2(#Gxppo z4KrjJlB?S<_!3zjE1vd>LnhIPFjWV78)J-~_mXaeb;mQx0t-c#1Gz3=ZX%cJd>kub zU0ft^Tz&+hAO8y|4#XfrXbuB}H2Y^2pNA||;>b1Z2hBUdW>!Ce8$+Xj^P#HNKxFiZ z0X#ij<*8(ep4v;368V+c))=ss1npe zw@Td!xeMt|a7If&jMccmYEBIKeb7S=@iYsM&1be3 zU7zY+fL9lB{Wx>++uN~9qMbpumE?avMgF^4QE_Kw^OCzc;b^f&eXlIS!9!J{(&iWV zF4t8+eisw4>5T2~q2z&A6q?@SF~Z;bC$YW(mxgC=v?Tqw=6N=ewR-o^X0HzPSDXpR zMN?UaR<6Exr$0(;Izr;DW-P}ON4$Q;=?>i#>i_C%>C}o*eLIZ+o3$`HsU^kk)2?qV zcwbz2GsC&pq+Z->0p&_*`?1%8zR;P2B%Nh0(#((&;?L^bjDh!oT5IGkbnHSuxc62^0uQ=N z;zLKui64v5;BOvvBywVV~JbCM46e>8i<$&mhB0QPJD$3u(kSV%U#{J&>Y zzi0zrDW)o)a)+K`tYz)SKL?E=rOf74<92L$DG8(*u=;v{(_>obrD^wX0%;6poG(bm8) ze{!vlF-;@IWrYclG2oeo2lzhX$PB+_xi+Fy{x>LP0f-Dilu*~VXs2n%T9Zv+t3fWA z8PiI*Qbh56*4~OD7!?9%4~6`@YLbSx)Ns}C6Q6p5Qn28+{K=Sd3;fe_-1^=M)A&a$ zKg}aSsV6ob8K~^*4lyJv2NMrq-=jQR;($xE(M~lwz-|9D&Y&4|d}^rIK0S|)5=+5< zNLmY&i}Y|ke@eIM%Hh?Bw_&wilZP0J^vj;W1zK#rbvsQg?eZR%Ds13FAXfXc%}-pg zxAE(Gb$8e`$SNwMAQilb1ZVu@XoND$W)^jqHjG>J{Rces}yC+10u{6F5qkk*tlf;Wk}8e`4~|yTHo+Gt)$UblrQ5#-Pyl5dyCy7qSGgOH@)QK`UkX( z=#MZs5ocNc=q%S|pY6tsmp}PaE3ABN@)&U;C39=@PYLaD>C|~tE%yAqj`K71bz}&f zPpHZCBIv6Z6CfR0M7Jzl&rnRSnVg(@ZrKnk!y{mHPOkgEx}6lK8`&3a1ld6QGQC)Z z=@ZvA4tiC^^pVxC-)x0sk(VGxUw{4G z7p0-bwS%3eve3^3>C_D&+MA=5ti1$9*$K6J;BH3qsQsHBqGYuyC?V?c$DWFp-RXyz z-6<0HCwebgK&JoPc>@kS^sWitGXGVJPYK46XVBL0OTN${SmyQM5tIx{5s&r4h+uEG zTu4_GvC-p;O5L;`Z~39~i?o8S5Td0vPqmylj@0a)6O9-6`>a>S**pRofzXiy%&=@T z9RA>Th|m*vdXzD(D+jT>ApPDNI^tdX+q+~p`|iN!`^LI%x@gE}Dxk7Tu-^7Qoj_I( zp?OgMY>|SUadq_}8+zW}SxI5yv!Gd7c5;h-=pcxWq8b=PQ5^=R!HJCA1^bEeyX!|a z+}S4AmQ{G|6E!(BB}bmx{%L|7y{FG8Oyo!Vs5Sp|%n7b49}t#E@G~`4yxoeq&S)C( zX~L2=gk+q~jzMHQ`v0)lE0g;-E$v_J5S@f8>FZwXLy0F)Vui^ONc}%tFYZ2kL?lL4 z*SU&4WgH_e_BhxXdzlF+NF-8FitgD;WI~D*|DV2nRE?u?&wsYc&J=;L_poSm!4IMp zq5n89@Wgk6O6cnfrVjklrS2w*&U)^8bm+h3@IN!P!%h?Rcer0xV2WKqi6+w9x zAwYe`>7W}Y?KC+#|C1#3$|gYQN=?&xonW=1XnJO~45=W!eI>K02PKQIT_Xn8Y4MOG zuGzbD>$B#3;etCBch zTSvLMlNT_$AGrU`1wV(?s>+Y`mqrZq#eosO9pAfsjyL`+_i-``7~*=6=IiV}Wo|zpvm{91$E$0R$!+i1dVCd~LRv zN_?nMLKYO$obV4rnTMaGPGkwBh6CLpK;iQtEMIxH9zj5Oj@s#IPN!+*P20InJvSzx zqwQkp@8IDr^oIpSji~2U1+wtTTZ|ueCf;>1gO(dITxQ|p_qg7L)kC|X*<>c*aH>y3 zpW0BDR=$?~Y8oGKpmUI9fD&(U2tXI`*~szO?~A>Vg|3Qac{NF&wy19(sY$xC(t@g% zf?p}s3%UyMWK{yhLa0N@p(P%4#0my`&yDxN$b4~E8CUo5k5TYkr+t$>PhV%qA{LFh zi^Ua4()*4{P62HmSV;txvMvy9s8QWx@$Tox>o+G#?4q2s7_!i5LgGSdgxB?Rdn9nx z!!<6!CQn0mI~~52bL9+#*NvI1+K_)^_ku+H7kU(Zbb9tTFG)o)&vV<>G;TO0Gf`Yb z=?ok&H=RnfL9OtDS42{5sgbzyo&*%d?Lnieei`D{gD2H*=HHZsT!Z{RaiGa-1djML zNeA0BO}!)c2aL@y^xY$uzDxe)rp70d+V-&dnJe#m6c zUQne^A?}>;*zT}A`YV6h6U*k`*PwNvziCsUIpv57W%bk;*xG8n%IEBSTpe=6u&D<0|T8@dzugY$h_#B^3?`)2|+HiF+)66Ub+C7v%nr4e3h0Uda7+I6ec57PG2}ATcM1KWfRwqL z$7AeunpAAGyXEM0>8jinPRa3!7S?kM$W)@8EyjzC>gl|5U`E|64KRSVw`Q_d*JK2q zhefMSr3h{ihJc|;^e8&YgHkK0OFLR4{L#Ycww&X!HK^A^)tFv@;UwTm|1kj$&7k8F z5;!Tzo-QXN4j(i2dGeRs6w6_j7_2QPBDQ%|IOTwB%vleOKvXyqs9cnm6tHAd<};ON ztr-P&?G-8<%BO=pRiM$LdDTWBPPng43k^zT`?CUx>9T7;0&R3`-}qln2D4D`#{C6M9A0JOK;;#leM$XRJ43n-+}%B z)6hysKG|StMrZJ6-Oi4&O-dWjYpCxz)|uwt=VpK*yqUM!lD=}!HHf5YRtJSwomeu| zcc24IK<-qD&9Fa~!~}?cJ3#uhkNmkE`91&n*o%?UShJ5;oo-yOMVB!Fv}kbRixU&K zL`Ou#t?3`{ZJ(u{g3vo;Z3TIkiYvSh$AC`@^5@;gylZ$!2m1+O)$80s813rr7lzCI z8hvpm3^Z@mDWlJwJY zT1xn`>x|7Ntcc)Ku!wIPI+Ki?Qv$Vet40d_1b1P-qSiORJ)Byr19>Unlwa=|;ZeKg zo#i^NqI3Am7Uyn@NNu%n|LJr1J_a1HrOEL-bGLTmrY7}rd59>LG*XTVDpzE+pYhzhyuv0mA1oo>Z!Er^}iV-GKf z{%qb}?ecAHj&$fo7hPUHJ7yDC?WPlSnK847m0$vLEf~`pFQ;4?os4dmK+2ZP+?)|7 zspTO0_esiFy{v-!v6v@hFTPC578+R8T_ilf-%D~f7aZV(8^7)X9V(sp@lWZ}@|Fo{ z?aTV#!W>MKe|tPA65hL&oIczX&ZFp5d-cuQYyhb1}=y7?c#-g==NL|gtV zOfkRXj>>Kx9dJG4dK>+Dvo&>hGHM}FIPT*7X<*PwDoXwzDI^#&VDDOrD*k!YpvSGP zu9vURru(u!_`X<%@##m9y@gD|on1#0hcSY@&(kFDlj>&U(w1%kuma%Jse-H98&g1p zP!d66mkH=qWdh{r$A%A|{=L0z;`2BVFjHtXOYkT#zWyCE@(QflO9^r0>38yyd^%O+ zvdfoXR<2~lSCB*p9uS>ibP~P8z2uQtihR(;qQEL$l3Tyl>!7JzzlL3TK|-c=C}bIx zGc_rccHPpvN77M!u!8d6qDo$8H7d}{&-T`Q@h(IdU62ntN;!E!>v=V;q7+1IH}wHEUQH`p%) z%3Gp4X+s)e!vtZ1#8iL#yN_W?4;td0Bqd6?^Rd1bSAn3hABeC!NL|#RD&h@|y^ot- zLX$f(3?0GB^R`EUl-ZQ_9$R#TJHx{FZ{?XCVs`KBizetb<6?d$i@R&ia92Wi1m#=G?s)rK#rOWMry59Jp1(2cH~yBrN8aX`EUQ9y3jIq1Z$af~ z_)^)I6VgQ$EjFgWr3&ZfdX_jbRc`MzMuLZT7wRU6u=0HCQNrhTV|S|t_QcA!!`$5o zR>3}6k047La2u8IDf_7nbC2^)zUrX!;IEqSaKq`;HFTj`6vlCe4r3Tm+Lj_Eqi;Is ziD)PBjqhAAW#!``wJBqMsoR@_kRB&+w-0NL0~eG0pI3hJ<5#j#x=PL%SC~PkJ8^dK zXjcHkB%ja)C_d6ihk3d;)RMyCqGmz$M%hPHUM>|bX|MZBRG9)AF#+-fId3LF+WQr! zOb>?Z5FHZZu`zRV{{*uxp7t(yBy)2LT~CBPcjtCo&o*BhFq;)!{&g0uQT>TabOGzk zWtW7l(ePZ{b)65wwZnE=Uu2sy<(*Bhnq zs)LK(jU~CJd%-L_uP#!=6e8@N8l^p#zT=ys+QkGPa_0qWqp{!9 z!=^9)a~{miUL3=dpD2^0`L{ks(CjnfJ*3VTpK?Dh=`3Y&4Hf z!y8!rcV}!RScVBucqMahHC;u&;?~0rYrMP2y_*RKbs!e8x}}8Iw=%0$NWE8`pkWTS zXiQdRZ;fx0m;(qV*ZNNt8q9_{;$j=71U$MjV*Y4#=LtkQ`GFRH5W*^dqIOBT8Q+ zfY=}6!~|`_O>`*}kdPaC{47#mBu9^tA7@79Qo|YeqyZ$y6d03FP%YbXVfrS#~B;s zW82no<8MF3m#+rg)OxH~oGvxvslEgKb|gKO30@JG_mUh8R3!P6NqJhflDem}Z#Bv_ z&7C`C9GCFh8EeewV369GfZP|Ow;6aETAo+d5X-tD`q(vH^R=Wg3!kg%Z@@MYripy8 zYE-@v9F;Hin_n4QCzfn(^qRXv*4xv-2Ee!uyy=c6Femm0JII?{G%TroE4@je?N%iB ze#0E=`T0@|al!)^R8X->gx#U@ocOMz`ndXz>7k#h)PnyOwY`?P5#X8(YJIG z`+FjIu||hFd2BRlK1B_J~~BQE)a;uF6qQs^+Nkq@ezBu<8h<;*{q7?3Sv1*||{80>n~8JIj~6C%*# zzMb_;OW*qY8CUbL9q0$-(}#MfvUO+@n|}PH;gajIyWMOQa3M>FJj+moTih9^TH4SY5rEjAbL_%PuzRT-nk_%_l+xNF=V&>^F$S|m_< z?Q@3P0Gzj#{r$dj^}T-ncK7(^mZ<2+yF7`N*Uqtpdz-R>4lAhfBu!diuG#=ShVJVR zf*+MuXtg)A$0xQ-NQ&g9ryg!tnK4=q7qRtCBw=#rxh76qH^oFk^gw=yIF|NpZZWouIlj@ujrp)-#|w?L;22L&j+><*GF$9vpc+Z z($wY?@_83!-I&k;e1?3S-rd82c+ws;ta1~hk!_DCSXv<%@?85 zPsl)qP{WK7F;3x+M9*6LylQfN_M7*8(Ql7mXaWS+_B*TgmH5s0_2_ad00{e4>9guo z_2Cw}>k1Q)+c?2kqX$;f9h?{#e{0UIl9zCTvmx1AD$XqT4L{EaF~k{bOoK7VAoppT zcGI-Cag?)RZgGvWcldl|Qyjm0o2p4Hp-aFkSG|7AA51X1F-{Ic*c7da4Ol4r&Fb! z_KluI1`C* z;C!erbX_NU9a1{+m~>|3GaB+27wqq}-2OA1#P|AA zOL=z{)B+AIT3SMPIv6tnxnUzXr8fiDU)dzn)|*M<632_&b0rX|kch8;`7kRB^gf?H zlC+p3b&9f(kxKM+L3y~VJ@)av^ms&SXv8K|Gv%M>!U@3ykg|O!*rY%BgmTAXk;!`F z2JZnu!$jZ0qT^ew)xiap!%c>-JRoe-$`OfCNmAcp&EwpRup}VzDSh;_spN?tkJfXY z>oH+vgLg#UndtVvJu3`nfcm-%=fkf-j)dq$x)GTP=vP=%&mn_oJZ zO>!s1DicHISdgY~Pz&@OZ+-TqaLIg^8k?Rb302)9UVExv7uU!zELDQ+$wEgK(z<*b zXp~GZnVO~1x~IAv&u<^!;51zuOJOmqMSmEgTTNel|5808spx9)ue2q*0pS?Gx>c_4 z&!IQ*b|KC+LNs$~%Y&wIFQ?BBrM+r)t317a#=IEE&=v!hYYq!nr0LV1-9AtfoTC{y9u@z~T$@H)%>~ z(o1cDu(_|clv}4%Yn6NYat~4BGj*Un4k6iu{&W5q4ns;KZWOywBFCMtqI9Hz5v*E3zyGg-{waW!kJ^R#!nc9wC7OqvPDw}{)W%gK0tr=iw0u!J?rAB4)n$TpD ze!ChzrBb>YVwyCOP4CW=I8o1&)1HYfUa{Fkceea+Y4IySsqKGj?c!egGw(6w%8lGAbvb@Vd!hFLF=(Kotyk4?nK1|Ltv{ zl+k=9W><)MK$^}-0^i+J)~FL5_E|C_4htuUzBEJ!_?XA=uDh|@FV?VaQQH_P6imWh z`hRtz)n#oJS-r{u@x&Ah&vQvWG&pR~SnM%aQeTGQFh|`I7$K#J$|$rM$`m zZfzU&BKPygY_r<+XRQCtgH~+{Xo)K)&|)TFP{D4du(5FzWi%9Ehpjx8sW1kBG;Zln zRCIw3gzhO+tVwcSx4S^FmlJu()sSZOuxq(u2h}Vm?r7a^iPuU#y{N)aOs(L*ZccffjBPi(N>(@#V>!3y`Bcx+Q&#T%bgU zn&F!WCOMmSMi*R7-pROaUKR+;_}Qd!yjXQoRQO$|N+ZEtvaY1+p^C#JZpk%iSPb;Z zV&@wAgJ1wrttrW==h7qn5ukaK&v_M^Q@6;dmlC(`@4vex z&w1B}T=YGgTmKOAiwU3)cEk{*NXrEIwXWi6jzi)0SHY5>OOmBkEK@Rs=YkipqYg7w zU=^?kT8K`+=&JOWZ12btE6ts)o!J}Kop|nn z{gSL`brBJVXW9)?D?_lRHDybC>w}~|8%KqxNc7d)FfG1 z>Zca%fC<-*6b!Gtv0Frh3DQqfQw9`k=HZOv@%w8W0&reKz7)Vao1078=R^py)N)Wf z6Ob!6$vcE}r(Lg3tZk0ETJt#k&2a^Ko@ea^^4(Lba!u9BZKBfzeT7b{q6-YPduIh% z%Kz2Z@p@*1Dg`T@6-*7w91c|t6b5R+^O5;OG~0naKqEq3K2x_WSdPPmfb=wbN}c_T<5YyA8idl!h&$^_&hM`8V7T9Wb8+Tv zRmSZ3Ximq{Z-yXuX!{1v@Ebk<`u9M zWV>VIx>pBdW&($jnO2*(7`6YR->F&KJE_a!66LZzv2GfScE(u@`f8E-)azr%%8Q@~1{T*a!UV`nF7?TY*t;&Bt3Bn-eUAMGPDoF~1?YSPjP0_k zAFn_db#!Em>(|;%q~6pxBVGhH10AZKybOWT94Tq81z+ln6^sLf#u}9-txj$+SPXFMnD zWo|AOh0D))LVm$$>HPUEh~-W=sqaQ_7+c)Eqvo}p(nn$UCH~kfVskuq!^}@!a#2r{ z=1oWH_%?>kpSAcUx`XV+_qrau%cz@JLwDxTTn0y(fI$%=nFJP@!41UNOYxFeZkyQa zYQBO^i#CJ%l=v_p+F;cadI^~$4o15@=n$rOUw+Mi?Plz#!kNcGwQhj|J`2DgD1&UC zE(O-WfkAHb&?H{7;j*B4j#n3krDB1`67sGUg2$K4K*viYrw1-%Q-{9bzm++r&11jn zi?Cwnhk78$JcXlm}*SueF($!#+{EZF3s=MB04>M8IbQj zF5NU>Iae0qKL1mU$kS#~E1;wYoQd8-cNx=KGQE8WU`8X|*R8%Tsd%l18VtTLT&1)Q_|?fI zgG&9Wdq&oV87XqLs_DWg7IcNm#JhokMRRwF+3<}*ts8Yvh92QfdOWt?X z29V#rkhTk=XOX|#znwkVgwXwN6E53ZEY>Vg8y`-h?%IKKexk>$l3%$!jnuYYPpq!A z5%Xi&STyyu)vHhO zU;|5$Q1_E36KX_E<{t(6(`FnZ0f=_D(7EE#2{N?e+atnw;nQJL%LdCosNG3r4orah z{H%IS;IO{1o0pT3NMa|*L^fcd{O1M8CM^B>gUMZWwq6GIe(S{O^?=V>eMz(0e4^&| zii=&RI%T10PKf3)(+N>L!6Z7Agn?U$;#))*&g0!8@ztnjPNFs;&e#uIOn_p-A)B5>rNBaMSi~YWLoJJLeKCdpz83jJf$YC3ADw zW_aw-(2|U7nPKs8$TbMLAhFBqAHe>Bb~T6odL`tEOUWvmhpMzZLHFhMY`%?J7Q%pF zVFvnZ9b)Gr|3ki$D?-O>twy{?84b!mm8bptB8X0-3H9?LA!92OGN(IJeJ*i&;{2g` z!p}|>JiANIJiGw;MaTl@wI`5UC>P(*UEJMH^6I>XFke_^ek}PJSfu#9iCzWhO)cK1r*cx952u5^aQUBN2h)R zVK+yE(pzk)09D!PCL*jv!-n>t%Zo}*$VaorZ(fLsGh_+BiCHvYm;i(JDETRfF$3B) z9)k^gy%(4(O_jVC)y+5q_yPTAH|%pE+d_;L z-fUN@d7^^XsnOm!e^rxIHDQdHTz#lxvrG>YkV5c;tP0WobQ+t>pFqCP^xo6+EtkI< zZ870=7vCBYJU&i%7qnQDKpUd{lG*h#C!N?4OeCbR%Of}rOrVT!|a4&9*3_(iaOWQ7GXa5Giw-u~<^v~B} zR^_!!AaQc_sx%-|Xd3k(SU2nP+ZI)`JKc#3iDuV$aREdp5)+^tdqi7HqNnta%JYz( zHP@dr_>X(h;BvlB*RL!XohHo>@{TTgPP`LL?|w zzrddi98XOkEKh5(eE##ROw%KG3(cn6SufI5*5w01Cy^4 zTwU}5o5y^ZdDO~zCP1m10woW|R3g%sO;l!jy}*^4Mt{$>V<#3Ehh>=9tnsQPk!cSZHr7PeoN zxrSBC!LK`wc8IVuNE4*CQU+KVhwTsJwST|s_-w>ZX7l_NSzqD(0Qg~qEOZ9xGPyhR z*M7)&-0^D24{Za1OX90>y%6b8nkRPAqCae;=sUSv|t1J$M@NYclGW-)SR4 z6p{}9+d5@Pq>$&z}9#hQUn zy-ZWa45AcIoxo0@cS<+f+bndPAqyP}ZXu&`)!N7=;DM^2 zN0U{)XUvaoX*~K`E7|`k)byNrp)=N-@rltqxre%IhlC&Hp4o5q@#V7`mD67NB}@;8 zhgJv2LKGSA7^F6uP#SKi|7^{S;l$&R8AH#y`@R9qNfS1;k~=$fAnGw;5R)JuC$}*; zHtS|#A{pauw8**Y3W>vvz1(I3?vj6@JIi5;>+QE&o>bG5^{#sJb398NZUpGoEPeV| zJ=RKuDWL`}?Ho_M9N_sbG=&r$mm)WAxT3A5Z_Y%de`??OLX1A3!YK%|dZ?B|-WnwI z88QJ9YI9X0wOIRa+h!6ya_^FIQK@C^{zRBD;#QxGsN3W;pxq5@3tnvyeUS-bvf_T>N62PU-2>Oeg-;H#dvg|BuCrU4H?Hobbp< zc0baqZ0(3{x=PWEsjuGJKKz&V#Zsr`OF36lcP2topp3OFVG;gYFI4$M(TOFx3&jNF zj)ln5VPRwphEScUYb2okRdsNc#9hlq{t*uOj z0o4A;;ix?|zMf+Jg99SWw;6jK_xvbCKVNCILPv!A=AlD-K}ne}blH3bGIv1XrR2v@ z5_%V9WEd?WKF_Q5Yj=R{sap=9T*Giv1~6Cf{7AUU%A%1U>%KT$0zo6hS$u$)nQFI0as_}Cg4y2k`4&yfk+ zt~Cq!^FgsXb%>&3yjvD21-V|04v$OH5(qlw3!M!TsxZGs&p9?n&H*|;J6mD9MkfXl(|^v_N0gEH(ZtwZ_QJKa z^YIHj44aMrH>aW}zzX3`vsKZwpR(-A(sCDD@Y%Js#iWUb>GN$NsegGt-4zdj+(<@< zUhd&_`Pvh-v*x%1O|y5;E?738+%7?t&y-%*9xr?CU4rF%JDL`ltEAQAYkU0$%M>tJ zjImsh?1Nenxe;T;XdKVyd&LPqhqEhrW?1L4SYx5GXZOW#)}no91Kkx*?M}xN=pOJ_ z=F%+ZXFk4OiVLdX#&g&p$2c*jYiPXKu0|9&j*ZrX48XTe+?v1{0kh%v3`Exy-b?Bb zPf<1aFrX2_?cIN=PQ1n=__P)pX-)yrl1XzK0cG4OF~LPXe$f~jN-bI z7@8n8Z-^F{^L%8W>P|irr;wRuyZFBC``zV_p~8!6=+2a$g7jcs@(_8hA}zGhXU_}t zSIA;lW0*7Gb*TmNL7;j_01=jzT$Ue2A1ggEL0SrUm>lQqP3?(heJ9?GRD3&=z1h1- z66_+e%;rLlK2d}u?A$I4NiV057>T_2R!2;4jJA$`B9uR5_ z55PWxECoi6k-8pF>}|@JM|#=(c!6(w%c2Ccyd~x}v&luSBs%?TrcN;d%D=(VbvRT% zm)y{ixiyVrcD6L*Mr6F|o8wkH^`OiWmj5H^Jp8Hf|1W+^Qb`&vA|oSZMpo8E+1akk z#l2-@ldP_BZ$#zF-h0oBo4wtJo4xn`gnNY|_ga_h-ru9|@1HpD_v^e~=R6JN@vsDp z%e+EHL+2CYY5s7sMrD<}(Yx_mPj|Pj>Mp@P1E}Ik|Zwwsrlu=0^pA$418> zqCwP*Sr)DHVLXOu?s!Ct~u-L|~=CmeJ=#`HuJ`qhKtUYbndVbzsO3)?3lNRM$LYy0-F;z<1c zo4S42uxb|7JxCHAF#Hf|Gg7d{>(g1A$^tw2wfyXk&+9a;PqJxY5oknIhEEKtod8NE$^K(oz-Bz)JB!y(TY7VWeev*dA*Rl*D|K|)(hq`0h_f(JOTQGzbN?+c< z5I4!2)MFmk$NuB-PUxG$H}7PLtLwUePmuddR9MJmGedtU`%$5}St`H0(nAnKff7Jv81XAz`!UJp>0@IDv`eVgFe zsAUq%)a}|S_7m2EA&d;r0YjmzwAapqCuIc7NRM%sAP))g`N%8UGpR=x72ZBLwRPc3 z-?IfD{Ajd)S$E^<+Ya67KIl!&GOk9Y(_3hqJZ^*2Kako$-pEi%hOQb4%vf2nNIZ4g zaT(`+y1j>kn63S|}^R`6$rR)NJ6=R*2C zecyP6K6gqx`sgyoekVL(_HED|FlCAkc&zMNg^-*uB*$g$2)L^FAe5$RY#2MD}9TLU7Fat|b!_CIW5%X<)54T0_pH$M1rxV>=DI848W z>%WAD=LAT*^O2+Jfb4l#OwHnuPbq?gGW(hS`2<&quBxV}{5B zA=OQo0z0G4JdfGnBmposC+359KxGB-m@dZA> zDv`NrNf5pr^oJ7=Ae4ZvgZKSWniyDq=}rfzXeYRnj6)eenT5F3j)3mZ!HXX4UHp*m z0p4Fp_e{AEekip6EiEDCW4yGg7|m}J-SwQhM`Fv%SV_xuAPMOG5?++ixdllQli(Ey zjui(uOaPy+ki(We3?cn9%bHU)u?5vG<>Df^jlSE}xYuSkgZUZK1|fg!vEJ1F2`m-o zc+%OCx_Mw$5KWau7iEZ)NyLbuyZoC;&vHY__daYeMx_l*rzLy@&&R39ZGk$48C5^Z z;o%0`U5FM#*ua4Kmm6^7*OCvwSKZiN^8n_{fc7xtWEqK=QMD!GE9PD{B=xN$Cxfls zM|IN>v(_YfOb5_jVa+CSGCR)H(U}@8BIZI|zV=wH?z#(4*7iBpxtMYmW6f@RY-={vGJMlYIOzg#Jqr0wDQ z+yZHC_E>&8Ae(oBKuX0_FS)@ondyK>ZeFAZ$I$C|K|j9Q&A+A^Q`crO`|~HNk96A7 ztnDk0UH}(wTCc9Va4c8DQ$>@vr?DPiuIMRgY>%}?=o}k{#vWtNPlZg#N0(SHQo`rT za49W=g37O*@9OTET-+%8j;^lNCzLz#AundY<^E$5CEx@)2~;?;s7!YSQxLw_{+_%+r~H9V=?gKU+qyvOq z;8`%Uaq0*q+MeY0*|vKcZ4qUEfNrs73yl?Ge6bv9BuYu5y?RG(@>bO%j@+lr?-;c6 zh?p7hJbZ@h@XN5(E*ZMKzL&<}c4ZY?KnF2k_Z_ok}~jP+E#)H2LE<)7>N9JcM_ zEg+=!e%==o)V}ihIq_SF%y<}fHU+xGrFKbYj_-EBZ)*&?OO+1Db|N*W`<>Yzk>~)j zzz+ksYWqj(Q0CE5O(PgxW0?E0bW zM#EK#9exqg>Zv@>>H$8a0|+08J-=3jCFp=`iQErDx0h6#PIMGI&3I#hDR7P|fy}mu~ z`6G{|u>YA)&+W6B()KGA)rO*mViUliBkE5`s+39`#daAw?Gc;jVT zA8PZs%3Ki8>t%h|8h3>nNXlqv(X;t{DCgE*0mkH{P zZ6#N5HaKld?akEMR7V2wOfPdv(jkl`d&iw;pnKwBJj=)bzGmk19^p+vIk|pQjeL@` zo6=9`g@06+3rXuvv9M{XcO>UTx{d~TfPc0%(kdt@iZ?M-fM}D|`NRNcwV`mKe&9kj z+i-T1X!i}SgRKC(_4sBGA9XE|kQ0|7Hyhm}Q9Lus>>uFcD|i?2V5VT>EPO5&mnHoc(gj)Wq*4)D-|IdLLuO}yh!H|n(Li2G5GWBR@~8UGAIa#2 zt-$f?#DJ9VLq@8tuPq#=G!O9b;=B~3P27N*x##(pQUd))qQiuQ+}F01Nu2;jFv`&! z_$JG4vyGK_t)NQjnZL#7aPA75VE?zzzRB@Kw5?pq4u z=J9YRWDw10iw?-_vL;F7)Rf34corEu32$k32aVd`zZQB4g$PY}e`~4KqC~GpjQ{qJ z;pF_pG}NCz*|8hnN<9`x0uTw&2dTJU#urq*!`j7dV_9HX{Y`l z%fAHE6<{bUVPpU+Nx5Z%8kD$pe}3D%i6!K49hm7-_+O1N`TjBT4d!R3&A_1oA2}6i zLiWxQ@lr~OoBHFibg0M0{J})$-K$ldI9u=uh=M+{Oof;)6c*oL*sRW6|A0ta?xC^~Hi(+>Z!&@tDvW<-}-36&DnLbGllNqYS~7-9f2G%erKdgsZ6mO+&!W$^QRDdxWTv$+8au z4ZpOHG2Lf~n*)DzbD0}c-P!_fuJ+Zx)=X3@93PR)bP z6fqa6v+Yf!>X^=<9E>f-Zf^6({UNL9sHe{-0|6ww6_)6nv|rxP=}p>(G{-aynTcfn zBCRjSf0{)9E^+-#dtnRy$Hi1++IH&-nAxuvO5?R1zf?D#m*Y8>`SZiKjq>{llbmOy9&$!|B*7H&3H~nS)$G|1Mudoicf-x%)k6RC6Xgk3gc6XE^JtrGx=TM89rDj z6-{t&mZ!ldc2%8hlrI&?Tu$k_&C08SnFK1!`9QwI$!K1S8Gq2=_Sr3RgTz5K{%7ad zyvdb?9*+Bi3A&(mVhQ8}8CmH)8_nx)+M3G5j2b7cf1?0#~X zMO>IIi1Pc`fXHub#7avi++O$P(_~;|`%77^;G?d3wU*IbP{V3ciQ?W7(6gbOK@U7G znCOZW&kxi!)H+$GT6MUfYWpxS$ZU`f@EDMFvX184nXX%>4^J?5gca@Sk2-=Ywmzgr< z%Cna;VrGGbliHX&ay(4m_gH;x!S9d=iikP6Cqa^0OR#ZwM*z?}+$M?@EjkRQyf)?WO{g;sI?`MhYpeYFZ@RogV>3A=l@IZUzjM)OTZ zM2OIv%!hb*9*F!`)$Q9#e-q4abZTa3y18l3)%@P@xUr}&7*aH1yc!G z5k9Rv7vg&25AkNKly55|JkCQ%-Dx4R<9(H-;*;73{#KM%%nvRcXQEL_=&p=0)I-_Y zLYRBdhvKvuw%1|rSPd;pzLc(&udSiGl<9!%99$=v4oF53z=(_T4bf(~B9q=lCbhDx z_gSw$8Jks9V=5IrWg^vUwv#?2dv~;%$S+2zc1RUYcFjfUr>cm`(_<-N^uj&vPiYsXo$&V)H?gI0c~WuLCLN`){{7WLn-!8&@VT1^M4$v&gOTfizm3 z!Qr|W)5q-Q?|WM$nQe>cvz|AivEi+DGegqcD;A4`@O6e>V{MJ<5U@jErVJA-DI1tYgcLceugh!f;G!qrV5#Dc#QKHH zH*Tl$x}nnbzN512u?vRG#=5eKAguvFO!DWLKhO=XJFd&ls&`p3I3FGroEUOZ^kB$x z!ji;C@SHkKxpR_>Eec{kG8Ifo)VKvid{s?bb=$w|1HsTtkw_b)6XN9pSkJUrwE435 zyJ)>T1L{Aot*x?|0-V$McOyh^h>k#Ctx@>KhFWY9wf z;G3=#itt7P29Y)RXv@>LXe*%U@Ao_7K?%)Qg8FXyoqs-A@vA@B9-B&i7brsTPVU$& zo1N`DFnnYeKJxUW8Gj@5C_C zkn)A~&U=xAZ(*&U!ZplIZNZy-WlCb&u6&H=g2PPxhf4=qXUR^KvHAgpz(+Si-A^Ca17TXEw1f8B0(ZLYVbKvL~P_J zL;VAR_Eqc@PMYFwH0IY#h9y03>T+*uT&-fW=y)Yl?rYfU#RP@)07EczfZ_{EAA3pu zy+~pjGjTgc`!G0OGyJ2|OUs&`f-4%!jsFS*=4n|f9ay~DX-d*TH~(avRVn>NKpDq})paKO6eggtoT1i%JKXpg<{{&^$3Tb!UY!mwxn&C` z@8*D%v_BB51xeqQhNe!R@HGCer5fCsc>ge!=iQE*iUJS96w+CFkru3B{u(!cCTI_&iE zFQb6KF^z@>nzbF14HeIQw$cTjW4yk0Ei|aVM$Nh1m;eT`CZVLMwZW9&8l;0ebw}dv z*gZ5!9%ComeDn5|MplYWV*uXDnYx<$P9f6|B;m$U*T4=vmr5M7JwM`&;uifeIm@}p zjjDQp#;G^{T1)M}O#H%M0lYM8pgYjwnoi5sy#b(X@uihc<&#Yk9-fFJ$Kg^ASF%Crxt^W^kEzYX7GeuQ9C&>ZT9tS| zLH*cR`dftOAm3H+Z+JW%kkOV#T*(TVAQ--W2_J3iRtnAV{urdfW5=086EQOg_=E1M zqXV+j-VO2^l5*0hl}|HwHl9sZfBN=V>~1Vk*yxal zTSFvw92*-_x4IwryJO%H`cZS?;L+oWR=gQ@#8|~m2~J(z&m=~99dhMRVs9(PEq`T8 z{F}TZYJ2yEaR_)PFA%cky^mD>w{wg`Y#!NzqNQ9xMO$@Z$$?r=Xd>2SRIX3?9v^k z42OkXP)0rhA*yt_*PUd$E%3h45I{kQksd;QiCr)+Fq*Wva0-9J^r*Z3Q)Fi zclII=+Z}KMMMN7V_mwZ@DeHm6MaOVprqW0KvDxt8OcTBk@Sl~8Ojr^zPHNEw=BkzS ztS$0qOUz1@eb~nmEgg6f5CP8&S*WDg$tp`pF7)^pt{C3|S-p?QuLOM6K3G*a36X|G zuTbn!gQ!76DYAyFbF2i{C|)QLodkRKm806&uo-VvOw%N5e;_`<$?Pn1hSdApPxQGp z5*T_+ss;lK7+#^;OOG+*sEr#S+5J4Vj`^7Q_&IGUiFZk`TSALZ9Rpr!4i_K5J-{DP z!@9KRh!m7`NC_pBQ2J?hStk$?Fg9wykCrjH`&S(D0w;`zdutTh{F0t?H&rj(Sye>V z3x*b6@q%W(F2}>{Hg<>3Lts#6lBr)#?dCr9zWSLfKGs=3q=}U+3VWMsTm6iOm6Rtjxm2w z!#N6r(@?d7;B|!$`)@z})jn>`Hv_WbL`@DCSF3_pC0OMsQUjw%d+5{odG*A_T3KdA z^~>cux^#e2HaG&|J)G+`MqMLHIFn8PIFPQ+d)93Z=bx{CFWt|AdeO!jWRD#&9uc#_ z@zcC1QbU<6?OD|}b^_-&j$Ut0>^WN+W%)`rF8cjJ<2*ugE;O?zu9SarZ9Z4>6e#{* zVYLnk&04?K^>4L3b5dJ1i|1_FU$-B)du?axPK62XF1m}8z?@|EGxYvQGZx9(0M8Yj zU?^s$`yZ&dH%`P&4NM1Q$EU&S94=-Pf6vYfUNLmx7=I1HbvycY4W27K#_U|`SgHB7 zl4s&NUz-XN-eTu?t;MV=bh1^eEo`)S!Q#b^{0cCLl_+2LVK_C{U{d0aOa1Jbo1J=3 z|5Ca^&$z>>(C^jGG++psf*}@B^fs7PeXS8`q+tWciIuIPe#R6BYr$8b=L~-U}$VX2V{S0pFllw^c(eG!O-4@ z*|N3BKdZahQ~a7?@3)p&3+ABAc*0@KgIM=K^H*&Y#QSr2^{y?L>`Yy|mO-p$GMcuh z&OBg)WHz2nCEIJR+cf-saR-+ytax;{8E*|DhQ5#7;67!&@9xvG(*YZ*AtkEo(2MnV!oNS6kmGhxo#&Ne^EKsKeyCs;gi2P*1|RLN2j;C) z=SVN}P$;+2K7Vf+T?UN#cuIk3FIR|uN4eOKhI=iQAd9AE5^q=_Z;`+FbOqZ);_jZ# zb^YE-BzR$G1sGEQKQrFRn<5cRgbzxI%r7pxKo1t1N7V-zbOwE2@EBU~e8F0-&DvwD z{Z<}tB7G)?aRh6|rK}L)Cng_G*&JriUpf6;DtcTGqYgK{BU`c#WVtINWEW-ya!FeX z#nnD{wt#H0ZyBE-6jUq#k&*5J0poLjn@v8yx`JN0q{Wa=F$acBc%%3dy#0r~4Xx!Q z)JVFKOT{RT-EOyRT%Rh7G~^v>Sih%)7EH6;TDB&sPC{lfBfS1zc45Vwlc{n%baLtC zlh}il(gDMql#_n=z;Q>t?2dQC-2Aw=>80Y@AD834Odh;w-CO`VanN2)O0bi)TA#Ld zIC&d^fmiSuE)83i|1tgLQvu6)a%fZAC;05hPgBR-QuVf$44&wgCMvap7xQAuRm*jr zf&b=F*C9m3anKh~MrLDJ-hk+hbGA{O-Bc#pP!Gk}v zii1;w;HYW}=|BB*U-+&!C}N09-ga%Mp%{jKvdFX_tENGpC@RPqdx>-6(4?N7>E&^V zw51pw@Yr-C#zC(xdH@M_Mm@B%U6@}m!Z~NepErg|-D(xc@46iCPP+3vWMM3XH4_59X672$?CFzsZ znK5&Pg0&Uawk*exe34SozsAEcPvF#!@s1(l?gP!4parN+@nx}FUWIz+>yvjZcwy^T zR2?I}TMYi{qzHG8uwH7%g$n9`wsH~BuuHK67@Fcid*z3)xw z^%nUqm3HsqgoVQSrz~KwK;Df#M%_dHRjmHh+IWM6WRK2-;Qd2#x4l*QW0e}H788Y8 z&iE&~Hq;&ZxoJvpRI<~tvn^))QflM{_wIVJ+?6^F)jf;m5oFMKNIhX=T4jtscAmIo z{aczI{RzN(01&==TcATL&<>oor7?!-J|G|}1D3n5)L_u^3R zlE_VBPfnI#``JI3S-=i}LF)}6r!@zq4M zqxxb>SeaVoZFfx;jRhcLCiP#e-^Xp)o>cR$sIO%6x}kic_shGg)_3i%e#~>J+>gn6 z$wbWt?_miX1hs#`sa+zs@x$%-&L0)LMKXP_K2D@G$e5-?0etq4!L+e}7l-P2YdRn) zWU3hH><-N%$~P)8Pm;x_+f+E@u1G{YKR-0Db8QDRo_DEQ9()kmm4P5A2j>k+=@H$} z$=2cdB?K4VSGc70d|Y=C`x|Ds2MKovW$aRZ$^J2)WIwUE+4t)GwAtt7Vsjrc(@wUY zq13FMvM$jRK5`z}%UA^AuH8lYk=a&47);RiT$@{s)yYrlF2~~a4@*T8X)lnzo4H1l zA?*Y@KtWxL*vR~tRnyGur^A&ZPBj#zK0qLxFdE zdCkRogzy4_&p)A&z(1=2c-RPWcQ7O5j8b1j5z|ZQNu{>FKH~b6I&3$%$sGz?_Imrk z?J>jpWR2+Q$Uwlk>X&s4GangKoMvyIgEn}Mz6S1{_*hC+xF#&y$Y;STGgc2Fg)Od*>AzMD+g?NbPqFePmYo!niJDmqX@iE;Af%F4()^4E zSvdatV9@a}IVA?M*n7S7l*$N>qe%RA!R${W+!n}Xq|61KQE#YNJLW>L&A>~sABVF1 z20Th-g1yKA);r+06b91n5H2OxbbLq3t9rqr!NvtEaB%se;+&<`S7qeiTYKjk8GRuA zH0x>qxJRgA?M2(|q@mI*i^#&UPEB{W9fT39hKTGF&a;wxEtms7Js*eCClz*6UNsN4 zzOaF{E?cU0_WiqMucu^Hxi0&UcC4*ON!aH-wG&-U(}tTpl99R>nH+dG0)1{dSr%`V zMm;?u>vbGajITvi)*9Lv>lVIzex>cNWmF&U^QYop1aH_71A|Ln6CWCwTIAK8uf6bZ z*t=?@U{BsZIQpS2!T|JRlp zjIF8}*?MsK$2(U+0raKP(^ag+IA1R+Gl;RKw1VM#S!GZ`Uexd}?c_zx{`sH4p0G8 zGU$NEc7(eWK|-~T9mz&M>7ds#*oSr2pz-<7CFnCgV$+FR=5v z#|C>#FK=SREq98NYlqzGNZM?`3(YrZdie{$(8rz>%S@IA)9sD@MB}nZ?f3FGItIa? zhOd1AT$wnG!=t;rsKfPXWS3v@NTY?SdCN57S7$7gLA~~irP(TO(4+?z1^om*Ov?)i zdr{*m-(~lxGpD*%&y(;eL8&nHqH1RxyLTDzMVI5K9~~gw=UKYtc`zVm_xN{lV@_+2 z@2x2V{bKK)%Clq47b;~v=h_OX-iC@L<&;hKq*B`~ACw+vATK69tmCcJry@mwA#?y8 zptf4?Eki+BeV)n)#2_3|wI_$VU10ao z!;kXYS{5-8kxICbohrSk+iOv~)vJy--<3x`cbEAEv&|$9IcAb}h*Gk;W^w1m&##VF z(5%*d;C_NYT{%#nFtwZdC5?=;$+R46Fi>pRfp~T+ee2b@qCM1oS?>({(Y~r(Jx_-^ zFtbLTFjW2BP%ZNWm@>F!`QkiProUedZv`+#WzDW)^SwgofC@^F;Y1mUn5|ajrFfYq z%J^ygjqiR{Z(qCZGXNb72r=?UL+oA7WW)IfV=z=judsl^2A8i$qeK2G@(t!&aXa-p z*?EJvwxs83P$0o6vM6ff#mEez@PL7z>GX?DK|UV#4}#+9RD{SIR10YQZ0_S*;akV? zDD$!IcgFQ1H*iLXo#QY}sSze7v)?-RJ`u zrUP>5fD|t84TUt3iT#ty4eY)ip0+`$6JNT6^o{2N%^(Y@qC<^wf(p57B#Cv1v8O`; zl+&O20rIiXr*D-vJD&*ddBH@d=F>6XKr$-$4bZ@uug0iKwEsPfGvlhl)W?c+1L>Pj zQBzQJ0=hQTz>Y)*5Lc+{7c(%!Hz%w}f2u3f*WDB_dv$w;<@P`f9Uyi+@L!cMS#A>A zux9M6XK87bV=`q>=b_F$5j%;8srr|>{#}70;hRa!t^yI?@0^cFkr={2lRmFCKLg*- z8d|oYVn!XOItX`^K-CyM<{rUUGB?$so@Rsk`+J-iJQYjelSiBmv(Im2d*XC9+w7?1 zISqk^3MIVMeM#p1YpWe;3&2oi%DYO*PjaC;x(Dp8g0Vj%TsWIKkJCru;$lUQ>uImK z!OfF&fRHeCZbF=Ar;`;6)p!&BIVkq}ZIhj+9{84=L(YF!u2?ZQ6gf#Am%{5u#&nz< z#7q(M#1khCe0b`EFoF*kWmH0pmN;jC$Z(q5=5>Vg(6ZIrw8Y!|PMIb?lm;a2l@Gg-!9U*_`nfs*3ZKFdj=R>y^*QXxjrJg zL;>lyD>bzjXvqGkf>{eUKH01g!H__g_qbQFNh$@D4C2UewT@*AIVK$HA}wHTRI(}4 zr&@c};94GY%7RyEOXVY)k2$Hxi1ae6>Q~c6jj|P^wy;#WH6i)ol1?RmVa4CH@3naA z52^l7%3b(YE6(@V!;I26o|0sK)XO}1Abb3nt$=T+OY{gUOLG}OmAlScesGEQz_L`g zr!m{4zRtc)y>W>bC=vo@q-;}~9scnyoP3Eq?nmQ`gWTZ{tA$b(t=}%jSANqj`(1!F zMR)m7Psk;7zy@oIBejjhQvIjSPBzhQ>&kms^ps;*iT&F1iOZ~|Vr}mifQ~3~Lgp4~ z_#qT&Jwk|n^QXaP2x9QgD!z8(RUV`=M7s)b332?nf_oJ^>Ta_w*8Zboyyofz{C?&t zJD($GU%qN&NYE-ah7OQMNtdCdHUoJfW8PB44)WC?mCw&2xfHJ^iW#hRUy5SZ=c%>$ zPJT!7)}R)X&U*4f-iiEDJ{9w8&9&-cd!ENlhD9_gg)Jx7h*CJnPskTfX|E0XK&fpC z#5)ga#;7YrYRzRgGo zRZQni5pP`SK^|i!RZ5Shaj8mvH1~uWxwOyC9BS#uF_WW$`rU7$m>)7UiKe(DEK$UC zTvd+V%$O|b9(lg%dB;e^Uq38-a78t-tV#6vJ9(oQx=SS_BQ4uVyoEXm8>tT@y=?y; z01+x|X7^oIyQWrZMEAY_U5c}C4=)>x{p-XZdbKFqnn(&z$KBH5U_Xa)wJo3Z5<_>X zQ?scX6`(woKc>U zIu3or<)v{k9kYcU_=i-ff0p;G;o}dFJK{H_*D-ee7A*byYMhn6SXU28CwI}?i3%`P zv&b$PjeofDH*v(K`bq@*Hx^^<2BnxmU+cLuQ*QJ}Vv69S;{;BX7?k53@F&`91XCZ< zy?M;5W0$!p&q1}TcUojv|#ARyzuduP`o2y_lj(WO6inFD5k>(~Fk8{5rFE6Wp>n(ny zU$XkCGN((}yKy2$<(lXZf821V6d5~&^q{6q$d;_VC|^o$O;0V}x>8)36BfjpAuesH zLl}_&tsg{YL*LkG6NJQq-xPWIYKP5z*|O)4X18Ij`mKhwxtFH>CC6QQP?^_Z3C6Zh~)}=S)$J&Hi z$l`oSXU$u|PF+=ms_5Tvw8eyEki^@H=)z=Yys`P#i%Mau>uSVBx3VC{dOO^pi-MYi#KzP zTEtXdefYM~ntLU_@HEpN>o_UgOu|oR5IW`OEQwAxdbkQP04=)=Nd9&U9%i$_%mW&L z=wW)%ZN<$j9OXlL_hibh1JnT0CMwd9AAIEOTifi&m(=R9eal21nIrH^Kg-JY_ht-n z=S7WZikNNVhrj9&?qv8tUPh2pS+!HuW}?lO!~j}zph1#Rouzg5U_C?{Qiya!aO-!4 z6WuSJapxEf{cH*Hagsb|61?LQh=-@sG)R?5ZR>4NGA_L>oR--(?X}zRwDF$adaQkI zJ@D*fH0$!~Q(L`K(G$!dlowlxurViBrr>f#(aimG@ zodgNU=7}sLmV^l9Hlg}NJr4BA*bXpG{kI<5DF)rCFwB>kxrEJXsJ79uDkiBe=WH2> zKjU&C>WThLwtQQnXpXZ5|H=&^)ua#)-n)r z5E6BJZBM2JZ<}bS+Ps41>_Ku62c^~QpRpP}Gz?U%e^1EK!q?ks?Dbwhdv_<>tcX&v01Sc<8B9CrTuG$^>Dem2LZ zOf#v{BFiKV{u+SwaqI~=b;s&3UIqZ_FPFc_qWz-%Ui`x*x z-R}%F)}^kHv^o+HSZ$Ow?@b2%4>1}AizfG1N=5hc2Kk10bVzXnF&iVQpb7`4tGsQ6 z@D zvoAOpIPY{A=1P=pvRQCUR=FQ#4tyfTUxbR_N zQaV@whU8ETODG0pRwy|zRl;vYzkkE@&9mOY3(KE00}^FH&dGIo(UjJ!U^EQ{byu?_ zd#BI=k5y`c-IkW>u1AOQuLGow8e+9gu8qYt8Xg@gVzxs~#XHuVT#3=b#xVKN{_wv; z+d16S)etHD#Ap!S04G%7O+ZPY z>y&OVMltO& zmw%Xq-oRhAkErLNT*;hFB607p0Fgd4OYN0Rz8u_Y?@C_ke*1^!*4c9R0bRE4M&(=; zbn@op3=r9@AT|1A(kWy8Q(cQdnC|9JbcX?nG)|isByT}_F-1H#7e;+PXa3jmUgOCq zV~IT8{Pa}W-A)@MSJ>m@@9iz{^%dl33{!EFU-`;IWyp6R(u)ol)}eVre|0%etHiB` zUK*)#YXiKeIeW4HwbHdv1>^^8s6$gs0`PE0)bM?zr5%O4uY!;vbTZZO%BGL;yTv1I zr$>wg^SUB&XWUm?@B!mSj-RT5Z3pR-g5`FVHU7ep{ElK>F`@HO*TNp?M+ztAD@gIc zrjz%`ppBHXh${Y5W|2B=x$1n=D?e-|Mi)8?`NxM<^BDsfQEW`>6L{x?vk>+cz^j8} zK#|$kiy^g;K01ILZkhO<;(LMDzFFEm_2%Mcz>!S%i#CHMQVLg4ke_H*9X+m)?;K& z`to9imKwD0wpZn17c6D(sITVpZU950*G~=NdZh-=c+b{3(yqIH-B~R-FoaBc-QX>W z1Lp4-iJmC;skw|(Hw$nXD)0ZEKr~o>Oj^uWL%jzfQ9L?EEf`MHH5@CEt?m1Km`~BH z@|+;oA0O@;0-57QF=9r?m0CW$)lS4b$$1y{-uVljvd~XPtYRqBbFttO}k}_@mfwUC(rP2B-(xxI&F{DnZ&If~j)c3r{NZCpQAU%sR$Ra_!zs`W) zy{$O~Iy_~nJ_S$e^uP7T!*rYpb0{G{rU{U^0CV}m;zCn9OL}FJ7*2%g(AsL(xbBWU zwg&Y8C0|VTiY%L@IjSGRUbcMHQ{iXdp9OGMYw4@)tI6T5|D2^Xe6ik~sH&rm;V%E~^-uA-?WA7fw}L6WQ|SUSe&*CzmP0 zL<#O;UYo|L2BQc5l~v=kbssHJE6#8Q9RPNxbik_Lw#?7TUWN zV8|XF@OYD=*@jI45uASM?jb&CBzPCJzx|uS@>#s{!K$%QiX0y9fdo^!ddN(qZEbQ{ znxtB-vwvCl&iZI-;zMrl>kBu~9Y>Ik52S4kJ9E=kX^ksND)o6w+UGj0NG*;VDv=>5A^#8O~dlT-Z}n#O&7(DH-Jj`wEk~t9|>5 zVvV0EOYysZNib2}gY;xNXUow6LuZ1+dGWN@i4tN%mI=DzSP8aqgU6tDKc=dWqMUEC z!FzBbBxktDsTeiPh8Tigydg1}S*(*$C73C7Q;!>6&s$oUWse<=1Gyg|VI)|zccE^R z5>?NAyI=67wdv*c$LG|Y3f!oBmSPkL#ZzQMB=a{N@JNOF#*NGAT`25QwyY(-m$x$P z)J#Gh)H{p$Eq2iQ{;u3XZ^{iP6~R01X2vLoo?m#FGR29=jeQjN@Yl@qlT3fg`$eS? z4R*Qob9Uv6BYc1d3qZ$Q>sUqEUV-US4yDq(m+{e?;T(ZZMYqNyd=~wbd0Q~+E0iIF zC{{v4=7@6Odxcx!n{YGT+t!Y$0PsiB#>EIW)d74F`N4m{teX{SrH z*5wJ4fR_P7FK@(LMHEgoY^TO}N*4LOUM+YR&KWve$)Pb?y>BdKL-Cre)9}2R@3O5Y z2IV=;`-#R$pg?jsS(?#Sbpg^=B>4xg!&!FK?Lh)CE6{O05p=AX~59_UYS?1EVCEMz~aA@WQ=a>Aoi)RU@ zO{ex1JWKDB*58#QIt0Yi_%=C)W55BvoAKtMc#GGZ7&2&3&ENFIpbwNtLdLX2+T(&V zCb^dM*kiN@dbD&$Qng&*fF9U9LEb@K%19x}^KA@#h zz*R0v&yM~*O8lTDk9YJ_h-rs zu&x$%+0V`5P2*Rz>Kb-SrF;Gf1l1>) zf`Cv<)uGF-X%FKi3%<*)D^q|4TPLLMaJ1vyh-+YR$C1#(#lt%6TlpI8(6dM`yeV_$ zJI5*(#296yM8-7IM=0nNdGq~lw+-VTQ!k&@Sy!j4>t!ApTrKf^Y^LP9f|(t=(KmYo zuZr(c20n7FSy4a0!_=Iakw5LRkD3@YQ;`Kn1tx*Fs!4Ay4mDz~GM0fpjFRH7Xl|-B zB~koB+QVo$6X(f4&)M_`Awxy1MM8oF$QQ6iO=OL zm;#()=u-p;th6tx7^yWJop|{4O`RNB1%kq$H zB{CCL={<+Y+^-VsUM!@U3F9YUU!i(=mg=yxa~)kkc9jhg=jlen>2#x!9%mosBW0)c z-F<@&NI+1+*653r*Qx^Oe^s_wRLC*~L8i2kgfHGru_>M%Z@KY&z}2uP?Y$uYW6A@; z+w9Q$1GRl{xp1>7ZQOc&ar?W0&?2X=U%Yw3MzQ6^+ga4b7B$-g(@yO+2tTUtk zazuw=UiB`O-hTC6(gg|k0nIRoRgB#VrUQIE0Bm4n9Cq5jyrccEM7+U6e2hV|A z)n{xh?;uN5QA0Ys=F>tn;MUms=IXg&q(knU9Vh#`Q;09asGBPRR5(UfMfz!Naa;R5 z%}HHYOnvcZ_siIbP?Gx9R|2&Oooe0NWqcM$k8BSih$XO;bDZezW2J2fVyw-w0Fq;j z)n8F8Kwg;8K%h}>*O>4x1=$M*+^Pfo;5YYI)bGEHZF+iuhgFQ#IBh!FMZL60YaTc8 z+3fFz<^aNf!N+yHWrCn7>)^CvV@B8HV@8z+w^Uuk<+fK2>G#uj-xxPu_?WU1CA+JN zcWfEU36=BnS9dP(lCbpt-gf`x2Z4U6Ql^c$cM!B_8E)&N8%4_$Eixibxqp0rejb0T z-23-vlPdD$x_DNpuoe{fFq{R*$ya<4_@tg)ZK5-uIY;jCG?M%Mv1%*mTKTpg5+{z( z(cX?pq3Gz-BA7v|R<1S&I}kH}`$ceMV4!x|p=W-k?S_@CmHc9<4i^q0NHe}sm#WrwNk!_8@OJ-G(bzM;;rsfs5*Ulcj(^8! zO;(ILyCxY7hpcyG^w#_2zl>-*$NQ!6&C}yMEWpV+`{pVY4Ll5t7!6!@qN9g8EJDb6jsQ6m%P23sUe@=3w3w;)r)?mBZQFWkAP6$~c>&&ZtC z#?xC_fLs;aKWjY>3kb?~ffv+#SEl76oYN)f_r{)-do ziNk_+!8%MREV7gOd1oN%M4yvJp3;n5k_~m-TC&S%|4jA9MSJ0>=jK87k^fA*6tJ=* zJ)Aa(DtZlUtuJ37oa@>qtXp1r>m=R)pZI=9f{sRFJsG{!?Hck9eSig!j4XidE5@|N zhVkbGvIXmzgKKwgNPqi#vwt`6x5~d=%>rX*eBQ;GAf#2^`ce5t-;lBR?Yqxgv{GJj z^K`z2(8d@fM4CTBjgb(l5GfelzS^3b@D6^c!s*K<{wx_sY6bPM04iXK77c1(R@b`6 zjlp_4)HwNc71Bl`t<+X;NJG`#3X$%I#AdJng%23(EsO)IoXcnadE0Tt%E^5>Q<`V` zqR}-@yWDR{R>jS`^T5bQl-3Tc-OQd60Vh@8rNIoGUA@#HWmStdk+A&yjOS{Pj{XHC zwwSh5`9^^MYCeVivx(0@uy3*Cfp9Vif0eqjarjun-N|(IZAQt0oroS5kSdGBM$(ih zkFvT5Yo}O%wf`FOob9LOJ+h;CUrZPT82{2h2Yp3kA+dpsVwxnFhGagOMo2WT>ekf3 z2$zO->o3m9+OD*xhEhK1ifLv8fzm)P1TC2bpe1)?9%2gB0#$<&ow}yWy4sEJI0vF% zJC@8>tC5B^W1PcoqL*X#=|PzJjnGL?XS5Lgn&U>-J<#S+o++xuNHXw98u)a!fI$x< zjlg@UsIdx#U;A_YQoC<`DS$*!Zb5@$3G>I z+ziT-la05b716fyz$h4FC5ME~psSJUwMHB251o$8{m_)u8CacL3ZyULd7rFjO$qK6~;8LqROfeB4fTWw8IXj(|Gou*!}Yy2xvcoo3VvDioJS zDwaNRcQaR=)adioEP6Iy$_0#U%YJP50=*EKutkc_X~{n|+4AP?OTi-Mw{;iDE4n*% zd000r=U(xM$FWFKm|rN{@pRvPRpc5SnNI}5qY$GO>yy3oW%8LZsX>9+9cc~u($A?v zsaGewhm+Qk8Sksw@vxXk6iv`_s6NDucCu-y{bS?)_UbFHs;V!#cE5c4R}a>aB`m<& z6;)>zK%42c0nYE(!`8aq_9TkA`}liky?=WLK1$Bi(NZ?$UjC z+S&1A##E}cj#-f1+4*tmsvD*3G2}2ru`C2Z9^rBH5I;0C3sEx?QQT$kC7m<;ie3{P z6V>|qQ?U32xuD&up}yA0+Zn~)BQ2}P0%W;?O;eSjX)M6#6FSmZ`-2jh|Fzejx!K$Y z*+Y{;5Bg5P-rlP6WHSM4LskyKn38oCASYbcF|VFMB%3+^ZSl4nAJN$pi>g)UVq#&>QMucU}hI;Ggb+3kHqN^F z|BQu#J$CKF$XL)bS|T|i@9>f>6_@RmV$?aSXie%ky#ajmZk&e{FUHeTv2^!O4O+|WnYg;CbjdaPU-`JDg# z^!CkZ55Gi+imdDGYcFl=-9Thh3IRnJl%{^kTYIf&{p&lg@5R8^n|z5c(4q3*ISBp0 z7z-94&xRQonRcJi7gz3j!#>ASw<-05(?S}rOgr~w9)9%46?Hre0uS(OWLnJALo6mt zPj!9LiOQjEx6HKY+k^2k&+`*JGhB}w1YVfwbaYL=7A z&`)j-<6YggOj~;3Fn`C(w8#SL&mr`Q$tNF?3obc_%~@Z^OE{Lgg8q*et3_rbZqof@ z2I((2pInlzILrCC*3Nhghh5*Y@eYDw3E;x6Ey}F`?I!m(1VObvoB6p9CoakdB?rnq zO($nYIECWfY-<-I(PQw((~O!Lq4G9t)9ZIh`T`|qB<`9PO`)^VvP;j)fL>83a%E=; zQ*VLkO!u;%RFgYR;y)|r@Z;4-Lj|W@?#_&PU?k)ZIj4{XxKXE#15@}*4${}vF^V;j zkDpfb;#Hbdkajh8RrEW_pkA;nPQ9z1ZbWf)46DE1@cgi7(qYj+;D@nsg2^q7qk2b| z=jKmWFq1GfZzlvrz5}u~JFV2b&avb)FyF$>$5U&hZ;wH`W0D@bzxp?}PLXuVIo!Nz9({Dv6>)Tv*H2^c;3{d~FN(G5YQJEkipgR%%66(qN0O-2dcJFptv-a4A~ zEw)zEsQq>4H{L;sL4r}FbHGx~Z+3>AKc+32NPM_%B-osDPVUsSQMZ7V$frgq+%R(0 ziZMcMMc4X$dR#2vo-&oq0BvNz!?Rk_69=2w)0K*zz zoXsk&ElnSm*63)6-cb`;smq~m4hR}hVT+3?HveIy_olP5jAbt6aqbK~F9u&u2K^;6 zgE^dO7{+R1LE_rw^Z*h<*4CnBI&=!iKBtgx5uHOei`KeEQuJ(+U#ykd`MYp*!r5RzYr0amlYT8Y7uQri<3?4ZwO3B;DANI{zXFG@|m)@&zQZeQ_ zh{7aFAiGe*{7WuPGYkw^tkf2VCJ?hnnazg5O>*8#Y(SGTeZzh=-qCZx z2}cgkRa;i}{h?Z4yaeHw=1{-S!8lN8{3XdWruk5+|V8Tm+?tFoT7R zr+z8Mk0z}Zh1!tEjp>j8j@PSNjyUuOlnj>Yj8qbs#dSLqwm9?6PDAFPj#Opt6_`srz-0X{~Q7 z=i^S4XYs0?mF<2yS$q>szC5LdpyR=G{E@Kzt&fqPDU%w{uK)Fq|Aik=ozIn@I%R0}@76{0JAPSf$HUG121EH5sRo3R}j|I6P0!1OF+ZOM*i zkeJ_s-Vt}jE1Nmz`P);Be>PF?D1Gja5`&BVMY>}|7!1bHKlMIB1lm0rS8#7SY@lvC z?O;Wc(?~*;9dr)i`;V08pcxVJ<-w&0Wk%n4cWatJhf(@7Nz%0GQ(cDL(F!IHryBN# z1sDS>`Dz-G8Mc)uI05*je7d6gNtg7?F!%CpuE&r)iwe3=P$e8X2Y2+2(EGeLao72! zWLpn%#B^s@#Hh^{iPJ#HvH&u_w%lo6@oSET@f0l<;D-Lm3|T4nj;AZhkT%1Gm+s9O zjPwXREDCPmcX@~6s;@sSVWiIAGNqWxdv^%QX(lStE9EhjT`s?g(;@#ZZE1nTl_@} zvjBy*G{ZJ}#u~3O0lkp$=3d9zyHUTR#hV%2?ExuJ8?K=K*hc8=pP2*}VCrz5-=X1b zi87Ga^_4Dr(Mb@UgQnq%qKSN79s_4}9QFrQ_)e_PJ-__ptk>kvH!q7dN*_L@f4RN5 zA#ncWMjH!o6HG^h$kB15OwVWY`t^%TW+%!!g(A;xyI_3dGhPS&b8Dcfe1bXIa|p@J zkTV7BpXIKd8L0hDS{MuF{ei5S+pjxNc5(VyE9iay3J38Sg;$VgQQ59|WiqXwkFnz6 z^$;G#sgyMb>J`yqTyE>HxqO$!d6#)TC%JfHRo3*Xr|9Fu>YHiI zO^!;?5seuXP1EfkW}3B4H7MRA1WEJ~;`znWRSZ)sHUKt8b1NpBNNfbnl8h+Y4;*Cy zl$jnrXWxC^CRh?x+sk~*jQBjuBYi$T%=xwdR~_%xNvG?gNhyksesgC%Txo8Fzjfbv zoT3KZ9(C<^0`a~KQxiZ>?4w3M?!;7aPxXu2o;yM8nLG)_tRaSbg6$+ zx6b!V(d~j4(LoKMV`W3v!qW`*M${0D1DxCDg%6zUmjNQt=-E3jgG@CuO96J^R zdF4a%n}|F(oH-86X$&XP z^an)FfH1$!=-?zdJLROiE(afVAAH)4^C2K>8=8U0h9m%Nuk zkT=8X4M*joTMHiUFBO;9p8Qo(TWo|Q0Aukak4J0KenlE?Cud#bo!Fk1DAmRdu&V>V z8CijS(96K5yt$%oj3kji41>vH@{=nz?eTj)a%!eK-vW2e+bUPF_o+h0jm@SRM6-{}y`_*%H=6T%N9jx}*m z#aK0>s1?noH+P?b<{GAzBcRz{HQtg=edX%=2HI}jUqB;Zjeo&Gu+rY*wGE2#+~kop za!vs&s7^`zQq*YggK_+(!UDW{*XA6@Cx6w84ao(_zl1$v*IL1h;zlvIz!arCH64w? z9GP9Z@2IoZ-*&+z@n4@XPa z+*@{|?l4v!LTPTkOAk^T>g8tB=Lq)u?a1A022Qe@xMa}L=s7KdXj(C(Z6@r^{ny?S zwvyKDCN>vjE|*xhmu$H}c-NM1xNV3ycF;u0Ek)QikYU%BV|4~C2y#~Lqs^D%RbY$+ z$uQ+EP3ia?g7P+)?(SG$9#=P%d5ZXIHBFSBjXX^c#>1@Wn>3U@jilL6Hp;CN(W<1p z7F9~1%-O9pmA~i{Em^7KqKXcO<7mlFnuQ_5cQyutR&^L>?9s7 zS9&EY^!s?9LXT|EW0@Y?^-MaeoQBJw_^KFfCyQ+(Y!_5ayZy{tBAUmqKfPO^B)J9_T ziC%t-r%cK%Z;TI@XPajJv^P4tko-O7-zpt5I>(PA)t|f>I^aI7Fk=nes_4Jty<2C~ z)aGW~@?F~m4^!!-O#I74Q|d(_8u!3T`-H*AI}cG_M3JYTKE$yAD^2>K#`R?&{N+Z# zBjfE71MiAE;`xt<4EuO8ckyl>p-{NfFtRx8)i7fF$XC)`po7X+rA zt80Ga-Q-vR-D%n}D(evqMry!3v7a}@wI5bz1SqG21ol;bp8j14IzCBDMBQ(WRDAig>)=^ZYw#Ylb!9^C4c$2=gq7Bh�t_dPEd?{mXXIt8o$w0VMK@Z+ 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
O|a8 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