diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 258038b8f1..85b116129c 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2548,7 +2548,7 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite _pendingFileContent.seek(_pendingFileContent.size()); _pendingFileContent.write(dataChunk); _pendingFileContent.close(); - + // Respond immediately - will timeout if we wait for restore. connection->respond(HTTPConnection::StatusCode200); if (itemName == "restore-file" || itemName == "restore-file-chunk-final" || itemName == "restore-file-chunk-only") { diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 780fad15f2..4e833f6b77 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -25,13 +25,13 @@ #include #include +#include #include #include #include #include #include #include -#include //for KillAvatarReason #include #include "DomainServerNodeData.h" @@ -870,14 +870,6 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointerwrite(nodeUUID.toRfc4122()); - packet->writePrimitive(KillAvatarReason::NoReason); - - // send to avatar mixer, it sends the kill to everyone else - limitedNodeList->broadcastToNodes(std::move(packet), NodeSet() << NodeType::AvatarMixer); - if (newPermissions) { qDebug() << "Removing connect permission for node" << uuidStringWithoutCurlyBraces(matchingNode->getUUID()) << "after kick request from" << uuidStringWithoutCurlyBraces(sendingNode->getUUID()); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c889377d6f..8df8d919ee 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2302,31 +2302,31 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo DependencyManager::get()->setPrecisionPicking(rayPickID, value); }); - EntityItem::setBillboardRotationOperator([this](const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode) { + EntityItem::setBillboardRotationOperator([this](const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode, const glm::vec3& frustumPos) { if (billboardMode == BillboardMode::YAW) { //rotate about vertical to face the camera - ViewFrustum frustum; - copyViewFrustum(frustum); - glm::vec3 dPosition = frustum.getPosition() - position; + glm::vec3 dPosition = frustumPos - position; // If x and z are 0, atan(x, z) is undefined, so default to 0 degrees float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z); return glm::quat(glm::vec3(0.0f, yawRotation, 0.0f)); } else if (billboardMode == BillboardMode::FULL) { - ViewFrustum frustum; - copyViewFrustum(frustum); - glm::vec3 cameraPos = frustum.getPosition(); // use the referencial from the avatar, y isn't always up glm::vec3 avatarUP = DependencyManager::get()->getMyAvatar()->getWorldOrientation() * Vectors::UP; // check to see if glm::lookAt will work / using glm::lookAt variable name - glm::highp_vec3 s(glm::cross(position - cameraPos, avatarUP)); + glm::highp_vec3 s(glm::cross(position - frustumPos, avatarUP)); // make sure s is not NaN for any component if (glm::length2(s) > 0.0f) { - return glm::conjugate(glm::toQuat(glm::lookAt(cameraPos, position, avatarUP))); + return glm::conjugate(glm::toQuat(glm::lookAt(frustumPos, position, avatarUP))); } } return rotation; }); + EntityItem::setPrimaryViewFrustumPositionOperator([this]() { + ViewFrustum viewFrustum; + copyViewFrustum(viewFrustum); + return viewFrustum.getPosition(); + }); render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([this](const QString& url, bool htmlContent, QSharedPointer& webSurface, bool& cachedWebSurface) { bool isTablet = url == TabletScriptingInterface::QML; @@ -4981,6 +4981,15 @@ void Application::idle() { } } + { + if (_keyboardFocusWaitingOnRenderable && getEntities()->renderableForEntityId(_keyboardFocusedEntity.get())) { + _keyboardFocusWaitingOnRenderable = false; + QUuid entityId = _keyboardFocusedEntity.get(); + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); + setKeyboardFocusEntity(entityId); + } + } + { PerformanceTimer perfTimer("pluginIdle"); PerformanceWarning warn(showWarnings, "Application::idle()... pluginIdle()"); @@ -5809,7 +5818,7 @@ void Application::setKeyboardFocusEntity(const QUuid& id) { if (qApp->getLoginDialogPoppedUp() && !_loginDialogID.isNull()) { if (id == _loginDialogID) { emit loginDialogFocusEnabled(); - } else { + } else if (!_keyboardFocusWaitingOnRenderable) { // that's the only entity we want in focus; return; } @@ -5826,7 +5835,10 @@ void Application::setKeyboardFocusEntity(const QUuid& id) { if (properties.getVisible()) { auto entities = getEntities(); auto entityId = _keyboardFocusedEntity.get(); - if (entities->wantsKeyboardFocus(entityId)) { + auto entityItemRenderable = entities->renderableForEntityId(entityId); + if (!entityItemRenderable) { + _keyboardFocusWaitingOnRenderable = true; + } else if (entityItemRenderable->wantsKeyboardFocus()) { entities->setProxyWindow(entityId, _window->windowHandle()); if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->pluginFocusOutEvent(); diff --git a/interface/src/Application.h b/interface/src/Application.h index d856297e41..a8cc9450c5 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -733,6 +733,7 @@ private: bool _failedToConnectToEntityServer { false }; bool _reticleClickPressed { false }; + bool _keyboardFocusWaitingOnRenderable { false }; int _avatarAttachmentRequest = 0; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 0b33220c01..55025b3b23 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -297,6 +297,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { avatar->setIsNewAvatar(false); } avatar->simulate(deltaTime, inView); + if (avatar->getSkeletonModel()->isLoaded() && avatar->getWorkloadRegion() == workload::Region::R1) { + _myAvatar->addAvatarHandsToFlow(avatar); + } avatar->updateRenderItem(renderTransaction); avatar->updateSpaceProxy(workloadTransaction); avatar->setLastRenderUpdateTime(startTime); @@ -716,7 +719,7 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic } } - if (rayAvatarResult._intersect && pickAgainstMesh) { + if (avatar && rayAvatarResult._intersect && pickAgainstMesh) { glm::vec3 localRayOrigin = avatar->worldToJointPoint(ray.origin, rayAvatarResult._intersectWithJoint); glm::vec3 localRayPoint = avatar->worldToJointPoint(ray.origin + rayAvatarResult._distance * rayDirection, rayAvatarResult._intersectWithJoint); diff --git a/interface/src/avatar/GrabManager.cpp b/interface/src/avatar/GrabManager.cpp index db1337b64d..8273def490 100644 --- a/interface/src/avatar/GrabManager.cpp +++ b/interface/src/avatar/GrabManager.cpp @@ -32,6 +32,17 @@ void GrabManager::simulateGrabs() { bool success; SpatiallyNestablePointer grabbedThing = SpatiallyNestable::findByID(grabbedThingID, success); if (success && grabbedThing) { + auto entity = std::dynamic_pointer_cast(grabbedThing); + if (entity) { + if (entity->getLocked()) { + continue; // even if someone else claims to be grabbing it, don't move a locked thing + } + const GrabPropertyGroup& grabProps = entity->getGrabProperties(); + if (!grabProps.getGrabbable()) { + continue; // even if someone else claims to be grabbing it, don't move non-grabbable + } + } + glm::vec3 finalPosition = acc.finalizePosition(); glm::quat finalOrientation = acc.finalizeOrientation(); grabbedThing->setTransform(createMatFromQuatAndPos(finalOrientation, finalPosition)); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp old mode 100755 new mode 100644 index ff865172ae..faa9f88ae9 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -932,7 +932,8 @@ void MyAvatar::simulate(float deltaTime, bool inView) { bool isPhysicsEnabled = qApp->isPhysicsEnabled(); bool zoneAllowsFlying = zoneInteractionProperties.first; bool collisionlessAllowed = zoneInteractionProperties.second; - _characterController.setFlyingAllowed((zoneAllowsFlying && _enableFlying) || !isPhysicsEnabled); + _characterController.setZoneFlyingAllowed(zoneAllowsFlying || !isPhysicsEnabled); + _characterController.setComfortFlyingAllowed(_enableFlying); _characterController.setCollisionlessAllowed(collisionlessAllowed); } @@ -5317,6 +5318,72 @@ void MyAvatar::releaseGrab(const QUuid& grabID) { } } +void MyAvatar::addAvatarHandsToFlow(const std::shared_ptr& otherAvatar) { + auto &flow = _skeletonModel->getRig().getFlow(); + for (auto &handJointName : HAND_COLLISION_JOINTS) { + int jointIndex = otherAvatar->getJointIndex(handJointName); + if (jointIndex != -1) { + glm::vec3 position = otherAvatar->getJointPosition(jointIndex); + flow.setOthersCollision(otherAvatar->getID(), jointIndex, position); + } + } +} + +void MyAvatar::useFlow(bool isActive, bool isCollidable, const QVariantMap& physicsConfig, const QVariantMap& collisionsConfig) { + if (_skeletonModel->isLoaded()) { + _skeletonModel->getRig().initFlow(isActive); + auto &flow = _skeletonModel->getRig().getFlow(); + auto &collisionSystem = flow.getCollisionSystem(); + collisionSystem.setActive(isCollidable); + auto physicsGroups = physicsConfig.keys(); + if (physicsGroups.size() > 0) { + for (auto &groupName : physicsGroups) { + auto settings = physicsConfig[groupName].toMap(); + FlowPhysicsSettings physicsSettings; + if (settings.contains("active")) { + physicsSettings._active = settings["active"].toBool(); + } + if (settings.contains("damping")) { + physicsSettings._damping = settings["damping"].toFloat(); + } + if (settings.contains("delta")) { + physicsSettings._delta = settings["delta"].toFloat(); + } + if (settings.contains("gravity")) { + physicsSettings._gravity = settings["gravity"].toFloat(); + } + if (settings.contains("inertia")) { + physicsSettings._inertia = settings["inertia"].toFloat(); + } + if (settings.contains("radius")) { + physicsSettings._radius = settings["radius"].toFloat(); + } + if (settings.contains("stiffness")) { + physicsSettings._stiffness = settings["stiffness"].toFloat(); + } + flow.setPhysicsSettingsForGroup(groupName, physicsSettings); + } + } + auto collisionJoints = collisionsConfig.keys(); + if (collisionJoints.size() > 0) { + collisionSystem.resetCollisions(); + for (auto &jointName : collisionJoints) { + int jointIndex = getJointIndex(jointName); + FlowCollisionSettings collisionsSettings; + auto settings = collisionsConfig[jointName].toMap(); + collisionsSettings._entityID = getID(); + if (settings.contains("radius")) { + collisionsSettings._radius = settings["radius"].toFloat(); + } + if (settings.contains("offset")) { + collisionsSettings._offset = vec3FromVariant(settings["offset"]); + } + collisionSystem.addCollisionSphere(jointIndex, collisionsSettings); + } + } + } +} + void MyAvatar::sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const { auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 984d7b297b..2c8dedd430 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1183,6 +1183,20 @@ public: void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) override; void avatarEntityDataToJson(QJsonObject& root) const override; int sendAvatarDataPacket(bool sendAll = false) override; + + void addAvatarHandsToFlow(const std::shared_ptr& otherAvatar); + + /**jsdoc + * Init flow simulation on avatar. + * @function MyAvatar.useFlow + * @param {boolean} - Set to true to activate flow simulation. + * @param {boolean} - Set to true to activate collisions. + * @param {Object} physicsConfig - object with the customized physic parameters + * i.e. {"hair": {"active": true, "stiffness": 0.0, "radius": 0.04, "gravity": -0.035, "damping": 0.8, "inertia": 0.8, "delta": 0.35}} + * @param {Object} collisionsConfig - object with the customized collision parameters + * i.e. {"Spine2": {"type": "sphere", "radius": 0.14, "offset": {"x": 0.0, "y": 0.2, "z": 0.0}}} + */ + Q_INVOKABLE void useFlow(bool isActive, bool isCollidable, const QVariantMap& physicsConfig = QVariantMap(), const QVariantMap& collisionsConfig = QVariantMap()); public slots: diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index 40c7c01b30..199fae77bf 100755 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -368,7 +368,6 @@ void OtherAvatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "grabs"); applyGrabChanges(); } - updateFadingStatus(); } diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index 696e122b30..7669f44806 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -48,6 +48,7 @@ public: void rebuildCollisionShape() override; void setWorkloadRegion(uint8_t region); + uint8_t getWorkloadRegion() { return _workloadRegion; } bool shouldBeInPhysicsSimulation() const; bool needsPhysicsUpdate() const; diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index 9efad22d09..e56ca984e0 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -72,7 +72,7 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) { Locker lock(_lock); EntityItemPointer entity = _entityTree->findEntityByID(entityID); - if (entity && entity->getCreated() < _startTime) { + if (entity && !entity->isLocalEntity() && entity->getCreated() < _startTime) { _trackedEntities.emplace(entityID, entity); int trackedEntityCount = (int)_trackedEntities.size(); @@ -81,7 +81,7 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) { _maxTrackedEntityCount = trackedEntityCount; _trackedEntityStabilityCount = 0; } - qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName(); + //qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName(); } } } diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index fb64dbe098..2c4c29ff65 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -27,8 +27,9 @@ QString Audio::HMD { "VR" }; Setting::Handle enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true }; float Audio::loudnessToLevel(float loudness) { - float level = 6.02059991f * fastLog2f(loudness); // level in dBFS - level = (level + 48.0f) * (1/39.0f); // map [-48, -9] dBFS to [0, 1] + float level = loudness * (1/32768.0f); // level in [0, 1] + level = 6.02059991f * fastLog2f(level); // convert to dBFS + level = (level + 48.0f) * (1/42.0f); // map [-48, -6] dBFS to [0, 1] return glm::clamp(level, 0.0f, 1.0f); } diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 660220c731..9b2f741531 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -295,14 +295,14 @@ QString Overlays::overlayToEntityType(const QString& type) { } \ } -static QHash savedRotations = QHash(); +static QHash> savedRotations = QHash>(); EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& overlayProps, const QString& type, bool add, const QUuid& id) { - glm::quat rotation; + std::pair rotation; return convertOverlayToEntityProperties(overlayProps, rotation, type, add, id); } -EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& overlayProps, glm::quat& rotationToSave, const QString& type, bool add, const QUuid& id) { +EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& overlayProps, std::pair& rotationToSave, const QString& type, bool add, const QUuid& id) { overlayProps["type"] = type; SET_OVERLAY_PROP_DEFAULT(alpha, 0.7); @@ -311,7 +311,11 @@ EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& ove RENAME_PROP(start, position); } RENAME_PROP(point, position); - RENAME_PROP(scale, dimensions); + if (type != "Model") { + RENAME_PROP(scale, dimensions); + } else { + RENAME_PROP(scale, modelScale); + } RENAME_PROP(size, dimensions); RENAME_PROP(orientation, rotation); RENAME_PROP(localOrientation, localRotation); @@ -561,27 +565,31 @@ EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& ove if (type == "Text" || type == "Image" || type == "Grid" || type == "Web") { glm::quat originalRotation = ENTITY_ITEM_DEFAULT_ROTATION; + bool local = false; { auto iter = overlayProps.find("rotation"); if (iter != overlayProps.end()) { originalRotation = quatFromVariant(iter.value()); + local = false; } else { iter = overlayProps.find("localRotation"); if (iter != overlayProps.end()) { originalRotation = quatFromVariant(iter.value()); + local = true; } else if (!add) { auto iter2 = savedRotations.find(id); if (iter2 != savedRotations.end()) { - originalRotation = iter2.value(); + originalRotation = iter2.value().first; + local = iter2.value().second; } } } } if (!add) { - savedRotations[id] = originalRotation; + savedRotations[id] = { originalRotation, local }; } else { - rotationToSave = originalRotation; + rotationToSave = { originalRotation, local }; } glm::vec3 dimensions = ENTITY_ITEM_DEFAULT_DIMENSIONS; @@ -612,7 +620,11 @@ EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& ove rotation = glm::angleAxis((float)M_PI, rotation * Vectors::UP) * rotation; } - overlayProps["localRotation"] = quatToVariant(rotation); + if (local) { + overlayProps["localRotation"] = quatToVariant(rotation); + } else { + overlayProps["rotation"] = quatToVariant(rotation); + } overlayProps["dimensions"] = vec3toVariant(glm::abs(dimensions)); } } @@ -636,7 +648,11 @@ QVariantMap Overlays::convertEntityToOverlayProperties(const EntityItemPropertie RENAME_PROP(position, start); } RENAME_PROP(position, point); - RENAME_PROP(dimensions, scale); + if (type != "Model") { + RENAME_PROP(dimensions, scale); + } else { + RENAME_PROP(modelScale, scale); + } RENAME_PROP(dimensions, size); RENAME_PROP(ignorePickIntersection, ignoreRayIntersection); @@ -790,7 +806,7 @@ QUuid Overlays::addOverlay(const QString& type, const QVariant& properties) { if (type == "rectangle3d") { propertyMap["shape"] = "Quad"; } - glm::quat rotationToSave; + std::pair rotationToSave; QUuid id = DependencyManager::get()->addEntityInternal(convertOverlayToEntityProperties(propertyMap, rotationToSave, entityType, true), entity::HostType::LOCAL); if (entityType == "Text" || entityType == "Image" || entityType == "Grid" || entityType == "Web") { savedRotations[id] = rotationToSave; @@ -1718,7 +1734,8 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and * start. - * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. + * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: size. + * @property {Vec3} scale - The scale factor applied to the model's dimensions. * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as position. diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 838a38eb54..93efc2bc0b 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -729,7 +729,7 @@ private: QVariantMap convertEntityToOverlayProperties(const EntityItemProperties& entityProps); EntityItemProperties convertOverlayToEntityProperties(QVariantMap& overlayProps, const QString& type, bool add, const QUuid& id); - EntityItemProperties convertOverlayToEntityProperties(QVariantMap& overlayProps, glm::quat& rotationToSave, const QString& type, bool add, const QUuid& id = QUuid()); + EntityItemProperties convertOverlayToEntityProperties(QVariantMap& overlayProps, std::pair& rotationToSave, const QString& type, bool add, const QUuid& id = QUuid()); private slots: void mousePressPointerEvent(const QUuid& id, const PointerEvent& event); diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 1adc04ee1b..a35e0237d0 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -99,65 +99,64 @@ void AnimClip::copyFromNetworkAnim() { assert(_networkAnim && _networkAnim->isLoaded() && _skeleton); _anim.clear(); - // build a mapping from animation joint indices to skeleton joint indices. + auto avatarSkeleton = getSkeleton(); + + // build a mapping from animation joint indices to avatar joint indices. // by matching joints with the same name. - const HFMModel& hfmModel = _networkAnim->getHFMModel(); - AnimSkeleton animSkeleton(hfmModel); - const auto animJointCount = animSkeleton.getNumJoints(); - const auto skeletonJointCount = _skeleton->getNumJoints(); - std::vector jointMap; - jointMap.reserve(animJointCount); - for (int i = 0; i < animJointCount; i++) { - int skeletonJoint = _skeleton->nameToJointIndex(animSkeleton.getJointName(i)); - jointMap.push_back(skeletonJoint); + const HFMModel& animModel = _networkAnim->getHFMModel(); + AnimSkeleton animSkeleton(animModel); + const int animJointCount = animSkeleton.getNumJoints(); + const int avatarJointCount = avatarSkeleton->getNumJoints(); + std::vector animToAvatarJointIndexMap; + animToAvatarJointIndexMap.reserve(animJointCount); + for (int animJointIndex = 0; animJointIndex < animJointCount; animJointIndex++) { + QString animJointName = animSkeleton.getJointName(animJointIndex); + int avatarJointIndex = avatarSkeleton->nameToJointIndex(animJointName); + animToAvatarJointIndexMap.push_back(avatarJointIndex); } - const int frameCount = hfmModel.animationFrames.size(); - _anim.resize(frameCount); + const int animFrameCount = animModel.animationFrames.size(); + _anim.resize(animFrameCount); - for (int frame = 0; frame < frameCount; frame++) { + for (int frame = 0; frame < animFrameCount; frame++) { - const HFMAnimationFrame& hfmAnimFrame = hfmModel.animationFrames[frame]; + const HFMAnimationFrame& animFrame = animModel.animationFrames[frame]; // init all joints in animation to default pose - // this will give us a resonable result for bones in the model skeleton but not in the animation. - _anim[frame].reserve(skeletonJointCount); - for (int skeletonJoint = 0; skeletonJoint < skeletonJointCount; skeletonJoint++) { - _anim[frame].push_back(_skeleton->getRelativeDefaultPose(skeletonJoint)); + // this will give us a resonable result for bones in the avatar skeleton but not in the animation. + _anim[frame].reserve(avatarJointCount); + for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) { + _anim[frame].push_back(avatarSkeleton->getRelativeDefaultPose(avatarJointIndex)); } - for (int animJoint = 0; animJoint < animJointCount; animJoint++) { - int skeletonJoint = jointMap[animJoint]; + for (int animJointIndex = 0; animJointIndex < animJointCount; animJointIndex++) { + int avatarJointIndex = animToAvatarJointIndexMap[animJointIndex]; - const glm::vec3& hfmAnimTrans = hfmAnimFrame.translations[animJoint]; - const glm::quat& hfmAnimRot = hfmAnimFrame.rotations[animJoint]; + // skip joints that are in the animation but not in the avatar. + if (avatarJointIndex >= 0 && avatarJointIndex < avatarJointCount) { - // skip joints that are in the animation but not in the skeleton. - if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) { + const glm::vec3& animTrans = animFrame.translations[animJointIndex]; + const glm::quat& animRot = animFrame.rotations[animJointIndex]; - AnimPose preRot, postRot; - preRot = animSkeleton.getPreRotationPose(animJoint); - postRot = animSkeleton.getPostRotationPose(animJoint); + const AnimPose& animPreRotPose = animSkeleton.getPreRotationPose(animJointIndex); + AnimPose animPostRotPose = animSkeleton.getPostRotationPose(animJointIndex); + AnimPose animRotPose(glm::vec3(1.0f), animRot, glm::vec3()); - // cancel out scale - preRot.scale() = glm::vec3(1.0f); - postRot.scale() = glm::vec3(1.0f); + // adjust anim scale to equal the scale from the avatar joint. + // we do not support animated scale. + const AnimPose& avatarDefaultPose = avatarSkeleton->getRelativeDefaultPose(avatarJointIndex); + animPostRotPose.scale() = avatarDefaultPose.scale(); - AnimPose rot(glm::vec3(1.0f), hfmAnimRot, glm::vec3()); - - // adjust translation offsets, so large translation animatons on the reference skeleton - // will be adjusted when played on a skeleton with short limbs. - const glm::vec3& hfmZeroTrans = hfmModel.animationFrames[0].translations[animJoint]; - const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint); + // retarget translation from animation to avatar + const glm::vec3& animZeroTrans = animModel.animationFrames[0].translations[animJointIndex]; float boneLengthScale = 1.0f; const float EPSILON = 0.0001f; - if (fabsf(glm::length(hfmZeroTrans)) > EPSILON) { - boneLengthScale = glm::length(relDefaultPose.trans()) / glm::length(hfmZeroTrans); + if (fabsf(glm::length(animZeroTrans)) > EPSILON) { + boneLengthScale = glm::length(avatarDefaultPose.trans()) / glm::length(animZeroTrans); } + AnimPose animTransPose = AnimPose(glm::vec3(1.0f), glm::quat(), avatarDefaultPose.trans() + boneLengthScale * (animTrans - animZeroTrans)); - AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans() + boneLengthScale * (hfmAnimTrans - hfmZeroTrans)); - - _anim[frame][skeletonJoint] = trans * preRot * rot * postRot; + _anim[frame][avatarJointIndex] = animTransPose * animPreRotPose * animRotPose * animPostRotPose; } } } @@ -165,7 +164,7 @@ void AnimClip::copyFromNetworkAnim() { // mirrorAnim will be re-built on demand, if needed. _mirrorAnim.clear(); - _poses.resize(skeletonJointCount); + _poses.resize(avatarJointCount); } void AnimClip::buildMirrorAnim() { diff --git a/libraries/animation/src/Flow.cpp b/libraries/animation/src/Flow.cpp new file mode 100644 index 0000000000..3bb80b9375 --- /dev/null +++ b/libraries/animation/src/Flow.cpp @@ -0,0 +1,783 @@ +// +// Flow.cpp +// +// Created by Luis Cuenca on 1/21/2019. +// Copyright 2019 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 "Flow.h" +#include "Rig.h" +#include "AnimSkeleton.h" + +const std::map PRESET_FLOW_DATA = { { "hair", FlowPhysicsSettings() }, +{ "skirt", FlowPhysicsSettings(true, 1.0f, DEFAULT_GRAVITY, 0.65f, 0.8f, 0.45f, 0.01f) }, +{ "breast", FlowPhysicsSettings(true, 1.0f, DEFAULT_GRAVITY, 0.65f, 0.8f, 0.45f, 0.01f) } }; + +const std::map PRESET_COLLISION_DATA = { + { "Spine2", FlowCollisionSettings(QUuid(), FlowCollisionType::CollisionSphere, glm::vec3(0.0f, 0.2f, 0.0f), 0.14f) }, + { "LeftArm", FlowCollisionSettings(QUuid(), FlowCollisionType::CollisionSphere, glm::vec3(0.0f, 0.02f, 0.0f), 0.05f) }, + { "RightArm", FlowCollisionSettings(QUuid(), FlowCollisionType::CollisionSphere, glm::vec3(0.0f, 0.02f, 0.0f), 0.05f) }, + { "HeadTop_End", FlowCollisionSettings(QUuid(), FlowCollisionType::CollisionSphere, glm::vec3(0.0f, -0.15f, 0.0f), 0.09f) } +}; + +FlowCollisionSphere::FlowCollisionSphere(const int& jointIndex, const FlowCollisionSettings& settings, bool isTouch) { + _jointIndex = jointIndex; + _radius = _initialRadius = settings._radius; + _offset = _initialOffset = settings._offset; + _entityID = settings._entityID; + _isTouch = isTouch; +} + +FlowCollisionResult FlowCollisionSphere::computeSphereCollision(const glm::vec3& point, float radius) const { + FlowCollisionResult result; + auto centerToJoint = point - _position; + result._distance = glm::length(centerToJoint) - radius; + result._offset = _radius - result._distance; + result._normal = glm::normalize(centerToJoint); + result._radius = _radius; + result._position = _position; + return result; +} + +FlowCollisionResult FlowCollisionSphere::checkSegmentCollision(const glm::vec3& point1, const glm::vec3& point2, const FlowCollisionResult& collisionResult1, const FlowCollisionResult& collisionResult2) { + FlowCollisionResult result; + auto segment = point2 - point1; + auto segmentLength = glm::length(segment); + auto maxDistance = glm::sqrt(powf(collisionResult1._radius, 2.0f) + powf(segmentLength, 2.0f)); + if (collisionResult1._distance < maxDistance && collisionResult2._distance < maxDistance) { + float segmentPercentage = collisionResult1._distance / (collisionResult1._distance + collisionResult2._distance); + glm::vec3 collisionPoint = point1 + segment * segmentPercentage; + glm::vec3 centerToSegment = collisionPoint - _position; + float distance = glm::length(centerToSegment); + if (distance < _radius) { + result._offset = _radius - distance; + result._position = _position; + result._radius = _radius; + result._normal = glm::normalize(centerToSegment); + result._distance = distance; + } + } + return result; +} + +void FlowCollisionSystem::addCollisionSphere(int jointIndex, const FlowCollisionSettings& settings, const glm::vec3& position, bool isSelfCollision, bool isTouch) { + auto collision = FlowCollisionSphere(jointIndex, settings, isTouch); + collision.setPosition(position); + if (isSelfCollision) { + _selfCollisions.push_back(collision); + } else { + _othersCollisions.push_back(collision); + } + +}; +void FlowCollisionSystem::resetCollisions() { + _allCollisions.clear(); + _othersCollisions.clear(); + _selfCollisions.clear(); +} +FlowCollisionResult FlowCollisionSystem::computeCollision(const std::vector collisions) { + FlowCollisionResult result; + if (collisions.size() > 1) { + for (size_t i = 0; i < collisions.size(); i++) { + result._offset += collisions[i]._offset; + result._normal = result._normal + collisions[i]._normal * collisions[i]._distance; + result._position = result._position + collisions[i]._position; + result._radius += collisions[i]._radius; + result._distance += collisions[i]._distance; + } + result._offset = result._offset / collisions.size(); + result._radius = 0.5f * glm::length(result._normal); + result._normal = glm::normalize(result._normal); + result._position = result._position / (float)collisions.size(); + result._distance = result._distance / collisions.size(); + } else if (collisions.size() == 1) { + result = collisions[0]; + } + result._count = (int)collisions.size(); + return result; +}; + +void FlowCollisionSystem::setScale(float scale) { + _scale = scale; + for (size_t j = 0; j < _selfCollisions.size(); j++) { + _selfCollisions[j]._radius = _selfCollisions[j]._initialRadius * scale; + _selfCollisions[j]._offset = _selfCollisions[j]._initialOffset * scale; + } +}; + +std::vector FlowCollisionSystem::checkFlowThreadCollisions(FlowThread* flowThread) { + std::vector> FlowThreadResults; + FlowThreadResults.resize(flowThread->_joints.size()); + for (size_t j = 0; j < _allCollisions.size(); j++) { + FlowCollisionSphere &sphere = _allCollisions[j]; + FlowCollisionResult rootCollision = sphere.computeSphereCollision(flowThread->_positions[0], flowThread->_radius); + std::vector collisionData = { rootCollision }; + bool tooFar = rootCollision._distance >(flowThread->_length + rootCollision._radius); + FlowCollisionResult nextCollision; + if (!tooFar) { + if (sphere._isTouch) { + for (size_t i = 1; i < flowThread->_joints.size(); i++) { + auto prevCollision = collisionData[i - 1]; + nextCollision = _allCollisions[j].computeSphereCollision(flowThread->_positions[i], flowThread->_radius); + collisionData.push_back(nextCollision); + if (prevCollision._offset > 0.0f) { + if (i == 1) { + FlowThreadResults[i - 1].push_back(prevCollision); + } + } else if (nextCollision._offset > 0.0f) { + FlowThreadResults[i].push_back(nextCollision); + } else { + FlowCollisionResult segmentCollision = _allCollisions[j].checkSegmentCollision(flowThread->_positions[i - 1], flowThread->_positions[i], prevCollision, nextCollision); + if (segmentCollision._offset > 0) { + FlowThreadResults[i - 1].push_back(segmentCollision); + FlowThreadResults[i].push_back(segmentCollision); + } + } + } + } else { + if (rootCollision._offset > 0.0f) { + FlowThreadResults[0].push_back(rootCollision); + } + for (size_t i = 1; i < flowThread->_joints.size(); i++) { + nextCollision = _allCollisions[j].computeSphereCollision(flowThread->_positions[i], flowThread->_radius); + if (nextCollision._offset > 0.0f) { + FlowThreadResults[i].push_back(nextCollision); + } + } + } + } + } + + std::vector results; + for (size_t i = 0; i < flowThread->_joints.size(); i++) { + results.push_back(computeCollision(FlowThreadResults[i])); + } + return results; +}; + +FlowCollisionSettings FlowCollisionSystem::getCollisionSettingsByJoint(int jointIndex) { + for (auto &collision : _selfCollisions) { + if (collision._jointIndex == jointIndex) { + return FlowCollisionSettings(collision._entityID, FlowCollisionType::CollisionSphere, collision._initialOffset, collision._initialRadius); + } + } + return FlowCollisionSettings(); +} +void FlowCollisionSystem::setCollisionSettingsByJoint(int jointIndex, const FlowCollisionSettings& settings) { + for (auto &collision : _selfCollisions) { + if (collision._jointIndex == jointIndex) { + collision._initialRadius = settings._radius; + collision._initialOffset = settings._offset; + collision._radius = _scale * settings._radius; + collision._offset = _scale * settings._offset; + } + } +} +void FlowCollisionSystem::prepareCollisions() { + _allCollisions.clear(); + _allCollisions.resize(_selfCollisions.size() + _othersCollisions.size()); + std::copy(_selfCollisions.begin(), _selfCollisions.begin() + _selfCollisions.size(), _allCollisions.begin()); + std::copy(_othersCollisions.begin(), _othersCollisions.begin() + _othersCollisions.size(), _allCollisions.begin() + _selfCollisions.size()); + _othersCollisions.clear(); +} + +FlowNode::FlowNode(const glm::vec3& initialPosition, FlowPhysicsSettings settings) { + _initialPosition = _previousPosition = _currentPosition = initialPosition; + _initialRadius = settings._radius; +} + +void FlowNode::update(float deltaTime, const glm::vec3& accelerationOffset) { + _acceleration = glm::vec3(0.0f, _settings._gravity, 0.0f); + _previousVelocity = _currentVelocity; + _currentVelocity = _currentPosition - _previousPosition; + _previousPosition = _currentPosition; + if (!_anchored) { + // Add inertia + const float FPS = 60.0f; + float timeRatio = _scale * (FPS * deltaTime); + float invertedTimeRatio = timeRatio > 0.0f ? 1.0f / timeRatio : 1.0f; + auto deltaVelocity = _previousVelocity - _currentVelocity; + auto centrifugeVector = glm::length(deltaVelocity) != 0.0f ? glm::normalize(deltaVelocity) : glm::vec3(); + _acceleration = _acceleration + centrifugeVector * _settings._inertia * glm::length(_currentVelocity) * invertedTimeRatio; + + // Add offset + _acceleration += accelerationOffset; + float accelerationFactor = powf(_settings._delta, 2.0f) * timeRatio; + glm::vec3 deltaAcceleration = _acceleration * accelerationFactor; + // Calculate new position + _currentPosition = _currentPosition + (_currentVelocity * _settings._damping) + deltaAcceleration; + } else { + _acceleration = glm::vec3(0.0f); + _currentVelocity = glm::vec3(0.0f); + } +}; + + +void FlowNode::solve(const glm::vec3& constrainPoint, float maxDistance, const FlowCollisionResult& collision) { + solveConstraints(constrainPoint, maxDistance); + solveCollisions(collision); +}; + +void FlowNode::solveConstraints(const glm::vec3& constrainPoint, float maxDistance) { + glm::vec3 constrainVector = _currentPosition - constrainPoint; + float difference = maxDistance / glm::length(constrainVector); + _currentPosition = difference < 1.0f ? constrainPoint + constrainVector * difference : _currentPosition; +}; + +void FlowNode::solveCollisions(const FlowCollisionResult& collision) { + _colliding = collision._offset > 0.0f; + _collision = collision; + if (_colliding) { + _currentPosition = _currentPosition + collision._normal * collision._offset; + _previousCollision = collision; + } else { + _previousCollision = FlowCollisionResult(); + } +}; + +FlowJoint::FlowJoint(int jointIndex, int parentIndex, int childIndex, const QString& name, const QString& group, const FlowPhysicsSettings& settings) { + _index = jointIndex; + _name = name; + _group = group; + _childIndex = childIndex; + _parentIndex = parentIndex; + FlowNode(glm::vec3(), settings); +}; + +void FlowJoint::setInitialData(const glm::vec3& initialPosition, const glm::vec3& initialTranslation, const glm::quat& initialRotation, const glm::vec3& parentPosition) { + _initialPosition = initialPosition; + _previousPosition = initialPosition; + _currentPosition = initialPosition; + _initialTranslation = initialTranslation; + _currentRotation = initialRotation; + _initialRotation = initialRotation; + _translationDirection = glm::normalize(_initialTranslation); + _parentPosition = parentPosition; + _initialLength = _length = glm::length(_initialPosition - parentPosition); +} + +void FlowJoint::setUpdatedData(const glm::vec3& updatedPosition, const glm::vec3& updatedTranslation, const glm::quat& updatedRotation, const glm::vec3& parentPosition, const glm::quat& parentWorldRotation) { + _updatedPosition = updatedPosition; + _updatedRotation = updatedRotation; + _updatedTranslation = updatedTranslation; + _parentPosition = parentPosition; + _parentWorldRotation = parentWorldRotation; +} + +void FlowJoint::setRecoveryPosition(const glm::vec3& recoveryPosition) { + _recoveryPosition = recoveryPosition; + _applyRecovery = true; +} + +void FlowJoint::update(float deltaTime) { + glm::vec3 accelerationOffset = glm::vec3(0.0f); + if (_settings._stiffness > 0.0f) { + glm::vec3 recoveryVector = _recoveryPosition - _currentPosition; + float recoveryFactor = powf(_settings._stiffness, 3.0f); + accelerationOffset = recoveryVector * recoveryFactor; + } + FlowNode::update(deltaTime, accelerationOffset); + if (_anchored) { + if (!_isHelper) { + _currentPosition = _updatedPosition; + } else { + _currentPosition = _parentPosition; + } + } +}; + +void FlowJoint::setScale(float scale, bool initScale) { + if (initScale) { + _initialLength = _length / scale; + } + _settings._radius = _initialRadius * scale; + _length = _initialLength * scale; + _scale = scale; +} + +void FlowJoint::solve(const FlowCollisionResult& collision) { + FlowNode::solve(_parentPosition, _length, collision); +}; + +void FlowJoint::toHelperJoint(const glm::vec3& initialPosition, float length) { + _initialPosition = initialPosition; + _isHelper = true; + _length = length; +} + +FlowThread::FlowThread(int rootIndex, std::map* joints) { + _jointsPointer = joints; + computeFlowThread(rootIndex); +} + +void FlowThread::resetLength() { + _length = 0.0f; + for (size_t i = 1; i < _joints.size(); i++) { + int index = _joints[i]; + _length += _jointsPointer->at(index)._length; + } +} + +void FlowThread::computeFlowThread(int rootIndex) { + int parentIndex = rootIndex; + if (_jointsPointer->size() == 0) { + return; + } + int childIndex = _jointsPointer->at(parentIndex)._childIndex; + std::vector indexes = { parentIndex }; + for (size_t i = 0; i < _jointsPointer->size(); i++) { + if (childIndex > -1) { + indexes.push_back(childIndex); + childIndex = _jointsPointer->at(childIndex)._childIndex; + } else { + break; + } + } + _length = 0.0f; + for (size_t i = 0; i < indexes.size(); i++) { + int index = indexes[i]; + _joints.push_back(index); + if (i > 0) { + _length += _jointsPointer->at(index)._length; + } + } +}; + +void FlowThread::computeRecovery() { + int parentIndex = _joints[0]; + auto parentJoint = _jointsPointer->at(parentIndex); + _jointsPointer->at(parentIndex)._recoveryPosition = parentJoint._recoveryPosition = parentJoint._currentPosition; + glm::quat parentRotation = parentJoint._parentWorldRotation * parentJoint._initialRotation; + for (size_t i = 1; i < _joints.size(); i++) { + auto joint = _jointsPointer->at(_joints[i]); + _jointsPointer->at(_joints[i])._recoveryPosition = joint._recoveryPosition = parentJoint._recoveryPosition + (parentRotation * (joint._initialTranslation * 0.01f)); + parentJoint = joint; + } +}; + +void FlowThread::update(float deltaTime) { + _positions.clear(); + auto &firstJoint = _jointsPointer->at(_joints[0]); + _radius = firstJoint._settings._radius; + computeRecovery(); + for (size_t i = 0; i < _joints.size(); i++) { + auto &joint = _jointsPointer->at(_joints[i]); + joint.update(deltaTime); + _positions.push_back(joint._currentPosition); + } +}; + +void FlowThread::solve(FlowCollisionSystem& collisionSystem) { + if (collisionSystem.getActive()) { + auto bodyCollisions = collisionSystem.checkFlowThreadCollisions(this); + for (size_t i = 0; i < _joints.size(); i++) { + int index = _joints[i]; + _jointsPointer->at(index).solve(bodyCollisions[i]); + } + } else { + for (size_t i = 0; i < _joints.size(); i++) { + int index = _joints[i]; + _jointsPointer->at(index).solve(FlowCollisionResult()); + } + } +}; + +void FlowThread::computeJointRotations() { + + auto pos0 = _rootFramePositions[0]; + auto pos1 = _rootFramePositions[1]; + + auto joint0 = _jointsPointer->at(_joints[0]); + auto joint1 = _jointsPointer->at(_joints[1]); + + auto initial_pos1 = pos0 + (joint0._initialRotation * (joint1._initialTranslation * 0.01f)); + + auto vec0 = initial_pos1 - pos0; + auto vec1 = pos1 - pos0; + + auto delta = rotationBetween(vec0, vec1); + + joint0._currentRotation = _jointsPointer->at(_joints[0])._currentRotation = delta * joint0._initialRotation; + + for (size_t i = 1; i < _joints.size() - 1; i++) { + auto nextJoint = _jointsPointer->at(_joints[i + 1]); + for (size_t j = i; j < _joints.size(); j++) { + _rootFramePositions[j] = glm::inverse(joint0._currentRotation) * _rootFramePositions[j] - (joint0._initialTranslation * 0.01f); + } + pos0 = _rootFramePositions[i]; + pos1 = _rootFramePositions[i + 1]; + initial_pos1 = pos0 + joint1._initialRotation * (nextJoint._initialTranslation * 0.01f); + + vec0 = initial_pos1 - pos0; + vec1 = pos1 - pos0; + + delta = rotationBetween(vec0, vec1); + + joint1._currentRotation = _jointsPointer->at(joint1._index)._currentRotation = delta * joint1._initialRotation; + joint0 = joint1; + joint1 = nextJoint; + } + +} + +void FlowThread::setScale(float scale, bool initScale) { + for (size_t i = 0; i < _joints.size(); i++) { + auto &joint = _jointsPointer->at(_joints[i]); + joint.setScale(scale, initScale); + } + resetLength(); +} + +FlowThread& FlowThread::operator=(const FlowThread& otherFlowThread) { + for (int jointIndex: otherFlowThread._joints) { + auto& joint = otherFlowThread._jointsPointer->at(jointIndex); + auto& myJoint = _jointsPointer->at(jointIndex); + myJoint._acceleration = joint._acceleration; + myJoint._currentPosition = joint._currentPosition; + myJoint._currentRotation = joint._currentRotation; + myJoint._currentVelocity = joint._currentVelocity; + myJoint._length = joint._length; + myJoint._parentPosition = joint._parentPosition; + myJoint._parentWorldRotation = joint._parentWorldRotation; + myJoint._previousPosition = joint._previousPosition; + myJoint._previousVelocity = joint._previousVelocity; + myJoint._scale = joint._scale; + myJoint._translationDirection = joint._translationDirection; + myJoint._updatedPosition = joint._updatedPosition; + myJoint._updatedRotation = joint._updatedRotation; + myJoint._updatedTranslation = joint._updatedTranslation; + myJoint._isHelper = joint._isHelper; + } + return *this; +} + +void Flow::calculateConstraints(const std::shared_ptr& skeleton, + AnimPoseVec& relativePoses, AnimPoseVec& absolutePoses) { + cleanUp(); + if (!skeleton) { + return; + } + auto flowPrefix = FLOW_JOINT_PREFIX.toUpper(); + auto simPrefix = SIM_JOINT_PREFIX.toUpper(); + std::vector handsIndices; + + for (int i = 0; i < skeleton->getNumJoints(); i++) { + auto name = skeleton->getJointName(i); + if (std::find(HAND_COLLISION_JOINTS.begin(), HAND_COLLISION_JOINTS.end(), name) != HAND_COLLISION_JOINTS.end()) { + handsIndices.push_back(i); + } + auto parentIndex = skeleton->getParentIndex(i); + if (parentIndex == -1) { + continue; + } + auto jointChildren = skeleton->getChildrenOfJoint(i); + // auto childIndex = jointChildren.size() > 0 ? jointChildren[0] : -1; + auto group = QStringRef(&name, 0, 3).toString().toUpper(); + auto split = name.split("_"); + bool isSimJoint = (group == simPrefix); + bool isFlowJoint = split.size() > 2 && split[0].toUpper() == flowPrefix; + if (isFlowJoint || isSimJoint) { + group = ""; + if (isSimJoint) { + for (int j = 1; j < name.size() - 1; j++) { + bool toFloatSuccess; + QStringRef(&name, (int)(name.size() - j), 1).toString().toFloat(&toFloatSuccess); + if (!toFloatSuccess && (name.size() - j) > (int)simPrefix.size()) { + group = QStringRef(&name, (int)simPrefix.size(), (int)(name.size() - j + 1)).toString(); + break; + } + } + if (group.isEmpty()) { + group = QStringRef(&name, (int)simPrefix.size(), name.size() - 1).toString(); + } + qCDebug(animation) << "Sim joint added to flow: " << name; + } else { + group = split[1]; + } + if (!group.isEmpty()) { + _flowJointKeywords.push_back(group); + FlowPhysicsSettings jointSettings; + if (PRESET_FLOW_DATA.find(group) != PRESET_FLOW_DATA.end()) { + jointSettings = PRESET_FLOW_DATA.at(group); + } else { + jointSettings = DEFAULT_JOINT_SETTINGS; + } + if (_flowJointData.find(i) == _flowJointData.end()) { + auto flowJoint = FlowJoint(i, parentIndex, -1, name, group, jointSettings); + _flowJointData.insert(std::pair(i, flowJoint)); + } + } + } else { + if (PRESET_COLLISION_DATA.find(name) != PRESET_COLLISION_DATA.end()) { + _collisionSystem.addCollisionSphere(i, PRESET_COLLISION_DATA.at(name)); + } + } + } + + for (auto &jointData : _flowJointData) { + int jointIndex = jointData.first; + glm::vec3 jointPosition, parentPosition, jointTranslation; + glm::quat jointRotation; + getJointPositionInWorldFrame(absolutePoses, jointIndex, jointPosition, _entityPosition, _entityRotation); + getJointTranslation(relativePoses, jointIndex, jointTranslation); + getJointRotation(relativePoses, jointIndex, jointRotation); + getJointPositionInWorldFrame(absolutePoses, jointData.second.getParentIndex(), parentPosition, _entityPosition, _entityRotation); + + jointData.second.setInitialData(jointPosition, jointTranslation, jointRotation, parentPosition); + } + + std::vector roots; + + for (auto &joint :_flowJointData) { + if (_flowJointData.find(joint.second.getParentIndex()) == _flowJointData.end()) { + joint.second.setAnchored(true); + roots.push_back(joint.first); + } else { + _flowJointData[joint.second.getParentIndex()].setChildIndex(joint.first); + } + } + int extraIndex = -1; + for (size_t i = 0; i < roots.size(); i++) { + FlowThread thread = FlowThread(roots[i], &_flowJointData); + // add threads with at least 2 joints + if (thread._joints.size() > 0) { + if (thread._joints.size() == 1) { + int jointIndex = roots[i]; + auto &joint = _flowJointData[jointIndex]; + auto &jointPosition = joint.getUpdatedPosition(); + auto newSettings = joint.getSettings(); + extraIndex = extraIndex > -1 ? extraIndex + 1 : skeleton->getNumJoints(); + joint.setChildIndex(extraIndex); + auto newJoint = FlowJoint(extraIndex, jointIndex, -1, joint.getName(), joint.getGroup(), newSettings); + newJoint.toHelperJoint(jointPosition, HELPER_JOINT_LENGTH); + glm::vec3 translation = glm::vec3(0.0f, HELPER_JOINT_LENGTH, 0.0f); + newJoint.setInitialData(jointPosition + translation, 100.0f * translation , Quaternions::IDENTITY, jointPosition); + _flowJointData.insert(std::pair(extraIndex, newJoint)); + FlowThread newThread = FlowThread(jointIndex, &_flowJointData); + if (newThread._joints.size() > 1) { + _jointThreads.push_back(newThread); + } + } else { + _jointThreads.push_back(thread); + } + } + } + + if (_jointThreads.size() == 0) { + onCleanup(); + } + if (handsIndices.size() > 0) { + FlowCollisionSettings handSettings; + handSettings._radius = HAND_COLLISION_RADIUS; + for (size_t i = 0; i < handsIndices.size(); i++) { + _collisionSystem.addCollisionSphere(handsIndices[i], handSettings, glm::vec3(), true, true); + } + } + _initialized = _jointThreads.size() > 0; +} + +void Flow::cleanUp() { + _flowJointData.clear(); + _jointThreads.clear(); + _flowJointKeywords.clear(); + _collisionSystem.resetCollisions(); + _initialized = false; + _isScaleSet = false; + onCleanup(); + } + +void Flow::setTransform(float scale, const glm::vec3& position, const glm::quat& rotation) { + _scale = scale; + _entityPosition = position; + _entityRotation = rotation; +} + +void Flow::setScale(float scale) { + _collisionSystem.setScale(_scale); + for (size_t i = 0; i < _jointThreads.size(); i++) { + _jointThreads[i].setScale(_scale, !_isScaleSet); + } + if (_lastScale != _scale) { + _lastScale = _scale; + _isScaleSet = true; + } + +} + +void Flow::update(float deltaTime, AnimPoseVec& relativePoses, AnimPoseVec& absolutePoses, const std::vector& overrideFlags) { + if (_initialized && _active) { + uint64_t startTime = usecTimestampNow(); + uint64_t updateExpiry = startTime + MAX_UPDATE_FLOW_TIME_BUDGET; + if (_scale != _lastScale) { + setScale(_scale); + } + for (size_t i = 0; i < _jointThreads.size(); i++) { + size_t index = _invertThreadLoop ? _jointThreads.size() - 1 - i : i; + auto &thread = _jointThreads[index]; + thread.update(deltaTime); + thread.solve(_collisionSystem); + if (!updateRootFramePositions(absolutePoses, index)) { + return; + } + thread.computeJointRotations(); + if (usecTimestampNow() > updateExpiry) { + break; + qWarning(animation) << "Flow Bones ran out of time while updating threads"; + } + } + setJoints(relativePoses, overrideFlags); + updateJoints(relativePoses, absolutePoses); + _invertThreadLoop = !_invertThreadLoop; + } +} + +void Flow::updateAbsolutePoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePoses) { + for (auto &joint : _flowJointData) { + int index = joint.second.getIndex(); + int parentIndex = joint.second.getParentIndex(); + if (index >= 0 && index < (int)relativePoses.size() && + parentIndex >= 0 && parentIndex < (int)absolutePoses.size()) { + absolutePoses[index] = absolutePoses[parentIndex] * relativePoses[index]; + } + } +} + +bool Flow::worldToJointPoint(const AnimPoseVec& absolutePoses, const glm::vec3& position, const int jointIndex, glm::vec3& jointSpacePosition) const { + glm::vec3 jointPos; + glm::quat jointRot; + if (getJointPositionInWorldFrame(absolutePoses, jointIndex, jointPos, _entityPosition, _entityRotation) && + getJointRotationInWorldFrame(absolutePoses, jointIndex, jointRot, _entityRotation)) { + glm::vec3 modelOffset = position - jointPos; + jointSpacePosition = glm::inverse(jointRot) * modelOffset; + return true; + } + return false; +} + +bool Flow::updateRootFramePositions(const AnimPoseVec& absolutePoses, size_t threadIndex) { + auto &joints = _jointThreads[threadIndex]._joints; + int rootIndex = _flowJointData[joints[0]].getParentIndex(); + _jointThreads[threadIndex]._rootFramePositions.clear(); + for (size_t j = 0; j < joints.size(); j++) { + glm::vec3 jointPos; + if (worldToJointPoint(absolutePoses, _flowJointData[joints[j]].getCurrentPosition(), rootIndex, jointPos)) { + _jointThreads[threadIndex]._rootFramePositions.push_back(jointPos); + } else { + return false; + } + } + return true; +} + +void Flow::updateJoints(AnimPoseVec& relativePoses, AnimPoseVec& absolutePoses) { + updateAbsolutePoses(relativePoses, absolutePoses); + for (auto &jointData : _flowJointData) { + int jointIndex = jointData.first; + glm::vec3 jointPosition, parentPosition, jointTranslation; + glm::quat jointRotation, parentWorldRotation; + if (!jointData.second.isHelper()) { + getJointPositionInWorldFrame(absolutePoses, jointIndex, jointPosition, _entityPosition, _entityRotation); + getJointTranslation(relativePoses, jointIndex, jointTranslation); + getJointRotation(relativePoses, jointIndex, jointRotation); + } else { + jointPosition = jointData.second.getCurrentPosition(); + jointTranslation = jointData.second.getCurrentTranslation(); + jointRotation = jointData.second.getCurrentRotation(); + } + getJointPositionInWorldFrame(absolutePoses, jointData.second.getParentIndex(), parentPosition, _entityPosition, _entityRotation); + getJointRotationInWorldFrame(absolutePoses, jointData.second.getParentIndex(), parentWorldRotation, _entityRotation); + jointData.second.setUpdatedData(jointPosition, jointTranslation, jointRotation, parentPosition, parentWorldRotation); + } + auto &selfCollisions = _collisionSystem.getSelfCollisions(); + for (auto &collision : selfCollisions) { + glm::quat jointRotation; + getJointPositionInWorldFrame(absolutePoses, collision._jointIndex, collision._position, _entityPosition, _entityRotation); + getJointRotationInWorldFrame(absolutePoses, collision._jointIndex, jointRotation, _entityRotation); + glm::vec3 worldOffset = jointRotation * collision._offset; + collision._position = collision._position + worldOffset; + } + _collisionSystem.prepareCollisions(); +} + +void Flow::setJoints(AnimPoseVec& relativePoses, const std::vector& overrideFlags) { + for (auto &thread : _jointThreads) { + auto &joints = thread._joints; + for (int jointIndex : joints) { + auto &joint = _flowJointData[jointIndex]; + if (jointIndex >= 0 && jointIndex < (int)relativePoses.size() && !overrideFlags[jointIndex]) { + relativePoses[jointIndex].rot() = joint.getCurrentRotation(); + } + } + } +} + +void Flow::setOthersCollision(const QUuid& otherId, int jointIndex, const glm::vec3& position) { + FlowCollisionSettings settings; + settings._entityID = otherId; + settings._radius = HAND_COLLISION_RADIUS; + _collisionSystem.addCollisionSphere(jointIndex, settings, position, false, true); +} + +void Flow::setPhysicsSettingsForGroup(const QString& group, const FlowPhysicsSettings& settings) { + for (auto &joint : _flowJointData) { + if (joint.second.getGroup().toUpper() == group.toUpper()) { + joint.second.setSettings(settings); + } + } +} + +bool Flow::getJointPositionInWorldFrame(const AnimPoseVec& absolutePoses, int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const { + if (jointIndex >= 0 && jointIndex < (int)absolutePoses.size()) { + glm::vec3 poseSetTrans = absolutePoses[jointIndex].trans(); + position = (rotation * poseSetTrans) + translation; + if (!isNaN(position)) { + return true; + } else { + position = glm::vec3(0.0f); + } + } + return false; +} + +bool Flow::getJointRotationInWorldFrame(const AnimPoseVec& absolutePoses, int jointIndex, glm::quat& result, const glm::quat& rotation) const { + if (jointIndex >= 0 && jointIndex < (int)absolutePoses.size()) { + result = rotation * absolutePoses[jointIndex].rot(); + return true; + } else { + return false; + } +} + +bool Flow::getJointRotation(const AnimPoseVec& relativePoses, int jointIndex, glm::quat& rotation) const { + if (jointIndex >= 0 && jointIndex < (int)relativePoses.size()) { + rotation = relativePoses[jointIndex].rot(); + return true; + } else { + return false; + } +} + +bool Flow::getJointTranslation(const AnimPoseVec& relativePoses, int jointIndex, glm::vec3& translation) const { + if (jointIndex >= 0 && jointIndex < (int)relativePoses.size()) { + translation = relativePoses[jointIndex].trans(); + return true; + } else { + return false; + } +} + +Flow& Flow::operator=(const Flow& otherFlow) { + _active = otherFlow.getActive(); + _scale = otherFlow.getScale(); + _isScaleSet = true; + auto &threads = otherFlow.getThreads(); + if (threads.size() == _jointThreads.size()) { + for (size_t i = 0; i < _jointThreads.size(); i++) { + _jointThreads[i] = threads[i]; + } + } + return *this; +} \ No newline at end of file diff --git a/libraries/animation/src/Flow.h b/libraries/animation/src/Flow.h new file mode 100644 index 0000000000..35464e9420 --- /dev/null +++ b/libraries/animation/src/Flow.h @@ -0,0 +1,328 @@ +// +// Flow.h +// +// Created by Luis Cuenca on 1/21/2019. +// Copyright 2019 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_Flow_h +#define hifi_Flow_h + +#include +#include +#include +#include +#include +#include +#include +#include "AnimPose.h" + +class Rig; +class AnimSkeleton; + +const float HAPTIC_TOUCH_STRENGTH = 0.25f; +const float HAPTIC_TOUCH_DURATION = 10.0f; +const float HAPTIC_SLOPE = 0.18f; +const float HAPTIC_THRESHOLD = 40.0f; + +const QString FLOW_JOINT_PREFIX = "flow"; +const QString SIM_JOINT_PREFIX = "sim"; + +const std::vector HAND_COLLISION_JOINTS = { "RightHandMiddle1", "RightHandThumb3", "LeftHandMiddle1", "LeftHandThumb3", "RightHandMiddle3", "LeftHandMiddle3" }; + +const float HAND_COLLISION_RADIUS = 0.03f; +const float HELPER_JOINT_LENGTH = 0.05f; + +const float DEFAULT_STIFFNESS = 0.0f; +const float DEFAULT_GRAVITY = -0.0096f; +const float DEFAULT_DAMPING = 0.85f; +const float DEFAULT_INERTIA = 0.8f; +const float DEFAULT_DELTA = 0.55f; +const float DEFAULT_RADIUS = 0.01f; + +const uint64_t MAX_UPDATE_FLOW_TIME_BUDGET = 2000; + +struct FlowPhysicsSettings { + FlowPhysicsSettings() {}; + FlowPhysicsSettings(bool active, float stiffness, float gravity, float damping, float inertia, float delta, float radius) { + _active = active; + _stiffness = stiffness; + _gravity = gravity; + _damping = damping; + _inertia = inertia; + _delta = delta; + _radius = radius; + } + bool _active{ true }; + float _stiffness{ DEFAULT_STIFFNESS }; + float _gravity{ DEFAULT_GRAVITY }; + float _damping{ DEFAULT_DAMPING }; + float _inertia{ DEFAULT_INERTIA }; + float _delta{ DEFAULT_DELTA }; + float _radius{ DEFAULT_RADIUS }; +}; + +enum FlowCollisionType { + CollisionSphere = 0 +}; + +struct FlowCollisionSettings { + FlowCollisionSettings() {}; + FlowCollisionSettings(const QUuid& id, const FlowCollisionType& type, const glm::vec3& offset, float radius) { + _entityID = id; + _type = type; + _offset = offset; + _radius = radius; + }; + QUuid _entityID; + FlowCollisionType _type { FlowCollisionType::CollisionSphere }; + float _radius { 0.05f }; + glm::vec3 _offset; +}; + +const FlowPhysicsSettings DEFAULT_JOINT_SETTINGS; + +struct FlowJointInfo { + FlowJointInfo() {}; + FlowJointInfo(int index, int parentIndex, int childIndex, const QString& name) { + _index = index; + _parentIndex = parentIndex; + _childIndex = childIndex; + _name = name; + } + int _index { -1 }; + QString _name; + int _parentIndex { -1 }; + int _childIndex { -1 }; +}; + +struct FlowCollisionResult { + int _count { 0 }; + float _offset { 0.0f }; + glm::vec3 _position; + float _radius { 0.0f }; + glm::vec3 _normal; + float _distance { 0.0f }; +}; + +class FlowCollisionSphere { +public: + FlowCollisionSphere() {}; + FlowCollisionSphere(const int& jointIndex, const FlowCollisionSettings& settings, bool isTouch = false); + void setPosition(const glm::vec3& position) { _position = position; } + FlowCollisionResult computeSphereCollision(const glm::vec3& point, float radius) const; + FlowCollisionResult checkSegmentCollision(const glm::vec3& point1, const glm::vec3& point2, const FlowCollisionResult& collisionResult1, const FlowCollisionResult& collisionResult2); + + QUuid _entityID; + + glm::vec3 _offset; + glm::vec3 _initialOffset; + glm::vec3 _position; + + bool _isTouch { false }; + int _jointIndex { -1 }; + int collisionIndex { -1 }; + float _radius { 0.0f }; + float _initialRadius{ 0.0f }; +}; + +class FlowThread; + +class FlowCollisionSystem { +public: + FlowCollisionSystem() {}; + void addCollisionSphere(int jointIndex, const FlowCollisionSettings& settings, const glm::vec3& position = { 0.0f, 0.0f, 0.0f }, bool isSelfCollision = true, bool isTouch = false); + FlowCollisionResult computeCollision(const std::vector collisions); + + std::vector checkFlowThreadCollisions(FlowThread* flowThread); + + std::vector& getSelfCollisions() { return _selfCollisions; }; + void setOthersCollisions(const std::vector& othersCollisions) { _othersCollisions = othersCollisions; } + void prepareCollisions(); + void resetCollisions(); + void resetOthersCollisions() { _othersCollisions.clear(); } + void setScale(float scale); + FlowCollisionSettings getCollisionSettingsByJoint(int jointIndex); + void setCollisionSettingsByJoint(int jointIndex, const FlowCollisionSettings& settings); + void setActive(bool active) { _active = active; } + bool getActive() const { return _active; } +protected: + std::vector _selfCollisions; + std::vector _othersCollisions; + std::vector _allCollisions; + float _scale { 1.0f }; + bool _active { false }; +}; + +class FlowNode { +public: + FlowNode() {}; + FlowNode(const glm::vec3& initialPosition, FlowPhysicsSettings settings); + + void update(float deltaTime, const glm::vec3& accelerationOffset); + void solve(const glm::vec3& constrainPoint, float maxDistance, const FlowCollisionResult& collision); + void solveConstraints(const glm::vec3& constrainPoint, float maxDistance); + void solveCollisions(const FlowCollisionResult& collision); + +protected: + + FlowPhysicsSettings _settings; + glm::vec3 _initialPosition; + glm::vec3 _previousPosition; + glm::vec3 _currentPosition; + + glm::vec3 _currentVelocity; + glm::vec3 _previousVelocity; + glm::vec3 _acceleration; + + FlowCollisionResult _collision; + FlowCollisionResult _previousCollision; + + float _initialRadius { 0.0f }; + + bool _anchored { false }; + bool _colliding { false }; + bool _active { true }; + + float _scale{ 1.0f }; +}; + +class FlowJoint : public FlowNode { +public: + friend class FlowThread; + + FlowJoint(): FlowNode() {}; + FlowJoint(int jointIndex, int parentIndex, int childIndex, const QString& name, const QString& group, const FlowPhysicsSettings& settings); + void toHelperJoint(const glm::vec3& initialPosition, float length); + void setInitialData(const glm::vec3& initialPosition, const glm::vec3& initialTranslation, const glm::quat& initialRotation, const glm::vec3& parentPosition); + void setUpdatedData(const glm::vec3& updatedPosition, const glm::vec3& updatedTranslation, const glm::quat& updatedRotation, const glm::vec3& parentPosition, const glm::quat& parentWorldRotation); + void setRecoveryPosition(const glm::vec3& recoveryPosition); + void update(float deltaTime); + void solve(const FlowCollisionResult& collision); + + void setScale(float scale, bool initScale); + bool isAnchored() const { return _anchored; } + void setAnchored(bool anchored) { _anchored = anchored; } + bool isHelper() const { return _isHelper; } + + const FlowPhysicsSettings& getSettings() { return _settings; } + void setSettings(const FlowPhysicsSettings& settings) { _settings = settings; } + + const glm::vec3& getCurrentPosition() const { return _currentPosition; } + int getIndex() const { return _index; } + int getParentIndex() const { return _parentIndex; } + void setChildIndex(int index) { _childIndex = index; } + const glm::vec3& getUpdatedPosition() const { return _updatedPosition; } + const QString& getGroup() const { return _group; } + const QString& getName() const { return _name; } + const glm::quat& getCurrentRotation() const { return _currentRotation; } + const glm::vec3& getCurrentTranslation() const { return _initialTranslation; } + const glm::vec3& getInitialPosition() const { return _initialPosition; } + +protected: + + int _index{ -1 }; + int _parentIndex{ -1 }; + int _childIndex{ -1 }; + QString _name; + QString _group; + + bool _isHelper{ false }; + + glm::vec3 _initialTranslation; + glm::quat _initialRotation; + + glm::vec3 _updatedPosition; + glm::vec3 _updatedTranslation; + glm::quat _updatedRotation; + + glm::quat _currentRotation; + glm::vec3 _recoveryPosition; + + glm::vec3 _parentPosition; + glm::quat _parentWorldRotation; + glm::vec3 _translationDirection; + + float _length { 0.0f }; + float _initialLength { 0.0f }; + + bool _applyRecovery { false }; +}; + +class FlowThread { +public: + FlowThread() {}; + FlowThread& operator=(const FlowThread& otherFlowThread); + + FlowThread(int rootIndex, std::map* joints); + + void resetLength(); + void computeFlowThread(int rootIndex); + void computeRecovery(); + void update(float deltaTime); + void solve(FlowCollisionSystem& collisionSystem); + void computeJointRotations(); + void setRootFramePositions(const std::vector& rootFramePositions) { _rootFramePositions = rootFramePositions; } + void setScale(float scale, bool initScale = false); + + std::vector _joints; + std::vector _positions; + float _radius{ 0.0f }; + float _length{ 0.0f }; + std::map* _jointsPointer; + std::vector _rootFramePositions; +}; + +class Flow : public QObject{ + Q_OBJECT +public: + Flow() { } + Flow& operator=(const Flow& otherFlow); + bool getActive() const { return _active; } + void setActive(bool active) { _active = active; } + bool isInitialized() const { return _initialized; } + float getScale() const { return _scale; } + void calculateConstraints(const std::shared_ptr& skeleton, AnimPoseVec& relativePoses, AnimPoseVec& absolutePoses); + void update(float deltaTime, AnimPoseVec& relativePoses, AnimPoseVec& absolutePoses, const std::vector& overrideFlags); + void setTransform(float scale, const glm::vec3& position, const glm::quat& rotation); + const std::map& getJoints() const { return _flowJointData; } + const std::vector& getThreads() const { return _jointThreads; } + void setOthersCollision(const QUuid& otherId, int jointIndex, const glm::vec3& position); + FlowCollisionSystem& getCollisionSystem() { return _collisionSystem; } + void setPhysicsSettingsForGroup(const QString& group, const FlowPhysicsSettings& settings); + void cleanUp(); + +signals: + void onCleanup(); + +private: + void updateAbsolutePoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePoses); + bool getJointPositionInWorldFrame(const AnimPoseVec& absolutePoses, int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const; + bool getJointRotationInWorldFrame(const AnimPoseVec& absolutePoses, int jointIndex, glm::quat& result, const glm::quat& rotation) const; + bool getJointRotation(const AnimPoseVec& relativePoses, int jointIndex, glm::quat& rotation) const; + bool getJointTranslation(const AnimPoseVec& relativePoses, int jointIndex, glm::vec3& translation) const; + bool worldToJointPoint(const AnimPoseVec& absolutePoses, const glm::vec3& position, const int jointIndex, glm::vec3& jointSpacePosition) const; + + void setJoints(AnimPoseVec& relativePoses, const std::vector& overrideFlags); + void updateJoints(AnimPoseVec& relativePoses, AnimPoseVec& absolutePoses); + bool updateRootFramePositions(const AnimPoseVec& absolutePoses, size_t threadIndex); + void setScale(float scale); + + float _scale { 1.0f }; + float _lastScale{ 1.0f }; + glm::vec3 _entityPosition; + glm::quat _entityRotation; + std::map _flowJointData; + std::vector _jointThreads; + std::vector _flowJointKeywords; + FlowCollisionSystem _collisionSystem; + bool _initialized { false }; + bool _active { false }; + bool _isScaleSet { false }; + bool _invertThreadLoop { false }; +}; + +#endif // hifi_Flow_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index be6240017f..9d4c1645d3 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -375,7 +375,6 @@ void Rig::reset(const HFMModel& hfmModel) { _animSkeleton = std::make_shared(hfmModel); - _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -759,7 +758,8 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos glm::vec3 forward = worldRotation * IDENTITY_FORWARD; glm::vec3 workingVelocity = worldVelocity; - + _internalFlow.setTransform(sensorToWorldScale, worldPosition, worldRotation * Quaternions::Y_180); + _networkFlow.setTransform(sensorToWorldScale, worldPosition, worldRotation * Quaternions::Y_180); { glm::vec3 localVel = glm::inverse(worldRotation) * workingVelocity; @@ -1234,12 +1234,26 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons _networkVars = networkTriggersOut; _lastContext = context; } + applyOverridePoses(); - buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); - buildAbsoluteRigPoses(_networkPoseSet._relativePoses, _networkPoseSet._absolutePoses); + + buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); + _internalFlow.update(deltaTime, _internalPoseSet._relativePoses, _internalPoseSet._absolutePoses, _internalPoseSet._overrideFlags); + + if (_sendNetworkNode) { + if (_internalFlow.getActive() && !_networkFlow.getActive()) { + _networkFlow = _internalFlow; + } + buildAbsoluteRigPoses(_networkPoseSet._relativePoses, _networkPoseSet._absolutePoses); + _networkFlow.update(deltaTime, _networkPoseSet._relativePoses, _networkPoseSet._absolutePoses, _internalPoseSet._overrideFlags); + } else if (_networkFlow.getActive()) { + _networkFlow.setActive(false); + } + // copy internal poses to external poses { QWriteLocker writeLock(&_externalPoseSetLock); + _externalPoseSet = _internalPoseSet; } } @@ -1920,7 +1934,6 @@ void Rig::initAnimGraph(const QUrl& url) { auto roleState = roleAnimState.second; overrideRoleAnimation(roleState.role, roleState.url, roleState.fps, roleState.loop, roleState.firstFrame, roleState.lastFrame); } - emit onLoadComplete(); }); connect(_animLoader.get(), &AnimNodeLoader::error, [url](int error, QString str) { @@ -2159,3 +2172,16 @@ void Rig::computeAvatarBoundingCapsule( glm::vec3 capsuleCenter = transformPoint(_geometryToRigTransform, (0.5f * (totalExtents.maximum + totalExtents.minimum))); localOffsetOut = capsuleCenter - hipsPosition; } + +void Rig::initFlow(bool isActive) { + _internalFlow.setActive(isActive); + if (isActive) { + if (!_internalFlow.isInitialized()) { + _internalFlow.calculateConstraints(_animSkeleton, _internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); + _networkFlow.calculateConstraints(_animSkeleton, _internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); + } + } else { + _internalFlow.cleanUp(); + _networkFlow.cleanUp(); + } +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 41c25a3c3e..2f0e2ad65b 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -25,6 +25,7 @@ #include "AnimNodeLoader.h" #include "SimpleMovingAverage.h" #include "AnimUtil.h" +#include "Flow.h" class Rig; class AnimInverseKinematics; @@ -233,6 +234,9 @@ public: const AnimContext::DebugAlphaMap& getDebugAlphaMap() const { return _lastContext.getDebugAlphaMap(); } const AnimVariantMap& getAnimVars() const { return _lastAnimVars; } const AnimContext::DebugStateMachineMap& getStateMachineMap() const { return _lastContext.getStateMachineMap(); } + void initFlow(bool isActive); + Flow& getFlow() { return _internalFlow; } + signals: void onLoadComplete(); @@ -424,6 +428,8 @@ protected: SnapshotBlendPoseHelper _hipsBlendHelper; ControllerParameters _previousControllerParameters; + Flow _internalFlow; + Flow _networkFlow; }; #endif /* defined(__hifi__Rig__) */ diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 60a95ff58a..8c50a195ee 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -175,7 +175,7 @@ static float computeLoudness(int16_t* samples, int numSamples, int numChannels, const int32_t CLIPPING_THRESHOLD = 32392; // -0.1 dBFS const int32_t CLIPPING_DETECTION = 3; // consecutive samples over threshold - float scale = numSamples ? 1.0f / (numSamples * 32768.0f) : 0.0f; + float scale = numSamples ? 1.0f / numSamples : 0.0f; int32_t loudness = 0; isClipping = false; @@ -249,6 +249,8 @@ AudioClient::AudioClient() : _outputBufferSizeFrames("audioOutputBufferFrames", DEFAULT_BUFFER_FRAMES), _sessionOutputBufferSizeFrames(_outputBufferSizeFrames.get()), _outputStarveDetectionEnabled("audioOutputStarveDetectionEnabled", DEFAULT_STARVE_DETECTION_ENABLED), + _lastRawInputLoudness(0.0f), + _lastSmoothedRawInputLoudness(0.0f), _lastInputLoudness(0.0f), _timeSinceLastClip(-1.0f), _muted(false), @@ -1144,6 +1146,9 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) { emit inputReceived(audioBuffer); } + // loudness after mute/gate + _lastInputLoudness = (_muted || !audioGateOpen) ? 0.0f : _lastRawInputLoudness; + // detect gate opening and closing bool openedInLastBlock = !_audioGateOpen && audioGateOpen; // the gate just opened bool closedInLastBlock = _audioGateOpen && !audioGateOpen; // the gate just closed @@ -1222,12 +1227,15 @@ void AudioClient::handleMicAudioInput() { // detect loudness and clipping on the raw input bool isClipping = false; - float inputLoudness = computeLoudness(inputAudioSamples.get(), inputSamplesRequired, _inputFormat.channelCount(), isClipping); + float loudness = computeLoudness(inputAudioSamples.get(), inputSamplesRequired, _inputFormat.channelCount(), isClipping); + _lastRawInputLoudness = loudness; - float tc = (inputLoudness > _lastInputLoudness) ? 0.378f : 0.967f; // 10ms attack, 300ms release @ 100Hz - inputLoudness += tc * (_lastInputLoudness - inputLoudness); - _lastInputLoudness = inputLoudness; + // envelope detection + float tc = (loudness > _lastSmoothedRawInputLoudness) ? 0.378f : 0.967f; // 10ms attack, 300ms release @ 100Hz + loudness += tc * (_lastSmoothedRawInputLoudness - loudness); + _lastSmoothedRawInputLoudness = loudness; + // clipping indicator if (isClipping) { _timeSinceLastClip = 0.0f; } else if (_timeSinceLastClip >= 0.0f) { @@ -1235,7 +1243,7 @@ void AudioClient::handleMicAudioInput() { } isClipping = (_timeSinceLastClip >= 0.0f) && (_timeSinceLastClip < 2.0f); // 2 second hold time - emit inputLoudnessChanged(_lastInputLoudness, isClipping); + emit inputLoudnessChanged(_lastSmoothedRawInputLoudness, isClipping); if (!_muted) { possibleResampling(_inputToNetworkResampler, diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 94ed2ce132..29036b7c71 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -127,7 +127,7 @@ public: const QAudioFormat& getOutputFormat() const { return _outputFormat; } - float getLastInputLoudness() const { return _lastInputLoudness; } // TODO: relative to noise floor? + float getLastInputLoudness() const { return _lastInputLoudness; } float getTimeSinceLastClip() const { return _timeSinceLastClip; } float getAudioAverageInputLoudness() const { return _lastInputLoudness; } @@ -355,7 +355,9 @@ private: StDev _stdev; QElapsedTimer _timeSinceLastReceived; - float _lastInputLoudness; + float _lastRawInputLoudness; // before mute/gate + float _lastSmoothedRawInputLoudness; + float _lastInputLoudness; // after mute/gate float _timeSinceLastClip; int _totalInputAudioSamples; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index b842597b88..d3ae030296 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1535,6 +1535,7 @@ void Avatar::setModelURLFinished(bool success) { // rig is ready void Avatar::rigReady() { buildUnscaledEyeHeightCache(); + buildSpine2SplineRatioCache(); computeMultiSphereShapes(); buildSpine2SplineRatioCache(); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 3d25c275b1..9f713637f5 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -235,6 +235,8 @@ public: virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; } virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; } + virtual glm::vec3 getSpine2SplineOffset() const { return _spine2SplineOffset; } + virtual float getSpine2SplineRatio() const { return _spine2SplineRatio; } // world-space to avatar-space rigconversion functions /**jsdoc diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 9d55d936a2..efd1399f30 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -219,7 +219,6 @@ void EntityTreeRenderer::clearNonLocalEntities() { std::unordered_map savedEntities; // remove all entities from the scene - _space->clear(); auto scene = _viewState->getMain3DScene(); if (scene) { render::Transaction transaction; @@ -259,8 +258,6 @@ void EntityTreeRenderer::clear() { resetEntitiesScriptEngine(); } // remove all entities from the scene - - _space->clear(); auto scene = _viewState->getMain3DScene(); if (scene) { render::Transaction transaction; @@ -1392,4 +1389,4 @@ bool EntityTreeRenderer::removeMaterialFromAvatar(const QUuid& avatarID, graphic return _removeMaterialFromAvatarOperator(avatarID, material, parentMaterialName); } return false; -} \ No newline at end of file +} diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index 96dd1733e7..6638bc0687 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -170,7 +170,7 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch* batch = args->_batch; - transform.setRotation(EntityItem::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode)); + transform.setRotation(EntityItem::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, args->getViewFrustum().getPosition())); transform.postScale(dimensions); batch->setModelTransform(transform); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index c3dae762c5..b61bb2cbda 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -60,7 +60,7 @@ bool ShapeEntityRenderer::needsRenderUpdate() const { } auto mat = _materials.find("0"); - if (mat != _materials.end() && (mat->second.needsUpdate() || mat->second.areTexturesLoading())) { + if (mat != _materials.end() && mat->second.shouldUpdate()) { return true; } @@ -195,7 +195,7 @@ bool ShapeEntityRenderer::useMaterialPipeline(const graphics::MultiMaterial& mat ShapeKey ShapeEntityRenderer::getShapeKey() { auto mat = _materials.find("0"); - if (mat != _materials.end() && (mat->second.needsUpdate() || mat->second.areTexturesLoading())) { + if (mat != _materials.end() && mat->second.shouldUpdate()) { RenderPipelines::updateMultiMaterial(mat->second); } diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 99912e9d91..dfc9277bf0 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -181,7 +181,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { gpu::Batch& batch = *args->_batch; auto transformToTopLeft = modelTransform; - transformToTopLeft.setRotation(EntityItem::getBillboardRotation(transformToTopLeft.getTranslation(), transformToTopLeft.getRotation(), _billboardMode)); + transformToTopLeft.setRotation(EntityItem::getBillboardRotation(transformToTopLeft.getTranslation(), transformToTopLeft.getRotation(), _billboardMode, args->getViewFrustum().getPosition())); transformToTopLeft.postTranslate(dimensions * glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index bf7820fecd..0f2c708d17 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -101,21 +101,15 @@ bool WebEntityRenderer::isTransparent() const { } bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { - if (_contextPosition != entity->getWorldPosition()) { - return true; - } - - { - QSharedPointer webSurface; - withReadLock([&] { - webSurface = _webSurface; - }); - if (webSurface && uvec2(getWindowSize(entity)) != toGlm(webSurface->size())) { + if (resultWithReadLock([&] { + if (_webSurface && uvec2(getWindowSize(entity)) != toGlm(_webSurface->size())) { + return true; + } + + if (_contextPosition != entity->getWorldPosition()) { return true; } - } - if(resultWithReadLock([&] { if (_color != entity->getColor()) { return true; } @@ -194,7 +188,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene auto newContentType = getContentType(newSourceURL); ContentType currentContentType; withReadLock([&] { - urlChanged = _sourceURL != newSourceURL; + urlChanged = newSourceURL.isEmpty() || newSourceURL != _tryingToBuildURL; }); currentContentType = _contentType; @@ -206,7 +200,6 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene } } - withWriteLock([&] { _inputMode = entity->getInputMode(); _dpi = entity->getDPI(); @@ -216,6 +209,8 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene _billboardMode = entity->getBillboardMode(); if (_contentType == ContentType::NoContent) { + _tryingToBuildURL = newSourceURL; + _sourceURL = newSourceURL; return; } @@ -226,10 +221,12 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene if (_webSurface) { if (_webSurface->getRootItem()) { - if (_contentType == ContentType::HtmlContent && urlChanged) { + if (_contentType == ContentType::HtmlContent && _sourceURL != newSourceURL) { _webSurface->getRootItem()->setProperty(URL_PROPERTY, newSourceURL); + _sourceURL = newSourceURL; + } else if (_contentType != ContentType::HtmlContent) { + _sourceURL = newSourceURL; } - _sourceURL = newSourceURL; { auto scriptURL = entity->getScriptURL(); @@ -294,20 +291,21 @@ void WebEntityRenderer::doRender(RenderArgs* args) { }); // Try to update the texture - { - QSharedPointer webSurface; - withReadLock([&] { - webSurface = _webSurface; - }); - if (!webSurface) { - return; + OffscreenQmlSurface::TextureAndFence newTextureAndFence; + bool newTextureAvailable = false; + if (!resultWithReadLock([&] { + if (!_webSurface) { + return false; } - OffscreenQmlSurface::TextureAndFence newTextureAndFence; - bool newTextureAvailable = webSurface->fetchTexture(newTextureAndFence); - if (newTextureAvailable) { - _texture->setExternalTexture(newTextureAndFence.first, newTextureAndFence.second); - } + newTextureAvailable = _webSurface->fetchTexture(newTextureAndFence); + return true; + })) { + return; + } + + if (newTextureAvailable) { + _texture->setExternalTexture(newTextureAndFence.first, newTextureAndFence.second); } static const glm::vec2 texMin(0.0f), texMax(1.0f), topLeft(-0.5f), bottomRight(0.5f); @@ -323,7 +321,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) { }); batch.setResourceTexture(0, _texture); - transform.setRotation(EntityItem::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode)); + transform.setRotation(EntityItem::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, args->getViewFrustum().getPosition())); batch.setModelTransform(transform); // Turn off jitter for these entities @@ -351,6 +349,8 @@ void WebEntityRenderer::buildWebSurface(const EntityItemPointer& entity, const Q _connections.push_back(QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, [entityItemID](const QVariant& message) { emit DependencyManager::get()->webEventReceived(entityItemID, message); })); + + _tryingToBuildURL = newSourceURL; } void WebEntityRenderer::destroyWebSurface() { @@ -383,11 +383,16 @@ glm::vec2 WebEntityRenderer::getWindowSize(const TypedEntityPointer& entity) con void WebEntityRenderer::hoverEnterEntity(const PointerEvent& event) { if (_inputMode == WebInputMode::MOUSE) { handlePointerEvent(event); - } else if (_webSurface) { - PointerEvent webEvent = event; - webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); - _webSurface->hoverBeginEvent(webEvent, _touchDevice); + return; } + + withReadLock([&] { + if (_webSurface) { + PointerEvent webEvent = event; + webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); + _webSurface->hoverBeginEvent(webEvent, _touchDevice); + } + }); } void WebEntityRenderer::hoverLeaveEntity(const PointerEvent& event) { @@ -398,34 +403,39 @@ void WebEntityRenderer::hoverLeaveEntity(const PointerEvent& event) { // QML onReleased is only triggered if a click has happened first. We need to send this "fake" mouse move event to properly trigger an onExited. PointerEvent endMoveEvent(PointerEvent::Move, event.getID()); handlePointerEvent(endMoveEvent); - } else if (_webSurface) { - PointerEvent webEvent = event; - webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); - _webSurface->hoverEndEvent(webEvent, _touchDevice); - } -} - -void WebEntityRenderer::handlePointerEvent(const PointerEvent& event) { - if (_inputMode == WebInputMode::TOUCH) { - handlePointerEventAsTouch(event); - } else { - handlePointerEventAsMouse(event); - } -} - -void WebEntityRenderer::handlePointerEventAsTouch(const PointerEvent& event) { - if (_webSurface) { - PointerEvent webEvent = event; - webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); - _webSurface->handlePointerEvent(webEvent, _touchDevice); - } -} - -void WebEntityRenderer::handlePointerEventAsMouse(const PointerEvent& event) { - if (!_webSurface) { return; } + withReadLock([&] { + if (_webSurface) { + PointerEvent webEvent = event; + webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); + _webSurface->hoverEndEvent(webEvent, _touchDevice); + } + }); +} + +void WebEntityRenderer::handlePointerEvent(const PointerEvent& event) { + withReadLock([&] { + if (!_webSurface) { + return; + } + + if (_inputMode == WebInputMode::TOUCH) { + handlePointerEventAsTouch(event); + } else { + handlePointerEventAsMouse(event); + } + }); +} + +void WebEntityRenderer::handlePointerEventAsTouch(const PointerEvent& event) { + PointerEvent webEvent = event; + webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); + _webSurface->handlePointerEvent(webEvent, _touchDevice); +} + +void WebEntityRenderer::handlePointerEventAsMouse(const PointerEvent& event) { glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi); QPointF windowPoint(windowPos.x, windowPos.y); @@ -459,16 +469,20 @@ void WebEntityRenderer::handlePointerEventAsMouse(const PointerEvent& event) { } void WebEntityRenderer::setProxyWindow(QWindow* proxyWindow) { - if (_webSurface) { - _webSurface->setProxyWindow(proxyWindow); - } + withReadLock([&] { + if (_webSurface) { + _webSurface->setProxyWindow(proxyWindow); + } + }); } QObject* WebEntityRenderer::getEventHandler() { - if (!_webSurface) { - return nullptr; - } - return _webSurface->getEventHandler(); + return resultWithReadLock([&]() -> QObject* { + if (!_webSurface) { + return nullptr; + } + return _webSurface->getEventHandler(); + }); } void WebEntityRenderer::emitScriptEvent(const QVariant& message) { diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 30b63a72df..0345898b62 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -82,6 +82,7 @@ private: QSharedPointer _webSurface { nullptr }; bool _cachedWebSurface { false }; gpu::TexturePointer _texture; + QString _tryingToBuildURL; glm::u8vec3 _color; float _alpha { 1.0f }; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 9f11b3c018..5dc218bc3b 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -49,7 +49,8 @@ int EntityItem::_maxActionsDataSize = 800; quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND; QString EntityItem::_marketplacePublicKey; -std::function EntityItem::_getBillboardRotationOperator = [](const glm::vec3&, const glm::quat& rotation, BillboardMode) { return rotation; }; +std::function EntityItem::_getBillboardRotationOperator = [](const glm::vec3&, const glm::quat& rotation, BillboardMode, const glm::vec3&) { return rotation; }; +std::function EntityItem::_getPrimaryViewFrustumPositionOperator = []() { return glm::vec3(0.0f); }; EntityItem::EntityItem(const EntityItemID& entityItemID) : SpatiallyNestable(NestableType::Entity, entityItemID) @@ -266,7 +267,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_HREF, getHref()); APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription()); APPEND_ENTITY_PROPERTY(PROP_POSITION, getLocalPosition()); - APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, getUnscaledDimensions()); + 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()); @@ -818,7 +819,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef }; READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, customUpdatePositionFromNetwork); } - READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, setUnscaledDimensions); + READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, setScaledDimensions); { // See comment above auto customUpdateRotationFromNetwork = [this, shouldUpdate, lastEdited](glm::quat value) { if (shouldUpdate(_lastUpdatedRotationTimestamp, value != _lastUpdatedRotationValue)) { @@ -1315,7 +1316,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire 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, getUnscaledDimensions); + 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); @@ -1462,7 +1463,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { 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, setUnscaledDimensions); + 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); @@ -1872,7 +1873,7 @@ glm::vec3 EntityItem::getScaledDimensions() const { void EntityItem::setScaledDimensions(const glm::vec3& value) { glm::vec3 parentScale = getSNScale(); - setUnscaledDimensions(value * parentScale); + setUnscaledDimensions(value / parentScale); } void EntityItem::setUnscaledDimensions(const glm::vec3& value) { diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 824261c022..0ca851e228 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -557,8 +557,10 @@ public: virtual void removeGrab(GrabPointer grab) override; virtual void disableGrab(GrabPointer grab) override; - static void setBillboardRotationOperator(std::function getBillboardRotationOperator) { _getBillboardRotationOperator = getBillboardRotationOperator; } - static glm::quat getBillboardRotation(const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode) { return _getBillboardRotationOperator(position, rotation, billboardMode); } + static void setBillboardRotationOperator(std::function getBillboardRotationOperator) { _getBillboardRotationOperator = getBillboardRotationOperator; } + static glm::quat getBillboardRotation(const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode, const glm::vec3& frustumPos) { return _getBillboardRotationOperator(position, rotation, billboardMode, frustumPos); } + static void setPrimaryViewFrustumPositionOperator(std::function getPrimaryViewFrustumPositionOperator) { _getPrimaryViewFrustumPositionOperator = getPrimaryViewFrustumPositionOperator; } + static glm::vec3 getPrimaryViewFrustumPosition() { return _getPrimaryViewFrustumPositionOperator(); } signals: void requestRenderUpdate(); @@ -748,7 +750,8 @@ protected: QHash _grabActions; private: - static std::function _getBillboardRotationOperator; + static std::function _getBillboardRotationOperator; + static std::function _getPrimaryViewFrustumPositionOperator; }; #endif // hifi_EntityItem_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 6738b1cedd..7575763bf9 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -580,6 +580,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { // 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); @@ -1012,6 +1013,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @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 {Vec3} modelScale - The scale factor applied to the model's dimensions. Deprecated. * @property {Color} color=255,255,255 - Currently not used. * @property {string} modelURL="" - The URL of the FBX of OBJ model. Baked FBX models' URLs end in ".baked.fbx".
* @property {string} textures="" - A JSON string of texture name, URL pairs used when rendering the model in place of the @@ -1683,6 +1685,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); COPY_PROPERTY_TO_QSCRIPTVALUE(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); @@ -2078,6 +2081,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool // 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); @@ -2357,6 +2361,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { // 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); @@ -2700,6 +2705,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr // 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); @@ -3989,6 +3995,7 @@ void EntityItemProperties::markAllChanged() { // Model _modelURLChanged = true; + _modelScaleChanged = true; _jointRotationsSetChanged = true; _jointRotationsChanged = true; _jointTranslationsSetChanged = true; @@ -4526,6 +4533,9 @@ QList EntityItemProperties::listChangedProperties() { if (modelURLChanged()) { out += "modelURL"; } + if (modelScaleChanged()) { + out += "scale"; + } if (jointRotationsSetChanged()) { out += "jointRotationsSet"; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 712f2d120f..afc3537559 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -279,6 +279,7 @@ public: // 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()); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 093df92dc1..cce30c9614 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -202,22 +202,23 @@ enum EntityPropertyList { // Model PROP_MODEL_URL = PROP_DERIVED_0, - PROP_JOINT_ROTATIONS_SET = PROP_DERIVED_1, - PROP_JOINT_ROTATIONS = PROP_DERIVED_2, - PROP_JOINT_TRANSLATIONS_SET = PROP_DERIVED_3, - PROP_JOINT_TRANSLATIONS = PROP_DERIVED_4, - PROP_RELAY_PARENT_JOINTS = PROP_DERIVED_5, - PROP_GROUP_CULLED = PROP_DERIVED_6, + 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, // Animation - PROP_ANIMATION_URL = PROP_DERIVED_7, - PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_8, - PROP_ANIMATION_FPS = PROP_DERIVED_9, - PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_10, - PROP_ANIMATION_PLAYING = PROP_DERIVED_11, - PROP_ANIMATION_LOOP = PROP_DERIVED_12, - PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_13, - PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_14, - PROP_ANIMATION_HOLD = PROP_DERIVED_15, + PROP_ANIMATION_URL = PROP_DERIVED_8, + PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_9, + PROP_ANIMATION_FPS = PROP_DERIVED_10, + PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_11, + PROP_ANIMATION_PLAYING = PROP_DERIVED_12, + PROP_ANIMATION_LOOP = PROP_DERIVED_13, + PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_14, + PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_15, + PROP_ANIMATION_HOLD = PROP_DERIVED_16, // Light PROP_IS_SPOTLIGHT = PROP_DERIVED_0, diff --git a/libraries/entities/src/ImageEntityItem.cpp b/libraries/entities/src/ImageEntityItem.cpp index 837e824f4a..090ae91277 100644 --- a/libraries/entities/src/ImageEntityItem.cpp +++ b/libraries/entities/src/ImageEntityItem.cpp @@ -159,7 +159,7 @@ bool ImageEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getWorldOrientation(); glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); - rotation = EntityItem::getBillboardRotation(position, rotation, _billboardMode); + rotation = EntityItem::getBillboardRotation(position, rotation, _billboardMode, EntityItem::getPrimaryViewFrustumPosition()); if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { glm::vec3 forward = rotation * Vectors::FRONT; diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index e365d0a7b6..bb8f375302 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -63,6 +63,7 @@ EntityItemProperties ModelEntityItem::getProperties(const EntityPropertyFlags& d COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); 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); @@ -85,6 +86,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) { 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); @@ -128,6 +130,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, 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); @@ -165,6 +168,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& 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; @@ -192,6 +196,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit 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()); @@ -708,3 +713,15 @@ bool ModelEntityItem::applyNewAnimationProperties(AnimationPropertyGroup newProp } return somethingChanged; } + +glm::vec3 ModelEntityItem::getModelScale() const { + return _modelScaleLock.resultWithReadLock([&] { + return getSNScale(); + }); +} + +void ModelEntityItem::setModelScale(const glm::vec3& modelScale) { + _modelScaleLock.withWriteLock([&] { + setSNScale(modelScale); + }); +} \ No newline at end of file diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 649a6cb50f..234cfa435e 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -126,6 +126,9 @@ public: QVector getJointTranslations() const; QVector getJointTranslationsSet() const; + glm::vec3 getModelScale() const; + void setModelScale(const glm::vec3& modelScale); + private: void setAnimationSettings(const QString& value); // only called for old bitstream format bool applyNewAnimationProperties(AnimationPropertyGroup newProperties); @@ -141,6 +144,7 @@ protected: // 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; + ReadWriteLockable _modelScaleLock; 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 diff --git a/libraries/entities/src/MovingEntitiesOperator.cpp b/libraries/entities/src/MovingEntitiesOperator.cpp index 4b908745e0..9dd5a4d206 100644 --- a/libraries/entities/src/MovingEntitiesOperator.cpp +++ b/libraries/entities/src/MovingEntitiesOperator.cpp @@ -55,13 +55,11 @@ void MovingEntitiesOperator::addEntityToMoveList(EntityItemPointer entity, const qCDebug(entities) << " oldContainingElement->bestFitBounds(newCubeClamped):" << oldContainingElement->bestFitBounds(newCubeClamped); } else { - qCDebug(entities) << " WARNING NO OLD CONTAINING ELEMENT!!!"; + qCDebug(entities) << " WARNING NO OLD CONTAINING ELEMENT for entity" << entity->getEntityItemID(); } } - + if (!oldContainingElement) { - qCDebug(entities) << "UNEXPECTED!!!! attempting to move entity "<< entity->getEntityItemID() - << "that has no containing element. "; return; // bail without adding. } diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index bc98c61ff7..5dff645c89 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -199,7 +199,7 @@ bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getWorldOrientation(); glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); - rotation = EntityItem::getBillboardRotation(position, rotation, _billboardMode); + rotation = EntityItem::getBillboardRotation(position, rotation, _billboardMode, EntityItem::getPrimaryViewFrustumPosition()); if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { glm::vec3 forward = rotation * Vectors::FRONT; diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index 5a948fbfd4..0748790df9 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -180,7 +180,7 @@ bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const g glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getWorldOrientation(); glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); - rotation = EntityItem::getBillboardRotation(position, rotation, _billboardMode); + rotation = EntityItem::getBillboardRotation(position, rotation, _billboardMode, EntityItem::getPrimaryViewFrustumPosition()); if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { glm::vec3 forward = rotation * Vectors::FRONT; diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index fdddf3640a..d24e906f98 100755 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -456,11 +456,11 @@ public: graphics::MaterialKey getMaterialKey() const { return graphics::MaterialKey(_schemaBuffer.get()._key); } const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; } - bool needsUpdate() const { return _needsUpdate; } void setNeedsUpdate(bool needsUpdate) { _needsUpdate = needsUpdate; } - void setTexturesLoading(bool value) { _texturesLoading = value; } - bool areTexturesLoading() const { return _texturesLoading; } + void setInitialized() { _initialized = true; } + + bool shouldUpdate() const { return !_initialized || _needsUpdate || _texturesLoading; } int getTextureCount() const { calculateMaterialInfo(); return _textureCount; } size_t getTextureSize() const { calculateMaterialInfo(); return _textureSize; } @@ -471,6 +471,7 @@ private: gpu::TextureTablePointer _textureTable { std::make_shared() }; bool _needsUpdate { false }; bool _texturesLoading { false }; + bool _initialized { false }; mutable size_t _textureSize { 0 }; mutable int _textureCount { 0 }; diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 9145b4a79e..f4221e3d49 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -315,7 +315,9 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { // wasn't an address - lookup the place name // we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after - attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path(), trigger); + if (!lookupUrl.host().isNull() && !lookupUrl.host().isEmpty()) { + attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path(), trigger); + } } } @@ -337,7 +339,7 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { // be loaded over http(s) // lookupUrl.scheme() == URL_SCHEME_HTTP || // lookupUrl.scheme() == HIFI_URL_SCHEME_HTTPS || - // TODO once a file can return a connection refusal if there were to be some kind of load error, we'd + // TODO once a file can return a connection refusal if there were to be some kind of load error, we'd // need to store the previous domain tried in _lastVisitedURL. For now , do not store it. _previousAPILookup.clear(); diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 8b9e37569c..eaa02f059e 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -645,103 +645,95 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, Node::LocalID localID, bool isReplicated, bool isUpstream, const QUuid& connectionSecret, const NodePermissions& permissions) { - QReadLocker readLocker(&_nodeMutex); - NodeHash::const_iterator it = _nodeHash.find(uuid); + { + QReadLocker readLocker(&_nodeMutex); + NodeHash::const_iterator it = _nodeHash.find(uuid); - if (it != _nodeHash.end()) { - SharedNodePointer& matchingNode = it->second; + if (it != _nodeHash.end()) { + SharedNodePointer& matchingNode = it->second; - matchingNode->setPublicSocket(publicSocket); - matchingNode->setLocalSocket(localSocket); - matchingNode->setPermissions(permissions); - matchingNode->setConnectionSecret(connectionSecret); - matchingNode->setIsReplicated(isReplicated); - matchingNode->setIsUpstream(isUpstream || NodeType::isUpstream(nodeType)); - matchingNode->setLocalID(localID); + matchingNode->setPublicSocket(publicSocket); + matchingNode->setLocalSocket(localSocket); + matchingNode->setPermissions(permissions); + matchingNode->setConnectionSecret(connectionSecret); + matchingNode->setIsReplicated(isReplicated); + matchingNode->setIsUpstream(isUpstream || NodeType::isUpstream(nodeType)); + matchingNode->setLocalID(localID); - return matchingNode; - } else { - auto it = _connectionIDs.find(uuid); - if (it == _connectionIDs.end()) { - _connectionIDs[uuid] = INITIAL_CONNECTION_ID; + return matchingNode; } - - // we didn't have this node, so add them - Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket); - newNode->setIsReplicated(isReplicated); - newNode->setIsUpstream(isUpstream || NodeType::isUpstream(nodeType)); - newNode->setConnectionSecret(connectionSecret); - newNode->setPermissions(permissions); - newNode->setLocalID(localID); - - // move the newly constructed node to the LNL thread - newNode->moveToThread(thread()); - - if (nodeType == NodeType::AudioMixer) { - LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer); - } - - SharedNodePointer newNodePointer(newNode, &QObject::deleteLater); - - // if this is a solo node type, we assume that the DS has replaced its assignment and we should kill the previous node - if (SOLO_NODE_TYPES.count(newNode->getType())) { - // while we still have the read lock, see if there is a previous solo node we'll need to remove - auto previousSoloIt = std::find_if(_nodeHash.cbegin(), _nodeHash.cend(), [newNode](const UUIDNodePair& nodePair){ - return nodePair.second->getType() == newNode->getType(); - }); - - if (previousSoloIt != _nodeHash.cend()) { - // we have a previous solo node, switch to a write lock so we can remove it - readLocker.unlock(); - - QWriteLocker writeLocker(&_nodeMutex); - - auto oldSoloNode = previousSoloIt->second; - - _localIDMap.unsafe_erase(oldSoloNode->getLocalID()); - _nodeHash.unsafe_erase(previousSoloIt); - handleNodeKill(oldSoloNode); - - // convert the current lock back to a read lock for insertion of new node - writeLocker.unlock(); - readLocker.relock(); - } - } - - // insert the new node and release our read lock -#if defined(Q_OS_ANDROID) || (defined(__clang__) && defined(Q_OS_LINUX)) - _nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer)); - _localIDMap.insert(std::pair(localID, newNodePointer)); -#else - _nodeHash.emplace(newNode->getUUID(), newNodePointer); - _localIDMap.emplace(localID, newNodePointer); -#endif - readLocker.unlock(); - - qCDebug(networking) << "Added" << *newNode; - - auto weakPtr = newNodePointer.toWeakRef(); // We don't want the lambdas to hold a strong ref - - emit nodeAdded(newNodePointer); - if (newNodePointer->getActiveSocket()) { - emit nodeActivated(newNodePointer); - } else { - connect(newNodePointer.data(), &NetworkPeer::socketActivated, this, [this, weakPtr] { - auto sharedPtr = weakPtr.lock(); - if (sharedPtr) { - emit nodeActivated(sharedPtr); - disconnect(sharedPtr.data(), &NetworkPeer::socketActivated, this, 0); - } - }); - } - - // Signal when a socket changes, so we can start the hole punch over. - connect(newNodePointer.data(), &NetworkPeer::socketUpdated, this, [this, weakPtr] { - emit nodeSocketUpdated(weakPtr); - }); - - return newNodePointer; } + + auto removeOldNode = [&](auto node) { + if (node) { + QWriteLocker writeLocker(&_nodeMutex); + _localIDMap.unsafe_erase(node->getLocalID()); + _nodeHash.unsafe_erase(node->getUUID()); + handleNodeKill(node); + } + }; + + // if this is a solo node type, we assume that the DS has replaced its assignment and we should kill the previous node + if (SOLO_NODE_TYPES.count(nodeType)) { + removeOldNode(soloNodeOfType(nodeType)); + } + // If there is a new node with the same socket, this is a reconnection, kill the old node + removeOldNode(findNodeWithAddr(publicSocket)); + removeOldNode(findNodeWithAddr(localSocket)); + + auto it = _connectionIDs.find(uuid); + if (it == _connectionIDs.end()) { + _connectionIDs[uuid] = INITIAL_CONNECTION_ID; + } + + // we didn't have this node, so add them + Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket); + newNode->setIsReplicated(isReplicated); + newNode->setIsUpstream(isUpstream || NodeType::isUpstream(nodeType)); + newNode->setConnectionSecret(connectionSecret); + newNode->setPermissions(permissions); + newNode->setLocalID(localID); + + // move the newly constructed node to the LNL thread + newNode->moveToThread(thread()); + + if (nodeType == NodeType::AudioMixer) { + LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer); + } + + SharedNodePointer newNodePointer(newNode, &QObject::deleteLater); + + + { + QReadLocker readLocker(&_nodeMutex); + // insert the new node and release our read lock + _nodeHash.insert({ newNode->getUUID(), newNodePointer }); + _localIDMap.insert({ localID, newNodePointer }); + } + + qCDebug(networking) << "Added" << *newNode; + + auto weakPtr = newNodePointer.toWeakRef(); // We don't want the lambdas to hold a strong ref + + emit nodeAdded(newNodePointer); + if (newNodePointer->getActiveSocket()) { + emit nodeActivated(newNodePointer); + } else { + connect(newNodePointer.data(), &NetworkPeer::socketActivated, this, [this, weakPtr] { + auto sharedPtr = weakPtr.lock(); + if (sharedPtr) { + emit nodeActivated(sharedPtr); + disconnect(sharedPtr.data(), &NetworkPeer::socketActivated, this, 0); + } + }); + } + + // Signal when a socket changes, so we can start the hole punch over. + connect(newNodePointer.data(), &NetworkPeer::socketUpdated, this, [this, weakPtr] { + emit nodeSocketUpdated(weakPtr); + }); + + return newNodePointer; } std::unique_ptr LimitedNodeList::constructPingPacket(const QUuid& nodeId, PingType_t pingType) { diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 5f55c189ce..b6fcd2f2ce 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -262,6 +262,7 @@ enum class EntityVersion : PacketVersion { RingGizmoEntities, ShowKeyboardFocusHighlight, WebBillboardMode, + ModelScale, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 66ce5f32bf..a5f1a0598f 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -781,18 +781,18 @@ void CharacterController::updateState() { const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight); if ((velocity.dot(_currentUp) <= (jumpSpeed / 2.0f)) && ((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport)) { SET_STATE(State::Ground, "hit ground"); - } else if (_flyingAllowed) { + } else if (_zoneFlyingAllowed) { btVector3 desiredVelocity = _targetVelocity; if (desiredVelocity.length2() < MIN_TARGET_SPEED_SQUARED) { desiredVelocity = btVector3(0.0f, 0.0f, 0.0f); } bool vertTargetSpeedIsNonZero = desiredVelocity.dot(_currentUp) > MIN_TARGET_SPEED; - if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (_takeoffJumpButtonID != _jumpButtonDownCount)) { + if (_comfortFlyingAllowed && (jumpButtonHeld || vertTargetSpeedIsNonZero) && (_takeoffJumpButtonID != _jumpButtonDownCount)) { SET_STATE(State::Hover, "double jump button"); - } else if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) { + } else if (_comfortFlyingAllowed && (jumpButtonHeld || vertTargetSpeedIsNonZero) && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) { SET_STATE(State::Hover, "jump button held"); - } else if (_floorDistance > _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT) { - // Transition to hover if we are above the fall threshold + } else if ((!rayHasHit && !_hasSupport) || _floorDistance > _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT) { + // Transition to hover if there's no ground beneath us or we are above the fall threshold, regardless of _comfortFlyingAllowed SET_STATE(State::Hover, "above fall threshold"); } } @@ -801,8 +801,10 @@ void CharacterController::updateState() { case State::Hover: btScalar horizontalSpeed = (velocity - velocity.dot(_currentUp) * _currentUp).length(); bool flyingFast = horizontalSpeed > (MAX_WALKING_SPEED * 0.75f); - if (!_flyingAllowed) { - SET_STATE(State::InAir, "flying not allowed"); + if (!_zoneFlyingAllowed) { + SET_STATE(State::InAir, "zone flying not allowed"); + } else if (!_comfortFlyingAllowed && (rayHasHit || _hasSupport || _floorDistance < FLY_TO_GROUND_THRESHOLD)) { + SET_STATE(State::InAir, "comfort flying not allowed"); } else if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) { SET_STATE(State::InAir, "near ground"); } else if (((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport) && !flyingFast) { @@ -847,12 +849,6 @@ bool CharacterController::getRigidBodyLocation(glm::vec3& avatarRigidBodyPositio return true; } -void CharacterController::setFlyingAllowed(bool value) { - if (value != _flyingAllowed) { - _flyingAllowed = value; - } -} - void CharacterController::setCollisionlessAllowed(bool value) { if (value != _collisionlessAllowed) { _collisionlessAllowed = value; diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index d59374a94a..c46c9c8361 100755 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -65,10 +65,10 @@ public: // overrides from btCharacterControllerInterface virtual void setWalkDirection(const btVector3 &walkDirection) override { assert(false); } virtual void setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval) override { assert(false); } - virtual void reset(btCollisionWorld* collisionWorld) override { } - virtual void warp(const btVector3& origin) override { } - virtual void debugDraw(btIDebugDraw* debugDrawer) override { } - virtual void setUpInterpolate(bool value) override { } + virtual void reset(btCollisionWorld* collisionWorld) override {} + virtual void warp(const btVector3& origin) override {} + virtual void debugDraw(btIDebugDraw* debugDrawer) override {} + virtual void setUpInterpolate(bool value) override {} virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTime) override; virtual void preStep(btCollisionWorld *collisionWorld) override; virtual void playerStep(btCollisionWorld *collisionWorld, btScalar dt) override; @@ -90,7 +90,7 @@ public: void preSimulation(); void postSimulation(); - void setPositionAndOrientation( const glm::vec3& position, const glm::quat& orientation); + void setPositionAndOrientation(const glm::vec3& position, const glm::quat& orientation); void getPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const; void setParentVelocity(const glm::vec3& parentVelocity); @@ -129,7 +129,8 @@ public: bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); - void setFlyingAllowed(bool value); + void setZoneFlyingAllowed(bool value) { _zoneFlyingAllowed = value; } + void setComfortFlyingAllowed(bool value) { _comfortFlyingAllowed = value; } void setCollisionlessAllowed(bool value); void setPendingFlagsUpdateCollisionMask(){ _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK; } @@ -212,7 +213,8 @@ protected: uint32_t _pendingFlags { 0 }; uint32_t _previousFlags { 0 }; - bool _flyingAllowed { true }; + bool _zoneFlyingAllowed { true }; + bool _comfortFlyingAllowed { true }; bool _collisionlessAllowed { true }; bool _collisionless { false }; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 81a81c5602..cfb78d6bbc 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -178,6 +178,7 @@ void CauterizedModel::updateClusterMatrices() { } } } + computeMeshPartLocalBounds(); // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 6409cdd231..b1104b8aad 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -83,7 +83,7 @@ void MeshPartPayload::updateKey(const render::ItemKey& key) { ItemKey::Builder builder(key); builder.withTypeShape(); - if (_drawMaterials.needsUpdate() || _drawMaterials.areTexturesLoading()) { + if (_drawMaterials.shouldUpdate()) { RenderPipelines::updateMultiMaterial(_drawMaterials); } @@ -329,7 +329,7 @@ void ModelMeshPartPayload::updateKey(const render::ItemKey& key) { builder.withDeformed(); } - if (_drawMaterials.needsUpdate() || _drawMaterials.areTexturesLoading()) { + if (_drawMaterials.shouldUpdate()) { RenderPipelines::updateMultiMaterial(_drawMaterials); } @@ -347,7 +347,7 @@ void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, PrimitiveMode pr return; } - if (_drawMaterials.needsUpdate() || _drawMaterials.areTexturesLoading()) { + if (_drawMaterials.shouldUpdate()) { RenderPipelines::updateMultiMaterial(_drawMaterials); } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b9b294d0e3..9bb3f72b31 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1169,6 +1169,7 @@ void Model::setURL(const QUrl& url) { resource->setLoadPriority(this, _loadingPriority); _renderWatcher.setResource(resource); } + _rig.initFlow(false); onInvalidate(); } @@ -1385,6 +1386,7 @@ void Model::updateClusterMatrices() { } } } + computeMeshPartLocalBounds(); // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 6f6f2ab856..c4a7368f39 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -382,11 +382,6 @@ void RenderPipelines::bindMaterial(graphics::MaterialPointer& material, gpu::Bat void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial) { auto& schemaBuffer = multiMaterial.getSchemaBuffer(); - if (multiMaterial.size() == 0) { - schemaBuffer.edit() = graphics::MultiMaterial::Schema(); - return; - } - auto& drawMaterialTextures = multiMaterial.getTextureTable(); multiMaterial.setTexturesLoading(false); @@ -732,14 +727,11 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial schema._key = (uint32_t)schemaKey._flags.to_ulong(); schemaBuffer.edit() = schema; multiMaterial.setNeedsUpdate(false); + multiMaterial.setInitialized(); } void RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures) { - if (multiMaterial.size() == 0) { - return; - } - - if (multiMaterial.needsUpdate() || multiMaterial.areTexturesLoading()) { + if (multiMaterial.shouldUpdate()) { updateMultiMaterial(multiMaterial); } diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 19fafdccf4..e7bdc87bf3 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -861,7 +861,7 @@ void SpatiallyNestable::setSNScale(const glm::vec3& scale, bool& success) { } }); if (success && changed) { - locationChanged(); + dimensionsChanged(); } } @@ -1420,11 +1420,16 @@ QUuid SpatiallyNestable::getEditSenderID() { return editSenderID; } -void SpatiallyNestable::bumpAncestorChainRenderableVersion() const { +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(); + parent->bumpAncestorChainRenderableVersion(depth + 1); } -} \ No newline at end of file +} diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 495c941c07..a802a25e89 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -221,7 +221,7 @@ public: bool hasGrabs(); virtual QUuid getEditSenderID(); - void bumpAncestorChainRenderableVersion() const; + void bumpAncestorChainRenderableVersion(int depth = 0) const; protected: QUuid _id; diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index 8dd94623b7..8ecc982dab 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -325,6 +325,11 @@ leftMargin: domainNameLeftMargin }; + // check to be sure we are going to look for an actual domain + if (!domain) { + doRequest = false; + } + if (doRequest) { var url = Account.metaverseServerURL + '/api/v1/places/' + domain; request({ diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 767128ca16..269283ea6d 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -708,7 +708,7 @@ SelectionDisplay = (function() { shape: "Cone", solid: true, visible: false, - ignoreRayIntersection: false, + ignorePickIntersection: true, drawInFront: true }; var handlePropertiesTranslateArrowCylinders = { @@ -716,7 +716,7 @@ SelectionDisplay = (function() { shape: "Cylinder", solid: true, visible: false, - ignoreRayIntersection: false, + ignorePickIntersection: true, drawInFront: true }; var handleTranslateXCone = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCones); @@ -741,7 +741,7 @@ SelectionDisplay = (function() { majorTickMarksAngle: ROTATE_DEFAULT_TICK_MARKS_ANGLE, majorTickMarksLength: 0.1, visible: false, - ignoreRayIntersection: false, + ignorePickIntersection: true, drawInFront: true }; var handleRotatePitchRing = Overlays.addOverlay("circle3d", handlePropertiesRotateRings); @@ -766,7 +766,7 @@ SelectionDisplay = (function() { solid: true, innerRadius: 0.9, visible: false, - ignoreRayIntersection: true, + ignorePickIntersection: true, drawInFront: true }); @@ -779,7 +779,7 @@ SelectionDisplay = (function() { visible: false, isFacingAvatar: true, drawInFront: true, - ignoreRayIntersection: true, + ignorePickIntersection: true, dimensions: { x: 0, y: 0 }, lineHeight: 0.0, topMargin: 0, @@ -791,7 +791,7 @@ SelectionDisplay = (function() { var handlePropertiesStretchCubes = { solid: true, visible: false, - ignoreRayIntersection: false, + ignorePickIntersection: true, drawInFront: true }; var handleStretchXCube = Overlays.addOverlay("cube", handlePropertiesStretchCubes); @@ -802,18 +802,17 @@ SelectionDisplay = (function() { Overlays.editOverlay(handleStretchZCube, { color: COLOR_BLUE }); var handlePropertiesStretchPanel = { - shape: "Quad", alpha: 0.5, solid: true, visible: false, - ignoreRayIntersection: true, + ignorePickIntersection: true, drawInFront: true }; - var handleStretchXPanel = Overlays.addOverlay("shape", handlePropertiesStretchPanel); + var handleStretchXPanel = Overlays.addOverlay("cube", handlePropertiesStretchPanel); Overlays.editOverlay(handleStretchXPanel, { color: COLOR_RED }); - var handleStretchYPanel = Overlays.addOverlay("shape", handlePropertiesStretchPanel); + var handleStretchYPanel = Overlays.addOverlay("cube", handlePropertiesStretchPanel); Overlays.editOverlay(handleStretchYPanel, { color: COLOR_GREEN }); - var handleStretchZPanel = Overlays.addOverlay("shape", handlePropertiesStretchPanel); + var handleStretchZPanel = Overlays.addOverlay("cube", handlePropertiesStretchPanel); Overlays.editOverlay(handleStretchZPanel, { color: COLOR_BLUE }); var handleScaleCube = Overlays.addOverlay("cube", { @@ -821,7 +820,7 @@ SelectionDisplay = (function() { color: COLOR_SCALE_CUBE, solid: true, visible: false, - ignoreRayIntersection: false, + ignorePickIntersection: true, drawInFront: true, borderSize: 1.4 }); @@ -841,7 +840,7 @@ SelectionDisplay = (function() { color: COLOR_GREEN, solid: true, visible: false, - ignoreRayIntersection: false, + ignorePickIntersection: true, drawInFront: true, borderSize: 1.4 }); @@ -854,6 +853,7 @@ SelectionDisplay = (function() { alpha: 0, solid: false, visible: false, + ignorePickIntersection: true, dashed: false }); @@ -865,6 +865,7 @@ SelectionDisplay = (function() { alpha: 0, solid: false, visible: false, + ignorePickIntersection: true, dashed: false }); @@ -877,7 +878,7 @@ SelectionDisplay = (function() { green: 0, blue: 0 }, - ignoreRayIntersection: true // always ignore this + ignorePickIntersection: true // always ignore this }); var yRailOverlay = Overlays.addOverlay("line3d", { visible: false, @@ -888,7 +889,7 @@ SelectionDisplay = (function() { green: 255, blue: 0 }, - ignoreRayIntersection: true // always ignore this + ignorePickIntersection: true // always ignore this }); var zRailOverlay = Overlays.addOverlay("line3d", { visible: false, @@ -899,7 +900,7 @@ SelectionDisplay = (function() { green: 0, blue: 255 }, - ignoreRayIntersection: true // always ignore this + ignorePickIntersection: true // always ignore this }); var allOverlays = [ @@ -972,7 +973,7 @@ SelectionDisplay = (function() { color: COLOR_DEBUG_PICK_PLANE, solid: true, visible: false, - ignoreRayIntersection: true, + ignorePickIntersection: true, drawInFront: false }); var debugPickPlaneHits = []; @@ -1802,6 +1803,7 @@ SelectionDisplay = (function() { isActiveTool(handleRotateYawRing) || isActiveTool(handleRotateRollRing); selectionBoxGeometry.visible = !inModeRotate && !isCameraInsideBox; + selectionBoxGeometry.ignorePickIntersection = !selectionBoxGeometry.visible; Overlays.editOverlay(selectionBox, selectionBoxGeometry); // UPDATE ICON TRANSLATE HANDLE @@ -1811,9 +1813,13 @@ SelectionDisplay = (function() { rotation: rotation }; iconSelectionBoxGeometry.visible = !inModeRotate && isCameraInsideBox; + iconSelectionBoxGeometry.ignorePickIntersection = !iconSelectionBoxGeometry.visible; Overlays.editOverlay(iconSelectionBox, iconSelectionBoxGeometry); } else { - Overlays.editOverlay(iconSelectionBox, { visible: false }); + Overlays.editOverlay(iconSelectionBox, { + visible: false, + ignorePickIntersection: true + }); } // UPDATE DUPLICATOR (CURRENTLY HIDDEN FOR NOW) @@ -1882,7 +1888,7 @@ SelectionDisplay = (function() { // FUNCTION: SET OVERLAYS VISIBLE that.setOverlaysVisible = function(isVisible) { for (var i = 0, length = allOverlays.length; i < length; i++) { - Overlays.editOverlay(allOverlays[i], { visible: isVisible }); + Overlays.editOverlay(allOverlays[i], { visible: isVisible, ignorePickIntersection: !isVisible }); } }; @@ -1894,18 +1900,18 @@ SelectionDisplay = (function() { }; that.setHandleTranslateXVisible = function(isVisible) { - Overlays.editOverlay(handleTranslateXCone, { visible: isVisible }); - Overlays.editOverlay(handleTranslateXCylinder, { visible: isVisible }); + Overlays.editOverlay(handleTranslateXCone, { visible: isVisible, ignorePickIntersection: !isVisible }); + Overlays.editOverlay(handleTranslateXCylinder, { visible: isVisible, ignorePickIntersection: !isVisible }); }; that.setHandleTranslateYVisible = function(isVisible) { - Overlays.editOverlay(handleTranslateYCone, { visible: isVisible }); - Overlays.editOverlay(handleTranslateYCylinder, { visible: isVisible }); + Overlays.editOverlay(handleTranslateYCone, { visible: isVisible, ignorePickIntersection: !isVisible }); + Overlays.editOverlay(handleTranslateYCylinder, { visible: isVisible, ignorePickIntersection: !isVisible }); }; that.setHandleTranslateZVisible = function(isVisible) { - Overlays.editOverlay(handleTranslateZCone, { visible: isVisible }); - Overlays.editOverlay(handleTranslateZCylinder, { visible: isVisible }); + Overlays.editOverlay(handleTranslateZCone, { visible: isVisible, ignorePickIntersection: !isVisible }); + Overlays.editOverlay(handleTranslateZCylinder, { visible: isVisible, ignorePickIntersection: !isVisible }); }; // FUNCTION: SET HANDLE ROTATE VISIBLE @@ -1916,15 +1922,15 @@ SelectionDisplay = (function() { }; that.setHandleRotatePitchVisible = function(isVisible) { - Overlays.editOverlay(handleRotatePitchRing, { visible: isVisible }); + Overlays.editOverlay(handleRotatePitchRing, { visible: isVisible, ignorePickIntersection: !isVisible }); }; that.setHandleRotateYawVisible = function(isVisible) { - Overlays.editOverlay(handleRotateYawRing, { visible: isVisible }); + Overlays.editOverlay(handleRotateYawRing, { visible: isVisible, ignorePickIntersection: !isVisible }); }; that.setHandleRotateRollVisible = function(isVisible) { - Overlays.editOverlay(handleRotateRollRing, { visible: isVisible }); + Overlays.editOverlay(handleRotateRollRing, { visible: isVisible, ignorePickIntersection: !isVisible }); }; // FUNCTION: SET HANDLE STRETCH VISIBLE @@ -1935,15 +1941,15 @@ SelectionDisplay = (function() { }; that.setHandleStretchXVisible = function(isVisible) { - Overlays.editOverlay(handleStretchXCube, { visible: isVisible }); + Overlays.editOverlay(handleStretchXCube, { visible: isVisible, ignorePickIntersection: !isVisible }); }; that.setHandleStretchYVisible = function(isVisible) { - Overlays.editOverlay(handleStretchYCube, { visible: isVisible }); + Overlays.editOverlay(handleStretchYCube, { visible: isVisible, ignorePickIntersection: !isVisible }); }; that.setHandleStretchZVisible = function(isVisible) { - Overlays.editOverlay(handleStretchZCube, { visible: isVisible }); + Overlays.editOverlay(handleStretchZCube, { visible: isVisible, ignorePickIntersection: !isVisible }); }; // FUNCTION: SET HANDLE SCALE VISIBLE @@ -1953,16 +1959,16 @@ SelectionDisplay = (function() { }; that.setHandleScaleVisible = function(isVisible) { - Overlays.editOverlay(handleScaleCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleCube, { visible: isVisible, ignorePickIntersection: !isVisible }); }; that.setHandleBoundingBoxVisible = function(isVisible) { - Overlays.editOverlay(handleBoundingBox, { visible: isVisible }); + Overlays.editOverlay(handleBoundingBox, { visible: isVisible, ignorePickIntersection: true }); }; // FUNCTION: SET HANDLE DUPLICATOR VISIBLE that.setHandleDuplicatorVisible = function(isVisible) { - Overlays.editOverlay(handleDuplicator, { visible: isVisible }); + Overlays.editOverlay(handleDuplicator, { visible: isVisible, ignorePickIntersection: !isVisible }); }; // FUNCTION: DEBUG PICK PLANE @@ -1975,7 +1981,7 @@ SelectionDisplay = (function() { position: pickPlanePosition, rotation: rotation, dimensions: dimensions, - visible: true + visible: true }); }; @@ -1986,7 +1992,7 @@ SelectionDisplay = (function() { shape: "Sphere", solid: true, visible: true, - ignoreRayIntersection: true, + ignorePickIntersection: true, drawInFront: false, color: COLOR_DEBUG_PICK_PLANE_HIT, position: pickHitPosition, @@ -2082,10 +2088,12 @@ SelectionDisplay = (function() { pushCommandForSelections(duplicatedEntityIDs); if (isConstrained) { Overlays.editOverlay(xRailOverlay, { - visible: false + visible: false, + ignorePickIntersection: true }); Overlays.editOverlay(zRailOverlay, { - visible: false + visible: false, + ignorePickIntersection: true }); } }, @@ -2174,22 +2182,26 @@ SelectionDisplay = (function() { Overlays.editOverlay(xRailOverlay, { start: xStart, end: xEnd, - visible: true + visible: true, + ignorePickIntersection: true }); Overlays.editOverlay(zRailOverlay, { start: zStart, end: zEnd, - visible: true + visible: true, + ignorePickIntersection: true }); isConstrained = true; } } else { if (isConstrained) { Overlays.editOverlay(xRailOverlay, { - visible: false + visible: false, + ignorePickIntersection: true }); Overlays.editOverlay(zRailOverlay, { - visible: false + visible: false, + ignorePickIntersection: true }); isConstrained = false; } @@ -2460,7 +2472,7 @@ SelectionDisplay = (function() { } if (stretchPanel !== null) { - Overlays.editOverlay(stretchPanel, { visible: true }); + Overlays.editOverlay(stretchPanel, { visible: true, ignorePickIntersection: false }); } var stretchCubePosition = Overlays.getProperty(handleStretchCube, "position"); var stretchPanelPosition = Overlays.getProperty(stretchPanel, "position"); @@ -2481,7 +2493,7 @@ SelectionDisplay = (function() { } if (stretchPanel !== null) { - Overlays.editOverlay(stretchPanel, { visible: false }); + Overlays.editOverlay(stretchPanel, { visible: false, ignorePickIntersection: true }); } activeStretchCubePanelOffset = null; @@ -2775,7 +2787,8 @@ SelectionDisplay = (function() { rotation: worldRotation, startAt: 0, endAt: 0, - visible: true + visible: true, + ignorePickIntersection: false }); // editOverlays may not have committed rotation changes. @@ -2805,13 +2818,13 @@ SelectionDisplay = (function() { if (wantDebug) { print("================== " + getMode() + "(addHandleRotateTool onEnd) -> ======================="); } - Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); + Overlays.editOverlay(rotationDegreesDisplay, { visible: false, ignorePickIntersection: true }); Overlays.editOverlay(selectedHandle, { hasTickMarks: false, solid: true, innerRadius: ROTATE_RING_IDLE_INNER_RADIUS }); - Overlays.editOverlay(handleRotateCurrentRing, { visible: false }); + Overlays.editOverlay(handleRotateCurrentRing, { visible: false, ignorePickIntersection: true }); pushCommandForSelections(); if (wantDebug) { print("================== " + getMode() + "(addHandleRotateTool onEnd) <- =======================");