merging with current upstrem

This commit is contained in:
samcake 2015-10-14 09:41:11 -07:00
commit 3d5bf9aa1a
48 changed files with 1250 additions and 519 deletions

View file

@ -176,7 +176,7 @@
"type": "checkbox", "type": "checkbox",
"label": "Enabled", "label": "Enabled",
"help": "Assigns an asset-server in your domain to serve files to clients via the ATP protocol (over UDP)", "help": "Assigns an asset-server in your domain to serve files to clients via the ATP protocol (over UDP)",
"default": false, "default": true,
"advanced": true "advanced": true
} }
] ]

View file

@ -480,7 +480,7 @@ function MyController(hand, triggerAction) {
var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset);
this.actionID = NULL_ACTION_ID; this.actionID = NULL_ACTION_ID;
this.actionID = Entities.addAction("kinematic-hold", this.grabbedEntity, { this.actionID = Entities.addAction("hold", this.grabbedEntity, {
hand: this.hand === RIGHT_HAND ? "right" : "left", hand: this.hand === RIGHT_HAND ? "right" : "left",
timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
relativePosition: offsetPosition, relativePosition: offsetPosition,

File diff suppressed because it is too large Load diff

View file

@ -12,6 +12,20 @@
// Assumes you only have the default keyboard connected // Assumes you only have the default keyboard connected
var hydra = Controller.Hardware.Hydra2;
if (hydra !== undefined) {
print("-----------------------------------");
var mapping = NewControllers.newMapping("Default");
var standard = Controller.Standard;
print("standard:" + standard);
mapping.from(hydra.LeftButton1).to(standard.A);
mapping.from(hydra.LeftButton2).to(standard.B);
mapping.from(hydra.LeftButton3).to(standard.X);
NewControllers.enableMapping("Default");
print("-----------------------------------");
}
Object.keys(Controller.Standard).forEach(function (input) { Object.keys(Controller.Standard).forEach(function (input) {
print("Controller.Standard." + input + ":" + Controller.Standard[input]); print("Controller.Standard." + input + ":" + Controller.Standard[input]);
}); });

View file

@ -118,8 +118,16 @@ CameraManager = function() {
that.targetYaw = 0; that.targetYaw = 0;
that.targetPitch = 0; that.targetPitch = 0;
that.focalPoint = { x: 0, y: 0, z: 0 }; that.focalPoint = {
that.targetFocalPoint = { x: 0, y: 0, z: 0 }; x: 0,
y: 0,
z: 0
};
that.targetFocalPoint = {
x: 0,
y: 0,
z: 0
};
easing = false; easing = false;
easingTime = 0; easingTime = 0;
@ -127,13 +135,18 @@ CameraManager = function() {
that.previousCameraMode = null; that.previousCameraMode = null;
that.lastMousePosition = { x: 0, y: 0 }; that.lastMousePosition = {
x: 0,
y: 0
};
that.enable = function() { that.enable = function() {
if (Camera.mode == "independent" || that.enabled) return; if (Camera.mode == "independent" || that.enabled) return;
for (var i = 0; i < CAPTURED_KEYS.length; i++) { for (var i = 0; i < CAPTURED_KEYS.length; i++) {
Controller.captureKeyEvents({ text: CAPTURED_KEYS[i] }); Controller.captureKeyEvents({
text: CAPTURED_KEYS[i]
});
} }
that.enabled = true; that.enabled = true;
@ -143,7 +156,7 @@ CameraManager = function() {
that.zoomDistance = INITIAL_ZOOM_DISTANCE; that.zoomDistance = INITIAL_ZOOM_DISTANCE;
that.targetZoomDistance = that.zoomDistance + 3.0; that.targetZoomDistance = that.zoomDistance + 3.0;
var focalPoint = Vec3.sum(Camera.getPosition(), var focalPoint = Vec3.sum(Camera.getPosition(),
Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation()))); Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation())));
// Determine the correct yaw and pitch to keep the camera in the same location // Determine the correct yaw and pitch to keep the camera in the same location
var dPos = Vec3.subtract(focalPoint, Camera.getPosition()); var dPos = Vec3.subtract(focalPoint, Camera.getPosition());
@ -169,7 +182,9 @@ CameraManager = function() {
if (!that.enabled) return; if (!that.enabled) return;
for (var i = 0; i < CAPTURED_KEYS.length; i++) { for (var i = 0; i < CAPTURED_KEYS.length; i++) {
Controller.releaseKeyEvents({ text: CAPTURED_KEYS[i] }); Controller.releaseKeyEvents({
text: CAPTURED_KEYS[i]
});
} }
that.enabled = false; that.enabled = false;
@ -335,19 +350,27 @@ CameraManager = function() {
var hasDragged = false; var hasDragged = false;
that.mousePressEvent = function(event) { that.mousePressEvent = function(event) {
if (cameraTool.mousePressEvent(event)) { if (cameraTool.mousePressEvent(event)) {
return true; return true;
} }
if (!that.enabled) return; if (!that.enabled) return;
if (event.isRightButton || (event.isLeftButton && event.isControl && !event.isShifted)) { if (event.isRightButton || (event.isLeftButton && event.isControl && !event.isShifted)) {
that.mode = MODE_ORBIT; that.mode = MODE_ORBIT;
} else if (event.isMiddleButton || (event.isLeftButton && event.isControl && event.isShifted)) { } else if (event.isMiddleButton || (event.isLeftButton && event.isControl && event.isShifted)) {
that.mode = MODE_PAN; that.mode = MODE_PAN;
} }
if (that.mode != MODE_INACTIVE) { if (that.mode !== MODE_INACTIVE) {
hasDragged = false; hasDragged = false;
return true; return true;
@ -357,10 +380,12 @@ CameraManager = function() {
} }
that.mouseReleaseEvent = function(event) { that.mouseReleaseEvent = function(event) {
if (!that.enabled) return; if (!that.enabled) return;
Window.setCursorVisible(true);
that.mode = MODE_INACTIVE; that.mode = MODE_INACTIVE;
Window.setCursorVisible(true);
} }
that.keyPressEvent = function(event) { that.keyPressEvent = function(event) {
@ -396,15 +421,31 @@ CameraManager = function() {
return; return;
} }
var yRot = Quat.angleAxis(that.yaw, { x: 0, y: 1, z: 0 }); var yRot = Quat.angleAxis(that.yaw, {
var xRot = Quat.angleAxis(that.pitch, { x: 1, y: 0, z: 0 }); x: 0,
y: 1,
z: 0
});
var xRot = Quat.angleAxis(that.pitch, {
x: 1,
y: 0,
z: 0
});
var q = Quat.multiply(yRot, xRot); var q = Quat.multiply(yRot, xRot);
var pos = Vec3.multiply(Quat.getFront(q), that.zoomDistance); var pos = Vec3.multiply(Quat.getFront(q), that.zoomDistance);
Camera.setPosition(Vec3.sum(that.focalPoint, pos)); Camera.setPosition(Vec3.sum(that.focalPoint, pos));
yRot = Quat.angleAxis(that.yaw - 180, { x: 0, y: 1, z: 0 }); yRot = Quat.angleAxis(that.yaw - 180, {
xRot = Quat.angleAxis(-that.pitch, { x: 1, y: 0, z: 0 }); x: 0,
y: 1,
z: 0
});
xRot = Quat.angleAxis(-that.pitch, {
x: 1,
y: 0,
z: 0
});
q = Quat.multiply(yRot, xRot); q = Quat.multiply(yRot, xRot);
if (easing) { if (easing) {
@ -483,7 +524,7 @@ CameraManager = function() {
} }
}); });
Controller.keyReleaseEvent.connect(function (event) { Controller.keyReleaseEvent.connect(function(event) {
if (event.text == "ESC" && that.enabled) { if (event.text == "ESC" && that.enabled) {
Camera.mode = lastAvatarCameraMode; Camera.mode = lastAvatarCameraMode;
cameraManager.disable(true); cameraManager.disable(true);
@ -503,9 +544,21 @@ CameraManager = function() {
CameraTool = function(cameraManager) { CameraTool = function(cameraManager) {
var that = {}; var that = {};
var RED = { red: 191, green: 78, blue: 38 }; var RED = {
var GREEN = { red: 26, green: 193, blue: 105 }; red: 191,
var BLUE = { red: 0, green: 131, blue: 204 }; green: 78,
blue: 38
};
var GREEN = {
red: 26,
green: 193,
blue: 105
};
var BLUE = {
red: 0,
green: 131,
blue: 204
};
var BORDER_WIDTH = 1; var BORDER_WIDTH = 1;
@ -513,10 +566,10 @@ CameraTool = function(cameraManager) {
var ORIENTATION_OVERLAY_HALF_SIZE = ORIENTATION_OVERLAY_SIZE / 2; var ORIENTATION_OVERLAY_HALF_SIZE = ORIENTATION_OVERLAY_SIZE / 2;
var ORIENTATION_OVERLAY_CUBE_SIZE = 10.5, var ORIENTATION_OVERLAY_CUBE_SIZE = 10.5,
var ORIENTATION_OVERLAY_OFFSET = { var ORIENTATION_OVERLAY_OFFSET = {
x: 30, x: 30,
y: 30, y: 30,
} }
var UI_WIDTH = 70; var UI_WIDTH = 70;
var UI_HEIGHT = 70; var UI_HEIGHT = 70;
@ -536,7 +589,11 @@ CameraTool = function(cameraManager) {
height: UI_HEIGHT + BORDER_WIDTH * 2, height: UI_HEIGHT + BORDER_WIDTH * 2,
alpha: 0, alpha: 0,
text: "", text: "",
backgroundColor: { red: 101, green: 101, blue: 101 }, backgroundColor: {
red: 101,
green: 101,
blue: 101
},
backgroundAlpha: 1.0, backgroundAlpha: 1.0,
visible: false, visible: false,
}); });
@ -548,7 +605,11 @@ CameraTool = function(cameraManager) {
height: UI_HEIGHT, height: UI_HEIGHT,
alpha: 0, alpha: 0,
text: "", text: "",
backgroundColor: { red: 51, green: 51, blue: 51 }, backgroundColor: {
red: 51,
green: 51,
blue: 51
},
backgroundAlpha: 1.0, backgroundAlpha: 1.0,
visible: false, visible: false,
}); });
@ -556,7 +617,11 @@ CameraTool = function(cameraManager) {
var defaultCubeProps = { var defaultCubeProps = {
size: ORIENTATION_OVERLAY_CUBE_SIZE, size: ORIENTATION_OVERLAY_CUBE_SIZE,
alpha: 1, alpha: 1,
color: { red: 255, green: 0, blue: 0 }, color: {
red: 255,
green: 0,
blue: 0
},
solid: true, solid: true,
visible: true, visible: true,
drawOnHUD: true, drawOnHUD: true,
@ -564,10 +629,26 @@ CameraTool = function(cameraManager) {
var defaultLineProps = { var defaultLineProps = {
lineWidth: 1.5, lineWidth: 1.5,
alpha: 1, alpha: 1,
position: { x: 0, y: 0, z: 0 }, position: {
start: { x: 0, y: 0, z: 0 }, x: 0,
end: { x: 0, y: 0, z: 0 }, y: 0,
color: { red: 255, green: 0, blue: 0 }, z: 0
},
start: {
x: 0,
y: 0,
z: 0
},
end: {
x: 0,
y: 0,
z: 0
},
color: {
red: 255,
green: 0,
blue: 0
},
visible: false, visible: false,
drawOnHUD: true, drawOnHUD: true,
}; };
@ -582,30 +663,66 @@ CameraTool = function(cameraManager) {
var OOHS = ORIENTATION_OVERLAY_HALF_SIZE; var OOHS = ORIENTATION_OVERLAY_HALF_SIZE;
var cubeX = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, { var cubeX = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, {
position: { x: -OOHS, y: OOHS, z: OOHS }, position: {
x: -OOHS,
y: OOHS,
z: OOHS
},
color: RED, color: RED,
})); }));
var cubeY = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, { var cubeY = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, {
position: { x: OOHS, y: -OOHS, z: OOHS }, position: {
x: OOHS,
y: -OOHS,
z: OOHS
},
color: GREEN, color: GREEN,
})); }));
var cubeZ = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, { var cubeZ = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, {
position: { x: OOHS, y: OOHS, z: -OOHS }, position: {
x: OOHS,
y: OOHS,
z: -OOHS
},
color: BLUE, color: BLUE,
})); }));
orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, { orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, {
start: { x: -OOHS, y: OOHS, z: OOHS }, start: {
end: { x: OOHS, y: OOHS, z: OOHS }, x: -OOHS,
y: OOHS,
z: OOHS
},
end: {
x: OOHS,
y: OOHS,
z: OOHS
},
color: RED, color: RED,
})); }));
orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, { orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, {
start: { x: OOHS, y: -OOHS, z: OOHS }, start: {
end: { x: OOHS, y: OOHS, z: OOHS }, x: OOHS,
y: -OOHS,
z: OOHS
},
end: {
x: OOHS,
y: OOHS,
z: OOHS
},
color: GREEN, color: GREEN,
})); }));
orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, { orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, {
start: { x: OOHS, y: OOHS, z: -OOHS }, start: {
end: { x: OOHS, y: OOHS, z: OOHS }, x: OOHS,
y: OOHS,
z: -OOHS
},
end: {
x: OOHS,
y: OOHS,
z: OOHS
},
color: BLUE, color: BLUE,
})); }));
@ -645,7 +762,10 @@ CameraTool = function(cameraManager) {
} }
that.mousePressEvent = function(event) { that.mousePressEvent = function(event) {
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); var clickedOverlay = Overlays.getOverlayAtPoint({
x: event.x,
y: event.y
});
if (clickedOverlay == cubeX) { if (clickedOverlay == cubeX) {
targetPitch = 0; targetPitch = 0;
@ -666,12 +786,18 @@ CameraTool = function(cameraManager) {
}; };
that.setVisible = function(visible) { that.setVisible = function(visible) {
orientationOverlay.setProperties({ visible: visible }); orientationOverlay.setProperties({
Overlays.editOverlay(background, { visible: visible }); visible: visible
Overlays.editOverlay(backgroundBorder, { visible: visible }); });
Overlays.editOverlay(background, {
visible: visible
});
Overlays.editOverlay(backgroundBorder, {
visible: visible
});
}; };
that.setVisible(false); that.setVisible(false);
return that; return that;
}; };

