From 182ae22b44a50472a5fa73c87cb5c4a1e5b59354 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 Feb 2016 09:58:35 -0800 Subject: [PATCH] merge James' doppelganger changes into Tony's --- .../dressing_room/createPlatformWithLights.js | 160 ++++++++++++++++++ .../dressing_room/createTableWithItems.js | 117 +++++++++++++ examples/dressing_room/freezeToggler.js | 76 +++++++++ examples/dressing_room/loadingAreaEntity.js | 34 ++++ examples/dressing_room/mirroredEntity.js | 48 ++++++ examples/dressing_room/setupDressingRoom.js | 11 ++ examples/dressing_room/wearablesManager.js | 138 +++++++++++++++ 7 files changed, 584 insertions(+) create mode 100644 examples/dressing_room/createPlatformWithLights.js create mode 100644 examples/dressing_room/createTableWithItems.js create mode 100644 examples/dressing_room/freezeToggler.js create mode 100644 examples/dressing_room/loadingAreaEntity.js create mode 100644 examples/dressing_room/mirroredEntity.js create mode 100644 examples/dressing_room/setupDressingRoom.js create mode 100644 examples/dressing_room/wearablesManager.js diff --git a/examples/dressing_room/createPlatformWithLights.js b/examples/dressing_room/createPlatformWithLights.js new file mode 100644 index 0000000000..10061163fd --- /dev/null +++ b/examples/dressing_room/createPlatformWithLights.js @@ -0,0 +1,160 @@ +// +// createDressingPlatform.js +// +// Created by James B. Pollack @imgntn on 1/7/2016 +// Copyright 2016 High Fidelity, Inc. +// +// This script shows how to hook up a model entity to your avatar to act as a doppelganger. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var basePlatform; +var basePosition = Vec3.sum(Vec3.sum(MyAvatar.position, { + x: 0, + y: -1, + z: 0 +}), Vec3.multiply(2, Quat.getFront(Camera.getOrientation()))); + +var loadArea; +var LOAD_AREA_SCRIPT_URL = Script.resolvePath('loadingAreaEntity.js'); + +function createBasePlatform() { + var properties = { + type: 'Box', + name: 'Hifi-Dressing-Room-Base', + dimensions: { + x: 4, + y: 0.10, + z: 4 + }, + color: { + red: 255, + green: 0, + blue: 255 + }, + position: basePosition, + collisionsWillMove: false, + ignoreForCollisions: false, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + } + basePlatform = Entities.addEntity(properties); +} + +function createLoadArea() { + // on enter, load the wearables manager and the doppelganger manager; + // on exit, stop the scripts (at least call cleanup); + var properties = { + type: 'Box', + shapeType: 'box', + name: 'Hifi-Dressing-Room-Load-Area', + dimensions: { + x: 0.25, + y: 0.25, + z: 0.25 + }, + color: { + red: 0, + green: 255, + blue: 0 + }, + visible: true, + position: basePosition, + collisionsWillMove: false, + ignoreForCollisions: true, + script: LOAD_AREA_SCRIPT_URL, + + } + loadArea = Entities.addEntity(properties); +} +var lights = []; + +function createLightAtPosition(position) { + var lightProperties = { + name: 'Hifi-Spotlight', + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 8 + }, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 0.035, + exponent: 1, + cutoff: 40, + lifetime: -1, + position: position, + rotation: getLightRotation(position) + }; + + light = Entities.addEntity(lightProperties); + lights.push(light); +} + +function createLights() { + var lightPosition = { + x: basePosition.x - 2, + y: basePosition.y + 3, + z: basePosition.z + } + createLightAtPosition(lightPosition); + + var lightPosition = { + x: basePosition.x + 2, + y: basePosition.y + 3, + z: basePosition.z + } + + createLightAtPosition(lightPosition); + var lightPosition = { + x: basePosition.x, + y: basePosition.y + 3, + z: basePosition.z + 2 + } + + createLightAtPosition(lightPosition); + var lightPosition = { + x: basePosition.x, + y: basePosition.y + 3, + z: basePosition.z - 2 + } + + createLightAtPosition(lightPosition); + +} + +function getLightRotation(myPosition) { + + var sourceToTargetVec = Vec3.subtract(basePosition, myPosition); + var emitOrientation = Quat.rotationBetween(Vec3.UNIT_NEG_Z, sourceToTargetVec); + + return emitOrientation +} + +function init() { + createBasePlatform(); + createLights(); + // createLoadArea(); +} + + +function cleanup() { + Entities.deleteEntity(basePlatform); + + while (lights.length > 0) { + Entities.deleteEntity(lights.pop()); + } + // Entities.deleteEntity(loadArea); +} +init(); +Script.scriptEnding.connect(cleanup) \ No newline at end of file diff --git a/examples/dressing_room/createTableWithItems.js b/examples/dressing_room/createTableWithItems.js new file mode 100644 index 0000000000..b8119e3077 --- /dev/null +++ b/examples/dressing_room/createTableWithItems.js @@ -0,0 +1,117 @@ +// +// createTableWithItems.js +// +// Created by James B. Pollack @imgntn on 1/7/2016 +// Copyright 2016 High Fidelity, Inc. +// +// This script shows how to hook up a model entity to your avatar to act as a doppelganger. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +var table, wearable; + +var TABLE_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/doppelganger/table.FBX'; +var TABLE_DIMENSIONS = { + x: 0.76, + y: 1.06, + z: 0.76 +}; + +function createTable() { + var avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); + var position, rotation; + var ids = Entities.findEntities(MyAvatar.position, 20); + var hasBase = false; + for (var i = 0; i < ids.length; i++) { + var entityID = ids[i]; + var props = Entities.getEntityProperties(entityID, "name"); + var name = props.name; + if (name === "Hifi-Dressing-Room-Base") { + var details = Entities.getEntityProperties(entityID, ["position", "dimensions", "rotation"]); + var rightVector = Quat.getRight(details.rotation); + var rightDistance = 1.5; + position = Vec3.sum(Vec3.multiply(rightDistance, rightVector), details.position); + position.y = details.position.y += TABLE_DIMENSIONS.y / 2 + rotation = details.rotation; + hasBase = true; + } + } + + if (hasBase === false) { + position = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(avatarRot))); + rotation = avatarRot; + } + + var tableProperties = { + name: 'Hifi-Dressing-Room-Table', + type: 'Model', + shapeType:'box', + modelURL: TABLE_MODEL_URL, + dimensions: TABLE_DIMENSIONS, + position: position, + rotation: rotation, + collisionsWillMove: false, + ignoreForCollisions: false, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + } + print('TABLE PROPS', JSON.stringify(tableProperties)) + table = Entities.addEntity(tableProperties); +} + + +function createWearable() { + var tableProperties = Entities.getEntityProperties(table); + var properties = { + type: 'Model', + modelURL: 'https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx', + name: 'Hifi-Wearable', + dimensions: { + x: 0.25, + y: 0.25, + z: 0.25 + }, + color: { + red: 0, + green: 255, + blue: 0 + }, + position: { + x: tableProperties.position.x, + y: tableProperties.position.y + tableProperties.dimensions.y / 1.5, + z: tableProperties.position.z + }, + userData: JSON.stringify({ + "grabbableKey": { + "invertSolidWhileHeld": false + }, + "wearable": { + "joints": ["head", "Head", "hair", "neck"] + }, + handControllerKey: { + disableReleaseVelocity: true, + disableMoveWithHead: true, + } + }) + } + wearable = Entities.addEntity(properties); +} + +function init() { + createTable(); + createWearable(); +} + +function cleanup() { + Entities.deleteEntity(table); + Entities.deleteEntity(wearable); + +} +init(); +Script.scriptEnding.connect(cleanup) \ No newline at end of file diff --git a/examples/dressing_room/freezeToggler.js b/examples/dressing_room/freezeToggler.js new file mode 100644 index 0000000000..6b83a606da --- /dev/null +++ b/examples/dressing_room/freezeToggler.js @@ -0,0 +1,76 @@ +// +// dopppelgangerEntity.js +// +// Created by James B. Pollack @imgntn on 1/6/2016 +// Copyright 2016 High Fidelity, Inc. +// +// for freezing / unfreezing the doppelganger +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + var COUNTDOWN_LENGTH = 0; + var _this; + + Dopppelganger = function() { + + _this = this; + }; + + Dopppelganger.prototype = { + isFrozen: false, + startNearTrigger: function() { + print('DOPPELGANGER NEAR TRIGGER') + }, + startFarTrigger: function() { + print('DOPPELGANGER FAR TRIGGER') + if (this.isFrozen === false) { + this.freeze(); + } else { + this.unfreeze(); + } + + }, + clickReleaseOnEntity: function(entityID, mouseEvent) { + print('DOPPELGANGER CLICK') + if (!mouseEvent.isLeftButton) { + return; + } + if (this.isFrozen === false) { + this.freeze(); + } else { + this.unfreeze(); + } + + }, + freeze: function() { + print('FREEZE YO') + this.isFrozen = true; + var data = { + action: 'freeze' + } + + Script.setTimeout(function() { + Messages.sendMessage('Hifi-Doppelganger-Freeze', JSON.stringify(data)); + }, COUNTDOWN_LENGTH) + + }, + unfreeze: function() { + this.isFrozen = false; + var data = { + action: 'unfreeze' + } + Messages.sendMessage('Hifi-Doppelganger-Freeze', JSON.stringify(data)); + }, + + preload: function(entityID) { + this.entityID = entityID; + this.initialProperties = Entities.getEntityProperties(this.entityID); + this.userData = JSON.parse(this.initialProperties.userData); + }, + }; + + return new Dopppelganger(); +}) \ No newline at end of file diff --git a/examples/dressing_room/loadingAreaEntity.js b/examples/dressing_room/loadingAreaEntity.js new file mode 100644 index 0000000000..73ea142d59 --- /dev/null +++ b/examples/dressing_room/loadingAreaEntity.js @@ -0,0 +1,34 @@ +// +// loadingAreaEntity.js +// +// Created by James B. Pollack @imgntn on 1/7/2016 +// Copyright 2016 High Fidelity, Inc. +// +// This script shows how to hook up a model entity to your avatar to act as a doppelganger. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + + var WEARABLES_MANAGER_SCRIPT = Script.resolvePath('wearablesManager.js'); + var DOPPELGANGER_SCRIPT = Script.resolvePath('doppelganger.js'); + var CREATE_TEST_WEARABLE_SCRIPT = Script.resolvePath('createTestWearable.js'); + this.preload = function(entityID) { + print("preload(" + entityID + ")"); + }; + + this.enterEntity = function(entityID) { + print("enterEntity(" + entityID + ")"); + // Script.load(WEARABLES_MANAGER_SCRIPT); + // Script.load(DOPPELGANGER_SCRIPT); + // Script.load(CREATE_TEST_WEARABLE_SCRIPT); + + }; + + this.leaveEntity = function(entityID) { + print("leaveEntity(" + entityID + ")"); + }; + +}) \ No newline at end of file diff --git a/examples/dressing_room/mirroredEntity.js b/examples/dressing_room/mirroredEntity.js new file mode 100644 index 0000000000..0310b21f96 --- /dev/null +++ b/examples/dressing_room/mirroredEntity.js @@ -0,0 +1,48 @@ +// +// mirroredEntity.js +// +// Created by James B. Pollack @imgntn on 1/6/2016 +// Copyright 2016 High Fidelity, Inc. +// +// when grabbed, this entity relays updates to update the base entity +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + + var _this; + + MirroredEntity = function() { + _this = this; + }; + + MirroredEntity.prototype = { + startNearGrab: function () { + print("I was just grabbed... entity:" + this.entityID); + }, + continueNearGrab: function () { + print("I am still being grabbed... entity:" + this.entityID); + var data = { + action:'updateBase', + baseEntity:this.userData.doppelgangerKey.baseEntity, + mirrorEntity:this.entityID, + doppelganger:this.userData.doppelgangerKey.doppelganger + } + Messages.sendMessage('Hifi-Doppelganger-Wearable',data) + }, + + releaseGrab: function () { + print("I was released... entity:" + this.entityID); + }, + + preload: function(entityID) { + this.entityID = entityID; + this.initialProperties = Entities.getEntityProperties(this.entityID); + this.userData = JSON.parse(this.initialProperties.userData); + }, + }; + + return new MirroredEntity(); +}) diff --git a/examples/dressing_room/setupDressingRoom.js b/examples/dressing_room/setupDressingRoom.js new file mode 100644 index 0000000000..6de346ace2 --- /dev/null +++ b/examples/dressing_room/setupDressingRoom.js @@ -0,0 +1,11 @@ +var createPlatformWithLights = Script.resolvePath('createPlatformWithLights.js?'+Math.random(0,100)); +Script.include(createPlatformWithLights); +var createTableWithItems = Script.resolvePath('createTableWithItems.js?'+Math.random(0,100)); +Script.include(createTableWithItems); +var doppelganger = Script.resolvePath('doppelganger.js?'+Math.random(0,100)); +Script.include(doppelganger); +var wearablesManager = Script.resolvePath('wearablesManager.js?'+Math.random(0,100)); +Script.include(wearablesManager); +var handControllerGrab = Script.resolvePath('../controllers/handControllerGrab.js?'+Math.random(0,100)); +Script.include(handControllerGrab); +//put it in an interior diff --git a/examples/dressing_room/wearablesManager.js b/examples/dressing_room/wearablesManager.js new file mode 100644 index 0000000000..99623023d0 --- /dev/null +++ b/examples/dressing_room/wearablesManager.js @@ -0,0 +1,138 @@ +// +// wearablesManager.js +// +// Created by James B. Pollack @imgntn on 1/7/2016 +// Copyright 2016 High Fidelity, Inc. +// +// This script handles messages from the grab script related to wearables, and interacts with a doppelganger. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// todo: +// add camera countdown / freezing unfreezing the doppelganger +// add ability to drop wearables on doppelganger +// which means creating a mirror entity on the avatar ... + +Script.include("../libraries/utils.js"); + +var NULL_UUID = "{00000000-0000-0000-0000-000000000000}"; +var DEFAULT_WEARABLE_DATA = { + joints: {} +}; + +function WearablesManager() { + + this.wearables = []; + + this.subscribeToMessages = function() { + Messages.subscribe('Hifi-Object-Manipulation'); + Messages.messageReceived.connect(this.handleWearableMessages); + } + + this.handleWearableMessages = function(channel, message, sender) { + // print('wearablesManager messageReceived ::: ' + channel + " ::: " + message) + if (channel !== 'Hifi-Object-Manipulation') { + return; + } + // if (sender !== MyAvatar.sessionUUID) { + // print('wearablesManager got message from wrong sender'); + // return; + // } + + var parsedMessage = null; + + try { + parsedMessage = JSON.parse(message); + } catch (e) { + print('error parsing wearable message'); + } + + if (parsedMessage.action === 'update' && manager.wearables.length !== 0) { + manager.updateWearable(parsedMessage.grabbedEntity) + } else if (parsedMessage.action === 'update' && manager.wearables.length === 0) { + } else if (parsedMessage.action === 'release') { + manager.checkIfWearable(parsedMessage.grabbedEntity) + } else { + print('unknown actions: ' + parsedMessage.action); + } + } + + this.updateWearable = function(grabbedEntity) { + if (this.wearables.length > 0) { + + //only do this check if we already have some wearables for the doppelganger + var hasWearableAlready = this.wearables.indexOf(grabbedEntity); + var props = Entities.getEntityProperties(grabbedEntity); + + if (hasWearableAlready > -1) { + var data = { + action: 'update', + baseEntity: grabbedEntity, + } + + Messages.sendMessage('Hifi-Doppelganger-Wearable', JSON.stringify(data)) + } + } + } + + this.checkIfWearableOnDoppelganger = function(grabbedEntity) { + var allowedJoints = getEntityCustomData('wearable', grabbedEntity, DEFAULT_WEARABLE_DATA).joints; + + var props = Entities.getEntityProperties(grabbedEntity, ["position", "parentID"]); + if (props.parentID === NULL_UUID || props.parentID === MyAvatar.sessionUUID) { + var bestJointName = ""; + var bestJointIndex = -1; + var bestJointDistance = 0; + for (var jointName in allowedJoints) { + //do this for the model + var jointIndex = Entities.getJointIndex(doppelganger.id,jointName); + var jointPosition = Entities.getJointPosition(doppelganger.id,jointIndex); + var distanceFromJoint = Vec3.distance(jointPosition, props.position); + if (distanceFromJoint < 0.4) { + if (bestJointIndex == -1 || distanceFromJoint < bestJointDistance) { + bestJointName = jointName; + bestJointIndex = jointIndex; + bestJointDistance = distanceFromJoint; + } + } + } + + if (bestJointIndex != -1) { + Entities.editEntity(grabbedEntity, { + parentID: doppelganger.id, + parentJointIndex: bestJointIndex + }); + + if (this.wearables.indexOf(grabbedEntity) < 0) { + var data = { + action: 'addToDoppelganger', + baseEntity: grabbedEntity, + } + Messages.sendMessage('Hifi-Doppelganger-Wearable-Avatar', JSON.stringify(data)); + this.wearables.push(grabbedEntity) + } + } else { + Entities.editEntity(grabbedEntity, { + parentID: NULL_UUID + }); + + var hasWearableAlready = this.wearables.indexOf(grabbedEntity); + if (hasWearableAlready > -1) { + var data = { + action: 'removeFromDoppelganger', + baseEntity: grabbedEntity + } + + Messages.sendMessage('Hifi-Doppelganger-Wearable-Avatar', JSON.stringify(data)); + } + + this.wearables.splice(hasWearableAlready, 1) + } + } + } +} + +var manager = new WearablesManager(); +manager.subscribeToMessages();