diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 72eab372cd..4f1153b4f4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,4 +1,4 @@
-The project embraces distributed development and if you'd like to help, we'll pay you -- find out more at Worklist.net. If you find a small bug and have a fix, pull requests are welcome. If you'd like to get paid for your work, make sure you report the bug via a job on Worklist.net.
+The project embraces distributed development and if you'd like to help, we'll pay you -- find out more at [Worklist.net](https://worklist.net). If you find a small bug and have a fix, pull requests are welcome. If you'd like to get paid for your work, make sure you report the bug via a job on Worklist.net.
We're hiring! We're looking for skilled developers; send your resume to hiring@highfidelity.io
@@ -16,7 +16,7 @@ Contributing
git checkout -b new_branch_name
```
4. Code
- * Follow the [coding standard](https://github.com/highfidelity/hifi/wiki/Coding-Standard)
+ * Follow the [coding standard](http://docs.highfidelity.io/v1.0/docs/coding-standard)
5. Commit
* Use [well formed commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
6. Update your branch
@@ -38,11 +38,11 @@ Contributing
Reporting Bugs
===
-1. Always update to the latest code on master, it is possible the bug has already been fixed!
-2. Search the [repository issues](https://github.com/highfidelity/hifi/issues) to make sure that somebody has not already reported the same bug.
-3. Open an [issue on GitHub](https://github.com/highfidelity/hifi/issues) including information about your system and how to reproduce the bug.
+1. Always update to the latest code on master, we make many merges every day and it is possible the bug has already been fixed!
+2. Search jobs [on Worklist](https://worklist.net) to make sure that somebody has not already reported the same bug.
+3. Add a [job on Worklist](https://worklist.net/job/add) including information about your system and how to reproduce the bug.
Requesting a feature
===
-1. Search the [repository issues](https://github.com/highfidelity/hifi/issues) to make sure that somebody has not already requested the same feature. If you find a matching request, feel free to add any additional comments to the existing issue.
-2. Submit an [issue on GitHub](https://github.com/highfidelity/hifi/issues) that is tagged as a feature.
\ No newline at end of file
+1. Search the [the Worklist](https://worklist.net) to make sure that somebody has not already requested the same feature. If you find a matching request, feel free to add any additional comments to the existing issue.
+2. Add a [job on Worklist](https://worklist.net/job/add) that is labeled as a Feature (and select any other appropriate Labels) and includes a detailed description of your request.
diff --git a/README.md b/README.md
index d5fa8de1b4..90f1ca0dc7 100644
--- a/README.md
+++ b/README.md
@@ -3,10 +3,10 @@ lab experimenting with Virtual Worlds and VR.
In this repository you'll find the source to many of the components in our
alpha-stage virtual world. The project embraces distributed development
-and if you'd like to help, we'll pay you -- find out more at Worklist.net.
+and if you'd like to help, we'll pay you -- find out more at [Worklist.net](https://worklist.net).
If you find a small bug and have a fix, pull requests are welcome. If you'd
like to get paid for your work, make sure you report the bug via a job on
-Worklist.net.
+[Worklist.net](https://worklist.net).
We're hiring! We're looking for skilled developers;
send your resume to hiring@highfidelity.io
@@ -14,6 +14,10 @@ send your resume to hiring@highfidelity.io
##### Chat with us
Come chat with us in [our Gitter](http://gitter.im/highfidelity/hifi) if you have any questions or just want to say hi!
+Documentation
+=========
+Documentation is available at [docs.highfidelity.io](http://docs.highfidelity.io), if something is missing, please suggest it via a new job on Worklist (add to the hifi-docs project).
+
Build Instructions
=========
All information required to build is found in the [build guide](BUILD.md).
diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp
index ea49b90852..140742ce88 100644
--- a/assignment-client/src/Agent.cpp
+++ b/assignment-client/src/Agent.cpp
@@ -206,7 +206,7 @@ void Agent::run() {
scriptURL = QUrl(_payload);
}
- NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply *reply = networkAccessManager.get(QNetworkRequest(scriptURL));
QNetworkDiskCache* cache = new QNetworkDiskCache();
diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp
index 24b6127d63..b30cd355d1 100644
--- a/assignment-client/src/AssignmentClient.cpp
+++ b/assignment-client/src/AssignmentClient.cpp
@@ -59,6 +59,7 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
const QString ASSIGNMENT_POOL_OPTION = "pool";
const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "wallet";
const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "a";
+ const QString CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION = "p";
Assignment::Type requestAssignmentType = Assignment::AllTypes;
@@ -87,17 +88,29 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
// create a NodeList as an unassigned client
NodeList* nodeList = NodeList::createInstance(NodeType::Unassigned);
+
+ unsigned short assignmentServerPort = DEFAULT_DOMAIN_SERVER_PORT;
+
+ // check for an overriden assignment server port
+ if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION)) {
+ assignmentServerPort =
+ argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toString().toUInt();
+ }
+
+ HifiSockAddr assignmentServerSocket(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME, assignmentServerPort);
// check for an overriden assignment server hostname
if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION)) {
_assignmentServerHostname = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION).toString();
- // set the custom assignment socket on our NodeList
- HifiSockAddr customAssignmentSocket = HifiSockAddr(_assignmentServerHostname, DEFAULT_DOMAIN_SERVER_PORT);
-
- nodeList->setAssignmentServerSocket(customAssignmentSocket);
+ // change the hostname for our assignment server
+ assignmentServerSocket = HifiSockAddr(_assignmentServerHostname, assignmentServerSocket.getPort());
}
+
+ nodeList->setAssignmentServerSocket(assignmentServerSocket);
+ qDebug() << "Assignment server socket is" << assignmentServerSocket;
+
// call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required
qDebug() << "Waiting for assignment -" << _requestAssignment;
diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp
index 681cc58cb3..37d89f3790 100644
--- a/assignment-client/src/audio/AudioMixer.cpp
+++ b/assignment-client/src/audio/AudioMixer.cpp
@@ -364,7 +364,6 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l
const float ZERO_DB = 1.0f;
const float NEGATIVE_ONE_DB = 0.891f;
const float NEGATIVE_THREE_DB = 0.708f;
- const float NEGATIVE_SIX_DB = 0.501f;
const float FILTER_GAIN_AT_0 = ZERO_DB; // source is in front
const float FILTER_GAIN_AT_90 = NEGATIVE_ONE_DB; // source is incident to left or right ear
diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp
index 5833d8a764..42af8ff1b9 100644
--- a/assignment-client/src/octree/OctreeServer.cpp
+++ b/assignment-client/src/octree/OctreeServer.cpp
@@ -940,7 +940,7 @@ void OctreeServer::run() {
qDebug("--statusHost=%s", statusHost);
_statusHost = statusHost;
} else {
- _statusHost = QHostAddress(getHostOrderLocalAddress()).toString();
+ _statusHost = getLocalAddress().toString();
}
qDebug("statusHost=%s", qPrintable(_statusHost));
diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json
index 2fbe33a4e1..568931e074 100644
--- a/domain-server/resources/describe-settings.json
+++ b/domain-server/resources/describe-settings.json
@@ -33,6 +33,14 @@
"label": "None: use the network information I have entered for this domain at data.highfidelity.io"
}
]
+ },
+ {
+ "name": "local_port",
+ "label": "Local UDP Port",
+ "help": "This is the local port your domain-server binds to for UDP connections.
Depending on your router, this may need to be changed to run multiple full automatic networking domain-servers in the same network.",
+ "default": "40102",
+ "type": "int",
+ "advanced": true
}
]
},
diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js
index 48b3b15e8f..fb6901b34d 100644
--- a/domain-server/resources/web/js/settings.js
+++ b/domain-server/resources/web/js/settings.js
@@ -46,6 +46,11 @@ var viewHelpers = {
form_group += ""
} else {
+
+ if (input_type == 'integer') {
+ input_type = "text"
+ }
+
form_group += ""
@@ -292,7 +297,7 @@ function chooseFromHighFidelityDomains(clickedButton) {
modal_buttons["success"] = {
label: 'Create new domain',
callback: function() {
- window.open("https://data.highfidelity.io/domains", '_blank');
+ window.open("https://data.highfidelity.io/user/domains", '_blank');
}
}
modal_body = "
You do not have any domains in your High Fidelity account." +
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 61310cad75..fa7a0fe012 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -57,6 +57,10 @@ DomainServer::DomainServer(int argc, char* argv[]) :
setApplicationName("domain-server");
QSettings::setDefaultFormat(QSettings::IniFormat);
+ // make sure we have a fresh AccountManager instance
+ // (need this since domain-server can restart itself and maintain static variables)
+ AccountManager::getInstance(true);
+
_settingsManager.setupConfigMap(arguments());
installNativeEventFilter(&_shutdownEventListener);
@@ -82,10 +86,6 @@ DomainServer::DomainServer(int argc, char* argv[]) :
void DomainServer::restart() {
qDebug() << "domain-server is restarting.";
- // make sure all static instances are reset
- LimitedNodeList::getInstance()->reset();
- AccountManager::getInstance(true);
-
exit(DomainServer::EXIT_CODE_REBOOT);
}
@@ -189,17 +189,20 @@ bool DomainServer::optionallySetupOAuth() {
const QString DOMAIN_CONFIG_ID_KEY = "id";
+const QString METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH = "metaverse.automatic_networking";
+const QString FULL_AUTOMATIC_NETWORKING_VALUE = "full";
+const QString IP_ONLY_AUTOMATIC_NETWORKING_VALUE = "ip";
+const QString DISABLED_AUTOMATIC_NETWORKING_VALUE = "disabled";
+
void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
- const QString CUSTOM_PORT_OPTION = "port";
- unsigned short domainServerPort = DEFAULT_DOMAIN_SERVER_PORT;
+ const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port";
+
+ QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
+ unsigned short domainServerPort = (unsigned short) localPortValue.toUInt();
QVariantMap& settingsMap = _settingsManager.getSettingsMap();
- if (settingsMap.contains(CUSTOM_PORT_OPTION)) {
- domainServerPort = (unsigned short) settingsMap.value(CUSTOM_PORT_OPTION).toUInt();
- }
-
unsigned short domainServerDTLSPort = 0;
if (_isUsingDTLS) {
@@ -310,12 +313,7 @@ bool DomainServer::optionallySetupAssignmentPayment() {
return true;
}
-const QString FULL_AUTOMATIC_NETWORKING_VALUE = "full";
-const QString IP_ONLY_AUTOMATIC_NETWORKING_VALUE = "ip";
-const QString DISABLED_AUTOMATIC_NETWORKING_VALUE = "disabled";
-
void DomainServer::setupAutomaticNetworking() {
- const QString METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH = "metaverse.automatic_networking";
if (!didSetupAccountManagerWithAccessToken()) {
qDebug() << "Cannot setup domain-server automatic networking without an access token.";
@@ -357,7 +355,8 @@ void DomainServer::setupAutomaticNetworking() {
connect(iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::performICEUpdates);
iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS);
- // call our sendHeartbeaToIceServer immediately anytime a public address changes
+ // call our sendHeartbeaToIceServer immediately anytime a local or public socket changes
+ connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHearbeatToIceServer);
connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHearbeatToIceServer);
// tell the data server which type of automatic networking we are using
diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp
index 28b1151f2d..7c92f1cc51 100644
--- a/domain-server/src/DomainServerSettingsManager.cpp
+++ b/domain-server/src/DomainServerSettingsManager.cpp
@@ -254,11 +254,14 @@ void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
QString settingType = groupObject[SETTING_DESCRIPTION_TYPE_KEY].toString();
const QString INPUT_DOUBLE_TYPE = "double";
+ const QString INPUT_INTEGER_TYPE = "int";
// make sure the resulting json value has the right type
if (settingType == INPUT_DOUBLE_TYPE) {
settingsVariant[key] = rootValue.toString().toDouble();
+ } else if (settingType == INPUT_INTEGER_TYPE) {
+ settingsVariant[key] = rootValue.toString().toInt();
} else {
settingsVariant[key] = rootValue.toString();
}
diff --git a/examples/editModels.js b/examples/editModels.js
index 9cdba8098f..2741bdccdd 100644
--- a/examples/editModels.js
+++ b/examples/editModels.js
@@ -1215,19 +1215,44 @@ var toolBar = (function () {
Overlays.editOverlay(loadFileMenuItem, { visible: active });
}
+ var RESIZE_INTERVAL = 50;
+ var RESIZE_TIMEOUT = 20000;
+ var RESIZE_MAX_CHECKS = RESIZE_TIMEOUT / RESIZE_INTERVAL;
function addModel(url) {
var position;
position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE));
if (position.x > 0 && position.y > 0 && position.z > 0) {
- Entities.addEntity({
+ var entityId = Entities.addEntity({
type: "Model",
position: position,
dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION },
modelURL: url
});
print("Model added: " + url);
+
+ var checkCount = 0;
+ function resize() {
+ var entityProperties = Entities.getEntityProperties(entityId);
+ var naturalDimensions = entityProperties.naturalDimensions;
+
+ checkCount++;
+
+ if (naturalDimensions.x == 0 && naturalDimensions.y == 0 && naturalDimensions.z == 0) {
+ if (checkCount < RESIZE_MAX_CHECKS) {
+ Script.setTimeout(resize, RESIZE_INTERVAL);
+ } else {
+ print("Resize failed: timed out waiting for model (" + url + ") to load");
+ }
+ } else {
+ entityProperties.dimensions = naturalDimensions;
+ Entities.editEntity(entityId, entityProperties);
+ }
+ }
+
+ Script.setTimeout(resize, RESIZE_INTERVAL);
+
} else {
print("Can't add model: Model would be out of bounds.");
}
diff --git a/examples/leapHands.js b/examples/leapHands.js
index 1095a9f4dc..222c0e4cf1 100644
--- a/examples/leapHands.js
+++ b/examples/leapHands.js
@@ -30,8 +30,6 @@ var leapHands = (function () {
CALIBRATING = 1,
CALIBRATED = 2,
CALIBRATION_TIME = 1000, // milliseconds
- PI = 3.141593,
- isWindows,
avatarScale,
avatarFaceModelURL,
avatarSkeletonModelURL,
@@ -132,9 +130,6 @@ var leapHands = (function () {
if (hands[0].controller.isActive() && hands[1].controller.isActive()) {
leapHandHeight = (hands[0].controller.getAbsTranslation().y + hands[1].controller.getAbsTranslation().y) / 2.0;
-
- // TODO: Temporary detection of Windows to work around Leap Controller problem.
- isWindows = (hands[1].controller.getAbsRotation().z > (0.25 * PI));
} else {
calibrationStatus = UNCALIBRATED;
return;
@@ -318,11 +313,7 @@ var leapHands = (function () {
j,
side,
handOffset,
- handRoll,
- handPitch,
- handYaw,
handRotation,
- wristAbsRotation,
locRotation,
cameraOrientation,
inverseAvatarOrientation;
@@ -361,20 +352,22 @@ var leapHands = (function () {
handOffset.x = -handOffset.x;
// Hand rotation in camera coordinates ...
- // TODO: 2.0* scale factors should not be necessary; Leap Motion controller code needs investigating.
- handRoll = 2.0 * -hands[h].controller.getAbsRotation().z;
- wristAbsRotation = wrists[h].controller.getAbsRotation();
- handPitch = 2.0 * wristAbsRotation.x - PI / 2.0;
- handYaw = 2.0 * -wristAbsRotation.y;
- // TODO: Roll values only work if hand is upside down; Leap Motion controller code needs investigating.
- handRoll = PI + handRoll;
+ handRotation = wrists[h].controller.getAbsRotation();
+ handRotation = {
+ x: handRotation.z,
+ y: handRotation.y,
+ z: handRotation.x,
+ w: handRotation.w
+ };
if (h === 0) {
- handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 1, z: 0 }),
- Quat.fromVec3Radians({ x: handRoll, y: handYaw, z: -handPitch }));
+ handRotation.x = -handRotation.x;
+ handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
+ handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 0, z: 1 }), handRotation);
} else {
- handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 1, z: 0 }),
- Quat.fromVec3Radians({ x: -handRoll, y: handYaw, z: handPitch }));
+ handRotation.z = -handRotation.z;
+ handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
+ handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 0, z: 1 }), handRotation);
}
// Hand rotation in avatar coordinates ...
@@ -392,25 +385,22 @@ var leapHands = (function () {
z: hands[h].zeroPosition.z - handOffset.z
};
- // TODO: 2.0* scale factors should not be necessary; Leap Motion controller code needs investigating.
- handRoll = 2.0 * -hands[h].controller.getAbsRotation().z;
- wristAbsRotation = wrists[h].controller.getAbsRotation();
- handPitch = 2.0 * -wristAbsRotation.x;
- handYaw = 2.0 * wristAbsRotation.y;
+ handRotation = wrists[h].controller.getAbsRotation();
+ handRotation = {
+ x: handRotation.z,
+ y: handRotation.y,
+ z: handRotation.x,
+ w: handRotation.w
+ };
- // TODO: Leap Motion controller's right-hand roll calculation only works if physical hand is upside down.
- // Approximate fix is to add a fudge factor.
- if (h === 1 && isWindows) {
- handRoll = handRoll + 0.6 * PI;
- }
-
- // Hand position and orientation ...
if (h === 0) {
+ handRotation.x = -handRotation.x;
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 1, z: 0 }),
- Quat.fromVec3Radians({ x: handRoll, y: handYaw, z: -handPitch }));
+ handRotation);
} else {
+ handRotation.z = -handRotation.z;
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 1, z: 0 }),
- Quat.fromVec3Radians({ x: -handRoll, y: handYaw, z: handPitch }));
+ handRotation);
}
}
diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js
index e07fb752e5..d9cf2c54fd 100644
--- a/examples/libraries/entitySelectionTool.js
+++ b/examples/libraries/entitySelectionTool.js
@@ -169,7 +169,14 @@ SelectionDisplay = (function () {
alpha: 0.2,
solid: true,
visible: false,
- rotation: yawOverlayRotation
+ rotation: yawOverlayRotation,
+ hasTickMarks: true,
+ majorTickMarksAngle: 12.5,
+ minorTickMarksAngle: 0,
+ majorTickMarksLength: -0.25,
+ minorTickMarksLength: 0,
+ majorTickMarksColor: { red: 0, green: 0, blue: 0 },
+ minorTickMarksColor: { red: 0, green: 0, blue: 0 },
});
var rotateOverlayOuter = Overlays.addOverlay("circle3d", {
@@ -179,7 +186,15 @@ SelectionDisplay = (function () {
alpha: 0.2,
solid: true,
visible: false,
- rotation: yawOverlayRotation
+ rotation: yawOverlayRotation,
+
+ hasTickMarks: true,
+ majorTickMarksAngle: 45.0,
+ minorTickMarksAngle: 5,
+ majorTickMarksLength: 0.25,
+ minorTickMarksLength: 0.1,
+ majorTickMarksColor: { red: 0, green: 0, blue: 0 },
+ minorTickMarksColor: { red: 0, green: 0, blue: 0 },
});
var rotateOverlayCurrent = Overlays.addOverlay("circle3d", {
@@ -571,7 +586,7 @@ SelectionDisplay = (function () {
innerRadius: 0.9,
startAt: 0,
endAt: 360,
- alpha: outerAlpha
+ alpha: outerAlpha,
});
Overlays.editOverlay(rotateOverlayCurrent,
@@ -584,7 +599,7 @@ SelectionDisplay = (function () {
size: outerRadius,
startAt: 0,
endAt: 0,
- innerRadius: 0.9
+ innerRadius: 0.9,
});
// TODO: we have not implemented the rotating handle/controls yet... so for now, these handles are hidden
diff --git a/examples/libraries/globals.js b/examples/libraries/globals.js
index 211e0d56b6..1bd851af77 100644
--- a/examples/libraries/globals.js
+++ b/examples/libraries/globals.js
@@ -8,4 +8,4 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-var HIFI_PUBLIC_BUCKET = "https://s3.amazonaws.com/hifi-public/";
\ No newline at end of file
+HIFI_PUBLIC_BUCKET = "https://s3.amazonaws.com/hifi-public/";
diff --git a/examples/overlaysExample.js b/examples/overlaysExample.js
index 2161b1c903..fef502c761 100644
--- a/examples/overlaysExample.js
+++ b/examples/overlaysExample.js
@@ -64,8 +64,8 @@ var text = Overlays.addOverlay("text", {
y: 100,
width: 150,
height: 50,
- color: { red: 0, green: 0, blue: 0},
- textColor: { red: 255, green: 0, blue: 0},
+ backgroundColor: { red: 255, green: 255, blue: 255},
+ color: { red: 255, green: 0, blue: 0},
topMargin: 4,
leftMargin: 4,
text: "Here is some text.\nAnd a second line."
diff --git a/examples/walk.js b/examples/walk.js
new file mode 100644
index 0000000000..a9e8f401d6
--- /dev/null
+++ b/examples/walk.js
@@ -0,0 +1,3851 @@
+//
+// walk.js
+//
+// version 1.007b
+//
+// Created by Davedub, August / September 2014
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+// path to the animation files
+var pathToAnimFiles = 'http://s3-us-west-1.amazonaws.com/highfidelity-public/procedural-animator/animation-files/'; // working (but only without https)
+
+// path to the images used for the overlays
+var pathToOverlays = 'http://s3-us-west-1.amazonaws.com/highfidelity-public/procedural-animator/overlays/'; // working (but only without https)
+
+// path to the sounds used for the footsteps
+var pathToSounds = 'http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Footsteps/';
+
+// load the animation datafiles
+Script.include(pathToAnimFiles+"dd-female-strut-walk-animation.js");
+Script.include(pathToAnimFiles+"dd-female-flying-up-animation.js");
+Script.include(pathToAnimFiles+"dd-female-flying-animation.js");
+Script.include(pathToAnimFiles+"dd-female-flying-down-animation.js");
+Script.include(pathToAnimFiles+"dd-female-standing-one-animation.js");
+Script.include(pathToAnimFiles+"dd-female-sidestep-left-animation.js");
+Script.include(pathToAnimFiles+"dd-female-sidestep-right-animation.js");
+Script.include(pathToAnimFiles+"dd-male-strut-walk-animation.js");
+Script.include(pathToAnimFiles+"dd-male-flying-up-animation.js");
+Script.include(pathToAnimFiles+"dd-male-flying-animation.js");
+Script.include(pathToAnimFiles+"dd-male-flying-down-animation.js");
+Script.include(pathToAnimFiles+"dd-male-standing-one-animation.js");
+Script.include(pathToAnimFiles+"dd-male-sidestep-left-animation.js");
+Script.include(pathToAnimFiles+"dd-male-sidestep-right-animation.js");
+
+// read in the data from the animation files
+var FemaleStrutWalkFile = new FemaleStrutWalk();
+var femaleStrutWalk = FemaleStrutWalkFile.loadAnimation();
+var FemaleFlyingUpFile = new FemaleFlyingUp();
+var femaleFlyingUp = FemaleFlyingUpFile.loadAnimation();
+var FemaleFlyingFile = new FemaleFlying();
+var femaleFlying = FemaleFlyingFile.loadAnimation();
+var FemaleFlyingDownFile = new FemaleFlyingDown();
+var femaleFlyingDown = FemaleFlyingDownFile.loadAnimation();
+var FemaleStandOneFile = new FemaleStandingOne();
+var femaleStandOne = FemaleStandOneFile.loadAnimation();
+var FemaleSideStepLeftFile = new FemaleSideStepLeft();
+var femaleSideStepLeft = FemaleSideStepLeftFile.loadAnimation();
+var FemaleSideStepRightFile = new FemaleSideStepRight();
+var femaleSideStepRight = FemaleSideStepRightFile.loadAnimation();
+var MaleStrutWalkFile = new MaleStrutWalk();
+var maleStrutWalk = MaleStrutWalkFile.loadAnimation();
+var MaleFlyingUpFile = new MaleFlyingUp();
+var maleFlyingUp = MaleFlyingUpFile.loadAnimation();
+var MaleFlyingFile = new MaleFlying();
+var maleFlying = MaleFlyingFile.loadAnimation();
+var MaleFlyingDownFile = new MaleFlyingDown();
+var maleFlyingDown = MaleFlyingDownFile.loadAnimation();
+var MaleStandOneFile = new MaleStandingOne();
+var maleStandOne = MaleStandOneFile.loadAnimation();
+var MaleSideStepLeftFile = new MaleSideStepLeft();
+var maleSideStepLeft = MaleSideStepLeftFile.loadAnimation();
+var MaleSideStepRightFile = new MaleSideStepRight();
+var maleSideStepRight = MaleSideStepRightFile.loadAnimation();
+
+// read in the sounds
+var footsteps = [];
+footsteps.push(new Sound(pathToSounds+"FootstepW2Left-12db.wav"));
+footsteps.push(new Sound(pathToSounds+"FootstepW2Right-12db.wav"));
+footsteps.push(new Sound(pathToSounds+"FootstepW3Left-12db.wav"));
+footsteps.push(new Sound(pathToSounds+"FootstepW3Right-12db.wav"));
+footsteps.push(new Sound(pathToSounds+"FootstepW5Left-12db.wav"));
+footsteps.push(new Sound(pathToSounds+"FootstepW5Right-12db.wav"));
+
+// all slider controls have a range (with the exception of phase controls that are always +-180) so we store them all here
+var sliderRanges = {"joints":[{"name":"hips","pitchRange":25,"yawRange":25,"rollRange":25,"pitchOffsetRange":25,"yawOffsetRange":25,"rollOffsetRange":25,"thrustRange":0.01,"bobRange":0.02,"swayRange":0.01},{"name":"upperLegs","pitchRange":90,"yawRange":35,"rollRange":35,"pitchOffsetRange":60,"yawOffsetRange":20,"rollOffsetRange":20},{"name":"lowerLegs","pitchRange":90,"yawRange":20,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":20,"rollOffsetRange":20},{"name":"feet","pitchRange":60,"yawRange":20,"rollRange":20,"pitchOffsetRange":60,"yawOffsetRange":50,"rollOffsetRange":50},{"name":"toes","pitchRange":90,"yawRange":20,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":20,"rollOffsetRange":20},{"name":"spine","pitchRange":40,"yawRange":40,"rollRange":40,"pitchOffsetRange":90,"yawOffsetRange":50,"rollOffsetRange":50},{"name":"spine1","pitchRange":20,"yawRange":40,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":50,"rollOffsetRange":50},{"name":"spine2","pitchRange":20,"yawRange":40,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":50,"rollOffsetRange":50},{"name":"shoulders","pitchRange":35,"yawRange":40,"rollRange":20,"pitchOffsetRange":180,"yawOffsetRange":180,"rollOffsetRange":180},{"name":"upperArms","pitchRange":90,"yawRange":90,"rollRange":90,"pitchOffsetRange":180,"yawOffsetRange":180,"rollOffsetRange":180},{"name":"lowerArms","pitchRange":90,"yawRange":90,"rollRange":120,"pitchOffsetRange":180,"yawOffsetRange":180,"rollOffsetRange":180},{"name":"hands","pitchRange":90,"yawRange":180,"rollRange":90,"pitchOffsetRange":180,"yawOffsetRange":180,"rollOffsetRange":180},{"name":"head","pitchRange":20,"yawRange":20,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":90,"rollOffsetRange":90}]};
+
+// internal state (FSM based) constants
+var STANDING = 1;
+var WALKING = 2;
+var SIDE_STEPPING = 3;
+var FLYING = 4;
+var CONFIG_WALK_STYLES = 5;
+var CONFIG_WALK_TWEAKS = 6;
+var CONFIG_WALK_JOINTS = 7;
+var CONFIG_STANDING = 8;
+var CONFIG_FLYING = 9;
+var CONFIG_FLYING_UP = 10;
+var CONFIG_FLYING_DOWN = 11;
+var CONFIG_SIDESTEP_LEFT = 12;
+var CONFIG_SIDESTEP_RIGHT = 14;
+var INTERNAL_STATE = STANDING;
+
+// status
+var powerOn = true;
+var paused = false; // pause animation playback whilst adjusting certain parameters
+var minimised = true;
+var armsFree = true; // set true for hydra support - temporary fix for Hydras
+var statsOn = false;
+var playFootStepSounds = true;
+
+// constants
+var MAX_WALK_SPEED = 1257; // max oscillation speed
+var FLYING_SPEED = 6.4;// 12.4; // m/s - real humans can't run any faster than 12.4 m/s
+var TERMINAL_VELOCITY = 300; // max speed imposed by Interface
+var DIRECTION_UP = 1;
+var DIRECTION_DOWN = 2;
+var DIRECTION_LEFT = 4;
+var DIRECTION_RIGHT = 8;
+var DIRECTION_FORWARDS = 16;
+var DIRECTION_BACKWARDS = 32;
+var DIRECTION_NONE = 64;
+var MALE = 1;
+var FEMALE = 2;
+
+// start of animation control section
+var cumulativeTime = 0.0;
+var lastOrientation;
+
+// avi gender and default animations
+var avatarGender = MALE;
+var selectedWalk = maleStrutWalk; // the currently selected animation walk file (to edit any animation, paste it's name here and select walk editing mode)
+var selectedStand = maleStandOne;
+var selectedFlyUp = maleFlyingUp;
+var selectedFly = maleFlying;
+var selectedFlyDown = maleFlyingDown;
+var selectedSideStepLeft = maleSideStepLeft;
+var selectedSideStepRight = maleSideStepRight;
+if(avatarGender===FEMALE) {
+
+ // to make toggling the default quick
+ selectedWalk = femaleStrutWalk;
+ selectedStand = femaleStandOne;
+ selectedFlyUp = femaleFlyingUp;
+ selectedFly = femaleFlying;
+ selectedFlyDown = femaleFlyingDown;
+ selectedSideStepLeft = femaleSideStepLeft;
+ selectedSideStepRight = femaleSideStepRight;
+}
+var currentAnimation = selectedStand; // the current animation
+var selectedJointIndex = 0; // the index of the joint currently selected for editing
+var currentTransition = null; // used as a pointer to a Transition
+
+// walkwheel (foot / ground speed matching)
+var sideStepCycleStartLeft = 270;
+var sideStepCycleStartRight = 90;
+var walkWheelPosition = 0;
+var nextStep = DIRECTION_RIGHT; // first step is always right, because the sine waves say so. Unless you're mirrored.
+var nFrames = 0; // counts number of frames
+var strideLength = 0; // stride calibration
+var aviFootSize = {x:0.1, y:0.1, z:0.25}; // experimental values for addition to stride length - TODO: analyse and confirm is increasing smaller stride lengths accuracy once we have better ground detection
+
+// stats
+var frameStartTime = 0; // when the frame first starts we take a note of the time
+var frameExecutionTimeMax = 0; // keep track of the longest frame execution time
+
+// constructor for recent RecentMotion (i.e. frame data) class
+function RecentMotion(velocity, acceleration, principleDirection, state) {
+ this.velocity = velocity;
+ this.acceleration = acceleration;
+ this.principleDirection = principleDirection;
+ this.state = state;
+}
+
+// constructor for the FramesHistory object
+function FramesHistory() {
+ this.recentMotions = [];
+ for(var i = 0 ; i < 10 ; i++) {
+ var blank = new RecentMotion({ x:0, y:0, z:0 }, { x:0, y:0, z:0 }, DIRECTION_FORWARDS, STANDING );
+ this.recentMotions.push(blank);
+ }
+
+ // recentDirection 'method' args: (direction enum, number of steps back to compare)
+ this.recentDirection = function () {
+
+ if( arguments[0] && arguments[1] ) {
+
+ var directionCount = 0;
+ if( arguments[1] > this.recentMotions.length )
+ arguments[1] = this.recentMotions.length;
+
+ for(var i = 0 ; i < arguments[1] ; i++ ) {
+ if( this.recentMotions[i].principleDirection === arguments[0] )
+ directionCount++;
+ }
+ return directionCount / arguments[1] === 1 ? true : false;
+ }
+ return false;
+ }
+
+ // recentState 'method' args: (state enum, number of steps back to compare)
+ this.recentState = function () {
+
+ if( arguments[0] && arguments[1] ) {
+
+ var stateCount = 0;
+ if( arguments[1] > this.recentMotions.length )
+ arguments[1] = this.recentMotions.length;
+
+ for(var i = 0 ; i < arguments[1] ; i++ ) {
+ if( this.recentMotions[i].state === arguments[0] )
+ stateCount++;
+ }
+ return stateCount / arguments[1] === 1 ? true : false;
+ }
+ return false;
+ }
+ this.lastWalkStartTime = 0; // short walks and long walks need different handling
+}
+var framesHistory = new FramesHistory();
+
+// constructor for animation Transition class
+function Transition(lastAnimation, nextAnimation, reachPoses, transitionDuration, easingLower, easingUpper) {
+ this.lastAnimation = lastAnimation; // name of last animation
+ if(lastAnimation === selectedWalk ||
+ nextAnimation === selectedSideStepLeft ||
+ nextAnimation === selectedSideStepRight)
+ this.walkingAtStart = true; // boolean - is the last animation a walking animation?
+ else
+ this.walkingAtStart = false; // boolean - is the last animation a walking animation?
+ this.nextAnimation = nextAnimation; // name of next animation
+ if(nextAnimation === selectedWalk ||
+ nextAnimation === selectedSideStepLeft ||
+ nextAnimation === selectedSideStepRight)
+ this.walkingAtEnd = true; // boolean - is the next animation a walking animation?
+ else
+ this.walkingAtEnd = false; // boolean - is the next animation a walking animation?
+ this.reachPoses = reachPoses; // array of reach poses - am very much looking forward to putting these in!
+ this.transitionDuration = transitionDuration; // length of transition (seconds)
+ this.easingLower = easingLower; // Bezier curve handle (normalised)
+ this.easingUpper = easingUpper; // Bezier curve handle (normalised)
+ this.startTime = new Date().getTime(); // Starting timestamp (seconds)
+ this.progress = 0; // how far are we through the transition?
+ this.walkWheelIncrement = 3; // how much to turn the walkwheel each frame when coming to a halt. Get's set to 0 once the feet are under the avi
+ this.walkWheelAdvance = 0; // how many degrees the walk wheel has been advanced during the transition
+ this.walkStopAngle = 0; // what angle should we stop the walk cycle? (calculated on the fly)
+}
+
+// convert a local (to the avi) translation to a global one
+function localToGlobal(localTranslation) {
+
+ var aviOrientation = MyAvatar.orientation;
+ var front = Quat.getFront(aviOrientation);
+ var right = Quat.getRight(aviOrientation);
+ var up = Quat.getUp (aviOrientation);
+ var aviFront = Vec3.multiply(front,localTranslation.z);
+ var aviRight = Vec3.multiply(right,localTranslation.x);
+ var aviUp = Vec3.multiply(up ,localTranslation.y);
+ var globalTranslation = {x:0,y:0,z:0}; // final value
+ globalTranslation = Vec3.sum(globalTranslation, aviFront);
+ globalTranslation = Vec3.sum(globalTranslation, aviRight);
+ globalTranslation = Vec3.sum(globalTranslation, aviUp);
+ return globalTranslation;
+}
+
+// similar ot above - convert hips translations to global and apply
+function translateHips(localHipsTranslation) {
+
+ var aviOrientation = MyAvatar.orientation;
+ var front = Quat.getFront(aviOrientation);
+ var right = Quat.getRight(aviOrientation);
+ var up = Quat.getUp (aviOrientation);
+ var aviFront = Vec3.multiply(front,localHipsTranslation.y);
+ var aviRight = Vec3.multiply(right,localHipsTranslation.x);
+ var aviUp = Vec3.multiply(up ,localHipsTranslation.z);
+ var AviTranslationOffset = {x:0,y:0,z:0}; // final value
+ AviTranslationOffset = Vec3.sum(AviTranslationOffset, aviFront);
+ AviTranslationOffset = Vec3.sum(AviTranslationOffset, aviRight);
+ AviTranslationOffset = Vec3.sum(AviTranslationOffset, aviUp);
+ MyAvatar.position = {x: MyAvatar.position.x + AviTranslationOffset.x,
+ y: MyAvatar.position.y + AviTranslationOffset.y,
+ z: MyAvatar.position.z + AviTranslationOffset.z };
+}
+
+// clear all joint data
+function resetJoints() {
+
+ var avatarJointNames = MyAvatar.getJointNames();
+ for (var i = 0; i < avatarJointNames.length; i++) {
+ MyAvatar.clearJointData(avatarJointNames[i]);
+ }
+}
+
+// play footstep sound
+function playFootstep(side) {
+
+ var options = new AudioInjectionOptions();
+ options.position = Camera.getPosition();
+ options.volume = 0.5;
+ var walkNumber = 2; // 0 to 2
+ if(side===DIRECTION_RIGHT && playFootStepSounds) {
+ Audio.playSound(footsteps[walkNumber+1], options);
+ }
+ else if(side===DIRECTION_LEFT && playFootStepSounds) {
+ Audio.playSound(footsteps[walkNumber], options);
+ }
+}
+
+// put the fingers into a relaxed pose
+function curlFingers() {
+
+ // left hand fingers
+ for(var i = 18 ; i < 34 ; i++) {
+ MyAvatar.setJointData(jointList[i], Quat.fromPitchYawRollDegrees(8,0,0));
+ }
+ // left hand thumb
+ for(var i = 34 ; i < 38 ; i++) {
+ MyAvatar.setJointData(jointList[i], Quat.fromPitchYawRollDegrees(0,0,0));
+ }
+ // right hand fingers
+ for(var i = 42 ; i < 58 ; i++) {
+ MyAvatar.setJointData(jointList[i], Quat.fromPitchYawRollDegrees(8,0,0));
+ }
+ // right hand thumb
+ for(var i = 58 ; i < 62 ; i++) {
+ MyAvatar.setJointData(jointList[i], Quat.fromPitchYawRollDegrees(0,0,0));
+ }
+}
+
+// additional maths functions
+function degToRad( degreesValue ) { return degreesValue * Math.PI / 180; }
+function radToDeg( radiansValue ) { return radiansValue * 180 / Math.PI; }
+
+// animateAvatar - sine wave generators working like clockwork
+function animateAvatar( deltaTime, velocity, principleDirection ) {
+
+ // adjusting the walk speed in edit mode causes a nasty flicker. pausing the animation stops this
+ if(paused) return;
+
+ var cycle = cumulativeTime;
+ var transitionProgress = 1;
+ var adjustedFrequency = currentAnimation.settings.baseFrequency;
+
+ // upper legs phase reversal for walking backwards
+ var forwardModifier = 1;
+ if(principleDirection===DIRECTION_BACKWARDS)
+ forwardModifier = -1;
+
+ // don't want to lean forwards if going directly upwards
+ var leanPitchModifier = 1;
+ if(principleDirection===DIRECTION_UP)
+ leanPitchModifier = 0;
+
+ // is there a Transition to include?
+ if(currentTransition!==null) {
+
+ // if is a new transiton
+ if(currentTransition.progress===0) {
+
+ if( currentTransition.walkingAtStart ) {
+
+ if( INTERNAL_STATE !== SIDE_STEPPING ) {
+
+ // work out where we want the walk cycle to stop
+ var leftStop = selectedWalk.settings.stopAngleForwards + 180;
+ var rightStop = selectedWalk.settings.stopAngleForwards;
+ if( principleDirection === DIRECTION_BACKWARDS ) {
+ leftStop = selectedWalk.settings.stopAngleBackwards + 180;
+ rightStop = selectedWalk.settings.stopAngleBackwards;
+ }
+
+ // find the closest stop point from the walk wheel's angle
+ var angleToLeftStop = 180 - Math.abs( Math.abs( walkWheelPosition - leftStop ) - 180);
+ var angleToRightStop = 180 - Math.abs( Math.abs( walkWheelPosition - rightStop ) - 180);
+ if( walkWheelPosition > angleToLeftStop ) angleToLeftStop = 360 - angleToLeftStop;
+ if( walkWheelPosition > angleToRightStop ) angleToRightStop = 360 - angleToRightStop;
+
+ currentTransition.walkWheelIncrement = 6;
+
+ // keep the walkwheel turning by setting the walkWheelIncrement until our feet are tucked nicely underneath us.
+ if( angleToLeftStop < angleToRightStop ) {
+
+ currentTransition.walkStopAngle = leftStop;
+
+ } else {
+
+ currentTransition.walkStopAngle = rightStop;
+ }
+
+ } else {
+
+ // freeze wheel for sidestepping transitions
+ currentTransition.walkWheelIncrement = 0;
+ }
+ }
+ } // end if( currentTransition.walkingAtStart )
+
+ // calculate the Transition progress
+ var elapasedTime = (new Date().getTime() - currentTransition.startTime) / 1000;
+ currentTransition.progress = elapasedTime / currentTransition.transitionDuration;
+ transitionProgress = getBezier((1-currentTransition.progress), {x:0,y:0}, currentTransition.easingLower, currentTransition.easingUpper, {x:1,y:1}).y;
+
+ if(currentTransition.progress>=1) {
+
+ // time to kill off the transition
+ delete currentTransition;
+ currentTransition = null;
+
+ } else {
+
+ if( currentTransition.walkingAtStart ) {
+
+ if( INTERNAL_STATE !== SIDE_STEPPING ) {
+
+ // if at a stop angle, hold the walk wheel position for remainder of transition
+ var tolerance = 7; // must be greater than the walkWheel increment
+ if(( walkWheelPosition > (currentTransition.walkStopAngle - tolerance )) &&
+ ( walkWheelPosition < (currentTransition.walkStopAngle + tolerance ))) {
+
+ currentTransition.walkWheelIncrement = 0;
+ }
+ // keep turning walk wheel until both feet are below the avi
+ walkWheelPosition += currentTransition.walkWheelIncrement;
+ currentTransition.walkWheelAdvance += currentTransition.walkWheelIncrement;
+ }
+ }
+ }
+ }
+
+ // will we need to use the walk wheel this frame?
+ if(currentAnimation === selectedWalk ||
+ currentAnimation === selectedSideStepLeft ||
+ currentAnimation === selectedSideStepRight ||
+ currentTransition !== null) {
+
+ // set the stride length
+ if( INTERNAL_STATE!==SIDE_STEPPING &&
+ INTERNAL_STATE!==CONFIG_SIDESTEP_LEFT &&
+ INTERNAL_STATE!==CONFIG_SIDESTEP_RIGHT &&
+ currentTransition===null ) {
+
+ // if the timing's right, take a snapshot of the stride max and recalibrate
+ var tolerance = 1.0; // higher the number, the higher the chance of a calibration taking place, but is traded off with lower accuracy
+ var strideOne = 40;
+ var strideTwo = 220;
+
+ if( principleDirection === DIRECTION_BACKWARDS ) {
+ strideOne = 130;
+ strideTwo = 300;
+ }
+
+ if(( walkWheelPosition < (strideOne+tolerance) && walkWheelPosition > (strideOne-tolerance) ) ||
+ ( walkWheelPosition < (strideTwo+tolerance) && walkWheelPosition > (strideTwo-tolerance) )) {
+
+ // calculate the feet's offset from each other (in local Z only)
+ var footRPos = localToGlobal(MyAvatar.getJointPosition("RightFoot"));
+ var footLPos = localToGlobal(MyAvatar.getJointPosition("LeftFoot"));
+ var footOffsetZ = Math.abs(footRPos.z - footLPos.z);
+ if(footOffsetZ>1) strideLength = 2 * footOffsetZ + aviFootSize.z; // sometimes getting very low value here - just ignore for now
+ if(statsOn) print('Stride length calibrated to '+strideLength.toFixed(4)+' metres at '+walkWheelPosition.toFixed(1)+' degrees');
+ if(principleDirection===DIRECTION_FORWARDS) {
+ currentAnimation.calibration.strideLengthForwards = strideLength;
+ } else if (principleDirection===DIRECTION_BACKWARDS) {
+ currentAnimation.calibration.strideLengthBackwards = strideLength;
+ }
+
+ } else {
+
+ if(principleDirection===DIRECTION_FORWARDS)
+ strideLength = currentAnimation.calibration.strideLengthForwards;
+ else if (principleDirection===DIRECTION_BACKWARDS)
+ strideLength = currentAnimation.calibration.strideLengthBackwards;
+ }
+ }
+ else if(( INTERNAL_STATE===SIDE_STEPPING ||
+ INTERNAL_STATE===CONFIG_SIDESTEP_LEFT ||
+ INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) ) {
+
+ // calculate lateral stride - same same, but different
+ // if the timing's right, take a snapshot of the stride max and recalibrate the stride length
+ var tolerance = 1.0; // higher the number, the higher the chance of a calibration taking place, but is traded off with lower accuracy
+ if(principleDirection===DIRECTION_LEFT) {
+
+ if( walkWheelPosition < (3+tolerance) && walkWheelPosition > (3-tolerance) ) {
+
+ // calculate the feet's offset from the hips (in local X only)
+ var footRPos = localToGlobal(MyAvatar.getJointPosition("RightFoot"));
+ var footLPos = localToGlobal(MyAvatar.getJointPosition("LeftFoot"));
+ var footOffsetX = Math.abs(footRPos.x - footLPos.x);
+ strideLength = footOffsetX;
+ if(statsOn) print('Stride width calibrated to '+strideLength.toFixed(4)+ ' metres at '+walkWheelPosition.toFixed(1)+' degrees');
+ currentAnimation.calibration.strideLengthLeft = strideLength;
+
+ } else {
+
+ strideLength = currentAnimation.calibration.strideLengthLeft;
+ }
+ }
+ else if (principleDirection===DIRECTION_RIGHT) {
+
+ if( walkWheelPosition < (170+tolerance) && walkWheelPosition > (170-tolerance) ) {
+
+ // calculate the feet's offset from the hips (in local X only)
+ var footRPos = localToGlobal(MyAvatar.getJointPosition("RightFoot"));
+ var footLPos = localToGlobal(MyAvatar.getJointPosition("LeftFoot"));
+ var footOffsetX = Math.abs(footRPos.x - footLPos.x);
+ strideLength = footOffsetX;
+ if(statsOn) print('Stride width calibrated to '+strideLength.toFixed(4)+ ' metres at '+walkWheelPosition.toFixed(1)+' degrees');
+ currentAnimation.calibration.strideLengthRight = strideLength;
+
+ } else {
+
+ strideLength = currentAnimation.calibration.strideLengthRight;
+ }
+ }
+ } // end stride length calculations
+
+ // wrap the stride length around a 'surveyor's wheel' twice and calculate the angular velocity at the given (linear) velocity
+ // omega = v / r , where r = circumference / 2 PI , where circumference = 2 * stride length
+ var wheelRadius = strideLength / Math.PI;
+ var angularVelocity = velocity / wheelRadius;
+
+ // calculate the degrees turned (at this angular velocity) since last frame
+ var radiansTurnedSinceLastFrame = deltaTime * angularVelocity;
+ var degreesTurnedSinceLastFrame = radToDeg(radiansTurnedSinceLastFrame);
+
+ // if we are in an edit mode, we will need fake time to turn the wheel
+ if( INTERNAL_STATE!==WALKING &&
+ INTERNAL_STATE!==SIDE_STEPPING )
+ degreesTurnedSinceLastFrame = currentAnimation.settings.baseFrequency / 70;
+
+ if( walkWheelPosition >= 360 )
+ walkWheelPosition = walkWheelPosition % 360;
+
+ // advance the walk wheel the appropriate amount
+ if( currentTransition===null || currentTransition.walkingAtEnd )
+ walkWheelPosition += degreesTurnedSinceLastFrame;
+
+ // set the new values for the exact correct walk cycle speed at this velocity
+ adjustedFrequency = 1;
+ cycle = walkWheelPosition;
+
+ // show stats and walk wheel?
+ if(statsOn) {
+
+ var distanceTravelled = velocity * deltaTime;
+ var deltaTimeMS = deltaTime * 1000;
+
+ if( INTERNAL_STATE===SIDE_STEPPING ||
+ INTERNAL_STATE===CONFIG_SIDESTEP_LEFT ||
+ INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) {
+
+ // draw the walk wheel turning around the z axis for sidestepping
+ var directionSign = 1;
+ if(principleDirection===DIRECTION_RIGHT) directionSign = -1;
+ var yOffset = hipsToFeetDistance - (wheelRadius/1.2); // /1.2 is a visual kludge, probably necessary because of either the 'avi feet penetrate floor' issue - TODO - once ground plane following is in Interface, lock this down
+ var sinWalkWheelPosition = wheelRadius * Math.sin(degToRad( directionSign * walkWheelPosition ));
+ var cosWalkWheelPosition = wheelRadius * Math.cos(degToRad( directionSign * -walkWheelPosition ));
+ var wheelXPos = {x: cosWalkWheelPosition, y:-sinWalkWheelPosition - yOffset, z: 0};
+ var wheelXEnd = {x: -cosWalkWheelPosition, y:sinWalkWheelPosition - yOffset, z: 0};
+ sinWalkWheelPosition = wheelRadius * Math.sin(degToRad( -directionSign * walkWheelPosition+90 ));
+ cosWalkWheelPosition = wheelRadius * Math.cos(degToRad( -directionSign * walkWheelPosition+90 ));
+ var wheelYPos = {x:cosWalkWheelPosition, y:sinWalkWheelPosition - yOffset, z: 0};
+ var wheelYEnd = {x:-cosWalkWheelPosition, y:-sinWalkWheelPosition - yOffset, z: 0};
+ Overlays.editOverlay(walkWheelYLine, {visible: true, position:wheelYPos, end:wheelYEnd});
+ Overlays.editOverlay(walkWheelZLine, {visible: true, position:wheelXPos, end:wheelXEnd});
+
+ } else {
+
+ // draw the walk wheel turning around the x axis for walking forwards or backwards
+ var yOffset = hipsToFeetDistance - (wheelRadius/1.2); // /1.2 is a visual kludge, probably necessary because of either the 'avi feet penetrate floor' issue - TODO - once ground plane following is in Interface, lock this down
+ var sinWalkWheelPosition = wheelRadius * Math.sin(degToRad((forwardModifier*-1) * walkWheelPosition));
+ var cosWalkWheelPosition = wheelRadius * Math.cos(degToRad((forwardModifier*-1) * -walkWheelPosition));
+ var wheelZPos = {x:0, y:-sinWalkWheelPosition - yOffset, z: cosWalkWheelPosition};
+ var wheelZEnd = {x:0, y:sinWalkWheelPosition - yOffset, z: -cosWalkWheelPosition};
+ sinWalkWheelPosition = wheelRadius * Math.sin(degToRad(forwardModifier * walkWheelPosition+90));
+ cosWalkWheelPosition = wheelRadius * Math.cos(degToRad(forwardModifier * walkWheelPosition+90));
+ var wheelYPos = {x:0, y:sinWalkWheelPosition - yOffset, z: cosWalkWheelPosition};
+ var wheelYEnd = {x:0, y:-sinWalkWheelPosition - yOffset, z: -cosWalkWheelPosition};
+ Overlays.editOverlay(walkWheelYLine, { visible: true, position:wheelYPos, end:wheelYEnd });
+ Overlays.editOverlay(walkWheelZLine, { visible: true, position:wheelZPos, end:wheelZEnd });
+ }
+
+ // populate stats overlay
+ var walkWheelInfo =
+ ' Walk Wheel Stats\n--------------------------------------\n \n \n'
+ + '\nFrame time: '+deltaTimeMS.toFixed(2)
+ + ' mS\nVelocity: '+velocity.toFixed(2)
+ + ' m/s\nDistance: '+distanceTravelled.toFixed(3)
+ + ' m\nOmega: '+angularVelocity.toFixed(3)
+ + ' rad / s\nDeg to turn: '+degreesTurnedSinceLastFrame.toFixed(2)
+ + ' deg\nWheel position: '+cycle.toFixed(1)
+ + ' deg\nWheel radius: '+wheelRadius.toFixed(3)
+ + ' m\nHips To Feet: '+hipsToFeetDistance.toFixed(3)
+ + ' m\nStride: '+strideLength.toFixed(3)
+ + ' m\n';
+ Overlays.editOverlay(walkWheelStats, {text: walkWheelInfo});
+ }
+ } // end of walk wheel and stride length calculation
+
+
+ // Start applying motion
+ var pitchOscillation = 0;
+ var yawOscillation = 0;
+ var rollOscillation = 0;
+ var pitchOscillationLast = 0;
+ var yawOscillationLast = 0;
+ var rollOscillationLast = 0;
+ var pitchOffset = 0;
+ var yawOffset = 0;
+ var rollOffset = 0;
+ var pitchOffsetLast = 0;
+ var yawOffsetLast = 0;
+ var rollOffsetLast = 0;
+
+ // calcualte any hips translation
+ // Note: can only apply hips translations whilst in a config (edit) mode at present, not whilst under locomotion
+ if( INTERNAL_STATE===CONFIG_WALK_STYLES ||
+ INTERNAL_STATE===CONFIG_WALK_TWEAKS ||
+ INTERNAL_STATE===CONFIG_WALK_JOINTS ||
+ INTERNAL_STATE===CONFIG_SIDESTEP_LEFT ||
+ INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ||
+ INTERNAL_STATE===CONFIG_FLYING ||
+ INTERNAL_STATE===CONFIG_FLYING_UP ||
+ INTERNAL_STATE===CONFIG_FLYING_DOWN ) {
+
+ // calculate hips translation
+ var motorOscillation = Math.sin(degToRad((cycle * adjustedFrequency * 2) + currentAnimation.joints[0].thrustPhase)) + currentAnimation.joints[0].thrustOffset;
+ var swayOscillation = Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[0].swayPhase)) + currentAnimation.joints[0].swayOffset;
+ var bobOscillation = Math.sin(degToRad((cycle * adjustedFrequency * 2) + currentAnimation.joints[0].bobPhase)) + currentAnimation.joints[0].bobOffset;
+
+ // apply hips translation
+ translateHips({x:swayOscillation*currentAnimation.joints[0].sway, y:motorOscillation*currentAnimation.joints[0].thrust, z:bobOscillation*currentAnimation.joints[0].bob});
+ }
+
+ // hips rotation
+ // apply the current Transition?
+ if(currentTransition!==null) {
+
+ if( currentTransition.walkingAtStart ) {
+
+ pitchOscillation = currentAnimation.joints[0].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2)
+ + currentAnimation.joints[0].pitchPhase)) + currentAnimation.joints[0].pitchOffset;
+
+ yawOscillation = currentAnimation.joints[0].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency )
+ + currentAnimation.joints[0].yawPhase)) + currentAnimation.joints[0].yawOffset;
+
+ rollOscillation = (currentAnimation.joints[0].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency )
+ + currentAnimation.joints[0].rollPhase)) + currentAnimation.joints[0].rollOffset);
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[0].pitch * Math.sin(degToRad(( walkWheelPosition * 2)
+ + currentTransition.lastAnimation.joints[0].pitchPhase)) + currentTransition.lastAnimation.joints[0].pitchOffset;
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[0].yaw * Math.sin(degToRad(( walkWheelPosition )
+ + currentTransition.lastAnimation.joints[0].yawPhase)) + currentTransition.lastAnimation.joints[0].yawOffset;
+
+ rollOscillationLast = (currentTransition.lastAnimation.joints[0].roll * Math.sin(degToRad(( walkWheelPosition )
+ + currentTransition.lastAnimation.joints[0].rollPhase)) + currentTransition.lastAnimation.joints[0].rollOffset);
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[0].pitch * Math.sin(degToRad((cycle * adjustedFrequency * 2)
+ + currentAnimation.joints[0].pitchPhase)) + currentAnimation.joints[0].pitchOffset;
+
+ yawOscillation = currentAnimation.joints[0].yaw * Math.sin(degToRad((cycle * adjustedFrequency )
+ + currentAnimation.joints[0].yawPhase)) + currentAnimation.joints[0].yawOffset;
+
+ rollOscillation = (currentAnimation.joints[0].roll * Math.sin(degToRad((cycle * adjustedFrequency )
+ + currentAnimation.joints[0].rollPhase)) + currentAnimation.joints[0].rollOffset);
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[0].pitch * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency * 2)
+ + currentTransition.lastAnimation.joints[0].pitchPhase)) + currentTransition.lastAnimation.joints[0].pitchOffset;
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[0].yaw * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[0].yawPhase)) + currentTransition.lastAnimation.joints[0].yawOffset;
+
+ rollOscillationLast = (currentTransition.lastAnimation.joints[0].roll * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[0].rollPhase)) + currentTransition.lastAnimation.joints[0].rollOffset);
+
+ }
+
+ pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
+ yawOscillation = (transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast);
+ rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast);
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[0].pitch * Math.sin(degToRad((cycle * adjustedFrequency * 2)
+ + currentAnimation.joints[0].pitchPhase)) + currentAnimation.joints[0].pitchOffset;
+
+ yawOscillation = currentAnimation.joints[0].yaw * Math.sin(degToRad((cycle * adjustedFrequency )
+ + currentAnimation.joints[0].yawPhase)) + currentAnimation.joints[0].yawOffset;
+
+ rollOscillation = (currentAnimation.joints[0].roll * Math.sin(degToRad((cycle * adjustedFrequency )
+ + currentAnimation.joints[0].rollPhase)) + currentAnimation.joints[0].rollOffset);
+ }
+
+ // apply hips rotation
+ MyAvatar.setJointData("Hips", Quat.fromPitchYawRollDegrees(-pitchOscillation + (leanPitchModifier * getLeanPitch(velocity)), // getLeanPitch - lean forwards as velocity increases
+ yawOscillation, // Yup, that's correct ;-)
+ rollOscillation + getLeanRoll(deltaTime, velocity))); // getLeanRoll - banking on cornering
+
+ // upper legs
+ if( INTERNAL_STATE!==SIDE_STEPPING &&
+ INTERNAL_STATE!==CONFIG_SIDESTEP_LEFT &&
+ INTERNAL_STATE!==CONFIG_SIDESTEP_RIGHT ) {
+
+ // apply the current Transition to the upper legs?
+ if(currentTransition!==null) {
+
+ if(currentTransition.walkingAtStart) {
+
+ pitchOscillation = currentAnimation.joints[1].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + (forwardModifier * currentAnimation.joints[1].pitchPhase)));
+ yawOscillation = currentAnimation.joints[1].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[1].yawPhase));
+ rollOscillation = currentAnimation.joints[1].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[1].rollPhase));
+
+ pitchOffset = currentAnimation.joints[1].pitchOffset;
+ yawOffset = currentAnimation.joints[1].yawOffset;
+ rollOffset = currentAnimation.joints[1].rollOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[1].pitch
+ * Math.sin(degToRad( walkWheelPosition + (forwardModifier * currentTransition.lastAnimation.joints[1].pitchPhase )));
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[1].yaw
+ * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[1].yawPhase ));
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[1].roll
+ * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[1].rollPhase ));
+
+ pitchOffsetLast = currentTransition.lastAnimation.joints[1].pitchOffset;
+ yawOffsetLast = currentTransition.lastAnimation.joints[1].yawOffset;
+ rollOffsetLast = currentTransition.lastAnimation.joints[1].rollOffset;
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[1].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + (forwardModifier * currentAnimation.joints[1].pitchPhase)));
+ yawOscillation = currentAnimation.joints[1].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].yawPhase));
+ rollOscillation = currentAnimation.joints[1].roll * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].rollPhase));
+
+ pitchOffset = currentAnimation.joints[1].pitchOffset;
+ yawOffset = currentAnimation.joints[1].yawOffset;
+ rollOffset = currentAnimation.joints[1].rollOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[1].pitch
+ * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
+ + (forwardModifier * currentTransition.lastAnimation.joints[1].pitchPhase )));
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[1].yaw
+ * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
+ + currentTransition.lastAnimation.joints[1].yawPhase ));
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[1].roll
+ * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
+ + currentTransition.lastAnimation.joints[1].rollPhase ));
+
+ pitchOffsetLast = currentTransition.lastAnimation.joints[1].pitchOffset;
+ yawOffsetLast = currentTransition.lastAnimation.joints[1].yawOffset;
+ rollOffsetLast = currentTransition.lastAnimation.joints[1].rollOffset;
+ }
+
+ pitchOscillation = (transitionProgress * pitchOscillation ) + ((1-transitionProgress) * pitchOscillationLast);
+ yawOscillation = (transitionProgress * yawOscillation ) + ((1-transitionProgress) * yawOscillationLast);
+ rollOscillation = (transitionProgress * rollOscillation ) + ((1-transitionProgress) * rollOscillationLast);
+
+ pitchOffset = (transitionProgress * pitchOffset) + ((1-transitionProgress) * pitchOffsetLast);
+ yawOffset = (transitionProgress * yawOffset) + ((1-transitionProgress) * yawOffsetLast);
+ rollOffset = (transitionProgress * rollOffset) + ((1-transitionProgress) * rollOffsetLast);
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[1].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + (forwardModifier * currentAnimation.joints[1].pitchPhase)));
+ yawOscillation = currentAnimation.joints[1].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].yawPhase));
+ rollOscillation = currentAnimation.joints[1].roll * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].rollPhase));
+
+ pitchOffset = currentAnimation.joints[1].pitchOffset;
+ yawOffset = currentAnimation.joints[1].yawOffset;
+ rollOffset = currentAnimation.joints[1].rollOffset;
+ }
+
+ // apply the upper leg rotations
+ MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees( pitchOscillation + pitchOffset, yawOscillation + yawOffset, -rollOscillation - rollOffset ));
+ MyAvatar.setJointData("LeftUpLeg", Quat.fromPitchYawRollDegrees( -pitchOscillation + pitchOffset, yawOscillation - yawOffset, -rollOscillation + rollOffset ));
+
+
+ // lower leg
+ if(currentTransition!==null) {
+
+ if(currentTransition.walkingAtStart) {
+
+ pitchOscillation = currentAnimation.joints[2].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[2].pitchPhase));
+ yawOscillation = currentAnimation.joints[2].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[2].yawPhase));
+ rollOscillation = currentAnimation.joints[2].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[2].rollPhase));
+
+ pitchOffset = currentAnimation.joints[2].pitchOffset;
+ yawOffset = currentAnimation.joints[2].yawOffset;
+ rollOffset = currentAnimation.joints[2].rollOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[2].pitch * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[2].pitchPhase));
+ yawOscillationLast = currentTransition.lastAnimation.joints[2].yaw * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[2].yawPhase));
+ rollOscillationLast = currentTransition.lastAnimation.joints[2].roll * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[2].rollPhase));
+
+ pitchOffsetLast = currentTransition.lastAnimation.joints[2].pitchOffset;
+ yawOffsetLast = currentTransition.lastAnimation.joints[2].yawOffset;
+ rollOffsetLast = currentTransition.lastAnimation.joints[2].rollOffset;
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[2].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].pitchPhase));
+ yawOscillation = currentAnimation.joints[2].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].yawPhase));
+ rollOscillation = currentAnimation.joints[2].roll * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].rollPhase));
+
+ pitchOffset = currentAnimation.joints[2].pitchOffset;
+ yawOffset = currentAnimation.joints[2].yawOffset;
+ rollOffset = currentAnimation.joints[2].rollOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[2].pitch
+ * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[2].pitchPhase));
+ yawOscillationLast = currentTransition.lastAnimation.joints[2].yaw
+ * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[2].yawPhase));
+ rollOscillationLast = currentTransition.lastAnimation.joints[2].roll
+ * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[2].rollPhase));
+
+ pitchOffsetLast = currentTransition.lastAnimation.joints[2].pitchOffset;
+ yawOffsetLast = currentTransition.lastAnimation.joints[2].yawOffset;
+ rollOffsetLast = currentTransition.lastAnimation.joints[2].rollOffset;
+ }
+
+ pitchOscillation = ( transitionProgress * pitchOscillation ) + ( (1-transitionProgress) * pitchOscillationLast );
+ yawOscillation = ( transitionProgress * yawOscillation ) + ( (1-transitionProgress) * yawOscillationLast );
+ rollOscillation = ( transitionProgress * rollOscillation ) + ( (1-transitionProgress) * rollOscillationLast );
+
+ pitchOffset = ( transitionProgress * pitchOffset ) + ( (1-transitionProgress) * pitchOffsetLast );
+ yawOffset = ( transitionProgress * yawOffset ) + ( (1-transitionProgress) * yawOffsetLast );
+ rollOffset = ( transitionProgress * rollOffset ) + ( (1-transitionProgress) * rollOffsetLast );
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[2].pitch * Math.sin( degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[2].pitchPhase ));
+ yawOscillation = currentAnimation.joints[2].yaw * Math.sin( degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[2].yawPhase ));
+ rollOscillation = currentAnimation.joints[2].roll * Math.sin( degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[2].rollPhase ));
+
+ pitchOffset = currentAnimation.joints[2].pitchOffset;
+ yawOffset = currentAnimation.joints[2].yawOffset;
+ rollOffset = currentAnimation.joints[2].rollOffset;
+ }
+
+ // apply lower leg joint rotations
+ MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees( -pitchOscillation + pitchOffset, yawOscillation - yawOffset, rollOscillation - rollOffset ));
+ MyAvatar.setJointData("LeftLeg", Quat.fromPitchYawRollDegrees( pitchOscillation + pitchOffset, yawOscillation + yawOffset, rollOscillation + rollOffset ));
+
+ } // end if !SIDE_STEPPING
+
+ else if( INTERNAL_STATE===SIDE_STEPPING ||
+ INTERNAL_STATE===CONFIG_SIDESTEP_LEFT ||
+ INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) {
+
+ // sidestepping uses the sinewave generators slightly differently for the legs
+ pitchOscillation = currentAnimation.joints[1].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].pitchPhase));
+ yawOscillation = currentAnimation.joints[1].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].yawPhase));
+ rollOscillation = currentAnimation.joints[1].roll * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].rollPhase));
+
+ // apply upper leg rotations for sidestepping
+ MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees(
+ -pitchOscillation + currentAnimation.joints[1].pitchOffset,
+ yawOscillation + currentAnimation.joints[1].yawOffset,
+ rollOscillation + currentAnimation.joints[1].rollOffset ));
+ MyAvatar.setJointData("LeftUpLeg", Quat.fromPitchYawRollDegrees(
+ pitchOscillation + currentAnimation.joints[1].pitchOffset,
+ yawOscillation - currentAnimation.joints[1].yawOffset,
+ -rollOscillation - currentAnimation.joints[1].rollOffset ));
+
+ // calculate lower leg joint rotations for sidestepping
+ pitchOscillation = currentAnimation.joints[2].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].pitchPhase));
+ yawOscillation = currentAnimation.joints[2].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].yawPhase));
+ rollOscillation = currentAnimation.joints[2].roll * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].rollPhase));
+
+ // apply lower leg joint rotations
+ MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees(
+ -pitchOscillation + currentAnimation.joints[2].pitchOffset,
+ yawOscillation - currentAnimation.joints[2].yawOffset,
+ rollOscillation - currentAnimation.joints[2].rollOffset)); // TODO: needs a kick just before fwd peak
+ MyAvatar.setJointData("LeftLeg", Quat.fromPitchYawRollDegrees(
+ pitchOscillation + currentAnimation.joints[2].pitchOffset,
+ yawOscillation + currentAnimation.joints[2].yawOffset,
+ rollOscillation + currentAnimation.joints[2].rollOffset));
+ }
+
+ // feet
+ if(currentTransition!==null) {
+
+ if(currentTransition.walkingAtStart) {
+
+ pitchOscillation = currentAnimation.joints[3].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[3].pitchPhase));
+ yawOscillation = currentAnimation.joints[3].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[3].yawPhase));
+ rollOscillation = currentAnimation.joints[3].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[3].rollPhase));
+
+ pitchOffset = currentAnimation.joints[3].pitchOffset;
+ yawOffset = currentAnimation.joints[3].yawOffset;
+ rollOffset = currentAnimation.joints[3].rollOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[3].pitch * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[3].pitchPhase));
+ yawOscillationLast = currentTransition.lastAnimation.joints[3].yaw * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[3].yawPhase));
+ rollOscillationLast = currentTransition.lastAnimation.joints[3].roll * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[3].rollPhase));
+
+ pitchOffsetLast = currentTransition.lastAnimation.joints[3].pitchOffset;
+ yawOffsetLast = currentTransition.lastAnimation.joints[3].yawOffset;
+ rollOffsetLast = currentTransition.lastAnimation.joints[3].rollOffset;
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[3].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].pitchPhase));
+ yawOscillation = currentAnimation.joints[3].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].yawPhase));
+ rollOscillation = currentAnimation.joints[3].roll * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].rollPhase));
+
+ pitchOffset = currentAnimation.joints[3].pitchOffset;
+ yawOffset = currentAnimation.joints[3].yawOffset;
+ rollOffset = currentAnimation.joints[3].rollOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[3].pitch
+ * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[3].pitchPhase));
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[3].yaw
+ * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[3].yawPhase));
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[3].roll
+ * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[3].rollPhase));
+
+ pitchOffsetLast = currentTransition.lastAnimation.joints[3].pitchOffset;
+ yawOffsetLast = currentTransition.lastAnimation.joints[3].yawOffset;
+ rollOffsetLast = currentTransition.lastAnimation.joints[3].rollOffset;
+ }
+
+ pitchOscillation = (transitionProgress * pitchOscillation ) + ((1-transitionProgress) * pitchOscillationLast);
+ yawOscillation = (transitionProgress * yawOscillation ) + ((1-transitionProgress) * yawOscillationLast);
+ rollOscillation = (transitionProgress * rollOscillation ) + ((1-transitionProgress) * rollOscillationLast);
+
+ pitchOffset = (transitionProgress * pitchOffset) + ((1-transitionProgress) * pitchOffsetLast);
+ yawOffset = (transitionProgress * yawOffset) + ((1-transitionProgress) * yawOffsetLast);
+ rollOffset = (transitionProgress * rollOffset) + ((1-transitionProgress) * rollOffsetLast);
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[3].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].pitchPhase));
+ yawOscillation = currentAnimation.joints[3].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].yawPhase));
+ rollOscillation = currentAnimation.joints[3].roll * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].rollPhase));
+
+ pitchOffset = currentAnimation.joints[3].pitchOffset;
+ yawOffset = currentAnimation.joints[3].yawOffset;
+ rollOffset = currentAnimation.joints[3].rollOffset;
+ }
+
+ // apply foot rotations
+ MyAvatar.setJointData("RightFoot", Quat.fromPitchYawRollDegrees( pitchOscillation + pitchOffset, yawOscillation + yawOffset, rollOscillation + rollOffset ));
+ MyAvatar.setJointData("LeftFoot", Quat.fromPitchYawRollDegrees( -pitchOscillation + pitchOffset, yawOscillation - yawOffset, rollOscillation - rollOffset ));
+
+ // play footfall sound yet? To determine this, we take the differential of the foot's pitch curve to decide when the foot hits the ground.
+ if( INTERNAL_STATE===WALKING ||
+ INTERNAL_STATE===SIDE_STEPPING ||
+ INTERNAL_STATE===CONFIG_WALK_STYLES ||
+ INTERNAL_STATE===CONFIG_WALK_TWEAKS ||
+ INTERNAL_STATE===CONFIG_WALK_JOINTS ||
+ INTERNAL_STATE===CONFIG_SIDESTEP_LEFT ||
+ INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) {
+
+ // finding dy/dx is as simple as determining the cosine wave for the foot's pitch function.
+ var feetPitchDifferential = Math.cos(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].pitchPhase));
+ var threshHold = 0.9; // sets the audio trigger point. with accuracy.
+ if(feetPitchDifferential<-threshHold &&
+ nextStep===DIRECTION_LEFT &&
+ principleDirection!==DIRECTION_UP &&
+ principleDirection!==DIRECTION_DOWN) {
+
+ playFootstep(DIRECTION_LEFT);
+ nextStep = DIRECTION_RIGHT;
+ }
+ else if(feetPitchDifferential>threshHold &&
+ nextStep===DIRECTION_RIGHT &&
+ principleDirection!==DIRECTION_UP &&
+ principleDirection!==DIRECTION_DOWN) {
+
+ playFootstep(DIRECTION_RIGHT);
+ nextStep = DIRECTION_LEFT;
+ }
+ }
+
+ // toes
+ if(currentTransition!==null) {
+
+ if(currentTransition.walkingAtStart) {
+
+ pitchOscillation = currentAnimation.joints[4].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[4].pitchPhase));
+ yawOscillation = currentAnimation.joints[4].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[4].yawPhase)) + currentAnimation.joints[4].yawOffset;
+ rollOscillation = currentAnimation.joints[4].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[4].rollPhase)) + currentAnimation.joints[4].rollOffset;
+
+ pitchOffset = currentAnimation.joints[4].pitchOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[4].pitch * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[4].pitchPhase));
+ yawOscillationLast = currentTransition.lastAnimation.joints[4].yaw * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[4].yawPhase)) + currentTransition.lastAnimation.joints[4].yawOffset;;
+ rollOscillationLast = currentTransition.lastAnimation.joints[4].roll * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[4].rollPhase))+ currentTransition.lastAnimation.joints[4].rollOffset;
+
+ pitchOffsetLast = currentTransition.lastAnimation.joints[4].pitchOffset;
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[4].pitch * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].pitchPhase));
+ yawOscillation = currentAnimation.joints[4].yaw * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].yawPhase)) + currentAnimation.joints[4].yawOffset;
+ rollOscillation = currentAnimation.joints[4].roll * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].rollPhase)) + currentAnimation.joints[4].rollOffset;
+
+ pitchOffset = currentAnimation.joints[4].pitchOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[4].pitch
+ * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[4].pitchPhase));
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[4].yaw
+ * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[4].yawPhase)) + currentTransition.lastAnimation.joints[4].yawOffset;;
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[4].roll
+ * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[4].rollPhase))+ currentTransition.lastAnimation.joints[4].rollOffset;
+
+ pitchOffsetLast = currentTransition.lastAnimation.joints[4].pitchOffset;
+ }
+
+ pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
+ yawOscillation = (transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast);
+ rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast);
+
+ pitchOffset = (transitionProgress * pitchOffset) + ((1-transitionProgress) * pitchOffsetLast);
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[4].pitch * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].pitchPhase));
+ yawOscillation = currentAnimation.joints[4].yaw * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].yawPhase)) + currentAnimation.joints[4].yawOffset;
+ rollOscillation = currentAnimation.joints[4].roll * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].rollPhase)) + currentAnimation.joints[4].rollOffset;
+
+ pitchOffset = currentAnimation.joints[4].pitchOffset;
+ }
+
+ // apply toe rotations
+ MyAvatar.setJointData("RightToeBase", Quat.fromPitchYawRollDegrees(-pitchOscillation + pitchOffset, yawOscillation, rollOscillation));
+ MyAvatar.setJointData("LeftToeBase", Quat.fromPitchYawRollDegrees( pitchOscillation + pitchOffset, yawOscillation, rollOscillation));
+
+ // spine
+ if( currentTransition !== null ) {
+
+ if( currentTransition.walkingAtStart ) {
+
+ pitchOscillation = currentAnimation.joints[5].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2 ) + currentAnimation.joints[5].pitchPhase)) + currentAnimation.joints[5].pitchOffset;
+ yawOscillation = currentAnimation.joints[5].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[5].yawPhase)) + currentAnimation.joints[5].yawOffset;
+ rollOscillation = currentAnimation.joints[5].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[5].rollPhase)) + currentAnimation.joints[5].rollOffset;
+
+ // calculate where we would have been if we'd continued in the last state
+ pitchOscillationLast = currentTransition.lastAnimation.joints[5].pitch
+ * Math.sin(degToRad(( walkWheelPosition * 2 ) + currentTransition.lastAnimation.joints[5].pitchPhase))
+ + currentTransition.lastAnimation.joints[5].pitchOffset;
+ yawOscillationLast = currentTransition.lastAnimation.joints[5].yaw
+ * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[5].yawPhase))
+ + currentTransition.lastAnimation.joints[5].yawOffset;
+ rollOscillationLast = currentTransition.lastAnimation.joints[5].roll
+ * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[5].rollPhase))
+ + currentTransition.lastAnimation.joints[5].rollOffset;
+ } else {
+
+ pitchOscillation = currentAnimation.joints[5].pitch * Math.sin( degToRad(( cycle * adjustedFrequency * 2)
+ + currentAnimation.joints[5].pitchPhase)) + currentAnimation.joints[5].pitchOffset;
+
+ yawOscillation = currentAnimation.joints[5].yaw * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[5].yawPhase)) + currentAnimation.joints[5].yawOffset;
+
+ rollOscillation = currentAnimation.joints[5].roll * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[5].rollPhase)) + currentAnimation.joints[5].rollOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[5].pitch
+ * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency * 2 )
+ + currentTransition.lastAnimation.joints[5].pitchPhase))
+ + currentTransition.lastAnimation.joints[5].pitchOffset;
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[5].yaw
+ * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[5].yawPhase))
+ + currentTransition.lastAnimation.joints[5].yawOffset;
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[5].roll
+ * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[5].rollPhase))
+ + currentTransition.lastAnimation.joints[5].rollOffset;
+ }
+
+ pitchOscillation = (transitionProgress * pitchOscillation ) + ((1-transitionProgress) * pitchOscillationLast);
+ yawOscillation = (transitionProgress * yawOscillation ) + ((1-transitionProgress) * yawOscillationLast);
+ rollOscillation = (transitionProgress * rollOscillation ) + ((1-transitionProgress) * rollOscillationLast);
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[5].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[5].pitchPhase)) + currentAnimation.joints[5].pitchOffset;
+ yawOscillation = currentAnimation.joints[5].yaw * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[5].yawPhase)) + currentAnimation.joints[5].yawOffset;
+ rollOscillation = currentAnimation.joints[5].roll * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[5].rollPhase)) + currentAnimation.joints[5].rollOffset;
+ }
+
+ // apply spine joint rotations
+ MyAvatar.setJointData("Spine", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation ));
+
+ // spine 1
+ if(currentTransition!==null) {
+
+ if(currentTransition.walkingAtStart) {
+
+ pitchOscillation = currentAnimation.joints[6].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2) + currentAnimation.joints[6].pitchPhase)) + currentAnimation.joints[6].pitchOffset;
+ yawOscillation = currentAnimation.joints[6].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[6].yawPhase)) + currentAnimation.joints[6].yawOffset;
+ rollOscillation = currentAnimation.joints[6].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[6].rollPhase)) + currentAnimation.joints[6].rollOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[6].pitch
+ * Math.sin(degToRad(( walkWheelPosition * 2 ) + currentTransition.lastAnimation.joints[6].pitchPhase))
+ + currentTransition.lastAnimation.joints[6].pitchOffset;
+ yawOscillationLast = currentTransition.lastAnimation.joints[6].yaw
+ * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[6].yawPhase))
+ + currentTransition.lastAnimation.joints[6].yawOffset;
+ rollOscillationLast = currentTransition.lastAnimation.joints[6].roll
+ * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[6].rollPhase))
+ + currentTransition.lastAnimation.joints[6].rollOffset;
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[6].pitch * Math.sin( degToRad(( cycle * adjustedFrequency * 2)
+ + currentAnimation.joints[6].pitchPhase)) + currentAnimation.joints[6].pitchOffset;
+
+ yawOscillation = currentAnimation.joints[6].yaw * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[6].yawPhase)) + currentAnimation.joints[6].yawOffset;
+
+ rollOscillation = currentAnimation.joints[6].roll * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[6].rollPhase)) + currentAnimation.joints[6].rollOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[6].pitch
+ * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency * 2 )
+ + currentTransition.lastAnimation.joints[6].pitchPhase))
+ + currentTransition.lastAnimation.joints[6].pitchOffset;
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[6].yaw
+ * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[6].yawPhase))
+ + currentTransition.lastAnimation.joints[6].yawOffset;
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[6].roll
+ * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[6].rollPhase))
+ + currentTransition.lastAnimation.joints[6].rollOffset;
+ }
+
+ pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
+ yawOscillation = (transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast);
+ rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast);
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[6].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[6].pitchPhase)) + currentAnimation.joints[6].pitchOffset;
+ yawOscillation = currentAnimation.joints[6].yaw * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[6].yawPhase)) + currentAnimation.joints[6].yawOffset;
+ rollOscillation = currentAnimation.joints[6].roll * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[6].rollPhase)) + currentAnimation.joints[6].rollOffset;
+ }
+ // apply spine1 joint rotations
+ MyAvatar.setJointData("Spine1", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation ));
+
+ // spine 2
+ if(currentTransition!==null) {
+
+ if(currentTransition.walkingAtStart) {
+ pitchOscillation = currentAnimation.joints[7].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2) + currentAnimation.joints[7].pitchPhase)) + currentAnimation.joints[7].pitchOffset;
+ yawOscillation = currentAnimation.joints[7].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[7].yawPhase)) + currentAnimation.joints[7].yawOffset;
+ rollOscillation = currentAnimation.joints[7].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[7].rollPhase)) + currentAnimation.joints[7].rollOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[7].pitch
+ * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[7].pitchPhase))
+ + currentTransition.lastAnimation.joints[7].pitchOffset;
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[7].yaw
+ * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[7].yawPhase))
+ + currentTransition.lastAnimation.joints[7].yawOffset;
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[7].roll
+ * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[7].rollPhase))
+ + currentTransition.lastAnimation.joints[7].rollOffset;
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[7].pitch
+ * Math.sin( degToRad(( cycle * adjustedFrequency * 2)
+ + currentAnimation.joints[7].pitchPhase))
+ + currentAnimation.joints[7].pitchOffset;
+
+ yawOscillation = currentAnimation.joints[7].yaw
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[7].yawPhase))
+ + currentAnimation.joints[7].yawOffset;
+
+ rollOscillation = currentAnimation.joints[7].roll
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[7].rollPhase))
+ + currentAnimation.joints[7].rollOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[7].pitch
+ * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[7].pitchPhase))
+ + currentTransition.lastAnimation.joints[7].pitchOffset;
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[7].yaw
+ * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[7].yawPhase))
+ + currentTransition.lastAnimation.joints[7].yawOffset;
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[7].roll
+ * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[7].rollPhase))
+ + currentTransition.lastAnimation.joints[7].rollOffset;
+ }
+
+ pitchOscillation = (transitionProgress * pitchOscillation ) + ((1-transitionProgress) * pitchOscillationLast);
+ yawOscillation = (transitionProgress * yawOscillation ) + ((1-transitionProgress) * yawOscillationLast);
+ rollOscillation = (transitionProgress * rollOscillation ) + ((1-transitionProgress) * rollOscillationLast);
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[7].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[7].pitchPhase))
+ + currentAnimation.joints[7].pitchOffset;
+
+ yawOscillation = currentAnimation.joints[7].yaw * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[7].yawPhase))
+ + currentAnimation.joints[7].yawOffset;
+
+ rollOscillation = currentAnimation.joints[7].roll * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[7].rollPhase))
+ + currentAnimation.joints[7].rollOffset;
+ }
+ // apply spine2 joint rotations
+ MyAvatar.setJointData("Spine2", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation ));
+
+ if(!armsFree) {
+
+ // shoulders
+ if(currentTransition!==null) {
+
+ if(currentTransition.walkingAtStart) {
+ pitchOscillation = currentAnimation.joints[8].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency )
+ + currentAnimation.joints[8].pitchPhase)) + currentAnimation.joints[8].pitchOffset;
+
+ yawOscillation = currentAnimation.joints[8].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency )
+ + currentAnimation.joints[8].yawPhase));
+
+ rollOscillation = currentAnimation.joints[8].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2)
+ + currentAnimation.joints[8].rollPhase)) + currentAnimation.joints[8].rollOffset;
+
+ yawOffset = currentAnimation.joints[8].yawOffset;
+
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[8].pitch
+ * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[8].pitchPhase))
+ + currentTransition.lastAnimation.joints[8].pitchOffset;
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[8].yaw
+ * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[8].yawPhase))
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[8].roll
+ * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[8].rollPhase))
+ + currentTransition.lastAnimation.joints[8].rollOffset;
+
+ yawOffsetLast = currentTransition.lastAnimation.joints[8].yawOffset;
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[8].pitch * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[8].pitchPhase)) + currentAnimation.joints[8].pitchOffset;
+
+ yawOscillation = currentAnimation.joints[8].yaw * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[8].yawPhase));
+
+ rollOscillation = currentAnimation.joints[8].roll * Math.sin( degToRad(( cycle * adjustedFrequency * 2)
+ + currentAnimation.joints[8].rollPhase)) + currentAnimation.joints[8].rollOffset;
+
+ yawOffset = currentAnimation.joints[8].yawOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[8].pitch
+ * Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
+ + currentTransition.lastAnimation.joints[8].pitchPhase))
+ + currentTransition.lastAnimation.joints[8].pitchOffset;
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[8].yaw
+ * Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
+ + currentTransition.lastAnimation.joints[8].yawPhase))
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[8].roll
+ * Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
+ + currentTransition.lastAnimation.joints[8].rollPhase))
+ + currentTransition.lastAnimation.joints[8].rollOffset;
+
+ yawOffsetLast = currentTransition.lastAnimation.joints[8].yawOffset;
+ }
+
+ pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
+ yawOscillation = (transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast);
+ rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast);
+
+ yawOffset = (transitionProgress * yawOffset) + ((1-transitionProgress) * yawOffsetLast);
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[8].pitch * Math.sin(degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[8].pitchPhase)) + currentAnimation.joints[8].pitchOffset;
+
+ yawOscillation = currentAnimation.joints[8].yaw * Math.sin(degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[8].yawPhase));
+
+ rollOscillation = currentAnimation.joints[8].roll * Math.sin(degToRad(( cycle * adjustedFrequency * 2)
+ + currentAnimation.joints[8].rollPhase)) + currentAnimation.joints[8].rollOffset;
+
+ yawOffset = currentAnimation.joints[8].yawOffset;
+ }
+
+ MyAvatar.setJointData("RightShoulder", Quat.fromPitchYawRollDegrees(pitchOscillation, yawOscillation + yawOffset, rollOscillation ));
+ MyAvatar.setJointData("LeftShoulder", Quat.fromPitchYawRollDegrees(pitchOscillation, yawOscillation - yawOffset, -rollOscillation ));
+
+ // upper arms
+ if(currentTransition!==null) {
+
+ if(currentTransition.walkingAtStart) {
+
+ pitchOscillation = currentAnimation.joints[9].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[9].pitchPhase));
+ yawOscillation = currentAnimation.joints[9].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[9].yawPhase));
+ rollOscillation = currentAnimation.joints[9].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2) + currentAnimation.joints[9].rollPhase)) + currentAnimation.joints[9].rollOffset;
+
+ pitchOffset = currentAnimation.joints[9].pitchOffset;
+ yawOffset = currentAnimation.joints[9].yawOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[9].pitch
+ * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[9].pitchPhase))
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[9].yaw
+ * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[9].yawPhase))
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[9].roll
+ * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[9].rollPhase))
+ + currentTransition.lastAnimation.joints[9].rollOffset;
+
+ pitchOffsetLast = currentTransition.lastAnimation.joints[9].pitchOffset;
+ yawOffsetLast = currentTransition.lastAnimation.joints[9].yawOffset;
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[9].pitch
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[9].pitchPhase));
+
+ yawOscillation = currentAnimation.joints[9].yaw
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[9].yawPhase));
+
+ rollOscillation = currentAnimation.joints[9].roll
+ * Math.sin(degToRad(( cycle * adjustedFrequency * 2)
+ + currentAnimation.joints[9].rollPhase))
+ + currentAnimation.joints[9].rollOffset;
+
+ pitchOffset = currentAnimation.joints[9].pitchOffset;
+ yawOffset = currentAnimation.joints[9].yawOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[9].pitch
+ * Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
+ + currentTransition.lastAnimation.joints[9].pitchPhase))
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[9].yaw
+ * Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
+ + currentTransition.lastAnimation.joints[9].yawPhase))
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[9].roll
+ * Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
+ + currentTransition.lastAnimation.joints[9].rollPhase))
+ + currentTransition.lastAnimation.joints[9].rollOffset;
+
+ pitchOffsetLast = currentTransition.lastAnimation.joints[9].pitchOffset;
+ yawOffsetLast = currentTransition.lastAnimation.joints[9].yawOffset;
+ }
+
+ pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
+ yawOscillation = (transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast);
+ rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast);
+
+ pitchOffset = (transitionProgress * pitchOffset) + ((1-transitionProgress) * pitchOffsetLast);
+ yawOffset = (transitionProgress * yawOffset) + ((1-transitionProgress) * yawOffsetLast);
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[9].pitch
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[9].pitchPhase));
+
+ yawOscillation = currentAnimation.joints[9].yaw
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[9].yawPhase));
+
+ rollOscillation = currentAnimation.joints[9].roll
+ * Math.sin( degToRad(( cycle * adjustedFrequency * 2)
+ + currentAnimation.joints[9].rollPhase))
+ + currentAnimation.joints[9].rollOffset;
+
+ pitchOffset = currentAnimation.joints[9].pitchOffset;
+ yawOffset = currentAnimation.joints[9].yawOffset;
+
+ }
+ MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees( -pitchOscillation + pitchOffset, yawOscillation - yawOffset, rollOscillation ));
+ MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees( pitchOscillation + pitchOffset, yawOscillation + yawOffset, -rollOscillation ));
+
+ // forearms
+ if(currentTransition!==null) {
+
+ if(currentTransition.walkingAtStart) {
+
+ pitchOscillation = currentAnimation.joints[10].pitch
+ * Math.sin( degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency )
+ + currentAnimation.joints[10].pitchPhase ))
+ + currentAnimation.joints[10].pitchOffset;
+
+ yawOscillation = currentAnimation.joints[10].yaw
+ * Math.sin( degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency )
+ + currentAnimation.joints[10].yawPhase ));
+
+ rollOscillation = currentAnimation.joints[10].roll
+ * Math.sin( degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency )
+ + currentAnimation.joints[10].rollPhase ));
+
+ yawOffset = currentAnimation.joints[10].yawOffset;
+ rollOffset = currentAnimation.joints[10].rollOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[10].pitch
+ * Math.sin( degToRad(( walkWheelPosition )
+ + currentTransition.lastAnimation.joints[10].pitchPhase))
+ + currentTransition.lastAnimation.joints[10].pitchOffset;
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[10].yaw
+ * Math.sin( degToRad(( walkWheelPosition )
+ + currentTransition.lastAnimation.joints[10].yawPhase));
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[10].roll
+ * Math.sin( degToRad(( walkWheelPosition )
+ + currentTransition.lastAnimation.joints[10].rollPhase));
+
+ yawOffsetLast = currentTransition.lastAnimation.joints[10].yawOffset;
+ rollOffsetLast = currentTransition.lastAnimation.joints[10].rollOffset;
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[10].pitch
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[10].pitchPhase ))
+ + currentAnimation.joints[10].pitchOffset;
+
+ yawOscillation = currentAnimation.joints[10].yaw
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[10].yawPhase ));
+
+ rollOscillation = currentAnimation.joints[10].roll
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[10].rollPhase ));
+
+ yawOffset = currentAnimation.joints[10].yawOffset;
+ rollOffset = currentAnimation.joints[10].rollOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[10].pitch
+ * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[10].pitchPhase))
+ + currentTransition.lastAnimation.joints[10].pitchOffset;
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[10].yaw
+ * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[10].yawPhase));
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[10].roll
+ * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[10].rollPhase));
+
+ yawOffsetLast = currentTransition.lastAnimation.joints[10].yawOffset;
+ rollOffsetLast = currentTransition.lastAnimation.joints[10].rollOffset;
+ }
+
+ // blend the previous and next
+ pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
+ yawOscillation = -(transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast);
+ rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast);
+
+ yawOffset = (transitionProgress * yawOffset) + ((1-transitionProgress) * yawOffsetLast);
+ rollOffset = (transitionProgress * rollOffset) + ((1-transitionProgress) * rollOffsetLast);
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[10].pitch
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[10].pitchPhase ))
+ + currentAnimation.joints[10].pitchOffset;
+
+ yawOscillation = currentAnimation.joints[10].yaw
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[10].yawPhase ));
+
+ rollOscillation = currentAnimation.joints[10].roll
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[10].rollPhase ));
+
+ yawOffset = currentAnimation.joints[10].yawOffset;
+ rollOffset = currentAnimation.joints[10].rollOffset;
+ }
+
+ // apply forearms rotations
+ MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation + yawOffset, rollOscillation + rollOffset ));
+ MyAvatar.setJointData("LeftForeArm", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation - yawOffset, rollOscillation - rollOffset ));
+
+ // hands
+ var sideStepSign = 1;
+ if(INTERNAL_STATE===SIDE_STEPPING) {
+ sideStepSign = 1;
+ }
+ if(currentTransition!==null) {
+
+ if(currentTransition.walkingAtStart) {
+
+ pitchOscillation = currentAnimation.joints[11].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[11].pitchPhase)) + currentAnimation.joints[11].pitchOffset;
+ yawOscillation = currentAnimation.joints[11].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[11].yawPhase)) + currentAnimation.joints[11].yawOffset;
+ rollOscillation = currentAnimation.joints[11].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[11].rollPhase)) ;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[11].pitch
+ * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[11].pitchPhase))
+ + currentTransition.lastAnimation.joints[11].pitchOffset;
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[11].yaw
+ * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[11].yawPhase))
+ + currentTransition.lastAnimation.joints[11].yawOffset;
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[11].roll
+ * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[11].rollPhase))
+
+ rollOffset = currentAnimation.joints[11].rollOffset;
+ rollOffsetLast = currentTransition.lastAnimation.joints[11].rollOffset;
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[11].pitch
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[11].pitchPhase))
+ + currentAnimation.joints[11].pitchOffset;
+
+ yawOscillation = currentAnimation.joints[11].yaw
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[11].yawPhase))
+ + currentAnimation.joints[11].yawOffset;
+
+ rollOscillation = currentAnimation.joints[11].roll
+ * Math.sin( degToRad(( cycle * adjustedFrequency )
+ + currentAnimation.joints[11].rollPhase));
+
+ rollOffset = currentAnimation.joints[11].rollOffset;
+
+ pitchOscillationLast = currentTransition.lastAnimation.joints[11].pitch
+ * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
+ + currentTransition.lastAnimation.joints[11].pitchPhase))
+ + currentTransition.lastAnimation.joints[11].pitchOffset;
+
+ yawOscillationLast = currentTransition.lastAnimation.joints[11].yaw
+ * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
+ + currentTransition.lastAnimation.joints[11].yawPhase))
+ + currentTransition.lastAnimation.joints[11].yawOffset;
+
+ rollOscillationLast = currentTransition.lastAnimation.joints[11].roll
+ * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency
+ + currentTransition.lastAnimation.joints[11].rollPhase))
+
+ rollOffset = currentAnimation.joints[11].rollOffset;
+ rollOffsetLast = currentTransition.lastAnimation.joints[11].rollOffset;
+ }
+
+ pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
+ yawOscillation = (transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast);
+ rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast);
+
+ rollOffset = (transitionProgress * rollOffset) + ((1-transitionProgress) * rollOffsetLast);
+
+ } else {
+
+ pitchOscillation = currentAnimation.joints[11].pitch * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[11].pitchPhase)) + currentAnimation.joints[11].pitchOffset;
+ yawOscillation = currentAnimation.joints[11].yaw * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[11].yawPhase)) + currentAnimation.joints[11].yawOffset;
+ rollOscillation = currentAnimation.joints[11].roll * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[11].rollPhase));
+
+ rollOffset = currentAnimation.joints[11].rollOffset;
+ }
+
+ // set the hand rotations
+ MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollDegrees( sideStepSign * pitchOscillation, yawOscillation, rollOscillation + rollOffset));
+ MyAvatar.setJointData("LeftHand", Quat.fromPitchYawRollDegrees( pitchOscillation, -yawOscillation, rollOscillation - rollOffset));
+
+ } // end if(!armsFree)
+
+ // head (includes neck joint) - currently zeroed out in STANDING animation files by request
+ if( currentTransition !== null ) {
+
+ if(currentTransition.walkingAtStart) {
+
+ pitchOscillation = 0.5 * currentAnimation.joints[12].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2)
+ + currentAnimation.joints[12].pitchPhase)) + currentAnimation.joints[12].pitchOffset;
+
+ yawOscillation = 0.5 * currentAnimation.joints[12].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency )
+ + currentAnimation.joints[12].yawPhase)) + currentAnimation.joints[12].yawOffset;
+
+ rollOscillation = 0.5 * currentAnimation.joints[12].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency )
+ + currentAnimation.joints[12].rollPhase)) + currentAnimation.joints[12].rollOffset;
+
+ pitchOscillationLast = 0.5 * currentTransition.lastAnimation.joints[12].pitch
+ * Math.sin(degToRad(( walkWheelPosition * 2) + currentTransition.lastAnimation.joints[12].pitchPhase))
+ + currentTransition.lastAnimation.joints[12].pitchOffset;
+
+ yawOscillationLast = 0.5 * currentTransition.lastAnimation.joints[12].yaw
+ * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[12].yawPhase))
+ + currentTransition.lastAnimation.joints[12].yawOffset;
+
+ rollOscillationLast = 0.5 * currentTransition.lastAnimation.joints[12].roll
+ * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[12].rollPhase))
+ + currentTransition.lastAnimation.joints[12].rollOffset;
+
+ } else {
+
+ pitchOscillation = 0.5 * currentAnimation.joints[12].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[12].pitchPhase)) + currentAnimation.joints[12].pitchOffset;
+ yawOscillation = 0.5 * currentAnimation.joints[12].yaw * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[12].yawPhase)) + currentAnimation.joints[12].yawOffset;
+ rollOscillation = 0.5 * currentAnimation.joints[12].roll * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[12].rollPhase)) + currentAnimation.joints[12].rollOffset;
+
+ pitchOscillationLast = 0.5 * currentTransition.lastAnimation.joints[12].pitch
+ * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency * 2)
+ + currentTransition.lastAnimation.joints[12].pitchPhase))
+ + currentTransition.lastAnimation.joints[12].pitchOffset;
+
+ yawOscillationLast = 0.5 * currentTransition.lastAnimation.joints[12].yaw
+ * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[12].yawPhase))
+ + currentTransition.lastAnimation.joints[12].yawOffset;
+
+ rollOscillationLast = 0.5 * currentTransition.lastAnimation.joints[12].roll
+ * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency )
+ + currentTransition.lastAnimation.joints[12].rollPhase))
+ + currentTransition.lastAnimation.joints[12].rollOffset;
+ }
+
+ pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast);
+ yawOscillation = (transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast);
+ rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast);
+
+ } else {
+
+ pitchOscillation = 0.5 * currentAnimation.joints[12].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[12].pitchPhase)) + currentAnimation.joints[12].pitchOffset;
+ yawOscillation = 0.5 * currentAnimation.joints[12].yaw * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[12].yawPhase)) + currentAnimation.joints[12].yawOffset;
+ rollOscillation = 0.5 * currentAnimation.joints[12].roll * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[12].rollPhase)) + currentAnimation.joints[12].rollOffset;
+ }
+
+ MyAvatar.setJointData( "Head", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation ));
+ MyAvatar.setJointData( "Neck", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation ));
+}
+
+
+
+// Bezier function for applying to transitions - src: Dan Pupius (www.pupius.net) http://13thparallel.com/archive/bezier-curves/
+Coord = function (x,y) {
+ if(!x) var x=0;
+ if(!y) var y=0;
+ return {x: x, y: y};
+}
+
+function B1(t) { return t*t*t }
+function B2(t) { return 3*t*t*(1-t) }
+function B3(t) { return 3*t*(1-t)*(1-t) }
+function B4(t) { return (1-t)*(1-t)*(1-t) }
+
+function getBezier(percent,C1,C2,C3,C4) {
+ var pos = new Coord();
+ pos.x = C1.x*B1(percent) + C2.x*B2(percent) + C3.x*B3(percent) + C4.x*B4(percent);
+ pos.y = C1.y*B1(percent) + C2.y*B2(percent) + C3.y*B3(percent) + C4.y*B4(percent);
+ return pos;
+}
+
+// Butterworth LP filter - coeffs calculated using: http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html
+var NZEROS = 8;
+var NPOLES = 8;
+var GAIN = 17.40692157;
+var xv = [0,0,0,0,0,0,0,0,0]; //xv.length = NZEROS+1;
+var yv = [0,0,0,0,0,0,0,0,0]; //yv.length = NPOLES+1;
+
+function filterButterworth(nextInputValue)
+{
+ xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4]; xv[4] = xv[5]; xv[5] = xv[6]; xv[6] = xv[7]; xv[7] = xv[8];
+ xv[8] = nextInputValue / GAIN;
+ yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4]; yv[4] = yv[5]; yv[5] = yv[6]; yv[6] = yv[7]; yv[7] = yv[8];
+ yv[8] = (xv[0] + xv[8]) + 8 * (xv[1] + xv[7]) + 28 * (xv[2] + xv[6])
+ + 56 * (xv[3] + xv[5]) + 70 * xv[4]
+ + ( -0.0033008230 * yv[0]) + ( -0.0440409341 * yv[1])
+ + ( -0.2663485333 * yv[2]) + ( -0.9570250765 * yv[3])
+ + ( -2.2596729000 * yv[4]) + ( -3.6088345059 * yv[5])
+ + ( -3.9148571397 * yv[6]) + ( -2.6527135283 * yv[7]);
+ return yv[8];
+}
+
+// the faster we go, the further we lean forward. the angle is calcualted here
+var leanAngles = []; // smooth out and add damping with simple averaging filter.
+leanAngles.length = 15;
+
+function getLeanPitch(velocity) {
+
+ if(velocity>TERMINAL_VELOCITY) velocity=TERMINAL_VELOCITY;
+ var leanProgress = velocity / TERMINAL_VELOCITY;
+ var responseSharpness = 1.8;
+ if(principleDirection==DIRECTION_BACKWARDS) responseSharpness = 3.6; // lean back a bit extra when walking backwards
+ var leanProgressBezier = getBezier((1-leanProgress),{x:0,y:0},{x:0,y:responseSharpness},{x:0,y:1},{x:1,y:1}).y;
+
+ // simple averaging filter seems to give best results
+ leanAngles.push(leanProgressBezier);
+ leanAngles.shift(); // FIFO
+ var totalLeanAngles = 0;
+ for(ea in leanAngles) totalLeanAngles += leanAngles[ea];
+ var leanProgressAverageFiltered = totalLeanAngles / leanAngles.length;
+
+ // calculate final return value
+ var leanPitchFinal = 0;
+ if(principleDirection===DIRECTION_BACKWARDS) {
+ leanPitchFinal = -currentAnimation.settings.flyingHipsPitch * leanProgressAverageFiltered;// + finalAccelerationResponse;
+ } else {
+ leanPitchFinal = currentAnimation.settings.flyingHipsPitch * leanProgressAverageFiltered;// + finalAccelerationResponse;
+ }
+ return leanPitchFinal;
+}
+
+// calculate the angle at which to bank into corners when turning
+var leanRollAngles = []; // smooth out and add damping with simple averaging filter
+leanRollAngles.length = 25;
+
+var angularVelocities = []; // keep a record of the last few so can filter out spurious values
+angularVelocities.length = 5;
+
+function getLeanRoll(deltaTime, velocity) {
+
+ // what's our current anglular velocity?
+ var angularVelocityMax = 70; // from observation
+ var currentOrientationVec3 = Quat.safeEulerAngles(MyAvatar.orientation);
+ var lastOrientationVec3 = Quat.safeEulerAngles(lastOrientation);
+ var deltaYaw = lastOrientationVec3.y-currentOrientationVec3.y;
+ lastOrientation = MyAvatar.orientation;
+
+ var angularVelocity = deltaYaw / deltaTime;
+ if(angularVelocity>angularVelocityMax) angularVelocity = angularVelocityMax;
+ if(angularVelocity<-angularVelocityMax) angularVelocity = -angularVelocityMax;
+
+ // filter the angular velocity for a nicer response and a bit of wobble (intentional overshoot / ringing)
+ angularVelocity = filterButterworth(angularVelocity);
+
+ var turnSign = 1;
+ if(angularVelocity<0) turnSign = -1;
+ if(principleDirection===DIRECTION_BACKWARDS)
+ turnSign *= -1;
+
+ // calculate the amount of roll based on both angular and linear velocities
+ if(velocity>TERMINAL_VELOCITY) velocity = TERMINAL_VELOCITY;
+ var leanRollProgress = (velocity / TERMINAL_VELOCITY) * (Math.abs(angularVelocity) / angularVelocityMax);
+
+ // apply our response curve
+ var leanRollProgressBezier = getBezier((1-leanRollProgress),{x:0,y:0},{x:0,y:2.5},{x:0,y:1},{x:1,y:1}).y;
+
+ // simple averaging filter
+ leanRollAngles.push(turnSign * leanRollProgressBezier);
+ leanRollAngles.shift(); // FIFO
+ var totalLeanRollAngles = 0;
+ for(var ea in leanRollAngles) totalLeanRollAngles += leanRollAngles[ea];
+ var leanRollProgressAverageFiltered = totalLeanRollAngles / leanRollAngles.length;
+
+ return currentAnimation.settings.maxBankingAngle * leanRollProgressAverageFiltered;
+}
+
+// set up the interface components, update the internal state and kick off any transitions
+function setInternalState(newInternalState) {
+
+ switch(newInternalState) {
+
+ case WALKING:
+
+ if(!minimised) doStandardMenu();
+ INTERNAL_STATE = WALKING;
+ return;
+
+ case FLYING:
+
+ if(!minimised) doStandardMenu();
+ INTERNAL_STATE = FLYING;
+ return;
+
+ case SIDE_STEPPING:
+
+ if(!minimised) doStandardMenu();
+ INTERNAL_STATE = SIDE_STEPPING;
+ return;
+
+ case CONFIG_WALK_STYLES:
+
+ INTERNAL_STATE = CONFIG_WALK_STYLES;
+ currentAnimation = selectedWalk;
+ if(!minimised) {
+ hidebuttonOverlays();
+ hideJointControls();
+ showFrontPanelButtons(false);
+ showWalkStyleButtons(true);
+ setBackground(controlsBackgroundWalkEditStyles);
+ setButtonOverlayVisible(onButton);
+ setButtonOverlayVisible(configWalkStylesButtonSelected);
+ setButtonOverlayVisible(configWalkTweaksButton);
+ setButtonOverlayVisible(configWalkJointsButton);
+ setButtonOverlayVisible(backButton);
+ setSliderThumbsVisible(false);
+ }
+ return;
+
+ case CONFIG_WALK_TWEAKS:
+
+ INTERNAL_STATE = CONFIG_WALK_TWEAKS;
+ currentAnimation = selectedWalk;
+ if(!minimised) {
+ hidebuttonOverlays();
+ hideJointControls();
+ showFrontPanelButtons(false);
+ showWalkStyleButtons(false);
+ setBackground(controlsBackgroundWalkEditTweaks);
+ setButtonOverlayVisible(onButton);
+ setButtonOverlayVisible(configWalkStylesButton);
+ setButtonOverlayVisible(configWalkTweaksButtonSelected);
+ setButtonOverlayVisible(configWalkJointsButton);
+ setButtonOverlayVisible(backButton);
+ initialiseWalkTweaks();
+ }
+ return;
+
+ case CONFIG_WALK_JOINTS:
+
+ INTERNAL_STATE = CONFIG_WALK_JOINTS;
+ currentAnimation = selectedWalk;
+ if(!minimised) {
+ hidebuttonOverlays();
+ showFrontPanelButtons(false);
+ showWalkStyleButtons(false);
+ setBackground(controlsBackgroundWalkEditJoints);
+ setButtonOverlayVisible(onButton);
+ setButtonOverlayVisible(configWalkStylesButton);
+ setButtonOverlayVisible(configWalkTweaksButton);
+ setButtonOverlayVisible(configWalkJointsButtonSelected);
+ setButtonOverlayVisible(backButton);
+ initialiseJointsEditingPanel(selectedJointIndex);
+ }
+ return;
+
+ case CONFIG_STANDING:
+
+ INTERNAL_STATE = CONFIG_STANDING;
+ currentAnimation = selectedStand;
+ if(!minimised) {
+ hidebuttonOverlays();
+ showFrontPanelButtons(false);
+ showWalkStyleButtons(false);
+ setBackground(controlsBackgroundWalkEditJoints);
+ setButtonOverlayVisible(onButton);
+ setButtonOverlayVisible(configSideStepRightButton);
+ setButtonOverlayVisible(configSideStepLeftButton);
+ setButtonOverlayVisible(configStandButtonSelected);
+ setButtonOverlayVisible(backButton);
+ initialiseJointsEditingPanel(selectedJointIndex);
+ }
+ return;
+
+ case CONFIG_SIDESTEP_LEFT:
+
+ INTERNAL_STATE = CONFIG_SIDESTEP_LEFT;
+ currentAnimation = selectedSideStepLeft;
+ if(!minimised) {
+ hidebuttonOverlays();
+ showFrontPanelButtons(false);
+ showWalkStyleButtons(false);
+ setBackground(controlsBackgroundWalkEditJoints);
+ setButtonOverlayVisible(onButton);
+ setButtonOverlayVisible(configSideStepRightButton);
+ setButtonOverlayVisible(configSideStepLeftButtonSelected);
+ setButtonOverlayVisible(configStandButton);
+ setButtonOverlayVisible(backButton);
+ initialiseJointsEditingPanel(selectedJointIndex);
+ }
+ return;
+
+ case CONFIG_SIDESTEP_RIGHT:
+
+ INTERNAL_STATE = CONFIG_SIDESTEP_RIGHT;
+ currentAnimation = selectedSideStepRight;
+ if(!minimised) {
+ hidebuttonOverlays();
+ showFrontPanelButtons(false);
+ showWalkStyleButtons(false);
+ setBackground(controlsBackgroundWalkEditJoints);
+ setButtonOverlayVisible(onButton);
+ setButtonOverlayVisible(configSideStepRightButtonSelected);
+ setButtonOverlayVisible(configSideStepLeftButton);
+ setButtonOverlayVisible(configStandButton);
+ setButtonOverlayVisible(backButton);
+ initialiseJointsEditingPanel(selectedJointIndex);
+ }
+ return;
+
+ case CONFIG_FLYING:
+
+ INTERNAL_STATE = CONFIG_FLYING;
+ currentAnimation = selectedFly;
+ if(!minimised) {
+ hidebuttonOverlays();
+ showFrontPanelButtons(false);
+ showWalkStyleButtons(false);
+ setBackground(controlsBackgroundWalkEditJoints);
+ setButtonOverlayVisible(onButton);
+ setButtonOverlayVisible(configFlyingUpButton);
+ setButtonOverlayVisible(configFlyingDownButton);
+ setButtonOverlayVisible(configFlyingButtonSelected);
+ setButtonOverlayVisible(backButton);
+ initialiseJointsEditingPanel(selectedJointIndex);
+ }
+ return;
+
+ case CONFIG_FLYING_UP:
+
+ INTERNAL_STATE = CONFIG_FLYING_UP;
+ currentAnimation = selectedFlyUp;
+ if(!minimised) {
+ hidebuttonOverlays();
+ showFrontPanelButtons(false);
+ showWalkStyleButtons(false);
+ setBackground(controlsBackgroundWalkEditJoints);
+ setButtonOverlayVisible(onButton);
+ setButtonOverlayVisible(configFlyingUpButtonSelected);
+ setButtonOverlayVisible(configFlyingDownButton);
+ setButtonOverlayVisible(configFlyingButton);
+ setButtonOverlayVisible(backButton);
+ initialiseJointsEditingPanel(selectedJointIndex);
+ }
+ return;
+
+ case CONFIG_FLYING_DOWN:
+
+ INTERNAL_STATE = CONFIG_FLYING_DOWN;
+ currentAnimation = selectedFlyDown;
+ if(!minimised) {
+ hidebuttonOverlays();
+ showFrontPanelButtons(false);
+ showWalkStyleButtons(false);
+ setBackground(controlsBackgroundWalkEditJoints);
+ setButtonOverlayVisible(onButton);
+ setButtonOverlayVisible(configFlyingUpButton);
+ setButtonOverlayVisible(configFlyingDownButtonSelected);
+ setButtonOverlayVisible(configFlyingButton);
+ setButtonOverlayVisible(backButton);
+ initialiseJointsEditingPanel(selectedJointIndex);
+ }
+ return;
+
+ case STANDING:
+ default:
+
+ INTERNAL_STATE = STANDING;
+ if(!minimised) doStandardMenu();
+
+ // initialisation - runs at script startup only
+ if(strideLength===0) {
+
+ if(principleDirection===DIRECTION_BACKWARDS)
+ strideLength = selectedWalk.calibration.strideLengthBackwards;
+ else
+ strideLength = selectedWalk.calibration.strideLengthForwards;
+
+ curlFingers();
+ }
+ return;
+ }
+}
+
+// Main loop
+
+// stabilising vars - most state changes are preceded by a couple of hints that they are about to happen rather than
+// momentarilly switching between states (causes flicker), we count the number of hints in a row before actually changing state
+var standHints = 0;
+var walkHints = 0;
+var flyHints = 0;
+var requiredHints = 2; // tweakable - debounce state changes - how many times do we get a state change request in a row before we actually change state? (used to be 4 or 5)
+var principleDirection = 0;
+
+// helper function for stats output
+function directionAsString(directionEnum) {
+
+ switch(directionEnum) {
+ case DIRECTION_UP: return 'Up';
+ case DIRECTION_DOWN: return 'Down';
+ case DIRECTION_LEFT: return 'Left';
+ case DIRECTION_RIGHT: return 'Right';
+ case DIRECTION_FORWARDS: return 'Forwards';
+ case DIRECTION_BACKWARDS: return 'Backwards';
+ default: return 'Unknown';
+ }
+}
+// helper function for stats output
+function internalStateAsString(internalState) {
+
+ switch(internalState) {
+ case STANDING: return 'Standing';
+ case WALKING: return 'Walking';
+ case SIDE_STEPPING: return 'Side Stepping';
+ case FLYING: return 'Flying';
+ default: return 'Editing';
+ }
+}
+
+Script.update.connect(function(deltaTime) {
+
+ if(powerOn) {
+
+ frameStartTime = new Date().getTime();
+ cumulativeTime += deltaTime;
+ nFrames++;
+ var speed = 0;
+
+ // firstly check for editing modes, as these require no positioning calculations
+ var editing = false;
+ switch(INTERNAL_STATE) {
+
+ case CONFIG_WALK_STYLES:
+ currentAnimation = selectedWalk;
+ animateAvatar(deltaTime, speed, principleDirection);
+ editing = true;
+ break;
+ case CONFIG_WALK_TWEAKS:
+ currentAnimation = selectedWalk;
+ animateAvatar(deltaTime, speed, DIRECTION_FORWARDS);
+ editing = true;
+ break;
+ case CONFIG_WALK_JOINTS:
+ currentAnimation = selectedWalk;
+ animateAvatar(deltaTime, speed, DIRECTION_FORWARDS);
+ editing = true;
+ break;
+ case CONFIG_STANDING:
+ currentAnimation = selectedStand;
+ animateAvatar(deltaTime, speed, DIRECTION_FORWARDS);
+ editing = true;
+ break;
+ case CONFIG_SIDESTEP_LEFT:
+ currentAnimation = selectedSideStepLeft;
+ animateAvatar(deltaTime, speed, DIRECTION_LEFT);
+ editing = true;
+ break;
+ case CONFIG_SIDESTEP_RIGHT:
+ currentAnimation = selectedSideStepRight;
+ animateAvatar(deltaTime, speed, DIRECTION_RIGHT);
+ editing = true;
+ break;
+ case CONFIG_FLYING:
+ currentAnimation = selectedFly;
+ animateAvatar(deltaTime, speed, DIRECTION_FORWARDS);
+ editing = true;
+ break;
+ case CONFIG_FLYING_UP:
+ currentAnimation = selectedFlyUp;
+ animateAvatar(deltaTime, speed, DIRECTION_UP);
+ editing = true;
+ break;
+ case CONFIG_FLYING_DOWN:
+ currentAnimation = selectedFlyDown;
+ animateAvatar(deltaTime, speed, DIRECTION_DOWN);
+ editing = true;
+ break;
+ default:
+ break;
+ }
+
+ // we have to declare these vars here ( outside 'if(!editing)' ), so they are still in scope
+ // when we record the frame's data and when we do the stats update at the end
+ var deltaX = 0;
+ var deltaY = 0;
+ var deltaZ = 0;
+ var acceleration = { x:0, y:0, z:0 };
+ var accelerationJS = MyAvatar.getAcceleration();
+
+ // calculate overriding (local) direction of translation for use later when decide which animation should be played
+ var inverseRotation = Quat.inverse(MyAvatar.orientation);
+ var localVelocity = Vec3.multiplyQbyV(inverseRotation, MyAvatar.getVelocity());
+
+ if(!editing) {
+
+ // the first thing to do is find out how fast we're going,
+ // what our acceleration is and which direction we're principly moving in
+
+ // calcualte (local) change in velocity
+ var velocity = MyAvatar.getVelocity();
+ speed = Vec3.length(velocity);
+
+ // determine the candidate animation to play
+ var actionToTake = 0;
+ if( speed < 0.5) {
+ actionToTake = STANDING;
+ standHints++;
+ }
+ else if( speed < FLYING_SPEED ) {
+ actionToTake = WALKING;
+ walkHints++;
+ }
+ else if( speed >= FLYING_SPEED ) {
+ actionToTake = FLYING;
+ flyHints++;
+ }
+
+ deltaX = localVelocity.x;
+ deltaY = localVelocity.y;
+ deltaZ = -localVelocity.z;
+
+ // determine the principle direction
+ if(Math.abs(deltaX)>Math.abs(deltaY)
+ &&Math.abs(deltaX)>Math.abs(deltaZ)) {
+ if(deltaX<0) {
+ principleDirection = DIRECTION_RIGHT;
+ } else {
+ principleDirection = DIRECTION_LEFT;
+ }
+ }
+ else if(Math.abs(deltaY)>Math.abs(deltaX)
+ &&Math.abs(deltaY)>Math.abs(deltaZ)) {
+ if(deltaY>0) {
+ principleDirection = DIRECTION_UP;
+ }
+ else {
+ principleDirection = DIRECTION_DOWN;
+ }
+ }
+ else if(Math.abs(deltaZ)>Math.abs(deltaX)
+ &&Math.abs(deltaZ)>Math.abs(deltaY)) {
+ if(deltaZ>0) {
+ principleDirection = DIRECTION_FORWARDS;
+ } else {
+ principleDirection = DIRECTION_BACKWARDS;
+ }
+ }
+
+ // NB: this section will change significantly once we are ground plane aware
+ // it will change even more once we have uneven surfaces to deal with
+
+ // maybe at walking speed, but sideways?
+ if( actionToTake === WALKING &&
+ ( principleDirection === DIRECTION_LEFT ||
+ principleDirection === DIRECTION_RIGHT )) {
+
+ actionToTake = SIDE_STEPPING;
+ }
+
+ // maybe at walking speed, but flying up?
+ if( actionToTake === WALKING &&
+ ( principleDirection === DIRECTION_UP )) {
+
+ actionToTake = FLYING;
+ standHints--;
+ flyHints++;
+ }
+
+ // maybe at walking speed, but flying down?
+ if( actionToTake === WALKING &&
+ ( principleDirection === DIRECTION_DOWN )) {
+
+ actionToTake = FLYING;
+ standHints--;
+ flyHints++;
+ }
+
+ // log this frame's motion for later reference
+ var accelerationX = ( framesHistory.recentMotions[0].velocity.x - localVelocity.x ) / deltaTime;
+ var accelerationY = ( localVelocity.y - framesHistory.recentMotions[0].velocity.y ) / deltaTime;
+ var accelerationZ = ( framesHistory.recentMotions[0].velocity.z - localVelocity.z ) / deltaTime;
+ acceleration = {x:accelerationX, y:accelerationY, z:accelerationZ};
+
+
+ // select appropriate animation and initiate Transition if required
+ switch(actionToTake) {
+
+ case STANDING:
+
+ if( standHints > requiredHints || INTERNAL_STATE===STANDING) { // wait for a few consecutive hints (17mS each)
+
+ standHints = 0;
+ walkHints = 0;
+ flyHints = 0;
+
+ // do we need to change state?
+ if( INTERNAL_STATE!==STANDING ) {
+
+ // initiate the transition
+ if(currentTransition) {
+ delete currentTransition;
+ currentTransition = null;
+ }
+
+ switch(currentAnimation) {
+
+ case selectedWalk:
+
+ // Walking to Standing
+ var timeWalking = new Date().getTime() - framesHistory.lastWalkStartTime;
+
+ var bezierCoeffsOne = {x:0.0, y:1.0};
+ var bezierCoeffsTwo = {x:0.0, y:1.0};
+ var transitionTime = 0.4;
+
+ // very different curves for incremental steps
+ if( timeWalking < 550 ) {
+ bezierCoeffsOne = {x:0.63, y:0.17};
+ bezierCoeffsTwo = {x:0.77, y:0.3};
+ transitionTime = 0.75;
+ }
+ currentTransition = new Transition( currentAnimation, selectedStand, [], transitionTime, bezierCoeffsOne, bezierCoeffsTwo );
+ break;
+
+
+ case selectedSideStepLeft:
+ case selectedSideStepRight:
+
+ break;
+
+ default:
+
+ currentTransition = new Transition(currentAnimation, selectedStand, [], 0.3, {x:0.5,y:0.08}, {x:0.28,y:1});
+ break;
+ }
+
+ setInternalState(STANDING);
+ currentAnimation = selectedStand;
+
+ }
+ animateAvatar(1,0,principleDirection);
+ }
+ break;
+
+ case WALKING:
+ case SIDE_STEPPING:
+ if( walkHints > requiredHints ||
+ INTERNAL_STATE===WALKING ||
+ INTERNAL_STATE===SIDE_STEPPING ) { // wait for few consecutive hints (17mS each)
+
+ standHints = 0;
+ walkHints = 0;
+ flyHints = 0;
+
+ if( actionToTake === WALKING && INTERNAL_STATE !== WALKING) {
+
+ // initiate the transition
+ if(currentTransition) {
+ delete currentTransition;
+ currentTransition = null;
+ }
+
+ // set the appropriate start position for the walk wheel
+ if( principleDirection === DIRECTION_BACKWARDS ) {
+
+ walkWheelPosition = selectedWalk.settings.startAngleBackwards;
+
+ } else {
+
+ walkWheelPosition = selectedWalk.settings.startAngleForwards;
+ }
+
+ switch(currentAnimation) {
+
+ case selectedStand:
+
+ // Standing to Walking
+ currentTransition = new Transition(currentAnimation, selectedWalk, [], 0.25, {x:0.5,y:0.08}, {x:0.05,y:0.75});
+ break;
+
+ case selectedSideStepLeft:
+ case selectedSideStepRight:
+
+ break;
+
+ default:
+
+ currentTransition = new Transition(currentAnimation, selectedWalk, [], 0.3, {x:0.5,y:0.08}, {x:0.05,y:0.75});
+ break;
+ }
+ framesHistory.lastWalkStartTime = new Date().getTime();
+ setInternalState(WALKING);
+ currentAnimation = selectedWalk;
+ }
+ else if(actionToTake===SIDE_STEPPING) {
+
+ var selectedSideStep = selectedSideStepRight;
+ if( principleDirection === DIRECTION_LEFT ) {
+
+ selectedSideStep = selectedSideStepLeft;
+
+ } else {
+
+ selectedSideStep = selectedSideStepRight;
+ }
+
+ if( INTERNAL_STATE !== SIDE_STEPPING ) {
+
+ if( principleDirection === DIRECTION_LEFT ) {
+
+ walkWheelPosition = sideStepCycleStartLeft;
+
+ } else {
+
+ walkWheelPosition = sideStepCycleStartRight;
+ }
+ switch(currentAnimation) {
+
+ case selectedStand:
+
+ break;
+
+ default:
+
+ break;
+ }
+ setInternalState(SIDE_STEPPING);
+ }
+
+ currentAnimation = selectedSideStep;
+ }
+ animateAvatar( deltaTime, speed, principleDirection );
+ }
+ break;
+
+ case FLYING:
+
+ if( flyHints > requiredHints - 1 || INTERNAL_STATE===FLYING ) { // wait for a few consecutive hints (17mS each)
+
+ standHints = 0;
+ walkHints = 0;
+ flyHints = 0;
+
+ if(INTERNAL_STATE!==FLYING) setInternalState(FLYING);
+
+ // change animation for flying directly up or down. TODO - check RecentMotions, if is a change then put a transition on it
+ if(principleDirection===DIRECTION_UP) {
+
+ if(currentAnimation !== selectedFlyUp) {
+
+ // initiate a Transition
+ if(currentTransition && currentTransition.nextAnimation!==selectedFlyUp) {
+ delete currentTransition;
+ currentTransition = null;
+ }
+ switch(currentAnimation) {
+
+ case selectedStand:
+
+ currentTransition = new Transition(currentAnimation, selectedFlyUp, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1});
+ break;
+
+ case selectedSideStepLeft:
+ case selectedSideStepRight:
+
+ break;
+
+ default:
+
+ currentTransition = new Transition(currentAnimation, selectedFlyUp, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1});
+ break;
+ }
+ currentAnimation = selectedFlyUp;
+ }
+
+ } else if(principleDirection==DIRECTION_DOWN) {
+
+ if(currentAnimation !== selectedFlyDown) { // TODO: as the locomotion gets cleaner (i.e. less false reports from Interface) this value can be reduced
+
+ // initiate a Transition
+ if(currentTransition && currentTransition.nextAnimation!==selectedFlyDown) {
+ delete currentTransition;
+ currentTransition = null;
+ }
+ switch(currentAnimation) {
+
+ case selectedStand:
+
+ currentTransition = new Transition(currentAnimation, selectedFlyDown, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1});
+ break;
+
+ case selectedSideStepLeft:
+ case selectedSideStepRight:
+
+ break;
+
+ default:
+
+ currentTransition = new Transition(currentAnimation, selectedFlyDown, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1});
+ break;
+ }
+ currentAnimation = selectedFlyDown;
+ }
+
+ } else {
+
+ if(currentAnimation !== selectedFly) { // TODO: as the locomotion gets cleaner (i.e. less false reports from Interface) this value can be reduced
+
+ // initiate a Transition
+ if(currentTransition && currentTransition.nextAnimation!==selectedFly) {
+ delete currentTransition;
+ currentTransition = null;
+ }
+ switch(currentAnimation) {
+
+ case selectedStand:
+
+ currentTransition = new Transition(currentAnimation, selectedFly, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1});
+ break;
+
+ case selectedSideStepLeft:
+ case selectedSideStepRight:
+
+ break;
+
+ default:
+
+ currentTransition = new Transition(currentAnimation, selectedFly, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1});
+ break;
+ }
+ currentAnimation = selectedFly;
+ }
+ }
+ animateAvatar(deltaTime, speed, principleDirection);
+ }
+ break;
+
+ } // end switch(actionToTake)
+
+ } // end if(!editing)
+
+
+ // record the frame's stats for later reference
+ var thisMotion = new RecentMotion(localVelocity, acceleration, principleDirection, INTERNAL_STATE);
+ framesHistory.recentMotions.push(thisMotion);
+ framesHistory.recentMotions.shift();
+
+
+ // before we go, populate the stats overlay
+ if( statsOn ) {
+
+ var cumulativeTimeMS = Math.floor(cumulativeTime*1000);
+ var deltaTimeMS = deltaTime * 1000;
+ var frameExecutionTime = new Date().getTime() - frameStartTime;
+ if(frameExecutionTime>frameExecutionTimeMax) frameExecutionTimeMax = frameExecutionTime;
+
+ var angluarVelocity = Vec3.length(MyAvatar.getAngularVelocity());
+
+ var debugInfo = '\n \n \n \n Stats\n--------------------------------------\n \n \n'
+ + '\nFrame number: '+nFrames
+ + '\nFrame time: '+deltaTimeMS.toFixed(2)
+ + ' mS\nRender time: '+frameExecutionTime.toFixed(0)
+ + ' mS\nLocalised speed: '+speed.toFixed(3)
+ + ' m/s\nCumulative Time '+cumulativeTimeMS.toFixed(0)
+ + ' mS\nState: '+internalStateAsString(INTERNAL_STATE)
+ + ' \nDirection: '+directionAsString(principleDirection)
+ + ' \nAngular Velocity: ' + angluarVelocity.toFixed(3)
+ + ' rad/s';
+ Overlays.editOverlay(debugStats, {text: debugInfo});
+
+ // update these every 250 mS (assuming 60 fps)
+ if( nFrames % 15 === 0 ) {
+ var debugInfo = ' Periodic Stats\n--------------------------------------\n \n \n'
+ + ' \n \nRender time peak hold: '+frameExecutionTimeMax.toFixed(0)
+ + ' mS\n \n \n(L) MyAvatar.getVelocity()'
+ + ' \n \nlocalVelocityX: '+deltaX.toFixed(1)
+ + ' m/s\nlocalVelocityY: '+deltaY.toFixed(1)
+ + ' m/s\nlocalVelocityZ: '+deltaZ.toFixed(1)
+ + ' m/s\n \n(G) MyAvatar.getAcceleration()'
+ + ' \n\nAcceleration X: '+accelerationJS.x.toFixed(1)
+ + ' m/s/s\nAcceleration Y: '+accelerationJS.y.toFixed(1)
+ + ' m/s/s\nAcceleration Z: '+accelerationJS.z.toFixed(1)
+ + ' m/s/s\n \n(L) Acceleration using\nMyAvatar.getVelocity()'
+ + ' \n \nAcceleration X: '+acceleration.x.toFixed(1)
+ + ' m/s/s\nAcceleration Y: '+acceleration.y.toFixed(1)
+ + ' m/s/s\nAcceleration Z: '+acceleration.z.toFixed(1)
+ + ' m/s/s';
+ Overlays.editOverlay(debugStatsPeriodic, {text: debugInfo});
+ frameExecutionTimeMax = 0;
+ }
+ }
+ }
+});
+
+
+// overlays start
+
+// controller dimensions
+var backgroundWidth = 350;
+var backgroundHeight = 700;
+var backgroundX = Window.innerWidth-backgroundWidth-58;
+var backgroundY = Window.innerHeight/2 - backgroundHeight/2;
+var minSliderX = backgroundX + 30;
+var maxSliderX = backgroundX + 295;
+var sliderRangeX = 295 - 30;
+var jointsControlWidth = 200;
+var jointsControlHeight = 300;
+var jointsControlX = backgroundX + backgroundWidth/2 - jointsControlWidth/2;
+var jointsControlY = backgroundY + 242 - jointsControlHeight/2;
+var buttonsY = 20; // distance from top of panel to buttons
+
+// arrays of overlay names
+var sliderThumbOverlays = []; // thumb sliders
+var backgroundOverlays = [];
+var buttonOverlays = [];
+var jointsControlOverlays = [];
+var bigButtonOverlays = [];
+
+
+// load UI backgrounds
+var controlsBackground = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight },
+ imageURL: pathToOverlays+"ddao-background.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+backgroundOverlays.push(controlsBackground);
+
+var controlsBackgroundWalkEditStyles = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight },
+ imageURL: pathToOverlays+"ddao-background-edit-styles.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+backgroundOverlays.push(controlsBackgroundWalkEditStyles);
+
+var controlsBackgroundWalkEditTweaks = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight },
+ imageURL: pathToOverlays+"ddao-background-edit-tweaks.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+backgroundOverlays.push(controlsBackgroundWalkEditTweaks);
+
+var controlsBackgroundWalkEditJoints = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight },
+ imageURL: pathToOverlays+"ddao-background-edit-joints.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+backgroundOverlays.push(controlsBackgroundWalkEditJoints);
+
+var controlsBackgroundFlyingEdit = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight },
+ imageURL: pathToOverlays+"ddao-background-flying-edit.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+backgroundOverlays.push(controlsBackgroundFlyingEdit);
+
+// minimised tab - not put in array, as is a one off
+var controlsMinimisedTab = Overlays.addOverlay("image", {
+ x: Window.innerWidth-58, y: Window.innerHeight -145, width: 50, height: 50,
+ //subImage: { x: 0, y: 50, width: 50, height: 50 },
+ imageURL: pathToOverlays + 'ddao-minimise-tab.png',
+ visible: minimised,
+ alpha: 0.9
+ });
+
+// load character joint selection control images
+var hipsJointControl = Overlays.addOverlay("image", {
+ bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
+ imageURL: pathToOverlays+"ddao-background-edit-hips.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+jointsControlOverlays.push(hipsJointControl);
+
+var upperLegsJointControl = Overlays.addOverlay("image", {
+ bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
+ imageURL: pathToOverlays+"ddao-background-edit-upper-legs.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+jointsControlOverlays.push(upperLegsJointControl);
+
+var lowerLegsJointControl = Overlays.addOverlay("image", {
+ bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
+ imageURL: pathToOverlays+"ddao-background-edit-lower-legs.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+jointsControlOverlays.push(lowerLegsJointControl);
+
+var feetJointControl = Overlays.addOverlay("image", {
+ bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
+ imageURL: pathToOverlays+"ddao-background-edit-feet.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+jointsControlOverlays.push(feetJointControl);
+
+var toesJointControl = Overlays.addOverlay("image", {
+ bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
+ imageURL: pathToOverlays+"ddao-background-edit-toes.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+jointsControlOverlays.push(toesJointControl);
+
+var spineJointControl = Overlays.addOverlay("image", {
+ bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
+ imageURL: pathToOverlays+"ddao-background-edit-spine.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+jointsControlOverlays.push(spineJointControl);
+
+var spine1JointControl = Overlays.addOverlay("image", {
+ bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
+ imageURL: pathToOverlays+"ddao-background-edit-spine1.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+jointsControlOverlays.push(spine1JointControl);
+
+var spine2JointControl = Overlays.addOverlay("image", {
+ bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
+ imageURL: pathToOverlays+"ddao-background-edit-spine2.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+jointsControlOverlays.push(spine2JointControl);
+
+var shouldersJointControl = Overlays.addOverlay("image", {
+ bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
+ imageURL: pathToOverlays+"ddao-background-edit-shoulders.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+jointsControlOverlays.push(shouldersJointControl);
+
+var upperArmsJointControl = Overlays.addOverlay("image", {
+ bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
+ imageURL: pathToOverlays+"ddao-background-edit-upper-arms.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+jointsControlOverlays.push(upperArmsJointControl);
+
+var forearmsJointControl = Overlays.addOverlay("image", {
+ bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
+ imageURL: pathToOverlays+"ddao-background-edit-forearms.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+jointsControlOverlays.push(forearmsJointControl);
+
+var handsJointControl = Overlays.addOverlay("image", {
+ bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
+ imageURL: pathToOverlays+"ddao-background-edit-hands.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+jointsControlOverlays.push(handsJointControl);
+
+var headJointControl = Overlays.addOverlay("image", {
+ bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300},
+ imageURL: pathToOverlays+"ddao-background-edit-head.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+jointsControlOverlays.push(headJointControl);
+
+
+// sider thumb overlays
+var sliderOne = Overlays.addOverlay("image", {
+ bounds: { x: 0, y: 0, width: 25, height: 25 },
+ imageURL: pathToOverlays+"ddao-slider-handle.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+sliderThumbOverlays.push(sliderOne);
+var sliderTwo = Overlays.addOverlay("image", {
+ bounds: { x: 0, y: 0, width: 25, height: 25 },
+ imageURL: pathToOverlays+"ddao-slider-handle.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+sliderThumbOverlays.push(sliderTwo);
+var sliderThree = Overlays.addOverlay("image", {
+ bounds: { x: 0, y: 0, width: 25, height: 25 },
+ imageURL: pathToOverlays+"ddao-slider-handle.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+sliderThumbOverlays.push(sliderThree);
+var sliderFour = Overlays.addOverlay("image", {
+ bounds: { x: 0, y: 0, width: 25, height: 25 },
+ imageURL: pathToOverlays+"ddao-slider-handle.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+sliderThumbOverlays.push(sliderFour);
+var sliderFive = Overlays.addOverlay("image", {
+ bounds: { x: 0, y: 0, width: 25, height: 25 },
+ imageURL: pathToOverlays+"ddao-slider-handle.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+sliderThumbOverlays.push(sliderFive);
+var sliderSix = Overlays.addOverlay("image", {
+ bounds: { x: 0, y: 0, width: 25, height: 25 },
+ imageURL: pathToOverlays+"ddao-slider-handle.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+sliderThumbOverlays.push(sliderSix);
+var sliderSeven = Overlays.addOverlay("image", {
+ bounds: { x: 0, y: 0, width: 25, height: 25 },
+ imageURL: pathToOverlays+"ddao-slider-handle.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+sliderThumbOverlays.push(sliderSeven);
+var sliderEight = Overlays.addOverlay("image", {
+ bounds: { x: 0, y: 0, width: 25, height: 25 },
+ imageURL: pathToOverlays+"ddao-slider-handle.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+sliderThumbOverlays.push(sliderEight);
+var sliderNine = Overlays.addOverlay("image", {
+ bounds: { x: 0, y: 0, width: 25, height: 25 },
+ imageURL: pathToOverlays+"ddao-slider-handle.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+sliderThumbOverlays.push(sliderNine);
+
+
+// button overlays
+var onButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+20, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-on-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(onButton);
+var offButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+20, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-off-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(offButton);
+var configWalkButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-edit-walk-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configWalkButton);
+var configWalkButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-edit-walk-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configWalkButtonSelected);
+var configStandButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-edit-stand-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configStandButton);
+var configStandButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-edit-stand-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configStandButtonSelected);
+
+var configFlyingButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-edit-fly-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configFlyingButton);
+var configFlyingButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-edit-fly-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configFlyingButtonSelected);
+
+var configFlyingUpButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-edit-fly-up-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configFlyingUpButton);
+var configFlyingUpButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-edit-fly-up-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configFlyingUpButtonSelected);
+
+var configFlyingDownButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-edit-fly-down-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configFlyingDownButton);
+var configFlyingDownButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-edit-fly-down-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configFlyingDownButtonSelected);
+
+var hideButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-hide-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(hideButton);
+var hideButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-hide-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(hideButtonSelected);
+var configWalkStylesButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-walk-styles-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configWalkStylesButton);
+var configWalkStylesButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-walk-styles-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configWalkStylesButtonSelected);
+var configWalkTweaksButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-walk-tweaks-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configWalkTweaksButton);
+var configWalkTweaksButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-walk-tweaks-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configWalkTweaksButtonSelected);
+
+var configSideStepLeftButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-edit-sidestep-left-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configSideStepLeftButton);
+var configSideStepLeftButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-edit-sidestep-left-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configSideStepLeftButtonSelected);
+
+var configSideStepRightButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-edit-sidestep-right-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configSideStepRightButton);
+var configSideStepRightButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-edit-sidestep-right-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configSideStepRightButtonSelected);
+
+var configWalkJointsButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-bones-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configWalkJointsButton);
+var configWalkJointsButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-bones-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(configWalkJointsButtonSelected);
+
+var backButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-back-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(backButton);
+var backButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 60, height: 47 },
+ imageURL: pathToOverlays+"ddao-back-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+buttonOverlays.push(backButtonSelected);
+
+// big button overlays - front panel
+var bigButtonYOffset = 408; // distance from top of panel to top of first button
+
+var femaleBigButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 230, height: 36},
+ imageURL: pathToOverlays+"ddao-female-big-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+bigButtonOverlays.push(femaleBigButton);
+
+var femaleBigButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 230, height: 36},
+ imageURL: pathToOverlays+"ddao-female-big-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+bigButtonOverlays.push(femaleBigButtonSelected);
+
+var maleBigButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 60, width: 230, height: 36},
+ imageURL: pathToOverlays+"ddao-male-big-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+bigButtonOverlays.push(maleBigButton);
+
+var maleBigButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 60, width: 230, height: 36},
+ imageURL: pathToOverlays+"ddao-male-big-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+bigButtonOverlays.push(maleBigButtonSelected);
+
+var armsFreeBigButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 120, width: 230, height: 36},
+ imageURL: pathToOverlays+"ddao-arms-free-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+bigButtonOverlays.push(armsFreeBigButton);
+
+var armsFreeBigButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 120, width: 230, height: 36},
+ imageURL: pathToOverlays+"ddao-arms-free-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+bigButtonOverlays.push(armsFreeBigButtonSelected);
+
+var footstepsBigButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 180, width: 230, height: 36},
+ imageURL: pathToOverlays+"ddao-footsteps-big-button.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+bigButtonOverlays.push(footstepsBigButton);
+
+var footstepsBigButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 180, width: 230, height: 36},
+ imageURL: pathToOverlays+"ddao-footsteps-big-button-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+bigButtonOverlays.push(footstepsBigButtonSelected);
+
+
+// walk styles
+bigButtonYOffset = 121;
+var strutWalkBigButton = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 230, height: 36 },
+ imageURL: pathToOverlays+"ddao-walk-select-button-strut.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+bigButtonOverlays.push(strutWalkBigButton);
+
+var strutWalkBigButtonSelected = Overlays.addOverlay("image", {
+ bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 230, height: 36 },
+ imageURL: pathToOverlays+"ddao-walk-select-button-strut-selected.png",
+ color: { red: 255, green: 255, blue: 255},
+ alpha: 1,
+ visible: false
+ });
+bigButtonOverlays.push(strutWalkBigButtonSelected);
+
+// overlays to show the walk wheel stats
+var walkWheelZLine = Overlays.addOverlay("line3d", {
+ position: { x: 0, y: 0, z:hipsToFeetDistance },
+ end: { x: 0, y: 0, z: -hipsToFeetDistance },
+ color: { red: 0, green: 255, blue: 255},
+ alpha: 1,
+ lineWidth: 5,
+ visible: statsOn,
+ anchor: "MyAvatar"
+ });
+var walkWheelYLine = Overlays.addOverlay("line3d", {
+ position: { x: 0, y: hipsToFeetDistance, z:0 },
+ end: { x: 0, y: -hipsToFeetDistance, z:0 },
+ color: { red: 255, green: 0, blue: 255},
+ alpha: 1,
+ lineWidth: 5,
+ visible: statsOn,
+ anchor: "MyAvatar"
+ });
+var debugStats = Overlays.addOverlay("text", {
+ x: backgroundX-199, y: backgroundY,
+ width: 200,
+ height: 180,
+ color: { red: 204, green: 204, blue: 204},
+ topMargin: 10,
+ leftMargin: 15,
+ visible: statsOn,
+ backgroundColor: { red: 34, green: 34, blue: 34},
+ alpha: 1.0,
+ text: "Debug Stats\n\n\nNothing to report yet."
+ });
+var debugStatsPeriodic = Overlays.addOverlay("text", {
+ x: backgroundX-199, y: backgroundY+179,
+ width: 200,
+ height: 392,
+ color: { red: 204, green: 204, blue: 204},
+ topMargin: 5,
+ leftMargin: 15,
+ visible: statsOn,
+ backgroundColor: { red: 34, green: 34, blue: 34},
+ alpha: 1.0,
+ text: "Debug Stats\n\n\nNothing to report yet."
+ });
+var walkWheelStats = Overlays.addOverlay("text", {
+ x: backgroundX-199, y: backgroundY+510,
+ width: 200,
+ height: 190,
+ color: { red: 204, green: 204, blue: 204},
+ topMargin: 5,
+ leftMargin: 15,
+ visible: statsOn,
+ backgroundColor: { red: 34, green: 34, blue: 34},
+ alpha: 1.0,
+ text: "WalkWheel Stats\n\n\nNothing to report yet.\n\n\nPlease start walking\nto see the walkwheel."
+ });
+
+// various show / hide GUI element functions
+function doStandardMenu() {
+ hidebuttonOverlays();
+ hideJointControls();
+ setBackground(controlsBackground);
+ if(powerOn) setButtonOverlayVisible(onButton);
+ else setButtonOverlayVisible(offButton);
+ setButtonOverlayVisible(configWalkButton);
+ setButtonOverlayVisible(configStandButton);
+ setButtonOverlayVisible(configFlyingButton);
+ setButtonOverlayVisible(hideButton);
+ setSliderThumbsVisible(false);
+ showFrontPanelButtons(true);
+ showWalkStyleButtons(false);
+}
+function showFrontPanelButtons(showButtons) {
+
+ if(avatarGender===FEMALE) {
+ Overlays.editOverlay(femaleBigButtonSelected, { visible: showButtons } );
+ Overlays.editOverlay(femaleBigButton, { visible: false } );
+ Overlays.editOverlay(maleBigButtonSelected, { visible: false } );
+ Overlays.editOverlay(maleBigButton, { visible: showButtons } );
+ } else {
+ Overlays.editOverlay(femaleBigButtonSelected, { visible: false } );
+ Overlays.editOverlay(femaleBigButton, { visible: showButtons } );
+ Overlays.editOverlay(maleBigButtonSelected, { visible: showButtons } );
+ Overlays.editOverlay(maleBigButton, { visible: false } );
+ }
+ if(armsFree) {
+ Overlays.editOverlay(armsFreeBigButtonSelected, { visible: showButtons } );
+ Overlays.editOverlay(armsFreeBigButton, { visible: false } );
+ } else {
+ Overlays.editOverlay(armsFreeBigButtonSelected, { visible: false } );
+ Overlays.editOverlay(armsFreeBigButton, { visible: showButtons } );
+ }
+ if(playFootStepSounds) {
+ Overlays.editOverlay(footstepsBigButtonSelected, { visible: showButtons } );
+ Overlays.editOverlay(footstepsBigButton, { visible: false } );
+ } else {
+ Overlays.editOverlay(footstepsBigButtonSelected, { visible: false } );
+ Overlays.editOverlay(footstepsBigButton, { visible: showButtons } );
+ }
+}
+function minimiseDialog() {
+
+ if(minimised) {
+ setBackground();
+ hidebuttonOverlays();
+ setSliderThumbsVisible(false);
+ hideJointControls();
+ showFrontPanelButtons(false);
+ Overlays.editOverlay(controlsMinimisedTab, { visible: true } );
+ } else {
+ setInternalState(STANDING); // show all the controls again
+ Overlays.editOverlay(controlsMinimisedTab, { visible: false } );
+ }
+}
+function setBackground(backgroundName) {
+ for(var i in backgroundOverlays) {
+ if(backgroundOverlays[i] === backgroundName)
+ Overlays.editOverlay(backgroundName, { visible: true } );
+ else Overlays.editOverlay(backgroundOverlays[i], { visible: false } );
+ }
+}
+function setButtonOverlayVisible(buttonOverlayName) {
+ for(var i in buttonOverlays) {
+ if(buttonOverlays[i] === buttonOverlayName) {
+ Overlays.editOverlay(buttonOverlayName, { visible: true } );
+ }
+ }
+}
+// top row menu type buttons (smaller)
+function hidebuttonOverlays() {
+ for(var i in buttonOverlays) {
+ Overlays.editOverlay(buttonOverlays[i], { visible: false } );
+ }
+}
+function hideJointControls() {
+ for(var i in jointsControlOverlays) {
+ Overlays.editOverlay(jointsControlOverlays[i], { visible: false } );
+ }
+}
+function setSliderThumbsVisible(thumbsVisible) {
+ for(var i = 0 ; i < sliderThumbOverlays.length ; i++) {
+ Overlays.editOverlay(sliderThumbOverlays[i], { visible: thumbsVisible } );
+ }
+}
+function initialiseJointsEditingPanel(propertyIndex) {
+
+ selectedJointIndex = propertyIndex;
+
+ // set the image for the selected joint on the character control
+ hideJointControls();
+ switch (selectedJointIndex) {
+ case 0:
+ Overlays.editOverlay(hipsJointControl, { visible: true });
+ break;
+ case 1:
+ Overlays.editOverlay(upperLegsJointControl, { visible: true });
+ break;
+ case 2:
+ Overlays.editOverlay(lowerLegsJointControl, { visible: true });
+ break;
+ case 3:
+ Overlays.editOverlay(feetJointControl, { visible: true });
+ break;
+ case 4:
+ Overlays.editOverlay(toesJointControl, { visible: true });
+ break;
+ case 5:
+ Overlays.editOverlay(spineJointControl, { visible: true });
+ break;
+ case 6:
+ Overlays.editOverlay(spine1JointControl, { visible: true });
+ break;
+ case 7:
+ Overlays.editOverlay(spine2JointControl, { visible: true });
+ break;
+ case 8:
+ Overlays.editOverlay(shouldersJointControl, { visible: true });
+ break;
+ case 9:
+ Overlays.editOverlay(upperArmsJointControl, { visible: true });
+ break;
+ case 10:
+ Overlays.editOverlay(forearmsJointControl, { visible: true });
+ break;
+ case 11:
+ Overlays.editOverlay(handsJointControl, { visible: true });
+ break;
+ case 12:
+ Overlays.editOverlay(headJointControl, { visible: true });
+ break;
+ }
+
+ // set sliders to adjust individual joint properties
+ var i = 0;
+ var yLocation = backgroundY+359;
+
+ // pitch your role
+ var sliderXPos = currentAnimation.joints[selectedJointIndex].pitch
+ / sliderRanges.joints[selectedJointIndex].pitchRange * sliderRangeX;
+ Overlays.editOverlay(sliderThumbOverlays[i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
+ sliderXPos = currentAnimation.joints[selectedJointIndex].yaw
+ / sliderRanges.joints[selectedJointIndex].yawRange * sliderRangeX;
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
+ sliderXPos = currentAnimation.joints[selectedJointIndex].roll
+ / sliderRanges.joints[selectedJointIndex].rollRange * sliderRangeX;
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
+
+ // set phases (full range, -180 to 180)
+ sliderXPos = (90 + currentAnimation.joints[selectedJointIndex].pitchPhase/2)/180 * sliderRangeX;
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
+ sliderXPos = (90 + currentAnimation.joints[selectedJointIndex].yawPhase/2)/180 * sliderRangeX;
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
+ sliderXPos = (90 + currentAnimation.joints[selectedJointIndex].rollPhase/2)/180 * sliderRangeX;
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
+
+ // offset ranges are also -ve thr' zero to +ve, so have to offset
+ sliderXPos = (((sliderRanges.joints[selectedJointIndex].pitchOffsetRange+currentAnimation.joints[selectedJointIndex].pitchOffset)/2)
+ /sliderRanges.joints[selectedJointIndex].pitchOffsetRange) * sliderRangeX;
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
+ sliderXPos = (((sliderRanges.joints[selectedJointIndex].yawOffsetRange+currentAnimation.joints[selectedJointIndex].yawOffset)/2)
+ /sliderRanges.joints[selectedJointIndex].yawOffsetRange) * sliderRangeX;
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
+ sliderXPos = (((sliderRanges.joints[selectedJointIndex].rollOffsetRange+currentAnimation.joints[selectedJointIndex].rollOffset)/2)
+ /sliderRanges.joints[selectedJointIndex].rollOffsetRange) * sliderRangeX;
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true });
+}
+
+function initialiseWalkTweaks() {
+
+ // set sliders to adjust walk properties
+ var i = 0;
+ var yLocation = backgroundY+71;
+
+ var sliderXPos = currentAnimation.settings.baseFrequency / MAX_WALK_SPEED * sliderRangeX; // walk speed
+ Overlays.editOverlay(sliderThumbOverlays[i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
+ sliderXPos = 0 * sliderRangeX; // start flying speed - depricated
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
+ sliderXPos = currentAnimation.joints[0].sway / sliderRanges.joints[0].swayRange * sliderRangeX; // Hips sway
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
+ sliderXPos = currentAnimation.joints[0].bob / sliderRanges.joints[0].bobRange * sliderRangeX; // Hips bob
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
+ sliderXPos = currentAnimation.joints[0].thrust / sliderRanges.joints[0].thrustRange * sliderRangeX; // Hips thrust
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
+ sliderXPos = (((sliderRanges.joints[1].rollOffsetRange+currentAnimation.joints[1].rollOffset)/2) // legs separation - is upper legs roll offset
+ / sliderRanges.joints[1].rollOffsetRange) * sliderRangeX;
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
+ sliderXPos = currentAnimation.joints[1].pitch / sliderRanges.joints[1].pitchRange * sliderRangeX; // stride - is upper legs pitch
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
+ sliderXPos = currentAnimation.joints[9].yaw / sliderRanges.joints[9].yawRange * sliderRangeX; // arms swing - is upper arms yaw
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
+ sliderXPos = (((sliderRanges.joints[9].pitchOffsetRange-currentAnimation.joints[9].pitchOffset)/2)
+ / sliderRanges.joints[9].pitchOffsetRange) * sliderRangeX; // arms out - is upper arms pitch offset
+ Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true });
+}
+
+function showWalkStyleButtons(showButtons) {
+
+ // set all big buttons to hidden, but skip the first 8, as are for the front panel
+ for(var i = 8 ; i < bigButtonOverlays.length ; i++) {
+ Overlays.editOverlay(bigButtonOverlays[i], { visible: false });
+ }
+
+ if(!showButtons) return;
+
+ // set all the non-selected ones to showing
+ for(var i = 8 ; i < bigButtonOverlays.length ; i+=2) {
+ Overlays.editOverlay(bigButtonOverlays[i], { visible: showButtons });
+ }
+
+ // set the currently selected one
+ if(selectedWalk === femaleStrutWalk || selectedWalk === maleStrutWalk) {
+ Overlays.editOverlay(strutWalkBigButtonSelected, { visible: showButtons });
+ Overlays.editOverlay(strutWalkBigButton, {visible: false});
+ }
+}
+
+// mouse event handlers
+var movingSliderOne = false;
+var movingSliderTwo = false;
+var movingSliderThree = false;
+var movingSliderFour = false;
+var movingSliderFive = false;
+var movingSliderSix = false;
+var movingSliderSeven = false;
+var movingSliderEight = false;
+var movingSliderNine = false;
+
+function mousePressEvent(event) {
+
+ var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
+
+ // check for a character joint control click
+ switch (clickedOverlay) {
+
+ case hideButton:
+ Overlays.editOverlay(hideButton, { visible: false } );
+ Overlays.editOverlay(hideButtonSelected, { visible: true } );
+ return;
+
+ case backButton:
+ Overlays.editOverlay(backButton, { visible: false } );
+ Overlays.editOverlay(backButtonSelected, { visible: true } );
+ return;
+
+ case controlsMinimisedTab:
+ // TODO: add visual user feedback for tab click
+ return;
+
+ case footstepsBigButton:
+ playFootStepSounds = true;
+ Overlays.editOverlay(footstepsBigButtonSelected, { visible: true } );
+ Overlays.editOverlay(footstepsBigButton, { visible: false } );
+ return;
+
+ case footstepsBigButtonSelected:
+ playFootStepSounds = false;
+ Overlays.editOverlay(footstepsBigButton, { visible: true } );
+ Overlays.editOverlay(footstepsBigButtonSelected, { visible: false } );
+ return;
+
+ case femaleBigButton:
+ case maleBigButtonSelected:
+ avatarGender = FEMALE;
+ selectedWalk = femaleStrutWalk;
+ selectedStand = femaleStandOne;
+ selectedFlyUp = femaleFlyingUp;
+ selectedFly = femaleFlying;
+ selectedFlyDown = femaleFlyingDown;
+ selectedSideStepLeft = femaleSideStepLeft;
+ selectedSideStepRight = femaleSideStepRight;
+ Overlays.editOverlay(femaleBigButtonSelected, { visible: true } );
+ Overlays.editOverlay(femaleBigButton, { visible: false } );
+ Overlays.editOverlay(maleBigButton, { visible: true } );
+ Overlays.editOverlay(maleBigButtonSelected, { visible: false } );
+ return;
+
+ case armsFreeBigButton:
+ armsFree = true;
+ Overlays.editOverlay(armsFreeBigButtonSelected, { visible: true } );
+ Overlays.editOverlay(armsFreeBigButton, { visible: false } );
+ return;
+
+ case armsFreeBigButtonSelected:
+ armsFree = false;
+ Overlays.editOverlay(armsFreeBigButtonSelected, { visible: false } );
+ Overlays.editOverlay(armsFreeBigButton, { visible: true } );
+ return;
+
+ case maleBigButton:
+ case femaleBigButtonSelected:
+ avatarGender = MALE;
+ selectedWalk = maleStrutWalk;
+ selectedStand = maleStandOne;
+ selectedFlyUp = maleFlyingUp;
+ selectedFly = maleFlying;
+ selectedFlyDown = maleFlyingDown;
+ selectedSideStepLeft = maleSideStepLeft;
+ selectedSideStepRight = maleSideStepRight;
+ Overlays.editOverlay(femaleBigButton, { visible: true } );
+ Overlays.editOverlay(femaleBigButtonSelected, { visible: false } );
+ Overlays.editOverlay(maleBigButtonSelected, { visible: true } );
+ Overlays.editOverlay(maleBigButton, { visible: false } );
+ return;
+
+
+ case strutWalkBigButton:
+ if(avatarGender===FEMALE) selectedWalk = femaleStrutWalk;
+ else selectedWalk = maleStrutWalk;
+ currentAnimation = selectedWalk;
+ showWalkStyleButtons(true);
+ return;
+
+ case strutWalkBigButtonSelected:
+
+ // toggle forwards / backwards walk display
+ if(principleDirection===DIRECTION_FORWARDS) {
+ principleDirection=DIRECTION_BACKWARDS;
+ } else principleDirection=DIRECTION_FORWARDS;
+ return;
+
+ case sliderOne:
+ movingSliderOne = true;
+ return;
+
+ case sliderTwo:
+ movingSliderTwo = true;
+ return;
+
+ case sliderThree:
+ movingSliderThree = true;
+ return;
+
+ case sliderFour:
+ movingSliderFour = true;
+ return;
+
+ case sliderFive:
+ movingSliderFive = true;
+ return;
+
+ case sliderSix:
+ movingSliderSix = true;
+ return;
+
+ case sliderSeven:
+ movingSliderSeven = true;
+ return;
+
+ case sliderEight:
+ movingSliderEight = true;
+ return;
+
+ case sliderNine:
+ movingSliderNine = true;
+ return;
+ }
+
+ if( INTERNAL_STATE===CONFIG_WALK_JOINTS ||
+ INTERNAL_STATE===CONFIG_STANDING ||
+ INTERNAL_STATE===CONFIG_FLYING ||
+ INTERNAL_STATE===CONFIG_FLYING_UP ||
+ INTERNAL_STATE===CONFIG_FLYING_DOWN ||
+ INTERNAL_STATE===CONFIG_SIDESTEP_LEFT ||
+ INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) {
+
+ // check for new joint selection and update display accordingly
+ var clickX = event.x - backgroundX - 75;
+ var clickY = event.y - backgroundY - 92;
+
+ if(clickX>60&&clickX<120&&clickY>123&&clickY<155) {
+ initialiseJointsEditingPanel(0);
+ return;
+ }
+ else if(clickX>63&&clickX<132&&clickY>156&&clickY<202) {
+ initialiseJointsEditingPanel(1);
+ return;
+ }
+ else if(clickX>58&&clickX<137&&clickY>203&&clickY<250) {
+ initialiseJointsEditingPanel(2);
+ return;
+ }
+ else if(clickX>58&&clickX<137&&clickY>250&&clickY<265) {
+ initialiseJointsEditingPanel(3);
+ return;
+ }
+ else if(clickX>58&&clickX<137&&clickY>265&&clickY<280) {
+ initialiseJointsEditingPanel(4);
+ return;
+ }
+ else if(clickX>78&&clickX<121&&clickY>111&&clickY<128) {
+ initialiseJointsEditingPanel(5);
+ return;
+ }
+ else if(clickX>78&&clickX<128&&clickY>89&&clickY<111) {
+ initialiseJointsEditingPanel(6);
+ return;
+ }
+ else if(clickX>85&&clickX<118&&clickY>77&&clickY<94) {
+ initialiseJointsEditingPanel(7);
+ return;
+ }
+ else if(clickX>64&&clickX<125&&clickY>55&&clickY<77) {
+ initialiseJointsEditingPanel(8);
+ return;
+ }
+ else if((clickX>44&&clickX<73&&clickY>71&&clickY<94)
+ ||(clickX>125&&clickX<144&&clickY>71&&clickY<94)) {
+ initialiseJointsEditingPanel(9);
+ return;
+ }
+ else if((clickX>28&&clickX<57&&clickY>94&&clickY<119)
+ ||(clickX>137&&clickX<170&&clickY>97&&clickY<114)) {
+ initialiseJointsEditingPanel(10);
+ return;
+ }
+ else if((clickX>18&&clickX<37&&clickY>115&&clickY<136)
+ ||(clickX>157&&clickX<182&&clickY>115&&clickY<136)) {
+ initialiseJointsEditingPanel(11);
+ return;
+ }
+ else if(clickX>81&&clickX<116&&clickY>12&&clickY<53) {
+ initialiseJointsEditingPanel(12);
+ return;
+ }
+ }
+}
+function mouseMoveEvent(event) {
+
+ // only need deal with slider changes
+ if(powerOn) {
+
+ if( INTERNAL_STATE===CONFIG_WALK_JOINTS ||
+ INTERNAL_STATE===CONFIG_STANDING ||
+ INTERNAL_STATE===CONFIG_FLYING ||
+ INTERNAL_STATE===CONFIG_FLYING_UP ||
+ INTERNAL_STATE===CONFIG_FLYING_DOWN ||
+ INTERNAL_STATE===CONFIG_SIDESTEP_LEFT ||
+ INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) {
+
+ var thumbClickOffsetX = event.x - minSliderX;
+ var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX;
+ if(thumbPositionNormalised<0) thumbPositionNormalised = 0;
+ if(thumbPositionNormalised>1) thumbPositionNormalised = 1;
+ var sliderX = thumbPositionNormalised * sliderRangeX ; // sets range
+
+ if(movingSliderOne) { // currently selected joint pitch
+ Overlays.editOverlay(sliderOne, { x: sliderX + minSliderX} );
+ currentAnimation.joints[selectedJointIndex].pitch = thumbPositionNormalised * sliderRanges.joints[selectedJointIndex].pitchRange;
+ }
+ else if(movingSliderTwo) { // currently selected joint yaw
+ Overlays.editOverlay(sliderTwo, { x: sliderX + minSliderX} );
+ currentAnimation.joints[selectedJointIndex].yaw = thumbPositionNormalised * sliderRanges.joints[selectedJointIndex].yawRange;
+ }
+ else if(movingSliderThree) { // currently selected joint roll
+ Overlays.editOverlay(sliderThree, { x: sliderX + minSliderX} );
+ currentAnimation.joints[selectedJointIndex].roll = thumbPositionNormalised * sliderRanges.joints[selectedJointIndex].rollRange;
+ }
+ else if(movingSliderFour) { // currently selected joint pitch phase
+ Overlays.editOverlay(sliderFour, { x: sliderX + minSliderX} );
+ var newPhase = 360 * thumbPositionNormalised - 180;
+ currentAnimation.joints[selectedJointIndex].pitchPhase = newPhase;
+ }
+ else if(movingSliderFive) { // currently selected joint yaw phase;
+ Overlays.editOverlay(sliderFive, { x: sliderX + minSliderX} );
+ var newPhase = 360 * thumbPositionNormalised - 180;
+ currentAnimation.joints[selectedJointIndex].yawPhase = newPhase;
+ }
+ else if(movingSliderSix) { // currently selected joint roll phase
+ Overlays.editOverlay(sliderSix, { x: sliderX + minSliderX} );
+ var newPhase = 360 * thumbPositionNormalised - 180;
+ currentAnimation.joints[selectedJointIndex].rollPhase = newPhase;
+ }
+ else if(movingSliderSeven) { // currently selected joint pitch offset
+ Overlays.editOverlay(sliderSeven, { x: sliderX + minSliderX} ); // currently selected joint pitch offset
+ var newOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[selectedJointIndex].pitchOffsetRange;
+ currentAnimation.joints[selectedJointIndex].pitchOffset = newOffset;
+ }
+ else if(movingSliderEight) { // currently selected joint yaw offset
+ Overlays.editOverlay(sliderEight, { x: sliderX + minSliderX} ); // currently selected joint yaw offset
+ var newOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[selectedJointIndex].yawOffsetRange;
+ currentAnimation.joints[selectedJointIndex].yawOffset = newOffset;
+ }
+ else if(movingSliderNine) { // currently selected joint roll offset
+ Overlays.editOverlay(sliderNine, { x: sliderX + minSliderX} ); // currently selected joint roll offset
+ var newOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[selectedJointIndex].rollOffsetRange;
+ currentAnimation.joints[selectedJointIndex].rollOffset = newOffset;
+ }
+ }
+ else if(INTERNAL_STATE===CONFIG_WALK_TWEAKS) {
+
+ var thumbClickOffsetX = event.x - minSliderX;
+ var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX;
+ if(thumbPositionNormalised<0) thumbPositionNormalised = 0;
+ if(thumbPositionNormalised>1) thumbPositionNormalised = 1;
+ var sliderX = thumbPositionNormalised * sliderRangeX ; // sets range
+
+ if(movingSliderOne) { // walk speed
+ paused = true; // avoid nasty jittering
+ Overlays.editOverlay(sliderOne, { x: sliderX + minSliderX} );
+ currentAnimation.settings.baseFrequency = thumbPositionNormalised * MAX_WALK_SPEED;
+ }
+ else if(movingSliderTwo) { // take flight speed
+ Overlays.editOverlay(sliderTwo, { x: sliderX + minSliderX} );
+ //currentAnimation.settings.takeFlightVelocity = thumbPositionNormalised * 300;
+ }
+ else if(movingSliderThree) { // hips sway
+ Overlays.editOverlay(sliderThree, { x: sliderX + minSliderX} );
+ currentAnimation.joints[0].sway = thumbPositionNormalised * sliderRanges.joints[0].swayRange;
+ }
+ else if(movingSliderFour) { // hips bob
+ Overlays.editOverlay(sliderFour, { x: sliderX + minSliderX} );
+ currentAnimation.joints[0].bob = thumbPositionNormalised * sliderRanges.joints[0].bobRange;
+ }
+ else if(movingSliderFive) { // hips thrust
+ Overlays.editOverlay(sliderFive, { x: sliderX + minSliderX} );
+ currentAnimation.joints[0].thrust = thumbPositionNormalised * sliderRanges.joints[0].thrustRange;
+ }
+ else if(movingSliderSix) { // legs separation
+ Overlays.editOverlay(sliderSix, { x: sliderX + minSliderX} );
+ currentAnimation.joints[1].rollOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[1].rollOffsetRange;
+ }
+ else if(movingSliderSeven) { // stride
+ Overlays.editOverlay(sliderSeven, { x: sliderX + minSliderX} );
+ currentAnimation.joints[1].pitch = thumbPositionNormalised * sliderRanges.joints[1].pitchRange;
+ }
+ else if(movingSliderEight) { // arms swing = upper arms yaw
+ Overlays.editOverlay(sliderEight, { x: sliderX + minSliderX} );
+ currentAnimation.joints[9].yaw = thumbPositionNormalised * sliderRanges.joints[9].yawRange;
+ }
+ else if(movingSliderNine) { // arms out = upper arms pitch offset
+ Overlays.editOverlay(sliderNine, { x: sliderX + minSliderX} );
+ currentAnimation.joints[9].pitchOffset = (thumbPositionNormalised-0.5) * -2 * sliderRanges.joints[9].pitchOffsetRange;
+ }
+ }
+ }
+}
+function mouseReleaseEvent(event) {
+
+ var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
+
+ if(paused) paused = false;
+
+ if(clickedOverlay === offButton) {
+ powerOn = true;
+ Overlays.editOverlay(offButton, { visible: false } );
+ Overlays.editOverlay(onButton, { visible: true } );
+ stand();
+ }
+ else if(clickedOverlay === hideButton || clickedOverlay === hideButtonSelected){
+ Overlays.editOverlay(hideButton, { visible: true } );
+ Overlays.editOverlay(hideButtonSelected, { visible: false } );
+ minimised = true;
+ minimiseDialog();
+ }
+ else if(clickedOverlay === controlsMinimisedTab) {
+ minimised = false;
+ minimiseDialog();
+ }
+ else if(powerOn) {
+
+ if(movingSliderOne) movingSliderOne = false;
+ else if(movingSliderTwo) movingSliderTwo = false;
+ else if(movingSliderThree) movingSliderThree = false;
+ else if(movingSliderFour) movingSliderFour = false;
+ else if(movingSliderFive) movingSliderFive = false;
+ else if(movingSliderSix) movingSliderSix = false;
+ else if(movingSliderSeven) movingSliderSeven = false;
+ else if(movingSliderEight) movingSliderEight = false;
+ else if(movingSliderNine) movingSliderNine = false;
+ else {
+
+ switch(clickedOverlay) {
+
+ case configWalkButtonSelected:
+ case configStandButtonSelected:
+ case configSideStepLeftButtonSelected:
+ case configSideStepRightButtonSelected:
+ case configFlyingButtonSelected:
+ case configFlyingUpButtonSelected:
+ case configFlyingDownButtonSelected:
+ case configWalkStylesButtonSelected:
+ case configWalkTweaksButtonSelected:
+ case configWalkJointsButtonSelected:
+ setInternalState(STANDING);
+ break;
+
+ case onButton:
+ powerOn = false;
+ setInternalState(STANDING);
+ Overlays.editOverlay(offButton, { visible: true } );
+ Overlays.editOverlay(onButton, { visible: false } );
+ resetJoints();
+ break;
+
+ case backButton:
+ case backButtonSelected:
+ Overlays.editOverlay(backButton, { visible: false } );
+ Overlays.editOverlay(backButtonSelected, { visible: false } );
+ setInternalState(STANDING);
+ break;
+
+ case configWalkStylesButton:
+ setInternalState(CONFIG_WALK_STYLES);
+ break;
+
+ case configWalkTweaksButton:
+ setInternalState(CONFIG_WALK_TWEAKS);
+ break;
+
+ case configWalkJointsButton:
+ setInternalState(CONFIG_WALK_JOINTS);
+ break;
+
+ case configWalkButton:
+ setInternalState(CONFIG_WALK_STYLES); // set the default walk adjustment panel here (i.e. first panel shown when Walk button clicked)
+ break;
+
+ case configStandButton:
+ setInternalState(CONFIG_STANDING);
+ break;
+
+ case configSideStepLeftButton:
+ setInternalState(CONFIG_SIDESTEP_LEFT);
+ break;
+
+ case configSideStepRightButton:
+ setInternalState(CONFIG_SIDESTEP_RIGHT);
+ break;
+
+ case configFlyingButton:
+ setInternalState(CONFIG_FLYING);
+ break;
+
+ case configFlyingUpButton:
+ setInternalState(CONFIG_FLYING_UP);
+ break;
+
+ case configFlyingDownButton:
+ setInternalState(CONFIG_FLYING_DOWN);
+ break;
+ }
+ }
+ }
+}
+Controller.mouseMoveEvent.connect(mouseMoveEvent);
+Controller.mousePressEvent.connect(mousePressEvent);
+Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
+
+// Script ending
+Script.scriptEnding.connect(function() {
+
+ // remove the background overlays
+ for(var i in backgroundOverlays) {
+ Overlays.deleteOverlay(backgroundOverlays[i]);
+ }
+ // remove the button overlays
+ for(var i in buttonOverlays) {
+ Overlays.deleteOverlay(buttonOverlays[i]);
+ }
+ // remove the slider thumb overlays
+ for(var i in sliderThumbOverlays) {
+ Overlays.deleteOverlay(sliderThumbOverlays[i]);
+ }
+ // remove the character joint control overlays
+ for(var i in bigButtonOverlays) {
+ Overlays.deleteOverlay(jointsControlOverlays[i]);
+ }
+ // remove the big button overlays
+ for(var i in bigButtonOverlays) {
+ Overlays.deleteOverlay(bigButtonOverlays[i]);
+ }
+ // remove the mimimised tab
+ Overlays.deleteOverlay(controlsMinimisedTab);
+
+ // remove the walk wheel overlays
+ Overlays.deleteOverlay(walkWheelYLine);
+ Overlays.deleteOverlay(walkWheelZLine);
+ Overlays.deleteOverlay(walkWheelStats);
+
+ // remove the debug stats overlays
+ Overlays.deleteOverlay(debugStats);
+ Overlays.deleteOverlay(debugStatsPeriodic);
+});
+
+var sideStep = 0.002; // i.e. 2mm increments whilst sidestepping - JS movement keys don't work well :-(
+function keyPressEvent(event) {
+
+ if (event.text == "q") {
+ // export currentAnimation as json string when q key is pressed. reformat result at http://www.freeformatter.com/json-formatter.html
+ print('\n');
+ print('walk.js dumping animation: '+currentAnimation.name+'\n');
+ print('\n');
+ print(JSON.stringify(currentAnimation), null, '\t');
+ }
+ else if (event.text == "t") {
+ statsOn = !statsOn;
+ Overlays.editOverlay(debugStats, {visible: statsOn});
+ Overlays.editOverlay(debugStatsPeriodic, {visible: statsOn});
+ Overlays.editOverlay(walkWheelStats, {visible: statsOn});
+ Overlays.editOverlay(walkWheelYLine, {visible: statsOn});
+ Overlays.editOverlay(walkWheelZLine, {visible: statsOn});
+ }
+}
+Controller.keyPressEvent.connect(keyPressEvent);
+
+// get the list of joint names
+var jointList = MyAvatar.getJointNames();
+
+// clear the joint data so can calculate hips to feet distance
+for(var i = 0 ; i < 5 ; i++) {
+ MyAvatar.setJointData(i, Quat.fromPitchYawRollDegrees(0,0,0));
+}
+// used to position the visual representation of the walkwheel only
+var hipsToFeetDistance = MyAvatar.getJointPosition("Hips").y - MyAvatar.getJointPosition("RightFoot").y;
+
+// This script is designed around the Finite State Machine (FSM) model, so to start things up we just select the STANDING state.
+setInternalState(STANDING);
\ No newline at end of file
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 215848b355..a31b148d1b 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -350,7 +350,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
- NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkDiskCache* cache = new QNetworkDiskCache();
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache");
networkAccessManager.setCache(cache);
diff --git a/interface/src/FileLogger.cpp b/interface/src/FileLogger.cpp
index cb3d43925d..4808842036 100644
--- a/interface/src/FileLogger.cpp
+++ b/interface/src/FileLogger.cpp
@@ -28,7 +28,7 @@ FileLogger::FileLogger(QObject* parent) :
setExtraDebugging(false);
_fileName = FileUtils::standardPath(LOGS_DIRECTORY);
- QHostAddress clientAddress = QHostAddress(getHostOrderLocalAddress());
+ QHostAddress clientAddress = getLocalAddress();
QDateTime now = QDateTime::currentDateTime();
_fileName.append(QString(FILENAME_FORMAT).arg(clientAddress.toString(), now.toString(DATETIME_FORMAT)));
}
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 75eb96adc9..954f04f47d 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -1238,7 +1238,7 @@ void Menu::toggleLocationList() {
if (!_userLocationsDialog) {
JavascriptObjectMap locationObjectMap;
locationObjectMap.insert("InterfaceLocation", LocationScriptingInterface::getInstance());
- _userLocationsDialog = DataWebDialog::dialogForPath("/locations", locationObjectMap);
+ _userLocationsDialog = DataWebDialog::dialogForPath("/user/locations", locationObjectMap);
}
if (!_userLocationsDialog->isVisible()) {
@@ -1282,7 +1282,7 @@ void Menu::nameLocation() {
if (!_newLocationDialog) {
JavascriptObjectMap locationObjectMap;
locationObjectMap.insert("InterfaceLocation", LocationScriptingInterface::getInstance());
- _newLocationDialog = DataWebDialog::dialogForPath("/locations/new", locationObjectMap);
+ _newLocationDialog = DataWebDialog::dialogForPath("/user/locations/new", locationObjectMap);
}
if (!_newLocationDialog->isVisible()) {
diff --git a/interface/src/ScriptsModel.cpp b/interface/src/ScriptsModel.cpp
index 8bea122338..b95b6ae735 100644
--- a/interface/src/ScriptsModel.cpp
+++ b/interface/src/ScriptsModel.cpp
@@ -117,7 +117,7 @@ void ScriptsModel::requestRemoteFiles(QString marker) {
}
url.setQuery(query);
- NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest request(url);
QNetworkReply* reply = networkAccessManager.get(request);
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index a31c25b572..087d670760 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -49,8 +49,8 @@ const float PITCH_SPEED = 100.0f; // degrees/sec
const float COLLISION_RADIUS_SCALAR = 1.2f; // pertains to avatar-to-avatar collisions
const float COLLISION_RADIUS_SCALE = 0.125f;
-const float MIN_KEYBOARD_CONTROL_SPEED = 1.5f;
-const float MAX_WALKING_SPEED = 3.0f * MIN_KEYBOARD_CONTROL_SPEED;
+const float MIN_KEYBOARD_CONTROL_SPEED = 0.50f;
+const float MAX_WALKING_SPEED = 4.5f;
// TODO: normalize avatar speed for standard avatar size, then scale all motion logic
// to properly follow avatar size.
@@ -1262,21 +1262,19 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
// Compute the target keyboard velocity (which ramps up slowly, and damps very quickly)
// the max magnitude of which depends on what we're doing:
- float finalMaxMotorSpeed = hasFloor ? _scale * MAX_WALKING_SPEED : _scale * MAX_KEYBOARD_MOTOR_SPEED;
float motorLength = glm::length(_keyboardMotorVelocity);
+ float finalMaxMotorSpeed = hasFloor ? _scale * MAX_WALKING_SPEED : _scale * MAX_KEYBOARD_MOTOR_SPEED;
+ float speedGrowthTimescale = 2.0f;
+ float speedIncreaseFactor = 1.8f;
+ motorLength *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor;
if (motorLength < _scale * MIN_KEYBOARD_CONTROL_SPEED) {
// an active keyboard motor should never be slower than this
- _keyboardMotorVelocity = _scale * MIN_KEYBOARD_CONTROL_SPEED * direction;
+ motorLength = _scale * MIN_KEYBOARD_CONTROL_SPEED;
motorEfficiency = 1.0f;
- } else {
- float KEYBOARD_MOTOR_LENGTH_TIMESCALE = 2.0f;
- float INCREASE_FACTOR = 1.8f;
- motorLength *= 1.0f + glm::clamp(deltaTime / KEYBOARD_MOTOR_LENGTH_TIMESCALE, 0.0f, 1.0f) * INCREASE_FACTOR;
- if (motorLength > finalMaxMotorSpeed) {
- motorLength = finalMaxMotorSpeed;
- }
- _keyboardMotorVelocity = motorLength * direction;
+ } else if (motorLength > finalMaxMotorSpeed) {
+ motorLength = finalMaxMotorSpeed;
}
+ _keyboardMotorVelocity = motorLength * direction;
_isPushing = true;
}
} else {
diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp
index 26505af0be..d81eaa36b4 100644
--- a/interface/src/ui/AddressBarDialog.cpp
+++ b/interface/src/ui/AddressBarDialog.cpp
@@ -26,7 +26,7 @@ AddressBarDialog::AddressBarDialog() :
void AddressBarDialog::setupUI() {
const QString DIALOG_STYLESHEET = "font-family: Helvetica, Arial, sans-serif;";
- const QString ADDRESSBAR_PLACEHOLDER = "Go to: domain, @user, #location";
+ const QString ADDRESSBAR_PLACEHOLDER = "Go to: domain, location, @user, /x,y,z";
const QString ADDRESSBAR_STYLESHEET = "padding: 5px 10px; font-size: 20px;";
const int ADDRESSBAR_MIN_WIDTH = 200;
diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp
index 9ff839256d..7a76bc2d7d 100644
--- a/interface/src/ui/ModelsBrowser.cpp
+++ b/interface/src/ui/ModelsBrowser.cpp
@@ -26,7 +26,7 @@
const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "attachments" };
-static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
+static const QString S3_URL = "https://s3.amazonaws.com/hifi-public";
static const QString PUBLIC_URL = "http://public.highfidelity.io";
static const QString MODELS_LOCATION = "models/";
@@ -221,7 +221,7 @@ void ModelHandler::update() {
}
for (int i = 0; i < _model.rowCount(); ++i) {
QUrl url(_model.item(i,0)->data(Qt::UserRole).toString());
- NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest request(url);
QNetworkReply* reply = networkAccessManager.head(request);
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
@@ -272,7 +272,7 @@ void ModelHandler::queryNewFiles(QString marker) {
// Download
url.setQuery(query);
- NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest request(url);
QNetworkReply* reply = networkAccessManager.get(request);
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
diff --git a/interface/src/ui/ScriptEditorWidget.cpp b/interface/src/ui/ScriptEditorWidget.cpp
index 1473e4a6a0..b55c753061 100644
--- a/interface/src/ui/ScriptEditorWidget.cpp
+++ b/interface/src/ui/ScriptEditorWidget.cpp
@@ -150,7 +150,7 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) {
disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
}
} else {
- NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
qDebug() << "Downloading included script at" << scriptPath;
QEventLoop loop;
diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp
index cecec29130..6ff256d48e 100644
--- a/interface/src/ui/overlays/Circle3DOverlay.cpp
+++ b/interface/src/ui/overlays/Circle3DOverlay.cpp
@@ -21,8 +21,15 @@ Circle3DOverlay::Circle3DOverlay() :
_startAt(0.0f),
_endAt(360.0f),
_outerRadius(1.0f),
- _innerRadius(0.0f)
+ _innerRadius(0.0f),
+ _hasTickMarks(false),
+ _majorTickMarksAngle(0.0f),
+ _minorTickMarksAngle(0.0f),
+ _majorTickMarksLength(0.0f),
+ _minorTickMarksLength(0.0f)
{
+ _majorTickMarksColor.red = _majorTickMarksColor.green = _majorTickMarksColor.blue = (unsigned char)0;
+ _minorTickMarksColor.red = _minorTickMarksColor.green = _minorTickMarksColor.blue = (unsigned char)0;
}
Circle3DOverlay::~Circle3DOverlay() {
@@ -142,6 +149,66 @@ void Circle3DOverlay::render() {
glVertex2f(lastOuterPoint.x, lastOuterPoint.y);
glEnd();
}
+
+ // draw our tick marks
+ // for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise
+ // we just draw a line...
+ if (getHasTickMarks()) {
+ glBegin(GL_LINES);
+
+ // draw our major tick marks
+ if (getMajorTickMarksAngle() > 0.0f && getMajorTickMarksLength() != 0.0f) {
+
+ xColor color = getMajorTickMarksColor();
+ glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
+
+ float angle = startAt;
+ float angleInRadians = glm::radians(angle);
+ float tickMarkLength = getMajorTickMarksLength();
+ float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius;
+ float endRadius = startRadius + tickMarkLength;
+
+ while (angle <= endAt) {
+ angleInRadians = glm::radians(angle);
+
+ glm::vec2 thisPointA(cos(angleInRadians) * startRadius, sin(angleInRadians) * startRadius);
+ glm::vec2 thisPointB(cos(angleInRadians) * endRadius, sin(angleInRadians) * endRadius);
+
+ glVertex2f(thisPointA.x, thisPointA.y);
+ glVertex2f(thisPointB.x, thisPointB.y);
+
+ angle += getMajorTickMarksAngle();
+ }
+ }
+
+ // draw our minor tick marks
+ if (getMinorTickMarksAngle() > 0.0f && getMinorTickMarksLength() != 0.0f) {
+
+ xColor color = getMinorTickMarksColor();
+ glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
+
+ float angle = startAt;
+ float angleInRadians = glm::radians(angle);
+ float tickMarkLength = getMinorTickMarksLength();
+ float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius;
+ float endRadius = startRadius + tickMarkLength;
+
+ while (angle <= endAt) {
+ angleInRadians = glm::radians(angle);
+
+ glm::vec2 thisPointA(cos(angleInRadians) * startRadius, sin(angleInRadians) * startRadius);
+ glm::vec2 thisPointB(cos(angleInRadians) * endRadius, sin(angleInRadians) * endRadius);
+
+ glVertex2f(thisPointA.x, thisPointA.y);
+ glVertex2f(thisPointB.x, thisPointB.y);
+
+ angle += getMinorTickMarksAngle();
+ }
+ }
+
+ glEnd();
+ }
+
glPopMatrix();
glPopMatrix();
@@ -173,6 +240,55 @@ void Circle3DOverlay::setProperties(const QScriptValue &properties) {
if (innerRadius.isValid()) {
setInnerRadius(innerRadius.toVariant().toFloat());
}
+
+ QScriptValue hasTickMarks = properties.property("hasTickMarks");
+ if (hasTickMarks.isValid()) {
+ setHasTickMarks(hasTickMarks.toVariant().toBool());
+ }
+
+ QScriptValue majorTickMarksAngle = properties.property("majorTickMarksAngle");
+ if (majorTickMarksAngle.isValid()) {
+ setMajorTickMarksAngle(majorTickMarksAngle.toVariant().toFloat());
+ }
+
+ QScriptValue minorTickMarksAngle = properties.property("minorTickMarksAngle");
+ if (minorTickMarksAngle.isValid()) {
+ setMinorTickMarksAngle(minorTickMarksAngle.toVariant().toFloat());
+ }
+
+ QScriptValue majorTickMarksLength = properties.property("majorTickMarksLength");
+ if (majorTickMarksLength.isValid()) {
+ setMajorTickMarksLength(majorTickMarksLength.toVariant().toFloat());
+ }
+
+ QScriptValue minorTickMarksLength = properties.property("minorTickMarksLength");
+ if (minorTickMarksLength.isValid()) {
+ setMinorTickMarksLength(minorTickMarksLength.toVariant().toFloat());
+ }
+
+ QScriptValue majorTickMarksColor = properties.property("majorTickMarksColor");
+ if (majorTickMarksColor.isValid()) {
+ QScriptValue red = majorTickMarksColor.property("red");
+ QScriptValue green = majorTickMarksColor.property("green");
+ QScriptValue blue = majorTickMarksColor.property("blue");
+ if (red.isValid() && green.isValid() && blue.isValid()) {
+ _majorTickMarksColor.red = red.toVariant().toInt();
+ _majorTickMarksColor.green = green.toVariant().toInt();
+ _majorTickMarksColor.blue = blue.toVariant().toInt();
+ }
+ }
+
+ QScriptValue minorTickMarksColor = properties.property("minorTickMarksColor");
+ if (minorTickMarksColor.isValid()) {
+ QScriptValue red = minorTickMarksColor.property("red");
+ QScriptValue green = minorTickMarksColor.property("green");
+ QScriptValue blue = minorTickMarksColor.property("blue");
+ if (red.isValid() && green.isValid() && blue.isValid()) {
+ _minorTickMarksColor.red = red.toVariant().toInt();
+ _minorTickMarksColor.green = green.toVariant().toInt();
+ _minorTickMarksColor.blue = blue.toVariant().toInt();
+ }
+ }
}
diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h
index cea9db0384..791d951105 100644
--- a/interface/src/ui/overlays/Circle3DOverlay.h
+++ b/interface/src/ui/overlays/Circle3DOverlay.h
@@ -26,17 +26,38 @@ public:
float getEndAt() const { return _endAt; }
float getOuterRadius() const { return _outerRadius; }
float getInnerRadius() const { return _innerRadius; }
+ bool getHasTickMarks() const { return _hasTickMarks; }
+ float getMajorTickMarksAngle() const { return _majorTickMarksAngle; }
+ float getMinorTickMarksAngle() const { return _minorTickMarksAngle; }
+ float getMajorTickMarksLength() const { return _majorTickMarksLength; }
+ float getMinorTickMarksLength() const { return _minorTickMarksLength; }
+ xColor getMajorTickMarksColor() const { return _majorTickMarksColor; }
+ xColor getMinorTickMarksColor() const { return _minorTickMarksColor; }
void setStartAt(float value) { _startAt = value; }
void setEndAt(float value) { _endAt = value; }
void setOuterRadius(float value) { _outerRadius = value; }
void setInnerRadius(float value) { _innerRadius = value; }
+ void setHasTickMarks(bool value) { _hasTickMarks = value; }
+ void setMajorTickMarksAngle(float value) { _majorTickMarksAngle = value; }
+ void setMinorTickMarksAngle(float value) { _minorTickMarksAngle = value; }
+ void setMajorTickMarksLength(float value) { _majorTickMarksLength = value; }
+ void setMinorTickMarksLength(float value) { _minorTickMarksLength = value; }
+ void setMajorTickMarksColor(const xColor& value) { _majorTickMarksColor = value; }
+ void setMinorTickMarksColor(const xColor& value) { _minorTickMarksColor = value; }
protected:
float _startAt;
float _endAt;
float _outerRadius;
float _innerRadius;
+ bool _hasTickMarks;
+ float _majorTickMarksAngle;
+ float _minorTickMarksAngle;
+ float _majorTickMarksLength;
+ float _minorTickMarksLength;
+ xColor _majorTickMarksColor;
+ xColor _minorTickMarksColor;
};
diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp
index 8322b9bea4..2b4e1e2f56 100644
--- a/interface/src/ui/overlays/ImageOverlay.cpp
+++ b/interface/src/ui/overlays/ImageOverlay.cpp
@@ -37,7 +37,7 @@ ImageOverlay::~ImageOverlay() {
// TODO: handle setting image multiple times, how do we manage releasing the bound texture?
void ImageOverlay::setImageURL(const QUrl& url) {
_isLoaded = false;
- NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
connect(reply, &QNetworkReply::finished, this, &ImageOverlay::replyFinished);
}
diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp
index 6fa002a664..2266385425 100644
--- a/libraries/audio/src/Sound.cpp
+++ b/libraries/audio/src/Sound.cpp
@@ -78,7 +78,7 @@ Sound::Sound(const QUrl& sampleURL, bool isStereo, QObject* parent) :
// assume we have a QApplication or QCoreApplication instance and use the
// QNetworkAccess manager to grab the raw audio file at the given URL
- NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
qDebug() << "Requesting audio file" << sampleURL.toDisplayString();
diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index ef7083e3bf..62cde44909 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -1048,7 +1048,7 @@ void AvatarData::setBillboardFromURL(const QString &billboardURL) {
QNetworkRequest billboardRequest;
billboardRequest.setUrl(QUrl(billboardURL));
- NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* networkReply = networkAccessManager.get(billboardRequest);
connect(networkReply, SIGNAL(finished()), this, SLOT(setBillboardFromNetworkReply()));
}
@@ -1113,7 +1113,7 @@ void AvatarData::updateJointMappings() {
_jointNames.clear();
if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) {
- NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* networkReply = networkAccessManager.get(QNetworkRequest(_skeletonModelURL));
connect(networkReply, SIGNAL(finished()), this, SLOT(setJointMappingsFromNetworkReply()));
}
diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp
index 7465eb1aac..0d089a2bd2 100644
--- a/libraries/avatars/src/Recording.cpp
+++ b/libraries/avatars/src/Recording.cpp
@@ -385,7 +385,7 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString
if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "ftp") {
// Download file if necessary
qDebug() << "Downloading recording at" << url;
- NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp
index 7d924d02de..bb471442ea 100644
--- a/libraries/networking/src/AccountManager.cpp
+++ b/libraries/networking/src/AccountManager.cpp
@@ -187,7 +187,7 @@ void AccountManager::invokedRequest(const QString& path,
const JSONCallbackParameters& callbackParams,
const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) {
- NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
@@ -359,7 +359,7 @@ void AccountManager::setAccessTokenForCurrentAuthURL(const QString& accessToken)
void AccountManager::requestAccessToken(const QString& login, const QString& password) {
- NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest request;
@@ -431,7 +431,7 @@ void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error)
}
void AccountManager::requestProfile() {
- NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QUrl profileURL = _authURL;
profileURL.setPath("/api/v1/users/profile");
diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp
index 5714e6923d..760c9f4c04 100644
--- a/libraries/networking/src/DomainHandler.cpp
+++ b/libraries/networking/src/DomainHandler.cpp
@@ -90,7 +90,7 @@ void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hos
void DomainHandler::setUUID(const QUuid& uuid) {
if (uuid != _uuid) {
_uuid = uuid;
- qDebug() << "Domain uuid changed to" << uuidStringWithoutCurlyBraces(_uuid);
+ qDebug() << "Domain ID changed to" << uuidStringWithoutCurlyBraces(_uuid);
}
}
diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp
index d30f7944d7..97e9721356 100644
--- a/libraries/networking/src/HifiSockAddr.cpp
+++ b/libraries/networking/src/HifiSockAddr.cpp
@@ -90,32 +90,29 @@ QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr) {
return dataStream;
}
-quint32 getHostOrderLocalAddress() {
+QHostAddress getLocalAddress() {
- static int localAddress = 0;
+ QHostAddress localAddress;
- if (localAddress == 0) {
- foreach(const QNetworkInterface &networkInterface, QNetworkInterface::allInterfaces()) {
- if (networkInterface.flags() & QNetworkInterface::IsUp
- && networkInterface.flags() & QNetworkInterface::IsRunning
- && networkInterface.flags() & ~QNetworkInterface::IsLoopBack) {
- // we've decided that this is the active NIC
- // enumerate it's addresses to grab the IPv4 address
- foreach(const QNetworkAddressEntry &entry, networkInterface.addressEntries()) {
- // make sure it's an IPv4 address that isn't the loopback
- if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol && !entry.ip().isLoopback()) {
- qDebug("Node's local address is %s", entry.ip().toString().toLocal8Bit().constData());
-
- // set our localAddress and break out
- localAddress = entry.ip().toIPv4Address();
- break;
- }
+ foreach(const QNetworkInterface &networkInterface, QNetworkInterface::allInterfaces()) {
+ if (networkInterface.flags() & QNetworkInterface::IsUp
+ && networkInterface.flags() & QNetworkInterface::IsRunning
+ && networkInterface.flags() & ~QNetworkInterface::IsLoopBack) {
+ // we've decided that this is the active NIC
+ // enumerate it's addresses to grab the IPv4 address
+ foreach(const QNetworkAddressEntry &entry, networkInterface.addressEntries()) {
+ // make sure it's an IPv4 address that isn't the loopback
+ if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol && !entry.ip().isLoopback()) {
+
+ // set our localAddress and break out
+ localAddress = entry.ip();
+ break;
}
}
-
- if (localAddress != 0) {
- break;
- }
+ }
+
+ if (!localAddress.isNull()) {
+ break;
}
}
diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h
index 8a591a60b8..42f815390a 100644
--- a/libraries/networking/src/HifiSockAddr.h
+++ b/libraries/networking/src/HifiSockAddr.h
@@ -58,7 +58,7 @@ private:
uint qHash(const HifiSockAddr& key, uint seed);
-quint32 getHostOrderLocalAddress();
+QHostAddress getLocalAddress();
Q_DECLARE_METATYPE(HifiSockAddr)
diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp
index 507788009a..dd33c96d74 100644
--- a/libraries/networking/src/LimitedNodeList.cpp
+++ b/libraries/networking/src/LimitedNodeList.cpp
@@ -70,6 +70,7 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
_nodeHashMutex(QMutex::Recursive),
_nodeSocket(this),
_dtlsSocket(NULL),
+ _localSockAddr(),
_publicSockAddr(),
_numCollectedPackets(0),
_numCollectedBytes(0),
@@ -89,6 +90,15 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
const int LARGER_BUFFER_SIZE = 1048576;
changeSocketBufferSizes(LARGER_BUFFER_SIZE);
+ // check for local socket updates every so often
+ const int LOCAL_SOCKET_UPDATE_INTERVAL_MSECS = 5 * 1000;
+ QTimer* localSocketUpdate = new QTimer(this);
+ connect(localSocketUpdate, &QTimer::timeout, this, &LimitedNodeList::updateLocalSockAddr);
+ localSocketUpdate->start(LOCAL_SOCKET_UPDATE_INTERVAL_MSECS);
+
+ // check the local socket right now
+ updateLocalSockAddr();
+
_packetStatTimer.start();
}
@@ -570,12 +580,19 @@ void LimitedNodeList::sendSTUNRequest() {
memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES);
// lookup the IP for the STUN server
- static HifiSockAddr stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT);
+ HifiSockAddr stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT);
_nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket),
stunSockAddr.getAddress(), stunSockAddr.getPort());
}
+void LimitedNodeList::rebindNodeSocket() {
+ quint16 oldPort = _nodeSocket.localPort();
+
+ _nodeSocket.close();
+ _nodeSocket.bind(QHostAddress::AnyIPv4, oldPort);
+}
+
bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) {
// check the cookie to make sure this is actually a STUN response
// and read the first attribute and make sure it is a XOR_MAPPED_ADDRESS
@@ -652,6 +669,23 @@ bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) {
return false;
}
+void LimitedNodeList::updateLocalSockAddr() {
+ HifiSockAddr newSockAddr(getLocalAddress(), _nodeSocket.localPort());
+ if (newSockAddr != _localSockAddr) {
+
+ if (_localSockAddr.isNull()) {
+ qDebug() << "Local socket is" << newSockAddr;
+ } else {
+ qDebug() << "Local socket has changed from" << _localSockAddr << "to" << newSockAddr;
+ }
+
+
+ _localSockAddr = newSockAddr;
+
+ emit localSockAddrChanged(_localSockAddr);
+ }
+}
+
void LimitedNodeList::sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr,
QUuid headerID, const QUuid& connectionRequestID) {
@@ -662,7 +696,7 @@ void LimitedNodeList::sendHeartbeatToIceServer(const HifiSockAddr& iceServerSock
QByteArray iceRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeIceServerHeartbeat, headerID);
QDataStream iceDataStream(&iceRequestByteArray, QIODevice::Append);
- iceDataStream << _publicSockAddr << HifiSockAddr(QHostAddress(getHostOrderLocalAddress()), _nodeSocket.localPort());
+ iceDataStream << _publicSockAddr << _localSockAddr;
if (!connectionRequestID.isNull()) {
iceDataStream << connectionRequestID;
diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h
index a7ffc7ec28..c416773201 100644
--- a/libraries/networking/src/LimitedNodeList.h
+++ b/libraries/networking/src/LimitedNodeList.h
@@ -69,6 +69,8 @@ public:
const QUuid& getSessionUUID() const { return _sessionUUID; }
void setSessionUUID(const QUuid& sessionUUID);
+
+ void rebindNodeSocket();
QUdpSocket& getNodeSocket() { return _nodeSocket; }
QUdpSocket& getDTLSSocket();
@@ -127,11 +129,15 @@ public slots:
void removeSilentNodes();
+ void updateLocalSockAddr();
+
void killNodeWithUUID(const QUuid& nodeUUID);
signals:
void uuidChanged(const QUuid& ownerUUID, const QUuid& oldUUID);
void nodeAdded(SharedNodePointer);
void nodeKilled(SharedNodePointer);
+
+ void localSockAddrChanged(const HifiSockAddr& localSockAddr);
void publicSockAddrChanged(const HifiSockAddr& publicSockAddr);
protected:
static std::auto_ptr