(function() { var _this = null; // direct copy from source code: faux joint indexes (-1 means invalid) const SENSOR_TO_WORLD_MATRIX_INDEX = 65534; // -2 const CONTROLLER_RIGHTHAND_INDEX = 65533; // -3 const CONTROLLER_LEFTHAND_INDEX = 65532; // -4 const ZERO_UUID = '{00000000-0000-0000-0000-000000000000}'; const SANETIZE_PROPERTIES = ['childEntities', 'parentID', 'id']; function entityListToTree(entitiesList) { function entityListToTreeRecursive(properties) { properties.childEntities = []; entitiesList.forEach(function(entityProperties) { if (properties.id !== undefined && properties.id === entityProperties.parentID) { properties.childEntities.push(entityListToTreeRecursive(entityProperties)); } }); return properties; } var entityTree = []; entitiesList.forEach(function(entityProperties) { if (entityProperties.parentID === undefined || entityProperties.parentID === ZERO_UUID) { entityTree.push(entityListToTreeRecursive(entityProperties)); } }); return entityTree; } // TODO: ATP support (currently the JS API for ATP does not support file links, only hashes) function importEntitiesJSON(importLink, parentProperties) { if (parentProperties === undefined) { parentProperties = {}; } var request = new XMLHttpRequest(); request.open('GET', importLink, false); request.send(); try { var response = JSON.parse(request.responseText); parentProperties.childEntities = entityListToTree(response.Entities); return parentProperties; } catch (e) { print('Failed importing entities JSON because: ' + JSON.stringify(e)); } return null; } //Creates an entity and returns a mixed object of the creation properties and the assigned entityID var createEntity = function(entityProperties, parent, clientOnly) { if (parent.rotation !== undefined) { if (entityProperties.rotation !== undefined) { entityProperties.rotation = Quat.multiply(parent.rotation, entityProperties.rotation); } else { entityProperties.rotation = parent.rotation; } } if (parent.position !== undefined) { var localPosition = (parent.rotation !== undefined) ? Vec3.multiplyQbyV(parent.rotation, entityProperties.position) : entityProperties.position; entityProperties.position = Vec3.sum(localPosition, parent.position) } if (parent.id !== undefined) { entityProperties.parentID = parent.id; } entityProperties.id = Entities.addEntity(entityProperties, clientOnly); return entityProperties; }; var createEntitiesFromTree = function(entityTree, parent, clientOnly) { if (parent === undefined) { parent = {}; } var createdTree = []; entityTree.forEach(function(entityProperties) { var sanetizedProperties = {}; Object.keys(entityProperties).forEach(function(propertyKey) { if (!entityProperties.hasOwnProperty(propertyKey) || SANETIZE_PROPERTIES.indexOf(propertyKey) !== -1) { return true; } sanetizedProperties[propertyKey] = entityProperties[propertyKey]; }); // Allow for non-entity parent objects, this allows us to offset groups of entities to a specific position/rotation var parentProperties = sanetizedProperties; if (entityProperties.type !== undefined) { parentProperties = createEntity(sanetizedProperties, parent, clientOnly); } if (entityProperties.childEntities !== undefined) { parentProperties.childEntities = createEntitiesFromTree(entityProperties.childEntities, parentProperties, clientOnly); } createdTree.push(parentProperties); }); return createdTree; }; function HandyAttacher() { _this = this; }; HandyAttacher.prototype = { attachEntity: function(entityID, attachHand) { var userData; try { userData = JSON.parse(Entities.getEntityProperties(entityID, ['userData']).userData); } catch (e) { return; } if (userData.attachmentEntityJSON === undefined) { return; } if (attachHand === 'preset') { if (userData.attachHand === undefined) { return; } attachHand = userData.attachHand; } //if (userData.deleteOtherHandAttachment) { // MyAvatar.jointNames.indexOf(attachHand === 'left' ? 'RightHand' : 'LeftHand') // print('I am here. Yes delete it'); //} // !! makes sure that clientOnly would contain a boolean representation of the value set in the userdata var clientOnly = userData.clientOnly ? !!userData.clientOnly : false; var controllerPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(attachHand === 'left' ? CONTROLLER_LEFTHAND_INDEX : CONTROLLER_RIGHTHAND_INDEX)), MyAvatar.position); var attachmentJSON = importEntitiesJSON(userData.attachmentEntityJSON, { //position: controllerPosition position: MyAvatar.getJointPosition(MyAvatar.jointNames.indexOf(attachHand === 'left' ? 'LeftHand' : 'RightHand')) }); print(attachmentJSON); if (attachmentJSON === null) { return; } // print(JSON.stringify(attachmentJSON)); var createdEntities = createEntitiesFromTree([attachmentJSON], {}, clientOnly); print("Created = " + JSON.stringify(createdEntities[0])); var probableGolfClubID = createdEntities[0].childEntities[0].id; Messages.sendLocalMessage('Hifi-Hand-Grab', JSON.stringify({hand: attachHand, entityID: probableGolfClubID})); }, // enterEntity: function(entityID) { // print (' on enter!!'); // _this.attachEntity(entityID, 'preset'); // }, startNearTrigger: function(entityID, args) { print (' on startNearTrigger!!'); _this.attachEntity(entityID, args[0]); } }; return new HandyAttacher(); })