merge master

This commit is contained in:
luiscuenca 2019-09-11 14:07:19 -07:00
commit 7c7c3cd223
No known key found for this signature in database
GPG key ID: 2387ECD129A6961D
79 changed files with 947 additions and 249 deletions

View file

@ -84,7 +84,7 @@ Agent::Agent(ReceivedMessage& message) :
DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
DependencyManager::set<ResourceManager>();
DependencyManager::set<PluginManager>();
DependencyManager::set<PluginManager>()->instantiate();
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
@ -511,6 +511,7 @@ void Agent::executeScript() {
DependencyManager::set<AssignmentParentFinder>(_entityViewer.getTree());
DependencyManager::get<ScriptEngines>()->runScriptInitializers(_scriptEngine);
_scriptEngine->run();
Frame::clearFrameHandler(AUDIO_FRAME_TYPE);

View file

@ -22,6 +22,7 @@
#include <HifiConfigVariantMap.h>
#include <SharedUtil.h>
#include <ShutdownEventListener.h>
#include <shared/ScriptInitializerMixin.h>
#include "Assignment.h"
#include "AssignmentClient.h"
@ -240,6 +241,7 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
QThread::currentThread()->setObjectName("main thread");
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
DependencyManager::set<ScriptInitializers>();
if (numForks || minForks || maxForks) {
AssignmentClientMonitor* monitor = new AssignmentClientMonitor(numForks, minForks, maxForks,

View file

@ -499,6 +499,8 @@ void AvatarMixer::handleAvatarKilled(SharedNodePointer avatarNode) {
} else {
_sessionDisplayNames.erase(displayNameIter);
}
nodeData->getAvatar().stopChallengeTimer();
}
std::unique_ptr<NLPacket> killPacket;

View file

@ -332,7 +332,7 @@ void MixerAvatar::sendOwnerChallenge() {
void MixerAvatar::processChallengeResponse(ReceivedMessage& response) {
QByteArray avatarID;
QMutexLocker certifyLocker(&_avatarCertifyLock);
QMetaObject::invokeMethod(&_challengeTimer, &QTimer::stop);
stopChallengeTimer();
if (_verifyState == challengeClient) {
QByteArray responseData = response.readAll();
if (responseData.length() < 8) {
@ -365,3 +365,11 @@ void MixerAvatar::processChallengeResponse(ReceivedMessage& response) {
qCDebug(avatars) << "WARNING: Unexpected avatar challenge-response in state" << stateToName(_verifyState);
}
}
void MixerAvatar::stopChallengeTimer() {
if (QThread::currentThread() == thread()) {
_challengeTimer.stop();
} else {
QMetaObject::invokeMethod(&_challengeTimer, &QTimer::stop);
}
}

View file

@ -34,6 +34,8 @@ public:
void processCertifyEvents();
void processChallengeResponse(ReceivedMessage& response);
void stopChallengeTimer();
// Avatar certification/verification:
enum VerifyState {
nonCertified, requestingFST, receivedFST, staticValidation, requestingOwner, ownerResponse,

View file

@ -17,9 +17,9 @@
#include <QJsonDocument>
#include <EntityTree.h>
#include <SimpleEntitySimulation.h>
#include <ResourceCache.h>
#include <ScriptCache.h>
#include <plugins/PluginManager.h>
#include <EntityEditFilters.h>
#include <NetworkingConstants.h>
#include <hfm/ModelFormatRegistry.h>
@ -36,12 +36,13 @@ const char* LOCAL_MODELS_PERSIST_FILE = "resources/models.svo";
EntityServer::EntityServer(ReceivedMessage& message) :
OctreeServer(message),
_entitySimulation(NULL),
_entitySimulation(nullptr),
_dynamicDomainVerificationTimer(this)
{
DependencyManager::set<ResourceManager>();
DependencyManager::set<ResourceCacheSharedItems>();
DependencyManager::set<ScriptCache>();
DependencyManager::set<PluginManager>()->instantiate();
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, AssignmentDynamicFactory>();
DependencyManager::set<AssignmentDynamicFactory>();

View file

@ -16,9 +16,11 @@
#include <memory>
#include "EntityItem.h"
#include <EntityItem.h>
#include <EntityTree.h>
#include <SimpleEntitySimulation.h>
#include "EntityServerConsts.h"
#include "EntityTree.h"
/// Handles assignments of type EntityServer - sending entities to various clients.
@ -27,9 +29,6 @@ struct ViewerSendingStats {
quint64 lastEdited;
};
class SimpleEntitySimulation;
using SimpleEntitySimulationPointer = std::shared_ptr<SimpleEntitySimulation>;
class EntityServer : public OctreeServer, public NewlyCreatedEntityHook {
Q_OBJECT
public:

View file

@ -64,7 +64,7 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
DependencyManager::set<ResourceScriptingInterface>();
DependencyManager::set<ResourceManager>();
DependencyManager::set<PluginManager>();
DependencyManager::set<PluginManager>()->instantiate();
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
@ -301,10 +301,17 @@ void EntityScriptServer::run() {
entityScriptingInterface->setEntityTree(_entityViewer.getTree());
DependencyManager::set<AssignmentParentFinder>(_entityViewer.getTree());
auto treePtr = _entityViewer.getTree();
DependencyManager::set<AssignmentParentFinder>(treePtr);
if (!_entitySimulation) {
SimpleEntitySimulationPointer simpleSimulation { new SimpleEntitySimulation() };
simpleSimulation->setEntityTree(treePtr);
treePtr->setSimulation(simpleSimulation);
_entitySimulation = simpleSimulation;
}
auto tree = _entityViewer.getTree().get();
auto tree = treePtr.get();
connect(tree, &EntityTree::deletingEntity, this, &EntityScriptServer::deletingEntity, Qt::QueuedConnection);
connect(tree, &EntityTree::addingEntity, this, &EntityScriptServer::addingEntity, Qt::QueuedConnection);
connect(tree, &EntityTree::entityServerScriptChanging, this, &EntityScriptServer::entityServerScriptChanging, Qt::QueuedConnection);
@ -451,10 +458,11 @@ void EntityScriptServer::resetEntitiesScriptEngine() {
connect(newEngine.data(), &ScriptEngine::update, this, [this] {
_entityViewer.queryOctree();
_entityViewer.getTree()->preUpdate();
_entityViewer.getTree()->update();
});
scriptEngines->runScriptInitializers(newEngine);
newEngine->runInThread();
auto newEngineSP = qSharedPointerCast<EntitiesScriptEngineProvider>(newEngine);
DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(newEngineSP);

View file

@ -21,6 +21,7 @@
#include <EntityEditPacketSender.h>
#include <plugins/CodecPlugin.h>
#include <ScriptEngine.h>
#include <SimpleEntitySimulation.h>
#include <ThreadedAssignment.h>
#include "../entities/EntityTreeHeadlessViewer.h"
@ -75,6 +76,7 @@ private:
static int _entitiesScriptEngineCount;
ScriptEnginePointer _entitiesScriptEngine;
SimpleEntitySimulationPointer _entitySimulation;
EntityEditPacketSender _entityEditSender;
EntityTreeHeadlessViewer _entityViewer;

View file

@ -38,7 +38,7 @@ macro(SET_PACKAGING_PARAMETERS)
set(BUILD_ORGANIZATION "High Fidelity")
set(HIGH_FIDELITY_PROTOCOL "hifi")
set(HIGH_FIDELITY_APP_PROTOCOL "hifiapp")
set(INTERFACE_BUNDLE_NAME "Interface")
set(INTERFACE_BUNDLE_NAME "interface")
set(INTERFACE_ICON_PREFIX "interface")
# add definition for this release type
@ -61,7 +61,7 @@ macro(SET_PACKAGING_PARAMETERS)
set(PR_BUILD 1)
set(BUILD_VERSION "PR${RELEASE_NUMBER}")
set(BUILD_ORGANIZATION "High Fidelity - PR${RELEASE_NUMBER}")
set(INTERFACE_BUNDLE_NAME "Interface")
set(INTERFACE_BUNDLE_NAME "interface")
set(INTERFACE_ICON_PREFIX "interface-beta")
# add definition for this release type
@ -70,7 +70,7 @@ macro(SET_PACKAGING_PARAMETERS)
set(DEV_BUILD 1)
set(BUILD_VERSION "dev")
set(BUILD_ORGANIZATION "High Fidelity - ${BUILD_VERSION}")
set(INTERFACE_BUNDLE_NAME "Interface")
set(INTERFACE_BUNDLE_NAME "interface")
set(INTERFACE_ICON_PREFIX "interface-beta")
# add definition for this release type

View file

@ -7,7 +7,8 @@
#
macro(SETUP_HIFI_CLIENT_SERVER_PLUGIN)
set(${TARGET_NAME}_SHARED 1)
setup_hifi_library(${ARGV})
set(PLUGIN_SUBFOLDER ${ARGN})
setup_hifi_library()
if (BUILD_CLIENT)
add_dependencies(interface ${TARGET_NAME})
@ -27,6 +28,11 @@ macro(SETUP_HIFI_CLIENT_SERVER_PLUGIN)
set(SERVER_PLUGIN_PATH "plugins")
endif()
if (PLUGIN_SUBFOLDER)
set(CLIENT_PLUGIN_PATH "${CLIENT_PLUGIN_PATH}/${PLUGIN_SUBFOLDER}")
set(SERVER_PLUGIN_PATH "${SERVER_PLUGIN_PATH}/${PLUGIN_SUBFOLDER}")
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_GENERATOR STREQUAL "Unix Makefiles")
set(CLIENT_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/${CLIENT_PLUGIN_PATH}/")
set(SERVER_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/assignment-client/${SERVER_PLUGIN_PATH}/")

View file

@ -1156,7 +1156,17 @@ FunctionEnd
Section "-Core installation"
;The following delete blocks are temporary and can be removed once users who had the initial installer have updated
; 2016-02-25 - The following delete blocks are temporary and can be removed once users who had the initial installer have updated
; 2019-09-10 - (3 and a half years later) Sure they are buddy. Sure they are.
; MessageBox MB_OK|MB_ICONEXCLAMATION "installer type is @INSTALLER_TYPE@"
;Delete any server executables that might have been installed by bad versions of the client-only installer, but ONLY if we are a client-only installer
${If} "@INSTALLER_TYPE@" == "client_only"
; MessageBox MB_OK|MB_ICONEXCLAMATION "trying to delete server binaries"
Delete "$INSTDIR\assignment-client.exe"
Delete "$INSTDIR\domain-server.exe"
${EndIf}
;Delete any server-console files installed before it was placed in sub-folder
Delete "$INSTDIR\server-console.exe"

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>high-fidelity.hifi</string>
</array>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>

View file

@ -824,6 +824,19 @@
},
"id": "seatedReactionPositiveCheer",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 64,
"loopFlag": false,
"startFrame": 1,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_agree_acknowledge.fbx"
},
"id": "seatedReactionPositiveAcknowledge",
"type": "clip"
}
],
"data": {
@ -869,6 +882,15 @@
"resume": false,
"transitions": [
]
},
{
"id": "seatedReactionPositiveAcknowledge",
"interpDuration": 1,
"interpTarget": 1,
"priority": 1,
"resume": false,
"transitions": [
]
}
],
"timeScale": 1,
@ -918,6 +940,19 @@
},
"id": "seatedReactionNegativeDisagreeDisbelief",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 70,
"loopFlag": false,
"startFrame": 0,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_disagree_dismiss.fbx"
},
"id": "seatedReactionNegativeDisagreeDismiss",
"type": "clip"
}
],
"data": {
@ -960,6 +995,17 @@
"resume": false,
"transitions": [
]
},
{
"easingType": "easeInOutQuad",
"id": "seatedReactionNegativeDisagreeDismiss",
"interpDuration": 1,
"interpTarget": 1,
"interpType": "evaluateBoth",
"priority": 1,
"resume": false,
"transitions": [
]
}
],
"timeScale": 1,
@ -1048,6 +1094,47 @@
},
"id": "seatedReactionRaiseHand02Outro",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 15,
"loopFlag": false,
"startFrame": 0,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_raisehand03_all.fbx"
},
"id": "seatedReactionRaiseHand03Intro",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 233,
"loopFlag": true,
"mirrorFlag": false,
"startFrame": 15,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_raisehand03_all.fbx"
},
"id": "seatedReactionRaiseHand03Loop",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 296,
"loopFlag": false,
"mirrorFlag": false,
"startFrame": 233,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_raisehand03_all.fbx"
},
"id": "seatedReactionRaiseHand03Outro",
"type": "clip"
}
],
"data": {
@ -1059,7 +1146,7 @@
"easingType": "easeInOutQuad",
"id": "seatedReactionRaiseHandIntro",
"interpDuration": 8,
"interpTarget": 8,
"interpTarget": 9,
"interpType": "evaluateBoth",
"priority": 1,
"resume": false,
@ -1140,6 +1227,49 @@
"var": "reactionRaiseHandEnabled"
}
]
},
{
"easingType": "easeInOutQuad",
"id": "seatedReactionRaiseHand03Intro",
"interpDuration": 8,
"interpTarget": 8,
"interpType": "evaluateBoth",
"priority": 1,
"resume": false,
"transitions": [
{
"randomSwitchState": "seatedReactionRaiseHand03Loop",
"var": "seatedReactionRaiseHand03IntroOnDone"
}
]
},
{
"id": "seatedReactionRaiseHand03Loop",
"interpDuration": 1,
"interpTarget": 1,
"priority": 0,
"resume": false,
"transitions": [
{
"randomSwitchState": "seatedReactionRaiseHand03Outro",
"var": "reactionRaiseHandDisabled"
}
]
},
{
"easingType": "easeInOutQuad",
"id": "seatedReactionRaiseHand03Outro",
"interpDuration": 12,
"interpTarget": 12,
"interpType": "evaluateBoth",
"priority": 0,
"resume": false,
"transitions": [
{
"randomSwitchState": "seatedReactionRaiseHand03Loop",
"var": "reactionRaiseHandEnabled"
}
]
}
],
"triggerRandomSwitch": ""
@ -1226,6 +1356,45 @@
},
"id": "seatedReactionApplaud02Outro",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 17,
"loopFlag": false,
"startFrame": 0,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_clap03_all.fbx"
},
"id": "seatedReactionApplaud03Intro",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 111,
"loopFlag": true,
"startFrame": 17,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_clap03_all.fbx"
},
"id": "seatedReactionApplaud03Loop",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 136,
"loopFlag": false,
"startFrame": 111,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_clap03_all.fbx"
},
"id": "seatedReactionApplaud03Outro",
"type": "clip"
}
],
"data": {
@ -1322,6 +1491,51 @@
"var": "reactionApplaudEnabled"
}
]
},
{
"easingType": "easeInOutQuad",
"id": "seatedReactionApplaud03Intro",
"interpDuration": 8,
"interpTarget": 8,
"interpType": "evaluateBoth",
"priority": 1,
"resume": false,
"transitions": [
{
"randomSwitchState": "seatedReactionApplaud03Loop",
"var": "seatedReactionApplaud03IntroOnDone"
}
]
},
{
"easingType": "easeInOutQuad",
"id": "seatedReactionApplaud03Loop",
"interpDuration": 1,
"interpTarget": 1,
"interpType": "evaluateBoth",
"priority": 0,
"resume": false,
"transitions": [
{
"randomSwitchState": "seatedReactionApplaud03Outro",
"var": "reactionApplaudDisabled"
}
]
},
{
"easingType": "easeInOutQuad",
"id": "seatedReactionApplaud03Outro",
"interpDuration": 12,
"interpTarget": 12,
"interpType": "evaluateBoth",
"priority": 0,
"resume": false,
"transitions": [
{
"randomSwitchState": "seatedReactionApplaud03Loop",
"var": "reactionApplaudEnabled"
}
]
}
],
"triggerRandomSwitch": ""
@ -1335,7 +1549,7 @@
"children": [
],
"data": {
"endFrame": 22,
"endFrame": 21,
"loopFlag": false,
"startFrame": 1,
"timeScale": 1,
@ -1348,9 +1562,9 @@
"children": [
],
"data": {
"endFrame": 43,
"endFrame": 100,
"loopFlag": true,
"startFrame": 22,
"startFrame": 21,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_all.fbx"
},
@ -1361,9 +1575,10 @@
"children": [
],
"data": {
"endFrame": 71,
"endFrame": 134,
"loopFlag": false,
"startFrame": 43,
"mirrorFlag": false,
"startFrame": 100,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_all.fbx"
},
@ -1483,6 +1698,10 @@
"state": "seatedTalkOverlay",
"var": "seatedReactionPositiveCheerOnDone"
},
{
"state": "seatedTalkOverlay",
"var": "seatedReactionPositiveAcknowledgeOnDone"
},
{
"state": "seatedReactionNegative",
"var": "reactionNegativeTrigger"
@ -1524,6 +1743,10 @@
"state": "seatedTalkOverlay",
"var": "seatedReactionNegativeDisagreeDisbeliefOnDone"
},
{
"state": "seatedTalkOverlay",
"var": "seatedReactionNegativeDisagreeDismissOnDone"
},
{
"state": "seatedReactionRaiseHand",
"var": "reactionRaiseHandEnabled"
@ -2432,7 +2655,7 @@
"children": [
],
"data": {
"endFrame": 58,
"endFrame": 64,
"loopFlag": false,
"startFrame": 1,
"timeScale": 1,
@ -2458,7 +2681,7 @@
"children": [
],
"data": {
"endFrame": 78,
"endFrame": 94,
"loopFlag": false,
"startFrame": 1,
"timeScale": 1,
@ -2471,7 +2694,7 @@
"children": [
],
"data": {
"endFrame": 66,
"endFrame": 68,
"loopFlag": false,
"startFrame": 1,
"timeScale": 1,
@ -2484,7 +2707,7 @@
"children": [
],
"data": {
"endFrame": 85,
"endFrame": 84,
"loopFlag": false,
"startFrame": 1,
"timeScale": 1,
@ -2981,7 +3204,7 @@
"children": [
],
"data": {
"endFrame": 18,
"endFrame": 17,
"loopFlag": false,
"startFrame": 1,
"timeScale": 1,
@ -2994,9 +3217,9 @@
"children": [
],
"data": {
"endFrame": 97,
"endFrame": 111,
"loopFlag": true,
"startFrame": 18,
"startFrame": 17,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_clap01_all.fbx"
},
@ -3007,9 +3230,9 @@
"children": [
],
"data": {
"endFrame": 146,
"endFrame": 160,
"loopFlag": false,
"startFrame": 97,
"startFrame": 111,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_clap01_all.fbx"
},
@ -3310,7 +3533,7 @@
"children": [
],
"data": {
"endFrame": 22,
"endFrame": 21,
"loopFlag": false,
"startFrame": 1,
"timeScale": 1,
@ -3323,9 +3546,9 @@
"children": [
],
"data": {
"endFrame": 40,
"endFrame": 100,
"loopFlag": true,
"startFrame": 22,
"startFrame": 21,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_all.fbx"
},
@ -3336,9 +3559,9 @@
"children": [
],
"data": {
"endFrame": 78,
"endFrame": 134,
"loopFlag": false,
"startFrame": 40,
"startFrame": 100,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_all.fbx"
},
@ -3407,8 +3630,8 @@
{
"easingType": "easeInOutQuad",
"id": "idleTalkOverlay",
"interpDuration": 24,
"interpTarget": 24,
"interpDuration": 20,
"interpTarget": 20,
"interpType": "evaluateBoth",
"transitions": [
{
@ -5648,4 +5871,4 @@
"type": "blendLinear"
},
"version": "1.1"
}
}

View file

@ -1,4 +1,4 @@
<!-- Copyright 2016 High Fidelity, Inc. -->
<!-- Copyright 2016 High Fidelity, Inc. -->
<html>
<head>
<meta charset="utf-8"/>
@ -77,9 +77,9 @@
var handControllerImageURL = null;
var index = 0;
var count = 3;
var handControllerRefURL = "https://docs.highfidelity.com/en/rc81/explore/get-started/vr-controls.html#vr-controls";
var keyboardRefURL = "https://docs.highfidelity.com/en/rc81/explore/get-started/desktop.html#movement-controls";
var gamepadRefURL = "https://docs.highfidelity.com/en/rc81/explore/get-started/vr-controls.html#gamepad";
var handControllerRefURL = "https://docs.highfidelity.com/explore/get-started/vr-controls.html#vr-controls";
var keyboardRefURL = "https://docs.highfidelity.com/explore/get-started/desktop.html#movement-controls";
var gamepadRefURL = "https://docs.highfidelity.com/explore/get-started/vr-controls.html#gamepad";
function showKbm() {
document.getElementById("main_image").setAttribute("src", "img/tablet-help-keyboard.jpg");

View file

@ -240,9 +240,96 @@ Rectangle {
}
}
// -- Plugin Permissions --
Item {
id: kpiContainer;
anchors.top: accountContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: childrenRect.height;
Rectangle {
id: kpiHeaderContainer;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 55;
color: hifi.colors.baseGrayHighlight;
HifiStylesUit.RalewaySemiBold {
text: "Plugin Permissions";
anchors.fill: parent;
anchors.leftMargin: 20;
color: hifi.colors.white;
size: 18;
}
}
Item {
id: kpiScriptContainer;
anchors.top: kpiHeaderContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: 80;
HifiControlsUit.CheckBox {
id: kpiScriptCheckbox;
readonly property string kpiSettingsKey: "private/enableScriptingPlugins"
checked: Settings.getValue(kpiSettingsKey, false);
text: "Enable custom script plugins (requires restart)"
// Anchors
anchors.verticalCenter: parent.verticalCenter;
anchors.left: parent.left;
anchors.leftMargin: 20;
boxSize: 24;
labelFontSize: 18;
colorScheme: hifi.colorSchemes.dark
color: hifi.colors.white;
width: 300;
onCheckedChanged: Settings.setValue(kpiSettingsKey, checked);
}
HifiStylesUit.RalewaySemiBold {
id: kpiScriptHelp;
text: '[?]';
// Anchors
anchors.verticalCenter: parent.verticalCenter;
anchors.left: kpiScriptCheckbox.right;
width: 30;
height: 30;
// Text size
size: 18;
// Style
color: hifi.colors.blueHighlight;
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
onEntered: {
parent.color = hifi.colors.blueAccent;
}
onExited: {
parent.color = hifi.colors.blueHighlight;
}
onClicked: {
lightboxPopup.titleText = "Script Plugin Infrastructure by Kasen";
lightboxPopup.bodyText = "Toggles the activation of scripting plugins in the 'plugins/scripting' folder. \n\n"
+ "Created by https://kasen.io/";
lightboxPopup.button1text = "OK";
lightboxPopup.button1method = function() {
lightboxPopup.visible = false;
}
lightboxPopup.visible = true;
}
}
}
}
}
Item {
id: walletContainer;
anchors.top: accountContainer.bottom;
anchors.top: kpiContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: childrenRect.height;

View file

@ -167,7 +167,7 @@ Flickable {
Component.onCompleted: {
var cpu = JSON.parse(PlatformInfo.getCPU(0));
var cpuModel = cpu.model;
if (cpuModel.length === 0) {
if (!cpuModel || cpuModel.length === 0) {
cpuModel = "Unknown";
}
@ -213,7 +213,7 @@ Flickable {
Component.onCompleted: {
var gpu = JSON.parse(PlatformInfo.getGPU(PlatformInfo.getMasterGPU()));
var gpuModel = gpu.model;
if (gpuModel.length === 0) {
if (!gpuModel || gpuModel.length === 0) {
gpuModel = "Unknown";
}
@ -327,7 +327,7 @@ Flickable {
var cpu = JSON.parse(PlatformInfo.getCPU(0));
var cpuModel = cpu.model;
if (cpuModel.length === 0) {
if (!cpuModel || cpuModel.length === 0) {
cpuModel = "Unknown";
}
@ -338,7 +338,7 @@ Flickable {
var gpu = JSON.parse(PlatformInfo.getGPU(PlatformInfo.getMasterGPU()));
var gpuModel = gpu.model;
if (gpuModel.length === 0) {
if (!gpuModel || gpuModel.length === 0) {
gpuModel = "Unknown";
}

View file

@ -35,10 +35,8 @@ TextField {
leftPadding: 0
rightPadding: root.rightGlyph === "" ? 0 : rightGlyphItem.implicitWidth + simplifiedUI.sizes.controls.textField.rightGlyphPadding
onFocusChanged: {
if (focus) {
Tablet.playSound(TabletEnums.ButtonClick);
}
onPressed: {
Tablet.playSound(TabletEnums.ButtonClick);
}
onHoveredChanged: {

View file

@ -811,6 +811,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
}
// Tell the plugin manager about our statically linked plugins
DependencyManager::set<ScriptInitializers>();
DependencyManager::set<PluginManager>();
auto pluginManager = PluginManager::getInstance();
pluginManager->setInputPluginProvider([] { return getInputPlugins(); });
@ -860,7 +861,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
#endif
DependencyManager::set<StatTracker>();
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT, defaultScriptsOverrideOption);
DependencyManager::set<ScriptInitializerMixin, NativeScriptInitializers>();
DependencyManager::set<Preferences>();
DependencyManager::set<recording::Deck>();
DependencyManager::set<recording::Recorder>();
@ -3429,7 +3429,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());
surfaceContext->setContextProperty("Desktop", DependencyManager::get<DesktopScriptingInterface>().data());
surfaceContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
surfaceContext->setContextProperty("Settings", new QMLSettingsScriptingInterface(surfaceContext));
surfaceContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
surfaceContext->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
surfaceContext->setContextProperty("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
@ -3545,7 +3545,7 @@ void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditiona
surfaceContext->setContextProperty("offscreenFlags", flags);
surfaceContext->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
surfaceContext->setContextProperty("Settings", new QMLSettingsScriptingInterface(surfaceContext));
surfaceContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
surfaceContext->setContextProperty("Performance", new PerformanceScriptingInterface());

View file

@ -313,8 +313,8 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
if (spine2Joint >= 0) {
params.spine2ShapeInfo = hfmModel.joints[spine2Joint].shapeInfo;
}
params.isTalking = head->getTimeWithoutTalking() <= 1.5f;
const float TALKING_TIME_THRESHOLD = 0.75f;
params.isTalking = head->getTimeWithoutTalking() <= TALKING_TIME_THRESHOLD;
myAvatar->updateRigControllerParameters(params);

View file

@ -512,13 +512,13 @@ void OtherAvatar::handleChangedAvatarEntityData() {
entity->setParentID(NULL_ID);
entity->setParentID(oldParentID);
if (entity->stillHasMyGrabAction()) {
if (entity->stillHasMyGrab()) {
// For this case: we want to ignore transform+velocities coming from authoritative OtherAvatar
// because the MyAvatar is grabbing and we expect the local grab state
// to have enough information to prevent simulation drift.
//
// Clever readers might realize this could cause problems. For example,
// if an ignored OtherAvagtar were to simultanously grab the object then there would be
// if an ignored OtherAvatar were to simultanously grab the object then there would be
// a noticeable discrepancy between participants in the distributed physics simulation,
// however the difference would be stable and would not drift.
properties.clearTransformOrVelocityChanges();

View file

@ -14,6 +14,7 @@
#include <QtCore/QProcess>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QLocalSocket>
#include <QLocalServer>
#include <QSharedMemory>
@ -117,21 +118,27 @@ int main(int argc, const char* argv[]) {
}
QString applicationPath;
// A temporary application instance is needed to get the location of the running executable
// Tests using high_resolution_clock show that this takes about 30-50 microseconds (on my machine, YMMV)
// If we wanted to avoid the QCoreApplication, we would need to write our own
// cross-platform implementation.
{
// A temporary application instance is needed to get the location of the running executable
// Tests using high_resolution_clock show that this takes about 30-50 microseconds (on my machine, YMMV)
// If we wanted to avoid the QCoreApplication, we would need to write our own
// cross-platform implementation.
QCoreApplication tempApp(argc, const_cast<char**>(argv));
#ifdef Q_OS_OSX
if (QFileInfo::exists(QCoreApplication::applicationDirPath() + "/../../../config.json")) {
applicationPath = QCoreApplication::applicationDirPath() + "/../../../";
} else {
applicationPath = QCoreApplication::applicationDirPath();
}
#else
applicationPath = QCoreApplication::applicationDirPath();
#endif
}
static const QString APPLICATION_CONFIG_FILENAME = "config.json";
QDir applicationDir(applicationPath);
QString configFileName = applicationDir.filePath(APPLICATION_CONFIG_FILENAME);
QFile configFile(configFileName);
QString launcherPath;
if (configFile.exists()) {
if (!configFile.open(QIODevice::ReadOnly)) {
qWarning() << "Found application config, but could not open it";

View file

@ -71,32 +71,44 @@ static QString getTargetDevice(bool hmd, QAudio::Mode mode) {
Qt::ItemFlags AudioDeviceList::_flags { Qt::ItemIsSelectable | Qt::ItemIsEnabled };
AudioDeviceList::AudioDeviceList(QAudio::Mode mode) : _mode(mode) {
auto& setting1 = getSetting(true, QAudio::AudioInput);
if (setting1.isSet()) {
qDebug() << "Device name in settings for HMD, Input" << setting1.get();
} else {
qDebug() << "Device name in settings for HMD, Input not set";
if (mode == QAudio::AudioInput) {
auto& setting1 = getSetting(true, QAudio::AudioInput);
if (setting1.isSet()) {
qDebug() << "Device name in settings for HMD, Input" << setting1.get();
_backupSelectedHMDDeviceName = setting1.get();
} else {
qDebug() << "Device name in settings for HMD, Input not set";
}
}
auto& setting2 = getSetting(true, QAudio::AudioOutput);
if (setting2.isSet()) {
qDebug() << "Device name in settings for HMD, Output" << setting2.get();
} else {
qDebug() << "Device name in settings for HMD, Output not set";
if (mode == QAudio::AudioOutput) {
auto& setting2 = getSetting(true, QAudio::AudioOutput);
if (setting2.isSet()) {
qDebug() << "Device name in settings for HMD, Output" << setting2.get();
_backupSelectedHMDDeviceName = setting2.get();
} else {
qDebug() << "Device name in settings for HMD, Output not set";
}
}
auto& setting3 = getSetting(false, QAudio::AudioInput);
if (setting3.isSet()) {
qDebug() << "Device name in settings for Desktop, Input" << setting3.get();
} else {
qDebug() << "Device name in settings for Desktop, Input not set";
if (mode == QAudio::AudioInput) {
auto& setting3 = getSetting(false, QAudio::AudioInput);
if (setting3.isSet()) {
qDebug() << "Device name in settings for Desktop, Input" << setting3.get();
_backupSelectedDesktopDeviceName = setting3.get();
} else {
qDebug() << "Device name in settings for Desktop, Input not set";
}
}
auto& setting4 = getSetting(false, QAudio::AudioOutput);
if (setting4.isSet()) {
qDebug() << "Device name in settings for Desktop, Output" << setting4.get();
} else {
qDebug() << "Device name in settings for Desktop, Output not set";
if (mode == QAudio::AudioOutput) {
auto& setting4 = getSetting(false, QAudio::AudioOutput);
if (setting4.isSet()) {
qDebug() << "Device name in settings for Desktop, Output" << setting4.get();
_backupSelectedDesktopDeviceName = setting4.get();
} else {
qDebug() << "Device name in settings for Desktop, Output not set";
}
}
}

View file

@ -38,6 +38,14 @@ void SettingsScriptingInterface::setValue(const QString& setting, const QVariant
if (getValue(setting) == value) {
return;
}
if (setting.startsWith("private/")) {
if (_restrictPrivateValues) {
qWarning() << "SettingsScriptingInterface::setValue -- restricted write: " << setting << value;
return;
} else {
qInfo() << "SettingsScriptingInterface::setValue -- allowing restricted write: " << setting << value;
}
}
// Make a deep-copy of the string.
// Dangling pointers can occur with QStrings that are implicitly shared from a QScriptEngine.
QString deepCopy = QString::fromUtf16(setting.utf16());

View file

@ -27,7 +27,6 @@
class SettingsScriptingInterface : public QObject {
Q_OBJECT
SettingsScriptingInterface() { };
public:
static SettingsScriptingInterface* getInstance();
@ -67,6 +66,16 @@ public slots:
signals:
void valueChanged(const QString& setting, const QVariant& value);
protected:
SettingsScriptingInterface(QObject* parent = nullptr) : QObject(parent) { };
bool _restrictPrivateValues { true };
};
class QMLSettingsScriptingInterface : public SettingsScriptingInterface {
Q_OBJECT
public:
QMLSettingsScriptingInterface(QObject* parent) : SettingsScriptingInterface(parent) { _restrictPrivateValues = false; }
};
#endif // hifi_SettingsScriptingInterface_h

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>high-fidelity.hifi</string>
</array>
</dict>
</plist>

View file

@ -50,6 +50,8 @@
repeats: YES];
NSError *error = nil;
NSFileManager *fileManager = [NSFileManager defaultManager];
Launcher* sharedLauncher = [Launcher sharedLauncher];
NSString* appPath = [sharedLauncher getAppPath];
NSString *destinationFileName = downloadTask.originalRequest.URL.lastPathComponent;
NSString* finalFilePath = [[[Launcher sharedLauncher] getAppPath] stringByAppendingPathComponent:destinationFileName];
NSURL *destinationURL = [NSURL URLWithString: [finalFilePath stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]] relativeToURL: [NSURL URLWithString:@"file://"]];
@ -59,7 +61,12 @@
}
[fileManager moveItemAtURL:location toURL:destinationURL error:&error];
Launcher* sharedLauncher = [Launcher sharedLauncher];
NSURL *oldInterfaceURL = [NSURL URLWithString: [[appPath stringByAppendingString:@"interface.app"] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]] relativeToURL: [NSURL URLWithString:@"file://"]];
if([fileManager fileExistsAtPath:[oldInterfaceURL path]])
{
[fileManager removeItemAtURL:oldInterfaceURL error:nil];
}
if (error) {
NSLog(@"Download Interface: failed to move file to destination -> error: %@", error);
@ -68,7 +75,6 @@
return;
}
[sharedLauncher setDownloadFilename:destinationFileName];
NSString* appPath = [sharedLauncher getAppPath];
NSString* downloadFileName = [sharedLauncher getDownloadFilename];
NSLog(@"extract interface zip");

View file

@ -3,6 +3,8 @@
@interface DownloadLauncher : NSObject<NSURLSessionDataDelegate, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLDownloadDelegate> {
}
@property (readonly) bool didBecomeDownloadTask;
- (void) downloadLauncher:(NSString*) launcherUrl;
@end

View file

@ -8,6 +8,13 @@ static const NSString *kIOError = @"IOError";
@implementation DownloadLauncher
-(id)init {
if ((self = [super init]) != nil) {
_didBecomeDownloadTask = false;
}
return self;
}
- (void) downloadLauncher:(NSString*)launcherUrl {
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:launcherUrl]
cachePolicy:NSURLRequestUseProtocolCachePolicy
@ -16,8 +23,8 @@ static const NSString *kIOError = @"IOError";
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
NSURLSessionDownloadTask *downloadTask = [defaultSession downloadTaskWithRequest:request];
[downloadTask resume];
NSURLSessionDataTask *task = [defaultSession dataTaskWithRequest:request];
[task resume];
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
@ -45,6 +52,25 @@ static const NSString *kIOError = @"IOError";
}
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSURLSessionResponseDisposition disposition = NSURLSessionResponseBecomeDownload;
if (httpResponse.statusCode != 200) {
NSLog(@"expected statusCode 200, got %ld", (long)httpResponse.statusCode);
disposition = NSURLSessionResponseCancel;
}
completionHandler(disposition);
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
_didBecomeDownloadTask = true;
}
-(void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTask didFinishDownloadingToURL:(NSURL*)location {
NSLog(@"Did finish downloading to url");
@try {
@ -95,9 +121,14 @@ static const NSString *kIOError = @"IOError";
}
- (void)URLSession:(NSURLSession*)session task:(NSURLSessionTask*)task didCompleteWithError:(NSError*)error {
NSLog(@"completed; error: %@", error);
if (error) {
if (_didBecomeDownloadTask && [task class] == [NSURLSessionDataTask class]) {
return;
}
NSLog(@"couldn't complete download: %@", error);
[[Launcher sharedLauncher] displayErrorPage];
} else {
NSLog(@"finished downloading Launcher");
}
}
@end

View file

@ -59,7 +59,7 @@
NSString* defaultBuildTag = [json valueForKey:@"default_tag"];
NSString* launcherVersion = [launcherValues valueForKey:@"version"];
NSString* launcherUrl = [[[launcherValues valueForKey:@"mac"] valueForKey:@"url"] stringByRemovingPercentEncoding];
NSString* launcherUrl = [[launcherValues valueForKey:@"mac"] valueForKey:@"url"];
BOOL appDirectoryExist = [fileManager fileExistsAtPath:[[sharedLauncher getAppPath] stringByAppendingString:@"interface.app"]];

View file

@ -14,19 +14,32 @@
return sharedSettings;
}
- (NSString*) getOldFilePath {
Launcher* sharedLauncher = [Launcher sharedLauncher];
NSString* appPath = [sharedLauncher getAppPath];
NSString* filePath = [appPath stringByAppendingString:@"interface.app/Contents/MacOS/"];
return filePath;
}
- (NSString*) getFilePath
{
Launcher* sharedLauncher = [Launcher sharedLauncher];
NSString* appPath = [sharedLauncher getAppPath];
NSString* filePath = [appPath stringByAppendingString:@"interface.app/Contents/MacOS/"];
return filePath;
return appPath;
}
- (void) readDataFromJsonFile
{
NSString* oldPath = [self getOldFilePath];
NSString* fileAtOldPath = [oldPath stringByAppendingString:@"config.json"];
NSString* filePath = [self getFilePath];
NSString* fileAtPath = [filePath stringByAppendingString:@"config.json"];
if ([[NSFileManager defaultManager] fileExistsAtPath:fileAtOldPath] && ![[NSFileManager defaultManager] fileExistsAtPath:fileAtPath]) {
BOOL success = [[NSFileManager defaultManager] moveItemAtPath:fileAtOldPath toPath:fileAtPath error:nil];
NSLog(@"move config to new location -> status: %@", success ? @"SUCCESS" : @"FAILED");
}
if ([[NSFileManager defaultManager] fileExistsAtPath:fileAtPath]) {
NSString* jsonString = [[NSString alloc] initWithData:[NSData dataWithContentsOfFile:fileAtPath] encoding:NSUTF8StringEncoding];
NSError * err;
@ -34,7 +47,6 @@
NSDictionary * json;
if (data != nil) {
json = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&err];
self.loggedIn = [[json valueForKey:@"loggedIn"] boolValue];
self.build = [[json valueForKey:@"build_version"] integerValue];
self.launcher = [json valueForKey:@"luancherPath"];
@ -64,18 +76,18 @@
NSError * err;
NSData * jsonData = [NSJSONSerialization dataWithJSONObject:json options:0 error:&err];
NSString * jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSString* filePath = [self getFilePath];
NSString* fileAtPath = [filePath stringByAppendingString:@"config.json"];
if (![[NSFileManager defaultManager] fileExistsAtPath:fileAtPath]) {
NSError * error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:filePath withIntermediateDirectories:FALSE attributes:nil error:&error];
[[NSFileManager defaultManager] createFileAtPath:fileAtPath contents:nil attributes:nil];
}
[[jsonString dataUsingEncoding:NSUTF8StringEncoding] writeToFile:fileAtPath atomically:NO];
}
-(id)init

View file

@ -2117,7 +2117,7 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
_previousIsTalking = params.isTalking;
const float TOTAL_EASE_IN_TIME = 0.75f;
const float TOTAL_EASE_OUT_TIME = 1.5f;
const float TOTAL_EASE_OUT_TIME = 0.75f;
if (params.isTalking) {
if (_talkIdleInterpTime < 1.0f) {
_talkIdleInterpTime += dt / TOTAL_EASE_IN_TIME;

View file

@ -49,7 +49,7 @@ void Head::simulate(float deltaTime) {
// Update audio trailing average for rendering facial animations
const float AUDIO_AVERAGING_SECS = 0.05f;
const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f;
const float AUDIO_LONG_TERM_AVERAGING_SECS = 15.0f;
_averageLoudness = glm::mix(_averageLoudness, audioLoudness, glm::min(deltaTime / AUDIO_AVERAGING_SECS, 1.0f));
if (_longTermAverageLoudness == -1.0f) {
@ -84,7 +84,7 @@ void Head::simulate(float deltaTime) {
if (getHasProceduralBlinkFaceMovement()) {
// Detect transition from talking to not; force blink after that and a delay
bool forceBlink = false;
const float TALKING_LOUDNESS = 100.0f;
const float TALKING_LOUDNESS = 150.0f;
const float BLINK_AFTER_TALKING = 0.25f;
_timeWithoutTalking += deltaTime;
if ((_averageLoudness - _longTermAverageLoudness) > TALKING_LOUDNESS) {
@ -176,7 +176,7 @@ void Head::simulate(float deltaTime) {
}
void Head::calculateMouthShapes(float deltaTime) {
const float JAW_OPEN_SCALE = 0.015f;
const float JAW_OPEN_SCALE = 0.35f;
const float JAW_OPEN_RATE = 0.9f;
const float JAW_CLOSE_RATE = 0.90f;
const float TIMESTEP_CONSTANT = 0.0032f;
@ -188,11 +188,13 @@ void Head::calculateMouthShapes(float deltaTime) {
const float FUNNEL_SPEED = 2.335f;
const float STOP_GAIN = 5.0f;
const float NORMAL_HZ = 60.0f; // the update rate the constant values were tuned for
const float MAX_DELTA_LOUDNESS = 100.0f;
float deltaTimeRatio = deltaTime / (1.0f / NORMAL_HZ);
// From the change in loudness, decide how much to open or close the jaw
float audioDelta = sqrtf(glm::max(_averageLoudness - _longTermAverageLoudness, 0.0f)) * JAW_OPEN_SCALE;
float deltaLoudness = glm::max(glm::min(_averageLoudness - _longTermAverageLoudness, MAX_DELTA_LOUDNESS), 0.0f) / MAX_DELTA_LOUDNESS;
float audioDelta = powf(deltaLoudness, 2.0f) * JAW_OPEN_SCALE;
if (audioDelta > _audioJawOpen) {
_audioJawOpen += (audioDelta - _audioJawOpen) * JAW_OPEN_RATE * deltaTimeRatio;
} else {

View file

@ -45,6 +45,7 @@
#include <QJsonArray>
ModelBaker::ModelBaker(const QUrl& inputModelURL, const QString& bakedOutputDirectory, const QString& originalOutputDirectory, bool hasBeenBaked) :
_originalInputModelURL(inputModelURL),
_modelURL(inputModelURL),
_bakedOutputDir(bakedOutputDirectory),
_originalOutputDir(originalOutputDirectory),

View file

@ -51,6 +51,7 @@ public:
virtual void setWasAborted(bool wasAborted) override;
QUrl getModelURL() const { return _modelURL; }
QUrl getOriginalInputModelURL() const { return _originalInputModelURL; }
virtual QUrl getFullOutputMappingURL() const;
QUrl getBakedModelURL() const { return _bakedModelURL; }
@ -67,6 +68,7 @@ protected:
void exportScene();
FBXNode _rootNode;
QUrl _originalInputModelURL;
QUrl _modelURL;
QUrl _outputURLSuffix;
QUrl _mappingURL;

View file

@ -250,6 +250,7 @@ void TextureBaker::processTexture() {
QFile file { _metaTextureFileName };
if (!file.open(QIODevice::WriteOnly) || file.write(data) == -1) {
handleError("Could not write meta texture for " + _textureURL.toString());
return;
} else {
_outputFiles.push_back(_metaTextureFileName);
}

View file

@ -585,26 +585,13 @@ void OpenGLDisplayPlugin::updateFrameData() {
std::function<void(gpu::Batch&, const gpu::TexturePointer&)> OpenGLDisplayPlugin::getHUDOperator() {
auto hudPipeline = _hudPipeline;
auto hudStereo = isStereo();
auto hudCompositeFramebufferSize = _compositeFramebuffer->getSize();
std::array<glm::ivec4, 2> hudEyeViewports;
for_each_eye([&](Eye eye) {
hudEyeViewports[eye] = eyeViewport(eye);
});
auto hudCompositeFramebufferSize = getRecommendedRenderSize();
return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture) {
if (hudPipeline && hudTexture) {
batch.enableStereo(false);
batch.setPipeline(hudPipeline);
batch.setResourceTexture(0, hudTexture);
if (hudStereo) {
for_each_eye([&](Eye eye) {
batch.setViewportTransform(hudEyeViewports[eye]);
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
} else {
batch.setViewportTransform(ivec4(uvec2(0), hudCompositeFramebufferSize));
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
batch.setViewportTransform(ivec4(uvec2(0), hudCompositeFramebufferSize));
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
};
}
@ -614,22 +601,14 @@ void OpenGLDisplayPlugin::compositePointer() {
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
auto cursorTransform = DependencyManager::get<CompositorHelper>()->getReticleTransform(glm::mat4());
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.setProjectionTransform(mat4());
batch.setFramebuffer(_compositeFramebuffer);
batch.setPipeline(_cursorPipeline);
batch.setResourceTexture(0, cursorData.texture);
batch.resetViewTransform();
batch.setModelTransform(cursorTransform);
if (isStereo()) {
for_each_eye([&](Eye eye) {
batch.setViewportTransform(eyeViewport(eye));
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
} else {
batch.setViewportTransform(ivec4(uvec2(0), _compositeFramebuffer->getSize()));
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
batch.setViewportTransform(ivec4(uvec2(0), _compositeFramebuffer->getSize()));
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
}
@ -871,16 +850,6 @@ bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
return Parent::beginFrameRender(frameIndex);
}
ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
uvec2 vpSize = _compositeFramebuffer->getSize();
vpSize.x /= 2;
uvec2 vpPos;
if (eye == Eye::Right) {
vpPos.x = vpSize.x;
}
return ivec4(vpPos, vpSize);
}
gpu::gl::GLBackend* OpenGLDisplayPlugin::getGLBackend() {
if (!_gpuContext || !_gpuContext->getBackend()) {
return nullptr;
@ -906,7 +875,7 @@ OpenGLDisplayPlugin::~OpenGLDisplayPlugin() {
}
void OpenGLDisplayPlugin::updateCompositeFramebuffer() {
auto renderSize = glm::uvec2(getRecommendedRenderSize());
auto renderSize = getRecommendedRenderSize();
if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) {
_compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_SRGBA_32, renderSize.x, renderSize.y));
}

View file

@ -137,7 +137,6 @@ protected:
void present(const std::shared_ptr<RefreshRateController>& refreshRateController);
virtual void swapBuffers();
ivec4 eyeViewport(Eye eye) const;
void render(std::function<void(gpu::Batch& batch)> f);

View file

@ -68,6 +68,16 @@ glm::mat4 HmdDisplayPlugin::getCullingProjection(const glm::mat4& baseProjection
return _cullingProjection;
}
glm::ivec4 HmdDisplayPlugin::eyeViewport(Eye eye) const {
uvec2 vpSize = getRecommendedRenderSize();
vpSize.x /= 2;
uvec2 vpPos;
if (eye == Eye::Right) {
vpPos.x = vpSize.x;
}
return ivec4(vpPos, vpSize);
}
#define DISABLE_PREVIEW_MENU_ITEM_DELAY_MS 500
bool HmdDisplayPlugin::internalActivate() {

View file

@ -34,6 +34,8 @@ public:
glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; }
bool isDisplayVisible() const override { return isHmdMounted(); }
ivec4 eyeViewport(Eye eye) const;
QRect getRecommendedHUDRect() const override final;
virtual glm::mat4 getHeadPose() const override;

View file

@ -29,7 +29,7 @@
#include <PrioritySortUtil.h>
#include <Rig.h>
#include <SceneScriptingInterface.h>
#include <ScriptEngine.h>
#include <ScriptEngines.h>
#include <EntitySimulation.h>
#include <ZoneRenderer.h>
#include <PhysicalEntitySimulation.h>
@ -146,7 +146,7 @@ int EntityTreeRenderer::_entitiesScriptEngineCount = 0;
void EntityTreeRenderer::resetEntitiesScriptEngine() {
_entitiesScriptEngine = scriptEngineFactory(ScriptEngine::ENTITY_CLIENT_SCRIPT, NO_SCRIPT,
QString("about:Entities %1").arg(++_entitiesScriptEngineCount));
_scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);
DependencyManager::get<ScriptEngines>()->runScriptInitializers(_entitiesScriptEngine);
_entitiesScriptEngine->runInThread();
auto entitiesScriptEngineProvider = qSharedPointerCast<EntitiesScriptEngineProvider>(_entitiesScriptEngine);
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();

View file

@ -14,6 +14,7 @@
#include <QUrl>
#include <ResourceManager.h>
#include <shared/ScriptInitializerMixin.h>
QList<EntityItemID> EntityEditFilters::getZonesByPosition(glm::vec3& position) {
QList<EntityItemID> zones;
@ -258,7 +259,13 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) {
if (hasCorrectSyntax(program)) {
// create a QScriptEngine for this script
QScriptEngine* engine = new QScriptEngine();
engine->evaluate(scriptContents);
engine->setObjectName("filter:" + entityID.toString());
engine->setProperty("type", "edit_filter");
engine->setProperty("fileName", urlString);
engine->setProperty("entityID", entityID);
engine->globalObject().setProperty("Script", engine->newQObject(engine));
DependencyManager::get<ScriptInitializers>()->runScriptInitializers(engine);
engine->evaluate(scriptContents, urlString);
if (!hadUncaughtExceptions(*engine, urlString)) {
// put the engine in the engine map (so we don't leak them, etc...)
FilterData filterData;

View file

@ -798,7 +798,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
auto lastEdited = lastEditedFromBufferAdjusted;
bool otherOverwrites = overwriteLocalData && !weOwnSimulation;
// calculate hasGrab once outside the lambda rather than calling it every time inside
bool hasGrab = stillHasGrabAction();
bool hasGrab = stillHasGrab();
auto shouldUpdate = [lastEdited, otherOverwrites, filterRejection, hasGrab](quint64 updatedTimestamp, bool valueChanged) {
if (hasGrab) {
return false;
@ -1075,16 +1075,13 @@ void EntityItem::setMass(float mass) {
void EntityItem::setHref(QString value) {
auto href = value.toLower();
// If the string has something and doesn't start with with "hifi://" it shouldn't be set
// We allow the string to be empty, because that's the initial state of this property
if (!value.isEmpty() &&
!(value.toLower().startsWith("hifi://")) &&
!(value.toLower().startsWith("file://"))
// TODO: serverless-domains will eventually support http and https also
) {
return;
}
// Let's let the user set the value of this property to anything, then let consumers of the property
// decide what to do with it. Currently, the only in-engine consumers are `EntityTreeRenderer::mousePressEvent()`
// and `OtherAvatar::handleChangedAvatarEntityData()` (to remove the href property from others' avatar entities).
//
// We want this property to be as flexible as possible. The value of this property _should_ only be values that can
// be handled by `AddressManager::handleLookupString()`. That function will return `false` and not do
// anything if the value of this property isn't something that function can handle.
withWriteLock([&] {
_href = value;
});
@ -1444,7 +1441,7 @@ void EntityItem::getTransformAndVelocityProperties(EntityItemProperties& propert
void EntityItem::upgradeScriptSimulationPriority(uint8_t priority) {
uint8_t newPriority = glm::max(priority, _scriptSimulationPriority);
if (newPriority < SCRIPT_GRAB_SIMULATION_PRIORITY && stillHasMyGrabAction()) {
if (newPriority < SCRIPT_GRAB_SIMULATION_PRIORITY && stillHasMyGrab()) {
newPriority = SCRIPT_GRAB_SIMULATION_PRIORITY;
}
if (newPriority != _scriptSimulationPriority) {
@ -1457,7 +1454,7 @@ void EntityItem::upgradeScriptSimulationPriority(uint8_t priority) {
void EntityItem::clearScriptSimulationPriority() {
// DO NOT markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY) here, because this
// is only ever called from the code that actually handles the dirty flags, and it knows best.
_scriptSimulationPriority = stillHasMyGrabAction() ? SCRIPT_GRAB_SIMULATION_PRIORITY : 0;
_scriptSimulationPriority = stillHasMyGrab() ? SCRIPT_GRAB_SIMULATION_PRIORITY : 0;
}
void EntityItem::setPendingOwnershipPriority(uint8_t priority) {
@ -2204,7 +2201,7 @@ void EntityItem::enableNoBootstrap() {
}
void EntityItem::disableNoBootstrap() {
if (!stillHasMyGrabAction()) {
if (!stillHasMyGrab()) {
_flags &= ~Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING;
_flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
@ -2290,33 +2287,25 @@ bool EntityItem::removeAction(EntitySimulationPointer simulation, const QUuid& a
return success;
}
bool EntityItem::stillHasGrabAction() const {
return !_grabActions.empty();
bool EntityItem::stillHasGrab() const {
return !(_grabs.empty());
}
// retutrns 'true' if there exists an action that returns 'true' for EntityActionInterface::isMine()
// returns 'true' if there exists an action that returns 'true' for EntityActionInterface::isMine()
// (e.g. the action belongs to the MyAvatar instance)
bool EntityItem::stillHasMyGrabAction() const {
QList<EntityDynamicPointer> holdActions = getActionsOfType(DYNAMIC_TYPE_HOLD);
QList<EntityDynamicPointer>::const_iterator i = holdActions.begin();
while (i != holdActions.end()) {
EntityDynamicPointer action = *i;
if (action->isMine()) {
return true;
}
i++;
bool EntityItem::stillHasMyGrab() const {
bool foundGrab = false;
if (!_grabs.empty()) {
_grabsLock.withReadLock([&] {
foreach (const GrabPointer &grab, _grabs) {
if (grab->getOwnerID() == Physics::getSessionUUID()) {
foundGrab = true;
break;
}
}
});
}
QList<EntityDynamicPointer> farGrabActions = getActionsOfType(DYNAMIC_TYPE_FAR_GRAB);
i = farGrabActions.begin();
while (i != farGrabActions.end()) {
EntityDynamicPointer action = *i;
if (action->isMine()) {
return true;
}
i++;
}
return false;
return foundGrab;
}
bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation) {

View file

@ -569,7 +569,7 @@ public:
static void setPrimaryViewFrustumPositionOperator(std::function<glm::vec3()> getPrimaryViewFrustumPositionOperator) { _getPrimaryViewFrustumPositionOperator = getPrimaryViewFrustumPositionOperator; }
static glm::vec3 getPrimaryViewFrustumPosition() { return _getPrimaryViewFrustumPositionOperator(); }
bool stillHasMyGrabAction() const;
bool stillHasMyGrab() const;
bool needsRenderUpdate() const { return resultWithReadLock<bool>([&] { return _needsRenderUpdate; }); }
void setNeedsRenderUpdate(bool needsRenderUpdate) { withWriteLock([&] { _needsRenderUpdate = needsRenderUpdate; }); }
@ -585,7 +585,7 @@ protected:
void setSimulated(bool simulated) { _simulated = simulated; }
const QByteArray getDynamicDataInternal() const;
bool stillHasGrabAction() const;
bool stillHasGrab() const;
void setDynamicDataInternal(QByteArray dynamicData);
virtual void dimensionsChanged() override;

View file

@ -2246,8 +2246,8 @@ void EntityTree::preUpdate() {
void EntityTree::update(bool simulate) {
PROFILE_RANGE(simulation_physics, "UpdateTree");
PerformanceTimer perfTimer("updateTree");
withWriteLock([&] {
if (simulate && _simulation) {
if (simulate && _simulation) {
withWriteLock([&] {
_simulation->updateEntities();
{
PROFILE_RANGE(simulation_physics, "Deletes");
@ -2265,8 +2265,8 @@ void EntityTree::update(bool simulate) {
deleteEntities(idsToDelete, true);
}
}
}
});
});
}
}
quint64 EntityTree::getAdjustedConsiderSince(quint64 sinceTime) {

View file

@ -107,7 +107,6 @@ void PrepareJointsTask::run(const baker::BakeContextPointer& context, const Inpu
glm::quat rotationOffset = itr.value();
jointRotationOffsets.insert(jointIndex, rotationOffset);
qCDebug(model_baker) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset;
break;
}
}
}

View file

@ -154,7 +154,7 @@ const char* Assignment::typeToString(Assignment::Type type) {
QDebug operator<<(QDebug debug, const Assignment &assignment) {
debug.nospace() << "UUID: " << qPrintable(assignment.getUUID().toString()) <<
", Type: " << assignment.getType();
", Type: " << assignment.getTypeName() << " (" << assignment.getType() << ")";
if (!assignment.getPool().isEmpty()) {
debug << ", Pool: " << assignment.getPool();

View file

@ -29,6 +29,9 @@ ThreadedAssignment::ThreadedAssignment(ReceivedMessage& message) :
_domainServerTimer(this),
_statsTimer(this)
{
// use <mixer-type> as a temporary targetName name until commonInit can be called later
LogHandler::getInstance().setTargetName(QString("<%1>").arg(getTypeName()));
static const int STATS_TIMEOUT_MS = 1000;
_statsTimer.setInterval(STATS_TIMEOUT_MS); // 1s, Qt::CoarseTimer acceptable
connect(&_statsTimer, &QTimer::timeout, this, &ThreadedAssignment::sendStatsPacket);

View file

@ -274,6 +274,7 @@ enum class EntityVersion : PacketVersion {
TextUnlit,
ShadowBiasAndDistance,
TextEntityFonts,
ScriptServerKinematicMotion,
// Add new versions above here
NUM_PACKET_TYPE,

View file

@ -740,7 +740,8 @@ bool EntityMotionState::shouldSendBid() const {
&& (_region == workload::Region::R1)
&& _ownershipState != EntityMotionState::OwnershipState::Unownable
&& glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) >= _entity->getSimulationPriority()
&& !_entity->getLocked();
&& !_entity->getLocked()
&& (!_body->isStaticOrKinematicObject() || _entity->stillHasMyGrab());
}
void EntityMotionState::setRigidBody(btRigidBody* body) {

View file

@ -84,6 +84,11 @@ bool isDisabled(QJsonObject metaData) {
return false;
}
int PluginManager::instantiate() {
auto loaders = getLoadedPlugins();
return std::count_if(loaders.begin(), loaders.end(), [](const auto& loader) { return (bool)loader->instance(); });
}
auto PluginManager::getLoadedPlugins() const -> const LoaderList& {
static std::once_flag once;
static LoaderList loadedPlugins;
@ -105,6 +110,16 @@ bool isDisabled(QJsonObject metaData) {
pluginDir.setNameFilters(QStringList() << "libplugins_lib*.so");
#endif
auto candidates = pluginDir.entryList();
if (_enableScriptingPlugins.get()) {
QDir scriptingPluginDir{ pluginDir };
scriptingPluginDir.cd("scripting");
qCDebug(plugins) << "Loading scripting plugins from " << scriptingPluginDir.path();
for (auto plugin : scriptingPluginDir.entryList()) {
candidates << "scripting/" + plugin;
}
}
for (auto plugin : candidates) {
qCDebug(plugins) << "Attempting plugin" << qPrintable(plugin);
QSharedPointer<QPluginLoader> loader(new QPluginLoader(pluginPath + plugin));
@ -139,6 +154,8 @@ bool isDisabled(QJsonObject metaData) {
qCDebug(plugins) << " " << qPrintable(loader->errorString());
}
}
} else {
qWarning() << "pluginPath does not exit..." << pluginDir;
}
});
return loadedPlugins;

View file

@ -10,6 +10,7 @@
#include <QObject>
#include <DependencyManager.h>
#include <SettingHandle.h>
#include "Forward.h"
@ -38,6 +39,7 @@ public:
void saveSettings();
void setContainer(PluginContainer* container) { _container = container; }
int instantiate();
void shutdown();
// Application that have statically linked plugins can expose them to the plugin manager with these function
@ -69,6 +71,9 @@ private:
using LoaderList = QList<Loader>;
const LoaderList& getLoadedPlugins() const;
Setting::Handle<bool> _enableScriptingPlugins {
"private/enableScriptingPlugins", (bool)qgetenv("enableScriptingPlugins").toInt()
};
};
// TODO: we should define this value in CMake, and then use CMake

View file

@ -237,6 +237,11 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const
}
}
QString ScriptEngine::getTypeAsString() const {
auto value = QVariant::fromValue(_type).toString();
return value.isEmpty() ? "unknown" : value.toLower();
}
QString ScriptEngine::getContext() const {
switch (_context) {
case CLIENT_SCRIPT:

View file

@ -122,6 +122,8 @@ public:
class ScriptEngine : public BaseScriptEngine, public EntitiesScriptEngineProvider {
Q_OBJECT
Q_PROPERTY(QString context READ getContext)
Q_PROPERTY(QString type READ getTypeAsString)
Q_PROPERTY(QString fileName MEMBER _fileNameString CONSTANT)
public:
enum Context {
@ -138,6 +140,7 @@ public:
AGENT,
AVATAR
};
Q_ENUM(Type)
static int processLevelMaxRetries;
ScriptEngine(Context context, const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString("about:ScriptEngine"));
@ -636,6 +639,7 @@ public:
void setType(Type type) { _type = type; };
Type getType() { return _type; };
QString getTypeAsString() const;
bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget
bool isRunning() const { return _isRunning; } // used by ScriptWidget

View file

@ -136,24 +136,6 @@ QUrl expandScriptUrl(const QUrl& rawScriptURL) {
QObject* scriptsModel();
bool NativeScriptInitializers::registerNativeScriptInitializer(NativeScriptInitializer initializer) {
return registerScriptInitializer([initializer](ScriptEnginePointer engine) {
initializer(qobject_cast<QScriptEngine*>(engine.data()));
});
}
bool NativeScriptInitializers::registerScriptInitializer(ScriptInitializer initializer) {
if (auto scriptEngines = DependencyManager::get<ScriptEngines>().data()) {
scriptEngines->registerScriptInitializer(initializer);
return true;
}
return false;
}
void ScriptEngines::registerScriptInitializer(ScriptInitializer initializer) {
_scriptInitializers.push_back(initializer);
}
void ScriptEngines::addScriptEngine(ScriptEnginePointer engine) {
if (!_isStopped) {
QMutexLocker locker(&_allScriptsMutex);
@ -590,12 +572,8 @@ void ScriptEngines::quitWhenFinished() {
}
int ScriptEngines::runScriptInitializers(ScriptEnginePointer scriptEngine) {
int ii=0;
for (auto initializer : _scriptInitializers) {
ii++;
initializer(scriptEngine);
}
return ii;
auto nativeCount = DependencyManager::get<ScriptInitializers>()->runScriptInitializers(scriptEngine.data());
return nativeCount + ScriptInitializerMixin<ScriptEnginePointer>::runScriptInitializers(scriptEngine);
}
void ScriptEngines::launchScriptEngine(ScriptEnginePointer scriptEngine) {

View file

@ -48,13 +48,8 @@ class ScriptEngine;
* scripts directory of the Interface installation.
* <em>Read-only.</em>
*/
class NativeScriptInitializers : public ScriptInitializerMixin {
public:
bool registerNativeScriptInitializer(NativeScriptInitializer initializer) override;
bool registerScriptInitializer(ScriptInitializer initializer) override;
};
class ScriptEngines : public QObject, public Dependency {
class ScriptEngines : public QObject, public Dependency, public ScriptInitializerMixin<ScriptEnginePointer> {
Q_OBJECT
Q_PROPERTY(ScriptsModel* scriptsModel READ scriptsModel CONSTANT)
@ -62,11 +57,9 @@ class ScriptEngines : public QObject, public Dependency {
Q_PROPERTY(QString debugScriptUrl READ getDebugScriptUrl WRITE setDebugScriptUrl)
public:
using ScriptInitializer = ScriptInitializerMixin::ScriptInitializer;
ScriptEngines(ScriptEngine::Context context, const QUrl& defaultScriptsOverride = QUrl());
void registerScriptInitializer(ScriptInitializer initializer);
int runScriptInitializers(ScriptEnginePointer engine);
int runScriptInitializers(ScriptEnginePointer engine) override;
void loadScripts();
void saveScripts();
@ -347,7 +340,6 @@ protected:
QHash<QUrl, ScriptEnginePointer> _scriptEnginesHash;
QSet<ScriptEnginePointer> _allKnownScriptEngines;
QMutex _allScriptsMutex;
std::list<ScriptInitializer> _scriptInitializers;
ScriptsModel _scriptsModel;
ScriptsModelFilter _scriptsModelFilter;
std::atomic<bool> _isStopped { false };

View file

@ -9,30 +9,38 @@
#pragma once
#include <functional>
#include <mutex>
#include <QSharedPointer>
#include "../DependencyManager.h"
class QScriptEngine;
class ScriptEngine;
class ScriptInitializerMixin : public QObject, public Dependency {
Q_OBJECT
template <typename T> class ScriptInitializerMixin {
public:
using ScriptInitializer = std::function<void(T)>;
virtual void registerScriptInitializer(ScriptInitializer initializer) {
InitializerLock lock(_scriptInitializerMutex);
_scriptInitializers.push_back(initializer);
}
virtual int runScriptInitializers(T engine) {
InitializerLock lock(_scriptInitializerMutex);
return std::count_if(_scriptInitializers.begin(), _scriptInitializers.end(),
[engine](auto initializer){ initializer(engine); return true; }
);
}
virtual ~ScriptInitializerMixin() {}
protected:
std::mutex _scriptInitializerMutex;
using InitializerLock = std::lock_guard<std::mutex>;
std::list<ScriptInitializer> _scriptInitializers;
};
class ScriptInitializers : public ScriptInitializerMixin<QScriptEngine*>, public Dependency {
public:
// Lightweight `QScriptEngine*` initializer (only depends on built-in Qt components)
// example registration:
// eg: [&](QScriptEngine* engine) -> bool {
// eg: [&](QScriptEngine* engine) {
// engine->globalObject().setProperties("API", engine->newQObject(...instance...))
// return true;
// }
using NativeScriptInitializer = std::function<void(QScriptEngine*)>;
virtual bool registerNativeScriptInitializer(NativeScriptInitializer initializer) = 0;
// Heavyweight `ScriptEngine*` initializer (tightly coupled to Interface and script-engine library internals)
// eg: [&](ScriptEnginePointer scriptEngine) -> bool {
// engine->registerGlobalObject("API", ...instance..);
// return true;
// }
using ScriptEnginePointer = QSharedPointer<ScriptEngine>;
using ScriptInitializer = std::function<void(ScriptEnginePointer)>;
virtual bool registerScriptInitializer(ScriptInitializer initializer) { return false; };
// };
};

View file

@ -43,3 +43,7 @@ set(DIR "pcmCodec")
add_subdirectory(${DIR})
set(DIR "hifiCodec")
add_subdirectory(${DIR})
# example plugins
set(DIR "KasenAPIExample")
add_subdirectory(${DIR})

View file

@ -0,0 +1,3 @@
set(TARGET_NAME KasenAPIExample)
setup_hifi_client_server_plugin(scripting)
link_hifi_libraries(shared plugins avatars networking graphics gpu)

View file

@ -0,0 +1,56 @@
//
// ExampleScriptPlugin.h
// plugins/KasenAPIExample/src
//
// Created by Kasen IO on 2019.07.14 | realities.dev | kasenvr@gmail.com
// Copyright 2019 Kasen IO
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// Supporting file containing all QtScript specific integration.
#ifndef EXAMPLE_SCRIPT_PLUGIN_H
#define EXAMPLE_SCRIPT_PLUGIN_H
#if DEV_BUILD
#pragma message("QtScript is deprecated see: doc.qt.io/qt-5/topics-scripting.html")
#endif
#include <QtScript/QScriptEngine>
#include <QtCore/QLoggingCategory>
#include <QCoreApplication>
#include <shared/ScriptInitializerMixin.h>
namespace example {
extern const QLoggingCategory& logger;
inline void setGlobalInstance(QScriptEngine* engine, const QString& name, QObject* object) {
auto value = engine->newQObject(object, QScriptEngine::QtOwnership);
engine->globalObject().setProperty(name, value);
qCDebug(logger) << "setGlobalInstance" << name << engine->property("fileName");
}
class ScriptPlugin : public QObject {
Q_OBJECT
QString _version;
Q_PROPERTY(QString version MEMBER _version CONSTANT)
protected:
inline ScriptPlugin(const QString& name, const QString& version) : _version(version) {
setObjectName(name);
if (!DependencyManager::get<ScriptInitializers>()) {
qCWarning(logger) << "COULD NOT INITIALIZE (ScriptInitializers unavailable)" << qApp << this;
return;
}
qCWarning(logger) << "registering w/ScriptInitializerMixin..." << DependencyManager::get<ScriptInitializers>().data();
DependencyManager::get<ScriptInitializers>()->registerScriptInitializer(
[this](QScriptEngine* engine) { setGlobalInstance(engine, objectName(), this); });
}
public slots:
inline QString toString() const { return QString("[%1 version=%2]").arg(objectName()).arg(_version); }
};
} // namespace example
#endif

View file

@ -0,0 +1,139 @@
//
// KasenAPIExample.cpp
// plugins/KasenAPIExample/src
//
// Created by Kasen IO on 2019.07.14 | realities.dev | kasenvr@gmail.com
// Copyright 2019 Kasen IO
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// Example of prototyping new JS APIs by leveraging the existing plugin system.
#include "ExampleScriptPlugin.h"
#include <QCoreApplication>
#include <QtCore/QJsonObject>
#include <QtCore/QLoggingCategory>
#include <QtCore/QThread>
#include <QtCore/QTimer>
#include <SharedUtil.h>
#include <AvatarHashMap.h>
namespace custom_api_example {
QLoggingCategory logger{ "custom_api_example" };
class KasenAPIExample : public example::ScriptPlugin {
Q_OBJECT
Q_PLUGIN_METADATA(IID "KasenAPIExample" FILE "plugin.json")
public:
KasenAPIExample() : example::ScriptPlugin("KasenAPIExample", "0.0.1") {
qCInfo(logger) << "plugin loaded" << qApp << toString() << QThread::currentThread();
}
public slots:
/**jsdoc
* Returns current microseconds (usecs) since Epoch. note: 1000usecs == 1ms
* @example <caption>Measure current setTimeout accuracy.</caption>
* var expected = 1000;
* var start = KasenAPIExample.now();
* Script.setTimeout(function () {
* var elapsed = (KasenAPIExample.now() - start)/1000;
* print("expected (ms):", expected, "actual (ms):", elapsed);
* }, expected);
*/
QVariant now() const {
return usecTimestampNow();
}
/**jsdoc
* Returns the available blendshape names for an avatar.
* @example <caption>Get blendshape names</caption>
* print(JSON.stringify(KasenAPIExample.getBlendshapeNames(MyAvatar.sessionUUID)));
*/
QStringList getBlendshapeNames(const QUuid& avatarID) const {
QVector<QString> out;
if (auto head = getAvatarHead(avatarID)) {
for (const auto& kv : head->getBlendshapeMap().toStdMap()) {
if (kv.second >= out.size()) out.resize(kv.second+1);
out[kv.second] = kv.first;
}
}
return out.toList();
}
/**jsdoc
* Returns a key-value object with active (non-zero) blendshapes.
* eg: { JawOpen: 1.0, ... }
* @example <caption>Get active blendshape map</caption>
* print(JSON.stringify(KasenAPIExample.getActiveBlendshapes(MyAvatar.sessionUUID)));
*/
QVariant getActiveBlendshapes(const QUuid& avatarID) const {
if (auto head = getAvatarHead(avatarID)) {
return head->toJson()["blendShapes"].toVariant();
}
return {};
}
QVariant getBlendshapeMapping(const QUuid& avatarID) const {
QVariantMap out;
if (auto head = getAvatarHead(avatarID)) {
for (const auto& kv : head->getBlendshapeMap().toStdMap()) {
out[kv.first] = kv.second;
}
}
return out;
}
QVariant getBlendshapes(const QUuid& avatarID) const {
QVariantMap result;
if (auto head = getAvatarHead(avatarID)) {
QStringList names = getBlendshapeNames(avatarID);
auto states = head->getBlendshapeStates();
result = {
{ "base", zipNonZeroValues(names, states.base) },
{ "summed", zipNonZeroValues(names, states.summed) },
{ "transient", zipNonZeroValues(names, states.transient) },
};
}
return result;
}
private:
static QVariantMap zipNonZeroValues(const QStringList& keys, const QVector<float>& values) {
QVariantMap out;
for (int i=1; i < values.size(); i++) {
if (fabs(values[i]) > 1.0e-6) {
out[keys.value(i)] = values[i];
}
}
return out;
}
struct _HeadHelper : public HeadData {
QMap<QString,int> getBlendshapeMap() const {
return _blendshapeLookupMap;
}
struct States { QVector<float> base, summed, transient; };
States getBlendshapeStates() const {
return {
_blendshapeCoefficients,
_summedBlendshapeCoefficients,
_transientBlendshapeCoefficients
};
}
};
static const _HeadHelper* getAvatarHead(const QUuid& avatarID) {
auto avatars = DependencyManager::get<AvatarHashMap>();
auto avatar = avatars ? avatars->getAvatarBySessionID(avatarID) : nullptr;
auto head = avatar ? avatar->getHeadData() : nullptr;
return reinterpret_cast<const _HeadHelper*>(head);
}
};
}
const QLoggingCategory& example::logger{ custom_api_example::logger };
#include "KasenAPIExample.moc"

View file

@ -0,0 +1,21 @@
{
"name":"Kasen JS API Example",
"version": 1,
"package": {
"author": "Revofire",
"homepage": "www.realities.dev",
"version": "0.0.1",
"engines": {
"hifi-interface": ">= 0.83.0",
"hifi-assignment-client": ">= 0.83.0"
},
"config": {
"client": true,
"entity_client": true,
"entity_server": true,
"edit_filter": true,
"agent": true,
"avatar": true
}
}
}

View file

@ -16,11 +16,13 @@
// *************************************
// #region dependencies
// The information needed to properly use the sprite sheets and get the general information
// about the emojis
var emojiList = Script.require("./emojiApp/resources/modules/emojiList.js");
var customEmojiList = Script.require("./emojiApp/resources/modules/customEmojiList.js");
// #endregion
// *************************************
// END dependencies
@ -181,6 +183,7 @@ function maybeClearClapSoundInterval() {
}
}
// URLs for this fn are relative to SimplifiedEmoteIndicator.qml
function toggleReaction(reaction) {
var reactionEnding = reactionsBegun.indexOf(reaction) > -1;
@ -192,6 +195,7 @@ function toggleReaction(reaction) {
}
}
function maybeDeleteRemoteIndicatorTimeout() {
if (restoreEmoteIndicatorTimeout) {
Script.clearTimeout(restoreEmoteIndicatorTimeout);
@ -199,6 +203,7 @@ function maybeDeleteRemoteIndicatorTimeout() {
}
}
var reactionsBegun = [];
var pointReticle = null;
var mouseMoveEventsConnected = false;
@ -229,6 +234,7 @@ function beginReactionWrapper(reaction) {
}
}
// Checks to see if there are any reticle entities already to delete
function deleteOldReticles() {
MyAvatar.getAvatarEntitiesVariant()
@ -313,6 +319,7 @@ function triggerReactionWrapper(reaction) {
}, WAIT_TO_RESTORE_EMOTE_INDICATOR_ICON_MS);
}
function maybeClearReticleUpdateLimiterTimeout() {
if (reticleUpdateRateLimiterTimer) {
Script.clearTimeout(reticleUpdateRateLimiterTimer);
@ -393,6 +400,7 @@ function onMessageFromEmoteAppBar(message) {
}
}
function getEmojiURLFromCode(code) {
var emojiObject = emojiList[emojiCodeMap[code]];
var emojiFilename;
@ -405,6 +413,7 @@ function getEmojiURLFromCode(code) {
return "../../emojiApp/resources/images/emojis/52px/" + emojiFilename;
}
function updateEmoteIndicatorIcon(iconURL) {
emoteAppBarWindow.sendToQml({
"source": "simplifiedEmote.js",
@ -451,7 +460,10 @@ function keyPressHandler(event) {
} else if (event.text === RAISE_HAND_KEY) {
toggleReaction("raiseHand");
} else if (event.text === APPLAUD_KEY) {
toggleReaction("applaud");
// Make sure this doesn't get triggered if you are flying, falling, or jumping
if (!MyAvatar.isInAir()) {
toggleReaction("applaud");
}
} else if (event.text === POINT_KEY) {
toggleReaction("point");
} else if (event.text === EMOTE_WINDOW && !(Settings.getValue("io.highfidelity.isEditing", false))) {
@ -639,6 +651,7 @@ function unload() {
// *************************************
// #region EMOJI_UTILITY
var EMOJI_52_BASE_URL = "../../resources/images/emojis/52px/";
function selectedEmoji(code) {
emojiAPI.addEmoji(code);
@ -744,6 +757,7 @@ function toggleEmojiApp() {
emojiAPI.registerAvimojiQMLWindow(emojiAppWindow);
}
// #endregion
// *************************************
// END EMOJI_MAIN

View file

@ -445,7 +445,7 @@ void DomainBaker::handleFinishedModelBaker() {
// enumerate the QJsonRef values for the URL of this model from our multi hash of
// entity objects needing a URL re-write
for (auto propertyEntityPair : _entitiesNeedingRewrite.values(baker->getModelURL())) {
for (auto propertyEntityPair : _entitiesNeedingRewrite.values(baker->getOriginalInputModelURL())) {
QString property = propertyEntityPair.first;
// convert the entity QJsonValueRef to a QJsonObject so we can modify its URL
auto entity = propertyEntityPair.second.toObject();
@ -485,10 +485,10 @@ void DomainBaker::handleFinishedModelBaker() {
}
// remove the baked URL from the multi hash of entities needing a re-write
_entitiesNeedingRewrite.remove(baker->getModelURL());
_entitiesNeedingRewrite.remove(baker->getOriginalInputModelURL());
// drop our shared pointer to this baker so that it gets cleaned up
_modelBakers.remove(baker->getModelURL());
_modelBakers.remove(baker->getOriginalInputModelURL());
// emit progress to tell listeners how many models we have baked
emit bakeProgress(++_completedSubBakes, _totalNumberOfSubBakes);