mirror of
https://github.com/overte-org/overte.git
synced 2025-04-18 00:26:33 +02:00
Merge branch 'master' into tony/grab-script-hand-animation
This commit is contained in:
commit
c2736cb68c
57 changed files with 1186 additions and 800 deletions
148
examples/tests/avatarAttachmentTest.js
Normal file
148
examples/tests/avatarAttachmentTest.js
Normal file
|
@ -0,0 +1,148 @@
|
|||
//
|
||||
// avatarAttachmentTest.js
|
||||
// examples/tests
|
||||
//
|
||||
// Created by Anthony Thibault on January 7, 2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// Test for MyAvatar attachment API
|
||||
// MyAvatar.setAttachmentData();
|
||||
// MyAvatar.getAttachmentData();
|
||||
|
||||
// Toggle button helper
|
||||
function ToggleButtonBuddy(x, y, width, height, urls) {
|
||||
this.up = Overlays.addOverlay("image", {
|
||||
x: x, y: y, width: width, height: height,
|
||||
subImage: { x: 0, y: height, width: width, height: height},
|
||||
imageURL: urls.up,
|
||||
visible: true,
|
||||
alpha: 1.0
|
||||
});
|
||||
this.down = Overlays.addOverlay("image", {
|
||||
x: x, y: y, width: width, height: height,
|
||||
subImage: { x: 0, y: height, width: width, height: height},
|
||||
imageURL: urls.down,
|
||||
visible: false,
|
||||
alpha: 1.0
|
||||
});
|
||||
this.callbacks = [];
|
||||
|
||||
var self = this;
|
||||
Controller.mousePressEvent.connect(function (event) {
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||
if (clickedOverlay === self.up) {
|
||||
// flip visiblity
|
||||
Overlays.editOverlay(self.up, {visible: false});
|
||||
Overlays.editOverlay(self.down, {visible: true});
|
||||
self.onToggle(true);
|
||||
} else if (clickedOverlay === self.down) {
|
||||
// flip visiblity
|
||||
Overlays.editOverlay(self.up, {visible: true});
|
||||
Overlays.editOverlay(self.down, {visible: false});
|
||||
self.onToggle(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
ToggleButtonBuddy.prototype.destroy = function () {
|
||||
Overlays.deleteOverlay(this.up);
|
||||
Overlays.deleteOverlay(this.down);
|
||||
};
|
||||
ToggleButtonBuddy.prototype.addToggleHandler = function (callback) {
|
||||
this.callbacks.push(callback);
|
||||
return callback;
|
||||
};
|
||||
ToggleButtonBuddy.prototype.removeToggleHandler = function (callback) {
|
||||
var index = this.callbacks.indexOf(callback);
|
||||
if (index != -1) {
|
||||
this.callbacks.splice(index, 1);
|
||||
}
|
||||
};
|
||||
ToggleButtonBuddy.prototype.onToggle = function (isDown) {
|
||||
var i, l = this.callbacks.length;
|
||||
for (i = 0; i < l; i++) {
|
||||
this.callbacks[i](isDown);
|
||||
}
|
||||
};
|
||||
|
||||
var windowDimensions = Controller.getViewportDimensions();
|
||||
var BUTTON_WIDTH = 64;
|
||||
var BUTTON_HEIGHT = 64;
|
||||
var BUTTON_PADDING = 10;
|
||||
var buttonPositionX = windowDimensions.x - BUTTON_PADDING - BUTTON_WIDTH;
|
||||
var buttonPositionY = (windowDimensions.y - BUTTON_HEIGHT) / 2 - (BUTTON_HEIGHT + BUTTON_PADDING);
|
||||
|
||||
var hatButton = new ToggleButtonBuddy(buttonPositionX, buttonPositionY, BUTTON_WIDTH, BUTTON_HEIGHT, {
|
||||
up: "https://s3.amazonaws.com/hifi-public/tony/icons/hat-up.svg",
|
||||
down: "https://s3.amazonaws.com/hifi-public/tony/icons/hat-down.svg"
|
||||
});
|
||||
|
||||
buttonPositionY += BUTTON_HEIGHT + BUTTON_PADDING;
|
||||
var coatButton = new ToggleButtonBuddy(buttonPositionX, buttonPositionY, BUTTON_WIDTH, BUTTON_HEIGHT, {
|
||||
up: "https://s3.amazonaws.com/hifi-public/tony/icons/coat-up.svg",
|
||||
down: "https://s3.amazonaws.com/hifi-public/tony/icons/coat-down.svg"
|
||||
});
|
||||
|
||||
var HAT_ATTACHMENT = {
|
||||
modelURL: "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx",
|
||||
jointName: "Head",
|
||||
translation: {"x": 0, "y": 0.2, "z": 0},
|
||||
rotation: {"x": 0, "y": 0, "z": 0, "w": 1},
|
||||
scale: 1,
|
||||
isSoft: false
|
||||
};
|
||||
|
||||
var COAT_ATTACHMENT = {
|
||||
modelURL: "https://hifi-content.s3.amazonaws.com/ozan/dev/clothes/coat/coat_rig.fbx",
|
||||
jointName: "Hips",
|
||||
translation: {"x": 0, "y": 0, "z": 0},
|
||||
rotation: {"x": 0, "y": 0, "z": 0, "w": 1},
|
||||
scale: 1,
|
||||
isSoft: true
|
||||
};
|
||||
|
||||
hatButton.addToggleHandler(function (isDown) {
|
||||
if (isDown) {
|
||||
wearAttachment(HAT_ATTACHMENT);
|
||||
} else {
|
||||
removeAttachment(HAT_ATTACHMENT);
|
||||
}
|
||||
});
|
||||
|
||||
coatButton.addToggleHandler(function (isDown) {
|
||||
if (isDown) {
|
||||
wearAttachment(COAT_ATTACHMENT);
|
||||
} else {
|
||||
removeAttachment(COAT_ATTACHMENT);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function wearAttachment(attachment) {
|
||||
MyAvatar.attach(attachment.modelURL,
|
||||
attachment.jointName,
|
||||
attachment.translation,
|
||||
attachment.rotation,
|
||||
attachment.scale,
|
||||
attachment.isSoft);
|
||||
}
|
||||
|
||||
function removeAttachment(attachment) {
|
||||
var attachments = MyAvatar.attachmentData;
|
||||
var i, l = attachments.length;
|
||||
for (i = 0; i < l; i++) {
|
||||
if (attachments[i].modelURL === attachment.modelURL) {
|
||||
attachments.splice(i, 1);
|
||||
MyAvatar.attachmentData = attachments;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
hatButton.destroy();
|
||||
coatbutton.destroy();
|
||||
});
|
|
@ -8,33 +8,6 @@ qmlWindow = new OverlayWindow({
|
|||
visible: true
|
||||
});
|
||||
|
||||
//qmlWindow.eventBridge.webEventReceived.connect(function(data) {
|
||||
// print("JS Side event received: " + data);
|
||||
//});
|
||||
//
|
||||
//var titles = ["A", "B", "C"];
|
||||
//var titleIndex = 0;
|
||||
//
|
||||
//Script.setInterval(function() {
|
||||
// qmlWindow.eventBridge.emitScriptEvent("JS Event sent");
|
||||
// var size = qmlWindow.size;
|
||||
// var position = qmlWindow.position;
|
||||
// print("Window visible: " + qmlWindow.visible)
|
||||
// if (qmlWindow.visible) {
|
||||
// print("Window size: " + size.x + "x" + size.y)
|
||||
// print("Window pos: " + position.x + "x" + position.y)
|
||||
// qmlWindow.setVisible(false);
|
||||
// } else {
|
||||
// qmlWindow.setVisible(true);
|
||||
// qmlWindow.setTitle(titles[titleIndex]);
|
||||
// qmlWindow.setSize(320 + Math.random() * 100, 240 + Math.random() * 100);
|
||||
// titleIndex += 1;
|
||||
// titleIndex %= titles.length;
|
||||
// }
|
||||
//}, 2 * 1000);
|
||||
//
|
||||
//Script.setTimeout(function() {
|
||||
// print("Closing script");
|
||||
// qmlWindow.close();
|
||||
// Script.stop();
|
||||
//}, 15 * 1000)
|
||||
Script.setInterval(function() {
|
||||
qmlWindow.raise();
|
||||
}, 2 * 1000);
|
||||
|
|
|
@ -16,6 +16,7 @@ import "styles"
|
|||
DialogContainer {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
z: 1000
|
||||
|
||||
objectName: "AddressBarDialog"
|
||||
|
||||
|
@ -150,6 +151,17 @@ DialogContainer {
|
|||
addressLine.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
running: root.enabled
|
||||
interval: 500
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (root.enabled && !addressLine.activeFocus) {
|
||||
addressLine.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
|
|
|
@ -10,21 +10,32 @@ import Hifi 1.0 as Hifi
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Controls 1.3
|
||||
import QtGraphicalEffects 1.0
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
Hifi.AvatarInputs {
|
||||
id: root
|
||||
objectName: "AvatarInputs"
|
||||
anchors.fill: parent
|
||||
width: mirrorWidth
|
||||
height: controls.height + mirror.height
|
||||
x: 10; y: 5
|
||||
|
||||
// width: 800
|
||||
// height: 600
|
||||
// color: "black"
|
||||
readonly property int iconPadding: 5
|
||||
readonly property int mirrorHeight: 215
|
||||
readonly property int mirrorWidth: 265
|
||||
readonly property int mirrorTopPad: iconPadding
|
||||
readonly property int mirrorLeftPad: 10
|
||||
readonly property int iconSize: 24
|
||||
readonly property int iconPadding: 5
|
||||
|
||||
Settings {
|
||||
category: "Overlay.AvatarInputs"
|
||||
property alias x: root.x
|
||||
property alias y: root.y
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: hover
|
||||
hoverEnabled: true
|
||||
drag.target: parent
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Item {
|
||||
id: mirror
|
||||
|
@ -32,18 +43,8 @@ Hifi.AvatarInputs {
|
|||
height: root.mirrorVisible ? root.mirrorHeight : 0
|
||||
visible: root.mirrorVisible
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.mirrorLeftPad
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: root.mirrorTopPad
|
||||
clip: true
|
||||
|
||||
MouseArea {
|
||||
id: hover
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: true
|
||||
}
|
||||
|
||||
Image {
|
||||
id: closeMirror
|
||||
visible: hover.containsMouse
|
||||
|
@ -64,7 +65,7 @@ Hifi.AvatarInputs {
|
|||
|
||||
Image {
|
||||
id: zoomIn
|
||||
visible: hover.containsMouse
|
||||
visible: hover.containsMouse
|
||||
width: root.iconSize
|
||||
height: root.iconSize
|
||||
anchors.bottom: parent.bottom
|
||||
|
@ -82,14 +83,11 @@ Hifi.AvatarInputs {
|
|||
}
|
||||
|
||||
Item {
|
||||
id: controls
|
||||
width: root.mirrorWidth
|
||||
height: 44
|
||||
visible: !root.isHMD
|
||||
|
||||
x: root.mirrorLeftPad
|
||||
y: root.mirrorVisible ? root.mirrorTopPad + root.mirrorHeight : 5
|
||||
|
||||
|
||||
anchors.top: mirror.bottom
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
|
165
interface/resources/qml/Global.js
Normal file
165
interface/resources/qml/Global.js
Normal file
|
@ -0,0 +1,165 @@
|
|||
var OFFSCREEN_ROOT_OBJECT_NAME = "desktopRoot"
|
||||
var OFFSCREEN_WINDOW_OBJECT_NAME = "topLevelWindow"
|
||||
|
||||
function findChild(item, name) {
|
||||
for (var i = 0; i < item.children.length; ++i) {
|
||||
if (item.children[i].objectName === name) {
|
||||
return item.children[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findParent(item, name) {
|
||||
while (item) {
|
||||
if (item.objectName === name) {
|
||||
return item;
|
||||
}
|
||||
item = item.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getDesktop(item) {
|
||||
return findParent(item, OFFSCREEN_ROOT_OBJECT_NAME);
|
||||
}
|
||||
|
||||
function findRootMenu(item) {
|
||||
item = getDesktop(item);
|
||||
return item ? item.rootMenu : null;
|
||||
}
|
||||
|
||||
|
||||
function getTopLevelWindows(item) {
|
||||
var desktop = getDesktop(item);
|
||||
var currentWindows = [];
|
||||
if (!desktop) {
|
||||
console.log("Could not find desktop for " + item)
|
||||
return currentWindows;
|
||||
}
|
||||
|
||||
for (var i = 0; i < desktop.children.length; ++i) {
|
||||
var child = desktop.children[i];
|
||||
if (Global.OFFSCREEN_WINDOW_OBJECT_NAME === child.objectName) {
|
||||
var windowId = child.toString();
|
||||
currentWindows.push(child)
|
||||
}
|
||||
}
|
||||
return currentWindows;
|
||||
}
|
||||
|
||||
|
||||
function getDesktopWindow(item) {
|
||||
item = findParent(item, OFFSCREEN_WINDOW_OBJECT_NAME);
|
||||
return item;
|
||||
}
|
||||
|
||||
function closeWindow(item) {
|
||||
item = findDialog(item);
|
||||
if (item) {
|
||||
item.enabled = false
|
||||
} else {
|
||||
console.warn("Could not find top level dialog")
|
||||
}
|
||||
}
|
||||
|
||||
function findMenuChild(menu, childName) {
|
||||
if (!menu) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (menu.type !== 2) {
|
||||
console.warn("Tried to find child of a non-menu");
|
||||
return null;
|
||||
}
|
||||
|
||||
var items = menu.items;
|
||||
var count = items.length;
|
||||
for (var i = 0; i < count; ++i) {
|
||||
var child = items[i];
|
||||
var name;
|
||||
switch (child.type) {
|
||||
case 2:
|
||||
name = child.title;
|
||||
break;
|
||||
case 1:
|
||||
name = child.text;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (name && name === childName) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findMenu(rootMenu, path) {
|
||||
if ('string' === typeof(path)) {
|
||||
path = [ path ]
|
||||
}
|
||||
|
||||
var currentMenu = rootMenu;
|
||||
for (var i = 0; currentMenu && i < path.length; ++i) {
|
||||
currentMenu = findMenuChild(currentMenu, path[i]);
|
||||
}
|
||||
|
||||
return currentMenu;
|
||||
}
|
||||
|
||||
function findInRootMenu(item, path) {
|
||||
return findMenu(findRootMenu(item), path);
|
||||
}
|
||||
|
||||
|
||||
function menuItemsToListModel(parent, items) {
|
||||
var newListModel = Qt.createQmlObject('import QtQuick 2.5; ListModel {}', parent);
|
||||
for (var i = 0; i < items.length; ++i) {
|
||||
var item = items[i];
|
||||
switch (item.type) {
|
||||
case 2:
|
||||
newListModel.append({"type":item.type, "name": item.title, "item": item})
|
||||
break;
|
||||
case 1:
|
||||
newListModel.append({"type":item.type, "name": item.text, "item": item})
|
||||
break;
|
||||
case 0:
|
||||
newListModel.append({"type":item.type, "name": "-----", "item": item})
|
||||
break;
|
||||
}
|
||||
}
|
||||
return newListModel;
|
||||
}
|
||||
|
||||
function raiseWindow(item) {
|
||||
var targetWindow = getDesktopWindow(item);
|
||||
if (!targetWindow) {
|
||||
console.warn("Could not find top level window for " + item);
|
||||
return;
|
||||
}
|
||||
|
||||
var desktop = getDesktop(targetWindow);
|
||||
if (!desktop) {
|
||||
//console.warn("Could not find desktop for window " + targetWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
var maxZ = 0;
|
||||
var minZ = 100000;
|
||||
var windows = desktop.windows;
|
||||
windows.sort(function(a, b){
|
||||
return a.z - b.z;
|
||||
});
|
||||
var lastZ = -1;
|
||||
var lastTargetZ = -1;
|
||||
for (var i = 0; i < windows.length; ++i) {
|
||||
var window = windows[i];
|
||||
if (window.z > lastZ) {
|
||||
lastZ = window.z;
|
||||
++lastTargetZ;
|
||||
}
|
||||
window.z = lastTargetZ;
|
||||
}
|
||||
targetWindow.z = lastTargetZ + 1;
|
||||
}
|
|
@ -47,10 +47,20 @@ VrDialog {
|
|||
anchors.fill: parent
|
||||
focus: true
|
||||
|
||||
property var originalUrl
|
||||
property var lastFixupTime: 0
|
||||
|
||||
onUrlChanged: {
|
||||
var currentUrl = url.toString();
|
||||
var newUrl = urlHandler.fixupUrl(currentUrl).toString();
|
||||
if (newUrl != currentUrl) {
|
||||
var now = new Date().valueOf();
|
||||
if (url === originalUrl && (now - lastFixupTime < 100)) {
|
||||
console.warn("URL fixup loop detected")
|
||||
return;
|
||||
}
|
||||
originalUrl = url
|
||||
lastFixupTime = now
|
||||
url = newUrl;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import QtWebChannel 1.0
|
|||
import QtWebSockets 1.0
|
||||
import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel
|
||||
|
||||
import "Global.js" as Global
|
||||
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
|
@ -23,6 +25,10 @@ VrDialog {
|
|||
contentImplicitWidth: clientArea.implicitWidth
|
||||
contentImplicitHeight: clientArea.implicitHeight
|
||||
property alias source: pageLoader.source
|
||||
|
||||
function raiseWindow() {
|
||||
Global.raiseWindow(root)
|
||||
}
|
||||
|
||||
Item {
|
||||
id: clientArea
|
||||
|
|
|
@ -2,16 +2,23 @@ import Hifi 1.0
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
import "Global.js" as Global
|
||||
|
||||
// This is our primary 'window' object to which all dialogs and controls will
|
||||
// be childed.
|
||||
Root {
|
||||
id: root
|
||||
objectName: "desktopRoot"
|
||||
anchors.fill: parent
|
||||
objectName: Global.OFFSCREEN_ROOT_OBJECT_NAME
|
||||
anchors.fill: parent;
|
||||
property var windows: [];
|
||||
property var rootMenu: Menu {
|
||||
objectName: "rootMenu"
|
||||
}
|
||||
|
||||
onChildrenChanged: {
|
||||
windows = Global.getTopLevelWindows(root);
|
||||
}
|
||||
|
||||
onParentChanged: {
|
||||
forceActiveFocus();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ import QtQuick.Controls 1.2
|
|||
import "."
|
||||
import "../styles"
|
||||
|
||||
import "../Global.js" as Global
|
||||
|
||||
/*
|
||||
* FIXME Need to create a client property here so that objects can be
|
||||
* placed in it without having to think about positioning within the outer
|
||||
|
@ -45,9 +47,18 @@ DialogBase {
|
|||
// trigger making it visible.
|
||||
if (enabled) {
|
||||
visible = true;
|
||||
if (root.parent) {
|
||||
Global.raiseWindow(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onParentChanged: {
|
||||
if (enabled && parent) {
|
||||
Global.raiseWindow(root);
|
||||
}
|
||||
}
|
||||
|
||||
// The actual animator
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
|
@ -60,6 +71,12 @@ DialogBase {
|
|||
onOpacityChanged: {
|
||||
visible = (opacity != 0.0);
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (visible) {
|
||||
Global.raiseWindow(root);
|
||||
}
|
||||
}
|
||||
|
||||
// Some dialogs should be destroyed when they become invisible,
|
||||
// so handle that
|
||||
|
@ -118,6 +135,7 @@ DialogBase {
|
|||
y: root.titleY
|
||||
width: root.titleWidth
|
||||
height: root.titleHeight
|
||||
onClicked: Global.raiseWindow(root)
|
||||
|
||||
drag {
|
||||
target: root
|
||||
|
|
|
@ -696,7 +696,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {
|
||||
using namespace controller;
|
||||
static auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
if (offscreenUi->navigationFocused()) {
|
||||
auto actionEnum = static_cast<Action>(action);
|
||||
int key = Qt::Key_unknown;
|
||||
|
@ -737,6 +737,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
case Action::UI_NAV_SELECT:
|
||||
key = Qt::Key_Return;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (navAxis) {
|
||||
|
@ -834,7 +836,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
return (float)qApp->getMyAvatar()->getCharacterController()->onGround();
|
||||
}));
|
||||
_applicationStateDevice->addInputVariant(QString("NavigationFocused"), controller::StateController::ReadLambda([]() -> float {
|
||||
static auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->navigationFocused() ? 1.0 : 0.0;
|
||||
}));
|
||||
|
||||
|
@ -1049,6 +1051,8 @@ void Application::cleanupBeforeQuit() {
|
|||
#ifdef HAVE_IVIEWHMD
|
||||
DependencyManager::destroy<EyeTracker>();
|
||||
#endif
|
||||
|
||||
DependencyManager::destroy<OffscreenUi>();
|
||||
}
|
||||
|
||||
void Application::emptyLocalCache() {
|
||||
|
@ -1082,8 +1086,8 @@ Application::~Application() {
|
|||
// remove avatars from physics engine
|
||||
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
|
||||
VectorOfMotionStates motionStates;
|
||||
DependencyManager::get<AvatarManager>()->getObjectsToDelete(motionStates);
|
||||
_physicsEngine->deleteObjects(motionStates);
|
||||
DependencyManager::get<AvatarManager>()->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
|
||||
DependencyManager::destroy<OffscreenUi>();
|
||||
DependencyManager::destroy<AvatarManager>();
|
||||
|
@ -1336,7 +1340,7 @@ void Application::paintGL() {
|
|||
renderArgs._context->syncCache();
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror)) {
|
||||
PerformanceTimer perfTimer("Mirror");
|
||||
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebuffer();
|
||||
|
||||
|
@ -1625,7 +1629,7 @@ void Application::aboutApp() {
|
|||
InfoView::show(INFO_HELP_PATH);
|
||||
}
|
||||
|
||||
void Application::showEditEntitiesHelp() {
|
||||
void Application::showHelp() {
|
||||
InfoView::show(INFO_EDIT_ENTITIES_PATH);
|
||||
}
|
||||
|
||||
|
@ -1983,7 +1987,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
|
||||
case Qt::Key_H:
|
||||
if (isShifted) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Mirror);
|
||||
Menu::getInstance()->triggerOption(MenuOption::MiniMirror);
|
||||
} else {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror));
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
|
@ -3083,11 +3087,11 @@ void Application::update(float deltaTime) {
|
|||
PerformanceTimer perfTimer("physics");
|
||||
|
||||
static VectorOfMotionStates motionStates;
|
||||
_entitySimulation.getObjectsToDelete(motionStates);
|
||||
_physicsEngine->deleteObjects(motionStates);
|
||||
_entitySimulation.getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_entitySimulation.getObjectsToAdd(motionStates);
|
||||
_entitySimulation.getObjectsToAddToPhysics(motionStates);
|
||||
_physicsEngine->addObjects(motionStates);
|
||||
|
||||
});
|
||||
|
@ -3100,9 +3104,9 @@ void Application::update(float deltaTime) {
|
|||
_entitySimulation.applyActionChanges();
|
||||
|
||||
AvatarManager* avatarManager = DependencyManager::get<AvatarManager>().data();
|
||||
avatarManager->getObjectsToDelete(motionStates);
|
||||
_physicsEngine->deleteObjects(motionStates);
|
||||
avatarManager->getObjectsToAdd(motionStates);
|
||||
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
avatarManager->getObjectsToAddToPhysics(motionStates);
|
||||
_physicsEngine->addObjects(motionStates);
|
||||
avatarManager->getObjectsToChange(motionStates);
|
||||
_physicsEngine->changeObjects(motionStates);
|
||||
|
@ -3739,7 +3743,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
auto backgroundRenderData = make_shared<BackgroundRenderData>(&_environment);
|
||||
auto backgroundRenderPayload = make_shared<BackgroundRenderData::Payload>(backgroundRenderData);
|
||||
BackgroundRenderData::_item = _main3DScene->allocateID();
|
||||
pendingChanges.resetItem(WorldBoxRenderData::_item, backgroundRenderPayload);
|
||||
pendingChanges.resetItem(BackgroundRenderData::_item, backgroundRenderPayload);
|
||||
} else {
|
||||
|
||||
}
|
||||
|
@ -4081,7 +4085,7 @@ bool Application::nearbyEntitiesAreReadyForPhysics() {
|
|||
});
|
||||
|
||||
foreach (EntityItemPointer entity, entities) {
|
||||
if (!entity->isReadyToComputeShape()) {
|
||||
if (entity->shouldBePhysical() && !entity->isReadyToComputeShape()) {
|
||||
static QString repeatedMessage =
|
||||
LogHandler::getInstance().addRepeatedMessageRegex("Physics disabled until entity loads: .*");
|
||||
qCDebug(interfaceapp) << "Physics disabled until entity loads: " << entity->getID() << entity->getName();
|
||||
|
|
|
@ -295,7 +295,7 @@ public slots:
|
|||
#endif
|
||||
|
||||
void aboutApp();
|
||||
void showEditEntitiesHelp();
|
||||
void showHelp();
|
||||
|
||||
void cycleCamera();
|
||||
void cameraMenuChanged();
|
||||
|
|
|
@ -51,13 +51,16 @@ Menu* Menu::getInstance() {
|
|||
}
|
||||
|
||||
Menu::Menu() {
|
||||
MenuWrapper * fileMenu = addMenu("File");
|
||||
#ifdef Q_OS_MAC
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole);
|
||||
#endif
|
||||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
// File/Application menu ----------------------------------
|
||||
MenuWrapper* fileMenu = addMenu("File");
|
||||
|
||||
// File > Quit
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, qApp,SLOT(quit()), QAction::QuitRole);
|
||||
|
||||
// File > Login menu items
|
||||
{
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Login);
|
||||
|
||||
|
@ -68,256 +71,269 @@ Menu::Menu() {
|
|||
dialogsManager.data(), &DialogsManager::toggleLoginDialog);
|
||||
}
|
||||
|
||||
// File Menu > Scripts section -- "Advanced" grouping
|
||||
addDisabledActionAndSeparator(fileMenu, "Scripts", UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O,
|
||||
qApp, SLOT(loadDialog()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScriptURL,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_O, qApp, SLOT(loadScriptURLDialog()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, qApp, SLOT(stopAllScripts()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R,
|
||||
qApp, SLOT(reloadAllScripts()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J,
|
||||
qApp, SLOT(toggleRunningScriptsWidget()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
// File > Update -- FIXME: needs implementation
|
||||
auto updateAction = addActionToQMenuAndActionHash(fileMenu, "Update");
|
||||
updateAction->setDisabled(true);
|
||||
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
// File > Help
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Help, 0, qApp, SLOT(showHelp()));
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "History");
|
||||
// File > Crash Reporter...-- FIXME: needs implementation
|
||||
auto crashReporterAction = addActionToQMenuAndActionHash(fileMenu, "Crash Reporter...");
|
||||
crashReporterAction->setDisabled(true);
|
||||
|
||||
QAction* backAction = addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Back,
|
||||
0,
|
||||
addressManager.data(),
|
||||
SLOT(goBack()));
|
||||
|
||||
QAction* forwardAction = addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Forward,
|
||||
0,
|
||||
addressManager.data(),
|
||||
SLOT(goForward()));
|
||||
|
||||
// connect to the AddressManager signal to enable and disable the back and forward menu items
|
||||
connect(addressManager.data(), &AddressManager::goBackPossible, backAction, &QAction::setEnabled);
|
||||
connect(addressManager.data(), &AddressManager::goForwardPossible, forwardAction, &QAction::setEnabled);
|
||||
|
||||
// set the two actions to start disabled since the stacks are clear on startup
|
||||
backAction->setDisabled(true);
|
||||
forwardAction->setDisabled(true);
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Location");
|
||||
qApp->getBookmarks()->setupMenus(this, fileMenu);
|
||||
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::AddressBar,
|
||||
Qt::CTRL | Qt::Key_L,
|
||||
dialogsManager.data(),
|
||||
SLOT(toggleAddressBar()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyAddress, 0,
|
||||
addressManager.data(), SLOT(copyAddress()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0,
|
||||
addressManager.data(), SLOT(copyPath()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Quit,
|
||||
Qt::CTRL | Qt::Key_Q,
|
||||
qApp,
|
||||
SLOT(quit()),
|
||||
QAction::QuitRole);
|
||||
// File > About
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole);
|
||||
|
||||
|
||||
// Audio menu ----------------------------------
|
||||
MenuWrapper* audioMenu = addMenu("Audio");
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
|
||||
// Audio > Mute
|
||||
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false,
|
||||
audioIO.data(), SLOT(toggleMute()));
|
||||
|
||||
// Audio > Level Meter [advanced] -- FIXME: needs implementation
|
||||
auto levelMeterAction = addCheckableActionToQMenuAndActionHash(audioMenu, "Level Meter", 0, false, NULL, NULL, UNSPECIFIED_POSITION, "Advanced");
|
||||
levelMeterAction->setDisabled(true);
|
||||
|
||||
|
||||
// Avatar menu ----------------------------------
|
||||
MenuWrapper* avatarMenu = addMenu("Avatar");
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
QObject* avatar = avatarManager->getMyAvatar();
|
||||
|
||||
// Avatar > Attachments...
|
||||
addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments, 0,
|
||||
dialogsManager.data(), SLOT(editAttachments()));
|
||||
|
||||
// Avatar > Size
|
||||
MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size");
|
||||
|
||||
// Avatar > Size > Increase
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::IncreaseAvatarSize,
|
||||
0, // QML Qt::Key_Plus,
|
||||
avatar, SLOT(increaseSize()));
|
||||
|
||||
// Avatar > Size > Decrease
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::DecreaseAvatarSize,
|
||||
0, // QML Qt::Key_Minus,
|
||||
avatar, SLOT(decreaseSize()));
|
||||
|
||||
// Avatar > Size > Reset
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::ResetAvatarSize,
|
||||
0, // QML Qt::Key_Equal,
|
||||
avatar, SLOT(resetSize()));
|
||||
|
||||
// Avatar > Reset Sensors
|
||||
addActionToQMenuAndActionHash(avatarMenu,
|
||||
MenuOption::ResetSensors,
|
||||
0, // QML Qt::Key_Apostrophe,
|
||||
qApp, SLOT(resetSensors()));
|
||||
|
||||
|
||||
// Display menu ----------------------------------
|
||||
// FIXME - this is not yet matching Alan's spec because it doesn't have
|
||||
// menus for "2D"/"3D" - we need to add support for detecting the appropriate
|
||||
// default 3D display mode
|
||||
addMenu(DisplayPlugin::MENU_PATH());
|
||||
MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu);
|
||||
QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu);
|
||||
displayModeGroup->setExclusive(true);
|
||||
|
||||
|
||||
// View menu ----------------------------------
|
||||
MenuWrapper* viewMenu = addMenu("View");
|
||||
QActionGroup* cameraModeGroup = new QActionGroup(viewMenu);
|
||||
|
||||
// View > [camera group]
|
||||
cameraModeGroup->setExclusive(true);
|
||||
|
||||
// View > First Person
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::FirstPerson, 0, // QML Qt:: Key_P
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
// View > Third Person
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::ThirdPerson, 0,
|
||||
true, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
// View > Mirror
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::FullscreenMirror, 0, // QML Qt::Key_H,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
// View > Independent [advanced]
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::IndependentMode, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged()),
|
||||
UNSPECIFIED_POSITION, "Advanced"));
|
||||
|
||||
// View > Entity Camera [advanced]
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::CameraEntityMode, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged()),
|
||||
UNSPECIFIED_POSITION, "Advanced"));
|
||||
|
||||
viewMenu->addSeparator();
|
||||
|
||||
// View > Mini Mirror
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::MiniMirror, 0, false);
|
||||
|
||||
|
||||
// Edit menu ----------------------------------
|
||||
MenuWrapper* editMenu = addMenu("Edit");
|
||||
|
||||
// Edit > Undo
|
||||
QUndoStack* undoStack = qApp->getUndoStack();
|
||||
QAction* undoAction = undoStack->createUndoAction(editMenu);
|
||||
undoAction->setShortcut(Qt::CTRL | Qt::Key_Z);
|
||||
addActionToQMenuAndActionHash(editMenu, undoAction);
|
||||
|
||||
// Edit > Redo
|
||||
QAction* redoAction = undoStack->createRedoAction(editMenu);
|
||||
redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z);
|
||||
addActionToQMenuAndActionHash(editMenu, redoAction);
|
||||
|
||||
addActionToQMenuAndActionHash(editMenu,
|
||||
MenuOption::Preferences,
|
||||
Qt::CTRL | Qt::Key_Comma,
|
||||
dialogsManager.data(),
|
||||
SLOT(editPreferences()),
|
||||
QAction::PreferencesRole);
|
||||
// Edit > Running Sccripts
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J,
|
||||
qApp, SLOT(toggleRunningScriptsWidget()));
|
||||
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::Attachments, 0,
|
||||
dialogsManager.data(), SLOT(editAttachments()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
// Edit > Open and Run Script from File... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O,
|
||||
qApp, SLOT(loadDialog()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
MenuWrapper* toolsMenu = addMenu("Tools");
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S,
|
||||
dialogsManager.data(), SLOT(showScriptEditor()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
// Edit > Open and Run Script from Url... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::LoadScriptURL,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_O, qApp, SLOT(loadScriptURLDialog()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Stop All Scripts... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0, qApp, SLOT(stopAllScripts()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Reload All Scripts... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R,
|
||||
qApp, SLOT(reloadAllScripts()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Scripts Editor... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S,
|
||||
dialogsManager.data(), SLOT(showScriptEditor()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Console... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::Console, Qt::CTRL | Qt::ALT | Qt::Key_J,
|
||||
DependencyManager::get<StandAloneJSConsole>().data(),
|
||||
SLOT(toggleConsole()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Reload All Content [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
|
||||
// Edit > Package Model... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::PackageModel, 0,
|
||||
qApp, SLOT(packageModel()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
|
||||
// Navigate menu ----------------------------------
|
||||
MenuWrapper* navigateMenu = addMenu("Navigate");
|
||||
|
||||
// Navigate > Home -- FIXME: needs implementation
|
||||
auto homeAction = addActionToQMenuAndActionHash(navigateMenu, "Home");
|
||||
homeAction->setDisabled(true);
|
||||
|
||||
addActionToQMenuAndActionHash(navigateMenu, MenuOption::AddressBar, Qt::CTRL | Qt::Key_L,
|
||||
dialogsManager.data(), SLOT(toggleAddressBar()));
|
||||
|
||||
// Navigate > Directory -- FIXME: needs implementation
|
||||
addActionToQMenuAndActionHash(navigateMenu, "Directory");
|
||||
|
||||
// Navigate > Bookmark related menus -- Note: the Bookmark class adds its own submenus here.
|
||||
qApp->getBookmarks()->setupMenus(this, navigateMenu);
|
||||
|
||||
// Navigate > Copy Address [advanced]
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
addActionToQMenuAndActionHash(navigateMenu, MenuOption::CopyAddress, 0,
|
||||
addressManager.data(), SLOT(copyAddress()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Navigate > Copy Path [advanced]
|
||||
addActionToQMenuAndActionHash(navigateMenu, MenuOption::CopyPath, 0,
|
||||
addressManager.data(), SLOT(copyPath()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
|
||||
// Market menu ----------------------------------
|
||||
MenuWrapper* marketMenu = addMenu("Market");
|
||||
|
||||
// Market > Marketplace... -- FIXME: needs implementation
|
||||
auto marketplaceAction = addActionToQMenuAndActionHash(marketMenu, "Marketplace...");
|
||||
marketplaceAction->setDisabled(true);
|
||||
|
||||
|
||||
// Settings menu ----------------------------------
|
||||
MenuWrapper* settingsMenu = addMenu("Settings");
|
||||
|
||||
// Settings > Advance Menus
|
||||
addCheckableActionToQMenuAndActionHash(settingsMenu, "Advanced Menus", 0, false, this, SLOT(toggleAdvancedMenus()));
|
||||
|
||||
// Settings > Developer Menus
|
||||
addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menus", 0, false, this, SLOT(toggleDeveloperMenus()));
|
||||
|
||||
// Settings > General...
|
||||
addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_Comma,
|
||||
dialogsManager.data(), SLOT(editPreferences()), QAction::PreferencesRole);
|
||||
|
||||
// Settings > Avatar...-- FIXME: needs implementation
|
||||
auto avatarAction = addActionToQMenuAndActionHash(settingsMenu, "Avatar...");
|
||||
avatarAction->setDisabled(true);
|
||||
|
||||
// Settings > Audio...-- FIXME: needs implementation
|
||||
auto audioAction = addActionToQMenuAndActionHash(settingsMenu, "Audio...");
|
||||
audioAction->setDisabled(true);
|
||||
|
||||
// Settings > LOD...-- FIXME: needs implementation
|
||||
auto lodAction = addActionToQMenuAndActionHash(settingsMenu, "LOD...");
|
||||
lodAction->setDisabled(true);
|
||||
|
||||
// Settings > Control with Speech [advanced]
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||
auto speechRecognizer = DependencyManager::get<SpeechRecognizer>();
|
||||
QAction* speechRecognizerAction = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::ControlWithSpeech,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_C,
|
||||
speechRecognizer->getEnabled(),
|
||||
speechRecognizer.data(),
|
||||
SLOT(setEnabled(bool)),
|
||||
UNSPECIFIED_POSITION, "Advanced");
|
||||
QAction* speechRecognizerAction = addCheckableActionToQMenuAndActionHash(settingsMenu, MenuOption::ControlWithSpeech,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_C,
|
||||
speechRecognizer->getEnabled(),
|
||||
speechRecognizer.data(),
|
||||
SLOT(setEnabled(bool)),
|
||||
UNSPECIFIED_POSITION, "Advanced");
|
||||
connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool)));
|
||||
#endif
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat,
|
||||
0, // QML Qt::Key_Backslash,
|
||||
dialogsManager.data(), SLOT(showIRCLink()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::AddRemoveFriends, 0,
|
||||
qApp, SLOT(showFriendsWindow()));
|
||||
|
||||
MenuWrapper* visibilityMenu = toolsMenu->addMenu("I Am Visible To");
|
||||
{
|
||||
QActionGroup* visibilityGroup = new QActionGroup(toolsMenu);
|
||||
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
|
||||
|
||||
QAction* visibleToEveryone = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToEveryone,
|
||||
0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::All,
|
||||
discoverabilityManager.data(), SLOT(setVisibility()));
|
||||
visibilityGroup->addAction(visibleToEveryone);
|
||||
|
||||
QAction* visibleToFriends = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToFriends,
|
||||
0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::Friends,
|
||||
discoverabilityManager.data(), SLOT(setVisibility()));
|
||||
visibilityGroup->addAction(visibleToFriends);
|
||||
|
||||
QAction* visibleToNoOne = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToNoOne,
|
||||
0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::None,
|
||||
discoverabilityManager.data(), SLOT(setVisibility()));
|
||||
visibilityGroup->addAction(visibleToNoOne);
|
||||
|
||||
connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged,
|
||||
discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged);
|
||||
}
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::ToolWindow,
|
||||
Qt::CTRL | Qt::ALT | Qt::Key_T,
|
||||
dialogsManager.data(),
|
||||
SLOT(toggleToolWindow()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::Console,
|
||||
Qt::CTRL | Qt::ALT | Qt::Key_J,
|
||||
DependencyManager::get<StandAloneJSConsole>().data(),
|
||||
SLOT(toggleConsole()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::ResetSensors,
|
||||
0, // QML Qt::Key_Apostrophe,
|
||||
qApp,
|
||||
SLOT(resetSensors()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0,
|
||||
qApp, SLOT(packageModel()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addMenu(DisplayPlugin::MENU_PATH());
|
||||
{
|
||||
MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu);
|
||||
QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu);
|
||||
displayModeGroup->setExclusive(true);
|
||||
}
|
||||
|
||||
MenuWrapper* avatarMenu = addMenu("Avatar");
|
||||
QObject* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
// Settings > Input Devices
|
||||
MenuWrapper* inputModeMenu = addMenu(MenuOption::InputMenu, "Advanced");
|
||||
QActionGroup* inputModeGroup = new QActionGroup(inputModeMenu);
|
||||
inputModeGroup->setExclusive(false);
|
||||
|
||||
MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size");
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::IncreaseAvatarSize,
|
||||
0, // QML Qt::Key_Plus,
|
||||
avatar,
|
||||
SLOT(increaseSize()));
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::DecreaseAvatarSize,
|
||||
0, // QML Qt::Key_Minus,
|
||||
avatar,
|
||||
SLOT(decreaseSize()));
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::ResetAvatarSize,
|
||||
0, // QML Qt::Key_Equal,
|
||||
avatar,
|
||||
SLOT(resetSize()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true,
|
||||
NULL, NULL, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
MenuWrapper* viewMenu = addMenu("View");
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
MenuWrapper* cameraModeMenu = viewMenu->addMenu("Camera Mode");
|
||||
QActionGroup* cameraModeGroup = new QActionGroup(cameraModeMenu);
|
||||
cameraModeGroup->setExclusive(true);
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
||||
MenuOption::FirstPerson, 0, // QML Qt:: Key_P
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
||||
MenuOption::ThirdPerson, 0,
|
||||
true, qApp, SLOT(cameraMenuChanged())));
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
||||
MenuOption::IndependentMode, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
||||
MenuOption::CameraEntityMode, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
||||
MenuOption::FullscreenMirror, 0, // QML Qt::Key_H,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror,
|
||||
0, //QML Qt::SHIFT | Qt::Key_H,
|
||||
true);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CenterPlayerInView,
|
||||
0, false, qApp, SLOT(rotationModeChanged()),
|
||||
UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::WorldAxes, 0, false, NULL, NULL, UNSPECIFIED_POSITION, "Developer");
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, 0, false, NULL, NULL, UNSPECIFIED_POSITION, "Developer");
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
qApp, SLOT(toggleLogDialog()), QAction::NoRole, UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::AudioNetworkStats, 0,
|
||||
dialogsManager.data(), SLOT(audioStatsDetails()), QAction::NoRole, UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0,
|
||||
dialogsManager.data(), SLOT(bandwidthDetails()), QAction::NoRole, UNSPECIFIED_POSITION, "Developer");
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0,
|
||||
dialogsManager.data(), SLOT(octreeStatsDetails()), QAction::NoRole, UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, "Advanced Menus", 0, false, this, SLOT(toggleAdvancedMenus()));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, "Developer Menus", 0, false, this, SLOT(toggleDeveloperMenus()));
|
||||
|
||||
// Developer menu ----------------------------------
|
||||
MenuWrapper* developerMenu = addMenu("Developer", "Developer");
|
||||
|
||||
// Developer > Render >>>
|
||||
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere,
|
||||
0, // QML Qt::SHIFT | Qt::Key_A,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DebugAmbientOcclusion);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Antialiasing);
|
||||
|
||||
// Developer > Render > Ambient Light
|
||||
MenuWrapper* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight);
|
||||
QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu);
|
||||
ambientLightGroup->setExclusive(true);
|
||||
|
@ -333,8 +349,10 @@ Menu::Menu() {
|
|||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false));
|
||||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false));
|
||||
|
||||
// Developer > Render > Throttle FPS If Not Focus
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true);
|
||||
|
||||
// Developer > Render > Resolution
|
||||
MenuWrapper* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution);
|
||||
QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu);
|
||||
resolutionGroup->setExclusive(true);
|
||||
|
@ -344,37 +362,40 @@ Menu::Menu() {
|
|||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false));
|
||||
|
||||
// Developer > Render > Stars
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars,
|
||||
0, // QML Qt::Key_Asterisk,
|
||||
true);
|
||||
|
||||
// Developer > Render > LOD Tools
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
|
||||
0, // QML Qt::SHIFT | Qt::Key_L,
|
||||
dialogsManager.data(), SLOT(lodTools()));
|
||||
|
||||
|
||||
// Developer > Assets >>>
|
||||
MenuWrapper* assetDeveloperMenu = developerMenu->addMenu("Assets");
|
||||
|
||||
auto& assetDialogFactory = AssetUploadDialogFactory::getInstance();
|
||||
assetDialogFactory.setDialogParent(this);
|
||||
|
||||
QAction* assetUpload = addActionToQMenuAndActionHash(assetDeveloperMenu,
|
||||
MenuOption::UploadAsset,
|
||||
0,
|
||||
&assetDialogFactory,
|
||||
SLOT(showDialog()));
|
||||
|
||||
MenuOption::UploadAsset,
|
||||
0,
|
||||
&assetDialogFactory,
|
||||
SLOT(showDialog()));
|
||||
|
||||
// disable the asset upload action by default - it gets enabled only if asset server becomes present
|
||||
assetUpload->setEnabled(false);
|
||||
|
||||
|
||||
auto& atpMigrator = ATPAssetMigrator::getInstance();
|
||||
atpMigrator.setDialogParent(this);
|
||||
|
||||
|
||||
addActionToQMenuAndActionHash(assetDeveloperMenu, MenuOption::AssetMigration,
|
||||
0, &atpMigrator,
|
||||
SLOT(loadEntityServerFile()));
|
||||
|
||||
0, &atpMigrator,
|
||||
SLOT(loadEntityServerFile()));
|
||||
|
||||
// Developer > Avatar >>>
|
||||
MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar");
|
||||
|
||||
// Developer > Avatar > Face Tracking
|
||||
MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking");
|
||||
{
|
||||
QActionGroup* faceTrackerGroup = new QActionGroup(avatarDebugMenu);
|
||||
|
@ -423,6 +444,7 @@ Menu::Menu() {
|
|||
#endif
|
||||
|
||||
#ifdef HAVE_IVIEWHMD
|
||||
// Developer > Avatar > Eye Tracking
|
||||
MenuWrapper* eyeTrackingMenu = avatarDebugMenu->addMenu("Eye Tracking");
|
||||
addCheckableActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::SMIEyeTracking, 0, false,
|
||||
qApp, SLOT(setActiveEyeTracker()));
|
||||
|
@ -439,9 +461,8 @@ Menu::Menu() {
|
|||
qApp, SLOT(setActiveEyeTracker()));
|
||||
#endif
|
||||
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false,
|
||||
avatarManager.data(), SLOT(setShouldShowReceiveStats(bool)));
|
||||
avatarManager.data(), SLOT(setShouldShowReceiveStats(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false);
|
||||
|
@ -450,13 +471,13 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false,
|
||||
avatar, SLOT(setEnableDebugDrawDefaultPose(bool)));
|
||||
avatar, SLOT(setEnableDebugDrawDefaultPose(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawAnimPose, 0, false,
|
||||
avatar, SLOT(setEnableDebugDrawAnimPose(bool)));
|
||||
avatar, SLOT(setEnableDebugDrawAnimPose(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawPosition, 0, false,
|
||||
avatar, SLOT(setEnableDebugDrawPosition(bool)));
|
||||
avatar, SLOT(setEnableDebugDrawPosition(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true,
|
||||
avatar, SLOT(setEnableMeshVisible(bool)));
|
||||
avatar, SLOT(setEnableMeshVisible(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true);
|
||||
|
@ -473,37 +494,37 @@ Menu::Menu() {
|
|||
avatar, SLOT(updateMotionBehaviorFromMenu()),
|
||||
UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
|
||||
|
||||
// Developer > Hands >>>
|
||||
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::LowVelocityFilter, 0, true,
|
||||
qApp, SLOT(setLowVelocityFilter(bool)));
|
||||
qApp, SLOT(setLowVelocityFilter(bool)));
|
||||
|
||||
MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
|
||||
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);
|
||||
|
||||
// Developer > Network >>>
|
||||
MenuWrapper* networkMenu = developerMenu->addMenu("Network");
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
|
||||
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false,
|
||||
qApp->getEntityEditPacketSender(),
|
||||
SLOT(toggleNackPackets()));
|
||||
qApp->getEntityEditPacketSender(),
|
||||
SLOT(toggleNackPackets()));
|
||||
addCheckableActionToQMenuAndActionHash(networkMenu,
|
||||
MenuOption::DisableActivityLogger,
|
||||
0,
|
||||
false,
|
||||
&UserActivityLogger::getInstance(),
|
||||
SLOT(disable(bool)));
|
||||
MenuOption::DisableActivityLogger,
|
||||
0,
|
||||
false,
|
||||
&UserActivityLogger::getInstance(),
|
||||
SLOT(disable(bool)));
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::CachesSize, 0,
|
||||
dialogsManager.data(), SLOT(cachesSizeDialog()));
|
||||
dialogsManager.data(), SLOT(cachesSizeDialog()));
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0,
|
||||
dialogsManager.data(), SLOT(toggleDiskCacheEditor()));
|
||||
dialogsManager.data(), SLOT(toggleDiskCacheEditor()));
|
||||
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ShowDSConnectTable, 0,
|
||||
dialogsManager.data(), SLOT(showDomainConnectionDialog()));
|
||||
dialogsManager.data(), SLOT(showDomainConnectionDialog()));
|
||||
|
||||
// Developer > Timing and Stats >>>
|
||||
MenuWrapper* timingMenu = developerMenu->addMenu("Timing and Stats");
|
||||
|
||||
MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true);
|
||||
|
@ -520,60 +541,34 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::ShowRealtimeEntityStats);
|
||||
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
// Developer > Audio >>>
|
||||
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction,
|
||||
0,
|
||||
true,
|
||||
audioIO.data(),
|
||||
SLOT(toggleAudioNoiseReduction()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, true,
|
||||
audioIO.data(), SLOT(toggleAudioNoiseReduction()));
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false,
|
||||
audioIO.data(), SLOT(toggleServerEcho()));
|
||||
audioIO.data(), SLOT(toggleServerEcho()));
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio, 0, false,
|
||||
audioIO.data(), SLOT(toggleLocalEcho()));
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::MuteAudio,
|
||||
Qt::CTRL | Qt::Key_M,
|
||||
false,
|
||||
audioIO.data(),
|
||||
SLOT(toggleMute()));
|
||||
addActionToQMenuAndActionHash(audioDebugMenu,
|
||||
MenuOption::MuteEnvironment,
|
||||
0,
|
||||
audioIO.data(),
|
||||
SLOT(sendMuteEnvironmentPacket()));
|
||||
audioIO.data(), SLOT(toggleLocalEcho()));
|
||||
addActionToQMenuAndActionHash(audioDebugMenu, MenuOption::MuteEnvironment, 0,
|
||||
audioIO.data(), SLOT(sendMuteEnvironmentPacket()));
|
||||
|
||||
auto scope = DependencyManager::get<AudioScope>();
|
||||
|
||||
MenuWrapper* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope");
|
||||
addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope,
|
||||
Qt::CTRL | Qt::Key_P, false,
|
||||
scope.data(),
|
||||
SLOT(toggle()));
|
||||
addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopePause,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_P ,
|
||||
false,
|
||||
scope.data(),
|
||||
SLOT(togglePause()));
|
||||
addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_P, false,
|
||||
scope.data(), SLOT(toggle()));
|
||||
addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopePause, Qt::CTRL | Qt::SHIFT | Qt::Key_P, false,
|
||||
scope.data(), SLOT(togglePause()));
|
||||
|
||||
addDisabledActionAndSeparator(audioScopeMenu, "Display Frames");
|
||||
{
|
||||
QAction *fiveFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiveFrames,
|
||||
0,
|
||||
true,
|
||||
scope.data(),
|
||||
SLOT(selectAudioScopeFiveFrames()));
|
||||
QAction* fiveFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiveFrames,
|
||||
0, true, scope.data(), SLOT(selectAudioScopeFiveFrames()));
|
||||
|
||||
QAction *twentyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeTwentyFrames,
|
||||
0,
|
||||
false,
|
||||
scope.data(),
|
||||
SLOT(selectAudioScopeTwentyFrames()));
|
||||
QAction* twentyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeTwentyFrames,
|
||||
0, false, scope.data(), SLOT(selectAudioScopeTwentyFrames()));
|
||||
|
||||
QAction *fiftyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiftyFrames,
|
||||
0,
|
||||
false,
|
||||
scope.data(),
|
||||
SLOT(selectAudioScopeFiftyFrames()));
|
||||
QAction* fiftyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiftyFrames,
|
||||
0, false, scope.data(), SLOT(selectAudioScopeFiftyFrames()));
|
||||
|
||||
QActionGroup* audioScopeFramesGroup = new QActionGroup(audioScopeMenu);
|
||||
audioScopeFramesGroup->addAction(fiveFrames);
|
||||
|
@ -581,19 +576,69 @@ Menu::Menu() {
|
|||
audioScopeFramesGroup->addAction(fiftyFrames);
|
||||
}
|
||||
|
||||
// Developer > Physics >>>
|
||||
MenuWrapper* physicsOptionsMenu = developerMenu->addMenu("Physics");
|
||||
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned);
|
||||
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls);
|
||||
|
||||
// Developer > Display Crash Options
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisplayCrashOptions, 0, true);
|
||||
// Developer > Crash Application
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::CrashInterface, 0, qApp, SLOT(crashApplication()));
|
||||
|
||||
MenuWrapper* helpMenu = addMenu("Help");
|
||||
addActionToQMenuAndActionHash(helpMenu, MenuOption::EditEntitiesHelp, 0, qApp, SLOT(showEditEntitiesHelp()));
|
||||
// Developer > Log...
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::Log, Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
qApp, SLOT(toggleLogDialog()));
|
||||
|
||||
// Developer > Stats
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats);
|
||||
|
||||
// Developer > Audio Stats...
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::AudioNetworkStats, 0,
|
||||
dialogsManager.data(), SLOT(audioStatsDetails()));
|
||||
|
||||
// Developer > Bandwidth Stats...
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::BandwidthDetails, 0,
|
||||
dialogsManager.data(), SLOT(bandwidthDetails()));
|
||||
|
||||
// Developer > Entity Stats...
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::OctreeStats, 0,
|
||||
dialogsManager.data(), SLOT(octreeStatsDetails()));
|
||||
|
||||
// Developer > World Axes
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::WorldAxes);
|
||||
|
||||
|
||||
|
||||
#if 0 /// -------------- REMOVED FOR NOW --------------
|
||||
addDisabledActionAndSeparator(navigateMenu, "History");
|
||||
QAction* backAction = addActionToQMenuAndActionHash(navigateMenu, MenuOption::Back, 0, addressManager.data(), SLOT(goBack()));
|
||||
QAction* forwardAction = addActionToQMenuAndActionHash(navigateMenu, MenuOption::Forward, 0, addressManager.data(), SLOT(goForward()));
|
||||
|
||||
// connect to the AddressManager signal to enable and disable the back and forward menu items
|
||||
connect(addressManager.data(), &AddressManager::goBackPossible, backAction, &QAction::setEnabled);
|
||||
connect(addressManager.data(), &AddressManager::goForwardPossible, forwardAction, &QAction::setEnabled);
|
||||
|
||||
// set the two actions to start disabled since the stacks are clear on startup
|
||||
backAction->setDisabled(true);
|
||||
forwardAction->setDisabled(true);
|
||||
|
||||
MenuWrapper* toolsMenu = addMenu("Tools");
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::ToolWindow,
|
||||
Qt::CTRL | Qt::ALT | Qt::Key_T,
|
||||
dialogsManager.data(),
|
||||
SLOT(toggleToolWindow()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true,
|
||||
NULL, NULL, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CenterPlayerInView,
|
||||
0, false, qApp, SLOT(rotationModeChanged()),
|
||||
UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
QAction* aboutAction = helpMenu->addAction(MenuOption::AboutApp);
|
||||
connect(aboutAction, SIGNAL(triggered()), qApp, SLOT(aboutApp()));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -205,7 +205,6 @@ namespace MenuOption {
|
|||
const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene";
|
||||
const QString EchoLocalAudio = "Echo Local Audio";
|
||||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
const QString EditEntitiesHelp = "Edit Entities Help...";
|
||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||
const QString EnableCharacterController = "Enable avatar collisions";
|
||||
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
|
||||
|
@ -219,8 +218,9 @@ namespace MenuOption {
|
|||
const QString FixGaze = "Fix Gaze (no saccade)";
|
||||
const QString Forward = "Forward";
|
||||
const QString FrameTimer = "Show Timer";
|
||||
const QString FullscreenMirror = "Fullscreen Mirror";
|
||||
const QString FullscreenMirror = "Mirror";
|
||||
const QString GlowWhenSpeaking = "Glow When Speaking";
|
||||
const QString Help = "Help...";
|
||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||
const QString IndependentMode = "Independent Mode";
|
||||
const QString InputMenu = "Avatar>Input Devices";
|
||||
|
@ -234,7 +234,7 @@ namespace MenuOption {
|
|||
const QString LogExtraTimings = "Log Extra Timing Details";
|
||||
const QString LowVelocityFilter = "Low Velocity Filter";
|
||||
const QString MeshVisible = "Draw Mesh";
|
||||
const QString Mirror = "Mirror";
|
||||
const QString MiniMirror = "Mini Mirror";
|
||||
const QString MuteAudio = "Mute Microphone";
|
||||
const QString MuteEnvironment = "Mute Environment";
|
||||
const QString MuteFaceTracking = "Mute Face Tracking";
|
||||
|
@ -249,7 +249,7 @@ namespace MenuOption {
|
|||
const QString PhysicsShowOwned = "Highlight Simulation Ownership";
|
||||
const QString PhysicsShowHulls = "Draw Collision Hulls";
|
||||
const QString PipelineWarnings = "Log Render Pipeline Warnings";
|
||||
const QString Preferences = "Preferences...";
|
||||
const QString Preferences = "General...";
|
||||
const QString Quit = "Quit";
|
||||
const QString ReloadAllScripts = "Reload All Scripts";
|
||||
const QString ReloadContent = "Reload Content (Clears all caches)";
|
||||
|
@ -277,7 +277,7 @@ namespace MenuOption {
|
|||
const QString RenderAmbientLight9 = "FUNSTON_BEACH_SUNSET";
|
||||
const QString ResetAvatarSize = "Reset Avatar Size";
|
||||
const QString ResetSensors = "Reset Sensors";
|
||||
const QString RunningScripts = "Running Scripts";
|
||||
const QString RunningScripts = "Running Scripts...";
|
||||
const QString RunTimingTests = "Run Timing Tests";
|
||||
const QString ScriptEditor = "Script Editor...";
|
||||
const QString ScriptedMotorControl = "Enable Scripted Motor Control";
|
||||
|
|
|
@ -108,7 +108,11 @@ Avatar::Avatar(RigPointer rig) :
|
|||
}
|
||||
|
||||
Avatar::~Avatar() {
|
||||
assert(_motionState == nullptr);
|
||||
assert(isDead()); // mark dead before calling the dtor
|
||||
if (_motionState) {
|
||||
delete _motionState;
|
||||
_motionState = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const float BILLBOARD_LOD_DISTANCE = 40.0f;
|
||||
|
@ -585,7 +589,7 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
|||
|
||||
// virtual
|
||||
void Avatar::simulateAttachments(float deltaTime) {
|
||||
for (int i = 0; i < _attachmentModels.size(); i++) {
|
||||
for (int i = 0; i < (int)_attachmentModels.size(); i++) {
|
||||
const AttachmentData& attachment = _attachmentData.at(i);
|
||||
auto& model = _attachmentModels.at(i);
|
||||
int jointIndex = getJointIndex(attachment.jointName);
|
||||
|
@ -940,7 +944,7 @@ static std::shared_ptr<Model> allocateAttachmentModel(bool isSoft, RigPointer ri
|
|||
|
||||
void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setAttachmentData", Qt::DirectConnection,
|
||||
QMetaObject::invokeMethod(this, "setAttachmentData", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QVector<AttachmentData>, attachmentData));
|
||||
return;
|
||||
}
|
||||
|
@ -949,14 +953,14 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
|||
AvatarData::setAttachmentData(attachmentData);
|
||||
|
||||
// if number of attachments has been reduced, remove excess models.
|
||||
while (_attachmentModels.size() > attachmentData.size()) {
|
||||
while ((int)_attachmentModels.size() > attachmentData.size()) {
|
||||
auto attachmentModel = _attachmentModels.back();
|
||||
_attachmentModels.pop_back();
|
||||
_attachmentsToRemove.push_back(attachmentModel);
|
||||
}
|
||||
|
||||
for (int i = 0; i < attachmentData.size(); i++) {
|
||||
if (i == _attachmentModels.size()) {
|
||||
if (i == (int)_attachmentModels.size()) {
|
||||
// if number of attachments has been increased, we need to allocate a new model
|
||||
_attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel.getRig()));
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ void AvatarActionHold::prepareForPhysicsSimulation() {
|
|||
// _palmRotationFromRigidBody = avatarRotationInverse * palmRotation;
|
||||
});
|
||||
|
||||
activateBody();
|
||||
activateBody(true);
|
||||
}
|
||||
|
||||
std::shared_ptr<Avatar> AvatarActionHold::getTarget(glm::quat& rotation, glm::vec3& position) {
|
||||
|
|
|
@ -76,6 +76,10 @@ AvatarManager::AvatarManager(QObject* parent) :
|
|||
packetReceiver.registerListener(PacketType::AvatarBillboard, this, "processAvatarBillboardPacket");
|
||||
}
|
||||
|
||||
AvatarManager::~AvatarManager() {
|
||||
_myAvatar->die();
|
||||
}
|
||||
|
||||
void AvatarManager::init() {
|
||||
_myAvatar->init();
|
||||
{
|
||||
|
@ -165,7 +169,12 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE);
|
||||
if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
|
||||
avatar->removeFromScene(*fadingIterator, scene, pendingChanges);
|
||||
fadingIterator = _avatarFades.erase(fadingIterator);
|
||||
// only remove from _avatarFades if we're sure its motionState has been removed from PhysicsEngine
|
||||
if (_motionStatesToRemoveFromPhysics.empty()) {
|
||||
fadingIterator = _avatarFades.erase(fadingIterator);
|
||||
} else {
|
||||
++fadingIterator;
|
||||
}
|
||||
} else {
|
||||
avatar->simulate(deltaTime);
|
||||
++fadingIterator;
|
||||
|
@ -193,20 +202,6 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe
|
|||
return newAvatar;
|
||||
}
|
||||
|
||||
// protected
|
||||
void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) {
|
||||
auto rawPointer = std::static_pointer_cast<Avatar>(avatar);
|
||||
AvatarMotionState* motionState = rawPointer->getMotionState();
|
||||
if (motionState) {
|
||||
// clean up physics stuff
|
||||
motionState->clearObjectBackPointer();
|
||||
rawPointer->setMotionState(nullptr);
|
||||
_avatarMotionStates.remove(motionState);
|
||||
_motionStatesToAdd.remove(motionState);
|
||||
_motionStatesToDelete.push_back(motionState);
|
||||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
void AvatarManager::removeAvatar(const QUuid& sessionUUID) {
|
||||
QWriteLocker locker(&_hashLock);
|
||||
|
@ -220,8 +215,18 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) {
|
|||
void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar) {
|
||||
AvatarHashMap::handleRemovedAvatar(removedAvatar);
|
||||
|
||||
removedAvatar->die();
|
||||
removeAvatarMotionState(removedAvatar);
|
||||
// removedAvatar is a shared pointer to an AvatarData but we need to get to the derived Avatar
|
||||
// class in this context so we can call methods that don't exist at the base class.
|
||||
Avatar* avatar = static_cast<Avatar*>(removedAvatar.get());
|
||||
avatar->die();
|
||||
|
||||
AvatarMotionState* motionState = avatar->getMotionState();
|
||||
if (motionState) {
|
||||
_motionStatesThatMightUpdate.remove(motionState);
|
||||
_motionStatesToAddToPhysics.remove(motionState);
|
||||
_motionStatesToRemoveFromPhysics.push_back(motionState);
|
||||
}
|
||||
|
||||
_avatarFades.push_back(removedAvatar);
|
||||
}
|
||||
|
||||
|
@ -274,22 +279,22 @@ AvatarData* AvatarManager::getAvatar(QUuid avatarID) {
|
|||
}
|
||||
|
||||
|
||||
void AvatarManager::getObjectsToDelete(VectorOfMotionStates& result) {
|
||||
void AvatarManager::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
result.swap(_motionStatesToDelete);
|
||||
result.swap(_motionStatesToRemoveFromPhysics);
|
||||
}
|
||||
|
||||
void AvatarManager::getObjectsToAdd(VectorOfMotionStates& result) {
|
||||
void AvatarManager::getObjectsToAddToPhysics(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
for (auto motionState : _motionStatesToAdd) {
|
||||
for (auto motionState : _motionStatesToAddToPhysics) {
|
||||
result.push_back(motionState);
|
||||
}
|
||||
_motionStatesToAdd.clear();
|
||||
_motionStatesToAddToPhysics.clear();
|
||||
}
|
||||
|
||||
void AvatarManager::getObjectsToChange(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
for (auto state : _avatarMotionStates) {
|
||||
for (auto state : _motionStatesThatMightUpdate) {
|
||||
if (state->_dirtyFlags > 0) {
|
||||
result.push_back(state);
|
||||
}
|
||||
|
@ -344,8 +349,8 @@ void AvatarManager::addAvatarToSimulation(Avatar* avatar) {
|
|||
// we don't add to the simulation now, we put it on a list to be added later
|
||||
AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
|
||||
avatar->setMotionState(motionState);
|
||||
_motionStatesToAdd.insert(motionState);
|
||||
_avatarMotionStates.insert(motionState);
|
||||
_motionStatesToAddToPhysics.insert(motionState);
|
||||
_motionStatesThatMightUpdate.insert(motionState);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ public:
|
|||
/// Registers the script types associated with the avatar manager.
|
||||
static void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
virtual ~AvatarManager();
|
||||
|
||||
void init();
|
||||
|
||||
MyAvatar* getMyAvatar() { return _myAvatar.get(); }
|
||||
|
@ -59,8 +61,8 @@ public:
|
|||
Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID);
|
||||
|
||||
|
||||
void getObjectsToDelete(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToAdd(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToAddToPhysics(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToChange(VectorOfMotionStates& motionStates);
|
||||
void handleOutgoingChanges(const VectorOfMotionStates& motionStates);
|
||||
void handleCollisionEvents(const CollisionEvents& collisionEvents);
|
||||
|
@ -80,7 +82,6 @@ private:
|
|||
// virtual overrides
|
||||
virtual AvatarSharedPointer newSharedAvatar();
|
||||
virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
||||
void removeAvatarMotionState(AvatarSharedPointer avatar);
|
||||
|
||||
virtual void removeAvatar(const QUuid& sessionUUID);
|
||||
virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar);
|
||||
|
@ -93,9 +94,9 @@ private:
|
|||
|
||||
bool _shouldShowReceiveStats = false;
|
||||
|
||||
SetOfAvatarMotionStates _avatarMotionStates;
|
||||
SetOfMotionStates _motionStatesToAdd;
|
||||
VectorOfMotionStates _motionStatesToDelete;
|
||||
SetOfAvatarMotionStates _motionStatesThatMightUpdate;
|
||||
SetOfMotionStates _motionStatesToAddToPhysics;
|
||||
VectorOfMotionStates _motionStatesToRemoveFromPhysics;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(AvatarManager::LocalLight)
|
||||
|
|
|
@ -25,20 +25,17 @@ AvatarMotionState::AvatarMotionState(Avatar* avatar, btCollisionShape* shape) :
|
|||
}
|
||||
|
||||
AvatarMotionState::~AvatarMotionState() {
|
||||
assert(_avatar);
|
||||
_avatar = nullptr;
|
||||
}
|
||||
|
||||
// virtual
|
||||
uint32_t AvatarMotionState::getIncomingDirtyFlags() {
|
||||
uint32_t dirtyFlags = 0;
|
||||
if (_body && _avatar) {
|
||||
dirtyFlags = _dirtyFlags;
|
||||
}
|
||||
return dirtyFlags;
|
||||
return _body ? _dirtyFlags : 0;
|
||||
}
|
||||
|
||||
void AvatarMotionState::clearIncomingDirtyFlags() {
|
||||
if (_body && _avatar) {
|
||||
if (_body) {
|
||||
_dirtyFlags = 0;
|
||||
}
|
||||
}
|
||||
|
@ -50,12 +47,9 @@ MotionType AvatarMotionState::computeObjectMotionType() const {
|
|||
|
||||
// virtual and protected
|
||||
btCollisionShape* AvatarMotionState::computeNewShape() {
|
||||
if (_avatar) {
|
||||
ShapeInfo shapeInfo;
|
||||
_avatar->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
return nullptr;
|
||||
ShapeInfo shapeInfo;
|
||||
_avatar->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
@ -65,9 +59,6 @@ bool AvatarMotionState::isMoving() const {
|
|||
|
||||
// virtual
|
||||
void AvatarMotionState::getWorldTransform(btTransform& worldTrans) const {
|
||||
if (!_avatar) {
|
||||
return;
|
||||
}
|
||||
worldTrans.setOrigin(glmToBullet(getObjectPosition()));
|
||||
worldTrans.setRotation(glmToBullet(getObjectRotation()));
|
||||
if (_body) {
|
||||
|
@ -76,11 +67,8 @@ void AvatarMotionState::getWorldTransform(btTransform& worldTrans) const {
|
|||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
// virtual
|
||||
void AvatarMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||
if (!_avatar) {
|
||||
return;
|
||||
}
|
||||
// HACK: The PhysicsEngine does not actually move OTHER avatars -- instead it slaves their local RigidBody to the transform
|
||||
// as specified by a remote simulation. However, to give the remote simulation time to respond to our own objects we tie
|
||||
// the other avatar's body to its true position with a simple spring. This is a HACK that will have to be improved later.
|
||||
|
@ -154,15 +142,8 @@ QUuid AvatarMotionState::getSimulatorID() const {
|
|||
return _avatar->getSessionUUID();
|
||||
}
|
||||
|
||||
// virtual
|
||||
int16_t AvatarMotionState::computeCollisionGroup() {
|
||||
// virtual
|
||||
int16_t AvatarMotionState::computeCollisionGroup() const {
|
||||
return COLLISION_GROUP_OTHER_AVATAR;
|
||||
}
|
||||
|
||||
// virtual
|
||||
void AvatarMotionState::clearObjectBackPointer() {
|
||||
ObjectMotionState::clearObjectBackPointer();
|
||||
_avatar = nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -21,55 +21,65 @@ class Avatar;
|
|||
class AvatarMotionState : public ObjectMotionState {
|
||||
public:
|
||||
AvatarMotionState(Avatar* avatar, btCollisionShape* shape);
|
||||
~AvatarMotionState();
|
||||
|
||||
virtual MotionType getMotionType() const { return _motionType; }
|
||||
virtual MotionType getMotionType() const override { return _motionType; }
|
||||
|
||||
virtual uint32_t getIncomingDirtyFlags();
|
||||
virtual void clearIncomingDirtyFlags();
|
||||
virtual uint32_t getIncomingDirtyFlags() override;
|
||||
virtual void clearIncomingDirtyFlags() override;
|
||||
|
||||
virtual MotionType computeObjectMotionType() const;
|
||||
virtual MotionType computeObjectMotionType() const override;
|
||||
|
||||
virtual bool isMoving() const;
|
||||
virtual bool isMoving() const override;
|
||||
|
||||
// this relays incoming position/rotation to the RigidBody
|
||||
virtual void getWorldTransform(btTransform& worldTrans) const;
|
||||
virtual void getWorldTransform(btTransform& worldTrans) const override;
|
||||
|
||||
// this relays outgoing position/rotation to the EntityItem
|
||||
virtual void setWorldTransform(const btTransform& worldTrans);
|
||||
virtual void setWorldTransform(const btTransform& worldTrans) override;
|
||||
|
||||
|
||||
// These pure virtual methods must be implemented for each MotionState type
|
||||
// and make it possible to implement more complicated methods in this base class.
|
||||
|
||||
virtual float getObjectRestitution() const;
|
||||
virtual float getObjectFriction() const;
|
||||
virtual float getObjectLinearDamping() const;
|
||||
virtual float getObjectAngularDamping() const;
|
||||
// pure virtual overrides from ObjectMotionState
|
||||
virtual float getObjectRestitution() const override;
|
||||
virtual float getObjectFriction() const override;
|
||||
virtual float getObjectLinearDamping() const override;
|
||||
virtual float getObjectAngularDamping() const override;
|
||||
|
||||
virtual glm::vec3 getObjectPosition() const;
|
||||
virtual glm::quat getObjectRotation() const;
|
||||
virtual glm::vec3 getObjectLinearVelocity() const;
|
||||
virtual glm::vec3 getObjectAngularVelocity() const;
|
||||
virtual glm::vec3 getObjectGravity() const;
|
||||
virtual glm::vec3 getObjectPosition() const override;
|
||||
virtual glm::quat getObjectRotation() const override;
|
||||
virtual glm::vec3 getObjectLinearVelocity() const override;
|
||||
virtual glm::vec3 getObjectAngularVelocity() const override;
|
||||
virtual glm::vec3 getObjectGravity() const override;
|
||||
|
||||
virtual const QUuid& getObjectID() const;
|
||||
virtual const QUuid& getObjectID() const override;
|
||||
|
||||
virtual QUuid getSimulatorID() const;
|
||||
virtual QUuid getSimulatorID() const override;
|
||||
|
||||
void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal);
|
||||
|
||||
void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; }
|
||||
|
||||
virtual int16_t computeCollisionGroup();
|
||||
virtual int16_t computeCollisionGroup() const override;
|
||||
|
||||
friend class AvatarManager;
|
||||
friend class Avatar;
|
||||
|
||||
protected:
|
||||
virtual bool isReadyToComputeShape() { return true; }
|
||||
// the dtor had been made protected to force the compiler to verify that it is only
|
||||
// ever called by the Avatar class dtor.
|
||||
~AvatarMotionState();
|
||||
|
||||
virtual bool isReadyToComputeShape() const override { return true; }
|
||||
virtual btCollisionShape* computeNewShape();
|
||||
virtual void clearObjectBackPointer();
|
||||
Avatar* _avatar;
|
||||
|
||||
// The AvatarMotionState keeps a RAW backpointer to its Avatar because all AvatarMotionState
|
||||
// instances are "owned" by their corresponding Avatar instance and are deleted in the Avatar dtor.
|
||||
// In other words, it is impossible for the Avatar to be deleted out from under its MotionState.
|
||||
// In conclusion: weak pointer shennanigans would be pure overhead.
|
||||
Avatar* _avatar; // do NOT use smartpointer here, no need for weakpointer
|
||||
|
||||
uint32_t _dirtyFlags;
|
||||
};
|
||||
|
||||
|
|
|
@ -1022,12 +1022,12 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
|
|||
}
|
||||
|
||||
void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||
Avatar::setAttachmentData(attachmentData);
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setAttachmentData", Qt::DirectConnection,
|
||||
QMetaObject::invokeMethod(this, "setAttachmentData", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QVector<AttachmentData>, attachmentData));
|
||||
return;
|
||||
}
|
||||
Avatar::setAttachmentData(attachmentData);
|
||||
_billboardValid = false;
|
||||
}
|
||||
|
||||
|
@ -1165,21 +1165,25 @@ void MyAvatar::setCollisionSoundURL(const QString& url) {
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::attach(const QString& modelURL, const QString& jointName, const glm::vec3& translation,
|
||||
const glm::quat& rotation, float scale, bool allowDuplicates, bool useSaved) {
|
||||
void MyAvatar::attach(const QString& modelURL, const QString& jointName,
|
||||
const glm::vec3& translation, const glm::quat& rotation,
|
||||
float scale, bool isSoft,
|
||||
bool allowDuplicates, bool useSaved) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
Avatar::attach(modelURL, jointName, translation, rotation, scale, allowDuplicates, useSaved);
|
||||
Avatar::attach(modelURL, jointName, translation, rotation, scale, isSoft, allowDuplicates, useSaved);
|
||||
return;
|
||||
}
|
||||
if (useSaved) {
|
||||
AttachmentData attachment = loadAttachmentData(modelURL, jointName);
|
||||
if (attachment.isValid()) {
|
||||
Avatar::attach(modelURL, attachment.jointName, attachment.translation,
|
||||
attachment.rotation, attachment.scale, allowDuplicates, useSaved);
|
||||
Avatar::attach(modelURL, attachment.jointName,
|
||||
attachment.translation, attachment.rotation,
|
||||
attachment.scale, attachment.isSoft,
|
||||
allowDuplicates, useSaved);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Avatar::attach(modelURL, jointName, translation, rotation, scale, allowDuplicates, useSaved);
|
||||
Avatar::attach(modelURL, jointName, translation, rotation, scale, isSoft, allowDuplicates, useSaved);
|
||||
}
|
||||
|
||||
void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel) {
|
||||
|
|
|
@ -295,7 +295,8 @@ private:
|
|||
void setScriptedMotorTimescale(float timescale);
|
||||
void setScriptedMotorFrame(QString frame);
|
||||
virtual void attach(const QString& modelURL, const QString& jointName = QString(),
|
||||
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f,
|
||||
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(),
|
||||
float scale = 1.0f, bool isSoft = false,
|
||||
bool allowDuplicates = false, bool useSaved = true) override;
|
||||
|
||||
//void beginFollowingHMD();
|
||||
|
|
|
@ -158,7 +158,7 @@ void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) {
|
|||
}
|
||||
|
||||
void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) {
|
||||
if (!qApp->isHMDMode() && Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
if (!qApp->isHMDMode() && Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror)) {
|
||||
gpu::Batch& batch = *renderArgs->_batch;
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
|
|
@ -62,7 +62,7 @@ void AvatarInputs::update() {
|
|||
if (!Menu::getInstance()) {
|
||||
return;
|
||||
}
|
||||
AI_UPDATE(mirrorVisible, Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode()
|
||||
AI_UPDATE(mirrorVisible, Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror) && !qApp->isHMDMode()
|
||||
&& !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror));
|
||||
AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking));
|
||||
AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking));
|
||||
|
@ -129,7 +129,7 @@ void AvatarInputs::toggleZoom() {
|
|||
}
|
||||
|
||||
void AvatarInputs::closeMirror() {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Mirror);
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror)) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::MiniMirror);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -176,20 +176,6 @@ void DialogsManager::showScriptEditor() {
|
|||
_scriptEditor->raise();
|
||||
}
|
||||
|
||||
void DialogsManager::showIRCLink() {
|
||||
if (!_ircInfoBox) {
|
||||
_ircInfoBox = new QMessageBox(QMessageBox::NoIcon,
|
||||
"High Fidelity IRC",
|
||||
"High Fidelity has an IRC channel on irc.freenode.net at #highfidelity.<br/><br/>Web chat is available <a href='http://webchat.freenode.net/?channels=highfidelity&uio=d4'>here</a>.",
|
||||
QMessageBox::Ok);
|
||||
_ircInfoBox->setTextFormat(Qt::RichText);
|
||||
_ircInfoBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||
_ircInfoBox->show();
|
||||
}
|
||||
|
||||
_ircInfoBox->raise();
|
||||
}
|
||||
|
||||
void DialogsManager::showDomainConnectionDialog() {
|
||||
// if the dialog already exists we delete it so the connection data is refreshed
|
||||
if (_domainConnectionDialog) {
|
||||
|
|
|
@ -57,7 +57,6 @@ public slots:
|
|||
void lodTools();
|
||||
void hmdTools(bool showTools);
|
||||
void showScriptEditor();
|
||||
void showIRCLink();
|
||||
void showDomainConnectionDialog();
|
||||
|
||||
// Application Update
|
||||
|
|
|
@ -1085,12 +1085,15 @@ void AvatarData::setAttachmentData(const QVector<AttachmentData>& attachmentData
|
|||
_attachmentData = attachmentData;
|
||||
}
|
||||
|
||||
void AvatarData::attach(const QString& modelURL, const QString& jointName, const glm::vec3& translation,
|
||||
const glm::quat& rotation, float scale, bool allowDuplicates, bool useSaved) {
|
||||
void AvatarData::attach(const QString& modelURL, const QString& jointName,
|
||||
const glm::vec3& translation, const glm::quat& rotation,
|
||||
float scale, bool isSoft,
|
||||
bool allowDuplicates, bool useSaved) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "attach", Q_ARG(const QString&, modelURL), Q_ARG(const QString&, jointName),
|
||||
Q_ARG(const glm::vec3&, translation), Q_ARG(const glm::quat&, rotation),
|
||||
Q_ARG(float, scale), Q_ARG(bool, allowDuplicates), Q_ARG(bool, useSaved));
|
||||
Q_ARG(const glm::vec3&, translation), Q_ARG(const glm::quat&, rotation),
|
||||
Q_ARG(float, scale), Q_ARG(bool, isSoft),
|
||||
Q_ARG(bool, allowDuplicates), Q_ARG(bool, useSaved));
|
||||
return;
|
||||
}
|
||||
QVector<AttachmentData> attachmentData = getAttachmentData();
|
||||
|
@ -1107,6 +1110,7 @@ void AvatarData::attach(const QString& modelURL, const QString& jointName, const
|
|||
data.translation = translation;
|
||||
data.rotation = rotation;
|
||||
data.scale = scale;
|
||||
data.isSoft = isSoft;
|
||||
attachmentData.append(data);
|
||||
setAttachmentData(attachmentData);
|
||||
}
|
||||
|
@ -1334,7 +1338,7 @@ QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) {
|
|||
attachment.translation >> attachment.rotation >> attachment.scale >> attachment.isSoft;
|
||||
}
|
||||
|
||||
void AttachmentDataObject::setModelURL(const QString& modelURL) const {
|
||||
void AttachmentDataObject::setModelURL(const QString& modelURL) {
|
||||
AttachmentData data = qscriptvalue_cast<AttachmentData>(thisObject());
|
||||
data.modelURL = modelURL;
|
||||
thisObject() = engine()->toScriptValue(data);
|
||||
|
@ -1344,7 +1348,7 @@ QString AttachmentDataObject::getModelURL() const {
|
|||
return qscriptvalue_cast<AttachmentData>(thisObject()).modelURL.toString();
|
||||
}
|
||||
|
||||
void AttachmentDataObject::setJointName(const QString& jointName) const {
|
||||
void AttachmentDataObject::setJointName(const QString& jointName) {
|
||||
AttachmentData data = qscriptvalue_cast<AttachmentData>(thisObject());
|
||||
data.jointName = jointName;
|
||||
thisObject() = engine()->toScriptValue(data);
|
||||
|
@ -1354,7 +1358,7 @@ QString AttachmentDataObject::getJointName() const {
|
|||
return qscriptvalue_cast<AttachmentData>(thisObject()).jointName;
|
||||
}
|
||||
|
||||
void AttachmentDataObject::setTranslation(const glm::vec3& translation) const {
|
||||
void AttachmentDataObject::setTranslation(const glm::vec3& translation) {
|
||||
AttachmentData data = qscriptvalue_cast<AttachmentData>(thisObject());
|
||||
data.translation = translation;
|
||||
thisObject() = engine()->toScriptValue(data);
|
||||
|
@ -1364,7 +1368,7 @@ glm::vec3 AttachmentDataObject::getTranslation() const {
|
|||
return qscriptvalue_cast<AttachmentData>(thisObject()).translation;
|
||||
}
|
||||
|
||||
void AttachmentDataObject::setRotation(const glm::quat& rotation) const {
|
||||
void AttachmentDataObject::setRotation(const glm::quat& rotation) {
|
||||
AttachmentData data = qscriptvalue_cast<AttachmentData>(thisObject());
|
||||
data.rotation = rotation;
|
||||
thisObject() = engine()->toScriptValue(data);
|
||||
|
@ -1374,7 +1378,7 @@ glm::quat AttachmentDataObject::getRotation() const {
|
|||
return qscriptvalue_cast<AttachmentData>(thisObject()).rotation;
|
||||
}
|
||||
|
||||
void AttachmentDataObject::setScale(float scale) const {
|
||||
void AttachmentDataObject::setScale(float scale) {
|
||||
AttachmentData data = qscriptvalue_cast<AttachmentData>(thisObject());
|
||||
data.scale = scale;
|
||||
thisObject() = engine()->toScriptValue(data);
|
||||
|
@ -1384,6 +1388,16 @@ float AttachmentDataObject::getScale() const {
|
|||
return qscriptvalue_cast<AttachmentData>(thisObject()).scale;
|
||||
}
|
||||
|
||||
void AttachmentDataObject::setIsSoft(bool isSoft) {
|
||||
AttachmentData data = qscriptvalue_cast<AttachmentData>(thisObject());
|
||||
data.isSoft = isSoft;
|
||||
thisObject() = engine()->toScriptValue(data);
|
||||
}
|
||||
|
||||
bool AttachmentDataObject::getIsSoft() const {
|
||||
return qscriptvalue_cast<AttachmentData>(thisObject()).isSoft;
|
||||
}
|
||||
|
||||
void registerAvatarTypes(QScriptEngine* engine) {
|
||||
qScriptRegisterSequenceMetaType<QVector<AttachmentData> >(engine);
|
||||
engine->setDefaultPrototype(qMetaTypeId<AttachmentData>(), engine->newQObject(
|
||||
|
|
|
@ -304,8 +304,9 @@ public:
|
|||
Q_INVOKABLE virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
|
||||
|
||||
Q_INVOKABLE virtual void attach(const QString& modelURL, const QString& jointName = QString(),
|
||||
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f,
|
||||
bool allowDuplicates = false, bool useSaved = true);
|
||||
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(),
|
||||
float scale = 1.0f, bool isSoft = false,
|
||||
bool allowDuplicates = false, bool useSaved = true);
|
||||
|
||||
Q_INVOKABLE void detachOne(const QString& modelURL, const QString& jointName = QString());
|
||||
Q_INVOKABLE void detachAll(const QString& modelURL, const QString& jointName = QString());
|
||||
|
@ -344,9 +345,6 @@ public:
|
|||
|
||||
glm::vec3 getClientGlobalPosition() { return _globalPosition; }
|
||||
|
||||
void die() { _isDead = true; }
|
||||
bool isDead() const { return _isDead; }
|
||||
|
||||
public slots:
|
||||
void sendAvatarDataPacket();
|
||||
void sendIdentityPacket();
|
||||
|
@ -423,8 +421,6 @@ protected:
|
|||
// updates about one avatar to another.
|
||||
glm::vec3 _globalPosition;
|
||||
|
||||
bool _isDead { false };
|
||||
|
||||
private:
|
||||
friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar);
|
||||
static QUrl _defaultFullAvatarModelUrl;
|
||||
|
@ -468,23 +464,27 @@ class AttachmentDataObject : public QObject, protected QScriptable {
|
|||
Q_PROPERTY(glm::vec3 translation READ getTranslation WRITE setTranslation)
|
||||
Q_PROPERTY(glm::quat rotation READ getRotation WRITE setRotation)
|
||||
Q_PROPERTY(float scale READ getScale WRITE setScale)
|
||||
Q_PROPERTY(bool isSoft READ getIsSoft WRITE setIsSoft)
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE void setModelURL(const QString& modelURL) const;
|
||||
Q_INVOKABLE void setModelURL(const QString& modelURL);
|
||||
Q_INVOKABLE QString getModelURL() const;
|
||||
|
||||
Q_INVOKABLE void setJointName(const QString& jointName) const;
|
||||
Q_INVOKABLE void setJointName(const QString& jointName);
|
||||
Q_INVOKABLE QString getJointName() const;
|
||||
|
||||
Q_INVOKABLE void setTranslation(const glm::vec3& translation) const;
|
||||
Q_INVOKABLE void setTranslation(const glm::vec3& translation);
|
||||
Q_INVOKABLE glm::vec3 getTranslation() const;
|
||||
|
||||
Q_INVOKABLE void setRotation(const glm::quat& rotation) const;
|
||||
Q_INVOKABLE void setRotation(const glm::quat& rotation);
|
||||
Q_INVOKABLE glm::quat getRotation() const;
|
||||
|
||||
Q_INVOKABLE void setScale(float scale) const;
|
||||
Q_INVOKABLE void setScale(float scale);
|
||||
Q_INVOKABLE float getScale() const;
|
||||
|
||||
Q_INVOKABLE void setIsSoft(bool scale);
|
||||
Q_INVOKABLE bool getIsSoft() const;
|
||||
};
|
||||
|
||||
void registerAvatarTypes(QScriptEngine* engine);
|
||||
|
|
|
@ -122,13 +122,19 @@ void RenderablePolyLineEntityItem::updateVertices() {
|
|||
glm::vec3 v1, v2, tangent, binormal, point;
|
||||
|
||||
int finalIndex = minVectorSize - 1;
|
||||
|
||||
// Guard against an empty polyline
|
||||
if (finalIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < finalIndex; i++) {
|
||||
float width = _strokeWidths.at(i);
|
||||
point = _points.at(i);
|
||||
|
||||
tangent = _points.at(i);
|
||||
|
||||
tangent = _points.at(i + 1) - point;
|
||||
tangent = _points.at(i + 1) - point;
|
||||
glm::vec3 normal = _normals.at(i);
|
||||
binormal = glm::normalize(glm::cross(tangent, normal)) * width;
|
||||
|
||||
|
@ -141,11 +147,6 @@ void RenderablePolyLineEntityItem::updateVertices() {
|
|||
_vertices << v1 << v2;
|
||||
}
|
||||
|
||||
// Guard against an empty polyline
|
||||
if (finalIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For last point we can assume binormals are the same since it represents the last two vertices of quad
|
||||
point = _points.at(finalIndex);
|
||||
v1 = point + binormal;
|
||||
|
|
|
@ -77,7 +77,7 @@ public:
|
|||
glm::mat4 localToVoxelMatrix() const;
|
||||
|
||||
virtual ShapeType getShapeType() const;
|
||||
virtual bool shouldBePhysical() const { return true; }
|
||||
virtual bool shouldBePhysical() const { return !isDead(); }
|
||||
virtual bool isReadyToComputeShape();
|
||||
virtual void computeShapeInfo(ShapeInfo& info);
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ public:
|
|||
}
|
||||
|
||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
|
||||
virtual bool shouldBePhysical() const { return true; }
|
||||
virtual bool shouldBePhysical() const { return !isDead(); }
|
||||
|
||||
virtual void debugDump() const;
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ namespace render {
|
|||
#define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10))
|
||||
#define debugTreeVector(V) V << "[" << V << " in meters ]"
|
||||
|
||||
|
||||
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
|
||||
/// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate
|
||||
/// one directly, instead you must only construct one of it's derived classes with additional features.
|
||||
|
@ -301,7 +302,7 @@ public:
|
|||
|
||||
virtual bool contains(const glm::vec3& point) const;
|
||||
|
||||
virtual bool isReadyToComputeShape() { return true; }
|
||||
virtual bool isReadyToComputeShape() { return !isDead(); }
|
||||
virtual void computeShapeInfo(ShapeInfo& info);
|
||||
virtual float getVolumeEstimate() const { return getDimensions().x * getDimensions().y * getDimensions().z; }
|
||||
|
||||
|
@ -336,6 +337,8 @@ public:
|
|||
|
||||
bool isMoving() const;
|
||||
|
||||
bool isSimulated() const { return _simulated; }
|
||||
|
||||
void* getPhysicsInfo() const { return _physicsInfo; }
|
||||
|
||||
void setPhysicsInfo(void* data) { _physicsInfo = data; }
|
||||
|
@ -390,6 +393,8 @@ public:
|
|||
|
||||
protected:
|
||||
|
||||
void setSimulated(bool simulated) { _simulated = simulated; }
|
||||
|
||||
const QByteArray getActionDataInternal() const;
|
||||
void setActionDataInternal(QByteArray actionData);
|
||||
|
||||
|
|
|
@ -38,15 +38,35 @@ void EntitySimulation::updateEntities() {
|
|||
sortEntitiesThatMoved();
|
||||
}
|
||||
|
||||
void EntitySimulation::getEntitiesToDelete(VectorOfEntities& entitiesToDelete) {
|
||||
void EntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
for (auto entity : _entitiesToDelete) {
|
||||
// this entity is still in its tree, so we insert into the external list
|
||||
// push this entity onto the external list
|
||||
entitiesToDelete.push_back(entity);
|
||||
}
|
||||
_entitiesToDelete.clear();
|
||||
}
|
||||
|
||||
void EntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||
// remove from all internal lists except _entitiesToDelete
|
||||
_mortalEntities.remove(entity);
|
||||
_entitiesToUpdate.remove(entity);
|
||||
_entitiesToSort.remove(entity);
|
||||
_simpleKinematicEntities.remove(entity);
|
||||
_allEntities.remove(entity);
|
||||
entity->setSimulated(false);
|
||||
}
|
||||
|
||||
void EntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
|
||||
assert(entity);
|
||||
assert(entity->isDead());
|
||||
if (entity->isSimulated()) {
|
||||
entity->clearActions(this);
|
||||
removeEntityInternal(entity);
|
||||
_entitiesToDelete.insert(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void EntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||
if (entity->isMoving() && !entity->getPhysicsInfo()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
|
@ -71,15 +91,9 @@ void EntitySimulation::expireMortalEntities(const quint64& now) {
|
|||
EntityItemPointer entity = *itemItr;
|
||||
quint64 expiry = entity->getExpiry();
|
||||
if (expiry < now) {
|
||||
_entitiesToDelete.insert(entity);
|
||||
itemItr = _mortalEntities.erase(itemItr);
|
||||
_entitiesToUpdate.remove(entity);
|
||||
_entitiesToSort.remove(entity);
|
||||
_simpleKinematicEntities.remove(entity);
|
||||
removeEntityInternal(entity);
|
||||
|
||||
_allEntities.remove(entity);
|
||||
entity->_simulated = false;
|
||||
entity->die();
|
||||
prepareEntityForDelete(entity);
|
||||
} else {
|
||||
if (expiry < _nextExpiry) {
|
||||
// remeber the smallest _nextExpiry so we know when to start the next search
|
||||
|
@ -97,7 +111,7 @@ void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
|
|||
SetOfEntities::iterator itemItr = _entitiesToUpdate.begin();
|
||||
while (itemItr != _entitiesToUpdate.end()) {
|
||||
EntityItemPointer entity = *itemItr;
|
||||
// TODO: catch transition from needing update to not as a "change"
|
||||
// TODO: catch transition from needing update to not as a "change"
|
||||
// so we don't have to scan for it here.
|
||||
if (!entity->needsToCallUpdate()) {
|
||||
itemItr = _entitiesToUpdate.erase(itemItr);
|
||||
|
@ -123,16 +137,9 @@ void EntitySimulation::sortEntitiesThatMoved() {
|
|||
AACube newCube = entity->getQueryAACube(success);
|
||||
if (success && !domainBounds.touches(newCube)) {
|
||||
qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
|
||||
_entitiesToDelete.insert(entity);
|
||||
_mortalEntities.remove(entity);
|
||||
_entitiesToUpdate.remove(entity);
|
||||
_simpleKinematicEntities.remove(entity);
|
||||
removeEntityInternal(entity);
|
||||
|
||||
_allEntities.remove(entity);
|
||||
entity->_simulated = false;
|
||||
|
||||
itemItr = _entitiesToSort.erase(itemItr);
|
||||
entity->die();
|
||||
prepareEntityForDelete(entity);
|
||||
} else {
|
||||
moveOperator.addEntityToMoveList(entity, newCube);
|
||||
++itemItr;
|
||||
|
@ -163,39 +170,25 @@ void EntitySimulation::addEntity(EntityItemPointer entity) {
|
|||
addEntityInternal(entity);
|
||||
|
||||
_allEntities.insert(entity);
|
||||
entity->_simulated = true;
|
||||
entity->setSimulated(true);
|
||||
|
||||
// DirtyFlags are used to signal changes to entities that have already been added,
|
||||
// DirtyFlags are used to signal changes to entities that have already been added,
|
||||
// so we can clear them for this entity which has just been added.
|
||||
entity->clearDirtyFlags();
|
||||
}
|
||||
|
||||
void EntitySimulation::removeEntity(EntityItemPointer entity) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
assert(entity);
|
||||
_entitiesToUpdate.remove(entity);
|
||||
_mortalEntities.remove(entity);
|
||||
_entitiesToSort.remove(entity);
|
||||
_simpleKinematicEntities.remove(entity);
|
||||
_entitiesToDelete.remove(entity);
|
||||
removeEntityInternal(entity);
|
||||
|
||||
_allEntities.remove(entity);
|
||||
entity->_simulated = false;
|
||||
}
|
||||
|
||||
void EntitySimulation::changeEntity(EntityItemPointer entity) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
assert(entity);
|
||||
if (!entity->_simulated) {
|
||||
if (!entity->isSimulated()) {
|
||||
// This entity was either never added to the simulation or has been removed
|
||||
// (probably for pending delete), so we don't want to keep a pointer to it
|
||||
// (probably for pending delete), so we don't want to keep a pointer to it
|
||||
// on any internal lists.
|
||||
return;
|
||||
}
|
||||
|
||||
// Although it is not the responsibility of the EntitySimulation to sort the tree for EXTERNAL changes
|
||||
// it IS responsibile for triggering deletes for entities that leave the bounds of the domain, hence
|
||||
// it IS responsibile for triggering deletes for entities that leave the bounds of the domain, hence
|
||||
// we must check for that case here, however we rely on the change event to have set DIRTY_POSITION flag.
|
||||
bool wasRemoved = false;
|
||||
uint32_t dirtyFlags = entity->getDirtyFlags();
|
||||
|
@ -205,13 +198,8 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) {
|
|||
AACube newCube = entity->getQueryAACube(success);
|
||||
if (success && !domainBounds.touches(newCube)) {
|
||||
qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
|
||||
_entitiesToDelete.insert(entity);
|
||||
_mortalEntities.remove(entity);
|
||||
_entitiesToUpdate.remove(entity);
|
||||
_entitiesToSort.remove(entity);
|
||||
_simpleKinematicEntities.remove(entity);
|
||||
removeEntityInternal(entity);
|
||||
entity->_simulated = false;
|
||||
entity->die();
|
||||
prepareEntityForDelete(entity);
|
||||
wasRemoved = true;
|
||||
}
|
||||
}
|
||||
|
@ -244,14 +232,15 @@ void EntitySimulation::clearEntities() {
|
|||
_entitiesToUpdate.clear();
|
||||
_entitiesToSort.clear();
|
||||
_simpleKinematicEntities.clear();
|
||||
_entitiesToDelete.clear();
|
||||
|
||||
clearEntitiesInternal();
|
||||
|
||||
for (auto entityItr : _allEntities) {
|
||||
entityItr->_simulated = false;
|
||||
for (auto entity : _allEntities) {
|
||||
entity->setSimulated(false);
|
||||
entity->die();
|
||||
}
|
||||
_allEntities.clear();
|
||||
_entitiesToDelete.clear();
|
||||
}
|
||||
|
||||
void EntitySimulation::moveSimpleKinematics(const quint64& now) {
|
||||
|
|
|
@ -63,25 +63,20 @@ public:
|
|||
/// \sideeffect sets relevant backpointers in entity, but maybe later when appropriate data structures are locked
|
||||
void addEntity(EntityItemPointer entity);
|
||||
|
||||
/// \param entity pointer to EntityItem to be removed
|
||||
/// \brief the actual removal may happen later when appropriate data structures are locked
|
||||
/// \sideeffect nulls relevant backpointers in entity
|
||||
void removeEntity(EntityItemPointer entity);
|
||||
|
||||
/// \param entity pointer to EntityItem to that may have changed in a way that would affect its simulation
|
||||
/// \param entity pointer to EntityItem that may have changed in a way that would affect its simulation
|
||||
/// call this whenever an entity was changed from some EXTERNAL event (NOT by the EntitySimulation itself)
|
||||
void changeEntity(EntityItemPointer entity);
|
||||
|
||||
void clearEntities();
|
||||
|
||||
void moveSimpleKinematics(const quint64& now);
|
||||
protected: // these only called by the EntityTree?
|
||||
|
||||
public:
|
||||
|
||||
EntityTreePointer getEntityTree() { return _entityTree; }
|
||||
|
||||
void getEntitiesToDelete(VectorOfEntities& entitiesToDelete);
|
||||
virtual void takeEntitiesToDelete(VectorOfEntities& entitiesToDelete);
|
||||
|
||||
/// \param entity pointer to EntityItem that needs to be put on the entitiesToDelete list and removed from others.
|
||||
virtual void prepareEntityForDelete(EntityItemPointer entity);
|
||||
|
||||
signals:
|
||||
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||
|
@ -106,6 +101,9 @@ protected:
|
|||
QList<EntityActionPointer> _actionsToAdd;
|
||||
QSet<QUuid> _actionsToRemove;
|
||||
|
||||
protected:
|
||||
SetOfEntities _entitiesToDelete; // entities simulation decided needed to be deleted (EntityTree will actually delete)
|
||||
|
||||
private:
|
||||
void moveSimpleKinematics();
|
||||
|
||||
|
@ -120,7 +118,6 @@ private:
|
|||
|
||||
|
||||
SetOfEntities _entitiesToUpdate; // entities that need to call EntityItem::update()
|
||||
SetOfEntities _entitiesToDelete; // entities simulation decided needed to be deleted (EntityTree will actually delete)
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -442,6 +442,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
|
|||
const RemovedEntities& entities = theOperator.getEntities();
|
||||
foreach(const EntityToDeleteDetails& details, entities) {
|
||||
EntityItemPointer theEntity = details.entity;
|
||||
theEntity->die();
|
||||
|
||||
if (getIsServer()) {
|
||||
// set up the deleted entities ID
|
||||
|
@ -453,8 +454,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
|
|||
}
|
||||
|
||||
if (_simulation) {
|
||||
theEntity->clearActions(_simulation);
|
||||
_simulation->removeEntity(theEntity);
|
||||
_simulation->prepareEntityForDelete(theEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1006,7 +1006,7 @@ void EntityTree::update() {
|
|||
withWriteLock([&] {
|
||||
_simulation->updateEntities();
|
||||
VectorOfEntities pendingDeletes;
|
||||
_simulation->getEntitiesToDelete(pendingDeletes);
|
||||
_simulation->takeEntitiesToDelete(pendingDeletes);
|
||||
|
||||
if (pendingDeletes.size() > 0) {
|
||||
// translate into list of ID's
|
||||
|
|
|
@ -377,7 +377,7 @@ void ModelEntityItem::setAnimationFPS(float value) {
|
|||
|
||||
// virtual
|
||||
bool ModelEntityItem::shouldBePhysical() const {
|
||||
return getShapeType() != SHAPE_TYPE_NONE;
|
||||
return !isDead() && getShapeType() != SHAPE_TYPE_NONE;
|
||||
}
|
||||
|
||||
void ModelEntityItem::resizeJointArrays(int newSize) {
|
||||
|
|
|
@ -53,6 +53,7 @@ void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
|||
}
|
||||
|
||||
void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||
EntitySimulation::removeEntityInternal(entity);
|
||||
_entitiesWithSimulator.remove(entity);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,11 +22,11 @@ public:
|
|||
virtual ~SimpleEntitySimulation() { clearEntitiesInternal(); }
|
||||
|
||||
protected:
|
||||
virtual void updateEntitiesInternal(const quint64& now);
|
||||
virtual void addEntityInternal(EntityItemPointer entity);
|
||||
virtual void removeEntityInternal(EntityItemPointer entity);
|
||||
virtual void changeEntityInternal(EntityItemPointer entity);
|
||||
virtual void clearEntitiesInternal();
|
||||
virtual void updateEntitiesInternal(const quint64& now) override;
|
||||
virtual void addEntityInternal(EntityItemPointer entity) override;
|
||||
virtual void removeEntityInternal(EntityItemPointer entity) override;
|
||||
virtual void changeEntityInternal(EntityItemPointer entity) override;
|
||||
virtual void clearEntitiesInternal() override;
|
||||
|
||||
SetOfEntities _entitiesWithSimulator;
|
||||
};
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
}
|
||||
|
||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_SPHERE; }
|
||||
virtual bool shouldBePhysical() const { return true; }
|
||||
virtual bool shouldBePhysical() const { return !isDead(); }
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
static bool getDrawZoneBoundaries() { return _drawZoneBoundaries; }
|
||||
static void setDrawZoneBoundaries(bool value) { _drawZoneBoundaries = value; }
|
||||
|
||||
virtual bool isReadyToComputeShape() { return true; }
|
||||
virtual bool isReadyToComputeShape() { return false; }
|
||||
void updateShapeType(ShapeType type) { _shapeType = type; }
|
||||
virtual ShapeType getShapeType() const;
|
||||
|
||||
|
|
|
@ -254,7 +254,7 @@ private:
|
|||
_quit = true;
|
||||
}
|
||||
|
||||
static const uint64_t MAX_SHUTDOWN_WAIT_SECS = 5;
|
||||
static const uint64_t MAX_SHUTDOWN_WAIT_SECS = 2;
|
||||
void stop() {
|
||||
if (_thread.isRunning()) {
|
||||
qDebug() << "Stopping QML render thread " << _thread.currentThreadId();
|
||||
|
|
|
@ -20,10 +20,16 @@
|
|||
|
||||
ThreadedAssignment::ThreadedAssignment(ReceivedMessage& message) :
|
||||
Assignment(message),
|
||||
_isFinished(false)
|
||||
|
||||
_isFinished(false),
|
||||
_domainServerTimer(this),
|
||||
_statsTimer(this)
|
||||
{
|
||||
static const int STATS_TIMEOUT_MS = 1000;
|
||||
_statsTimer.setInterval(STATS_TIMEOUT_MS);
|
||||
connect(&_statsTimer, &QTimer::timeout, this, &ThreadedAssignment::sendStatsPacket);
|
||||
|
||||
connect(&_domainServerTimer, &QTimer::timeout, this, &ThreadedAssignment::checkInWithDomainServerOrExit);
|
||||
_domainServerTimer.setInterval(DOMAIN_SERVER_CHECK_IN_MSECS);
|
||||
}
|
||||
|
||||
void ThreadedAssignment::setFinished(bool isFinished) {
|
||||
|
@ -47,16 +53,9 @@ void ThreadedAssignment::setFinished(bool isFinished) {
|
|||
// send a disconnect packet to the domain
|
||||
nodeList->getDomainHandler().disconnect();
|
||||
|
||||
if (_domainServerTimer) {
|
||||
// stop the domain-server check in timer by calling deleteLater so it gets cleaned up on NL thread
|
||||
_domainServerTimer->deleteLater();
|
||||
_domainServerTimer = nullptr;
|
||||
}
|
||||
|
||||
if (_statsTimer) {
|
||||
_statsTimer->deleteLater();
|
||||
_statsTimer = nullptr;
|
||||
}
|
||||
// stop our owned timers
|
||||
_domainServerTimer.stop();
|
||||
_statsTimer.stop();
|
||||
|
||||
// call our virtual aboutToFinish method - this gives the ThreadedAssignment subclass a chance to cleanup
|
||||
aboutToFinish();
|
||||
|
@ -66,30 +65,22 @@ void ThreadedAssignment::setFinished(bool isFinished) {
|
|||
}
|
||||
}
|
||||
|
||||
void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeType, bool shouldSendStats) {
|
||||
void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeType) {
|
||||
// change the logging target name while the assignment is running
|
||||
LogHandler::getInstance().setTargetName(targetName);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->setOwnerType(nodeType);
|
||||
|
||||
_domainServerTimer = new QTimer;
|
||||
connect(_domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
|
||||
_domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
|
||||
|
||||
// send a domain-server check in immediately
|
||||
// send a domain-server check in immediately and start the timer to fire them every DOMAIN_SERVER_CHECK_IN_MSECS
|
||||
checkInWithDomainServerOrExit();
|
||||
|
||||
// move the domain server time to the NL so check-ins fire from there
|
||||
_domainServerTimer->moveToThread(nodeList->thread());
|
||||
_domainServerTimer.start();
|
||||
|
||||
if (shouldSendStats) {
|
||||
// start sending stats packet once we connect to the domain
|
||||
connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &ThreadedAssignment::startSendingStats);
|
||||
|
||||
// stop sending stats if we disconnect
|
||||
connect(&nodeList->getDomainHandler(), &DomainHandler::disconnectedFromDomain, this, &ThreadedAssignment::stopSendingStats);
|
||||
}
|
||||
// start sending stats packet once we connect to the domain
|
||||
connect(&nodeList->getDomainHandler(), SIGNAL(connectedToDomain(const QString&)), &_statsTimer, SLOT(start()));
|
||||
|
||||
// stop sending stats if we disconnect
|
||||
connect(&nodeList->getDomainHandler(), &DomainHandler::disconnectedFromDomain, &_statsTimer, &QTimer::stop);
|
||||
}
|
||||
|
||||
void ThreadedAssignment::addPacketStatsAndSendStatsPacket(QJsonObject &statsObject) {
|
||||
|
@ -111,28 +102,12 @@ void ThreadedAssignment::sendStatsPacket() {
|
|||
addPacketStatsAndSendStatsPacket(statsObject);
|
||||
}
|
||||
|
||||
void ThreadedAssignment::startSendingStats() {
|
||||
// send the stats packet every 1s
|
||||
if (!_statsTimer) {
|
||||
_statsTimer = new QTimer;
|
||||
connect(_statsTimer, &QTimer::timeout, this, &ThreadedAssignment::sendStatsPacket);
|
||||
}
|
||||
|
||||
_statsTimer->start(1000);
|
||||
}
|
||||
|
||||
void ThreadedAssignment::stopSendingStats() {
|
||||
if (_statsTimer) {
|
||||
// stop sending stats, we just disconnected from domain
|
||||
_statsTimer->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadedAssignment::checkInWithDomainServerOrExit() {
|
||||
if (DependencyManager::get<NodeList>()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
setFinished(true);
|
||||
} else {
|
||||
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
QMetaObject::invokeMethod(nodeList.data(), "sendDomainServerCheckIn");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,17 +38,15 @@ signals:
|
|||
void finished();
|
||||
|
||||
protected:
|
||||
void commonInit(const QString& targetName, NodeType_t nodeType, bool shouldSendStats = true);
|
||||
void commonInit(const QString& targetName, NodeType_t nodeType);
|
||||
bool _isFinished;
|
||||
QTimer* _domainServerTimer = nullptr;
|
||||
QTimer* _statsTimer = nullptr;
|
||||
QTimer _domainServerTimer;
|
||||
QTimer _statsTimer;
|
||||
|
||||
protected slots:
|
||||
void domainSettingsRequestFailed();
|
||||
|
||||
private slots:
|
||||
void startSendingStats();
|
||||
void stopSendingStats();
|
||||
void checkInWithDomainServerOrExit();
|
||||
};
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5;
|
|||
|
||||
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
|
||||
bool EntityMotionState::entityTreeIsLocked() const {
|
||||
EntityTreeElementPointer element = _entity ? _entity->getElement() : nullptr;
|
||||
EntityTreeElementPointer element = _entity->getElement();
|
||||
EntityTreePointer tree = element ? element->getTree() : nullptr;
|
||||
if (!tree) {
|
||||
return true;
|
||||
|
@ -50,7 +50,8 @@ bool entityTreeIsLocked() {
|
|||
|
||||
EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer entity) :
|
||||
ObjectMotionState(shape),
|
||||
_entity(entity),
|
||||
_entityPtr(entity),
|
||||
_entity(entity.get()),
|
||||
_sentInactive(true),
|
||||
_lastStep(0),
|
||||
_serverPosition(0.0f),
|
||||
|
@ -69,14 +70,14 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
|
|||
_loopsWithoutOwner(0)
|
||||
{
|
||||
_type = MOTIONSTATE_TYPE_ENTITY;
|
||||
assert(_entity != nullptr);
|
||||
assert(_entity);
|
||||
assert(entityTreeIsLocked());
|
||||
setMass(_entity->computeMass());
|
||||
}
|
||||
|
||||
EntityMotionState::~EntityMotionState() {
|
||||
// be sure to clear _entity before calling the destructor
|
||||
assert(!_entity);
|
||||
assert(_entity);
|
||||
_entity = nullptr;
|
||||
}
|
||||
|
||||
void EntityMotionState::updateServerPhysicsVariables(const QUuid& sessionID) {
|
||||
|
@ -138,11 +139,6 @@ bool EntityMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine*
|
|||
return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
|
||||
}
|
||||
|
||||
void EntityMotionState::clearObjectBackPointer() {
|
||||
ObjectMotionState::clearObjectBackPointer();
|
||||
_entity = nullptr;
|
||||
}
|
||||
|
||||
MotionType EntityMotionState::computeObjectMotionType() const {
|
||||
if (!_entity) {
|
||||
return MOTION_TYPE_STATIC;
|
||||
|
@ -221,22 +217,16 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
|||
|
||||
|
||||
// virtual and protected
|
||||
bool EntityMotionState::isReadyToComputeShape() {
|
||||
if (_entity) {
|
||||
return _entity->isReadyToComputeShape();
|
||||
}
|
||||
return false;
|
||||
bool EntityMotionState::isReadyToComputeShape() const {
|
||||
return _entity->isReadyToComputeShape();
|
||||
}
|
||||
|
||||
// virtual and protected
|
||||
btCollisionShape* EntityMotionState::computeNewShape() {
|
||||
if (_entity) {
|
||||
ShapeInfo shapeInfo;
|
||||
assert(entityTreeIsLocked());
|
||||
_entity->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
return nullptr;
|
||||
ShapeInfo shapeInfo;
|
||||
assert(entityTreeIsLocked());
|
||||
_entity->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
|
||||
bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const {
|
||||
|
@ -553,26 +543,17 @@ void EntityMotionState::clearIncomingDirtyFlags() {
|
|||
|
||||
// virtual
|
||||
quint8 EntityMotionState::getSimulationPriority() const {
|
||||
if (_entity) {
|
||||
return _entity->getSimulationPriority();
|
||||
}
|
||||
return NO_PRORITY;
|
||||
return _entity->getSimulationPriority();
|
||||
}
|
||||
|
||||
// virtual
|
||||
QUuid EntityMotionState::getSimulatorID() const {
|
||||
if (_entity) {
|
||||
assert(entityTreeIsLocked());
|
||||
return _entity->getSimulatorID();
|
||||
}
|
||||
return QUuid();
|
||||
assert(entityTreeIsLocked());
|
||||
return _entity->getSimulatorID();
|
||||
}
|
||||
|
||||
// virtual
|
||||
void EntityMotionState::bump(quint8 priority) {
|
||||
if (_entity) {
|
||||
setOutgoingPriority(glm::max(VOLUNTEER_SIMULATION_PRIORITY, --priority));
|
||||
}
|
||||
setOutgoingPriority(glm::max(VOLUNTEER_SIMULATION_PRIORITY, --priority));
|
||||
}
|
||||
|
||||
void EntityMotionState::resetMeasuredBodyAcceleration() {
|
||||
|
@ -623,19 +604,13 @@ void EntityMotionState::setMotionType(MotionType motionType) {
|
|||
|
||||
|
||||
// virtual
|
||||
QString EntityMotionState::getName() {
|
||||
if (_entity) {
|
||||
assert(entityTreeIsLocked());
|
||||
return _entity->getName();
|
||||
}
|
||||
return "";
|
||||
QString EntityMotionState::getName() const {
|
||||
assert(entityTreeIsLocked());
|
||||
return _entity->getName();
|
||||
}
|
||||
|
||||
// virtual
|
||||
int16_t EntityMotionState::computeCollisionGroup() {
|
||||
if (!_entity) {
|
||||
return COLLISION_GROUP_STATIC;
|
||||
}
|
||||
int16_t EntityMotionState::computeCollisionGroup() const {
|
||||
if (_entity->getIgnoreForCollisions()) {
|
||||
return COLLISION_GROUP_COLLISIONLESS;
|
||||
}
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
#ifndef hifi_EntityMotionState_h
|
||||
#define hifi_EntityMotionState_h
|
||||
|
||||
#include <EntityTypes.h>
|
||||
#include <AACube.h>
|
||||
|
||||
#include "ObjectMotionState.h"
|
||||
|
||||
class EntityItem;
|
||||
|
||||
// From the MotionState's perspective:
|
||||
// Inside = physics simulation
|
||||
|
@ -38,10 +38,10 @@ public:
|
|||
virtual bool isMoving() const;
|
||||
|
||||
// this relays incoming position/rotation to the RigidBody
|
||||
virtual void getWorldTransform(btTransform& worldTrans) const;
|
||||
virtual void getWorldTransform(btTransform& worldTrans) const override;
|
||||
|
||||
// this relays outgoing position/rotation to the EntityItem
|
||||
virtual void setWorldTransform(const btTransform& worldTrans);
|
||||
virtual void setWorldTransform(const btTransform& worldTrans) override;
|
||||
|
||||
bool isCandidateForOwnership(const QUuid& sessionID) const;
|
||||
bool remoteSimulationOutOfSync(uint32_t simulationStep);
|
||||
|
@ -55,32 +55,32 @@ public:
|
|||
void resetAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount = 0; }
|
||||
quint8 getAccelerationNearlyGravityCount() { return _accelerationNearlyGravityCount; }
|
||||
|
||||
virtual float getObjectRestitution() const { return _entity->getRestitution(); }
|
||||
virtual float getObjectFriction() const { return _entity->getFriction(); }
|
||||
virtual float getObjectLinearDamping() const { return _entity->getDamping(); }
|
||||
virtual float getObjectAngularDamping() const { return _entity->getAngularDamping(); }
|
||||
virtual float getObjectRestitution() const override { return _entity->getRestitution(); }
|
||||
virtual float getObjectFriction() const override { return _entity->getFriction(); }
|
||||
virtual float getObjectLinearDamping() const override { return _entity->getDamping(); }
|
||||
virtual float getObjectAngularDamping() const override { return _entity->getAngularDamping(); }
|
||||
|
||||
virtual glm::vec3 getObjectPosition() const { return _entity->getPosition() - ObjectMotionState::getWorldOffset(); }
|
||||
virtual glm::quat getObjectRotation() const { return _entity->getRotation(); }
|
||||
virtual glm::vec3 getObjectLinearVelocity() const { return _entity->getVelocity(); }
|
||||
virtual glm::vec3 getObjectAngularVelocity() const { return _entity->getAngularVelocity(); }
|
||||
virtual glm::vec3 getObjectGravity() const { return _entity->getGravity(); }
|
||||
virtual glm::vec3 getObjectLinearVelocityChange() const;
|
||||
virtual glm::vec3 getObjectPosition() const override { return _entity->getPosition() - ObjectMotionState::getWorldOffset(); }
|
||||
virtual glm::quat getObjectRotation() const override { return _entity->getRotation(); }
|
||||
virtual glm::vec3 getObjectLinearVelocity() const override { return _entity->getVelocity(); }
|
||||
virtual glm::vec3 getObjectAngularVelocity() const override { return _entity->getAngularVelocity(); }
|
||||
virtual glm::vec3 getObjectGravity() const override { return _entity->getGravity(); }
|
||||
virtual glm::vec3 getObjectLinearVelocityChange() const override;
|
||||
|
||||
virtual const QUuid& getObjectID() const { return _entity->getID(); }
|
||||
virtual const QUuid& getObjectID() const override { return _entity->getID(); }
|
||||
|
||||
virtual quint8 getSimulationPriority() const;
|
||||
virtual QUuid getSimulatorID() const;
|
||||
virtual void bump(quint8 priority);
|
||||
virtual quint8 getSimulationPriority() const override;
|
||||
virtual QUuid getSimulatorID() const override;
|
||||
virtual void bump(quint8 priority) override;
|
||||
|
||||
EntityItemPointer getEntity() const { return _entity; }
|
||||
EntityItemPointer getEntity() const { return _entityPtr.lock(); }
|
||||
|
||||
void resetMeasuredBodyAcceleration();
|
||||
void measureBodyAcceleration();
|
||||
|
||||
virtual QString getName();
|
||||
virtual QString getName() const override;
|
||||
|
||||
virtual int16_t computeCollisionGroup();
|
||||
virtual int16_t computeCollisionGroup() const override;
|
||||
|
||||
// eternal logic can suggest a simuator priority bid for the next outgoing update
|
||||
void setOutgoingPriority(quint8 priority);
|
||||
|
@ -92,12 +92,19 @@ protected:
|
|||
bool entityTreeIsLocked() const;
|
||||
#endif
|
||||
|
||||
virtual bool isReadyToComputeShape();
|
||||
virtual bool isReadyToComputeShape() const override;
|
||||
virtual btCollisionShape* computeNewShape();
|
||||
virtual void clearObjectBackPointer();
|
||||
virtual void setMotionType(MotionType motionType);
|
||||
|
||||
EntityItemPointer _entity;
|
||||
// In the glorious future (when entities lib depends on physics lib) the EntityMotionState will be
|
||||
// properly "owned" by the EntityItem and will be deleted by it in the dtor. In pursuit of that
|
||||
// state of affairs we can't keep a real EntityItemPointer as data member (it would produce a
|
||||
// recursive dependency). Instead we keep a EntityItemWeakPointer to break that dependency while
|
||||
// still granting us the capability to generate EntityItemPointers as necessary (for external data
|
||||
// structures that use the MotionState to get to the EntityItem).
|
||||
EntityItemWeakPointer _entityPtr;
|
||||
// Meanwhile we also keep a raw EntityItem* for internal stuff where the pointer is guaranteed valid.
|
||||
EntityItem* _entity;
|
||||
|
||||
bool _sentInactive; // true if body was inactive when we sent last update
|
||||
|
||||
|
|
|
@ -237,10 +237,13 @@ void ObjectAction::setAngularVelocity(glm::vec3 angularVelocity) {
|
|||
rigidBody->activate();
|
||||
}
|
||||
|
||||
void ObjectAction::activateBody() {
|
||||
void ObjectAction::activateBody(bool forceActivation) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (rigidBody) {
|
||||
rigidBody->activate();
|
||||
rigidBody->activate(forceActivation);
|
||||
assert(rigidBody->isActive());
|
||||
} else {
|
||||
qDebug() << "ObjectAction::activateBody -- no rigid body" << (void*)rigidBody;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ protected:
|
|||
virtual void setLinearVelocity(glm::vec3 linearVelocity) override;
|
||||
virtual glm::vec3 getAngularVelocity() override;
|
||||
virtual void setAngularVelocity(glm::vec3 angularVelocity) override;
|
||||
virtual void activateBody();
|
||||
virtual void activateBody(bool forceActivation = false);
|
||||
virtual void forceBodyNonStatic();
|
||||
|
||||
EntityItemWeakPointer _ownerEntity;
|
||||
|
|
|
@ -72,7 +72,8 @@ ObjectMotionState::ObjectMotionState(btCollisionShape* shape) :
|
|||
|
||||
ObjectMotionState::~ObjectMotionState() {
|
||||
assert(!_body);
|
||||
assert(!_shape);
|
||||
releaseShape();
|
||||
_type = MOTIONSTATE_TYPE_INVALID;
|
||||
}
|
||||
|
||||
void ObjectMotionState::setBodyLinearVelocity(const glm::vec3& velocity) const {
|
||||
|
|
|
@ -134,9 +134,9 @@ public:
|
|||
virtual QUuid getSimulatorID() const = 0;
|
||||
virtual void bump(quint8 priority) {}
|
||||
|
||||
virtual QString getName() { return ""; }
|
||||
virtual QString getName() const { return ""; }
|
||||
|
||||
virtual int16_t computeCollisionGroup() = 0;
|
||||
virtual int16_t computeCollisionGroup() const = 0;
|
||||
|
||||
bool isActive() const { return _body ? _body->isActive() : false; }
|
||||
|
||||
|
@ -148,14 +148,11 @@ public:
|
|||
friend class PhysicsEngine;
|
||||
|
||||
protected:
|
||||
virtual bool isReadyToComputeShape() = 0;
|
||||
virtual bool isReadyToComputeShape() const = 0;
|
||||
virtual btCollisionShape* computeNewShape() = 0;
|
||||
void setMotionType(MotionType motionType);
|
||||
void updateCCDConfiguration();
|
||||
|
||||
// clearObjectBackPointer() overrrides should call the base method, then actually clear the object back pointer.
|
||||
virtual void clearObjectBackPointer() { _type = MOTIONSTATE_TYPE_INVALID; }
|
||||
|
||||
void setRigidBody(btRigidBody* body);
|
||||
|
||||
MotionStateType _type = MOTIONSTATE_TYPE_INVALID; // type of MotionState
|
||||
|
|
|
@ -44,10 +44,11 @@ void PhysicalEntitySimulation::updateEntitiesInternal(const quint64& now) {
|
|||
|
||||
void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||
assert(entity);
|
||||
assert(!entity->isDead());
|
||||
if (entity->shouldBePhysical()) {
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (!motionState) {
|
||||
_pendingAdds.insert(entity);
|
||||
_entitiesToAddToPhysics.insert(entity);
|
||||
}
|
||||
} else if (entity->isMoving()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
|
@ -55,14 +56,33 @@ void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
|||
}
|
||||
|
||||
void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||
EntitySimulation::removeEntityInternal(entity);
|
||||
_entitiesToAddToPhysics.remove(entity);
|
||||
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
motionState->clearObjectBackPointer();
|
||||
entity->setPhysicsInfo(nullptr);
|
||||
_pendingRemoves.insert(motionState);
|
||||
_outgoingChanges.remove(motionState);
|
||||
_entitiesToRemoveFromPhysics.insert(entity);
|
||||
} else {
|
||||
_entitiesToDelete.insert(entity);
|
||||
}
|
||||
_pendingAdds.remove(entity);
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
for (auto entity : _entitiesToDelete) {
|
||||
// this entity is still in its tree, so we insert into the external list
|
||||
entitiesToDelete.push_back(entity);
|
||||
|
||||
// Someday when we invert the entities/physics lib dependencies we can let EntityItem delete its own PhysicsInfo
|
||||
// rather than do it here
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
delete motionState;
|
||||
entity->setPhysicsInfo(nullptr);
|
||||
}
|
||||
}
|
||||
_entitiesToDelete.clear();
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||
|
@ -74,8 +94,8 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
|||
// the entity should be removed from the physical simulation
|
||||
_pendingChanges.remove(motionState);
|
||||
_physicalObjects.remove(motionState);
|
||||
_pendingRemoves.insert(motionState);
|
||||
_outgoingChanges.remove(motionState);
|
||||
_entitiesToRemoveFromPhysics.insert(entity);
|
||||
if (entity->isMoving()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
}
|
||||
|
@ -85,7 +105,7 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
|||
} else if (entity->shouldBePhysical()) {
|
||||
// The intent is for this object to be in the PhysicsEngine, but it has no MotionState yet.
|
||||
// Perhaps it's shape has changed and it can now be added?
|
||||
_pendingAdds.insert(entity);
|
||||
_entitiesToAddToPhysics.insert(entity);
|
||||
_simpleKinematicEntities.remove(entity); // just in case it's non-physical-kinematic
|
||||
} else if (entity->isMoving()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
|
@ -102,55 +122,70 @@ void PhysicalEntitySimulation::clearEntitiesInternal() {
|
|||
// first disconnect each MotionStates from its Entity
|
||||
for (auto stateItr : _physicalObjects) {
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(&(*stateItr));
|
||||
EntityItemPointer entity = motionState->getEntity();
|
||||
if (entity) {
|
||||
entity->setPhysicsInfo(nullptr);
|
||||
}
|
||||
motionState->clearObjectBackPointer();
|
||||
_entitiesToDelete.insert(motionState->getEntity());
|
||||
}
|
||||
|
||||
// then delete the objects (aka MotionStates)
|
||||
_physicsEngine->deleteObjects(_physicalObjects);
|
||||
// then remove the objects (aka MotionStates) from physics
|
||||
_physicsEngine->removeObjects(_physicalObjects);
|
||||
|
||||
// finally clear all lists (which now have only dangling pointers)
|
||||
// delete the MotionStates
|
||||
// TODO: after we invert the entities/physics lib dependencies we will let EntityItem delete
|
||||
// its own PhysicsInfo rather than do it here
|
||||
for (auto entity : _entitiesToDelete) {
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
delete motionState;
|
||||
entity->setPhysicsInfo(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// finally clear all lists maintained by this class
|
||||
_physicalObjects.clear();
|
||||
_pendingRemoves.clear();
|
||||
_pendingAdds.clear();
|
||||
_entitiesToRemoveFromPhysics.clear();
|
||||
_entitiesToAddToPhysics.clear();
|
||||
_pendingChanges.clear();
|
||||
_outgoingChanges.clear();
|
||||
}
|
||||
|
||||
// virtual
|
||||
void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
|
||||
assert(entity);
|
||||
assert(entity->isDead());
|
||||
entity->clearActions(this);
|
||||
removeEntityInternal(entity);
|
||||
}
|
||||
// end EntitySimulation overrides
|
||||
|
||||
|
||||
void PhysicalEntitySimulation::getObjectsToDelete(VectorOfMotionStates& result) {
|
||||
void PhysicalEntitySimulation::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
QMutexLocker lock(&_mutex);
|
||||
for (auto stateItr : _pendingRemoves) {
|
||||
EntityMotionState* motionState = &(*stateItr);
|
||||
_pendingChanges.remove(motionState);
|
||||
_physicalObjects.remove(motionState);
|
||||
|
||||
EntityItemPointer entity = motionState->getEntity();
|
||||
if (entity) {
|
||||
_pendingAdds.remove(entity);
|
||||
entity->setPhysicsInfo(nullptr);
|
||||
motionState->clearObjectBackPointer();
|
||||
for (auto entity: _entitiesToRemoveFromPhysics) {
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
_pendingChanges.remove(motionState);
|
||||
_physicalObjects.remove(motionState);
|
||||
result.push_back(motionState);
|
||||
}
|
||||
_entitiesToAddToPhysics.remove(entity);
|
||||
if (entity->isDead()) {
|
||||
_entitiesToDelete.insert(entity);
|
||||
}
|
||||
result.push_back(motionState);
|
||||
}
|
||||
_pendingRemoves.clear();
|
||||
_entitiesToRemoveFromPhysics.clear();
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::getObjectsToAdd(VectorOfMotionStates& result) {
|
||||
void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
QMutexLocker lock(&_mutex);
|
||||
SetOfEntities::iterator entityItr = _pendingAdds.begin();
|
||||
while (entityItr != _pendingAdds.end()) {
|
||||
EntityItemPointer entity = *entityItr;
|
||||
SetOfEntities::iterator entityItr = _entitiesToAddToPhysics.begin();
|
||||
while (entityItr != _entitiesToAddToPhysics.end()) {
|
||||
EntityItemPointer entity = (*entityItr);
|
||||
assert(!entity->getPhysicsInfo());
|
||||
if (!entity->shouldBePhysical()) {
|
||||
// this entity should no longer be on the internal _pendingAdds
|
||||
entityItr = _pendingAdds.erase(entityItr);
|
||||
if (entity->isDead()) {
|
||||
prepareEntityForDelete(entity);
|
||||
} else if (!entity->shouldBePhysical()) {
|
||||
// this entity should no longer be on the internal _entitiesToAddToPhysics
|
||||
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
||||
if (entity->isMoving()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
}
|
||||
|
@ -163,7 +198,7 @@ void PhysicalEntitySimulation::getObjectsToAdd(VectorOfMotionStates& result) {
|
|||
entity->setPhysicsInfo(static_cast<void*>(motionState));
|
||||
_physicalObjects.insert(motionState);
|
||||
result.push_back(motionState);
|
||||
entityItr = _pendingAdds.erase(entityItr);
|
||||
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
||||
} else {
|
||||
//qDebug() << "Warning! Failed to generate new shape for entity." << entity->getName();
|
||||
++entityItr;
|
||||
|
@ -199,12 +234,11 @@ void PhysicalEntitySimulation::handleOutgoingChanges(const VectorOfMotionStates&
|
|||
if (state && state->getType() == MOTIONSTATE_TYPE_ENTITY) {
|
||||
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
||||
EntityItemPointer entity = entityState->getEntity();
|
||||
if (entity) {
|
||||
if (entityState->isCandidateForOwnership(sessionID)) {
|
||||
_outgoingChanges.insert(entityState);
|
||||
}
|
||||
_entitiesToSort.insert(entityState->getEntity());
|
||||
assert(entity.get());
|
||||
if (entityState->isCandidateForOwnership(sessionID)) {
|
||||
_outgoingChanges.insert(entityState);
|
||||
}
|
||||
_entitiesToSort.insert(entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ public:
|
|||
virtual void addAction(EntityActionPointer action) override;
|
||||
virtual void applyActionChanges() override;
|
||||
|
||||
virtual void takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) override;
|
||||
|
||||
protected: // only called by EntitySimulation
|
||||
// overrides for EntitySimulation
|
||||
virtual void updateEntitiesInternal(const quint64& now) override;
|
||||
|
@ -44,8 +46,10 @@ protected: // only called by EntitySimulation
|
|||
virtual void clearEntitiesInternal() override;
|
||||
|
||||
public:
|
||||
void getObjectsToDelete(VectorOfMotionStates& result);
|
||||
void getObjectsToAdd(VectorOfMotionStates& result);
|
||||
virtual void prepareEntityForDelete(EntityItemPointer entity) override;
|
||||
|
||||
void getObjectsToRemoveFromPhysics(VectorOfMotionStates& result);
|
||||
void getObjectsToAddToPhysics(VectorOfMotionStates& result);
|
||||
void setObjectsToChange(const VectorOfMotionStates& objectsToChange);
|
||||
void getObjectsToChange(VectorOfMotionStates& result);
|
||||
|
||||
|
@ -55,12 +59,10 @@ public:
|
|||
EntityEditPacketSender* getPacketSender() { return _entityPacketSender; }
|
||||
|
||||
private:
|
||||
// incoming changes
|
||||
SetOfEntityMotionStates _pendingRemoves; // EntityMotionStates to be removed from PhysicsEngine (and deleted)
|
||||
SetOfEntities _pendingAdds; // entities to be be added to PhysicsEngine (and a their EntityMotionState created)
|
||||
SetOfEntityMotionStates _pendingChanges; // EntityMotionStates already in PhysicsEngine that need their physics changed
|
||||
SetOfEntities _entitiesToRemoveFromPhysics;
|
||||
SetOfEntities _entitiesToAddToPhysics;
|
||||
|
||||
// outgoing changes
|
||||
SetOfEntityMotionStates _pendingChanges; // EntityMotionStates already in PhysicsEngine that need their physics changed
|
||||
SetOfEntityMotionStates _outgoingChanges; // EntityMotionStates for which we need to send updates to entity-server
|
||||
|
||||
SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine
|
||||
|
|
|
@ -67,7 +67,8 @@ void PhysicsEngine::init() {
|
|||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::addObject(ObjectMotionState* motionState) {
|
||||
// private
|
||||
void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
||||
assert(motionState);
|
||||
|
||||
btVector3 inertia(0.0f, 0.0f, 0.0f);
|
||||
|
@ -144,7 +145,8 @@ void PhysicsEngine::addObject(ObjectMotionState* motionState) {
|
|||
motionState->clearIncomingDirtyFlags();
|
||||
}
|
||||
|
||||
void PhysicsEngine::removeObject(ObjectMotionState* object) {
|
||||
// private
|
||||
void PhysicsEngine::removeObjectFromDynamicsWorld(ObjectMotionState* object) {
|
||||
// wake up anything touching this object
|
||||
bump(object);
|
||||
removeContacts(object);
|
||||
|
@ -154,38 +156,34 @@ void PhysicsEngine::removeObject(ObjectMotionState* object) {
|
|||
_dynamicsWorld->removeRigidBody(body);
|
||||
}
|
||||
|
||||
void PhysicsEngine::deleteObjects(const VectorOfMotionStates& objects) {
|
||||
void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) {
|
||||
for (auto object : objects) {
|
||||
removeObject(object);
|
||||
removeObjectFromDynamicsWorld(object);
|
||||
|
||||
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
object->setRigidBody(nullptr);
|
||||
body->setMotionState(nullptr);
|
||||
delete body;
|
||||
object->releaseShape();
|
||||
delete object;
|
||||
}
|
||||
}
|
||||
|
||||
// Same as above, but takes a Set instead of a Vector. Should only be called during teardown.
|
||||
void PhysicsEngine::deleteObjects(const SetOfMotionStates& objects) {
|
||||
void PhysicsEngine::removeObjects(const SetOfMotionStates& objects) {
|
||||
for (auto object : objects) {
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
removeObject(object);
|
||||
removeObjectFromDynamicsWorld(object);
|
||||
|
||||
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
|
||||
object->setRigidBody(nullptr);
|
||||
body->setMotionState(nullptr);
|
||||
delete body;
|
||||
object->releaseShape();
|
||||
delete object;
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::addObjects(const VectorOfMotionStates& objects) {
|
||||
for (auto object : objects) {
|
||||
addObject(object);
|
||||
addObjectToDynamicsWorld(object);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,8 +209,8 @@ VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& ob
|
|||
}
|
||||
|
||||
void PhysicsEngine::reinsertObject(ObjectMotionState* object) {
|
||||
removeObject(object);
|
||||
addObject(object);
|
||||
removeObjectFromDynamicsWorld(object);
|
||||
addObjectToDynamicsWorld(object);
|
||||
}
|
||||
|
||||
void PhysicsEngine::removeContacts(ObjectMotionState* motionState) {
|
||||
|
|
|
@ -54,11 +54,9 @@ public:
|
|||
void setSessionUUID(const QUuid& sessionID) { _sessionID = sessionID; }
|
||||
const QUuid& getSessionID() const { return _sessionID; }
|
||||
|
||||
void addObject(ObjectMotionState* motionState);
|
||||
void removeObject(ObjectMotionState* motionState);
|
||||
void removeObjects(const VectorOfMotionStates& objects);
|
||||
void removeObjects(const SetOfMotionStates& objects); // only called during teardown
|
||||
|
||||
void deleteObjects(const VectorOfMotionStates& objects);
|
||||
void deleteObjects(const SetOfMotionStates& objects); // only called during teardown
|
||||
void addObjects(const VectorOfMotionStates& objects);
|
||||
VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects);
|
||||
void reinsertObject(ObjectMotionState* object);
|
||||
|
@ -86,8 +84,6 @@ public:
|
|||
/// \brief call bump on any objects that touch the object corresponding to motionState
|
||||
void bump(ObjectMotionState* motionState);
|
||||
|
||||
void removeRigidBody(btRigidBody* body);
|
||||
|
||||
void setCharacterController(CharacterController* character);
|
||||
|
||||
void dumpNextStats() { _dumpNextStats = true; }
|
||||
|
@ -100,6 +96,9 @@ public:
|
|||
void forEachAction(std::function<void(EntityActionPointer)> actor);
|
||||
|
||||
private:
|
||||
void addObjectToDynamicsWorld(ObjectMotionState* motionState);
|
||||
void removeObjectFromDynamicsWorld(ObjectMotionState* motionState);
|
||||
|
||||
void removeContacts(ObjectMotionState* motionState);
|
||||
|
||||
void doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB);
|
||||
|
@ -116,7 +115,6 @@ private:
|
|||
|
||||
ContactMap _contactMap;
|
||||
uint32_t _numContactFrames = 0;
|
||||
uint32_t _lastNumSubstepsAtUpdateInternal = 0;
|
||||
|
||||
/// character collisions
|
||||
CharacterController* _myAvatarController;
|
||||
|
|
|
@ -51,6 +51,8 @@
|
|||
|
||||
#include "MIDIEvent.h"
|
||||
|
||||
static const QString SCRIPT_EXCEPTION_FORMAT = "[UncaughtException] %1 in %2:%3";
|
||||
|
||||
Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature)
|
||||
static int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature>();
|
||||
|
||||
|
@ -112,7 +114,7 @@ static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName
|
|||
const auto line = QString::number(engine.uncaughtExceptionLineNumber());
|
||||
engine.clearExceptions();
|
||||
|
||||
auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line);
|
||||
auto message = QString(SCRIPT_EXCEPTION_FORMAT).arg(exception, fileName, line);
|
||||
if (!backtrace.empty()) {
|
||||
static const auto lineSeparator = "\n ";
|
||||
message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
|
||||
|
@ -133,6 +135,10 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam
|
|||
_allScriptsMutex.lock();
|
||||
_allKnownScriptEngines.insert(this);
|
||||
_allScriptsMutex.unlock();
|
||||
|
||||
connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) {
|
||||
hadUncaughtExceptions(*this, _fileNameString);
|
||||
});
|
||||
}
|
||||
|
||||
ScriptEngine::~ScriptEngine() {
|
||||
|
|
|
@ -115,6 +115,9 @@ public:
|
|||
void forEachChild(std::function<void(SpatiallyNestablePointer)> actor);
|
||||
void forEachDescendant(std::function<void(SpatiallyNestablePointer)> actor);
|
||||
|
||||
void die() { _isDead = true; }
|
||||
bool isDead() const { return _isDead; }
|
||||
|
||||
protected:
|
||||
const NestableType _nestableType; // EntityItem or an AvatarData
|
||||
QUuid _id;
|
||||
|
@ -141,7 +144,8 @@ protected:
|
|||
private:
|
||||
mutable ReadWriteLockable _transformLock;
|
||||
Transform _transform; // this is to be combined with parent's world-transform to produce this' world-transform.
|
||||
mutable bool _parentKnowsMe = false;
|
||||
mutable bool _parentKnowsMe { false };
|
||||
bool _isDead { false };
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include <QtQuick/QQuickItem>
|
||||
#include <QtQml/QQmlContext>
|
||||
#include <QtQml/QQmlEngine>
|
||||
|
||||
#include <QtWebSockets/QWebSocketServer>
|
||||
#include <QtWebSockets/QWebSocket>
|
||||
|
@ -90,7 +92,6 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
|||
QString url;
|
||||
QString title;
|
||||
int width = 100, height = 100;
|
||||
bool isToolWindow = false;
|
||||
bool visible = true;
|
||||
if (argumentCount > 1) {
|
||||
|
||||
|
@ -141,6 +142,7 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
|||
Q_ARG(std::function<void(QQmlContext*, QObject*)>, [&](QQmlContext* context, QObject* object) {
|
||||
setupServer();
|
||||
retVal = function(context, object);
|
||||
context->engine()->setObjectOwnership(retVal->_qmlWindow, QQmlEngine::CppOwnership);
|
||||
registerObject(url.toLower(), retVal);
|
||||
if (!title.isEmpty()) {
|
||||
retVal->setTitle(title);
|
||||
|
@ -274,7 +276,7 @@ void QmlWindowClass::hasClosed() {
|
|||
}
|
||||
|
||||
void QmlWindowClass::raise() {
|
||||
// FIXME
|
||||
QMetaObject::invokeMethod(_qmlWindow, "raiseWindow", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
#include "QmlWindowClass.moc"
|
||||
|
|
|
@ -58,7 +58,7 @@ bool SixenseManager::_sixenseLoaded = false;
|
|||
const QString SixenseManager::NAME = "Sixense";
|
||||
const QString SixenseManager::HYDRA_ID_STRING = "Razer Hydra";
|
||||
|
||||
const QString MENU_PARENT = "Avatar";
|
||||
const QString MENU_PARENT = "Developer";
|
||||
const QString MENU_NAME = "Sixense";
|
||||
const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME;
|
||||
const QString TOGGLE_SMOOTH = "Smooth Sixense Movement";
|
||||
|
|
|
@ -77,6 +77,8 @@ void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const Q
|
|||
fprintf(stdout, "Fatal: %s\n", qPrintable(msg));
|
||||
txt += msg;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (outStream) {
|
||||
|
|
Loading…
Reference in a new issue