mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 07:12:40 +02:00
add a button to Avatar panel to lock or unlock wearables.
allow grabbing / adjusting others' wearables if they are unlocked.
This commit is contained in:
parent
5f76c96340
commit
29af3b1612
9 changed files with 116 additions and 44 deletions
|
@ -16,6 +16,8 @@ Rectangle {
|
|||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
z: 1000
|
||||
|
@ -48,6 +50,7 @@ Rectangle {
|
|||
|
||||
property var jointNames: []
|
||||
property var currentAvatarSettings;
|
||||
property bool wearablesLocked;
|
||||
|
||||
function fetchAvatarModelName(marketId, avatar) {
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
|
@ -187,6 +190,8 @@ Rectangle {
|
|||
updateCurrentAvatarInBookmarks(currentAvatar);
|
||||
} else if (message.method === 'selectAvatarEntity') {
|
||||
adjustWearables.selectWearableByID(message.entityID);
|
||||
} else if (message.method === 'wearablesLockedChanged') {
|
||||
wearablesLocked = message.wearablesLocked;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -507,6 +512,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
SquareLabel {
|
||||
id: adjustLabel
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: wearablesLabel.verticalCenter
|
||||
glyphText: "\ue02e"
|
||||
|
@ -515,6 +521,17 @@ Rectangle {
|
|||
adjustWearables.open(currentAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
SquareLabel {
|
||||
anchors.right: adjustLabel.left
|
||||
anchors.verticalCenter: wearablesLabel.verticalCenter
|
||||
anchors.rightMargin: 15
|
||||
glyphText: wearablesLocked ? hifi.glyphs.lock : hifi.glyphs.unlock;
|
||||
|
||||
onClicked: {
|
||||
emitSendToScript({'method' : 'toggleWearablesLock'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
|
|
@ -344,6 +344,7 @@ Item {
|
|||
readonly property string stop_square: "\ue01e"
|
||||
readonly property string avatarTPose: "\ue01f"
|
||||
readonly property string lock: "\ue006"
|
||||
readonly property string unlock: "\ue039"
|
||||
readonly property string checkmark: "\ue020"
|
||||
readonly property string leftRightArrows: "\ue021"
|
||||
readonly property string hfc: "\ue022"
|
||||
|
|
|
@ -330,6 +330,7 @@ QtObject {
|
|||
readonly property string stop_square: "\ue01e"
|
||||
readonly property string avatarTPose: "\ue01f"
|
||||
readonly property string lock: "\ue006"
|
||||
readonly property string unlock: "\ue039"
|
||||
readonly property string checkmark: "\ue020"
|
||||
readonly property string leftRightArrows: "\ue021"
|
||||
readonly property string hfc: "\ue022"
|
||||
|
|
|
@ -2170,7 +2170,7 @@ private:
|
|||
bool getEnableStepResetRotation() const { return _stepResetRotationEnabled; }
|
||||
void setEnableDrawAverageFacing(bool drawAverage) { _drawAverageFacingEnabled = drawAverage; }
|
||||
bool getEnableDrawAverageFacing() const { return _drawAverageFacingEnabled; }
|
||||
bool isMyAvatar() const override { return true; }
|
||||
virtual bool isMyAvatar() const override { return true; }
|
||||
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
|
||||
virtual glm::vec3 getSkeletonPosition() const override;
|
||||
int _skeletonModelChangeCount { 0 };
|
||||
|
|
|
@ -365,7 +365,7 @@ void OtherAvatar::handleChangedAvatarEntityData() {
|
|||
// AVATAR ENTITY UPDATE FLOW
|
||||
// - if queueEditEntityMessage() sees "AvatarEntity" HostType it calls _myAvatar->storeAvatarEntityDataPayload()
|
||||
// - storeAvatarEntityDataPayload() saves the payload and flags the trait instance for the entity as updated,
|
||||
// - ClientTraitsHandler::sendChangedTraitsToMixea() sends the entity bytes to the mixer which relays them to other interfaces
|
||||
// - ClientTraitsHandler::sendChangedTraitsToMixer() sends the entity bytes to the mixer which relays them to other interfaces
|
||||
// - AvatarHashMap::processBulkAvatarTraits() on other interfaces calls avatar->processTraitInstance()
|
||||
// - AvatarData::processTraitInstance() calls storeAvatarEntityDataPayload(), which sets _avatarEntityDataChanged = true
|
||||
// - (My)Avatar::simulate() calls handleChangedAvatarEntityData() every frame which checks _avatarEntityDataChanged
|
||||
|
|
|
@ -372,11 +372,22 @@ bool Avatar::applyGrabChanges() {
|
|||
target->removeGrab(grab);
|
||||
_avatarGrabs.erase(itr);
|
||||
grabAddedOrRemoved = true;
|
||||
if (isMyAvatar()) {
|
||||
const EntityItemPointer& entity = std::dynamic_pointer_cast<EntityItem>(target);
|
||||
if (entity && entity->getEntityHostType() == entity::HostType::AVATAR && entity->getSimulationOwner().getID() == getID()) {
|
||||
EntityItemProperties properties = entity->getProperties();
|
||||
sendPacket(entity->getID());
|
||||
const EntityItemPointer& entity = std::dynamic_pointer_cast<EntityItem>(target);
|
||||
if (entity && entity->getEntityHostType() == entity::HostType::AVATAR) {
|
||||
// grabs are able to move avatar-entities which belong ot other avatars (assuming
|
||||
// the entities are grabbable, unlocked, etc). Regardless of who released the grab
|
||||
// on this entity, the entity's owner needs to send off an update.
|
||||
QUuid entityOwnerID = entity->getOwningAvatarID();
|
||||
if (entityOwnerID == getMyAvatarID() || entityOwnerID == AVATAR_SELF_ID) {
|
||||
bool success;
|
||||
SpatiallyNestablePointer myAvatarSN = SpatiallyNestable::findByID(entityOwnerID, success);
|
||||
if (success) {
|
||||
std::shared_ptr<Avatar> myAvatar = std::dynamic_pointer_cast<Avatar>(myAvatarSN);
|
||||
if (myAvatar) {
|
||||
EntityItemProperties properties = entity->getProperties();
|
||||
myAvatar->sendPacket(entity->getID(), properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -2103,3 +2114,12 @@ void Avatar::updateDescendantRenderIDs() {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
QUuid Avatar::getMyAvatarID() const {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
if (nodeList) {
|
||||
return nodeList->getSessionUUID();
|
||||
} else {
|
||||
return QUuid();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -180,7 +180,8 @@ public:
|
|||
/// Returns the distance to use as a LOD parameter.
|
||||
float getLODDistance() const;
|
||||
|
||||
virtual bool isMyAvatar() const override { return false; }
|
||||
QUuid getMyAvatarID() const;
|
||||
|
||||
virtual void createOrb() { }
|
||||
|
||||
enum class LoadingStatus {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"use strict";
|
||||
/*jslint vars:true, plusplus:true, forin:true*/
|
||||
/*global Tablet, Settings, Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, Vec3, Quat, HMD, Controller, Account, UserActivityLogger, Messages, Window, XMLHttpRequest, print, location, getControllerWorldLocation*/
|
||||
/*global Tablet, Script, Entities, MyAvatar, Camera, Quat, HMD, Account, UserActivityLogger, Messages, print,
|
||||
AvatarBookmarks, ContextOverlay, AddressManager
|
||||
*/
|
||||
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
|
||||
//
|
||||
// avatarapp.js
|
||||
|
@ -14,7 +16,7 @@
|
|||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
|
||||
var request = Script.require('request').request;
|
||||
// var request = Script.require('request').request;
|
||||
var AVATARAPP_QML_SOURCE = "hifi/AvatarApp.qml";
|
||||
Script.include("/~/system/libraries/controllers.js");
|
||||
|
||||
|
@ -22,7 +24,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
var ENTRY_AVATAR_URL = "avatarUrl";
|
||||
var ENTRY_AVATAR_ENTITIES = "avatarEntites";
|
||||
var ENTRY_AVATAR_SCALE = "avatarScale";
|
||||
var ENTRY_VERSION = "version";
|
||||
// var ENTRY_VERSION = "version";
|
||||
|
||||
function executeLater(callback) {
|
||||
Script.setTimeout(callback, 300);
|
||||
|
@ -44,7 +46,7 @@ function getMyAvatarWearables() {
|
|||
}
|
||||
|
||||
var localRotation = entity.properties.localRotation;
|
||||
entity.properties.localRotationAngles = Quat.safeEulerAngles(localRotation)
|
||||
entity.properties.localRotationAngles = Quat.safeEulerAngles(localRotation);
|
||||
wearablesArray.push(entity);
|
||||
}
|
||||
|
||||
|
@ -52,7 +54,7 @@ function getMyAvatarWearables() {
|
|||
}
|
||||
|
||||
function getMyAvatar() {
|
||||
var avatar = {}
|
||||
var avatar = {};
|
||||
avatar[ENTRY_AVATAR_URL] = MyAvatar.skeletonModelURL;
|
||||
avatar[ENTRY_AVATAR_SCALE] = MyAvatar.getAvatarScale();
|
||||
avatar[ENTRY_AVATAR_ENTITIES] = getMyAvatarWearables();
|
||||
|
@ -68,7 +70,7 @@ function getMyAvatarSettings() {
|
|||
collisionSoundUrl : MyAvatar.collisionSoundURL,
|
||||
animGraphUrl: MyAvatar.getAnimGraphUrl(),
|
||||
animGraphOverrideUrl : MyAvatar.getAnimGraphOverrideUrl(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function updateAvatarWearables(avatar, callback, wearablesOverride) {
|
||||
|
@ -76,7 +78,7 @@ function updateAvatarWearables(avatar, callback, wearablesOverride) {
|
|||
var wearables = wearablesOverride ? wearablesOverride : getMyAvatarWearables();
|
||||
avatar[ENTRY_AVATAR_ENTITIES] = wearables;
|
||||
|
||||
sendToQml({'method' : 'wearablesUpdated', 'wearables' : wearables})
|
||||
sendToQml({'method' : 'wearablesUpdated', 'wearables' : wearables});
|
||||
|
||||
if(callback)
|
||||
callback();
|
||||
|
@ -101,7 +103,7 @@ var adjustWearables = {
|
|||
this.opened = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var currentAvatarWearablesBackup = null;
|
||||
var currentAvatar = null;
|
||||
|
@ -112,7 +114,7 @@ function onTargetScaleChanged() {
|
|||
if(currentAvatar.scale !== MyAvatar.getAvatarScale()) {
|
||||
currentAvatar.scale = MyAvatar.getAvatarScale();
|
||||
if(notifyScaleChanged) {
|
||||
sendToQml({'method' : 'scaleChanged', 'value' : currentAvatar.scale})
|
||||
sendToQml({'method' : 'scaleChanged', 'value' : currentAvatar.scale});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +128,7 @@ function onSkeletonModelURLChanged() {
|
|||
function onDominantHandChanged(dominantHand) {
|
||||
if(currentAvatarSettings.dominantHand !== dominantHand) {
|
||||
currentAvatarSettings.dominantHand = dominantHand;
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'dominantHand', 'value' : dominantHand})
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'dominantHand', 'value' : dominantHand});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,32 +142,33 @@ function onHmdAvatarAlignmentTypeChanged(type) {
|
|||
function onCollisionsEnabledChanged(enabled) {
|
||||
if(currentAvatarSettings.collisionsEnabled !== enabled) {
|
||||
currentAvatarSettings.collisionsEnabled = enabled;
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'collisionsEnabled', 'value' : enabled})
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'collisionsEnabled', 'value' : enabled});
|
||||
}
|
||||
}
|
||||
|
||||
function onOtherAvatarsCollisionsEnabledChanged(enabled) {
|
||||
if (currentAvatarSettings.otherAvatarsCollisionsEnabled !== enabled) {
|
||||
currentAvatarSettings.otherAvatarsCollisionsEnabled = enabled;
|
||||
sendToQml({ 'method': 'settingChanged', 'name': 'otherAvatarsCollisionsEnabled', 'value': enabled })
|
||||
sendToQml({ 'method': 'settingChanged', 'name': 'otherAvatarsCollisionsEnabled', 'value': enabled });
|
||||
}
|
||||
}
|
||||
|
||||
function onNewCollisionSoundUrl(url) {
|
||||
if(currentAvatarSettings.collisionSoundUrl !== url) {
|
||||
currentAvatarSettings.collisionSoundUrl = url;
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'collisionSoundUrl', 'value' : url})
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'collisionSoundUrl', 'value' : url});
|
||||
}
|
||||
}
|
||||
|
||||
function onAnimGraphUrlChanged(url) {
|
||||
if (currentAvatarSettings.animGraphUrl !== url) {
|
||||
currentAvatarSettings.animGraphUrl = url;
|
||||
sendToQml({ 'method': 'settingChanged', 'name': 'animGraphUrl', 'value': currentAvatarSettings.animGraphUrl })
|
||||
sendToQml({ 'method': 'settingChanged', 'name': 'animGraphUrl', 'value': currentAvatarSettings.animGraphUrl });
|
||||
|
||||
if (currentAvatarSettings.animGraphOverrideUrl !== MyAvatar.getAnimGraphOverrideUrl()) {
|
||||
currentAvatarSettings.animGraphOverrideUrl = MyAvatar.getAnimGraphOverrideUrl();
|
||||
sendToQml({ 'method': 'settingChanged', 'name': 'animGraphOverrideUrl', 'value': currentAvatarSettings.animGraphOverrideUrl })
|
||||
sendToQml({ 'method': 'settingChanged', 'name': 'animGraphOverrideUrl',
|
||||
'value': currentAvatarSettings.animGraphOverrideUrl });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,6 +181,33 @@ var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/wallet/Wallet.qml";
|
|||
var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace";
|
||||
var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("html/js/marketplacesInject.js");
|
||||
|
||||
function getWearablesLocked() {
|
||||
var wearablesLocked = true;
|
||||
var wearablesArray = getMyAvatarWearables();
|
||||
wearablesArray.forEach(function(wearable) {
|
||||
if (isGrabbable(wearable.id)) {
|
||||
wearablesLocked = false;
|
||||
}
|
||||
});
|
||||
|
||||
return wearablesLocked;
|
||||
}
|
||||
|
||||
function lockWearables() {
|
||||
var wearablesArray = getMyAvatarWearables();
|
||||
wearablesArray.forEach(function(wearable) {
|
||||
setGrabbable(wearable.id, false);
|
||||
});
|
||||
}
|
||||
|
||||
function unlockWearables() {
|
||||
var wearablesArray = getMyAvatarWearables();
|
||||
wearablesArray.forEach(function(wearable) {
|
||||
setGrabbable(wearable.id, true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml.
|
||||
switch (message.method) {
|
||||
case 'getAvatars':
|
||||
|
@ -201,7 +231,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
|||
}
|
||||
}
|
||||
|
||||
sendToQml(message)
|
||||
sendToQml(message);
|
||||
break;
|
||||
case 'selectAvatar':
|
||||
Entities.addingWearable.disconnect(onAddingWearable);
|
||||
|
@ -228,7 +258,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
|||
message.properties.localRotationAngles = Quat.safeEulerAngles(message.properties.localRotation);
|
||||
}
|
||||
|
||||
sendToQml({'method' : 'wearableUpdated', 'entityID' : message.entityID, wearableIndex : message.wearableIndex, properties : message.properties, updateUI : false})
|
||||
sendToQml({'method' : 'wearableUpdated', 'entityID' : message.entityID, wearableIndex : message.wearableIndex, properties : message.properties, updateUI : false});
|
||||
break;
|
||||
case 'adjustWearablesOpened':
|
||||
currentAvatarWearablesBackup = getMyAvatarWearables();
|
||||
|
@ -305,11 +335,11 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
|||
var currentAvatarURL = MyAvatar.getFullAvatarURLFromPreferences();
|
||||
if(currentAvatarURL !== message.avatarURL) {
|
||||
MyAvatar.useFullAvatarURL(message.avatarURL);
|
||||
sendToQml({'method' : 'externalAvatarApplied', 'avatarURL' : message.avatarURL})
|
||||
sendToQml({'method' : 'externalAvatarApplied', 'avatarURL' : message.avatarURL});
|
||||
}
|
||||
break;
|
||||
case 'navigate':
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system")
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
if(message.url.indexOf('app://') === 0) {
|
||||
if (message.url === 'app://marketplace') {
|
||||
tablet.gotoWebScreen(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL);
|
||||
|
@ -345,7 +375,17 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
|||
MyAvatar.collisionSoundURL = message.settings.collisionSoundUrl;
|
||||
MyAvatar.setAnimGraphOverrideUrl(message.settings.animGraphOverrideUrl);
|
||||
|
||||
settings = getMyAvatarSettings();
|
||||
currentAvatarSettings = getMyAvatarSettings();
|
||||
break;
|
||||
case 'toggleWearablesLock':
|
||||
var wearablesLocked = getWearablesLocked();
|
||||
wearablesLocked = !wearablesLocked;
|
||||
if (wearablesLocked) {
|
||||
lockWearables();
|
||||
} else {
|
||||
unlockWearables();
|
||||
}
|
||||
sendToQml({'method' : 'wearablesLockedChanged', 'wearablesLocked' : wearablesLocked});
|
||||
break;
|
||||
default:
|
||||
print('Unrecognized message from AvatarApp.qml');
|
||||
|
@ -398,7 +438,7 @@ function ensureWearableSelected(entityID) {
|
|||
|
||||
function isEntityBeingWorn(entityID) {
|
||||
return Entities.getEntityProperties(entityID, 'parentID').parentID === MyAvatar.sessionUUID;
|
||||
};
|
||||
}
|
||||
|
||||
function onSelectedEntity(entityID, pointerEvent) {
|
||||
if(selectedAvatarEntityID !== entityID && isEntityBeingWorn(entityID))
|
||||
|
@ -445,14 +485,14 @@ function handleWearableMessages(channel, message, sender) {
|
|||
// for some reasons Entities.getEntityProperties returns more than was asked..
|
||||
var propertyNames = ['localPosition', 'localRotation', 'dimensions', 'naturalDimensions'];
|
||||
var entityProperties = Entities.getEntityProperties(selectedAvatarEntityID, propertyNames);
|
||||
var properties = {}
|
||||
var properties = {};
|
||||
|
||||
propertyNames.forEach(function(propertyName) {
|
||||
properties[propertyName] = entityProperties[propertyName];
|
||||
})
|
||||
});
|
||||
|
||||
properties.localRotationAngles = Quat.safeEulerAngles(properties.localRotation);
|
||||
sendToQml({'method' : 'wearableUpdated', 'entityID' : selectedAvatarEntityID, 'wearableIndex' : -1, 'properties' : properties, updateUI : true})
|
||||
sendToQml({'method' : 'wearableUpdated', 'entityID' : selectedAvatarEntityID, 'wearableIndex' : -1, 'properties' : properties, updateUI : true});
|
||||
|
||||
}, 1000);
|
||||
} else if(parsedMessage.action === 'release') {
|
||||
|
@ -481,8 +521,8 @@ function onBookmarkDeleted(bookmarkName) {
|
|||
function onBookmarkAdded(bookmarkName) {
|
||||
var bookmark = AvatarBookmarks.getBookmark(bookmarkName);
|
||||
bookmark.avatarEntites.forEach(function(avatarEntity) {
|
||||
avatarEntity.properties.localRotationAngles = Quat.safeEulerAngles(avatarEntity.properties.localRotation)
|
||||
})
|
||||
avatarEntity.properties.localRotationAngles = Quat.safeEulerAngles(avatarEntity.properties.localRotation);
|
||||
});
|
||||
|
||||
sendToQml({ 'method': 'bookmarkAdded', 'bookmarkName': bookmarkName, 'bookmark': bookmark });
|
||||
}
|
||||
|
@ -601,14 +641,8 @@ function onTabletScreenChanged(type, url) {
|
|||
onAvatarAppScreen = onAvatarAppScreenNow;
|
||||
|
||||
if(onAvatarAppScreenNow) {
|
||||
var message = {
|
||||
'method' : 'initialize',
|
||||
'data' : {
|
||||
'jointNames' : MyAvatar.getJointNames()
|
||||
}
|
||||
};
|
||||
|
||||
sendToQml(message)
|
||||
sendToQml({ method : 'initialize', data : { jointNames : MyAvatar.getJointNames() }});
|
||||
sendToQml({ method : 'wearablesLockedChanged', wearablesLocked : getWearablesLocked()});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -341,8 +341,6 @@ entityIsGrabbable = function (eigProps) {
|
|||
var grabbable = getGrabbableData(eigProps).grabbable;
|
||||
if (!grabbable ||
|
||||
eigProps.locked ||
|
||||
isAnothersAvatarEntity(eigProps) ||
|
||||
isAnothersChildEntity(eigProps) ||
|
||||
FORBIDDEN_GRAB_TYPES.indexOf(eigProps.type) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue