mirror of
https://github.com/overte-org/overte.git
synced 2025-07-22 22:34:12 +02:00
Merge pull request #7037 from sethalves/dressing-room
Dressing room + grab fixes
This commit is contained in:
commit
c28612ea9a
11 changed files with 958 additions and 40 deletions
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
//For procedural walk animation
|
//For procedural walk animation
|
||||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||||
Script.include(HIFI_PUBLIC_BUCKET + "scripts/proceduralAnimationAPI.js");
|
Script.include(HIFI_PUBLIC_BUCKET + "scripts/acScripts/proceduralAnimationAPI.js");
|
||||||
|
|
||||||
var procAnimAPI = new ProcAnimAPI();
|
var procAnimAPI = new ProcAnimAPI();
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ var DEFAULT_WEARABLE_DATA = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var MINIMUM_DROP_DISTANCE_FROM_JOINT = 0.4;
|
var MINIMUM_DROP_DISTANCE_FROM_JOINT = 0.8;
|
||||||
var ATTACHED_ENTITY_SEARCH_DISTANCE = 10.0;
|
var ATTACHED_ENTITY_SEARCH_DISTANCE = 10.0;
|
||||||
var ATTACHED_ENTITIES_SETTINGS_KEY = "ATTACHED_ENTITIES";
|
var ATTACHED_ENTITIES_SETTINGS_KEY = "ATTACHED_ENTITIES";
|
||||||
var DRESSING_ROOM_DISTANCE = 2.0;
|
var DRESSING_ROOM_DISTANCE = 2.0;
|
||||||
|
@ -161,7 +161,7 @@ function AttachedEntitiesManager() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var jointIndex = MyAvatar.getJointIndex(jointName);
|
var jointIndex = MyAvatar.getJointIndex(jointName);
|
||||||
if (jointIndex > 0) {
|
if (jointIndex >= 0) {
|
||||||
var jointPosition = MyAvatar.getJointPosition(jointIndex);
|
var jointPosition = MyAvatar.getJointPosition(jointIndex);
|
||||||
var distanceFromJoint = Vec3.distance(jointPosition, props.position);
|
var distanceFromJoint = Vec3.distance(jointPosition, props.position);
|
||||||
if (distanceFromJoint <= MINIMUM_DROP_DISTANCE_FROM_JOINT) {
|
if (distanceFromJoint <= MINIMUM_DROP_DISTANCE_FROM_JOINT) {
|
||||||
|
@ -182,7 +182,9 @@ function AttachedEntitiesManager() {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (bestJointOffset && bestJointOffset.constructor === Array && bestJointOffset.length > 1) {
|
if (bestJointOffset && bestJointOffset.constructor === Array && bestJointOffset.length > 1) {
|
||||||
if (!this.avatarIsInDressingRoom()) {
|
if (this.avatarIsInDressingRoom()) {
|
||||||
|
this.updateRelativeOffsets(grabbedEntity);
|
||||||
|
} else {
|
||||||
// don't snap the entity to the preferred position if the avatar is in the dressing room.
|
// don't snap the entity to the preferred position if the avatar is in the dressing room.
|
||||||
wearProps.localPosition = bestJointOffset[0];
|
wearProps.localPosition = bestJointOffset[0];
|
||||||
wearProps.localRotation = bestJointOffset[1];
|
wearProps.localRotation = bestJointOffset[1];
|
||||||
|
|
|
@ -19,6 +19,7 @@ Script.include("../libraries/utils.js");
|
||||||
//
|
//
|
||||||
var WANT_DEBUG = false;
|
var WANT_DEBUG = false;
|
||||||
var WANT_DEBUG_STATE = false;
|
var WANT_DEBUG_STATE = false;
|
||||||
|
var WANT_DEBUG_SEARCH_NAME = null;
|
||||||
|
|
||||||
//
|
//
|
||||||
// these tune time-averaging and "on" value for analog trigger
|
// these tune time-averaging and "on" value for analog trigger
|
||||||
|
@ -214,6 +215,10 @@ function getTag() {
|
||||||
return "grab-" + MyAvatar.sessionUUID;
|
return "grab-" + MyAvatar.sessionUUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function entityHasActions(entityID) {
|
||||||
|
return Entities.getActionIDs(entityID).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
function entityIsGrabbedByOther(entityID) {
|
function entityIsGrabbedByOther(entityID) {
|
||||||
// by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*.
|
// by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*.
|
||||||
var actionIDs = Entities.getActionIDs(entityID);
|
var actionIDs = Entities.getActionIDs(entityID);
|
||||||
|
@ -774,6 +779,7 @@ function MyController(hand) {
|
||||||
this.search = function() {
|
this.search = function() {
|
||||||
this.grabbedEntity = null;
|
this.grabbedEntity = null;
|
||||||
this.isInitialGrab = false;
|
this.isInitialGrab = false;
|
||||||
|
this.doubleParentGrab = false;
|
||||||
|
|
||||||
if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) {
|
if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) {
|
||||||
this.setState(STATE_RELEASE);
|
this.setState(STATE_RELEASE);
|
||||||
|
@ -867,36 +873,58 @@ function MyController(hand) {
|
||||||
grabbable = false;
|
grabbable = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("grabbable" in grabbableDataForCandidate) {
|
if ("grabbable" in grabbableDataForCandidate) {
|
||||||
// if userData indicates that this is grabbable or not, override the default.
|
// if userData indicates that this is grabbable or not, override the default.
|
||||||
grabbable = grabbableDataForCandidate.grabbable;
|
grabbable = grabbableDataForCandidate.grabbable;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!grabbable && !grabbableDataForCandidate.wantsTrigger) {
|
if (!grabbable && !grabbableDataForCandidate.wantsTrigger) {
|
||||||
|
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||||
|
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': not grabbable.");
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (forbiddenTypes.indexOf(propsForCandidate.type) >= 0) {
|
if (forbiddenTypes.indexOf(propsForCandidate.type) >= 0) {
|
||||||
|
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||||
|
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': forbidden entity type.");
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) {
|
if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) {
|
||||||
|
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||||
|
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': locked and not triggerable.");
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (forbiddenNames.indexOf(propsForCandidate.name) >= 0) {
|
if (forbiddenNames.indexOf(propsForCandidate.name) >= 0) {
|
||||||
|
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||||
|
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': forbidden name.");
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
distance = Vec3.distance(propsForCandidate.position, handPosition);
|
distance = Vec3.distance(propsForCandidate.position, handPosition);
|
||||||
if (distance > PICK_MAX_DISTANCE) {
|
if (distance > PICK_MAX_DISTANCE) {
|
||||||
// too far away, don't grab
|
// too far away, don't grab
|
||||||
|
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||||
|
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': too far away.");
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (propsForCandidate.parentID != NULL_UUID && this.state == STATE_EQUIP_SEARCHING) {
|
if (propsForCandidate.parentID != NULL_UUID && this.state == STATE_EQUIP_SEARCHING) {
|
||||||
// don't allow a double-equip
|
// don't allow a double-equip
|
||||||
|
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||||
|
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': it's a child");
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state == STATE_SEARCHING && !isPhysical && distance > NEAR_PICK_MAX_DISTANCE) {
|
if (this.state == STATE_SEARCHING && !isPhysical && distance > NEAR_PICK_MAX_DISTANCE) {
|
||||||
// we can't distance-grab non-physical
|
// we can't distance-grab non-physical
|
||||||
|
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||||
|
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': not physical and too far for near-grab");
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -918,7 +946,7 @@ function MyController(hand) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// near grab or equip with action
|
// near grab or equip with action
|
||||||
if (near) {
|
if (near && (grabbableData.refCount < 1 || entityHasActions(this.grabbedEntity))) {
|
||||||
this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP);
|
this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -926,6 +954,9 @@ function MyController(hand) {
|
||||||
if ((isPhysical || this.state == STATE_EQUIP_SEARCHING) && !near) {
|
if ((isPhysical || this.state == STATE_EQUIP_SEARCHING) && !near) {
|
||||||
if (entityIsGrabbedByOther(intersection.entityID)) {
|
if (entityIsGrabbedByOther(intersection.entityID)) {
|
||||||
// don't distance grab something that is already grabbed.
|
// don't distance grab something that is already grabbed.
|
||||||
|
if (WANT_DEBUG_SEARCH_NAME && props.name == WANT_DEBUG_SEARCH_NAME) {
|
||||||
|
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': already grabbed by another.");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.temporaryPositionOffset = null;
|
this.temporaryPositionOffset = null;
|
||||||
|
@ -951,6 +982,18 @@ function MyController(hand) {
|
||||||
if (grabbableData.refCount < 1) {
|
if (grabbableData.refCount < 1) {
|
||||||
this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP);
|
this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP);
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
// it's not physical and it's already held via parenting. go ahead and grab it, but
|
||||||
|
// save off the current parent and joint. this wont always be right if there are more than
|
||||||
|
// two grabs and the order of release isn't opposite of the order of grabs.
|
||||||
|
this.doubleParentGrab = true;
|
||||||
|
this.previousParentID = props.parentID;
|
||||||
|
this.previousParentJointIndex = props.parentJointIndex;
|
||||||
|
this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (WANT_DEBUG_SEARCH_NAME && props.name == WANT_DEBUG_SEARCH_NAME) {
|
||||||
|
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': fell through.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -979,7 +1022,9 @@ function MyController(hand) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.distanceGrabTimescale = function(mass, distance) {
|
this.distanceGrabTimescale = function(mass, distance) {
|
||||||
var timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME * mass / DISTANCE_HOLDING_UNITY_MASS * distance / DISTANCE_HOLDING_UNITY_DISTANCE;
|
var timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME * mass /
|
||||||
|
DISTANCE_HOLDING_UNITY_MASS * distance /
|
||||||
|
DISTANCE_HOLDING_UNITY_DISTANCE;
|
||||||
if (timeScale < DISTANCE_HOLDING_ACTION_TIMEFRAME) {
|
if (timeScale < DISTANCE_HOLDING_ACTION_TIMEFRAME) {
|
||||||
timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME;
|
timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME;
|
||||||
}
|
}
|
||||||
|
@ -1310,7 +1355,8 @@ function MyController(hand) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var handRotation = this.getHandRotation();
|
// var handRotation = this.getHandRotation();
|
||||||
|
var handRotation = (this.hand === RIGHT_HAND) ? MyAvatar.getRightPalmRotation() : MyAvatar.getLeftPalmRotation();
|
||||||
var handPosition = this.getHandPosition();
|
var handPosition = this.getHandPosition();
|
||||||
|
|
||||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||||
|
@ -1337,7 +1383,7 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isPhysical = this.propsArePhysical(grabbedProperties);
|
var isPhysical = this.propsArePhysical(grabbedProperties) || entityHasActions(this.grabbedEntity);
|
||||||
if (isPhysical && this.state == STATE_NEAR_GRABBING) {
|
if (isPhysical && this.state == STATE_NEAR_GRABBING) {
|
||||||
// grab entity via action
|
// grab entity via action
|
||||||
if (!this.setupHoldAction()) {
|
if (!this.setupHoldAction()) {
|
||||||
|
@ -1403,6 +1449,17 @@ function MyController(hand) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID"]);
|
||||||
|
if (props.parentID == MyAvatar.sessionUUID &&
|
||||||
|
Vec3.length(props.localPosition) > NEAR_PICK_MAX_DISTANCE * 2.0) {
|
||||||
|
// for whatever reason, the held/equipped entity has been pulled away. ungrab or unequip.
|
||||||
|
print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand.");
|
||||||
|
this.setState(STATE_RELEASE);
|
||||||
|
this.callEntityMethodOnGrabbed(this.state == STATE_NEAR_GRABBING ? "releaseGrab" : "releaseEquip",
|
||||||
|
[JSON.stringify(this.hand)]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Keep track of the fingertip velocity to impart when we release the object.
|
// Keep track of the fingertip velocity to impart when we release the object.
|
||||||
// Note that the idea of using a constant 'tip' velocity regardless of the
|
// Note that the idea of using a constant 'tip' velocity regardless of the
|
||||||
// object's actual held offset is an idea intended to make it easier to throw things:
|
// object's actual held offset is an idea intended to make it easier to throw things:
|
||||||
|
@ -1602,21 +1659,14 @@ function MyController(hand) {
|
||||||
if (this.grabbedEntity !== null) {
|
if (this.grabbedEntity !== null) {
|
||||||
if (this.actionID !== null) {
|
if (this.actionID !== null) {
|
||||||
Entities.deleteAction(this.grabbedEntity, this.actionID);
|
Entities.deleteAction(this.grabbedEntity, this.actionID);
|
||||||
//sometimes we want things to stay right where they are when we let go.
|
// sometimes we want things to stay right where they are when we let go.
|
||||||
|
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {});
|
||||||
var releaseVelocityData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
var releaseVelocityData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||||
if (releaseVelocityData.disableReleaseVelocity === true || !this.isInitialGrab) {
|
if (releaseVelocityData.disableReleaseVelocity === true ||
|
||||||
Entities.editEntity(this.grabbedEntity, {
|
// this next line allowed both:
|
||||||
velocity: {
|
// (1) far-grab, pull to self, near grab, then throw
|
||||||
x: 0,
|
// (2) equip something physical and adjust it with a other-hand grab without the thing drifting
|
||||||
y: 0,
|
(!this.isInitialGrab && grabData.refCount > 1)) {
|
||||||
z: 0
|
|
||||||
},
|
|
||||||
angularVelocity: {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
z: 0
|
|
||||||
}
|
|
||||||
});
|
|
||||||
noVelocity = true;
|
noVelocity = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1721,15 +1771,27 @@ function MyController(hand) {
|
||||||
data["dynamic"] &&
|
data["dynamic"] &&
|
||||||
data["parentID"] == NULL_UUID &&
|
data["parentID"] == NULL_UUID &&
|
||||||
!data["collisionless"]) {
|
!data["collisionless"]) {
|
||||||
forceVelocity = true;
|
deactiveProps["velocity"] = {x: 0.0, y: 0.1, z: 0.0};
|
||||||
|
}
|
||||||
|
if (noVelocity) {
|
||||||
|
deactiveProps["velocity"] = {x: 0.0, y: 0.0, z: 0.0};
|
||||||
|
deactiveProps["angularVelocity"] = {x: 0.0, y: 0.0, z: 0.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
Entities.editEntity(entityID, deactiveProps);
|
Entities.editEntity(entityID, deactiveProps);
|
||||||
|
|
||||||
if (forceVelocity) {
|
|
||||||
Entities.editEntity(entityID, {velocity:{x:0, y:0.1, z:0}});
|
|
||||||
}
|
|
||||||
data = null;
|
data = null;
|
||||||
|
} else if (this.doubleParentGrab) {
|
||||||
|
// we parent-grabbed this from another parent grab. try to put it back where we found it.
|
||||||
|
var deactiveProps = {
|
||||||
|
parentID: this.previousParentID,
|
||||||
|
parentJointIndex: this.previousParentJointIndex,
|
||||||
|
velocity: {x: 0.0, y: 0.0, z: 0.0},
|
||||||
|
angularVelocity: {x: 0.0, y: 0.0, z: 0.0}
|
||||||
|
};
|
||||||
|
Entities.editEntity(entityID, deactiveProps);
|
||||||
|
} else if (noVelocity) {
|
||||||
|
Entities.editEntity(entityID, {velocity: {x: 0.0, y: 0.0, z: 0.0},
|
||||||
|
angularVelocity: {x: 0.0, y: 0.0, z: 0.0}});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
data = null;
|
data = null;
|
||||||
|
|
160
examples/dressing_room/createPlatformWithLights.js
Normal file
160
examples/dressing_room/createPlatformWithLights.js
Normal file
|
@ -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)
|
117
examples/dressing_room/createTableWithItems.js
Normal file
117
examples/dressing_room/createTableWithItems.js
Normal file
|
@ -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)
|
|
@ -13,9 +13,12 @@
|
||||||
|
|
||||||
var TEST_MODEL_URL = 'https://s3.amazonaws.com/hifi-public/ozan/avatars/albert/albert/albert.fbx';
|
var TEST_MODEL_URL = 'https://s3.amazonaws.com/hifi-public/ozan/avatars/albert/albert/albert.fbx';
|
||||||
|
|
||||||
var doppelgangers = [];
|
|
||||||
|
|
||||||
var MIRROR_JOINT_DATA = true;
|
var MIRROR_JOINT_DATA = true;
|
||||||
|
var MIRRORED_ENTITY_SCRIPT_URL = Script.resolvePath('mirroredEntity.js');
|
||||||
|
var FREEZE_TOGGLER_SCRIPT_URL = Script.resolvePath('freezeToggler.js?' + Math.random(0, 1000))
|
||||||
|
var THROTTLE = false;
|
||||||
|
var THROTTLE_RATE = 100;
|
||||||
|
var doppelgangers = [];
|
||||||
|
|
||||||
function Doppelganger(avatar) {
|
function Doppelganger(avatar) {
|
||||||
this.initialProperties = {
|
this.initialProperties = {
|
||||||
|
@ -25,13 +28,21 @@ function Doppelganger(avatar) {
|
||||||
// dimensions: getAvatarDimensions(avatar),
|
// dimensions: getAvatarDimensions(avatar),
|
||||||
position: putDoppelgangerAcrossFromAvatar(this, avatar),
|
position: putDoppelgangerAcrossFromAvatar(this, avatar),
|
||||||
rotation: rotateDoppelgangerTowardAvatar(this, avatar),
|
rotation: rotateDoppelgangerTowardAvatar(this, avatar),
|
||||||
|
collisionsWillMove: false,
|
||||||
|
ignoreForCollisions: false,
|
||||||
|
script: FREEZE_TOGGLER_SCRIPT_URL,
|
||||||
|
userData: JSON.stringify({
|
||||||
|
grabbableKey: {
|
||||||
|
grabbable: false,
|
||||||
|
wantsTrigger: true
|
||||||
|
}
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
this.id = createDoppelgangerEntity(this);
|
this.id = createDoppelgangerEntity(this);
|
||||||
this.avatar = avatar;
|
this.avatar = avatar;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getJointData(avatar) {
|
function getJointData(avatar) {
|
||||||
var allJointData = [];
|
var allJointData = [];
|
||||||
var jointNames = MyAvatar.jointNames;
|
var jointNames = MyAvatar.jointNames;
|
||||||
|
@ -297,30 +308,113 @@ function createDoppelgangerEntity(doppelganger) {
|
||||||
|
|
||||||
function putDoppelgangerAcrossFromAvatar(doppelganger, avatar) {
|
function putDoppelgangerAcrossFromAvatar(doppelganger, avatar) {
|
||||||
var avatarRot = Quat.fromPitchYawRollDegrees(0, avatar.bodyYaw, 0.0);
|
var avatarRot = Quat.fromPitchYawRollDegrees(0, avatar.bodyYaw, 0.0);
|
||||||
var basePosition = Vec3.sum(avatar.position, Vec3.multiply(1.5, Quat.getFront(avatarRot)));
|
var position;
|
||||||
return basePosition;
|
|
||||||
|
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"]);
|
||||||
|
details.position.y += getAvatarFootOffset();
|
||||||
|
details.position.y += details.dimensions.y / 2;
|
||||||
|
position = details.position;
|
||||||
|
hasBase = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasBase === false) {
|
||||||
|
position = Vec3.sum(avatar.position, Vec3.multiply(1.5, Quat.getFront(avatarRot)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAvatarFootOffset() {
|
||||||
|
var data = getJointData();
|
||||||
|
var upperLeg, lowerLeg, foot, toe, toeTop;
|
||||||
|
data.forEach(function(d) {
|
||||||
|
|
||||||
|
var jointName = d.joint;
|
||||||
|
if (jointName === "RightUpLeg") {
|
||||||
|
upperLeg = d.translation.y;
|
||||||
|
}
|
||||||
|
if (jointName === "RightLeg") {
|
||||||
|
lowerLeg = d.translation.y;
|
||||||
|
}
|
||||||
|
if (jointName === "RightFoot") {
|
||||||
|
foot = d.translation.y;
|
||||||
|
}
|
||||||
|
if (jointName === "RightToeBase") {
|
||||||
|
toe = d.translation.y;
|
||||||
|
}
|
||||||
|
if (jointName === "RightToe_End") {
|
||||||
|
toeTop = d.translation.y
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var myPosition = MyAvatar.position;
|
||||||
|
var offset = upperLeg + lowerLeg + foot + toe + toeTop;
|
||||||
|
offset = offset / 100;
|
||||||
|
return offset
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function getAvatarDimensions(avatar) {
|
function getAvatarDimensions(avatar) {
|
||||||
return dimensions;
|
return dimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function rotateDoppelgangerTowardAvatar(doppelganger, avatar) {
|
function rotateDoppelgangerTowardAvatar(doppelganger, avatar) {
|
||||||
var avatarRot = Quat.fromPitchYawRollDegrees(0, avatar.bodyYaw, 0.0);
|
var avatarRot = Quat.fromPitchYawRollDegrees(0, avatar.bodyYaw, 0.0);
|
||||||
avatarRot = Vec3.multiply(-1, avatarRot);
|
|
||||||
|
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, "rotation");
|
||||||
|
avatarRot = details.rotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasBase === false) {
|
||||||
|
avatarRot = Vec3.multiply(-1, avatarRot);
|
||||||
|
}
|
||||||
return avatarRot;
|
return avatarRot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isConnected = false;
|
||||||
|
|
||||||
function connectDoppelgangerUpdates() {
|
function connectDoppelgangerUpdates() {
|
||||||
// Script.update.connect(updateDoppelganger);
|
Script.update.connect(updateDoppelganger);
|
||||||
Script.setInterval(updateDoppelganger, 100);
|
isConnected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function disconnectDoppelgangerUpdates() {
|
function disconnectDoppelgangerUpdates() {
|
||||||
Script.update.disconnect(updateDoppelganger);
|
print('SHOULD DISCONNECT')
|
||||||
|
if (isConnected === true) {
|
||||||
|
Script.update.disconnect(updateDoppelganger);
|
||||||
|
}
|
||||||
|
isConnected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDoppelganger() {
|
var sinceLastUpdate = 0;
|
||||||
|
|
||||||
|
function updateDoppelganger(deltaTime) {
|
||||||
|
if (THROTTLE === true) {
|
||||||
|
sinceLastUpdate = sinceLastUpdate + deltaTime * 100;
|
||||||
|
if (sinceLastUpdate > THROTTLE_RATE) {
|
||||||
|
sinceLastUpdate = 0;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var absoluteXforms = buildAbsoluteXformsFromMyAvatar();
|
var absoluteXforms = buildAbsoluteXformsFromMyAvatar();
|
||||||
if (MIRROR_JOINT_DATA) {
|
if (MIRROR_JOINT_DATA) {
|
||||||
var mirroredAbsoluteXforms = [];
|
var mirroredAbsoluteXforms = [];
|
||||||
|
@ -346,14 +440,190 @@ function makeDoppelgangerForMyAvatar() {
|
||||||
connectDoppelgangerUpdates();
|
connectDoppelgangerUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function subscribeToWearableMessages() {
|
||||||
|
Messages.subscribe('Hifi-Doppelganger-Wearable');
|
||||||
|
Messages.messageReceived.connect(handleWearableMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
function subscribeToFreezeMessages() {
|
||||||
|
Messages.subscribe('Hifi-Doppelganger-Freeze');
|
||||||
|
Messages.messageReceived.connect(handleFreezeMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFreezeMessages(channel, message, sender) {
|
||||||
|
if (channel !== 'Hifi-Doppelganger-Freeze') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sender !== MyAvatar.sessionUUID) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedMessage = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
parsedMessage = JSON.parse(message);
|
||||||
|
} catch (e) {
|
||||||
|
print('error parsing wearable message');
|
||||||
|
}
|
||||||
|
print('MESSAGE ACTION::' + parsedMessage.action)
|
||||||
|
if (parsedMessage.action === 'freeze') {
|
||||||
|
print('ACTUAL FREEZE')
|
||||||
|
disconnectDoppelgangerUpdates();
|
||||||
|
}
|
||||||
|
if (parsedMessage.action === 'unfreeze') {
|
||||||
|
print('ACTUAL UNFREEZE')
|
||||||
|
|
||||||
|
connectDoppelgangerUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var wearablePairs = [];
|
||||||
|
|
||||||
|
function handleWearableMessages(channel, message, sender) {
|
||||||
|
if (channel !== 'Hifi-Doppelganger-Wearable' || 'Hifi-Doppelganger-Wearable-Avatar') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sender !== MyAvatar.sessionUUID) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedMessage = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
parsedMessage = JSON.parse(message);
|
||||||
|
} catch (e) {
|
||||||
|
print('error parsing wearable message');
|
||||||
|
}
|
||||||
|
print('parsed message!!!')
|
||||||
|
|
||||||
|
if (channel === 'Hifi-Doppelganger-Wearable') {
|
||||||
|
mirrorEntitiesForDoppelganger(doppelgangers[0], parsedMessage);
|
||||||
|
}
|
||||||
|
if (channel === 'Hifi-Doppelganger-Wearable') {
|
||||||
|
mirrorEntitiesForAvatar(parsedMessge);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function mirrorEntitiesForDoppelganger(doppelganger, parsedMessage) {
|
||||||
|
var doppelgangerProps = Entities.getEntityProperties(doppelganger.id);
|
||||||
|
|
||||||
|
var action = parsedMessage.action;
|
||||||
|
print('IN MIRROR ENTITIES CALL' + action)
|
||||||
|
|
||||||
|
var baseEntity = parsedMessage.baseEntity;
|
||||||
|
|
||||||
|
var wearableProps = Entities.getEntityProperties(baseEntity);
|
||||||
|
|
||||||
|
print('WEARABLE PROPS::')
|
||||||
|
delete wearableProps.id;
|
||||||
|
delete wearableProps.created;
|
||||||
|
delete wearableProps.age;
|
||||||
|
delete wearableProps.ageAsText;
|
||||||
|
//delete wearableProps.position;
|
||||||
|
// add to dg
|
||||||
|
// add to avatar
|
||||||
|
// moved item on dg
|
||||||
|
// moved item on avatar
|
||||||
|
// remove item from dg
|
||||||
|
// remove item from avatar
|
||||||
|
|
||||||
|
var joint = wearableProps.parentJointIndex;
|
||||||
|
if (action === 'add') {
|
||||||
|
print('IN DOPPELGANGER ADD');
|
||||||
|
|
||||||
|
wearableProps.parentID = doppelganger.id;
|
||||||
|
wearableProps.parentJointIndex = joint;
|
||||||
|
|
||||||
|
//create a new one
|
||||||
|
wearableProps.script = MIRRORED_ENTITY_SCRIPT_URL;
|
||||||
|
wearableProps.name = 'Hifi-Doppelganger-Mirrored-Entity';
|
||||||
|
wearableProps.userData = JSON.stringify({
|
||||||
|
doppelgangerKey: {
|
||||||
|
baseEntity: baseEntity,
|
||||||
|
doppelganger: doppelganger.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
var mirrorEntity = Entities.addEntity(wearableProps);
|
||||||
|
|
||||||
|
var mirrorEntityProps = Entities.getEntityProperties(mirrorEntity)
|
||||||
|
print('MIRROR PROPS::' + JSON.stringify(mirrorEntityProps))
|
||||||
|
var wearablePair = {
|
||||||
|
baseEntity: baseEntity,
|
||||||
|
mirrorEntity: mirrorEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
wearablePairs.push(wearablePair);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === 'update') {
|
||||||
|
wearableProps.parentID = doppelganger.id;
|
||||||
|
|
||||||
|
var mirrorEntity = getMirrorEntityForBaseEntity(baseEntity);
|
||||||
|
// print('MIRROR ENTITY, newPosition' + mirrorEntity + ":::" + JSON.stringify(newPosition))
|
||||||
|
Entities.editEntity(mirrorEntity, wearableProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === 'remove') {
|
||||||
|
Entities.deleteEntity(getMirrorEntityForBaseEntity(baseEntity))
|
||||||
|
wearablePairs = wearablePairs.filter(function(obj) {
|
||||||
|
return obj.baseEntity !== baseEntity;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === 'updateBase') {
|
||||||
|
//this gets called when the mirrored entity gets grabbed. now we move the
|
||||||
|
var mirrorEntityProperties = Entities.getEntityProperties(message.mirrorEntity)
|
||||||
|
var doppelgangerToMirrorEntity = Vec3.subtract(doppelgangerProps.position, mirrorEntityProperties.position);
|
||||||
|
var newPosition = Vec3.sum(MyAvatar.position, doppelgangerToMirrorEntity);
|
||||||
|
|
||||||
|
delete mirrorEntityProperties.id;
|
||||||
|
delete mirrorEntityProperties.created;
|
||||||
|
delete mirrorEntityProperties.age;
|
||||||
|
delete mirrorEntityProperties.ageAsText;
|
||||||
|
mirrorEntityProperties.position = newPosition;
|
||||||
|
mirrorEntityProperties.parentID = MyAvatar.sessionUUID;
|
||||||
|
Entities.editEntity(message.baseEntity, mirrorEntityProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMirrorEntityForBaseEntity(baseEntity) {
|
||||||
|
var result = wearablePairs.filter(function(obj) {
|
||||||
|
return obj.baseEntity === baseEntity;
|
||||||
|
});
|
||||||
|
if (result.length === 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return result[0].mirrorEntity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBaseEntityForMirrorEntity(mirrorEntity) {
|
||||||
|
var result = wearablePairs.filter(function(obj) {
|
||||||
|
return obj.mirrorEntity === mirrorEntity;
|
||||||
|
});
|
||||||
|
if (result.length === 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return result[0].baseEntity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
makeDoppelgangerForMyAvatar();
|
makeDoppelgangerForMyAvatar();
|
||||||
|
subscribeToWearableMessages();
|
||||||
|
subscribeToWearableMessagesForAvatar();
|
||||||
|
subscribeToFreezeMessages();
|
||||||
|
|
||||||
function cleanup() {
|
function cleanup() {
|
||||||
//disconnectDoppelgangerUpdates();
|
if (isConnected === true) {
|
||||||
|
disconnectDoppelgangerUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
doppelgangers.forEach(function(doppelganger) {
|
doppelgangers.forEach(function(doppelganger) {
|
||||||
|
print('DOPPELGANGER' + doppelganger.id)
|
||||||
Entities.deleteEntity(doppelganger.id);
|
Entities.deleteEntity(doppelganger.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Script.scriptEnding.connect(cleanup);
|
||||||
Script.scriptEnding.connect(cleanup);
|
|
76
examples/dressing_room/freezeToggler.js
Normal file
76
examples/dressing_room/freezeToggler.js
Normal file
|
@ -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();
|
||||||
|
})
|
34
examples/dressing_room/loadingAreaEntity.js
Normal file
34
examples/dressing_room/loadingAreaEntity.js
Normal file
|
@ -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 + ")");
|
||||||
|
};
|
||||||
|
|
||||||
|
})
|
48
examples/dressing_room/mirroredEntity.js
Normal file
48
examples/dressing_room/mirroredEntity.js
Normal file
|
@ -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();
|
||||||
|
})
|
11
examples/dressing_room/setupDressingRoom.js
Normal file
11
examples/dressing_room/setupDressingRoom.js
Normal file
|
@ -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
|
138
examples/dressing_room/wearablesManager.js
Normal file
138
examples/dressing_room/wearablesManager.js
Normal file
|
@ -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();
|
Loading…
Reference in a new issue