View file

@ -16,24 +16,38 @@
{ {
"jointName": "RightHand", "jointName": "RightHand",
"positionVar": "rightHandPosition", "positionVar": "rightHandPosition",
"rotationVar": "rightHandRotation" "rotationVar": "rightHandRotation",
"typeVar": "rightHandType"
}, },
{ {
"jointName": "LeftHand", "jointName": "LeftHand",
"positionVar": "leftHandPosition", "positionVar": "leftHandPosition",
"rotationVar": "leftHandRotation" "rotationVar": "leftHandRotation",
"typeVar": "leftHandType"
},
{
"jointName": "RightFoot",
"positionVar": "rightFootPosition",
"rotationVar": "rightFootRotation",
"typeVar": "rightFootType"
},
{
"jointName": "LeftFoot",
"positionVar": "leftFootPosition",
"rotationVar": "leftFootRotation",
"typeVar": "leftFootType"
}, },
{ {
"jointName": "Neck", "jointName": "Neck",
"positionVar": "neckPosition", "positionVar": "neckPosition",
"rotationVar": "neckRotation", "rotationVar": "neckRotation",
"typeVar": "headAndNeckType" "typeVar": "neckType"
}, },
{ {
"jointName": "Head", "jointName": "Head",
"positionVar": "headPosition", "positionVar": "headPosition",
"rotationVar": "headRotation", "rotationVar": "headRotation",
"typeVar": "headAndNeckType" "typeVar": "headType"
} }
] ]
}, },
@ -51,7 +65,7 @@
"id": "spineLean", "id": "spineLean",
"type": "manipulator", "type": "manipulator",
"data": { "data": {
"alpha": 1.0, "alpha": 0.0,
"joints": [ "joints": [
{ "var": "lean", "jointName": "Spine" } { "var": "lean", "jointName": "Spine" }
] ]

View file

@ -126,6 +126,7 @@
#include "Stars.h" #include "Stars.h"
#include "ui/AddressBarDialog.h" #include "ui/AddressBarDialog.h"
#include "ui/AvatarInputs.h" #include "ui/AvatarInputs.h"
#include "ui/AssetUploadDialogFactory.h"
#include "ui/DataWebDialog.h" #include "ui/DataWebDialog.h"
#include "ui/DialogsManager.h" #include "ui/DialogsManager.h"
#include "ui/LoginDialog.h" #include "ui/LoginDialog.h"
@ -159,6 +160,7 @@ static const QString SVO_JSON_EXTENSION = ".svo.json";
static const QString JS_EXTENSION = ".js"; static const QString JS_EXTENSION = ".js";
static const QString FST_EXTENSION = ".fst"; static const QString FST_EXTENSION = ".fst";
static const QString FBX_EXTENSION = ".fbx"; static const QString FBX_EXTENSION = ".fbx";
static const QString OBJ_EXTENSION = ".obj";
static const int MIRROR_VIEW_TOP_PADDING = 5; static const int MIRROR_VIEW_TOP_PADDING = 5;
static const int MIRROR_VIEW_LEFT_PADDING = 10; static const int MIRROR_VIEW_LEFT_PADDING = 10;
@ -179,9 +181,6 @@ static const unsigned int THROTTLED_SIM_FRAMERATE = 15;
static const int TARGET_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / TARGET_SIM_FRAMERATE; static const int TARGET_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / TARGET_SIM_FRAMERATE;
static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE;
const QString CHECK_VERSION_URL = "https://highfidelity.com/latestVersion.xml";
const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion";
#ifndef __APPLE__ #ifndef __APPLE__
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
#else #else
@ -197,8 +196,7 @@ const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensi
{ SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_EXTENSION, &Application::importSVOFromURL },
{ SVO_JSON_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL },
{ JS_EXTENSION, &Application::askToLoadScript }, { JS_EXTENSION, &Application::askToLoadScript },
{ FST_EXTENSION, &Application::askToSetAvatarUrl }, { FST_EXTENSION, &Application::askToSetAvatarUrl }
{ FBX_EXTENSION, &Application::askToUploadAsset }
}; };
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@ -1892,16 +1890,6 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
computePickRay(mappedEvent.x(), mappedEvent.y())); computePickRay(mappedEvent.x(), mappedEvent.y()));
sendEvent(this, &actionEvent); sendEvent(this, &actionEvent);
} else if (event->button() == Qt::RightButton) {
// "right click" on controllers to toggle the overlay
if (deviceID > 0) {
_overlayConductor.setEnabled(!_overlayConductor.getEnabled());
}
} else if (event->button() == Qt::MiddleButton) {
// mouse middle click to toggle the overlay
if (deviceID == 0) {
_overlayConductor.setEnabled(!_overlayConductor.getEnabled());
}
} }
} }
} }
@ -2023,21 +2011,14 @@ void Application::dropEvent(QDropEvent *event) {
const QMimeData* mimeData = event->mimeData(); const QMimeData* mimeData = event->mimeData();
for (auto& url : mimeData->urls()) { for (auto& url : mimeData->urls()) {
QString urlString = url.toString(); QString urlString = url.toString();
if (canAcceptURL(urlString) && acceptURL(urlString)) { if (acceptURL(urlString, true)) {
event->acceptProposedAction(); event->acceptProposedAction();
} }
} }
} }
void Application::dragEnterEvent(QDragEnterEvent* event) { void Application::dragEnterEvent(QDragEnterEvent* event) {
const QMimeData* mimeData = event->mimeData(); event->acceptProposedAction();
for (auto& url : mimeData->urls()) {
auto urlString = url.toString();
if (canAcceptURL(urlString)) {
event->acceptProposedAction();
break;
}
}
} }
bool Application::acceptSnapshot(const QString& urlString) { bool Application::acceptSnapshot(const QString& urlString) {
@ -2770,6 +2751,8 @@ void Application::update(float deltaTime) {
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
updateDialogs(deltaTime); // update various stats dialogs if present updateDialogs(deltaTime); // update various stats dialogs if present
_avatarUpdate->synchronousProcess();
{ {
PerformanceTimer perfTimer("physics"); PerformanceTimer perfTimer("physics");
myAvatar->relayDriveKeysToCharacterController(); myAvatar->relayDriveKeysToCharacterController();
@ -2831,8 +2814,6 @@ void Application::update(float deltaTime) {
_overlays.update(deltaTime); _overlays.update(deltaTime);
} }
_avatarUpdate->synchronousProcess();
// Update _viewFrustum with latest camera and view frustum data... // Update _viewFrustum with latest camera and view frustum data...
// NOTE: we get this from the view frustum, to make it simpler, since the // NOTE: we get this from the view frustum, to make it simpler, since the
// loadViewFrumstum() method will get the correct details from the camera // loadViewFrumstum() method will get the correct details from the camera
@ -3973,26 +3954,26 @@ bool Application::canAcceptURL(const QString& urlString) {
return false; return false;
} }
bool Application::acceptURL(const QString& urlString) { bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
if (urlString.startsWith(HIFI_URL_SCHEME)) { if (urlString.startsWith(HIFI_URL_SCHEME)) {
// this is a hifi URL - have the AddressManager handle it // this is a hifi URL - have the AddressManager handle it
QMetaObject::invokeMethod(DependencyManager::get<AddressManager>().data(), "handleLookupString", QMetaObject::invokeMethod(DependencyManager::get<AddressManager>().data(), "handleLookupString",
Qt::AutoConnection, Q_ARG(const QString&, urlString)); Qt::AutoConnection, Q_ARG(const QString&, urlString));
return true; return true;
} else { }
QUrl url(urlString);
QHashIterator<QString, AcceptURLMethod> i(_acceptedExtensions); QUrl url(urlString);
QString lowerPath = url.path().toLower(); QHashIterator<QString, AcceptURLMethod> i(_acceptedExtensions);
while (i.hasNext()) { QString lowerPath = url.path().toLower();
i.next(); while (i.hasNext()) {
if (lowerPath.endsWith(i.key())) { i.next();
AcceptURLMethod method = i.value(); if (lowerPath.endsWith(i.key())) {
(this->*method)(urlString); AcceptURLMethod method = i.value();
return true; return (this->*method)(urlString);
}
} }
} }
return false;
return defaultUpload && askToUploadAsset(urlString);
} }
void Application::setSessionUUID(const QUuid& sessionUUID) { void Application::setSessionUUID(const QUuid& sessionUUID) {
@ -4076,8 +4057,36 @@ bool Application::askToUploadAsset(const QString& filename) {
QUrl url { filename }; QUrl url { filename };
if (auto upload = DependencyManager::get<AssetClient>()->createUpload(url.toLocalFile())) { if (auto upload = DependencyManager::get<AssetClient>()->createUpload(url.toLocalFile())) {
QMessageBox messageBox;
messageBox.setWindowTitle("Asset upload");
messageBox.setText("You are about to upload the following file to the asset server:\n" +
url.toDisplayString());
messageBox.setInformativeText("Do you want to continue?");
messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
messageBox.setDefaultButton(QMessageBox::Ok);
// Option to drop model in world for models
if (filename.endsWith(FBX_EXTENSION) || filename.endsWith(OBJ_EXTENSION)) {
auto checkBox = new QCheckBox(&messageBox);
checkBox->setText("Add to scene");
messageBox.setCheckBox(checkBox);
}
if (messageBox.exec() != QMessageBox::Ok) {
upload->deleteLater();
return false;
}
// connect to the finished signal so we know when the AssetUpload is done // connect to the finished signal so we know when the AssetUpload is done
QObject::connect(upload, &AssetUpload::finished, this, &Application::assetUploadFinished); if (messageBox.checkBox() && (messageBox.checkBox()->checkState() == Qt::Checked)) {
// Custom behavior for models
QObject::connect(upload, &AssetUpload::finished, this, &Application::modelUploadFinished);
} else {
QObject::connect(upload, &AssetUpload::finished,
&AssetUploadDialogFactory::getInstance(),
&AssetUploadDialogFactory::handleUploadFinished);
}
// start the upload now // start the upload now
upload->start(); upload->start();
@ -4089,47 +4098,26 @@ bool Application::askToUploadAsset(const QString& filename) {
return false; return false;
} }
void Application::assetUploadFinished(AssetUpload* upload, const QString& hash) { void Application::modelUploadFinished(AssetUpload* upload, const QString& hash) {
if (upload->getError() != AssetUpload::NoError) { auto filename = QFileInfo(upload->getFilename()).fileName();
// figure out the right error message for the message box
QString additionalError; if ((upload->getError() == AssetUpload::NoError) &&
(filename.endsWith(FBX_EXTENSION) || filename.endsWith(OBJ_EXTENSION))) {
switch (upload->getError()) { auto entities = DependencyManager::get<EntityScriptingInterface>();
case AssetUpload::PermissionDenied:
additionalError = "You do not have permission to upload content to this asset-server.";
break;
case AssetUpload::TooLarge:
additionalError = "The uploaded content was too large and could not be stored in the asset-server.";
break;
case AssetUpload::FileOpenError:
additionalError = "The file could not be opened. Please check your permissions and try again.";
break;
case AssetUpload::NetworkError:
additionalError = "The file could not be opened. Please check your network connectivity.";
break;
default:
// not handled, do not show a message box
return;
}
// display a message box with the error EntityItemProperties properties;
auto filename = QFileInfo(upload->getFilename()).fileName(); properties.setType(EntityTypes::Model);
QString errorMessage = QString("Failed to upload %1.\n\n%2").arg(filename, additionalError); properties.setModelURL(QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension()));
QMessageBox::warning(_window, "Failed Upload", errorMessage); properties.setPosition(_myCamera.getPosition() + _myCamera.getOrientation() * Vectors::FRONT * 2.0f);
properties.setName(QUrl(upload->getFilename()).fileName());
entities->addEntity(properties);
upload->deleteLater();
} else {
AssetUploadDialogFactory::getInstance().handleUploadFinished(upload, hash);
} }
auto entities = DependencyManager::get<EntityScriptingInterface>();
auto myAvatar = getMyAvatar();
EntityItemProperties properties;
properties.setType(EntityTypes::Model);
properties.setModelURL(QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension()));
properties.setPosition(myAvatar->getPosition() + myAvatar->getOrientation() * Vectors::FRONT * 2.0f);
properties.setName(QUrl(upload->getFilename()).fileName());
entities->addEntity(properties);
upload->deleteLater();
} }
ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded, ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded,

View file

@ -211,7 +211,7 @@ public:
void setScriptsLocation(const QString& scriptsLocation); void setScriptsLocation(const QString& scriptsLocation);
bool canAcceptURL(const QString& url); bool canAcceptURL(const QString& url);
bool acceptURL(const QString& url); bool acceptURL(const QString& url, bool defaultUpload = false);
void setMaxOctreePacketsPerSecond(int maxOctreePPS); void setMaxOctreePacketsPerSecond(int maxOctreePPS);
int getMaxOctreePacketsPerSecond(); int getMaxOctreePacketsPerSecond();
@ -329,7 +329,7 @@ private slots:
bool askToSetAvatarUrl(const QString& url); bool askToSetAvatarUrl(const QString& url);
bool askToLoadScript(const QString& scriptFilenameOrURL); bool askToLoadScript(const QString& scriptFilenameOrURL);
bool askToUploadAsset(const QString& asset); bool askToUploadAsset(const QString& asset);
void assetUploadFinished(AssetUpload* upload, const QString& hash); void modelUploadFinished(AssetUpload* upload, const QString& hash);
void setSessionUUID(const QUuid& sessionUUID); void setSessionUUID(const QUuid& sessionUUID);
void domainChanged(const QString& domainHostname); void domainChanged(const QString& domainHostname);

View file

@ -152,7 +152,7 @@ void MyAvatar::reset() {
setEnableRigAnimations(true); setEnableRigAnimations(true);
// Reset dynamic state. // Reset dynamic state.
_wasPushing = _isPushing = _isBraking = _billboardValid = _goToPending = _straighteningLean = false; _wasPushing = _isPushing = _isBraking = _billboardValid = _straighteningLean = false;
_skeletonModel.reset(); _skeletonModel.reset();
getHead()->reset(); getHead()->reset();
_targetVelocity = glm::vec3(0.0f); _targetVelocity = glm::vec3(0.0f);

View file

@ -14,6 +14,8 @@
#include <HandData.h> #include <HandData.h>
#include <HFBackEvent.h> #include <HFBackEvent.h>
#include <controllers/NewControllerScriptingInterface.h>
#include "Application.h" #include "Application.h"
#include "devices/MotionTracker.h" #include "devices/MotionTracker.h"
#include "ControllerScriptingInterface.h" #include "ControllerScriptingInterface.h"
@ -31,6 +33,11 @@ ControllerScriptingInterface::ControllerScriptingInterface() :
} }
ControllerScriptingInterface::~ControllerScriptingInterface() {
delete _newControllerScriptingInterface;
}
static int actionMetaTypeId = qRegisterMetaType<UserInputMapper::Action>(); static int actionMetaTypeId = qRegisterMetaType<UserInputMapper::Action>();
static int inputChannelMetaTypeId = qRegisterMetaType<UserInputMapper::InputChannel>(); static int inputChannelMetaTypeId = qRegisterMetaType<UserInputMapper::InputChannel>();
static int inputMetaTypeId = qRegisterMetaType<UserInputMapper::Input>(); static int inputMetaTypeId = qRegisterMetaType<UserInputMapper::Input>();
@ -121,6 +128,11 @@ void ControllerScriptingInterface::registerControllerTypes(ScriptEngine* engine)
qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue);
wireUpControllers(engine); wireUpControllers(engine);
// hack in the new controller scripting interface...
_newControllerScriptingInterface = new controller::NewControllerScriptingInterface();
engine->registerGlobalObject("NewControllers", _newControllerScriptingInterface);
} }
void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {

View file

@ -18,6 +18,9 @@
#include <AbstractControllerScriptingInterface.h> #include <AbstractControllerScriptingInterface.h>
class PalmData; class PalmData;
namespace controller {
class NewControllerScriptingInterface;
}
class InputController : public AbstractInputController { class InputController : public AbstractInputController {
Q_OBJECT Q_OBJECT
@ -55,7 +58,8 @@ class ControllerScriptingInterface : public AbstractControllerScriptingInterface
public: public:
ControllerScriptingInterface(); ControllerScriptingInterface();
~ControllerScriptingInterface();
virtual void registerControllerTypes(ScriptEngine* engine); virtual void registerControllerTypes(ScriptEngine* engine);
void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
@ -169,6 +173,7 @@ private:
void wireUpControllers(ScriptEngine* engine); void wireUpControllers(ScriptEngine* engine);
controller::NewControllerScriptingInterface* _newControllerScriptingInterface = nullptr;
}; };
const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip

View file

@ -11,11 +11,6 @@
#include "AssetUploadDialogFactory.h" #include "AssetUploadDialogFactory.h"
#include <AssetClient.h>
#include <AssetUpload.h>
#include <AssetUtils.h>
#include <NodeList.h>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtWidgets/QDialogButtonBox> #include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QFileDialog> #include <QtWidgets/QFileDialog>
@ -24,6 +19,12 @@
#include <QtWidgets/QLineEdit> #include <QtWidgets/QLineEdit>
#include <QtWidgets/QVBoxLayout> #include <QtWidgets/QVBoxLayout>
#include <AssetClient.h>
#include <AssetUpload.h>
#include <AssetUtils.h>
#include <NodeList.h>
#include <ResourceManager.h>
AssetUploadDialogFactory& AssetUploadDialogFactory::getInstance() { AssetUploadDialogFactory& AssetUploadDialogFactory::getInstance() {
static AssetUploadDialogFactory staticInstance; static AssetUploadDialogFactory staticInstance;
return staticInstance; return staticInstance;
@ -85,7 +86,7 @@ void AssetUploadDialogFactory::handleUploadFinished(AssetUpload* upload, const Q
// setup the line edit to hold the copiable text // setup the line edit to hold the copiable text
QLineEdit* lineEdit = new QLineEdit; QLineEdit* lineEdit = new QLineEdit;
QString atpURL = QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension()); QString atpURL = QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension());
// set the ATP URL as the text value so it's copiable // set the ATP URL as the text value so it's copiable
lineEdit->insert(atpURL); lineEdit->insert(atpURL);

View file

@ -30,7 +30,6 @@ public:
public slots: public slots:
void showDialog(); void showDialog();
private slots:
void handleUploadFinished(AssetUpload* upload, const QString& hash); void handleUploadFinished(AssetUpload* upload, const QString& hash);
private: private:

View file

@ -56,9 +56,9 @@ void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) con
} }
void AnimInverseKinematics::setTargetVars( void AnimInverseKinematics::setTargetVars(
const QString& jointName, const QString& jointName,
const QString& positionVar, const QString& positionVar,
const QString& rotationVar, const QString& rotationVar,
const QString& typeVar) { const QString& typeVar) {
// if there are dups, last one wins. // if there are dups, last one wins.
bool found = false; bool found = false;
@ -95,14 +95,20 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
} }
} else { } else {
IKTarget target; IKTarget target;
AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses); target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition));
target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans); if (target.getType() != IKTarget::Type::Unknown) {
target.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot); AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses);
target.setType(animVars.lookup(targetVar.typeVar, QString(""))); glm::quat rotation = animVars.lookup(targetVar.rotationVar, defaultPose.rot);
target.index = targetVar.jointIndex; glm::vec3 translation = animVars.lookup(targetVar.positionVar, defaultPose.trans);
targets.push_back(target); if (target.getType() == IKTarget::Type::HipsRelativeRotationAndPosition) {
if (target.index > _maxTargetIndex) { translation += _hipsOffset;
_maxTargetIndex = target.index; }
target.setPose(rotation, translation);
target.setIndex(targetVar.jointIndex);
targets.push_back(target);
if (targetVar.jointIndex > _maxTargetIndex) {
_maxTargetIndex = targetVar.jointIndex;
}
} }
} }
} }
@ -141,107 +147,116 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
do { do {
int lowestMovedIndex = _relativePoses.size(); int lowestMovedIndex = _relativePoses.size();
for (auto& target: targets) { for (auto& target: targets) {
if (target.type == IKTarget::Type::RotationOnly) { IKTarget::Type targetType = target.getType();
if (targetType == IKTarget::Type::RotationOnly) {
// the final rotation will be enforced after the iterations // the final rotation will be enforced after the iterations
continue; continue;
} }
AnimPose targetPose = target.pose;
// cache tip absolute transform // cache tip absolute transform
int tipIndex = target.index; int tipIndex = target.getIndex();
int pivotIndex = _skeleton->getParentIndex(tipIndex);
if (pivotIndex == -1) {
continue;
}
int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex);
if (pivotsParentIndex == -1) {
// TODO?: handle case where tip's parent is root?
continue;
}
glm::vec3 tipPosition = absolutePoses[tipIndex].trans; glm::vec3 tipPosition = absolutePoses[tipIndex].trans;
glm::quat tipRotation = absolutePoses[tipIndex].rot; glm::quat tipRotation = absolutePoses[tipIndex].rot;
// cache tip's parent's absolute rotation so we can recompute the tip's parent-relative // cache tip's parent's absolute rotation so we can recompute the tip's parent-relative
// as we proceed walking down the joint chain // as we proceed walking down the joint chain
int pivotIndex = _skeleton->getParentIndex(tipIndex); glm::quat tipParentRotation = absolutePoses[pivotIndex].rot;
glm::quat tipParentRotation;
if (pivotIndex != -1) {
tipParentRotation = absolutePoses[pivotIndex].rot;
}
// descend toward root, pivoting each joint to get tip closer to target // descend toward root, pivoting each joint to get tip closer to target
int ancestorCount = 1; while (pivotsParentIndex != -1) {
while (pivotIndex != -1) {
// compute the two lines that should be aligned // compute the two lines that should be aligned
glm::vec3 jointPosition = absolutePoses[pivotIndex].trans; glm::vec3 jointPosition = absolutePoses[pivotIndex].trans;
glm::vec3 leverArm = tipPosition - jointPosition; glm::vec3 leverArm = tipPosition - jointPosition;
glm::vec3 targetLine = targetPose.trans - jointPosition;
// compute the swing that would get get tip closer
glm::vec3 axis = glm::cross(leverArm, targetLine);
float axisLength = glm::length(axis);
glm::quat deltaRotation; glm::quat deltaRotation;
const float MIN_AXIS_LENGTH = 1.0e-4f; if (targetType == IKTarget::Type::RotationAndPosition ||
if (axisLength > MIN_AXIS_LENGTH) { targetType == IKTarget::Type::HipsRelativeRotationAndPosition) {
// compute deltaRotation for alignment (swings tip closer to target) // compute the swing that would get get tip closer
axis /= axisLength; glm::vec3 targetLine = target.getTranslation() - jointPosition;
float angle = acosf(glm::dot(leverArm, targetLine) / (glm::length(leverArm) * glm::length(targetLine))); glm::vec3 axis = glm::cross(leverArm, targetLine);
float axisLength = glm::length(axis);
const float MIN_AXIS_LENGTH = 1.0e-4f;
if (axisLength > MIN_AXIS_LENGTH) {
// compute deltaRotation for alignment (swings tip closer to target)
axis /= axisLength;
float angle = acosf(glm::dot(leverArm, targetLine) / (glm::length(leverArm) * glm::length(targetLine)));
// NOTE: even when axisLength is not zero (e.g. lever-arm and pivot-arm are not quite aligned) it is // NOTE: even when axisLength is not zero (e.g. lever-arm and pivot-arm are not quite aligned) it is
// still possible for the angle to be zero so we also check that to avoid unnecessary calculations. // still possible for the angle to be zero so we also check that to avoid unnecessary calculations.
const float MIN_ADJUSTMENT_ANGLE = 1.0e-4f; const float MIN_ADJUSTMENT_ANGLE = 1.0e-4f;
if (angle > MIN_ADJUSTMENT_ANGLE) { if (angle > MIN_ADJUSTMENT_ANGLE) {
// reduce angle by a fraction (reduces IK swing contribution of this joint) // reduce angle by a fraction (for stability)
angle /= (float)ancestorCount; const float fraction = 0.5f;
deltaRotation = glm::angleAxis(angle, axis); angle *= fraction;
} deltaRotation = glm::angleAxis(angle, axis);
// The swing will re-orient the tip but there will tend to be be a non-zero delta between the tip's // The swing will re-orient the tip but there will tend to be be a non-zero delta between the tip's
// new rotation and its target. We compute that delta here and rotate the tipJoint accordingly. // new rotation and its target. This is the final parent-relative rotation that the tip joint have
glm::quat tipRelativeRotation = glm::inverse(deltaRotation * tipParentRotation) * targetPose.rot; // make to achieve its target rotation.
glm::quat tipRelativeRotation = glm::inverse(deltaRotation * tipParentRotation) * target.getRotation();
// enforce tip's constraint // enforce tip's constraint
RotationConstraint* constraint = getConstraint(tipIndex); RotationConstraint* constraint = getConstraint(tipIndex);
if (constraint) { if (constraint) {
bool constrained = constraint->apply(tipRelativeRotation); bool constrained = constraint->apply(tipRelativeRotation);
if (constrained) { if (constrained) {
// The tip's final parent-relative rotation violates its constraint // The tip's final parent-relative rotation would violate its constraint
// so we try to twist this pivot to compensate. // so we try to pre-twist this pivot to compensate.
glm::quat constrainedTipRotation = deltaRotation * tipParentRotation * tipRelativeRotation; glm::quat constrainedTipRotation = deltaRotation * tipParentRotation * tipRelativeRotation;
glm::quat missingRotation = targetPose.rot * glm::inverse(constrainedTipRotation); glm::quat missingRotation = target.getRotation() * glm::inverse(constrainedTipRotation);
glm::quat swingPart; glm::quat swingPart;
glm::quat twistPart; glm::quat twistPart;
glm::vec3 axis = glm::normalize(deltaRotation * leverArm); glm::vec3 axis = glm::normalize(deltaRotation * leverArm);
swingTwistDecomposition(missingRotation, axis, swingPart, twistPart); swingTwistDecomposition(missingRotation, axis, swingPart, twistPart);
deltaRotation = twistPart * deltaRotation; float dotSign = copysignf(1.0f, twistPart.w);
} deltaRotation = glm::normalize(glm::lerp(glm::quat(), dotSign * twistPart, fraction)) * deltaRotation;
// we update the tip rotation here to rotate it as close to its target orientation as possible }
// before moving on to next pivot }
tipRotation = tipParentRotation * tipRelativeRotation;
}
}
++ancestorCount;
int parentIndex = _skeleton->getParentIndex(pivotIndex);
if (parentIndex == -1) {
// TODO? apply constraints to root?
// TODO? harvest the root's transform as movement of entire skeleton?
} else {
// compute joint's new parent-relative rotation after swing
// Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q
glm::quat newRot = glm::normalize(glm::inverse(
absolutePoses[parentIndex].rot) *
deltaRotation *
absolutePoses[pivotIndex].rot);
// enforce pivot's constraint
RotationConstraint* constraint = getConstraint(pivotIndex);
if (constraint) {
bool constrained = constraint->apply(newRot);
if (constrained) {
// the constraint will modify the movement of the tip so we have to compute the modified
// model-frame deltaRotation
// Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^
deltaRotation = absolutePoses[parentIndex].rot *
newRot *
glm::inverse(absolutePoses[pivotIndex].rot);
} }
} }
} else if (targetType == IKTarget::Type::HmdHead) {
// store the rotation change in the accumulator // An HmdHead target slaves the orientation of the end-effector by distributing rotation
_accumulators[pivotIndex].add(newRot); // deltas up the hierarchy. Its target position is enforced later by shifting the hips.
deltaRotation = target.getRotation() * glm::inverse(tipRotation);
float dotSign = copysignf(1.0f, deltaRotation.w);
const float ANGLE_DISTRIBUTION_FACTOR = 0.15f;
deltaRotation = glm::normalize(glm::lerp(glm::quat(), dotSign * deltaRotation, ANGLE_DISTRIBUTION_FACTOR));
} }
// compute joint's new parent-relative rotation after swing
// Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q
glm::quat newRot = glm::normalize(glm::inverse(
absolutePoses[pivotsParentIndex].rot) *
deltaRotation *
absolutePoses[pivotIndex].rot);
// enforce pivot's constraint
RotationConstraint* constraint = getConstraint(pivotIndex);
if (constraint) {
bool constrained = constraint->apply(newRot);
if (constrained) {
// the constraint will modify the movement of the tip so we have to compute the modified
// model-frame deltaRotation
// Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^
deltaRotation = absolutePoses[pivotsParentIndex].rot *
newRot *
glm::inverse(absolutePoses[pivotIndex].rot);
}
}
// store the rotation change in the accumulator
_accumulators[pivotIndex].add(newRot);
// this joint has been changed so we check to see if it has the lowest index // this joint has been changed so we check to see if it has the lowest index
if (pivotIndex < lowestMovedIndex) { if (pivotIndex < lowestMovedIndex) {
lowestMovedIndex = pivotIndex; lowestMovedIndex = pivotIndex;
@ -252,7 +267,8 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
tipRotation = glm::normalize(deltaRotation * tipRotation); tipRotation = glm::normalize(deltaRotation * tipRotation);
tipParentRotation = glm::normalize(deltaRotation * tipParentRotation); tipParentRotation = glm::normalize(deltaRotation * tipParentRotation);
pivotIndex = _skeleton->getParentIndex(pivotIndex); pivotIndex = pivotsParentIndex;
pivotsParentIndex = _skeleton->getParentIndex(pivotIndex);
} }
} }
++numLoops; ++numLoops;
@ -275,26 +291,15 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
} }
} while (numLoops < MAX_IK_LOOPS); } while (numLoops < MAX_IK_LOOPS);
/* KEEP: example code for measuring endeffector error of IK solution
for (uint32_t i = 0; i < targets.size(); ++i) {
auto& target = targets[i];
if (target.type == IKTarget::Type::RotationOnly) {
continue;
}
glm::vec3 tipPosition = absolutePoses[target.index].trans;
std::cout << i << " IK error = " << glm::distance(tipPosition, target.pose.trans) << std::endl;
}
*/
// finally set the relative rotation of each tip to agree with absolute target rotation // finally set the relative rotation of each tip to agree with absolute target rotation
for (auto& target: targets) { for (auto& target: targets) {
int tipIndex = target.index; int tipIndex = target.getIndex();
int parentIndex = _skeleton->getParentIndex(tipIndex); int parentIndex = _skeleton->getParentIndex(tipIndex);
if (parentIndex != -1) { if (parentIndex != -1) {
AnimPose targetPose = target.pose; const glm::quat& targetRotation = target.getRotation();
// compute tip's new parent-relative rotation // compute tip's new parent-relative rotation
// Q = Qp * q --> q' = Qp^ * Q // Q = Qp * q --> q' = Qp^ * Q
glm::quat newRelativeRotation = glm::inverse(absolutePoses[parentIndex].rot) * targetPose.rot; glm::quat newRelativeRotation = glm::inverse(absolutePoses[parentIndex].rot) * targetRotation;
RotationConstraint* constraint = getConstraint(tipIndex); RotationConstraint* constraint = getConstraint(tipIndex);
if (constraint) { if (constraint) {
constraint->apply(newRelativeRotation); constraint->apply(newRelativeRotation);
@ -303,7 +308,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
// to help this rotation target get met. // to help this rotation target get met.
} }
_relativePoses[tipIndex].rot = newRelativeRotation; _relativePoses[tipIndex].rot = newRelativeRotation;
absolutePoses[tipIndex].rot = targetPose.rot; absolutePoses[tipIndex].rot = targetRotation;
} }
} }
} }
@ -323,7 +328,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
// relax toward underpose // relax toward underpose
// HACK: this relaxation needs to be constant per-frame rather than per-realtime // HACK: this relaxation needs to be constant per-frame rather than per-realtime
// in order to prevent IK "flutter" for bad FPS. The bad news is that the good parts // in order to prevent IK "flutter" for bad FPS. The bad news is that the good parts
// of this relaxation will be FPS dependent (low FPS will make the limbs align slower // of this relaxation will be FPS dependent (low FPS will make the limbs align slower
// in real-time), however most people will not notice this and this problem is less // in real-time), however most people will not notice this and this problem is less
// annoying than the flutter. // annoying than the flutter.
const float blend = (1.0f / 60.0f) / (0.25f); // effectively: dt / RELAXATION_TIMESCALE const float blend = (1.0f / 60.0f) / (0.25f); // effectively: dt / RELAXATION_TIMESCALE
@ -343,7 +348,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
// build a list of targets from _targetVarVec // build a list of targets from _targetVarVec
std::vector<IKTarget> targets; std::vector<IKTarget> targets;
computeTargets(animVars, targets, underPoses); computeTargets(animVars, targets, underPoses);
if (targets.empty()) { if (targets.empty()) {
// no IK targets but still need to enforce constraints // no IK targets but still need to enforce constraints
std::map<int, RotationConstraint*>::iterator constraintItr = _constraints.begin(); std::map<int, RotationConstraint*>::iterator constraintItr = _constraints.begin();
@ -355,7 +360,50 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
++constraintItr; ++constraintItr;
} }
} else { } else {
// shift the hips according to the offset from the previous frame
float offsetLength = glm::length(_hipsOffset);
const float MIN_HIPS_OFFSET_LENGTH = 0.03f;
if (offsetLength > MIN_HIPS_OFFSET_LENGTH) {
// but only if offset is long enough
float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength);
_relativePoses[0].trans = underPoses[0].trans + scaleFactor * _hipsOffset;
}
solveWithCyclicCoordinateDescent(targets); solveWithCyclicCoordinateDescent(targets);
// compute the new target hips offset (for next frame)
// by looking for discrepancies between where a targeted endEffector is
// and where it wants to be (after IK solutions are done)
glm::vec3 newHipsOffset = Vectors::ZERO;
for (auto& target: targets) {
int targetIndex = target.getIndex();
if (targetIndex == _headIndex && _headIndex != -1) {
// special handling for headTarget
if (target.getType() == IKTarget::Type::RotationOnly) {
// we want to shift the hips to bring the underpose closer
// to where the head happens to be (overpose)
glm::vec3 under = _skeleton->getAbsolutePose(_headIndex, underPoses).trans;
glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans;
const float HEAD_OFFSET_SLAVE_FACTOR = 0.65f;
newHipsOffset += HEAD_OFFSET_SLAVE_FACTOR * (actual - under);
} else if (target.getType() == IKTarget::Type::HmdHead) {
// we want to shift the hips to bring the head to its designated position
glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans;
_hipsOffset += target.getTranslation() - actual;
// and ignore all other targets
newHipsOffset = _hipsOffset;
break;
}
} else if (target.getType() == IKTarget::Type::RotationAndPosition) {
glm::vec3 actualPosition = _skeleton->getAbsolutePose(targetIndex, _relativePoses).trans;
glm::vec3 targetPosition = target.getTranslation();
newHipsOffset += targetPosition - actualPosition;
}
}
// smooth transitions by relaxing _hipsOffset toward the new value
const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.15f;
_hipsOffset += (newHipsOffset - _hipsOffset) * (dt / HIPS_OFFSET_SLAVE_TIMESCALE);
} }
} }
return _relativePoses; return _relativePoses;
@ -477,7 +525,7 @@ void AnimInverseKinematics::initConstraints() {
stConstraint->setSwingLimits(minDots); stConstraint->setSwingLimits(minDots);
constraint = static_cast<RotationConstraint*>(stConstraint); constraint = static_cast<RotationConstraint*>(stConstraint);
} else if (0 == baseName.compare("UpLegXXX", Qt::CaseInsensitive)) { } else if (0 == baseName.compare("UpLeg", Qt::CaseInsensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
stConstraint->setTwistLimits(-PI / 4.0f, PI / 4.0f); stConstraint->setTwistLimits(-PI / 4.0f, PI / 4.0f);
@ -581,7 +629,7 @@ void AnimInverseKinematics::initConstraints() {
} else if (0 == baseName.compare("Neck", Qt::CaseInsensitive)) { } else if (0 == baseName.compare("Neck", Qt::CaseInsensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
const float MAX_NECK_TWIST = PI / 2.0f; const float MAX_NECK_TWIST = PI / 4.0f;
stConstraint->setTwistLimits(-MAX_NECK_TWIST, MAX_NECK_TWIST); stConstraint->setTwistLimits(-MAX_NECK_TWIST, MAX_NECK_TWIST);
std::vector<float> minDots; std::vector<float> minDots;
@ -589,6 +637,18 @@ void AnimInverseKinematics::initConstraints() {
minDots.push_back(cosf(MAX_NECK_SWING)); minDots.push_back(cosf(MAX_NECK_SWING));
stConstraint->setSwingLimits(minDots); stConstraint->setSwingLimits(minDots);
constraint = static_cast<RotationConstraint*>(stConstraint);
} else if (0 == baseName.compare("Head", Qt::CaseInsensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
const float MAX_HEAD_TWIST = PI / 4.0f;
stConstraint->setTwistLimits(-MAX_HEAD_TWIST, MAX_HEAD_TWIST);
std::vector<float> minDots;
const float MAX_HEAD_SWING = PI / 4.0f;
minDots.push_back(cosf(MAX_HEAD_SWING));
stConstraint->setSwingLimits(minDots);
constraint = static_cast<RotationConstraint*>(stConstraint); constraint = static_cast<RotationConstraint*>(stConstraint);
} else if (0 == baseName.compare("ForeArm", Qt::CaseInsensitive)) { } else if (0 == baseName.compare("ForeArm", Qt::CaseInsensitive)) {
// The elbow joint rotates about the parent-frame's zAxis (-zAxis) for the Right (Left) arm. // The elbow joint rotates about the parent-frame's zAxis (-zAxis) for the Right (Left) arm.
@ -621,7 +681,7 @@ void AnimInverseKinematics::initConstraints() {
eConstraint->setAngleLimits(minAngle, maxAngle); eConstraint->setAngleLimits(minAngle, maxAngle);
constraint = static_cast<RotationConstraint*>(eConstraint); constraint = static_cast<RotationConstraint*>(eConstraint);
} else if (0 == baseName.compare("LegXXX", Qt::CaseInsensitive)) { } else if (0 == baseName.compare("Leg", Qt::CaseInsensitive)) {
// The knee joint rotates about the parent-frame's -xAxis. // The knee joint rotates about the parent-frame's -xAxis.
ElbowConstraint* eConstraint = new ElbowConstraint(); ElbowConstraint* eConstraint = new ElbowConstraint();
glm::quat referenceRotation = _defaultRelativePoses[i].rot; glm::quat referenceRotation = _defaultRelativePoses[i].rot;
@ -652,7 +712,7 @@ void AnimInverseKinematics::initConstraints() {
eConstraint->setAngleLimits(minAngle, maxAngle); eConstraint->setAngleLimits(minAngle, maxAngle);
constraint = static_cast<RotationConstraint*>(eConstraint); constraint = static_cast<RotationConstraint*>(eConstraint);
} else if (0 == baseName.compare("FootXXX", Qt::CaseInsensitive)) { } else if (0 == baseName.compare("Foot", Qt::CaseInsensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
stConstraint->setTwistLimits(-PI / 4.0f, PI / 4.0f); stConstraint->setTwistLimits(-PI / 4.0f, PI / 4.0f);
@ -697,7 +757,9 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
if (skeleton) { if (skeleton) {
initConstraints(); initConstraints();
_headIndex = _skeleton->nameToJointIndex("Head");
} else { } else {
clearConstraints(); clearConstraints();
_headIndex = -1;
} }
} }

View file

@ -16,6 +16,7 @@
#include <vector> #include <vector>
#include "AnimNode.h" #include "AnimNode.h"
#include "IKTarget.h"
#include "RotationAccumulator.h" #include "RotationAccumulator.h"
@ -37,18 +38,6 @@ public:
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
protected: protected:
struct IKTarget {
enum class Type {
RotationAndPosition,
RotationOnly
};
AnimPose pose;
int index;
Type type = Type::RotationAndPosition;
void setType(const QString& typeVar) { type = ((typeVar == "RotationOnly") ? Type::RotationOnly : Type::RotationAndPosition); }
};
void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses); void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses);
void solveWithCyclicCoordinateDescent(const std::vector<IKTarget>& targets); void solveWithCyclicCoordinateDescent(const std::vector<IKTarget>& targets);
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override; virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
@ -60,6 +49,10 @@ protected:
void clearConstraints(); void clearConstraints();
void initConstraints(); void initConstraints();
// no copies
AnimInverseKinematics(const AnimInverseKinematics&) = delete;
AnimInverseKinematics& operator=(const AnimInverseKinematics&) = delete;
struct IKTargetVar { struct IKTargetVar {
IKTargetVar(const QString& jointNameIn, IKTargetVar(const QString& jointNameIn,
const QString& positionVarIn, const QString& positionVarIn,
@ -85,9 +78,9 @@ protected:
AnimPoseVec _defaultRelativePoses; // poses of the relaxed state AnimPoseVec _defaultRelativePoses; // poses of the relaxed state
AnimPoseVec _relativePoses; // current relative poses AnimPoseVec _relativePoses; // current relative poses
// no copies // experimental data for moving hips during IK
AnimInverseKinematics(const AnimInverseKinematics&) = delete; int _headIndex = -1;
AnimInverseKinematics& operator=(const AnimInverseKinematics&) = delete; glm::vec3 _hipsOffset = Vectors::ZERO;
// _maxTargetIndex is tracked to help optimize the recalculation of absolute poses // _maxTargetIndex is tracked to help optimize the recalculation of absolute poses
// during the the cyclic coordinate descent algorithm // during the the cyclic coordinate descent algorithm

View file

@ -0,0 +1,34 @@
//
// IKTarget.cpp
//
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "IKTarget.h"
void IKTarget::setPose(const glm::quat& rotation, const glm::vec3& translation) {
_pose.rot = rotation;
_pose.trans = translation;
}
void IKTarget::setType(int type) {
switch (type) {
case (int)Type::RotationAndPosition:
_type = Type::RotationAndPosition;
break;
case (int)Type::RotationOnly:
_type = Type::RotationOnly;
break;
case (int)Type::HmdHead:
_type = Type::HmdHead;
break;
case (int)Type::HipsRelativeRotationAndPosition:
_type = Type::HipsRelativeRotationAndPosition;
break;
default:
_type = Type::Unknown;
}
}

View file

@ -0,0 +1,43 @@
//
// IKTarget.h
//
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_IKTarget_h
#define hifi_IKTarget_h
#include "AnimSkeleton.h"
class IKTarget {
public:
enum class Type {
RotationAndPosition,
RotationOnly,
HmdHead,
HipsRelativeRotationAndPosition,
Unknown,
};
IKTarget() {}
const glm::vec3& getTranslation() const { return _pose.trans; }
const glm::quat& getRotation() const { return _pose.rot; }
int getIndex() const { return _index; }
Type getType() const { return _type; }
void setPose(const glm::quat& rotation, const glm::vec3& translation);
void setIndex(int index) { _index = index; }
void setType(int);
private:
AnimPose _pose;
int _index = -1;
Type _type = Type::RotationAndPosition;
};
#endif // hifi_IKTarget_h

View file

@ -14,13 +14,14 @@
#include <glm/gtx/vector_angle.hpp> #include <glm/gtx/vector_angle.hpp>
#include <queue> #include <queue>
#include "NumericalConstants.h" #include <NumericalConstants.h>
#include <DebugDraw.h>
#include "AnimationHandle.h" #include "AnimationHandle.h"
#include "AnimationLogging.h" #include "AnimationLogging.h"
#include "AnimSkeleton.h" #include "AnimSkeleton.h"
#include "DebugDraw.h" #include "IKTarget.h"
#include "Rig.h"
void Rig::HeadParameters::dump() const { void Rig::HeadParameters::dump() const {
qCDebug(animation, "HeadParameters ="); qCDebug(animation, "HeadParameters =");
@ -1057,9 +1058,11 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) {
_animVars.set("headPosition", headPos); _animVars.set("headPosition", headPos);
_animVars.set("headRotation", headRot); _animVars.set("headRotation", headRot);
_animVars.set("headAndNeckType", QString("RotationAndPosition")); _animVars.set("headType", (int)IKTarget::Type::HmdHead);
_animVars.set("neckPosition", neckPos); _animVars.set("neckPosition", neckPos);
_animVars.set("neckRotation", neckRot); _animVars.set("neckRotation", neckRot);
//_animVars.set("neckType", (int)IKTarget::Type::RotationOnly);
_animVars.set("neckType", (int)IKTarget::Type::Unknown); // 'Unknown' disables the target
} else { } else {
@ -1070,9 +1073,11 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) {
_animVars.unset("headPosition"); _animVars.unset("headPosition");
_animVars.set("headRotation", realLocalHeadOrientation); _animVars.set("headRotation", realLocalHeadOrientation);
_animVars.set("headAndNeckType", QString("RotationOnly")); _animVars.set("headAndNeckType", (int)IKTarget::Type::RotationOnly);
_animVars.set("headType", (int)IKTarget::Type::RotationOnly);
_animVars.unset("neckPosition"); _animVars.unset("neckPosition");
_animVars.unset("neckRotation"); _animVars.unset("neckRotation");
_animVars.set("neckType", (int)IKTarget::Type::RotationOnly);
} }
} else if (!_enableAnimGraph) { } else if (!_enableAnimGraph) {
@ -1130,16 +1135,20 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) {
if (params.isLeftEnabled) { if (params.isLeftEnabled) {
_animVars.set("leftHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.leftPosition); _animVars.set("leftHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.leftPosition);
_animVars.set("leftHandRotation", rootBindPose.rot * yFlipHACK * params.leftOrientation); _animVars.set("leftHandRotation", rootBindPose.rot * yFlipHACK * params.leftOrientation);
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
} else { } else {
_animVars.unset("leftHandPosition"); _animVars.unset("leftHandPosition");
_animVars.unset("leftHandRotation"); _animVars.unset("leftHandRotation");
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
} }
if (params.isRightEnabled) { if (params.isRightEnabled) {
_animVars.set("rightHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.rightPosition); _animVars.set("rightHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.rightPosition);
_animVars.set("rightHandRotation", rootBindPose.rot * yFlipHACK * params.rightOrientation); _animVars.set("rightHandRotation", rootBindPose.rot * yFlipHACK * params.rightOrientation);
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
} else { } else {
_animVars.unset("rightHandPosition"); _animVars.unset("rightHandPosition");
_animVars.unset("rightHandRotation"); _animVars.unset("rightHandRotation");
_animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
} }
// set leftHand grab vars // set leftHand grab vars

View file

@ -7,5 +7,10 @@
// //
#include "Mapping.h" #include "Mapping.h"
namespace controller {
Mapping::Mapping(const QString& name ) : _name(name) {
}
}

View file

@ -27,7 +27,10 @@ namespace controller {
using Map = std::map<Endpoint::Pointer, Route::List>; using Map = std::map<Endpoint::Pointer, Route::List>;
using Pointer = std::shared_ptr<Mapping>; using Pointer = std::shared_ptr<Mapping>;
Mapping(const QString& name);
Map _channelMappings; Map _channelMappings;
QString _name; QString _name;
protected: protected:

View file

@ -59,6 +59,25 @@ namespace controller {
QJSValue _callable; QJSValue _callable;
}; };
class ScriptEndpoint : public Endpoint {
public:
ScriptEndpoint(const QScriptValue& callable)
: Endpoint(UserInputMapper::Input(-1)), _callable(callable) {
}
virtual float value() {
float result = (float)_callable.call().toNumber();
return result;
}
virtual void apply(float newValue, float oldValue, const Pointer& source) {
_callable.call(QScriptValue(), QScriptValueList({ QScriptValue(newValue) }));
}
private:
QScriptValue _callable;
};
class CompositeEndpoint : public Endpoint, Endpoint::Pair { class CompositeEndpoint : public Endpoint, Endpoint::Pair {
public: public:
CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second) CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second)
@ -158,7 +177,7 @@ namespace controller {
qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName; qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName;
} }
qDebug() << "Creating new Mapping " << mappingName; qDebug() << "Creating new Mapping " << mappingName;
Mapping::Pointer mapping = std::make_shared<Mapping>(); auto mapping = std::make_shared<Mapping>(mappingName);
_mappingsByName[mappingName] = mapping; _mappingsByName[mappingName] = mapping;
return new MappingBuilderProxy(*this, mapping); return new MappingBuilderProxy(*this, mapping);
} }
@ -297,6 +316,11 @@ namespace controller {
return endpointFor(UserInputMapper::Input(endpoint.toInt32())); return endpointFor(UserInputMapper::Input(endpoint.toInt32()));
} }
if (endpoint.isFunction()) {
auto result = std::make_shared<ScriptEndpoint>(endpoint);
return result;
}
qWarning() << "Unsupported input type " << endpoint.toString(); qWarning() << "Unsupported input type " << endpoint.toString();
return Endpoint::Pointer(); return Endpoint::Pointer();
} }

View file

@ -39,7 +39,7 @@ namespace controller {
Q_INVOKABLE float getValue(const int& source); Q_INVOKABLE float getValue(const int& source);
Q_INVOKABLE void update(); Q_INVOKABLE void update();
Q_INVOKABLE QObject* newMapping(const QString& mappingName); Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString());
Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true); Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true);
Q_INVOKABLE void disableMapping(const QString& mappingName) { Q_INVOKABLE void disableMapping(const QString& mappingName) {
enableMapping(mappingName, false); enableMapping(mappingName, false);

View file

@ -44,7 +44,6 @@ QObject* MappingBuilderProxy::join(const QJSValue& source1, const QJSValue& sour
return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint));
} }
const QString JSON_NAME = QStringLiteral("name"); const QString JSON_NAME = QStringLiteral("name");
const QString JSON_CHANNELS = QStringLiteral("channels"); const QString JSON_CHANNELS = QStringLiteral("channels");
const QString JSON_CHANNEL_FROM = QStringLiteral("from"); const QString JSON_CHANNEL_FROM = QStringLiteral("from");
@ -84,4 +83,9 @@ QObject* MappingBuilderProxy::from(const QJsonValue& json) {
} }
} }
QObject* MappingBuilderProxy::enable(bool enable) {
_parent.enableMapping(_mapping->_name, enable);
return this;
}

