overte/scripts/system/attachedEntitiesManager.js
pull[bot] 2f359cc15f
[pull] master from overte-org:master (#93)
* mirrors wip

* fix view + projection, texture flipping, billboarding

* wip portals

* wip

* fix cpu frustum culling (hacky?)

* fix mirrors in deferred

* mirrors on models + text

* portals use exit as ignoreItem

* cleanup

* entity tags

* wild guess to handle view correction, hide portalExitID in create when mirrorMode != portal

* let's try this??

* plz

* promising

* fix paramsOffset and view flipping

* portals shouldn't flip

* break when tag found

* fix portal view calculation

* Revert "Mirrors + Portals"

* Revert "Revert "Mirrors + Portals""

* web entity wantsKeyboardFocus property

* fix typo

* move audio zones to zone entity properties

* fix audio zones in create

* set dynamic factory

* new procecural particle entity type

* fix particle intersection

* shorten create labels

* fix 0 update props case

* Ability to smooth model animations

* sound entities

* fix layered simulate items

* fix stereo sound speed

* support non-localOnly sound avatar entities

* add sound url prompt

* support registration point, improve locking

* remove keyboardRasied

* locking attempt #2

* fix keyboardRasied typo

* add default particle props

* add unlit property for shapes

* Merge branch master into protocol_changes

* add ambient light color

* fix create issue

* fix create issue

* add tonemapping props to zones, wip ambient occlusion

* wip ambient occlusion

* it's working!

* remove attachments

* fix non-localOnly positional sounds not updating

* change AO default to HBAO, remove from create

* more graphics options

* fix AO setting + effects in mirrors

* fix AA in mirrors

* alezia's fixes

* fix haze in mirrors

* add comment for SKYBOX_DISTANCE

* new line

* model loading priority updates over time, takes into account out of bounds, avatar entities have higher priority, and fsts can specify to wait for wearables to load before rendering

* add loadPriority to model entities, working on other avatars waitForWearables

* fix build error

* try to fix isServer assert

* fix stats + waitForWearables

* Listen for click instead of release.

* Reverted initial commit. Implemented hack to listen for menu click events.

* Missed some reverts.

* Missed another one.

* Prevent duplicate actions.

* Added extra needed checks.

* Fix without formatting? (#91)

* Hopefully fixed formatting.

* Things can't be too easy.

* Remove google poly

* automated entity property serialization

* cleanup + automate EntityPropertyFlags

* text vertical alignment, use uint8_t for entity property enums, fix text recalculating too often

* fix text size

* Update interface/resources/controllers/keyboardMouse.json

Co-authored-by: HifiExperiments <53453710+HifiExperiments@users.noreply.github.com>

* fix component mode serialization

* Fixed mouse look in selfie mode.

* fix text debug assert on invalid or unloaded font

* missed some enums

* fix ADD_GROUP_PROPERTY_TO_MAP

* fix PROP_GRAB_EQUIPPABLE_INDICATOR_URL missing urlPermission

* fix KeyLightPropertyGroup legacy properties

* fix PolyLineEntityItem::getEntityProperties

* comment cmake script

* fix copyright

* Replaced key value with key text.
Added additional comment about the specific delete key used.

* weekly promoted place

Highlight the first place in the list as the weekly promoted place

* Fixed lingering references to `avatarIcon`.

Signed-off-by: armored-dragon <publicmail@armoreddragon.com>

* Adding icon for "Grab And Equip" section

Adding icon for "Grab And Equip" section

* Add "Grab And Equip" section

Add "Grab And Equip" section for the grabbale and Equipable groups of properties.

* Add files via upload

* Add tooltips for the "Grab and Equip" properties

Add the tooltips for the "Grab and Equip" properties

* Text adjustments for grab.equippable

Text adjustments for grab.equippable

* Make Maturity Filter persisted

Make Maturity Filter persisted and with a default value (Teen & Everyone)

* Adjust the default value for maturity

Adjust the default value for maturity

* move "triggerable" under GRAB & EQUIP

move "triggerable" under GRAB & EQUIP

* Remove hifi-screenshare
Cherry picked and updated from Tivoli dd5b6ea6ee5597a06603e16509640e7ed18106bb

Co-authored-by: Julian Groß <julian.g@posteo.de>

* Insert placeholder to not break protocol yet.

* Fix incorrectly resolved merge conflict, left too much code.

* Fixes based on review comments on previous PR

* Remove code accidentally re-added during a conflict fix

* bump protocol

* rebuild fonts with full charset (NOT -allglyphs)

* Attempt at fixing Windows master branch builds

* Change minimum angular velocity to a lower one

* Fix Uuid.NULL behavior

---------

Signed-off-by: armored-dragon <publicmail@armoreddragon.com>
Co-authored-by: HifiExperiments <thingsandstuffblog@gmail.com>
Co-authored-by: ksuprynowicz <ksuprynowicz@post.pl>
Co-authored-by: Dale Glass <51060919+daleglass@users.noreply.github.com>
Co-authored-by: HifiExperiments <53453710+HifiExperiments@users.noreply.github.com>
Co-authored-by: Julian Groß <julian.g@posteo.de>
Co-authored-by: armored-dragon <publicmail@armoreddragon.com>
Co-authored-by: Armored-Dragon <github56254@armoreddragon.com>
Co-authored-by: Alezia Kurdis <60075796+AleziaKurdis@users.noreply.github.com>
Co-authored-by: Maki <mxmcube@gmail.com>
Co-authored-by: Dale Glass <dale@daleglass.net>
2024-10-24 06:32:53 +00:00

285 lines
No EOL
11 KiB
JavaScript

//
// attachedEntitiesManager.js
//
// Created by Seth Alves on 2016-1-20
// 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
//
Script.include("libraries/utils.js");
var DEFAULT_WEARABLE_DATA = {
joints: {}
};
var MINIMUM_DROP_DISTANCE_FROM_JOINT = 0.8;
var ATTACHED_ENTITY_SEARCH_DISTANCE = 10.0;
var ATTACHED_ENTITIES_SETTINGS_KEY = "ATTACHED_ENTITIES";
var DRESSING_ROOM_DISTANCE = 2.0;
var SHOW_TOOL_BAR = false;
// tool bar
if (SHOW_TOOL_BAR) {
var BUTTON_SIZE = 64;
var PADDING = 6;
Script.include(["libraries/toolBars.js"]);
var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.attachedEntities.toolbar");
var lockButton = toolBar.addTool({
width: BUTTON_SIZE,
height: BUTTON_SIZE,
imageURL: Script.resolvePath("assets/images/lock.svg"),
color: {
red: 255,
green: 255,
blue: 255
},
alpha: 1,
visible: true
}, false);
}
function mousePressEvent(event) {
var clickedOverlay = Overlays.getOverlayAtPoint({
x: event.x,
y: event.y
});
if (lockButton === toolBar.clicked(clickedOverlay)) {
manager.toggleLocked();
}
}
function scriptEnding() {
if (SHOW_TOOL_BAR) {
toolBar.cleanup();
}
}
if (SHOW_TOOL_BAR) {
Controller.mousePressEvent.connect(mousePressEvent);
}
Script.scriptEnding.connect(scriptEnding);
// attached entites
function AttachedEntitiesManager() {
var clothingLocked = false;
this.subscribeToMessages = function() {
Messages.subscribe('Hifi-Object-Manipulation');
Messages.messageReceived.connect(this.handleWearableMessages);
}
this.handleWearableMessages = function(channel, message, sender) {
if (channel !== 'Hifi-Object-Manipulation') {
return;
}
var parsedMessage = null;
try {
parsedMessage = JSON.parse(message);
} catch (e) {
print('error parsing wearable message');
return;
}
if (parsedMessage.action === 'update' ||
parsedMessage.action === 'loaded') {
// ignore
} else if (parsedMessage.action === 'release') {
manager.handleEntityRelease(parsedMessage.grabbedEntity, parsedMessage.joint)
// manager.saveAttachedEntities();
} else if (parsedMessage.action === 'equip') {
// manager.saveAttachedEntities();
} else {
print('attachedEntitiesManager -- unknown actions: ' + parsedMessage.action);
}
}
this.handleEntityRelease = function(grabbedEntity, releasedFromJoint) {
// if this is still equipped, just rewrite the position information.
var grabData = getEntityCustomData('grabKey', grabbedEntity, {});
var allowedJoints = getEntityCustomData('wearable', grabbedEntity, DEFAULT_WEARABLE_DATA).joints;
var props = Entities.getEntityProperties(grabbedEntity, ["position", "parentID", "parentJointIndex"]);
if (props.parentID === Uuid.NONE || props.parentID === MyAvatar.sessionUUID) {
var bestJointName = "";
var bestJointIndex = -1;
var bestJointDistance = 0;
var bestJointOffset = null;
for (var jointName in allowedJoints) {
if ((releasedFromJoint == "LeftHand" || releasedFromJoint == "RightHand") &&
(jointName == "LeftHand" || jointName == "RightHand")) {
// don't auto-attach to a hand if a hand just dropped something
continue;
}
var jointIndex = MyAvatar.getJointIndex(jointName);
if (jointIndex >= 0) {
var jointPosition = MyAvatar.getJointPosition(jointIndex);
var distanceFromJoint = Vec3.distance(jointPosition, props.position);
if (distanceFromJoint <= MINIMUM_DROP_DISTANCE_FROM_JOINT) {
if (bestJointIndex == -1 || distanceFromJoint < bestJointDistance) {
bestJointName = jointName;
bestJointIndex = jointIndex;
bestJointDistance = distanceFromJoint;
bestJointOffset = allowedJoints[jointName];
}
}
}
}
if (bestJointIndex != -1) {
var wearProps = Entities.getEntityProperties(grabbedEntity);
wearProps.parentID = MyAvatar.sessionUUID;
wearProps.parentJointIndex = bestJointIndex;
delete wearProps.localPosition;
delete wearProps.localRotation;
var updatePresets = false;
if (bestJointOffset && bestJointOffset.constructor === Array) {
if (!clothingLocked || bestJointOffset.length < 2) {
// we're unlocked or this thing didn't have a preset position, so update it
updatePresets = true;
} else {
// don't snap the entity to the preferred position if unlocked
wearProps.localPosition = bestJointOffset[0];
wearProps.localRotation = bestJointOffset[1];
}
}
Entities.deleteEntity(grabbedEntity);
//the true boolean here after add entity adds it as an 'avatar entity', which can travel with you from server to server.
var newEntity = Entities.addEntity(wearProps, true);
if (updatePresets) {
this.updateRelativeOffsets(newEntity);
}
} else if (props.parentID != Uuid.NONE) {
// drop the entity and set it to have no parent (not on the avatar), unless it's being equipped in a hand.
if (props.parentID === MyAvatar.sessionUUID &&
(props.parentJointIndex == MyAvatar.getJointIndex("RightHand") ||
props.parentJointIndex == MyAvatar.getJointIndex("LeftHand"))) {
// this is equipped on a hand -- don't clear the parent.
} else {
var wearProps = Entities.getEntityProperties(grabbedEntity);
wearProps.parentID = Uuid.NONE;
wearProps.parentJointIndex = -1;
delete wearProps.id;
delete wearProps.created;
delete wearProps.age;
delete wearProps.ageAsText;
delete wearProps.naturalDimensions;
delete wearProps.naturalPosition;
delete wearProps.actionData;
delete wearProps.sittingPoints;
delete wearProps.boundingBox;
delete wearProps.avatarEntity;
delete wearProps.owningAvatarID;
delete wearProps.localPosition;
delete wearProps.localRotation;
Entities.deleteEntity(grabbedEntity);
Entities.addEntity(wearProps);
}
}
}
}
this.updateRelativeOffsets = function(entityID) {
// save the preferred (current) relative position and rotation into the user-data of the entity
var props = Entities.getEntityProperties(entityID);
if (props.parentID == MyAvatar.sessionUUID) {
grabData = getEntityCustomData('grabKey', entityID, {});
var wearableData = getEntityCustomData('wearable', entityID, DEFAULT_WEARABLE_DATA);
var currentJointName = MyAvatar.getJointNames()[props.parentJointIndex];
wearableData.joints[currentJointName] = [props.localPosition, props.localRotation];
setEntityCustomData('wearable', entityID, wearableData);
return true;
}
return false;
}
// this.saveAttachedEntities = function() {
// print("--- saving attached entities ---");
// saveData = [];
// var nearbyEntities = Entities.findEntities(MyAvatar.position, ATTACHED_ENTITY_SEARCH_DISTANCE);
// for (i = 0; i < nearbyEntities.length; i++) {
// var entityID = nearbyEntities[i];
// if (this.updateRelativeOffsets(entityID)) {
// var props = Entities.getEntityProperties(entityID); // refresh, because updateRelativeOffsets changed them
// this.scrubProperties(props);
// saveData.push(props);
// }
// }
// Settings.setValue(ATTACHED_ENTITIES_SETTINGS_KEY, JSON.stringify(saveData));
// }
// this.scrubProperties = function(props) {
// var toScrub = ["queryAACube", "position", "rotation",
// "created", "ageAsText", "naturalDimensions",
// "naturalPosition", "velocity", "acceleration",
// "angularVelocity", "boundingBox"];
// toScrub.forEach(function(propertyName) {
// delete props[propertyName];
// });
// // if the userData has a grabKey, clear old state
// if ("userData" in props) {
// try {
// parsedUserData = JSON.parse(props.userData);
// if ("grabKey" in parsedUserData) {
// parsedUserData.grabKey.refCount = 0;
// delete parsedUserData.grabKey["avatarId"];
// props["userData"] = JSON.stringify(parsedUserData);
// }
// } catch (e) {
// }
// }
// }
// this.loadAttachedEntities = function(grabbedEntity) {
// print("--- loading attached entities ---");
// jsonAttachmentData = Settings.getValue(ATTACHED_ENTITIES_SETTINGS_KEY);
// var loadData = [];
// try {
// loadData = JSON.parse(jsonAttachmentData);
// } catch (e) {
// print('error parsing saved attachment data');
// return;
// }
// for (i = 0; i < loadData.length; i ++) {
// var savedProps = loadData[ i ];
// var currentProps = Entities.getEntityProperties(savedProps.id);
// if (currentProps.id == savedProps.id &&
// // TODO -- also check that parentJointIndex matches?
// currentProps.parentID == MyAvatar.sessionUUID) {
// // entity is already in-world. TODO -- patch it up?
// continue;
// }
// this.scrubProperties(savedProps);
// delete savedProps["id"];
// savedProps.parentID = MyAvatar.sessionUUID; // this will change between sessions
// var loadedEntityID = Entities.addEntity(savedProps, true);
// Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
// action: 'loaded',
// grabbedEntity: loadedEntityID
// }));
// }
// }
}
var manager = new AttachedEntitiesManager();
manager.subscribeToMessages();