diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 69bdf1df3f..8066223318 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -1379,6 +1379,8 @@ function addTableRow(row) { var setting_name = table.attr("name"); row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS); + var focusChanged = false; + _.each(row.children(), function(element) { if ($(element).hasClass("numbered")) { // Index row @@ -1429,6 +1431,11 @@ function addTableRow(row) { }); } + if (!focusChanged) { + input.focus(); + focusChanged = true; + } + if (isCheckbox) { $(input).find("input").attr("data-changed", "true"); } else { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 09308baabb..e029ca6ada 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1091,22 +1091,40 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons // trigger scripted collision sounds and events for locally owned objects EntityItemPointer entityA = entityTree->findEntityByEntityItemID(idA); - if ((bool)entityA && myNodeID == entityA->getSimulatorID()) { - playEntityCollisionSound(entityA, collision); - emit collisionWithEntity(idA, idB, collision); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(idA, "collisionWithEntity", idB, collision); - } - } EntityItemPointer entityB = entityTree->findEntityByEntityItemID(idB); - if ((bool)entityB && myNodeID == entityB->getSimulatorID()) { - playEntityCollisionSound(entityB, collision); - // since we're swapping A and B we need to send the inverted collision - Collision invertedCollision(collision); - invertedCollision.invert(); - emit collisionWithEntity(idB, idA, invertedCollision); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(idB, "collisionWithEntity", idA, invertedCollision); + if ((bool)entityA && (bool)entityB) { + QUuid entityASimulatorID = entityA->getSimulatorID(); + QUuid entityBSimulatorID = entityB->getSimulatorID(); + bool entityAIsDynamic = entityA->getDynamic(); + bool entityBIsDynamic = entityB->getDynamic(); + +#ifdef WANT_DEBUG + bool bothEntitiesStatic = !entityAIsDynamic && !entityBIsDynamic; + if (bothEntitiesStatic) { + qCDebug(entities) << "A collision has occurred between two static entities!"; + qCDebug(entities) << "Entity A ID:" << entityA->getID(); + qCDebug(entities) << "Entity B ID:" << entityB->getID(); + } + assert(!bothEntitiesStatic); +#endif + + if ((myNodeID == entityASimulatorID && entityAIsDynamic) || (myNodeID == entityBSimulatorID && (!entityAIsDynamic || entityASimulatorID.isNull()))) { + playEntityCollisionSound(entityA, collision); + emit collisionWithEntity(idA, idB, collision); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(idA, "collisionWithEntity", idB, collision); + } + } + + if ((myNodeID == entityBSimulatorID && entityBIsDynamic) || (myNodeID == entityASimulatorID && (!entityBIsDynamic || entityBSimulatorID.isNull()))) { + playEntityCollisionSound(entityB, collision); + // since we're swapping A and B we need to send the inverted collision + Collision invertedCollision(collision); + invertedCollision.invert(); + emit collisionWithEntity(idB, idA, invertedCollision); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(idB, "collisionWithEntity", idA, invertedCollision); + } } } } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 64ee0bc869..236daf6443 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -210,9 +210,12 @@ public: }; glm::mat4 getGlobalTransform(const QMultiMap& _connectionParentMap, - const QHash& models, QString nodeID, bool mixamoHack) { + const QHash& models, QString nodeID, bool mixamoHack, const QString& url) { glm::mat4 globalTransform; + QVector visitedNodes; // Used to prevent following a cycle while (!nodeID.isNull()) { + visitedNodes.append(nodeID); // Append each node we visit + const FBXModel& model = models.value(nodeID); globalTransform = glm::translate(model.translation) * model.preTransform * glm::mat4_cast(model.preRotation * model.rotation * model.postRotation) * model.postTransform * globalTransform; @@ -223,6 +226,11 @@ glm::mat4 getGlobalTransform(const QMultiMap& _connectionParen QList parentIDs = _connectionParentMap.values(nodeID); nodeID = QString(); foreach (const QString& parentID, parentIDs) { + if (visitedNodes.contains(parentID)) { + qCWarning(modelformat) << "Ignoring loop detected in FBX connection map for" << url; + continue; + } + if (models.contains(parentID)) { nodeID = parentID; break; @@ -347,10 +355,18 @@ void addBlendshapes(const ExtractedBlendshape& extracted, const QList& connectionParentMap, - const QHash& models, const QString& modelID) { + const QHash& models, const QString& modelID, const QString& url) { QString topID = modelID; + QVector visitedNodes; // Used to prevent following a cycle forever { + visitedNodes.append(topID); // Append each node we visit + foreach (const QString& parentID, connectionParentMap.values(topID)) { + if (visitedNodes.contains(parentID)) { + qCWarning(modelformat) << "Ignoring loop detected in FBX connection map for" << url; + continue; + } + if (models.contains(parentID)) { topID = parentID; goto outerContinue; @@ -1307,7 +1323,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (!clusters.contains(clusterID)) { continue; } - QString topID = getTopModelID(_connectionParentMap, models, _connectionChildMap.value(clusterID)); + QString topID = getTopModelID(_connectionParentMap, models, _connectionChildMap.value(clusterID), url); _connectionChildMap.remove(_connectionParentMap.take(model.key()), model.key()); _connectionParentMap.insert(model.key(), topID); goto outerBreak; @@ -1329,7 +1345,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS first = id; } } - QString topID = getTopModelID(_connectionParentMap, models, first); + QString topID = getTopModelID(_connectionParentMap, models, first, url); appendModelIDs(_connectionParentMap.value(topID), _connectionChildMap, models, remainingModels, modelIDs); } @@ -1511,7 +1527,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // accumulate local transforms QString modelID = models.contains(it.key()) ? it.key() : _connectionParentMap.value(it.key()); - glm::mat4 modelTransform = getGlobalTransform(_connectionParentMap, models, modelID, geometry.applicationName == "mixamo.com"); + glm::mat4 modelTransform = getGlobalTransform(_connectionParentMap, models, modelID, geometry.applicationName == "mixamo.com", url); // compute the mesh extents from the transformed vertices foreach (const glm::vec3& vertex, extracted.mesh.vertices) { diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index f88015a4e4..2cc3a2c42e 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -114,7 +114,8 @@ public: EntityServerScriptLog, AdjustAvatarSorting, OctreeFileReplacement, - LAST_PACKET_TYPE = OctreeFileReplacement + CollisionEventChanges, + LAST_PACKET_TYPE = CollisionEventChanges }; }; diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index f5c3e6eafa..993cf22d83 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1036,8 +1036,8 @@ function getControllerJointIndex(hand) { "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND"); } - - return MyAvatar.getJointIndex("Head"); + + return MyAvatar.getJointIndex("Head"); } // global EquipHotspotBuddy instance @@ -1331,7 +1331,7 @@ function MyController(hand) { if (this.stylus) { return; } - + var stylusProperties = { name: "stylus", url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx", @@ -2134,7 +2134,7 @@ function MyController(hand) { return null; } }; - + this.chooseNearEquipHotspotsForFarToNearEquip = function(candidateEntities, distance) { var equippableHotspots = flatten(candidateEntities.map(function(entityID) { return _this.collectEquipHotspots(entityID); @@ -2291,7 +2291,7 @@ function MyController(hand) { return; } } - + if (isInEditMode()) { this.searchIndicatorOn(rayPickInfo.searchRay); if (this.triggerSmoothedGrab()) { @@ -2347,10 +2347,11 @@ function MyController(hand) { var avatar = AvatarList.getAvatar(this.otherGrabbingUUID); var IN_FRONT_OF_AVATAR = { x: 0, y: 0.2, z: 0.4 }; // Up from hips and in front of avatar. var startPosition = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.rotation, IN_FRONT_OF_AVATAR)); - var finishPisition = Vec3.sum(rayPickInfo.properties.position, // Entity's centroid. - Vec3.multiplyQbyV(rayPickInfo.properties.rotation , - Vec3.multiplyVbyV(rayPickInfo.properties.dimensions, - Vec3.subtract(DEFAULT_REGISTRATION_POINT, rayPickInfo.properties.registrationPoint)))); + var rayHitProps = entityPropertiesCache.getProps(rayPickInfo.entityID); + var finishPisition = Vec3.sum(rayHitProps.position, // Entity's centroid. + Vec3.multiplyQbyV(rayHitProps.rotation, + Vec3.multiplyVbyV(rayHitProps.dimensions, + Vec3.subtract(DEFAULT_REGISTRATION_POINT, rayHitProps.registrationPoint)))); this.otherGrabbingLineOn(startPosition, finishPisition, COLORS_GRAB_DISTANCE_HOLD); } else { this.otherGrabbingLineOff(); @@ -3442,14 +3443,14 @@ function MyController(hand) { }; this.offEnter = function() { - // Reuse the existing search distance if lasers were active since + // Reuse the existing search distance if lasers were active since // they will be shown in OFF state while in edit mode. var existingSearchDistance = this.searchSphereDistance; this.release(); - + if (isInEditMode()) { this.searchSphereDistance = existingSearchDistance; - } + } }; this.entityLaserTouchingEnter = function() { @@ -4154,7 +4155,7 @@ var updateWrapper = function () { } Script.setTimeout(updateWrapper, UPDATE_SLEEP_MS); -} +}; Script.setTimeout(updateWrapper, UPDATE_SLEEP_MS); function cleanup() {