mirror of
https://github.com/lubosz/overte.git
synced 2025-04-27 00:55:42 +02:00
commit
32db63e177
9 changed files with 282 additions and 65 deletions
interface/src/scripting
MenuScriptingInterface.cppMenuScriptingInterface.hWindowScriptingInterface.cppWindowScriptingInterface.h
libraries/ui/src/ui
scripts/developer/tests/performance
|
@ -125,3 +125,8 @@ void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool
|
|||
Q_ARG(const QString&, menuOption),
|
||||
Q_ARG(bool, isChecked));
|
||||
}
|
||||
|
||||
void MenuScriptingInterface::triggerOption(const QString& menuOption) {
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "triggerOption", Q_ARG(const QString&, menuOption));
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,8 @@ public slots:
|
|||
|
||||
bool isOptionChecked(const QString& menuOption);
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
void triggerOption(const QString& menuOption);
|
||||
|
||||
signals:
|
||||
void menuItemEvent(const QString& menuItem);
|
||||
|
|
|
@ -206,3 +206,7 @@ void WindowScriptingInterface::takeSnapshot(bool notify, float aspectRatio) {
|
|||
void WindowScriptingInterface::shareSnapshot(const QString& path) {
|
||||
qApp->shareSnapshot(path);
|
||||
}
|
||||
|
||||
bool WindowScriptingInterface::isPhysicsEnabled() {
|
||||
return qApp->isPhysicsEnabled();
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ public slots:
|
|||
void copyToClipboard(const QString& text);
|
||||
void takeSnapshot(bool notify = true, float aspectRatio = 0.0f);
|
||||
void shareSnapshot(const QString& path);
|
||||
bool isPhysicsEnabled();
|
||||
|
||||
signals:
|
||||
void domainChanged(const QString& domainHostname);
|
||||
|
|
|
@ -62,7 +62,6 @@ public:
|
|||
MenuWrapper* getMenu(const QString& menuName);
|
||||
MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu);
|
||||
|
||||
void triggerOption(const QString& menuOption);
|
||||
QAction* getActionForOption(const QString& menuOption);
|
||||
|
||||
QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
|
@ -112,6 +111,8 @@ public slots:
|
|||
|
||||
void toggleDeveloperMenus();
|
||||
void toggleAdvancedMenus();
|
||||
|
||||
void triggerOption(const QString& menuOption);
|
||||
|
||||
static bool isSomeSubmenuShown() { return _isSomeSubmenuShown; }
|
||||
|
||||
|
|
95
scripts/developer/tests/performance/crowd-agent.js
Normal file
95
scripts/developer/tests/performance/crowd-agent.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
"use strict";
|
||||
/*jslint vars: true, plusplus: true*/
|
||||
/*global Agent, Avatar, Script, Entities, Vec3, Quat, print*/
|
||||
//
|
||||
// crowd-agent.js
|
||||
// scripts/developer/tests/performance/
|
||||
//
|
||||
// Created by Howard Stearns on 9/29/16.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Add this to domain-settings scripts url with n instances. It will lie dormant until
|
||||
// a script like summon.js calls up to n avatars to be around you.
|
||||
|
||||
var MESSAGE_CHANNEL = "io.highfidelity.summon-crowd";
|
||||
|
||||
print('crowd-agent version 1');
|
||||
|
||||
/* Observations:
|
||||
- File urls for AC scripts silently fail. Use a local server (e.g., python SimpleHTTPServer) for development.
|
||||
- URLs are cached regardless of server headers. Must use cache-defeating query parameters.
|
||||
- JSON.stringify(Avatar) silently fails (even when Agent.isAvatar)
|
||||
*/
|
||||
|
||||
function startAgent(parameters) { // Can also be used to update.
|
||||
print('crowd-agent starting params', JSON.stringify(parameters), JSON.stringify(Agent));
|
||||
Agent.isAvatar = true;
|
||||
if (parameters.position) {
|
||||
Avatar.position = parameters.position;
|
||||
}
|
||||
if (parameters.orientation) {
|
||||
Avatar.orientation = parameters.orientation;
|
||||
}
|
||||
if (parameters.skeletonModelURL) {
|
||||
Avatar.skeletonModelURL = parameters.skeletonModelURL;
|
||||
}
|
||||
if (parameters.animationData) {
|
||||
data = parameters.animationData;
|
||||
Avatar.startAnimation(data.url, data.fps || 30, 1.0, (data.loopFlag === undefined) ? true : data.loopFlag, false, data.startFrame || 0, data.endFrame);
|
||||
}
|
||||
print('crowd-agent avatars started');
|
||||
}
|
||||
function stopAgent(parameters) {
|
||||
Agent.isAvatar = false;
|
||||
print('crowd-agent stopped', JSON.stringify(parameters), JSON.stringify(Agent));
|
||||
}
|
||||
|
||||
function messageSend(message) {
|
||||
Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message));
|
||||
}
|
||||
function messageHandler(channel, messageString, senderID) {
|
||||
if (channel !== MESSAGE_CHANNEL) {
|
||||
return;
|
||||
}
|
||||
print('crowd-agent message', channel, messageString, senderID);
|
||||
if (Agent.sessionUUID === senderID) { // ignore my own
|
||||
return;
|
||||
}
|
||||
var message = {};
|
||||
try {
|
||||
message = JSON.parse(messageString);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
switch (message.key) {
|
||||
case "HELO":
|
||||
messageSend({key: 'hello'}); // Allow the coordinator to count responses and make assignments.
|
||||
break;
|
||||
case 'hello': // ignore responses (e.g., from other agents)
|
||||
break;
|
||||
case "SUMMON":
|
||||
if (message.rcpt === Agent.sessionUUID) {
|
||||
startAgent(message);
|
||||
}
|
||||
break;
|
||||
case "STOP":
|
||||
if (message.rcpt === Agent.sessionUUID) {
|
||||
stopAgent(message);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
print("crowd-agent received unrecognized message:", channel, messageString, senderID);
|
||||
}
|
||||
}
|
||||
Messages.subscribe(MESSAGE_CHANNEL);
|
||||
Messages.messageReceived.connect(messageHandler);
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
print('crowd-agent shutting down');
|
||||
Messages.messageReceived.disconnect(messageHandler);
|
||||
Messages.unsubscribe(MESSAGE_CHANNEL);
|
||||
print('crowd-agent unsubscribed');
|
||||
});
|
|
@ -20,15 +20,18 @@ var EXPECTED_HMD_FRAMERATE = 90;
|
|||
var MAXIMUM_LOAD_TIME = 60; // seconds
|
||||
var MINIMUM_AVATARS = 25; // FIXME: not implemented yet. Requires agent scripts. Idea is to have them organize themselves to the right number.
|
||||
|
||||
var version = 1;
|
||||
var version = 2;
|
||||
function debug() {
|
||||
print.apply(null, [].concat.apply(['hrs fixme', version], [].map.call(arguments, JSON.stringify)));
|
||||
}
|
||||
|
||||
var emptyishPlace = 'empty';
|
||||
var cachePlaces = ['localhost', 'Welcome'];
|
||||
var isInCachePlace = cachePlaces.indexOf(location.hostname) >= 0;
|
||||
var defaultPlace = isInCachePlace ? 'Playa' : location.hostname;
|
||||
function isNowIn(place) { // true if currently in specified place
|
||||
return location.hostname.toLowerCase() === place.toLowerCase();
|
||||
}
|
||||
|
||||
var cachePlaces = ['dev-Welcome', 'localhost']; // For now, list the lighter weight one first.
|
||||
var isInCachePlace = cachePlaces.some(isNowIn);
|
||||
var defaultPlace = isInCachePlace ? 'dev-Playa' : location.hostname;
|
||||
var prompt = "domain-check.js version " + version + "\n\nWhat place should we enter?";
|
||||
debug(cachePlaces, isInCachePlace, defaultPlace, prompt);
|
||||
var entryPlace = Window.prompt(prompt, defaultPlace);
|
||||
|
@ -73,10 +76,17 @@ function startTwirl(targetRotation, degreesPerUpdate, interval, strafeDistance,
|
|||
|
||||
function doLoad(place, continuationWithLoadTime) { // Go to place and call continuationWithLoadTime(loadTimeInSeconds)
|
||||
var start = Date.now(), timeout, onDownloadUpdate, finishedTwirl = false, loadTime;
|
||||
// There are two ways to learn of changes: connect to change signals, or poll.
|
||||
// Until we get reliable results, we'll poll.
|
||||
var POLL_INTERVAL = 500, poll;
|
||||
function setHandlers() {
|
||||
//Stats.downloadsPendingChanged.connect(onDownloadUpdate); downloadsChanged, and physics...
|
||||
poll = Script.setInterval(onDownloadUpdate, POLL_INTERVAL);
|
||||
}
|
||||
function clearHandlers() {
|
||||
debug('clearHandlers');
|
||||
Stats.downloadsPendingChanged.disconnect(onDownloadUpdate);
|
||||
Stats.downloadsChanged.disconnect(onDownloadUpdate);
|
||||
//Stats.downloadsPendingChanged.disconnect(onDownloadUpdate); downloadsChanged, and physics..
|
||||
Script.clearInterval(poll);
|
||||
}
|
||||
function waitForLoad(flag) {
|
||||
debug('entry', place, 'initial downloads/pending', Stats.downloads, Stats.downloadsPending);
|
||||
|
@ -93,13 +103,11 @@ function doLoad(place, continuationWithLoadTime) { // Go to place and call conti
|
|||
continuationWithLoadTime(loadTime);
|
||||
}
|
||||
});
|
||||
Stats.downloadsPendingChanged.connect(onDownloadUpdate);
|
||||
Stats.downloadsChanged.connect(onDownloadUpdate);
|
||||
setHandlers();
|
||||
}
|
||||
function isLoading() {
|
||||
// FIXME: This tells us when download are completed, but it doesn't tell us when the objects are parsed and loaded.
|
||||
// We really want something like _physicsEnabled, but that isn't signalled.
|
||||
return Stats.downloads || Stats.downloadsPending;
|
||||
// FIXME: We should also confirm that textures have loaded.
|
||||
return Stats.downloads || Stats.downloadsPending || !Window.isPhysicsEnabled();
|
||||
}
|
||||
onDownloadUpdate = function onDownloadUpdate() {
|
||||
debug('update downloads/pending', Stats.downloads, Stats.downloadsPending);
|
||||
|
@ -114,17 +122,9 @@ function doLoad(place, continuationWithLoadTime) { // Go to place and call conti
|
|||
}
|
||||
};
|
||||
|
||||
function doit() {
|
||||
debug('go', place);
|
||||
location.hostChanged.connect(waitForLoad);
|
||||
location.handleLookupString(place);
|
||||
}
|
||||
if (location.placename.toLowerCase() === place.toLowerCase()) {
|
||||
location.handleLookupString(emptyishPlace);
|
||||
Script.setTimeout(doit, 1000);
|
||||
} else {
|
||||
doit();
|
||||
}
|
||||
debug('go', place);
|
||||
location.hostChanged.connect(waitForLoad);
|
||||
location.handleLookupString(place);
|
||||
}
|
||||
|
||||
var config = Render.getConfig("Stats");
|
||||
|
@ -144,6 +144,7 @@ function doRender(continuation) {
|
|||
});
|
||||
}
|
||||
|
||||
var TELEPORT_PAUSE = 500;
|
||||
function maybePrepareCache(continuation) {
|
||||
var prepareCache = Window.confirm("Prepare cache?\n\n\
|
||||
Should we start with all and only those items cached that are encountered when visiting:\n" + cachePlaces.join(', ') + "\n\
|
||||
|
@ -151,8 +152,6 @@ If 'yes', cache will be cleared and we will visit these two, with a turn in each
|
|||
You would want to say 'no' (and make other preparations) if you were testing these places.");
|
||||
|
||||
if (prepareCache) {
|
||||
location.handleLookupString(emptyishPlace);
|
||||
Window.alert("Please do menu Edit->Reload Content (Clears all caches) and THEN press 'ok'.");
|
||||
function loadNext() {
|
||||
var place = cachePlaces.shift();
|
||||
doLoad(place, function (prepTime) {
|
||||
|
@ -164,16 +163,19 @@ You would want to say 'no' (and make other preparations) if you were testing the
|
|||
}
|
||||
});
|
||||
}
|
||||
loadNext();
|
||||
location.handleLookupString(cachePlaces[cachePlaces.length - 1]);
|
||||
Menu.triggerOption("Reload Content (Clears all caches)");
|
||||
Script.setTimeout(loadNext, TELEPORT_PAUSE);
|
||||
} else {
|
||||
continuation();
|
||||
location.handleLookupString(isNowIn(cachePlaces[0]) ? cachePlaces[1] : cachePlaces[0]);
|
||||
Script.setTimeout(continuation, TELEPORT_PAUSE);
|
||||
}
|
||||
}
|
||||
|
||||
function maybeRunTribbles(continuation) {
|
||||
if (Window.confirm("Run tribbles?\n\n\
|
||||
At most, only one participant should say yes.")) {
|
||||
Script.load('http://howard-stearns.github.io/models/scripts/tests/performance/tribbles.js'); // FIXME: replace with AWS
|
||||
Script.load('http://cdn.highfidelity.com/davidkelly/production/scripts/tests/performance/tribbles.js');
|
||||
Script.setTimeout(continuation, 3000);
|
||||
} else {
|
||||
continuation();
|
||||
|
|
103
scripts/developer/tests/performance/summon.js
Normal file
103
scripts/developer/tests/performance/summon.js
Normal file
|
@ -0,0 +1,103 @@
|
|||
"use strict";
|
||||
/*jslint vars: true, plusplus: true*/
|
||||
/*global Agent, Avatar, Script, Entities, Vec3, Quat, print*/
|
||||
//
|
||||
// crowd-agent.js
|
||||
// scripts/developer/tests/performance/
|
||||
//
|
||||
// Created by Howard Stearns on 9/29/16.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// See crowd-agent.js
|
||||
|
||||
var version = 1;
|
||||
var label = "summon";
|
||||
function debug() {
|
||||
print.apply(null, [].concat.apply([label, version], [].map.call(arguments, JSON.stringify)));
|
||||
}
|
||||
var MINIMUM_AVATARS = 25;
|
||||
var DENSITY = 0.3; // square meters per person. Some say 10 sq ft is arm's length (0.9m^2), 4.5 is crowd (0.4m^2), 2.5 is mosh pit (0.2m^2).
|
||||
var spread = Math.sqrt(MINIMUM_AVATARS * DENSITY); // meters
|
||||
var turnSpread = 90; // How many degrees should turn from front range over.
|
||||
|
||||
function coord() { return (Math.random() * spread) - (spread / 2); } // randomly distribute a coordinate zero += spread/2.
|
||||
|
||||
|
||||
var summonedAgents = [];
|
||||
var MESSAGE_CHANNEL = "io.highfidelity.summon-crowd";
|
||||
function messageSend(message) {
|
||||
Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message));
|
||||
}
|
||||
function messageHandler(channel, messageString, senderID) {
|
||||
if (channel !== MESSAGE_CHANNEL) {
|
||||
return;
|
||||
}
|
||||
debug('message', channel, messageString, senderID);
|
||||
if (MyAvatar.sessionUUID === senderID) { // ignore my own
|
||||
return;
|
||||
}
|
||||
var message = {}, avatarIdentifiers;
|
||||
try {
|
||||
message = JSON.parse(messageString);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
switch (message.key) {
|
||||
case "hello":
|
||||
// There can be avatars we've summoned that do not yet appear in the AvatarList.
|
||||
avatarIdentifiers = AvatarList.getAvatarIdentifiers().filter(function (id) { return summonedAgents.indexOf(id) === -1; });
|
||||
debug('present', avatarIdentifiers, summonedAgents);
|
||||
if ((summonedAgents.length + avatarIdentifiers.length) < MINIMUM_AVATARS ) {
|
||||
summonedAgents.push(senderID);
|
||||
messageSend({
|
||||
key: 'SUMMON',
|
||||
rcpt: senderID,
|
||||
position: Vec3.sum(MyAvatar.position, {x: coord(), y: 0, z: coord()}),
|
||||
orientation: Quat.fromPitchYawRollDegrees(0, Quat.safeEulerAngles(MyAvatar.orientation).y + (turnSpread * (Math.random() - 0.5)), 0)/*,
|
||||
// No need to specify skeletonModelURL
|
||||
//skeletonModelURL: "file:///c:/Program Files/High Fidelity Release/resources/meshes/being_of_light/being_of_light.fbx",
|
||||
//skeletonModelURL: "file:///c:/Program Files/High Fidelity Release/resources/meshes/defaultAvatar_full.fst"/,
|
||||
animationData: { // T-pose until we get animations working again.
|
||||
"url": "file:///C:/Program Files/High Fidelity Release/resources/avatar/animations/idle.fbx",
|
||||
//"url": "file:///c:/Program Files/High Fidelity Release/resources/avatar/animations/walk_fwd.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 300.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
}*/
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "HELO":
|
||||
Window.alert("Someone else is summoning avatars.");
|
||||
break;
|
||||
default:
|
||||
print("crowd-agent received unrecognized message:", messageString);
|
||||
}
|
||||
}
|
||||
Messages.subscribe(MESSAGE_CHANNEL);
|
||||
Messages.messageReceived.connect(messageHandler);
|
||||
Script.scriptEnding.connect(function () {
|
||||
debug('stopping agents', summonedAgents);
|
||||
summonedAgents.forEach(function (id) { messageSend({key: 'STOP', rcpt: id}); });
|
||||
debug('agents stopped');
|
||||
Script.setTimeout(function () {
|
||||
Messages.messageReceived.disconnect(messageHandler);
|
||||
Messages.unsubscribe(MESSAGE_CHANNEL);
|
||||
debug('unsubscribed');
|
||||
}, 500);
|
||||
});
|
||||
|
||||
messageSend({key: 'HELO'}); // Ask agents to report in now.
|
||||
Script.setTimeout(function () {
|
||||
if (0 === summonedAgents.length) {
|
||||
Window.alert("No agents reported.\n\Please run " + MINIMUM_AVATARS + " instances of\n\
|
||||
http://cdn.highfidelity.com/davidkelly/production/scripts/tests/performance/crowd-agent.js\n\
|
||||
on your domain server.");
|
||||
} else if (summonedAgents.length < MINIMUM_AVATARS) {
|
||||
Window.alert("Only " + summonedAgents.length + " of the expected " + MINIMUM_AVATARS + " agents reported in.");
|
||||
}
|
||||
}, 5000);
|
|
@ -54,42 +54,46 @@ function randomVector(range) {
|
|||
};
|
||||
}
|
||||
|
||||
Script.setInterval(function () {
|
||||
if (!Entities.serversExist() || !Entities.canRez()) {
|
||||
return;
|
||||
}
|
||||
if (totalCreated >= NUMBER_TO_CREATE) {
|
||||
print("Created " + totalCreated + " tribbles.");
|
||||
Script.stop();
|
||||
}
|
||||
if (!Entities.canRezTmp()) {
|
||||
Window.alert("Cannot create temp objects here.");
|
||||
Script.stop();
|
||||
} else {
|
||||
Script.setInterval(function () {
|
||||
if (!Entities.serversExist()) {
|
||||
return;
|
||||
}
|
||||
if (totalCreated >= NUMBER_TO_CREATE) {
|
||||
print("Created " + totalCreated + " tribbles.");
|
||||
Script.stop();
|
||||
}
|
||||
|
||||
var i, numToCreate = RATE_PER_SECOND * (SCRIPT_INTERVAL / 1000.0);
|
||||
var parameters = JSON.stringify({
|
||||
moveTimeout: MOVE_TIMEOUT,
|
||||
moveRate: MOVE_RATE,
|
||||
editTimeout: EDIT_TIMEOUT,
|
||||
editRate: EDIT_RATE,
|
||||
debug: {flow: false, send: false, receive: false}
|
||||
});
|
||||
for (i = 0; (i < numToCreate) && (totalCreated < NUMBER_TO_CREATE); i++) {
|
||||
Entities.addEntity({
|
||||
userData: parameters,
|
||||
type: TYPE,
|
||||
name: "tribble-" + totalCreated,
|
||||
position: Vec3.sum(center, randomVector({ x: RANGE, y: RANGE, z: RANGE })),
|
||||
dimensions: {x: SIZE, y: SIZE, z: SIZE},
|
||||
color: {red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255},
|
||||
velocity: VELOCITY,
|
||||
angularVelocity: Vec3.multiply(Math.random(), ANGULAR_VELOCITY),
|
||||
damping: DAMPING,
|
||||
angularDamping: ANGULAR_DAMPING,
|
||||
gravity: GRAVITY,
|
||||
collisionsWillMove: true,
|
||||
lifetime: LIFETIME,
|
||||
script: Script.resolvePath("tribbleEntity.js")
|
||||
var i, numToCreate = RATE_PER_SECOND * (SCRIPT_INTERVAL / 1000.0);
|
||||
var parameters = JSON.stringify({
|
||||
moveTimeout: MOVE_TIMEOUT,
|
||||
moveRate: MOVE_RATE,
|
||||
editTimeout: EDIT_TIMEOUT,
|
||||
editRate: EDIT_RATE,
|
||||
debug: {flow: false, send: false, receive: false}
|
||||
});
|
||||
for (i = 0; (i < numToCreate) && (totalCreated < NUMBER_TO_CREATE); i++) {
|
||||
Entities.addEntity({
|
||||
userData: parameters,
|
||||
type: TYPE,
|
||||
name: "tribble-" + totalCreated,
|
||||
position: Vec3.sum(center, randomVector({ x: RANGE, y: RANGE, z: RANGE })),
|
||||
dimensions: {x: SIZE, y: SIZE, z: SIZE},
|
||||
color: {red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255},
|
||||
velocity: VELOCITY,
|
||||
angularVelocity: Vec3.multiply(Math.random(), ANGULAR_VELOCITY),
|
||||
damping: DAMPING,
|
||||
angularDamping: ANGULAR_DAMPING,
|
||||
gravity: GRAVITY,
|
||||
collisionsWillMove: true,
|
||||
lifetime: LIFETIME,
|
||||
script: Script.resolvePath("tribbleEntity.js")
|
||||
});
|
||||
|
||||
totalCreated++;
|
||||
}
|
||||
}, SCRIPT_INTERVAL);
|
||||
|
||||
totalCreated++;
|
||||
}
|
||||
}, SCRIPT_INTERVAL);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue