Merge pull request #579 from Armored-Dragon/FBT-Smoothing-Fix

Avatar Smoothing
This commit is contained in:
ksuprynowicz 2023-09-22 10:27:16 +02:00 committed by GitHub
commit 57a2ebb800
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 426 additions and 2 deletions

View file

@ -219,4 +219,3 @@ Script.scriptEnding.connect(function() {
}
tablet.removeButton(tabletButton);
});

View file

@ -237,4 +237,3 @@ Script.scriptEnding.connect(function() {
}
tablet.removeButton(tabletButton);
});

View file

@ -0,0 +1,115 @@
//
// Copyright 2023 Overte e.V.
let smoothing_settings = {
enabled: true,
targets: {
LeftHand: { transform: 1, rotation: 1 },
RightHand: { transform: 1, rotation: 1 },
LeftFoot: { transform: 1, rotation: 1 },
RightFoot: { transform: 1, rotation: 1 },
Hips: { transform: 1, rotation: 1 },
Spine2: { transform: 1, rotation: 1 },
},
};
let mapping;
let mapping_settings = {
name: "org.overte.controllers.smoothing",
channels: [],
};
const html_url = Script.resolvePath("./ui/index.html");
let tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
let shown = false;
tablet.screenChanged.connect(onScreenChanged);
Script.scriptEnding.connect(shutdownTabletApp);
let tabletButton = tablet.addButton({
text: "AviSmooth",
icon: Script.resolvePath("./img/icon.png"),
activeIcon: Script.resolvePath("./img/icon-a.png"),
});
tabletButton.clicked.connect(() => {
tablet.gotoWebScreen(html_url);
});
function onScreenChanged(type, url) {
if (type !== "Web" || url !== html_url) {
tablet.webEventReceived.disconnect(onWebEventReceived);
tabletButton.editProperties({ isActive: false });
shown = false;
} else {
tabletButton.editProperties({ isActive: true });
tablet.webEventReceived.connect(onWebEventReceived);
shown = true;
smoothing_settings = Settings.getValue(
"smoothing_settings",
smoothing_settings
);
}
}
function shutdownTabletApp() {
if (mapping) mapping.disable(); // Disable custom mapping
tablet.removeButton(tabletButton); // Remove the app
tablet.screenChanged.disconnect(onScreenChanged);
tablet.webEventReceived.disconnect(onWebEventReceived);
}
const _sendMessage = (message) =>
tablet.emitScriptEvent(JSON.stringify(message));
function onWebEventReceived(message) {
message = JSON.parse(message);
if (message.action === "ready") {
_sendMessage({
action: "initialize",
data: smoothing_settings,
});
}
if (message.action === "new_settings") {
smoothing_settings = message.data;
mappingChanged();
}
if (message.action === "set_state") {
smoothing_settings.enabled = message.data;
mappingChanged();
}
}
function mappingChanged() {
Settings.setValue("smoothing_settings", smoothing_settings);
if (mapping) mapping.disable();
if (smoothing_settings.enabled) {
// Build mapping_settings
mapping_settings.channels = [];
Object.keys(smoothing_settings.targets).forEach((target) =>
mapping_settings.channels.push(_generateChannel(target))
);
function _generateChannel(name) {
return {
from: `Standard.${name}`,
to: `Actions.${name}`,
filters: [
{
type: "exponentialSmoothing",
translation: smoothing_settings.targets[name].transform,
rotation: smoothing_settings.targets[name].rotation,
},
],
};
}
mapping = Controller.parseMapping(JSON.stringify(mapping_settings));
mapping.enable();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,39 @@
body {
font-family: Verdana, Geneva, Tahoma, sans-serif;
}
.container {
margin-bottom: 10px;
}
.container .content .type {
display: grid;
grid-template-columns: 1fr 3fr 1fr;
grid-template-rows: 1fr 1fr;
width: 100%;
}
.container .content .type span {
margin: 0 10px;
}
.container .content .type .header {
grid-column-start: 1;
grid-column-end: 3;
}
.container .content .type .type-value {
text-align: center;
}
.container .content .type input {
width: 100%;
grid-column-start: 1;
grid-column-end: 3;
}
.container:last-child {
margin-bottom: 0;
}
.horizontal-button-container {
margin-top: 10px;
}
.horizontal-button-container button:focus {
outline: 0;
}

View file

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link type="text/css" rel="stylesheet" href="theme.css" />
<link type="text/css" rel="stylesheet" href="index.css" />
</head>
<body>
<div id="target-list"></div>
<div class="horizontal-button-container">
<button class="generic" onclick="applySettings()">Apply</button>
<button id="toggle-button" class="bad" onclick="toggleSmoothing()">
Disable
</button>
</div>
</body>
<template id="target-template">
<div data-name="[BONE/TARGET NAME]" class="container color-primary">
<div class="container-header color-secondary">
[BONE/TARGET NAME PRETTY]
</div>
<div class="content">
<div class="type" data-value="rotation">
<span class="header">Rotation</span>
<input type="range" min="1" max="100" value="100" />
<span class="type-value">[VALUE % PRETTY]</span>
</div>
<div class="type" data-value="transform">
<span class="header">Transform</span>
<input type="range" min="1" max="100" value="100" />
<span class="type-value">[VALUE % PRETTY]</span>
</div>
</div>
</div>
</template>
<script src="./index.js"></script>
</html>

View file

@ -0,0 +1,107 @@
// Helper functions
const qs = (target) => document.querySelector(target);
const qsa = (target) => document.querySelectorAll(target);
// Message listeners
const _sendMessage = (message) =>
EventBridge.emitWebEvent(JSON.stringify(message));
EventBridge.scriptEventReceived.connect((message) =>
newMessage(JSON.parse(message))
);
// Settings & data
let smoothing_settings = {};
function newMessage(message) {
if (message.action === "initialize") {
initialize(message.data);
}
}
function initialize(data) {
smoothing_settings = data;
// Clear all existing listings (if any)
qsa("body .target-list").forEach((item) => item.remove());
// Set state
if (smoothing_settings.enabled === false) _toggleEnabledFalse();
if (smoothing_settings.enabled === true) _toggleEnabledTrue();
// For each target point
Object.keys(smoothing_settings.targets).forEach((target) => {
// Use the target data to build a listing
let template = qs("#target-template").content.cloneNode(true);
template.querySelector(".container").dataset.name = target;
template.querySelector(".container-header").innerText = target;
const rotation_area = template.querySelector('[data-value="rotation"]');
const transform_area = template.querySelector(
'[data-value="transform"]'
);
rotation_area.querySelector("input").value = _fromDecimal(
smoothing_settings.targets[target].rotation
);
transform_area.querySelector("input").value = _fromDecimal(
smoothing_settings.targets[target].transform
);
rotation_area.querySelector(".type-value").innerText = _formatPercent(
_fromDecimal(smoothing_settings.targets[target].rotation)
);
transform_area.querySelector(".type-value").innerText = _formatPercent(
_fromDecimal(smoothing_settings.targets[target].transform)
);
rotation_area.querySelector("input").addEventListener("change", () => {
rotation_area.querySelector(".type-value").innerText =
_formatPercent(rotation_area.querySelector("input").value);
smoothing_settings.targets[target].rotation = _toDecimal(
rotation_area.querySelector("input").value
);
});
transform_area.querySelector("input").addEventListener("change", () => {
transform_area.querySelector(".type-value").innerText =
_formatPercent(transform_area.querySelector("input").value);
smoothing_settings.targets[target].transform = _toDecimal(
transform_area.querySelector("input").value
);
});
// // Append our newly created child
qs("#target-list").appendChild(template);
});
}
qsa("input").forEach((button) =>
button.addEventListener("click", (event) => event.target.blur())
);
function toggleSmoothing() {
if (smoothing_settings.enabled) _toggleEnabledFalse();
else _toggleEnabledTrue();
}
function _toggleEnabledFalse() {
_sendMessage({ action: "set_state", data: false });
qs("#toggle-button").classList.remove("bad");
qs("#toggle-button").classList.add("good");
qs("#toggle-button").innerText = "Enable";
smoothing_settings.enabled = false;
}
function _toggleEnabledTrue() {
_sendMessage({ action: "set_state", data: true });
qs("#toggle-button").classList.remove("good");
qs("#toggle-button").classList.add("bad");
qs("#toggle-button").innerText = "Disable";
smoothing_settings.enabled = true;
}
_sendMessage({ action: "ready" });
const applySettings = () =>
_sendMessage({ action: "new_settings", data: smoothing_settings });
const _formatPercent = (value) => parseInt(value).toString() + " %";
const _toDecimal = (value) => value / 100;
const _fromDecimal = (value) => value * 100;

View file

@ -0,0 +1,47 @@
body {
font-family: Verdana, Geneva, Tahoma, sans-serif;
}
.container {
margin-bottom: 10px;
.content {
.type {
display: grid;
grid-template-columns: 1fr 3fr 1fr;
grid-template-rows: 1fr 1fr;
width: 100%;
span {
margin: 0 10px;
}
.header {
grid-column-start: 1;
grid-column-end: 3;
}
.type-value {
text-align: center;
}
input {
width: 100%;
grid-column-start: 1;
grid-column-end: 3;
}
}
}
}
.container:last-child {
margin-bottom: 0;
}
.horizontal-button-container {
margin-top: 10px;
button:focus{
outline: 0;
}
}

View file

@ -0,0 +1,75 @@
body {
margin: 2px;
box-sizing: border-box;
background-color: #101010;
font-size: 16px;
color: white;
}
.color-primary {
background-color: #212121;
}
.color-secondary {
background-color: #333333;
}
.color-tertiary {
background-color: #454545;
}
.container {
width: 100%;
min-height: 2px;
}
.container .container-header {
font-size: 18px;
padding: 0.5rem 1rem;
box-sizing: border-box;
margin-bottom: 5px;
}
.container .content {
padding: 0 10px;
}
.horizontal-button-container {
display: flex;
flex-direction: row;
}
.horizontal-button-container button {
width: 100%;
margin: auto;
margin-right: 10px;
}
.horizontal-button-container button:last-child {
margin-right: 0;
}
button {
border: 0;
padding: 1rem;
box-sizing: border-box;
background-color: #696e6f;
color: white;
cursor: pointer;
}
button:hover {
filter: brightness(70%);
}
button:active {
filter: brightness(50%);
}
button.good {
background-color: #077e30;
}
button.bad {
background-color: #771d1d;
}
button.generic {
background-color: #505186;
}