mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 07:12:40 +02:00
avatar selection & wearables adjustments work-on-progress
This commit is contained in:
parent
57568d120e
commit
b6769f9678
8 changed files with 192 additions and 45 deletions
|
@ -25,12 +25,34 @@ Rectangle {
|
|||
|
||||
property var jointNames;
|
||||
|
||||
function setCurrentAvatar(currentAvatar) {
|
||||
currentAvatarModel.clear();
|
||||
|
||||
root.currentAvatar = {
|
||||
'name' : '',
|
||||
'url' : allAvatars.makeThumbnailUrl(currentAvatar.avatarUrl),
|
||||
'wearables' : currentAvatar.avatarEntites ? currentAvatar.avatarEntites : [],
|
||||
'entry' : currentAvatar,
|
||||
'getMoreAvatars' : false
|
||||
};
|
||||
|
||||
console.debug('AvatarApp.qml: currentAvatar: ', JSON.stringify(root.currentAvatar, null, '\t'))
|
||||
currentAvatarModel.append(root.currentAvatar);
|
||||
root.currentAvatar = currentAvatarModel.get(currentAvatarModel.count - 1);
|
||||
}
|
||||
|
||||
function fromScript(message) {
|
||||
console.debug('AvatarApp.qml: fromScript: ', JSON.stringify(message, null, '\t'))
|
||||
|
||||
if(message.method === 'initialize') {
|
||||
jointNames = message.reply.jointNames;
|
||||
emitSendToScript({'method' : getAvatarsMethod});
|
||||
} else if(message.method === 'bookmarkLoaded') {
|
||||
setCurrentAvatar(message.reply.currentAvatar);
|
||||
selectedAvatarId = message.reply.name;
|
||||
var avatarIndex = allAvatars.findAvatarIndex(selectedAvatarId);
|
||||
allAvatars.move(avatarIndex, 0, 1);
|
||||
view.setPage(0);
|
||||
} else if(message.method === getAvatarsMethod) {
|
||||
var getAvatarsReply = message.reply;
|
||||
allAvatars.populate(getAvatarsReply.bookmarks);
|
||||
|
@ -70,20 +92,10 @@ Rectangle {
|
|||
|
||||
console.debug('selectedAvatarIndex = -1, avatar is not favorite')
|
||||
|
||||
setCurrentAvatar(currentAvatar);
|
||||
|
||||
if(selectedAvatarIndex === -1) {
|
||||
|
||||
var currentAvatarEntry = {
|
||||
'name' : '',
|
||||
'url' : Qt.resolvedUrl(allAvatars.urls[i++ % allAvatars.urls.length]),
|
||||
'wearables' : currentAvatar.avatarEntites ? currentAvatar.avatarEntites : [],
|
||||
'entry' : currentAvatar,
|
||||
'getMoreAvatars' : false
|
||||
};
|
||||
|
||||
currentAvatarModel.append(currentAvatarEntry);
|
||||
currentAvatarEntry = allAvatars.get(allAvatars.count - 1);
|
||||
|
||||
selectedAvatar = currentAvatarEntry;
|
||||
selectedAvatar = root.currentAvatar;
|
||||
view.setPage(0);
|
||||
|
||||
console.debug('selectedAvatar = ', JSON.stringify(selectedAvatar, null, '\t'))
|
||||
|
@ -93,6 +105,8 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
property var currentAvatar;
|
||||
|
||||
property string selectedAvatarId: ''
|
||||
onSelectedAvatarIdChanged: {
|
||||
console.debug('selectedAvatarId: ', selectedAvatarId)
|
||||
|
@ -174,6 +188,9 @@ Rectangle {
|
|||
anchors.top: header.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
jointNames: root.jointNames
|
||||
onWearableChanged: {
|
||||
emitSendToScript({'method' : 'adjustWearable', 'id' : id, 'properties' : properties})
|
||||
}
|
||||
|
||||
z: 3
|
||||
}
|
||||
|
@ -365,7 +382,7 @@ Rectangle {
|
|||
anchors.fill: parent
|
||||
onClicked: {
|
||||
console.debug('adjustWearables.open');
|
||||
adjustWearables.open(selectedAvatar);
|
||||
adjustWearables.open(currentAvatar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -488,11 +505,7 @@ Rectangle {
|
|||
property int verticalSpacing: 36
|
||||
|
||||
function selectAvatar(avatar) {
|
||||
AvatarBookmarks.loadBookmark(avatar.name);
|
||||
selectedAvatarId = avatar.name;
|
||||
var avatarIndex = allAvatars.findAvatarIndex(selectedAvatarId);
|
||||
allAvatars.move(avatarIndex, 0, 1);
|
||||
view.setPage(0);
|
||||
emitSendToScript({'method' : 'selectAvatar', 'name' : avatar.name})
|
||||
}
|
||||
|
||||
AvatarsModel {
|
||||
|
@ -630,7 +643,10 @@ Rectangle {
|
|||
imageUrl: url
|
||||
border.color: container.highlighted ? style.colors.blueHighlight : 'transparent'
|
||||
border.width: container.highlighted ? 2 : 0
|
||||
wearablesCount: !getMoreAvatars ? wearables.count : 0
|
||||
wearablesCount: {
|
||||
console.debug('getMoreAvatars: ', getMoreAvatars, 'name: ', name);
|
||||
return !getMoreAvatars ? wearables.count : 0
|
||||
}
|
||||
onWearablesCountChanged: {
|
||||
console.debug('delegate: AvatarThumbnail.wearablesCount: ', wearablesCount)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ Rectangle {
|
|||
height: 706
|
||||
color: 'white'
|
||||
|
||||
signal wearableChanged(var id, var properties);
|
||||
|
||||
property bool modified: false;
|
||||
Component.onCompleted: {
|
||||
modified = false;
|
||||
|
@ -24,11 +26,13 @@ Rectangle {
|
|||
property var onButton1Clicked;
|
||||
property var jointNames;
|
||||
|
||||
property var wearables: ({})
|
||||
function open(avatar) {
|
||||
console.debug('AdjustWearables.qml: open');
|
||||
console.debug('AdjustWearables.qml: open: ', JSON.stringify(avatar, null, '\t'));
|
||||
|
||||
visible = true;
|
||||
wearablesCombobox.model.clear();
|
||||
wearables = {};
|
||||
|
||||
console.debug('AdjustWearables.qml: avatar.wearables.count: ', avatar.wearables.count);
|
||||
for(var i = 0; i < avatar.wearables.count; ++i) {
|
||||
|
@ -38,6 +42,12 @@ Rectangle {
|
|||
for(var j = (wearable.modelURL.length - 1); j >= 0; --j) {
|
||||
if(wearable.modelURL[j] === '/') {
|
||||
wearable.text = wearable.modelURL.substring(j + 1) + ' [%jointIndex%]'.replace('%jointIndex%', jointNames[wearable.parentJointIndex]);
|
||||
wearables[wearable.id] = {
|
||||
position: wearable.localPosition,
|
||||
rotation: wearable.localRotation,
|
||||
dimensions: wearable.dimensions
|
||||
};
|
||||
|
||||
console.debug('wearable.text = ', wearable.text);
|
||||
break;
|
||||
}
|
||||
|
@ -79,6 +89,25 @@ Rectangle {
|
|||
|
||||
model: ListModel {
|
||||
}
|
||||
|
||||
comboBox.onCurrentIndexChanged: {
|
||||
console.debug('wearable index changed: ', currentIndex);
|
||||
var currentWearable = wearablesCombobox.model.get(currentIndex)
|
||||
|
||||
if(currentWearable) {
|
||||
position.notify = false;
|
||||
position.xvalue = currentWearable.localPosition.x
|
||||
position.yvalue = currentWearable.localPosition.y
|
||||
position.zvalue = currentWearable.localPosition.z
|
||||
position.notify = true;
|
||||
|
||||
rotation.notify = false;
|
||||
rotation.xvalue = currentWearable.localRotationAngles.x
|
||||
rotation.yvalue = currentWearable.localRotationAngles.y
|
||||
rotation.zvalue = currentWearable.localRotationAngles.z
|
||||
rotation.notify = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
|
@ -101,9 +130,25 @@ Rectangle {
|
|||
id: position
|
||||
backgroundColor: "lightgray"
|
||||
|
||||
onXvalueChanged: modified = true;
|
||||
onYvalueChanged: modified = true;
|
||||
onZvalueChanged: modified = true;
|
||||
function notifyPositionChanged() {
|
||||
modified = true;
|
||||
var properties = {};
|
||||
properties.localPosition = { 'x' : xvalue, 'y' : yvalue, 'z' : zvalue }
|
||||
|
||||
var currentWearable = wearablesCombobox.model.get(wearablesCombobox.currentIndex)
|
||||
wearableChanged(currentWearable.id, properties);
|
||||
}
|
||||
|
||||
property bool notify: false;
|
||||
|
||||
onXvalueChanged: if(notify) notifyPositionChanged();
|
||||
onYvalueChanged: if(notify) notifyPositionChanged();
|
||||
onZvalueChanged: if(notify) notifyPositionChanged();
|
||||
|
||||
decimals: 4
|
||||
realFrom: -100
|
||||
realTo: 100
|
||||
realStepSize: 0.0001
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,9 +172,25 @@ Rectangle {
|
|||
id: rotation
|
||||
backgroundColor: "lightgray"
|
||||
|
||||
onXvalueChanged: modified = true;
|
||||
onYvalueChanged: modified = true;
|
||||
onZvalueChanged: modified = true;
|
||||
function notifyRotationChanged() {
|
||||
modified = true;
|
||||
var properties = {};
|
||||
properties.localRotationAngles = { 'x' : xvalue, 'y' : yvalue, 'z' : zvalue }
|
||||
|
||||
var currentWearable = wearablesCombobox.model.get(wearablesCombobox.currentIndex)
|
||||
wearableChanged(currentWearable.id, properties);
|
||||
}
|
||||
|
||||
property bool notify: false;
|
||||
|
||||
onXvalueChanged: if(notify) notifyRotationChanged();
|
||||
onYvalueChanged: if(notify) notifyRotationChanged();
|
||||
onZvalueChanged: if(notify) notifyRotationChanged();
|
||||
|
||||
decimals: 4
|
||||
realFrom: -100
|
||||
realTo: 100
|
||||
realStepSize: 0.0001
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,16 +3,22 @@ import QtQuick 2.9
|
|||
ListModel {
|
||||
id: model
|
||||
|
||||
function makeThumbnailUrl(avatarUrl) {
|
||||
var splittedUrl = avatarUrl.split('/');
|
||||
var marketId = splittedUrl[splittedUrl.length - 2];
|
||||
var indexOfVSuffix = marketId.indexOf('-v');
|
||||
if(indexOfVSuffix !== -1) {
|
||||
marketId = marketId.substring(0, indexOfVSuffix);
|
||||
}
|
||||
var avatarThumbnailUrl = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/%marketId%/large/hifi-mp-%marketId%.jpg"
|
||||
.split('%marketId%').join(marketId);
|
||||
|
||||
return avatarThumbnailUrl;
|
||||
}
|
||||
|
||||
function populate(bookmarks) {
|
||||
for(var avatarName in bookmarks) {
|
||||
var splittedUrl = bookmarks[avatarName].avatarUrl.split('/');
|
||||
var marketId = splittedUrl[splittedUrl.length - 2];
|
||||
var indexOfVSuffix = marketId.indexOf('-v');
|
||||
if(indexOfVSuffix !== -1) {
|
||||
marketId = marketId.substring(0, indexOfVSuffix);
|
||||
}
|
||||
var avatarThumbnailUrl = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/%marketId%/medium/hifi-mp-%marketId%.jpg"
|
||||
.split('%marketId%').join(marketId);
|
||||
var avatarThumbnailUrl = makeThumbnailUrl(bookmarks[avatarName].avatarUrl);
|
||||
|
||||
var avatarEntry = {
|
||||
'name' : avatarName,
|
||||
|
|
|
@ -5,6 +5,7 @@ import "../../controls-uit" as HifiControlsUit
|
|||
import "../../controls" as HifiControls
|
||||
|
||||
Row {
|
||||
id: root
|
||||
width: parent.width
|
||||
height: xspinner.controlHeight
|
||||
|
||||
|
@ -12,11 +13,16 @@ Row {
|
|||
property int spinboxWidth: (parent.width - 2 * spinboxSpace) / 3
|
||||
property color backgroundColor: "darkgray"
|
||||
|
||||
property int decimals: 4
|
||||
property real realFrom: 0
|
||||
property real realTo: 100
|
||||
property real realStepSize: 0.0001
|
||||
|
||||
spacing: spinboxSpace
|
||||
|
||||
property real xvalue: xspinner.value
|
||||
property real yvalue: yspinner.value
|
||||
property real zvalue: zspinner.value
|
||||
property alias xvalue: xspinner.realValue
|
||||
property alias yvalue: yspinner.realValue
|
||||
property alias zvalue: zspinner.realValue
|
||||
|
||||
HifiControlsUit.SpinBox {
|
||||
id: xspinner
|
||||
|
@ -25,6 +31,10 @@ Row {
|
|||
backgroundColor: parent.backgroundColor
|
||||
colorLabelInside: hifi.colors.redHighlight
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
decimals: root.decimals;
|
||||
realFrom: root.realFrom
|
||||
realTo: root.realTo
|
||||
realStepSize: root.realStepSize
|
||||
}
|
||||
|
||||
HifiControlsUit.SpinBox {
|
||||
|
@ -34,6 +44,10 @@ Row {
|
|||
backgroundColor: parent.backgroundColor
|
||||
colorLabelInside: hifi.colors.greenHighlight
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
decimals: root.decimals;
|
||||
realFrom: root.realFrom
|
||||
realTo: root.realTo
|
||||
realStepSize: root.realStepSize
|
||||
}
|
||||
|
||||
HifiControlsUit.SpinBox {
|
||||
|
@ -43,5 +57,9 @@ Row {
|
|||
backgroundColor: parent.backgroundColor
|
||||
colorLabelInside: hifi.colors.primaryHighlight
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
decimals: root.decimals;
|
||||
realFrom: root.realFrom
|
||||
realTo: root.realTo
|
||||
realStepSize: root.realStepSize
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,6 +162,8 @@ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) {
|
|||
|
||||
const QVariantList& avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList();
|
||||
addAvatarEntities(avatarEntities);
|
||||
|
||||
emit bookmarkLoaded(bookmarkName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -234,6 +236,7 @@ void AvatarBookmarks::changeToBookmarkedAvatar() {
|
|||
const QVariantList& avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList();
|
||||
addAvatarEntities(avatarEntities);
|
||||
|
||||
emit bookmarkLoaded(action->text());
|
||||
} else {
|
||||
qCDebug(interfaceapp) << " Bookmark entry does not match client version, make sure client has a handler for the new AvatarBookmark";
|
||||
}
|
||||
|
|
|
@ -45,6 +45,9 @@ public slots:
|
|||
void removeBookmark(const QString& bookmarkName);
|
||||
QVariantMap getBookmarks() { return _bookmarks; }
|
||||
|
||||
signals:
|
||||
void bookmarkLoaded(const QString& bookmarkName);
|
||||
|
||||
protected:
|
||||
void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) override;
|
||||
void readFromFile() override;
|
||||
|
|
|
@ -1596,7 +1596,11 @@ QVariantList MyAvatar::getAvatarEntitiesVariant() {
|
|||
auto modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(child);
|
||||
if (modelEntity) {
|
||||
QVariantMap avatarEntityData;
|
||||
EntityItemProperties entityProperties = modelEntity->getProperties();
|
||||
EncodeBitstreamParams params;
|
||||
auto desiredProperties = modelEntity->getEntityProperties(params);
|
||||
desiredProperties += PROP_LOCAL_POSITION;
|
||||
desiredProperties += PROP_LOCAL_ROTATION;
|
||||
EntityItemProperties entityProperties = modelEntity->getProperties(desiredProperties);
|
||||
QScriptValue scriptProperties = EntityItemPropertiesToScriptValue(&scriptEngine, entityProperties);
|
||||
avatarEntityData["properties"] = scriptProperties.toVariant();
|
||||
avatarEntitiesData.append(QVariant(avatarEntityData));
|
||||
|
|
|
@ -25,24 +25,51 @@ var ENTRY_AVATAR_ENTITIES = "avatarEntites";
|
|||
var ENTRY_AVATAR_SCALE = "avatarScale";
|
||||
var ENTRY_VERSION = "version";
|
||||
|
||||
function getCurrentAvatar() {
|
||||
var currentAvatar = {}
|
||||
currentAvatar[ENTRY_AVATAR_URL] = MyAvatar.skeletonModelURL;
|
||||
currentAvatar[ENTRY_AVATAR_SCALE] = MyAvatar.getAvatarScale();
|
||||
currentAvatar[ENTRY_AVATAR_ATTACHMENTS] = MyAvatar.getAttachmentsVariant();
|
||||
currentAvatar[ENTRY_AVATAR_ENTITIES] = MyAvatar.getAvatarEntitiesVariant();
|
||||
|
||||
for(var i = 0; i < currentAvatar[ENTRY_AVATAR_ENTITIES].length; ++i) {
|
||||
var wearable = currentAvatar[ENTRY_AVATAR_ENTITIES][i];
|
||||
console.debug('updating localRotationAngles for wearable: ', JSON.stringify(wearable, null, '\t'));
|
||||
var localRotation = wearable.properties.localRotation;
|
||||
wearable.properties.localRotationAngles = Quat.safeEulerAngles(localRotation)
|
||||
}
|
||||
|
||||
return currentAvatar;
|
||||
}
|
||||
|
||||
function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml.
|
||||
console.debug('fromQml: message = ', JSON.stringify(message, null, '\t'))
|
||||
|
||||
switch (message.method) {
|
||||
case 'getAvatars':
|
||||
var currentAvatar = {}
|
||||
currentAvatar[ENTRY_AVATAR_URL] = MyAvatar.skeletonModelURL;
|
||||
currentAvatar[ENTRY_AVATAR_SCALE] = MyAvatar.getAvatarScale();
|
||||
currentAvatar[ENTRY_AVATAR_ATTACHMENTS] = MyAvatar.getAttachmentsVariant();
|
||||
currentAvatar[ENTRY_AVATAR_ENTITIES] = MyAvatar.getAvatarEntitiesVariant();
|
||||
|
||||
message.reply = {
|
||||
'bookmarks' : AvatarBookmarks.getBookmarks(),
|
||||
'currentAvatar' : currentAvatar
|
||||
'currentAvatar' : getCurrentAvatar()
|
||||
};
|
||||
|
||||
console.debug('avatarapp.js: currentAvatar: ', JSON.stringify(message.reply.currentAvatar, null, '\t'))
|
||||
sendToQml(message)
|
||||
break;
|
||||
case 'adjustWearable':
|
||||
if(message.properties.localRotationAngles) {
|
||||
message.properties.localRotation = Quat.fromVec3Degrees(message.properties.localRotationAngles)
|
||||
delete message.properties.localRotationAngles;
|
||||
}
|
||||
|
||||
console.debug('Entities.editEntity(message.id, message.properties)'.replace('message.id', message.id).replace('message.properties', JSON.stringify(message.properties)));
|
||||
Entities.editEntity(message.id, message.properties);
|
||||
break;
|
||||
case 'selectAvatar':
|
||||
console.debug('avatarapp.js: selecting avatar: ', message.name);
|
||||
AvatarBookmarks.loadBookmark(message.name);
|
||||
break;
|
||||
|
||||
default:
|
||||
print('Unrecognized message from AvatarApp.qml:', JSON.stringify(message));
|
||||
}
|
||||
|
@ -52,6 +79,12 @@ function sendToQml(message) {
|
|||
tablet.sendToQml(message);
|
||||
}
|
||||
|
||||
function onBookmarkLoaded(bookmarkName) {
|
||||
var currentAvatar = getCurrentAvatar();
|
||||
console.debug('avatarapp.js: onBookmarkLoaded: ', JSON.stringify(currentAvatar, 0, 4));
|
||||
sendToQml({'method' : 'bookmarkLoaded', 'reply' : {'name' : bookmarkName, 'currentAvatar' : getCurrentAvatar()} });
|
||||
}
|
||||
|
||||
//
|
||||
// Manage the connection between the button and the window.
|
||||
//
|
||||
|
@ -69,6 +102,7 @@ function startup() {
|
|||
});
|
||||
button.clicked.connect(onTabletButtonClicked);
|
||||
tablet.screenChanged.connect(onTabletScreenChanged);
|
||||
AvatarBookmarks.bookmarkLoaded.connect(onBookmarkLoaded);
|
||||
// Window.domainChanged.connect(clearLocalQMLDataAndClosePAL);
|
||||
// Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL);
|
||||
// Users.avatarDisconnected.connect(avatarDisconnected);
|
||||
|
@ -160,6 +194,8 @@ function shutdown() {
|
|||
button.clicked.disconnect(onTabletButtonClicked);
|
||||
tablet.removeButton(button);
|
||||
tablet.screenChanged.disconnect(onTabletScreenChanged);
|
||||
AvatarBookmarks.bookmarkLoaded.disconnect(onBookmarkLoaded);
|
||||
|
||||
// Window.domainChanged.disconnect(clearLocalQMLDataAndClosePAL);
|
||||
// Window.domainConnectionRefused.disconnect(clearLocalQMLDataAndClosePAL);
|
||||
// AvatarList.avatarAddedEvent.disconnect(avatarAdded);
|
||||
|
|
Loading…
Reference in a new issue