//// EntityScriptingInterface.cpp// libraries/entities/src//// Created by Brad Hefta-Gaub on 12/6/13.// Copyright 2013 High Fidelity, Inc.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "EntityScriptingInterface.h" #include <glm/gtc/matrix_transform.hpp>#include <glm/gtx/transform.hpp> #include <QFutureWatcher>#include <QtConcurrent/QtConcurrentRun>#include <QJsonObject>#include <QJsonDocument>#include <QJsonArray> #include <shared/QtHelpers.h>#include <VariantMapToScriptValue.h>#include <SharedUtil.h>#include <SpatialParentFinder.h>#include <AvatarHashMap.h> #include "EntityItemID.h"#include "EntitiesLogging.h"#include "EntityDynamicFactoryInterface.h"#include "EntityDynamicInterface.h"#include "EntitySimulation.h"#include "EntityTree.h"#include "LightEntityItem.h"#include "ModelEntityItem.h"#include "QVariantGLM.h"#include "SimulationOwner.h"#include "ZoneEntityItem.h"#include "WebEntityItem.h"#include <EntityScriptClient.h>#include <Profile.h>#include "GrabPropertyGroup.h" const QString GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":true}}";const QString NOT_GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":false}}"; EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership) : _entityTree(nullptr), _bidOnSimulationOwnership(bidOnSimulationOwnership){ auto nodeList = DependencyManager::get<NodeList>(); connect(nodeList.data(), &NodeList::isAllowedEditorChanged, this, &EntityScriptingInterface::canAdjustLocksChanged); connect(nodeList.data(), &NodeList::canRezChanged, this, &EntityScriptingInterface::canRezChanged); connect(nodeList.data(), &NodeList::canRezTmpChanged, this, &EntityScriptingInterface::canRezTmpChanged); connect(nodeList.data(), &NodeList::canRezCertifiedChanged, this, &EntityScriptingInterface::canRezCertifiedChanged); connect(nodeList.data(), &NodeList::canRezTmpCertifiedChanged, this, &EntityScriptingInterface::canRezTmpCertifiedChanged); connect(nodeList.data(), &NodeList::canWriteAssetsChanged, this, &EntityScriptingInterface::canWriteAssetsChanged); // If the user clicks somewhere where there is no entity at all, we will release focus connect(this, &EntityScriptingInterface::mousePressOffEntity, [=]() { setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); }); auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket");} void EntityScriptingInterface::queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties) { getEntityPacketSender()->queueEditEntityMessage(packetType, _entityTree, entityID, properties);} void EntityScriptingInterface::resetActivityTracking() { _activityTracking.addedEntityCount = 0; _activityTracking.deletedEntityCount = 0; _activityTracking.editedEntityCount = 0;} bool EntityScriptingInterface::canAdjustLocks() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->isAllowedEditor();} bool EntityScriptingInterface::canRez() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->getThisNodeCanRez();} bool EntityScriptingInterface::canRezTmp() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->getThisNodeCanRezTmp();} bool EntityScriptingInterface::canRezCertified() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->getThisNodeCanRezCertified();} bool EntityScriptingInterface::canRezTmpCertified() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->getThisNodeCanRezTmpCertified();} bool EntityScriptingInterface::canWriteAssets() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->getThisNodeCanWriteAssets();} bool EntityScriptingInterface::canReplaceContent() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->getThisNodeCanReplaceContent();} void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) { if (_entityTree) { disconnect(_entityTree.get(), &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity); disconnect(_entityTree.get(), &EntityTree::deletingEntity, this, &EntityScriptingInterface::deletingEntity); disconnect(_entityTree.get(), &EntityTree::clearingEntities, this, &EntityScriptingInterface::clearingEntities); } _entityTree = elementTree; if (_entityTree) { connect(_entityTree.get(), &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity); connect(_entityTree.get(), &EntityTree::deletingEntity, this, &EntityScriptingInterface::deletingEntity); connect(_entityTree.get(), &EntityTree::clearingEntities, this, &EntityScriptingInterface::clearingEntities); }} EntityItemProperties convertPropertiesToScriptSemantics(const EntityItemProperties& entitySideProperties, bool scalesWithParent) { // In EntityTree code, properties.position and properties.rotation are relative to the parent. In javascript, // they are in world-space. The local versions are put into localPosition and localRotation and position and // rotation are converted from local to world space. EntityItemProperties scriptSideProperties = entitySideProperties; scriptSideProperties.setLocalPosition(entitySideProperties.getPosition()); scriptSideProperties.setLocalRotation(entitySideProperties.getRotation()); scriptSideProperties.setLocalVelocity(entitySideProperties.getVelocity()); scriptSideProperties.setLocalAngularVelocity(entitySideProperties.getAngularVelocity()); scriptSideProperties.setLocalDimensions(entitySideProperties.getDimensions()); bool success; glm::vec3 worldPosition = SpatiallyNestable::localToWorld(entitySideProperties.getPosition(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); glm::quat worldRotation = SpatiallyNestable::localToWorld(entitySideProperties.getRotation(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); glm::vec3 worldVelocity = SpatiallyNestable::localToWorldVelocity(entitySideProperties.getVelocity(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); glm::vec3 worldAngularVelocity = SpatiallyNestable::localToWorldAngularVelocity(entitySideProperties.getAngularVelocity(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); glm::vec3 worldDimensions = SpatiallyNestable::localToWorldDimensions(entitySideProperties.getDimensions(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); scriptSideProperties.setPosition(worldPosition); scriptSideProperties.setRotation(worldRotation); scriptSideProperties.setVelocity(worldVelocity); scriptSideProperties.setAngularVelocity(worldAngularVelocity); scriptSideProperties.setDimensions(worldDimensions); return scriptSideProperties;} // TODO: this method looks expensive and should take properties by reference, update it, and return voidEntityItemProperties convertPropertiesFromScriptSemantics(const EntityItemProperties& scriptSideProperties, bool scalesWithParent) { // convert position and rotation properties from world-space to local, unless localPosition and localRotation // are set. If they are set, they overwrite position and rotation. EntityItemProperties entitySideProperties = scriptSideProperties; bool success; if (scriptSideProperties.localPositionChanged()) { entitySideProperties.setPosition(scriptSideProperties.getLocalPosition()); } else if (scriptSideProperties.positionChanged()) { glm::vec3 localPosition = SpatiallyNestable::worldToLocal(entitySideProperties.getPosition(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); entitySideProperties.setPosition(localPosition); } if (scriptSideProperties.localRotationChanged()) { entitySideProperties.setRotation(scriptSideProperties.getLocalRotation()); } else if (scriptSideProperties.rotationChanged()) { glm::quat localRotation = SpatiallyNestable::worldToLocal(entitySideProperties.getRotation(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); entitySideProperties.setRotation(localRotation); } if (scriptSideProperties.localVelocityChanged()) { entitySideProperties.setVelocity(scriptSideProperties.getLocalVelocity()); } else if (scriptSideProperties.velocityChanged()) { glm::vec3 localVelocity = SpatiallyNestable::worldToLocalVelocity(entitySideProperties.getVelocity(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); entitySideProperties.setVelocity(localVelocity); } if (scriptSideProperties.localAngularVelocityChanged()) { entitySideProperties.setAngularVelocity(scriptSideProperties.getLocalAngularVelocity()); } else if (scriptSideProperties.angularVelocityChanged()) { glm::vec3 localAngularVelocity = SpatiallyNestable::worldToLocalAngularVelocity(entitySideProperties.getAngularVelocity(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); entitySideProperties.setAngularVelocity(localAngularVelocity); } if (scriptSideProperties.localDimensionsChanged()) { entitySideProperties.setDimensions(scriptSideProperties.getLocalDimensions()); } else if (scriptSideProperties.dimensionsChanged()) { glm::vec3 localDimensions = SpatiallyNestable::worldToLocalDimensions(entitySideProperties.getDimensions(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); entitySideProperties.setDimensions(localDimensions); } return entitySideProperties;} void synchronizeSpatialKey(const GrabPropertyGroup& grabProperties, QJsonObject& grabbableKey, bool& userDataChanged) { if (grabProperties.equippableLeftPositionChanged() || grabProperties.equippableRightPositionChanged() || grabProperties.equippableRightRotationChanged() || grabProperties.equippableIndicatorURLChanged() || grabProperties.equippableIndicatorScaleChanged() || grabProperties.equippableIndicatorOffsetChanged()) { QJsonObject spatialKey = grabbableKey["spatialKey"].toObject(); if (grabProperties.equippableLeftPositionChanged()) { if (grabProperties.getEquippableLeftPosition() == INITIAL_LEFT_EQUIPPABLE_POSITION) { spatialKey.remove("leftRelativePosition"); } else { spatialKey["leftRelativePosition"] = QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableLeftPosition())); } } if (grabProperties.equippableRightPositionChanged()) { if (grabProperties.getEquippableRightPosition() == INITIAL_RIGHT_EQUIPPABLE_POSITION) { spatialKey.remove("rightRelativePosition"); } else { spatialKey["rightRelativePosition"] = QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableRightPosition())); } } if (grabProperties.equippableLeftRotationChanged()) { spatialKey["relativeRotation"] = QJsonValue::fromVariant(quatToQMap(grabProperties.getEquippableLeftRotation())); } else if (grabProperties.equippableRightRotationChanged()) { spatialKey["relativeRotation"] = QJsonValue::fromVariant(quatToQMap(grabProperties.getEquippableRightRotation())); } grabbableKey["spatialKey"] = spatialKey; userDataChanged = true; }} void synchronizeGrabbableKey(const GrabPropertyGroup& grabProperties, QJsonObject& userData, bool& userDataChanged) { if (grabProperties.triggerableChanged() || grabProperties.grabbableChanged() || grabProperties.grabFollowsControllerChanged() || grabProperties.grabKinematicChanged() || grabProperties.equippableChanged() || grabProperties.equippableLeftPositionChanged() || grabProperties.equippableRightPositionChanged() || grabProperties.equippableRightRotationChanged()) { QJsonObject grabbableKey = userData["grabbableKey"].toObject(); if (grabProperties.triggerableChanged()) { if (grabProperties.getTriggerable()) { grabbableKey["triggerable"] = true; } else { grabbableKey.remove("triggerable"); } } if (grabProperties.grabbableChanged()) { if (grabProperties.getGrabbable()) { grabbableKey.remove("grabbable"); } else { grabbableKey["grabbable"] = false; } } if (grabProperties.grabFollowsControllerChanged()) { if (grabProperties.getGrabFollowsController()) { grabbableKey.remove("ignoreIK"); } else { grabbableKey["ignoreIK"] = false; } } if (grabProperties.grabKinematicChanged()) { if (grabProperties.getGrabKinematic()) { grabbableKey.remove("kinematic"); } else { grabbableKey["kinematic"] = false; } } if (grabProperties.equippableChanged()) { if (grabProperties.getEquippable()) { grabbableKey["equippable"] = true; } else { grabbableKey.remove("equippable"); } } if (grabbableKey.contains("spatialKey")) { synchronizeSpatialKey(grabProperties, grabbableKey, userDataChanged); } userData["grabbableKey"] = grabbableKey; userDataChanged = true; }} void synchronizeGrabJoints(const GrabPropertyGroup& grabProperties, QJsonObject& joints) { QJsonArray rightHand = joints["RightHand"].toArray(); QJsonObject rightHandPosition = rightHand.size() > 0 ? rightHand[0].toObject() : QJsonObject(); QJsonObject rightHandRotation = rightHand.size() > 1 ? rightHand[1].toObject() : QJsonObject(); QJsonArray leftHand = joints["LeftHand"].toArray(); QJsonObject leftHandPosition = leftHand.size() > 0 ? leftHand[0].toObject() : QJsonObject(); QJsonObject leftHandRotation = leftHand.size() > 1 ? leftHand[1].toObject() : QJsonObject(); if (grabProperties.equippableLeftPositionChanged()) { leftHandPosition = QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableLeftPosition())).toObject(); } if (grabProperties.equippableRightPositionChanged()) { rightHandPosition = QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableRightPosition())).toObject(); } if (grabProperties.equippableLeftRotationChanged()) { leftHandRotation = QJsonValue::fromVariant(quatToQMap(grabProperties.getEquippableLeftRotation())).toObject(); } if (grabProperties.equippableRightRotationChanged()) { rightHandRotation = QJsonValue::fromVariant(quatToQMap(grabProperties.getEquippableRightRotation())).toObject(); } rightHand = QJsonArray(); rightHand.append(rightHandPosition); rightHand.append(rightHandRotation); joints["RightHand"] = rightHand; leftHand = QJsonArray(); leftHand.append(leftHandPosition); leftHand.append(leftHandRotation); joints["LeftHand"] = leftHand;} void synchronizeEquipHotspot(const GrabPropertyGroup& grabProperties, QJsonObject& userData, bool& userDataChanged) { if (grabProperties.equippableLeftPositionChanged() || grabProperties.equippableRightPositionChanged() || grabProperties.equippableRightRotationChanged() || grabProperties.equippableIndicatorURLChanged() || grabProperties.equippableIndicatorScaleChanged() || grabProperties.equippableIndicatorOffsetChanged()) { QJsonArray equipHotspots = userData["equipHotspots"].toArray(); QJsonObject equipHotspot = equipHotspots[0].toObject(); QJsonObject joints = equipHotspot["joints"].toObject(); synchronizeGrabJoints(grabProperties, joints); if (grabProperties.equippableIndicatorURLChanged()) { equipHotspot["modelURL"] = grabProperties.getEquippableIndicatorURL(); } if (grabProperties.equippableIndicatorScaleChanged()) { QJsonObject scale = QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableIndicatorScale())).toObject(); equipHotspot["radius"] = scale; equipHotspot["modelScale"] = scale; } if (grabProperties.equippableIndicatorOffsetChanged()) { equipHotspot["position"] = QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableIndicatorOffset())).toObject(); } equipHotspot["joints"] = joints; equipHotspots = QJsonArray(); equipHotspots.append(equipHotspot); userData["equipHotspots"] = equipHotspots; userDataChanged = true; }} void synchronizeWearable(const GrabPropertyGroup& grabProperties, QJsonObject& userData, bool& userDataChanged) { if (grabProperties.equippableLeftPositionChanged() || grabProperties.equippableRightPositionChanged() || grabProperties.equippableRightRotationChanged() || grabProperties.equippableIndicatorURLChanged() || grabProperties.equippableIndicatorScaleChanged() || grabProperties.equippableIndicatorOffsetChanged()) { QJsonObject wearable = userData["wearable"].toObject(); QJsonObject joints = wearable["joints"].toObject(); synchronizeGrabJoints(grabProperties, joints); wearable["joints"] = joints; userData["wearable"] = wearable; userDataChanged = true; }} void synchronizeEditedGrabProperties(EntityItemProperties& properties, const QString& previousUserdata) { // After sufficient warning to content creators, we should be able to remove this. if (properties.grabbingRelatedPropertyChanged()) { // This edit touches a new-style grab property, so make userData agree... GrabPropertyGroup& grabProperties = properties.getGrab(); bool userDataChanged { false }; // if the edit changed userData, use the updated version coming along with the edit. If not, use // what was already in the entity. QByteArray userDataString; if (properties.userDataChanged()) { userDataString = properties.getUserData().toUtf8(); } else { userDataString = previousUserdata.toUtf8();; } QJsonObject userData = QJsonDocument::fromJson(userDataString).object(); if (userData.contains("grabbableKey")) { synchronizeGrabbableKey(grabProperties, userData, userDataChanged); } if (userData.contains("equipHotspots")) { synchronizeEquipHotspot(grabProperties, userData, userDataChanged); } if (userData.contains("wearable")) { synchronizeWearable(grabProperties, userData, userDataChanged); } if (userDataChanged) { properties.setUserData(QJsonDocument(userData).toJson()); } } else if (properties.userDataChanged()) { // This edit touches userData (and doesn't touch a new-style grab property). Check for grabbableKey in the // userdata and make the new-style grab properties agree convertGrabUserDataToProperties(properties); }} QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, bool clientOnly) { PROFILE_RANGE(script_entities, __FUNCTION__); _activityTracking.addedEntityCount++; auto nodeList = DependencyManager::get<NodeList>(); const auto sessionID = nodeList->getSessionUUID(); EntityItemProperties propertiesWithSimID = properties; if (clientOnly) { propertiesWithSimID.setClientOnly(clientOnly); propertiesWithSimID.setOwningAvatarID(sessionID); } propertiesWithSimID.setLastEditedBy(sessionID); bool scalesWithParent = propertiesWithSimID.getScalesWithParent(); propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent); propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged()); synchronizeEditedGrabProperties(propertiesWithSimID, QString()); EntityItemID id; // If we have a local entity tree set, then also update it. bool success = addLocalEntityCopy(propertiesWithSimID, id); // queue the packet if (success) {Lines 1–500 of 2267. Load more
// // EntityScriptingInterface.cpp // libraries/entities/src // // Created by Brad Hefta-Gaub on 12/6/13. // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "EntityScriptingInterface.h" #include <glm/gtc/matrix_transform.hpp> #include <glm/gtx/transform.hpp> #include <QFutureWatcher> #include <QtConcurrent/QtConcurrentRun> #include <QJsonObject> #include <QJsonDocument> #include <QJsonArray> #include <shared/QtHelpers.h> #include <VariantMapToScriptValue.h> #include <SharedUtil.h> #include <SpatialParentFinder.h> #include <AvatarHashMap.h> #include "EntityItemID.h" #include "EntitiesLogging.h" #include "EntityDynamicFactoryInterface.h" #include "EntityDynamicInterface.h" #include "EntitySimulation.h" #include "EntityTree.h" #include "LightEntityItem.h" #include "ModelEntityItem.h" #include "QVariantGLM.h" #include "SimulationOwner.h" #include "ZoneEntityItem.h" #include "WebEntityItem.h" #include <EntityScriptClient.h> #include <Profile.h> #include "GrabPropertyGroup.h" const QString GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":true}}"; const QString NOT_GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":false}}"; EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership) : _entityTree(nullptr), _bidOnSimulationOwnership(bidOnSimulationOwnership) { auto nodeList = DependencyManager::get<NodeList>(); connect(nodeList.data(), &NodeList::isAllowedEditorChanged, this, &EntityScriptingInterface::canAdjustLocksChanged); connect(nodeList.data(), &NodeList::canRezChanged, this, &EntityScriptingInterface::canRezChanged); connect(nodeList.data(), &NodeList::canRezTmpChanged, this, &EntityScriptingInterface::canRezTmpChanged); connect(nodeList.data(), &NodeList::canRezCertifiedChanged, this, &EntityScriptingInterface::canRezCertifiedChanged); connect(nodeList.data(), &NodeList::canRezTmpCertifiedChanged, this, &EntityScriptingInterface::canRezTmpCertifiedChanged); connect(nodeList.data(), &NodeList::canWriteAssetsChanged, this, &EntityScriptingInterface::canWriteAssetsChanged); // If the user clicks somewhere where there is no entity at all, we will release focus connect(this, &EntityScriptingInterface::mousePressOffEntity, [=]() { setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); }); auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket"); } void EntityScriptingInterface::queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties) { getEntityPacketSender()->queueEditEntityMessage(packetType, _entityTree, entityID, properties); } void EntityScriptingInterface::resetActivityTracking() { _activityTracking.addedEntityCount = 0; _activityTracking.deletedEntityCount = 0; _activityTracking.editedEntityCount = 0; } bool EntityScriptingInterface::canAdjustLocks() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->isAllowedEditor(); } bool EntityScriptingInterface::canRez() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->getThisNodeCanRez(); } bool EntityScriptingInterface::canRezTmp() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->getThisNodeCanRezTmp(); } bool EntityScriptingInterface::canRezCertified() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->getThisNodeCanRezCertified(); } bool EntityScriptingInterface::canRezTmpCertified() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->getThisNodeCanRezTmpCertified(); } bool EntityScriptingInterface::canWriteAssets() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->getThisNodeCanWriteAssets(); } bool EntityScriptingInterface::canReplaceContent() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->getThisNodeCanReplaceContent(); } void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) { if (_entityTree) { disconnect(_entityTree.get(), &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity); disconnect(_entityTree.get(), &EntityTree::deletingEntity, this, &EntityScriptingInterface::deletingEntity); disconnect(_entityTree.get(), &EntityTree::clearingEntities, this, &EntityScriptingInterface::clearingEntities); } _entityTree = elementTree; if (_entityTree) { connect(_entityTree.get(), &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity); connect(_entityTree.get(), &EntityTree::deletingEntity, this, &EntityScriptingInterface::deletingEntity); connect(_entityTree.get(), &EntityTree::clearingEntities, this, &EntityScriptingInterface::clearingEntities); } } EntityItemProperties convertPropertiesToScriptSemantics(const EntityItemProperties& entitySideProperties, bool scalesWithParent) { // In EntityTree code, properties.position and properties.rotation are relative to the parent. In javascript, // they are in world-space. The local versions are put into localPosition and localRotation and position and // rotation are converted from local to world space. EntityItemProperties scriptSideProperties = entitySideProperties; scriptSideProperties.setLocalPosition(entitySideProperties.getPosition()); scriptSideProperties.setLocalRotation(entitySideProperties.getRotation()); scriptSideProperties.setLocalVelocity(entitySideProperties.getVelocity()); scriptSideProperties.setLocalAngularVelocity(entitySideProperties.getAngularVelocity()); scriptSideProperties.setLocalDimensions(entitySideProperties.getDimensions()); bool success; glm::vec3 worldPosition = SpatiallyNestable::localToWorld(entitySideProperties.getPosition(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); glm::quat worldRotation = SpatiallyNestable::localToWorld(entitySideProperties.getRotation(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); glm::vec3 worldVelocity = SpatiallyNestable::localToWorldVelocity(entitySideProperties.getVelocity(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); glm::vec3 worldAngularVelocity = SpatiallyNestable::localToWorldAngularVelocity(entitySideProperties.getAngularVelocity(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); glm::vec3 worldDimensions = SpatiallyNestable::localToWorldDimensions(entitySideProperties.getDimensions(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); scriptSideProperties.setPosition(worldPosition); scriptSideProperties.setRotation(worldRotation); scriptSideProperties.setVelocity(worldVelocity); scriptSideProperties.setAngularVelocity(worldAngularVelocity); scriptSideProperties.setDimensions(worldDimensions); return scriptSideProperties; } // TODO: this method looks expensive and should take properties by reference, update it, and return void EntityItemProperties convertPropertiesFromScriptSemantics(const EntityItemProperties& scriptSideProperties, bool scalesWithParent) { // convert position and rotation properties from world-space to local, unless localPosition and localRotation // are set. If they are set, they overwrite position and rotation. EntityItemProperties entitySideProperties = scriptSideProperties; bool success; if (scriptSideProperties.localPositionChanged()) { entitySideProperties.setPosition(scriptSideProperties.getLocalPosition()); } else if (scriptSideProperties.positionChanged()) { glm::vec3 localPosition = SpatiallyNestable::worldToLocal(entitySideProperties.getPosition(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); entitySideProperties.setPosition(localPosition); } if (scriptSideProperties.localRotationChanged()) { entitySideProperties.setRotation(scriptSideProperties.getLocalRotation()); } else if (scriptSideProperties.rotationChanged()) { glm::quat localRotation = SpatiallyNestable::worldToLocal(entitySideProperties.getRotation(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); entitySideProperties.setRotation(localRotation); } if (scriptSideProperties.localVelocityChanged()) { entitySideProperties.setVelocity(scriptSideProperties.getLocalVelocity()); } else if (scriptSideProperties.velocityChanged()) { glm::vec3 localVelocity = SpatiallyNestable::worldToLocalVelocity(entitySideProperties.getVelocity(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); entitySideProperties.setVelocity(localVelocity); } if (scriptSideProperties.localAngularVelocityChanged()) { entitySideProperties.setAngularVelocity(scriptSideProperties.getLocalAngularVelocity()); } else if (scriptSideProperties.angularVelocityChanged()) { glm::vec3 localAngularVelocity = SpatiallyNestable::worldToLocalAngularVelocity(entitySideProperties.getAngularVelocity(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); entitySideProperties.setAngularVelocity(localAngularVelocity); } if (scriptSideProperties.localDimensionsChanged()) { entitySideProperties.setDimensions(scriptSideProperties.getLocalDimensions()); } else if (scriptSideProperties.dimensionsChanged()) { glm::vec3 localDimensions = SpatiallyNestable::worldToLocalDimensions(entitySideProperties.getDimensions(), entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), scalesWithParent, success); entitySideProperties.setDimensions(localDimensions); } return entitySideProperties; } void synchronizeSpatialKey(const GrabPropertyGroup& grabProperties, QJsonObject& grabbableKey, bool& userDataChanged) { if (grabProperties.equippableLeftPositionChanged() || grabProperties.equippableRightPositionChanged() || grabProperties.equippableRightRotationChanged() || grabProperties.equippableIndicatorURLChanged() || grabProperties.equippableIndicatorScaleChanged() || grabProperties.equippableIndicatorOffsetChanged()) { QJsonObject spatialKey = grabbableKey["spatialKey"].toObject(); if (grabProperties.equippableLeftPositionChanged()) { if (grabProperties.getEquippableLeftPosition() == INITIAL_LEFT_EQUIPPABLE_POSITION) { spatialKey.remove("leftRelativePosition"); } else { spatialKey["leftRelativePosition"] = QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableLeftPosition())); } } if (grabProperties.equippableRightPositionChanged()) { if (grabProperties.getEquippableRightPosition() == INITIAL_RIGHT_EQUIPPABLE_POSITION) { spatialKey.remove("rightRelativePosition"); } else { spatialKey["rightRelativePosition"] = QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableRightPosition())); } } if (grabProperties.equippableLeftRotationChanged()) { spatialKey["relativeRotation"] = QJsonValue::fromVariant(quatToQMap(grabProperties.getEquippableLeftRotation())); } else if (grabProperties.equippableRightRotationChanged()) { spatialKey["relativeRotation"] = QJsonValue::fromVariant(quatToQMap(grabProperties.getEquippableRightRotation())); } grabbableKey["spatialKey"] = spatialKey; userDataChanged = true; } } void synchronizeGrabbableKey(const GrabPropertyGroup& grabProperties, QJsonObject& userData, bool& userDataChanged) { if (grabProperties.triggerableChanged() || grabProperties.grabbableChanged() || grabProperties.grabFollowsControllerChanged() || grabProperties.grabKinematicChanged() || grabProperties.equippableChanged() || grabProperties.equippableLeftPositionChanged() || grabProperties.equippableRightPositionChanged() || grabProperties.equippableRightRotationChanged()) { QJsonObject grabbableKey = userData["grabbableKey"].toObject(); if (grabProperties.triggerableChanged()) { if (grabProperties.getTriggerable()) { grabbableKey["triggerable"] = true; } else { grabbableKey.remove("triggerable"); } } if (grabProperties.grabbableChanged()) { if (grabProperties.getGrabbable()) { grabbableKey.remove("grabbable"); } else { grabbableKey["grabbable"] = false; } } if (grabProperties.grabFollowsControllerChanged()) { if (grabProperties.getGrabFollowsController()) { grabbableKey.remove("ignoreIK"); } else { grabbableKey["ignoreIK"] = false; } } if (grabProperties.grabKinematicChanged()) { if (grabProperties.getGrabKinematic()) { grabbableKey.remove("kinematic"); } else { grabbableKey["kinematic"] = false; } } if (grabProperties.equippableChanged()) { if (grabProperties.getEquippable()) { grabbableKey["equippable"] = true; } else { grabbableKey.remove("equippable"); } } if (grabbableKey.contains("spatialKey")) { synchronizeSpatialKey(grabProperties, grabbableKey, userDataChanged); } userData["grabbableKey"] = grabbableKey; userDataChanged = true; } } void synchronizeGrabJoints(const GrabPropertyGroup& grabProperties, QJsonObject& joints) { QJsonArray rightHand = joints["RightHand"].toArray(); QJsonObject rightHandPosition = rightHand.size() > 0 ? rightHand[0].toObject() : QJsonObject(); QJsonObject rightHandRotation = rightHand.size() > 1 ? rightHand[1].toObject() : QJsonObject(); QJsonArray leftHand = joints["LeftHand"].toArray(); QJsonObject leftHandPosition = leftHand.size() > 0 ? leftHand[0].toObject() : QJsonObject(); QJsonObject leftHandRotation = leftHand.size() > 1 ? leftHand[1].toObject() : QJsonObject(); if (grabProperties.equippableLeftPositionChanged()) { leftHandPosition = QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableLeftPosition())).toObject(); } if (grabProperties.equippableRightPositionChanged()) { rightHandPosition = QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableRightPosition())).toObject(); } if (grabProperties.equippableLeftRotationChanged()) { leftHandRotation = QJsonValue::fromVariant(quatToQMap(grabProperties.getEquippableLeftRotation())).toObject(); } if (grabProperties.equippableRightRotationChanged()) { rightHandRotation = QJsonValue::fromVariant(quatToQMap(grabProperties.getEquippableRightRotation())).toObject(); } rightHand = QJsonArray(); rightHand.append(rightHandPosition); rightHand.append(rightHandRotation); joints["RightHand"] = rightHand; leftHand = QJsonArray(); leftHand.append(leftHandPosition); leftHand.append(leftHandRotation); joints["LeftHand"] = leftHand; } void synchronizeEquipHotspot(const GrabPropertyGroup& grabProperties, QJsonObject& userData, bool& userDataChanged) { if (grabProperties.equippableLeftPositionChanged() || grabProperties.equippableRightPositionChanged() || grabProperties.equippableRightRotationChanged() || grabProperties.equippableIndicatorURLChanged() || grabProperties.equippableIndicatorScaleChanged() || grabProperties.equippableIndicatorOffsetChanged()) { QJsonArray equipHotspots = userData["equipHotspots"].toArray(); QJsonObject equipHotspot = equipHotspots[0].toObject(); QJsonObject joints = equipHotspot["joints"].toObject(); synchronizeGrabJoints(grabProperties, joints); if (grabProperties.equippableIndicatorURLChanged()) { equipHotspot["modelURL"] = grabProperties.getEquippableIndicatorURL(); } if (grabProperties.equippableIndicatorScaleChanged()) { QJsonObject scale = QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableIndicatorScale())).toObject(); equipHotspot["radius"] = scale; equipHotspot["modelScale"] = scale; } if (grabProperties.equippableIndicatorOffsetChanged()) { equipHotspot["position"] = QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableIndicatorOffset())).toObject(); } equipHotspot["joints"] = joints; equipHotspots = QJsonArray(); equipHotspots.append(equipHotspot); userData["equipHotspots"] = equipHotspots; userDataChanged = true; } } void synchronizeWearable(const GrabPropertyGroup& grabProperties, QJsonObject& userData, bool& userDataChanged) { if (grabProperties.equippableLeftPositionChanged() || grabProperties.equippableRightPositionChanged() || grabProperties.equippableRightRotationChanged() || grabProperties.equippableIndicatorURLChanged() || grabProperties.equippableIndicatorScaleChanged() || grabProperties.equippableIndicatorOffsetChanged()) { QJsonObject wearable = userData["wearable"].toObject(); QJsonObject joints = wearable["joints"].toObject(); synchronizeGrabJoints(grabProperties, joints); wearable["joints"] = joints; userData["wearable"] = wearable; userDataChanged = true; } } void synchronizeEditedGrabProperties(EntityItemProperties& properties, const QString& previousUserdata) { // After sufficient warning to content creators, we should be able to remove this. if (properties.grabbingRelatedPropertyChanged()) { // This edit touches a new-style grab property, so make userData agree... GrabPropertyGroup& grabProperties = properties.getGrab(); bool userDataChanged { false }; // if the edit changed userData, use the updated version coming along with the edit. If not, use // what was already in the entity. QByteArray userDataString; if (properties.userDataChanged()) { userDataString = properties.getUserData().toUtf8(); } else { userDataString = previousUserdata.toUtf8();; } QJsonObject userData = QJsonDocument::fromJson(userDataString).object(); if (userData.contains("grabbableKey")) { synchronizeGrabbableKey(grabProperties, userData, userDataChanged); } if (userData.contains("equipHotspots")) { synchronizeEquipHotspot(grabProperties, userData, userDataChanged); } if (userData.contains("wearable")) { synchronizeWearable(grabProperties, userData, userDataChanged); } if (userDataChanged) { properties.setUserData(QJsonDocument(userData).toJson()); } } else if (properties.userDataChanged()) { // This edit touches userData (and doesn't touch a new-style grab property). Check for grabbableKey in the // userdata and make the new-style grab properties agree convertGrabUserDataToProperties(properties); } } QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, bool clientOnly) { PROFILE_RANGE(script_entities, __FUNCTION__); _activityTracking.addedEntityCount++; auto nodeList = DependencyManager::get<NodeList>(); const auto sessionID = nodeList->getSessionUUID(); EntityItemProperties propertiesWithSimID = properties; if (clientOnly) { propertiesWithSimID.setClientOnly(clientOnly); propertiesWithSimID.setOwningAvatarID(sessionID); } propertiesWithSimID.setLastEditedBy(sessionID); bool scalesWithParent = propertiesWithSimID.getScalesWithParent(); propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent); propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged()); synchronizeEditedGrabProperties(propertiesWithSimID, QString()); EntityItemID id; // If we have a local entity tree set, then also update it. bool success = addLocalEntityCopy(propertiesWithSimID, id); // queue the packet if (success) {