788 lines
No EOL
29 KiB
JavaScript
788 lines
No EOL
29 KiB
JavaScript
(function () {
|
|
|
|
var _this;
|
|
|
|
var DEBUG = true;
|
|
|
|
Script.include("/~/system/libraries/utils.js");
|
|
|
|
//#region UTILITIES
|
|
|
|
function getEntityPropertiesForImageInFrontOfCamera(positionInFront, dimensions, url) {
|
|
return {
|
|
type: "Image",
|
|
grab: { grabbable: false },
|
|
dynamic: false,
|
|
parentJointIndex: MyAvatar.getJointIndex("_CAMERA_MATRIX"),
|
|
imageURL: url,
|
|
position: Vec3.sum(Camera.position, Vec3.multiplyQbyV(Camera.orientation, positionInFront)),
|
|
dimensions: dimensions,
|
|
rotation: Camera.rotation,
|
|
parentID: MyAvatar.sessionUUID,
|
|
ignoreRayIntersection: false,
|
|
visible: true,
|
|
emissive: true,
|
|
renderLayer: "front"
|
|
}
|
|
}
|
|
|
|
// String utility
|
|
function startsWith(str, searchString, position) {
|
|
position = position || 0;
|
|
return str.substr(position, searchString.length) === searchString;
|
|
}
|
|
|
|
function rolesToOverride() {
|
|
// Get all animation roles that sit will override
|
|
return MyAvatar.getAnimationRoles().filter(function (role) {
|
|
return !(startsWith(role, "right") || startsWith(role, "left"));
|
|
});
|
|
}
|
|
|
|
//#endregion UTILITIES
|
|
|
|
// #region CREATE MODE OVERLAY
|
|
|
|
// alpha value change during edit mode
|
|
var MINIMUM_ALPHA = 0.3; // 50% alpha value
|
|
var OVERLAY_ALPHA = 0.1; // 10% alpha value for that.createModeOverlay
|
|
var CREATE_OVERLAY_DIMENSIONS_OFFSET = 0.02; // add 0.02 m to the sides of the cube to avoid z fighting
|
|
|
|
var checkAlpha = false; // false if chair alpha is > MINIMUM_ALPHA, true if chair alpha is < MINIMUM_ALPH
|
|
|
|
// Creates an overlay if the user is in create mode
|
|
// Enabled only if the chair alpha value is <= MINIMUM_ALPHA
|
|
function checkOrCreateCreateModeOverlay() {
|
|
if (!_this.checkAlpha) {
|
|
return;
|
|
}
|
|
|
|
// check Alpha is enabled
|
|
if (_this.checkAlpha) {
|
|
|
|
// is in Edit mode && alpha value has not changed
|
|
if (isInEditMode() && !_this.createModeOverlay) {
|
|
|
|
var properties = Entities.getEntityProperties(_this.entityID);
|
|
|
|
var position = properties.position;
|
|
var registrationPoint = properties.registrationPoint;
|
|
var dimensions = properties.dimensions;
|
|
var rotation = properties.rotation;
|
|
|
|
// Local position relative to cube
|
|
// And adjust for registrationPoint to match the cube exactly
|
|
var localOffset = {
|
|
x: NEG_ONE * (registrationPoint.x - HALF) * dimensions.x,
|
|
y: NEG_ONE * (registrationPoint.y - HALF) * dimensions.y,
|
|
z: NEG_ONE * (registrationPoint.z - HALF) * dimensions.z
|
|
};
|
|
|
|
var worldOffset = Vec3.multiplyQbyV(rotation, localOffset);
|
|
var worldPosition = Vec3.sum(position, worldOffset);
|
|
|
|
// Create visible cube
|
|
_this.createModeOverlay = Overlays.addOverlay("cube", {
|
|
position: {
|
|
x: worldPosition.x,
|
|
y: worldPosition.y,
|
|
z: worldPosition.z
|
|
},
|
|
rotation: rotation,
|
|
dimensions: {
|
|
x: dimensions.x + CREATE_OVERLAY_DIMENSIONS_OFFSET,
|
|
y: dimensions.y - CREATE_OVERLAY_DIMENSIONS_OFFSET * HALF, // Able to select from top in HMD create mode
|
|
z: dimensions.z + CREATE_OVERLAY_DIMENSIONS_OFFSET
|
|
},
|
|
solid: true,
|
|
alpha: OVERLAY_ALPHA,
|
|
parentID: _this.entityID
|
|
});
|
|
|
|
}
|
|
|
|
// Is in Edit mode && alpha value has changed
|
|
if (!isInEditMode() && _this.createModeOverlay) {
|
|
deleteCreateModeOverlay();
|
|
}
|
|
}
|
|
}
|
|
|
|
function deleteCreateModeOverlay() {
|
|
if (_this.createModeOverlay) {
|
|
Overlays.deleteOverlay(_this.createModeOverlay);
|
|
_this.createModeOverlay = false;
|
|
}
|
|
}
|
|
|
|
// #endregion CREATE MODE OVERLAY
|
|
|
|
var STANDUP_DISTANCE_M = 0.5; // m
|
|
var CHAIR_DISMOUNT_OFFSET_M = -0.5; // m in front of chair
|
|
var STANDUP_DELAY_MS = 25; // ms for timeout in standup
|
|
function standUp() {
|
|
if (DEBUG) {
|
|
console.log("standup");
|
|
}
|
|
|
|
// get the entityID, previous position and orientation
|
|
var sitCurrentSettings = Settings.getValue(SETTING_KEY_AVATAR_SITTING);
|
|
var settingsEntityID = sitCurrentSettings[0];
|
|
var settingsPreviousPosition = sitCurrentSettings[1];
|
|
var settingsPreviousOrientation = sitCurrentSettings[2];
|
|
|
|
MyAvatar.clearPinOnJoint(MyAvatar.getJointIndex("Hips"));
|
|
|
|
// STANDING FROM THIS CHAIR
|
|
if (settingsEntityID === _this.entityID) {
|
|
// standing up from this chair
|
|
// Need to enable roles and drive keys and reposition avatar
|
|
|
|
// ENABLE DRIVE KEYS
|
|
for (var i in OVERRIDDEN_DRIVE_KEYS) {
|
|
MyAvatar.enableDriveKey(OVERRIDDEN_DRIVE_KEYS[i]);
|
|
}
|
|
|
|
// RESTORE ANIMATION ROLES
|
|
var roles = rolesToOverride();
|
|
for (var j in roles) {
|
|
MyAvatar.restoreRoleAnimation(roles[j]);
|
|
}
|
|
|
|
// If changed seat do not do this
|
|
MyAvatar.collisionsEnabled = true;
|
|
MyAvatar.hmdLeanRecenterEnabled = true;
|
|
Script.setTimeout(function () {
|
|
MyAvatar.centerBody();
|
|
}, STANDUP_DELAY_MS);
|
|
|
|
// SET AVATAR POSITION AND ORIENTATION TO PREVIOUS VALUES
|
|
var avatarDistance = Vec3.distance(MyAvatar.position, _this.seatCenterPosition);
|
|
var properties = Entities.getEntityProperties(_this.entityID);
|
|
if (avatarDistance < STANDUP_DISTANCE_M) { // might need to
|
|
// Avatar did not teleport far away from chair
|
|
// Therefore apply previous position OR the default dismount position and orientation
|
|
if (settingsPreviousPosition && settingsPreviousOrientation) {
|
|
// Apply previous position and orientation because they exist
|
|
MyAvatar.position = settingsPreviousPosition;
|
|
MyAvatar.orientation = settingsPreviousOrientation;
|
|
} else {
|
|
// Calculate chair default dismount position in front of chair
|
|
var offset = {
|
|
x: 0,
|
|
y: 1.0,
|
|
z: CHAIR_DISMOUNT_OFFSET_M - properties.dimensions.z * properties.registrationPoint.z
|
|
};
|
|
var position = Vec3.sum(properties.position, Vec3.multiplyQbyV(properties.rotation, offset));
|
|
MyAvatar.position = position;
|
|
}
|
|
}
|
|
|
|
Settings.setValue(SETTING_KEY_AVATAR_SITTING, "");
|
|
}
|
|
|
|
// if avatar changed chairs
|
|
// remove update interval from avatar
|
|
// redraw click to sit for all avatars
|
|
// disconnect signals
|
|
// reset values for this chair
|
|
// if avatar is standing up
|
|
// if avatar teleported, do not apply previous position and orientation
|
|
// reapply position and orientation and restore roles
|
|
|
|
if (!_this.locked) {
|
|
Entities.editEntity(_this.entityID, { locked: false });
|
|
}
|
|
|
|
_this.driveKeyPressedStart = false;
|
|
_this.sitDownSettlePeriod = false;
|
|
_this.isSittingDown = false;
|
|
deleteStandUp();
|
|
|
|
Entities.callEntityServerMethod(_this.entityID, "onStandUp");
|
|
|
|
stopUpdateInterval();
|
|
|
|
if (_this.connectedSignals) {
|
|
MyAvatar.scaleChanged.disconnect(_this.standUp);
|
|
MyAvatar.onLoadComplete.disconnect(_this.standUp);
|
|
location.hostChanged.disconnect(_this.standUp);
|
|
Script.scriptEnding.disconnect(_this.standUp);
|
|
MyAvatar.skeletonModelURLChanged.disconnect(_this.standUp);
|
|
MyAvatar.wentAway.disconnect(_this.standUp);
|
|
_this.connectedSignals = false;
|
|
}
|
|
|
|
console.log("calling entity server method addAllOtherSittableOverlays");
|
|
|
|
Script.setTimeout(function () {
|
|
// wait til avatar is out of range of the chair
|
|
Entities.callEntityServerMethod(
|
|
_this.entityID,
|
|
"addAllOtherSittableOverlays",
|
|
AvatarList.getAvatarsInRange(_this.seatCenterPosition, CAN_SIT_M)
|
|
);
|
|
}, 500 + STANDUP_DELAY_MS);
|
|
// Entities.callEntityMethod(_this.zoneID, "checkIfAvatarIsInsideZone");
|
|
}
|
|
|
|
// Checks for Avatar Spine Error
|
|
var NEG_ONE = -1;
|
|
function setHeadToHipsDistance() {
|
|
// get hips world pos while sitting
|
|
if (MyAvatar.getJointIndex("Head") === NEG_ONE) {
|
|
// this can probably be adjusted to be more accurate
|
|
_this.headToHipsDistance = MyAvatar.getHeight() * HALF;
|
|
} else {
|
|
var headTranslation = MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(MyAvatar.getJointIndex("Head"));
|
|
var hipsTranslation = MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(MyAvatar.getJointIndex("Hips"));
|
|
_this.headToHipsDistance = Math.abs(headTranslation.y - hipsTranslation.y);
|
|
}
|
|
}
|
|
|
|
// Calculates where the avatar's hips will seat
|
|
// Used to calculate pin hip position. Adds CHAIR_OFFSET_RATIO * chair's y dimension to the y center of the seat.
|
|
// Avatar sit position y = CHAIR_OFFSET_RATIO * height of chair
|
|
var CHAIR_OFFSET_RATIO = 0.1;
|
|
function calculateSeatCenterPositionForPinningAvatarHips() {
|
|
var properties = Entities.getEntityProperties(_this.entityID);
|
|
|
|
var yOffset = properties.dimensions.y * CHAIR_OFFSET_RATIO;
|
|
_this.seatCenterPosition = properties.position;
|
|
_this.seatCenterPosition.y = properties.position.y + yOffset;
|
|
}
|
|
|
|
function sendRequestToServerScriptToStartSitDown() {
|
|
Entities.callEntityServerMethod(
|
|
_this.entityID,
|
|
"onSitDown",
|
|
[MyAvatar.sessionUUID]
|
|
);
|
|
}
|
|
|
|
// Called from entity server script to begin sitting down sequence
|
|
var SETTING_KEY_AVATAR_SITTING = "com.highfidelity.avatar.isSitting";
|
|
var SIT_SETTLE_TIME_MS = 350; // Do not pop avatar out of chair immediates if there's an issue
|
|
var STANDUP_DELAY_MS = 25; // ms for timeout in standup
|
|
var SIT_DELAY_MS = 50; // ms for timeouts in sit
|
|
var CAN_SIT_M = 5; // zone radius
|
|
function startSitDown() {
|
|
if (DEBUG) {
|
|
console.log("startSitDown");
|
|
}
|
|
|
|
Entities.callEntityServerMethod(
|
|
_this.entityID,
|
|
"removeAllOtherSittableOverlays",
|
|
AvatarList.getAvatarsInRange(_this.seatCenterPosition, CAN_SIT_M)
|
|
);
|
|
|
|
deleteSittableUI();
|
|
|
|
// Set isSitting value in Settings
|
|
var sitPreviousSettings = Settings.getValue(SETTING_KEY_AVATAR_SITTING);
|
|
var sitNewSettings = [_this.entityID, MyAvatar.position, MyAvatar.orientation]; // ***
|
|
if (sitPreviousSettings && sitPreviousSettings.length === 3) {
|
|
// changed chair, only change entityID, keep old values for previousAvatarPosition and previousAvatarOrientation
|
|
sitNewSettings[1] = sitPreviousSettings[1];
|
|
sitNewSettings[2] = sitPreviousSettings[2];
|
|
}
|
|
Settings.setValue(SETTING_KEY_AVATAR_SITTING, sitNewSettings);
|
|
|
|
if (HMD.active) {
|
|
createPresit();
|
|
Script.setTimeout(function () {
|
|
beforeSitDown();
|
|
}, PRESIT_FRAME_DURATION * _this.preSitLoadedImages.length);
|
|
} else {
|
|
beforeSitDown();
|
|
}
|
|
}
|
|
|
|
function beforeSitDown() {
|
|
// Set the value of head to hips distance
|
|
// If avatar deviates outside of the minimum and maximum, the avatar will pop out of the chair
|
|
setHeadToHipsDistance();
|
|
|
|
// Lock chair and save old setting
|
|
_this.locked = Entities.getEntityProperties(_this.entityID, 'locked').locked;
|
|
Entities.editEntity(_this.entityID, { locked: true });
|
|
|
|
calculateSeatCenterPositionForPinningAvatarHips();
|
|
|
|
sitDownAndPinAvatar();
|
|
}
|
|
|
|
var ANIMATION_URL = Script.resolvePath("./resources/animations/sittingIdle.fbx");
|
|
var ANIMATION_FPS = 30;
|
|
var ANIMATION_FIRST_FRAME = 1;
|
|
var ANIMATION_LAST_FRAME = 350;
|
|
var UPDATE_INTERVAL_MS = 50;
|
|
var OVERRIDDEN_DRIVE_KEYS = [
|
|
DriveKeys.TRANSLATE_X,
|
|
DriveKeys.TRANSLATE_Y,
|
|
DriveKeys.TRANSLATE_Z,
|
|
DriveKeys.STEP_TRANSLATE_X,
|
|
DriveKeys.STEP_TRANSLATE_Y,
|
|
DriveKeys.STEP_TRANSLATE_Z
|
|
];
|
|
function sitDownAndPinAvatar() {
|
|
MyAvatar.collisionsEnabled = false;
|
|
MyAvatar.hmdLeanRecenterEnabled = false;
|
|
|
|
if(HMD.active) {
|
|
deletePresit();
|
|
}
|
|
|
|
var roles = rolesToOverride();
|
|
for (var i in roles) { // restore roles to prevent overlap
|
|
MyAvatar.restoreRoleAnimation(roles[i]);
|
|
MyAvatar.overrideRoleAnimation(roles[i], ANIMATION_URL, ANIMATION_FPS, true,
|
|
ANIMATION_FIRST_FRAME, ANIMATION_LAST_FRAME);
|
|
}
|
|
for (var j in OVERRIDDEN_DRIVE_KEYS) {
|
|
MyAvatar.disableDriveKey(OVERRIDDEN_DRIVE_KEYS[j]);
|
|
}
|
|
|
|
MyAvatar.centerBody();
|
|
var hipIndex = MyAvatar.getJointIndex("Hips");
|
|
|
|
var properties = Entities.getEntityProperties(_this.entityID);
|
|
|
|
Script.setTimeout(function () {
|
|
|
|
// if (HMD.active && SHOW_PRESIT_IMAGE_IN_HMD) {
|
|
// preSitRemove();
|
|
// }
|
|
|
|
_this.isSittingDown = true;
|
|
_this.sitDownSettlePeriod = Date.now() + SIT_SETTLE_TIME_MS;
|
|
|
|
MyAvatar.pinJoint(hipIndex, _this.seatCenterPosition, properties.rotation);
|
|
|
|
stopUpdateInterval();
|
|
|
|
_this.whileSittingUpdateIntervalID = Script.setInterval(_this.whileSittingUpdate, UPDATE_INTERVAL_MS);
|
|
|
|
deleteSittableUI();
|
|
|
|
if(!_this.connectedSignals) {
|
|
MyAvatar.scaleChanged.connect(_this.standUp);
|
|
MyAvatar.onLoadComplete.connect(_this.standUp);
|
|
location.hostChanged.connect(_this.standUp);
|
|
Script.scriptEnding.connect(_this.standUp);
|
|
MyAvatar.skeletonModelURLChanged.connect(_this.standUp);
|
|
MyAvatar.wentAway.connect(_this.standUp);
|
|
_this.connectedSignals = true;
|
|
}
|
|
}, SIT_DELAY_MS);
|
|
}
|
|
|
|
function stopUpdateInterval() {
|
|
if (_this.whileSittingUpdateIntervalID) {
|
|
Script.clearInterval(_this.whileSittingUpdateIntervalID);
|
|
_this.whileSittingUpdateIntervalID = false;
|
|
}
|
|
}
|
|
|
|
var AVATAR_MOVED_TOO_FAR_DISTANCE_M = 0.5;
|
|
var MAX_HEAD_DEVIATION_RATIO = 1.2;
|
|
var MIN_HEAD_DEVIATION_RATIO = 0.8;
|
|
var DRIVE_KEY_RELEASE_TIME_MS = 800; // ms
|
|
var HEAD_DEVIATION_MAX_TIME_TO_STAND_MS = 1000;
|
|
function whileSittingUpdate() {
|
|
var now = Date.now();
|
|
|
|
// ACTIVE DRIVE KEY
|
|
// Check if a drive key is pressed
|
|
var hasActiveDriveKey = false;
|
|
for (var i in OVERRIDDEN_DRIVE_KEYS) {
|
|
if (MyAvatar.getRawDriveKey(OVERRIDDEN_DRIVE_KEYS[i]) !== 0.0) {
|
|
hasActiveDriveKey = true;
|
|
if (!_this.driveKeyPressedStart) {
|
|
_this.driveKeyPressedStart = now;
|
|
}
|
|
if (!_this.standUpID) {
|
|
createStandUp();
|
|
if (DEBUG) {
|
|
console.log("active drive key pressed once");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Only standup if user has been pushing a drive key for DRIVE_KEY_RELEASE_TIME_MS
|
|
if (hasActiveDriveKey) {
|
|
if (_this.driveKeyPressedStart && (now - _this.driveKeyPressedStart) > DRIVE_KEY_RELEASE_TIME_MS) {
|
|
_this.standUp();
|
|
if (DEBUG) {
|
|
console.log("active drive key pressed caused standup");
|
|
}
|
|
|
|
}
|
|
} else {
|
|
if (_this.standUpID) {
|
|
_this.driveKeyPressedStart = false;
|
|
deleteStandUp();
|
|
}
|
|
}
|
|
|
|
if (_this.sitDownSettlePeriod && now < _this.sitDownSettlePeriod) {
|
|
// below considerations only apply if sit down settle period has passed
|
|
return;
|
|
} else {
|
|
_this.sitDownSettlePeriod = false;
|
|
}
|
|
|
|
// AVATAR DISTANCE
|
|
if (Vec3.distance(MyAvatar.position, _this.seatCenterPosition) > AVATAR_MOVED_TOO_FAR_DISTANCE_M) {
|
|
_this.standUp();
|
|
if (DEBUG) {
|
|
console.log("avatar distance caused standup");
|
|
}
|
|
return;
|
|
}
|
|
|
|
// AVATAR SPINE ERROR
|
|
if (HMD.active) {
|
|
// If head is more than certain distance from hips or less than certain distance, stand up
|
|
var headPoseValue = Controller.getPoseValue(Controller.Standard.Head);
|
|
var headWorldPosition = Vec3.sum(
|
|
Vec3.multiplyQbyV(MyAvatar.orientation, headPoseValue.translation),
|
|
MyAvatar.position
|
|
);
|
|
var headDeviation = Vec3.distance(headWorldPosition, _this.seatCenterPosition);
|
|
var headDeviationRatio = headDeviation / _this.headToHipsDistance;
|
|
|
|
if (headDeviationRatio < MIN_HEAD_DEVIATION_RATIO || headDeviationRatio > MAX_HEAD_DEVIATION_RATIO) {
|
|
if (!_this.deviationTimeStart) {
|
|
_this.deviationTimeStart = now;
|
|
}
|
|
if (now - _this.deviationTimeStart > HEAD_DEVIATION_MAX_TIME_TO_STAND_MS) {
|
|
_this.deviationTimeStart = false;
|
|
_this.standUp();
|
|
if (DEBUG) {
|
|
console.log("avatar spine error caused standup");
|
|
}
|
|
}
|
|
} else {
|
|
_this.deviationTimeStart = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
var AVATAR_SITTING_IN_CHAIR_RANGE = 0.01;
|
|
function onEnterCanSitZone(id, params) {
|
|
if (params && params.length > 0) {
|
|
_this.zoneID = params[0];
|
|
}
|
|
|
|
if (!isInEditMode()) {
|
|
calculateSeatCenterPositionForPinningAvatarHips();
|
|
var isSittingInChair = AvatarList.isAvatarInRange(_this.seatCenterPosition, AVATAR_SITTING_IN_CHAIR_RANGE);
|
|
console.log("onEnterCanSitZone" + !_this.sittableUIID + !isSittingInChair);
|
|
if (!_this.sittableUIID && !isSittingInChair) {
|
|
createSittableUI();
|
|
}
|
|
} else {
|
|
// is editting chair do not create sittable
|
|
checkOrCreateCreateModeOverlay();
|
|
}
|
|
}
|
|
|
|
function onLeaveCanSitZone() {
|
|
deleteSittableUI();
|
|
}
|
|
|
|
//#region PRESIT - LOCAL ENTITY shown in HMD before sitting and after clicking sittable overlay
|
|
// Has the sitting animation and "Please Face Forward"
|
|
|
|
var PRESIT_FRAME_DURATION = 160; // ms time duration for HMD presit overlay
|
|
var PRESIT_URL_ROOT = Script.resolvePath("./resources/images/presit/sitConfirm");
|
|
var PRESIT_URL_POSTFIX = ".png";
|
|
var PRESIT_URL_NUM = 12;
|
|
var PRESIT_URL_TEXT = Script.resolvePath("./resources/images/presit/pleaseFaceForward.png");
|
|
// Prefetch all presit overlay images into user's client
|
|
function prefetchPresitImages() {
|
|
var str;
|
|
for (var i = 0; i < PRESIT_URL_NUM; i++) {
|
|
str = i + 1;
|
|
_this.preSitLoadedImages[i] = TextureCache.prefetch(Script.resolvePath(PRESIT_URL_ROOT + str + PRESIT_URL_POSTFIX));
|
|
console.log()
|
|
}
|
|
_this.preSitTextLoadedImage = TextureCache.prefetch(PRESIT_URL_TEXT);
|
|
}
|
|
|
|
var SIT_ANIMATION_POSITION_IN_FRONT = { x: 0, y: 0.1, z: -1 };
|
|
var SIT_ANIMATION_DIMENSIONS = { x: 0.2, y: 0.2 };
|
|
var PLEASE_FACE_FORWARD_POSITION_IN_FRONT = { x: 0, y: -0.05, z: -1 };
|
|
var PLEASE_FACE_FORWARD_DIMENSIONS = { x: 0.425, y: 0.425 };
|
|
function createPresit() {
|
|
var currentPresitAnimationFrame = 0;
|
|
_this.presitAnimationImageID = Entities.addEntity(
|
|
getEntityPropertiesForImageInFrontOfCamera(
|
|
SIT_ANIMATION_POSITION_IN_FRONT,
|
|
SIT_ANIMATION_DIMENSIONS,
|
|
_this.preSitLoadedImages[currentPresitAnimationFrame].url
|
|
),
|
|
"local"
|
|
);
|
|
|
|
_this.presitTextID = Entities.addEntity(
|
|
getEntityPropertiesForImageInFrontOfCamera(
|
|
PLEASE_FACE_FORWARD_POSITION_IN_FRONT,
|
|
PLEASE_FACE_FORWARD_DIMENSIONS,
|
|
_this.preSitTextLoadedImage.url
|
|
),
|
|
"local"
|
|
);
|
|
|
|
// Flash through the presit animation images via overlay for a smooth avatar sitting animation
|
|
_this.presitIntervalID = Script.setInterval(function () {
|
|
if (_this.presitAnimationImageID) {
|
|
currentPresitAnimationFrame = currentPresitAnimationFrame + 1;
|
|
if (currentPresitAnimationFrame >= _this.preSitLoadedImages.length - 1) {
|
|
deletePresit();
|
|
}
|
|
Entities.editEntity(_this.presitAnimationImageID, { imageURL: _this.preSitLoadedImages[currentPresitAnimationFrame].url });
|
|
}
|
|
}, PRESIT_FRAME_DURATION);
|
|
}
|
|
|
|
function deletePresit() {
|
|
if (_this.presitAnimationImageID) {
|
|
Entities.deleteEntity(_this.presitAnimationImageID);
|
|
_this.presitAnimationImageID = false;
|
|
}
|
|
if (_this.presitTextID) {
|
|
Entities.deleteEntity(_this.presitTextID);
|
|
_this.presitTextID = false;
|
|
}
|
|
if (_this.presitIntervalID) {
|
|
Script.clearInterval(_this.presitIntervalID);
|
|
_this.presitIntervalID = false;
|
|
}
|
|
}
|
|
|
|
//#endregion PRESIT
|
|
|
|
//#region HOLD TO STANDUP
|
|
|
|
var OVERLAY_URL_STANDUP = Script.resolvePath("./resources/images/holdToStandUp.png");
|
|
var STAND_UP_POSITION_IN_FRONT = { x: 0, y: 0, z: -1 };
|
|
var STAND_UP_DIMENSIONS = { x: 0.2, y: 0.2 };
|
|
function createStandUp() {
|
|
if (DEBUG) {
|
|
console.log("creating standup")
|
|
}
|
|
|
|
_this.standUpID = Entities.addEntity(
|
|
getEntityPropertiesForImageInFrontOfCamera(
|
|
STAND_UP_POSITION_IN_FRONT,
|
|
STAND_UP_DIMENSIONS,
|
|
OVERLAY_URL_STANDUP
|
|
),
|
|
"local"
|
|
);
|
|
}
|
|
|
|
function deleteStandUp() {
|
|
if (_this.standUpID) {
|
|
Entities.deleteEntity(_this.standUpID);
|
|
_this.standUpID = false;
|
|
}
|
|
}
|
|
|
|
//#endregion HOLD TO STANDUP
|
|
|
|
//#region SITTABLE
|
|
|
|
var UI_DEBUG = true;
|
|
|
|
var SITTABLE_START_ALPHA = 0.7;
|
|
var SITTABLE_DIMENSIONS = { x: 0.3, y: 0.3 };
|
|
var SITTABLE_IMAGE_URL_HMD = Script.resolvePath("./resources/images/triggerToSit.png");
|
|
var SITTABLE_IMAGE_URL_DESKTOP = Script.resolvePath("./resources/images/clickToSit.png");
|
|
var SITTABLE_Y_OFFSET = 0.01;
|
|
function createSittableUI() {
|
|
if (_this.sittableID) {
|
|
// already created
|
|
return;
|
|
}
|
|
|
|
if (UI_DEBUG) {
|
|
console.log("createSittableUI()");
|
|
}
|
|
var properties = Entities.getEntityProperties(_this.entityID);
|
|
|
|
// calculate
|
|
var localOffset = { x: 0, y: SITTABLE_Y_OFFSET, z: 0 };
|
|
var worldOffset = Vec3.multiplyQbyV(properties.rotation, localOffset);
|
|
var sittablePosition = Vec3.sum(properties.position, worldOffset);
|
|
|
|
_this.sittableID = Entities.addEntity({
|
|
type: "Image",
|
|
grab: {
|
|
grabbable: false,
|
|
},
|
|
dynamic: false,
|
|
position: sittablePosition,
|
|
rotation: Quat.multiply(
|
|
properties.rotation,
|
|
Quat.fromVec3Degrees({ x: -90, y: 180, z: 0 })
|
|
),
|
|
parentID: _this.entityID,
|
|
dimensions: SITTABLE_DIMENSIONS,
|
|
imageURL: HMD.active ? SITTABLE_IMAGE_URL_HMD : SITTABLE_IMAGE_URL_DESKTOP,
|
|
ignoreRayIntersection: false,
|
|
alpha: SITTABLE_START_ALPHA,
|
|
script: Script.resolvePath("./resources/sittableUIClient.js") + "?" + Math.random(),
|
|
visible: true,
|
|
emissive: true
|
|
},
|
|
"local"
|
|
);
|
|
}
|
|
|
|
|
|
// Remove sittable local entity if it exists
|
|
function deleteSittableUI() {
|
|
if (UI_DEBUG) {
|
|
print("deleteSittableUI");
|
|
}
|
|
|
|
if (_this.sittableID) {
|
|
Entities.deleteEntity(_this.sittableID);
|
|
_this.sittableID = false;
|
|
}
|
|
}
|
|
|
|
//#endregion SITTABLE
|
|
|
|
function updateUserData() {
|
|
var properties = Entities.getEntityProperties(_this.entityID);
|
|
try {
|
|
_this.userData = JSON.parse(properties.userData);
|
|
} catch (e) {
|
|
console.log("Issue parsing userData" + e);
|
|
}
|
|
|
|
if (!_this.userData || _this.userData.canClickOnModelToSit === undefined) {
|
|
Entities.editEntity(_this.entityID, { userData: JSON.stringify(DEFAULT_SIT_USER_DATA_WITH_CUSTOM_SETTINGS) });
|
|
}
|
|
}
|
|
|
|
var DEFAULT_SIT_USER_DATA_WITH_CUSTOM_SETTINGS = {
|
|
canClickOnModelToSit: false
|
|
};
|
|
function preload(id) {
|
|
_this.entityID = id;
|
|
prefetchPresitImages();
|
|
// download sit animation
|
|
AnimationCache.prefetch(ANIMATION_URL);
|
|
updateUserData();
|
|
|
|
var properties = Entities.getEntityProperties(_this.entityID);
|
|
_this.checkAlpha = properties.alpha <= MINIMUM_ALPHA;
|
|
}
|
|
|
|
function unload() {
|
|
deleteSittableUI();
|
|
deletePresit();
|
|
deleteStandUp();
|
|
deleteCreateModeOverlay();
|
|
standUp();
|
|
|
|
if (_this.connectedSignals) {
|
|
MyAvatar.scaleChanged.disconnect(_this.standUp);
|
|
MyAvatar.onLoadComplete.disconnect(_this.standUp);
|
|
location.hostChanged.disconnect(_this.standUp);
|
|
Script.scriptEnding.disconnect(_this.standUp);
|
|
MyAvatar.skeletonModelURLChanged.disconnect(_this.standUp);
|
|
MyAvatar.wentAway.disconnect(_this.standUp);
|
|
_this.connectedSignals = false;
|
|
}
|
|
}
|
|
|
|
// Called by server script in heartbeat
|
|
function check() {
|
|
Entities.callEntityServerMethod(_this.entityID, "checkResolved");
|
|
}
|
|
|
|
function mouseReleaseOnEntity(id, event) {
|
|
console.log("mouseReleaseOnEntity");
|
|
if (event.isPrimaryButton) {
|
|
console.log("isPrimaryButton");
|
|
updateUserData();
|
|
if (_this.userData && _this.userData.canClickOnModelToSit) {
|
|
console.log("sitTime");
|
|
Entities.callEntityServerMethod(_this.entityID, "onSitDown", [MyAvatar.sessionUUID]);
|
|
}
|
|
}
|
|
}
|
|
|
|
function SitClient() {
|
|
_this = this;
|
|
this.sittableUIID = false;
|
|
this.canSitZoneID = false;
|
|
|
|
// for presit local entity and animation
|
|
this.presitIntervalID = false;
|
|
this.presitTextID = false;
|
|
this.presitAnimationImageID = false;
|
|
|
|
// for prefetch on presit
|
|
this.preSitLoadedImages = [];
|
|
this.preSitTextLoadedImage = null;
|
|
|
|
this.locked = null;
|
|
|
|
this.seatCenterPosition = null;
|
|
this.headToHipsDistance = null;
|
|
this.isSittingDown = false;
|
|
this.sitDownSettlePeriod = false;
|
|
|
|
this.whileSittingUpdateIntervalID = false;
|
|
|
|
this.standUpID = false;
|
|
|
|
this.driveKeyPressedStart = false;
|
|
|
|
this.deviationTimeStart = false;
|
|
this.zoneID = false;
|
|
|
|
this.connectedSignals = false;
|
|
|
|
// CUSTOMIZATIONS
|
|
this.userData = {};
|
|
this.canClickOnModelToSit = false;
|
|
|
|
this.checkAlpha = false;
|
|
_this.createModeOverlay = false;
|
|
}
|
|
|
|
SitClient.prototype = {
|
|
remotelyCallable: [
|
|
"onEnterCanSitZone",
|
|
"onLeaveCanSitZone",
|
|
"startSitDown",
|
|
"sendRequestToServerScriptToStartSitDown",
|
|
"check"
|
|
],
|
|
// Zone methods
|
|
onEnterCanSitZone: onEnterCanSitZone,
|
|
onLeaveCanSitZone: onLeaveCanSitZone,
|
|
// Entity liftime methods
|
|
preload: preload,
|
|
unload: unload,
|
|
// Sitting lifetime methods
|
|
sendRequestToServerScriptToStartSitDown: sendRequestToServerScriptToStartSitDown,
|
|
mouseReleaseOnEntity: mouseReleaseOnEntity,
|
|
startSitDown: startSitDown,
|
|
whileSittingUpdate: whileSittingUpdate,
|
|
check: check,
|
|
standUp: standUp
|
|
}
|
|
return new SitClient();
|
|
}); |