View file

@ -31,15 +31,18 @@ public:
Q_INVOKABLE QObject* from(const QJSValue& source); Q_INVOKABLE QObject* from(const QJSValue& source);
Q_INVOKABLE QObject* from(const QScriptValue& source); Q_INVOKABLE QObject* from(const QScriptValue& source);
Q_INVOKABLE QObject* join(const QJSValue& source1, const QJSValue& source2); Q_INVOKABLE QObject* join(const QJSValue& source1, const QJSValue& source2);
Q_INVOKABLE QObject* enable(bool enable = true);
Q_INVOKABLE QObject* disable() { return enable(false); }
// JSON route creation point // JSON route creation point
Q_INVOKABLE QObject* from(const QJsonValue& json); Q_INVOKABLE QObject* from(const QJsonValue& json);
void parse(const QJsonObject& json); void parse(const QJsonObject& json);
// void serialize(QJsonObject& json); // void serialize(QJsonObject& json);
protected: protected:
QObject* from(const Endpoint::Pointer& source); QObject* from(const Endpoint::Pointer& source);

View file

@ -27,7 +27,4 @@ void NullDisplayPlugin::preRender() {}
void NullDisplayPlugin::preDisplay() {} void NullDisplayPlugin::preDisplay() {}
void NullDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) {} void NullDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) {}
void NullDisplayPlugin::finishFrame() {} void NullDisplayPlugin::finishFrame() {}
void NullDisplayPlugin::activate() {}
void NullDisplayPlugin::deactivate() {}
void NullDisplayPlugin::stop() {} void NullDisplayPlugin::stop() {}

