Fixes rotation glitch (hopefully).
Fixes nametag flashing on update.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
This commit is contained in:
Armored Dragon 2024-02-23 10:49:40 -06:00
parent 721075b34b
commit 14766a792b
No known key found for this signature in database
GPG key ID: C7207ACC3382AD8B

View file

@ -1,56 +1,79 @@
// //
// Copyright 2023 Overte e.V. // Copyright 2024 Overte e.V.
// //
// Written by Armored Dragon // Written by Armored Dragon
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
let user_nametags = {}; (function () {
let user_uuids = []; "use strict";
let visible = Settings.getValue("Nametags_toggle", true); let user_nametags = {};
let maximum_name_length = 50; let visible = Settings.getValue("Nametags_toggle", true);
let last_camera_mode = Camera.mode; let maximum_name_length = 50;
let check_interval; let last_camera_mode = Camera.mode;
const logs = (info) => console.log("[NAMETAGS] " + info); _updateList();
// New user connected AvatarManager.avatarAddedEvent.connect(_addUser); // New user connected
AvatarManager.avatarAddedEvent.connect(reset); AvatarManager.avatarRemovedEvent.connect(_removeUser); // User disconnected
// User disconnected Script.update.connect(_adjustNametags); // Delta time
AvatarManager.avatarRemovedEvent.connect(reset);
function reset() { Script.scriptEnding.connect(_scriptEnding); // Script was uninstalled
if (!visible) return; Menu.menuItemEvent.connect(_toggleState); // Toggle the nametag
clear();
startup();
}
function startup() { // Toolbar icon
let tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
let tabletButton = tablet.addButton({
icon: Script.resolvePath("./assets/nametags-i.svg"),
activeIcon: Script.resolvePath("./assets/nametags-a.svg"),
text: "NAMETAGS",
isActive: visible,
});
tabletButton.clicked.connect(_toggleState);
// Menu item
Menu.addMenuItem({
menuName: "View",
menuItemName: "Nametags",
shortcutKey: "CTRL+N",
isCheckable: true,
isChecked: visible,
});
function _updateList() {
const include_self = !HMD.active && !Camera.mode.includes("first person"); const include_self = !HMD.active && !Camera.mode.includes("first person");
var user_list = AvatarList.getAvatarIdentifiers();
if (include_self) user_list.push(MyAvatar.sessionUUID);
user_uuids = AvatarList.getAvatarIdentifiers(); // Filter undefined values out
if (include_self) user_uuids.push(MyAvatar.sessionUUID); user_list = user_list.filter((uuid) => uuid);
user_uuids = user_uuids.filter((uuid) => uuid); // Remove empty, undefined values from array
user_uuids.forEach((avatar) => { user_list.forEach(_addUser);
let uuid = avatar; }
if (user_nametags[uuid]) return;
const definite_avatar = AvatarList.getAvatar(uuid);
const display_name = definite_avatar.displayName ? definite_avatar.displayName.substring(0, maximum_name_length) : "Anonymous";
const headJointIndex = definite_avatar.getJointIndex("Head"); // Add a user to the user list
const jointInObjectFrame = definite_avatar.getAbsoluteJointTranslationInObjectFrame(headJointIndex); function _addUser(user_uuid) {
if (!visible) return;
if (user_nametags[user_uuid]) return;
user_nametags[uuid] = { overlay: { text: {}, background: {} } }; const user = AvatarList.getAvatar(user_uuid);
user_nametags[uuid].overlay.text = Entities.addEntity( const display_name = user.displayName ? user.displayName.substring(0, maximum_name_length) : "Anonymous";
const headJointIndex = user.getJointIndex("Head");
const jointInObjectFrame = user.getAbsoluteJointTranslationInObjectFrame(headJointIndex);
console.log(`Registering ${display_name} (${user_uuid}) nametag`);
user_nametags[user_uuid] = { text: {}, background: {} };
user_nametags[user_uuid].text = Entities.addEntity(
{ {
type: "Text", type: "Text",
text: display_name, text: display_name,
backgroundAlpha: 0.0, backgroundAlpha: 0.0,
billboardMode: "full", billboardMode: "full",
unlit: true, unlit: true,
parentID: uuid, parentID: user_uuid,
position: Vec3.sum(definite_avatar.position, { x: 0, y: 0.4 + jointInObjectFrame.y, z: 0 }), position: Vec3.sum(user.position, { x: 0, y: 0.4 + jointInObjectFrame.y, z: 0 }),
visible: true, visible: true,
isSolid: false, isSolid: false,
topMargin: 0.025, topMargin: 0.025,
@ -59,15 +82,15 @@ function startup() {
}, },
"local" "local"
); );
user_nametags[uuid].overlay.background = Entities.addEntity( user_nametags[user_uuid].background = Entities.addEntity(
{ {
type: "Image", type: "Image",
dimensions: { x: 0.8, y: 0.2, z: 0.1 }, dimensions: { x: 0.8, y: 0.2, z: 0.1 },
emissive: true, emissive: true,
alpha: 0.8, alpha: 0.8,
keepAspectRatio: false, keepAspectRatio: false,
position: Vec3.sum(definite_avatar.position, { x: 0, y: 0.4 + jointInObjectFrame.y, z: 0 }), position: Vec3.sum(user.position, { x: 0, y: 0.4 + jointInObjectFrame.y, z: 0 }),
parentID: user_nametags[uuid].overlay.text, parentID: user_nametags[user_uuid].text,
billboardMode: "full", billboardMode: "full",
imageURL: Script.resolvePath("./assets/badge.svg"), imageURL: Script.resolvePath("./assets/badge.svg"),
}, },
@ -77,103 +100,60 @@ function startup() {
// We need to have this on a timeout because "textSize" can not be determined instantly after the entity was created. // We need to have this on a timeout because "textSize" can not be determined instantly after the entity was created.
// https://apidocs.overte.org/Entities.html#.textSize // https://apidocs.overte.org/Entities.html#.textSize
Script.setTimeout(() => { Script.setTimeout(() => {
let textSize = Entities.textSize(user_nametags[uuid].overlay.text, display_name); let textSize = Entities.textSize(user_nametags[user_uuid].text, display_name);
Entities.editEntity(user_nametags[uuid].overlay.text, { dimensions: { x: textSize.width + 0.25, y: textSize.height - 0.05, z: 0.1 } }); Entities.editEntity(user_nametags[user_uuid].text, { dimensions: { x: textSize.width + 0.25, y: textSize.height - 0.05, z: 0.1 } });
Entities.editEntity(user_nametags[uuid].overlay.background, { Entities.editEntity(user_nametags[user_uuid].background, {
dimensions: { x: Math.max(textSize.width + 0.25, 0.6), y: textSize.height - 0.05, z: 0.1 }, dimensions: { x: Math.max(textSize.width + 0.25, 0.6), y: textSize.height - 0.05, z: 0.1 },
}); });
}, 100); }, 100);
check_interval = Script.setInterval(adjustNameTag, 5000);
});
}
function clear() {
for (let i = 0; Object.keys(user_nametags).length > i; i++) {
Entities.deleteEntity(user_nametags[Object.keys(user_nametags)[i]].overlay.text);
Entities.deleteEntity(user_nametags[Object.keys(user_nametags)[i]].overlay.background);
} }
user_uuids = {};
user_nametags = {};
Script.clearInterval(adjustNameTag);
}
function adjustNameTag() {
const user_list = Object.keys(user_nametags);
user_list.forEach((uuid) => { // Remove a user from the user list
const definite_avatar = AvatarList.getAvatar(uuid); function _removeUser(user_uuid) {
const display_name = definite_avatar.displayName ? definite_avatar.displayName.substring(0, maximum_name_length) : "Anonymous"; console.log(`Deleting ${user_uuid} nametag`);
const headJointIndex = definite_avatar.getJointIndex("Head"); Entities.deleteEntity(user_nametags[user_uuid].text);
const jointInObjectFrame = definite_avatar.getAbsoluteJointTranslationInObjectFrame(headJointIndex); Entities.deleteEntity(user_nametags[user_uuid].background);
delete user_nametags[user_uuid];
Entities.editEntity(user_nametags[uuid].overlay.background, { }
position: Vec3.sum(definite_avatar.position, { x: 0, y: 0.4 + jointInObjectFrame.y, z: 0 }),
});
Entities.editEntity(user_nametags[uuid].overlay.text, {
position: Vec3.sum(definite_avatar.position, { x: 0, y: 0.4 + jointInObjectFrame.y, z: 0 }),
text: display_name,
});
// // We need to have this on a timeout because "textSize" can not be determined instantly after the entity was created.
// // https://apidocs.overte.org/Entities.html#.textSize
Script.setTimeout(() => {
let textSize = Entities.textSize(user_nametags[uuid].overlay.text, display_name);
Entities.editEntity(user_nametags[uuid].overlay.text, { dimensions: { x: textSize.width + 0.25, y: textSize.height - 0.05, z: 0.1 } });
Entities.editEntity(user_nametags[uuid].overlay.background, {
dimensions: { x: Math.max(textSize.width + 0.25, 0.6), y: textSize.height - 0.05, z: 0.1 },
});
}, 100);
});
// Updates positions of existing nametags
function _adjustNametags() {
if (last_camera_mode !== Camera.mode) { if (last_camera_mode !== Camera.mode) {
reset(); if (Camera.mode.includes("first person")) _removeUser(MyAvatar.sessionUUID);
else _addUser(MyAvatar.sessionUUID);
last_camera_mode = Camera.mode; last_camera_mode = Camera.mode;
} }
}
function scriptEnding() { Object.keys(user_nametags).forEach((user_uuid) => {
clear(); const user = AvatarList.getAvatar(user_uuid);
const display_name = user.displayName ? user.displayName.substring(0, maximum_name_length) : "Anonymous";
const headJointIndex = user.getJointIndex("Head");
const jointInObjectFrame = user.getAbsoluteJointTranslationInObjectFrame(headJointIndex);
Entities.editEntity(user_nametags[user_uuid].text, {
position: Vec3.sum(user.position, { x: 0, y: jointInObjectFrame.y + Math.abs(user.scale - 1) + 0.4, z: 0 }),
text: display_name,
});
});
}
// Enable or disable nametags
function _toggleState() {
visible = !visible;
tabletButton.editProperties({ isActive: visible });
Settings.setValue("Nametags_toggle", visible);
if (!visible) Object.keys(user_nametags).forEach(_removeUser);
if (visible) _updateList();
}
function _scriptEnding() {
tablet.removeButton(tabletButton); tablet.removeButton(tabletButton);
Menu.removeMenuItem("View", "Nametags"); Menu.removeMenuItem("View", "Nametags");
}
function toggleState() {
visible = !visible;
tabletButton.editProperties({ isActive: visible }); for (let i = 0; Object.keys(user_nametags).length > i; i++) {
Entities.deleteEntity(user_nametags[Object.keys(user_nametags)[i]].text);
clear(); Entities.deleteEntity(user_nametags[Object.keys(user_nametags)[i]].background);
}
if (visible) startup(); user_nametags = {};
Settings.setValue("Nametags_toggle", visible); }
} })();
function toggleStateMenu() {
let is_checked = Menu.isOptionChecked("Nametags");
if (is_checked !== visible) toggleState();
// Toolbar
tabletButton.editProperties({ isActive: visible });
}
// Tablet icon
let tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
let tabletButton = tablet.addButton({
icon: Script.resolvePath("./assets/nametags-i.svg"),
activeIcon: Script.resolvePath("./assets/nametags-a.svg"),
text: "NAMETAGS",
isActive: visible,
});
// Menu item
Menu.addMenuItem({
menuName: "View",
menuItemName: "Nametags",
shortcutKey: "CTRL+N",
isCheckable: true,
isChecked: visible,
});
Menu.menuItemEvent.connect(toggleStateMenu);
tabletButton.clicked.connect(toggleState);
Script.scriptEnding.connect(scriptEnding);
if (visible) {
startup();
tabletButton.editProperties({ isActive: visible });
toggleStateMenu();
}