View file

@ -15,8 +15,6 @@ public:
virtual ~NullDisplayPlugin() final {} virtual ~NullDisplayPlugin() final {}
virtual const QString & getName() const override; virtual const QString & getName() const override;
void activate() override;
void deactivate() override;
void stop() override; void stop() override;
virtual glm::uvec2 getRecommendedRenderSize() const override; virtual glm::uvec2 getRecommendedRenderSize() const override;

View file

@ -57,12 +57,12 @@ void OpenGLDisplayPlugin::customizeContext() {
} }
void OpenGLDisplayPlugin::activate() { void OpenGLDisplayPlugin::activate() {
_active = true; DisplayPlugin::activate();
_timer.start(1); _timer.start(1);
} }
void OpenGLDisplayPlugin::stop() { void OpenGLDisplayPlugin::stop() {
_active = false; DisplayPlugin::activate();
_timer.stop(); _timer.stop();
} }

View file

@ -44,7 +44,6 @@ protected:
mutable QTimer _timer; mutable QTimer _timer;
ProgramPtr _program; ProgramPtr _program;
ShapeWrapperPtr _plane; ShapeWrapperPtr _plane;
bool _active{ false };
bool _vsyncSupported{ false }; bool _vsyncSupported{ false };
}; };

View file

@ -22,7 +22,7 @@ InputPluginList getInputPlugins() {
InputPlugin* PLUGIN_POOL[] = { InputPlugin* PLUGIN_POOL[] = {
new KeyboardMouseDevice(), new KeyboardMouseDevice(),
new SDL2Manager(), new SDL2Manager(),
//new SixenseManager(), new SixenseManager(),
//new ViveControllerManager(), //new ViveControllerManager(),
nullptr nullptr
}; };

View file

@ -10,9 +10,9 @@
// //
#include "KeyboardMouseDevice.h" #include "KeyboardMouseDevice.h"
#include <QtGUI/QKeyEvent> #include <QtGui/QKeyEvent>
#include <QtGUI/QMouseEvent> #include <QtGui/QMouseEvent>
#include <QtGUI/QTouchEvent> #include <QtGui/QTouchEvent>
const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse";

View file

@ -13,6 +13,9 @@
#define hifi_KeyboardMouseDevice_h #define hifi_KeyboardMouseDevice_h
#include <chrono> #include <chrono>
#include <QtCore/QPoint>
#include "InputDevice.h" #include "InputDevice.h"
#include "InputPlugin.h" #include "InputPlugin.h"
@ -65,9 +68,6 @@ public:
virtual bool isJointController() const override { return false; } virtual bool isJointController() const override { return false; }
const QString& getName() const override { return NAME; } const QString& getName() const override { return NAME; }
virtual void activate() override {};
virtual void deactivate() override {};
virtual void pluginFocusOutEvent() override { focusOutEvent(); } virtual void pluginFocusOutEvent() override { focusOutEvent(); }
virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); }

View file

@ -34,8 +34,6 @@ public:
virtual void init() override; virtual void init() override;
virtual void deinit() override; virtual void deinit() override;
virtual void activate() override {};
virtual void deactivate() override {};
virtual void pluginFocusOutEvent() override; virtual void pluginFocusOutEvent() override;
virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override;

View file

@ -85,6 +85,7 @@ bool SixenseManager::isSupported() const {
} }
void SixenseManager::activate() { void SixenseManager::activate() {
InputPlugin::activate();
#ifdef HAVE_SIXENSE #ifdef HAVE_SIXENSE
_calibrationState = CALIBRATION_STATE_IDLE; _calibrationState = CALIBRATION_STATE_IDLE;
_avatarPosition = DEFAULT_AVATAR_POSITION; _avatarPosition = DEFAULT_AVATAR_POSITION;
@ -125,6 +126,7 @@ void SixenseManager::activate() {
} }
void SixenseManager::deactivate() { void SixenseManager::deactivate() {
InputPlugin::deactivate();
#ifdef HAVE_SIXENSE #ifdef HAVE_SIXENSE
CONTAINER->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH); CONTAINER->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH);
CONTAINER->removeMenu(MENU_PATH); CONTAINER->removeMenu(MENU_PATH);

View file

@ -17,8 +17,6 @@
const float CONTROLLER_THRESHOLD = 0.3f; const float CONTROLLER_THRESHOLD = 0.3f;
const float MAX_AXIS = 32768.0f;
StandardController::~StandardController() { StandardController::~StandardController() {
} }

View file

@ -79,6 +79,7 @@ bool ViveControllerManager::isSupported() const {
} }
void ViveControllerManager::activate() { void ViveControllerManager::activate() {
InputPlugin::activate();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
CONTAINER->addMenu(MENU_PATH); CONTAINER->addMenu(MENU_PATH);
CONTAINER->addMenuItem(MENU_PATH, RENDER_CONTROLLERS, CONTAINER->addMenuItem(MENU_PATH, RENDER_CONTROLLERS,
@ -143,6 +144,8 @@ void ViveControllerManager::activate() {
} }
void ViveControllerManager::deactivate() { void ViveControllerManager::deactivate() {
InputPlugin::deactivate();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
CONTAINER->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS); CONTAINER->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS);
CONTAINER->removeMenu(MENU_PATH); CONTAINER->removeMenu(MENU_PATH);

View file

@ -14,10 +14,8 @@
#include <algorithm> #include <algorithm>
#include <QtCore/QThread> #include <QtCore/QThread>
#include <QtNetwork/QAbstractNetworkCache>
#include "AssetClient.h" #include "AssetClient.h"
#include "NetworkAccessManager.h"
#include "NetworkLogging.h" #include "NetworkLogging.h"
#include "NodeList.h" #include "NodeList.h"
#include "ResourceCache.h" #include "ResourceCache.h"
@ -41,14 +39,14 @@ void AssetRequest::start() {
} }
// Try to load from cache // Try to load from cache
if (loadFromCache()) { _data = loadFromCache(getUrl());
if (!_data.isNull()) {
_info.hash = _hash; _info.hash = _hash;
_info.size = _data.size(); _info.size = _data.size();
_error = NoError; _error = NoError;
_state = Finished; _state = Finished;
emit finished(this); emit finished(this);
qCDebug(asset_client) << getUrl().toDisplayString() << "loaded from disk cache.";
return; return;
} }
@ -112,9 +110,7 @@ void AssetRequest::start() {
_totalReceived += data.size(); _totalReceived += data.size();
emit progress(_totalReceived, _info.size); emit progress(_totalReceived, _info.size);
if (saveToCache(data)) { saveToCache(getUrl(), data);
qCDebug(asset_client) << getUrl().toDisplayString() << "saved to disk cache";
}
} else { } else {
// hash doesn't match - we have an error // hash doesn't match - we have an error
_error = HashVerificationFailed; _error = HashVerificationFailed;
@ -133,49 +129,6 @@ void AssetRequest::start() {
} }
QUrl AssetRequest::getUrl() const { QUrl AssetRequest::getUrl() const {
if (!_extension.isEmpty()) { return ::getUrl(_hash, _extension);
return QUrl(QString("%1:%2.%3").arg(URL_SCHEME_ATP, _hash, _extension));
} else {
return QUrl(QString("%1:%2").arg(URL_SCHEME_ATP, _hash));
}
}
bool AssetRequest::loadFromCache() {
if (auto cache = NetworkAccessManager::getInstance().cache()) {
auto url = getUrl();
if (auto ioDevice = cache->data(url)) {
_data = ioDevice->readAll();
return true;
} else {
qCDebug(asset_client) << url.toDisplayString() << "not in disk cache";
}
} else {
qCWarning(asset_client) << "No disk cache to load assets from.";
}
return false;
}
bool AssetRequest::saveToCache(const QByteArray& file) const {
if (auto cache = NetworkAccessManager::getInstance().cache()) {
auto url = getUrl();
if (!cache->metaData(url).isValid()) {
QNetworkCacheMetaData metaData;
metaData.setUrl(url);
metaData.setSaveToDisk(true);
metaData.setLastModified(QDateTime::currentDateTime());
metaData.setExpirationDate(QDateTime()); // Never expires
if (auto ioDevice = cache->prepare(metaData)) {
ioDevice->write(file);
cache->insert(ioDevice);
return true;
}
qCWarning(asset_client) << "Could not save" << url.toDisplayString() << "to disk cache.";
}
} else {
qCWarning(asset_client) << "No disk cache to save assets to.";
}
return false;
} }

View file

@ -53,9 +53,6 @@ signals:
void progress(qint64 totalReceived, qint64 total); void progress(qint64 totalReceived, qint64 total);
private: private:
bool loadFromCache();
bool saveToCache(const QByteArray& file) const;
State _state = NotStarted; State _state = NotStarted;
Error _error = NoError; Error _error = NoError;
AssetInfo _info; AssetInfo _info;

View file

@ -25,7 +25,7 @@ AssetUpload::AssetUpload(QObject* object, const QString& filename) :
void AssetUpload::start() { void AssetUpload::start() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "start", Qt::AutoConnection); QMetaObject::invokeMethod(this, "start");
return; return;
} }
@ -37,14 +37,15 @@ void AssetUpload::start() {
// file opened, read the data and grab the extension // file opened, read the data and grab the extension
_extension = QFileInfo(_filename).suffix(); _extension = QFileInfo(_filename).suffix();
auto data = file.readAll(); _data = file.readAll();
// ask the AssetClient to upload the asset and emit the proper signals from the passed callback // ask the AssetClient to upload the asset and emit the proper signals from the passed callback
auto assetClient = DependencyManager::get<AssetClient>(); auto assetClient = DependencyManager::get<AssetClient>();
qCDebug(asset_client) << "Attempting to upload" << _filename << "to asset-server."; qCDebug(asset_client) << "Attempting to upload" << _filename << "to asset-server.";
assetClient->uploadAsset(data, _extension, [this](bool responseReceived, AssetServerError error, const QString& hash){ assetClient->uploadAsset(_data, _extension, [this](bool responseReceived, AssetServerError error,
const QString& hash){
if (!responseReceived) { if (!responseReceived) {
_error = NetworkError; _error = NetworkError;
} else { } else {
@ -63,6 +64,11 @@ void AssetUpload::start() {
break; break;
} }
} }
if (_error == NoError && hash == hashData(_data).toHex()) {
saveToCache(getUrl(hash, _extension), _data);
}
emit finished(this, hash); emit finished(this, hash);
}); });
} else { } else {

View file

@ -50,6 +50,7 @@ signals:
private: private:
QString _filename; QString _filename;
QString _extension; QString _extension;
QByteArray _data;
Error _error; Error _error;
}; };

View file

@ -0,0 +1,69 @@
//
// AssetUtils.h
// libraries/networking/src
//
// Created by Clément Brisset on 10/12/2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AssetUtils.h"
#include <QtCore/QCryptographicHash>
#include <QtNetwork/QAbstractNetworkCache>
#include "NetworkAccessManager.h"
#include "NetworkLogging.h"
#include "ResourceManager.h"
QUrl getUrl(const QString& hash, const QString& extension) {
if (!extension.isEmpty()) {
return QUrl(QString("%1:%2.%3").arg(URL_SCHEME_ATP, hash, extension));
} else {
return QUrl(QString("%1:%2").arg(URL_SCHEME_ATP, hash));
}
}
QByteArray hashData(const QByteArray& data) {
return QCryptographicHash::hash(data, QCryptographicHash::Sha256);
}
QByteArray loadFromCache(const QUrl& url) {
if (auto cache = NetworkAccessManager::getInstance().cache()) {
if (auto ioDevice = cache->data(url)) {
qCDebug(asset_client) << url.toDisplayString() << "loaded from disk cache.";
return ioDevice->readAll();
} else {
qCDebug(asset_client) << url.toDisplayString() << "not in disk cache";
}
} else {
qCWarning(asset_client) << "No disk cache to load assets from.";
}
return QByteArray();
}
bool saveToCache(const QUrl& url, const QByteArray& file) {
if (auto cache = NetworkAccessManager::getInstance().cache()) {
if (!cache->metaData(url).isValid()) {
QNetworkCacheMetaData metaData;
metaData.setUrl(url);
metaData.setSaveToDisk(true);
metaData.setLastModified(QDateTime::currentDateTime());
metaData.setExpirationDate(QDateTime()); // Never expires
if (auto ioDevice = cache->prepare(metaData)) {
ioDevice->write(file);
cache->insert(ioDevice);
qCDebug(asset_client) << url.toDisplayString() << "saved to disk cache";
return true;
}
qCWarning(asset_client) << "Could not save" << url.toDisplayString() << "to disk cache.";
}
} else {
qCWarning(asset_client) << "No disk cache to save assets to.";
}
return false;
}

View file

@ -12,10 +12,11 @@
#ifndef hifi_AssetUtils_h #ifndef hifi_AssetUtils_h
#define hifi_AssetUtils_h #define hifi_AssetUtils_h
#include <QtCore/QCryptographicHash>
#include <cstdint> #include <cstdint>
#include <QtCore/QByteArray>
#include <QtCore/QUrl>
using MessageID = uint32_t; using MessageID = uint32_t;
using DataOffset = int64_t; using DataOffset = int64_t;
@ -31,8 +32,11 @@ enum AssetServerError : uint8_t {
PermissionDenied PermissionDenied
}; };
const QString ATP_SCHEME = "atp"; QUrl getUrl(const QString& hash, const QString& extension = QString());
inline QByteArray hashData(const QByteArray& data) { return QCryptographicHash::hash(data, QCryptographicHash::Sha256); } QByteArray hashData(const QByteArray& data);
QByteArray loadFromCache(const QUrl& url);
bool saveToCache(const QUrl& url, const QByteArray& file);
#endif #endif

View file

@ -319,10 +319,10 @@ void Resource::attemptRequest() {
void Resource::finishedLoading(bool success) { void Resource::finishedLoading(bool success) {
if (success) { if (success) {
qDebug() << "Finished loading:" << _url; qDebug().noquote() << "Finished loading:" << _url.toDisplayString();
_loaded = true; _loaded = true;
} else { } else {
qDebug() << "Failed to load:" << _url; qDebug().noquote() << "Failed to load:" << _url.toDisplayString();
_failedToLoad = true; _failedToLoad = true;
} }
_loadPriorities.clear(); _loadPriorities.clear();
@ -339,13 +339,13 @@ void Resource::makeRequest() {
_request = ResourceManager::createResourceRequest(this, _activeUrl); _request = ResourceManager::createResourceRequest(this, _activeUrl);
if (!_request) { if (!_request) {
qDebug() << "Failed to get request for " << _url; qDebug().noquote() << "Failed to get request for" << _url.toDisplayString();
ResourceCache::requestCompleted(this); ResourceCache::requestCompleted(this);
finishedLoading(false); finishedLoading(false);
return; return;
} }
qDebug() << "Starting request for: " << _url; qDebug().noquote() << "Starting request for:" << _url.toDisplayString();
connect(_request, &ResourceRequest::progress, this, &Resource::handleDownloadProgress); connect(_request, &ResourceRequest::progress, this, &Resource::handleDownloadProgress);
connect(_request, &ResourceRequest::finished, this, &Resource::handleReplyFinished); connect(_request, &ResourceRequest::finished, this, &Resource::handleReplyFinished);
@ -368,7 +368,8 @@ void Resource::handleReplyFinished() {
auto result = _request->getResult(); auto result = _request->getResult();
if (result == ResourceRequest::Success) { if (result == ResourceRequest::Success) {
_data = _request->getData(); _data = _request->getData();
qDebug() << "Request finished for " << _url << ", " << _activeUrl; auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString());
qDebug().noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo);
finishedLoading(true); finishedLoading(true);
emit loaded(_data); emit loaded(_data);

View file

@ -33,9 +33,18 @@ public:
virtual void deinit(); virtual void deinit();
/// Called when a plugin is being activated for use. May be called multiple times. /// Called when a plugin is being activated for use. May be called multiple times.
virtual void activate() = 0; virtual void activate() {
_active = true;
}
/// Called when a plugin is no longer being used. May be called multiple times. /// Called when a plugin is no longer being used. May be called multiple times.
virtual void deactivate() = 0; virtual void deactivate() {
_active = false;
}
virtual bool isActive() {
return _active;
}
/** /**
* Called by the application during it's idle phase. If the plugin needs to do * Called by the application during it's idle phase. If the plugin needs to do
@ -48,6 +57,7 @@ public:
virtual void loadSettings() {} virtual void loadSettings() {}
protected: protected:
bool _active{ false };
static PluginContainer* CONTAINER; static PluginContainer* CONTAINER;
static QString UNKNOWN_PLUGIN_ID; static QString UNKNOWN_PLUGIN_ID;

View file

@ -2,7 +2,7 @@
set(TARGET_NAME controllers-test) set(TARGET_NAME controllers-test)
# This is not a testcase -- just set it up as a regular hifi project # This is not a testcase -- just set it up as a regular hifi project
setup_hifi_project(Script) setup_hifi_project(Script Qml)
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
# link in the shared libraries # link in the shared libraries

View file

@ -8,10 +8,21 @@ import "./controls"
Column { Column {
id: root id: root
property var xbox: NewControllers.Hardware.X360Controller1
property var actions: NewControllers.Actions property var actions: NewControllers.Actions
property var standard: NewControllers.Standard property var standard: NewControllers.Standard
property string mappingName: "TestMapping" property var testMapping: null
property var xbox: null
Component.onCompleted: {
var patt = /^X360Controller/;
for (var prop in NewControllers.Hardware) {
if(patt.test(prop)) {
root.xbox = NewControllers.Hardware[prop]
break
}
}
}
spacing: 12 spacing: 12
@ -49,13 +60,15 @@ Column {
mapping.from(xbox.LT).to(standard.LT); mapping.from(xbox.LT).to(standard.LT);
mapping.from(xbox.RT).to(standard.RT); mapping.from(xbox.RT).to(standard.RT);
NewControllers.enableMapping("Default"); NewControllers.enableMapping("Default");
enabled = false;
text = "Built"
} }
} }
Button { Button {
text: "Build Mapping" text: "Build Mapping"
onClicked: { onClicked: {
var mapping = NewControllers.newMapping(root.mappingName); var mapping = NewControllers.newMapping();
// Inverting a value // Inverting a value
mapping.from(xbox.RY).invert().to(standard.RY); mapping.from(xbox.RY).invert().to(standard.RY);
// Assigning a value from a function // Assigning a value from a function
@ -63,17 +76,22 @@ Column {
// Constrainting a value to -1, 0, or 1, with a deadzone // Constrainting a value to -1, 0, or 1, with a deadzone
mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY); mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY);
mapping.join(standard.LB, standard.RB).pulse(0.5).to(actions.Yaw); mapping.join(standard.LB, standard.RB).pulse(0.5).to(actions.Yaw);
mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT);
mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT);
testMapping = mapping;
enabled = false
text = "Built"
} }
} }
Button { Button {
text: "Enable Mapping" text: "Enable Mapping"
onClicked: NewControllers.enableMapping(root.mappingName) onClicked: root.testMapping.enable()
} }
Button { Button {
text: "Disable Mapping" text: "Disable Mapping"
onClicked: NewControllers.disableMapping(root.mappingName) onClicked: root.testMapping.disable()
} }
} }
@ -85,6 +103,7 @@ Column {
Row { Row {
spacing: 8
ScrollingGraph { ScrollingGraph {
controlId: NewControllers.Actions.Yaw controlId: NewControllers.Actions.Yaw
label: "Yaw" label: "Yaw"
@ -92,6 +111,22 @@ Column {
max: 3.0 max: 3.0
size: 128 size: 128
} }
ScrollingGraph {
controlId: NewControllers.Actions.YAW_LEFT
label: "Yaw Left"
min: -3.0
max: 3.0
size: 128
}
ScrollingGraph {
controlId: NewControllers.Actions.YAW_RIGHT
label: "Yaw Right"
min: -3.0
max: 3.0
size: 128
}
} }
} }

View file

@ -82,6 +82,9 @@ public:
int main(int argc, char** argv) { int main(int argc, char** argv) {
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
for (auto path : qApp->libraryPaths()) {
qDebug() << path;
}
for (auto path : qApp->libraryPaths()) { for (auto path : qApp->libraryPaths()) {
qDebug() << path; qDebug() << path;

View file

@ -52,7 +52,6 @@
}; };
MasterReset = function() { MasterReset = function() {
var resetKey = "resetMe"; var resetKey = "resetMe";
var GRABBABLE_DATA_KEY = "grabbableKey"; var GRABBABLE_DATA_KEY = "grabbableKey";
@ -407,11 +406,6 @@
type: 'Box', type: 'Box',
position: startPosition, position: startPosition,
dimensions: TARGET_DIMENSIONS, dimensions: TARGET_DIMENSIONS,
color: {
red: 0,
green: 255,
blue: 0
},
rotation: rotation, rotation: rotation,
visible: false, visible: false,
collisionsWillMove: false, collisionsWillMove: false,
@ -658,6 +652,7 @@
green: 146, green: 146,
blue: 24 blue: 24
}, },
isSpotlight: false,
userData: JSON.stringify({ userData: JSON.stringify({
resetMe: { resetMe: {
resetMe: true, resetMe: true,
@ -685,6 +680,7 @@
green: 146, green: 146,
blue: 24 blue: 24
}, },
isSpotlight: false,
userData: JSON.stringify({ userData: JSON.stringify({
resetMe: { resetMe: {
resetMe: true, resetMe: true,
@ -734,7 +730,6 @@
var sconceLight3 = Entities.addEntity({ var sconceLight3 = Entities.addEntity({
type: "Light", type: "Light",
position: { position: {
@ -755,6 +750,7 @@
green: 146, green: 146,
blue: 24 blue: 24
}, },
isSpotlight: false,
userData: JSON.stringify({ userData: JSON.stringify({
resetMe: { resetMe: {
resetMe: true, resetMe: true,
@ -783,6 +779,7 @@
green: 146, green: 146,
blue: 24 blue: 24
}, },
isSpotlight: false,
userData: JSON.stringify({ userData: JSON.stringify({
resetMe: { resetMe: {
resetMe: true, resetMe: true,
@ -810,6 +807,7 @@
green: 146, green: 146,
blue: 24 blue: 24
}, },
isSpotlight: false,
userData: JSON.stringify({ userData: JSON.stringify({
resetMe: { resetMe: {
resetMe: true, resetMe: true,
@ -888,7 +886,7 @@
y: 1.13, y: 1.13,
z: 0.2 z: 0.2
}, },
rotation: rotation2, rotation: rotation,
collisionsWillMove: true, collisionsWillMove: true,
gravity: { gravity: {
x: 0, x: 0,
@ -1256,7 +1254,7 @@
y: 0.05, y: 0.05,
z: 0.25 z: 0.25
} }
},]; }, ];
var modelURL, entity; var modelURL, entity;
for (i = 0; i < blockTypes.length; i++) { for (i = 0; i < blockTypes.length; i++) {
@ -1305,7 +1303,6 @@
Script.scriptEnding.connect(cleanup); Script.scriptEnding.connect(cleanup);
} }
}; };
// entity scripts always need to return a newly constructed object of our type // entity scripts always need to return a newly constructed object of our type
return new ResetSwitch(); return new ResetSwitch();
}); });

View file

@ -625,6 +625,7 @@ MasterReset = function() {
green: 146, green: 146,
blue: 24 blue: 24
}, },
isSpotlight: false,
userData: JSON.stringify({ userData: JSON.stringify({
resetMe: { resetMe: {
resetMe: true, resetMe: true,
@ -652,6 +653,7 @@ MasterReset = function() {
green: 146, green: 146,
blue: 24 blue: 24
}, },
isSpotlight: false,
userData: JSON.stringify({ userData: JSON.stringify({
resetMe: { resetMe: {
resetMe: true, resetMe: true,
@ -701,7 +703,6 @@ MasterReset = function() {
var sconceLight3 = Entities.addEntity({ var sconceLight3 = Entities.addEntity({
type: "Light", type: "Light",
position: { position: {
@ -722,6 +723,7 @@ MasterReset = function() {
green: 146, green: 146,
blue: 24 blue: 24
}, },
isSpotlight: false,
userData: JSON.stringify({ userData: JSON.stringify({
resetMe: { resetMe: {
resetMe: true, resetMe: true,
@ -750,6 +752,7 @@ MasterReset = function() {
green: 146, green: 146,
blue: 24 blue: 24
}, },
isSpotlight: false,
userData: JSON.stringify({ userData: JSON.stringify({
resetMe: { resetMe: {
resetMe: true, resetMe: true,
@ -777,6 +780,7 @@ MasterReset = function() {
green: 146, green: 146,
blue: 24 blue: 24
}, },
isSpotlight: false,
userData: JSON.stringify({ userData: JSON.stringify({
resetMe: { resetMe: {
resetMe: true, resetMe: true,
@ -1223,7 +1227,7 @@ MasterReset = function() {
y: 0.05, y: 0.05,
z: 0.25 z: 0.25
} }
},]; }, ];
var modelURL, entity; var modelURL, entity;
for (i = 0; i < blockTypes.length; i++) { for (i = 0; i < blockTypes.length; i++) {
@ -1271,4 +1275,4 @@ MasterReset = function() {
Script.scriptEnding.connect(cleanup); Script.scriptEnding.connect(cleanup);
} }
}; };