Merge branch 'vr_menus' into infoview

Conflicts:
	tests/ui/src/main.cpp
This commit is contained in:
Brad Davis 2015-04-28 01:17:51 -07:00
commit 9836ffed97
36 changed files with 429 additions and 1267 deletions

View file

@ -1,7 +1,7 @@
###Dependencies ###Dependencies
* [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 2.8.12.2 * [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 2.8.12.2
* [Qt](http://qt-project.org/downloads) ~> 5.3.2 * [Qt](http://www.qt.io/download-open-source) ~> 5.4.1
* [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1g * [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1g
* IMPORTANT: OpenSSL 1.0.1g is critical to avoid a security vulnerability. * IMPORTANT: OpenSSL 1.0.1g is critical to avoid a security vulnerability.
* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional) * [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
@ -37,12 +37,12 @@ Hifi uses CMake to generate build files and project files for your platform.
####Qt ####Qt
In order for CMake to find the Qt5 find modules, you will need to set an ENV variable pointing to your Qt installation. In order for CMake to find the Qt5 find modules, you will need to set an ENV variable pointing to your Qt installation.
For example, a Qt5 5.3.2 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment). For example, a Qt5 5.4.1 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment).
The path it needs to be set to will depend on where and how Qt5 was installed. e.g. The path it needs to be set to will depend on where and how Qt5 was installed. e.g.
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.3.2/clang_64/lib/cmake/ export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.4.1/clang_64/lib/cmake/
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.3.2/lib/cmake export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.4.1/lib/cmake
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
####Generating build files ####Generating build files
@ -57,7 +57,7 @@ Any variables that need to be set for CMake to find dependencies can be set as E
For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation: For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation:
cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.3.2/lib/cmake cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.4.1/lib/cmake
####Finding Dependencies ####Finding Dependencies

View file

@ -10,7 +10,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies
We have a [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas) that you can use/tap to install some of the dependencies. In the code block above qt5 is installed from a formula in this repository. We have a [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas) that you can use/tap to install some of the dependencies. In the code block above qt5 is installed from a formula in this repository.
*Our [qt5 homebrew formula](https://raw.github.com/highfidelity/homebrew-formulas/master/qt5.rb) is for a patched version of Qt 5.3.x stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.* *Our [qt5 homebrew formula](https://raw.github.com/highfidelity/homebrew-formulas/master/qt5.rb) is for a patched version of Qt 5.4.x stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.*
###Xcode ###Xcode
If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles. If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles.

File diff suppressed because it is too large Load diff

View file

@ -325,7 +325,6 @@ Dialog {
case Qt.Key_Enter: case Qt.Key_Enter:
case Qt.Key_Return: case Qt.Key_Return:
console.log("Accepting");
event.accepted = true event.accepted = true
content.accept() content.accept()
break break

View file

@ -0,0 +1,115 @@
import QtQuick 2.4
import QtQuick.Controls 1.3
import Hifi 1.0
// Currently for testing a pure QML replacement menu
Item {
Item {
objectName: "AllActions"
Action {
id: aboutApp
objectName: "HifiAction_" + MenuConstants.AboutApp
text: qsTr("About Interface")
}
//
// File Menu
//
Action {
id: login
objectName: "HifiAction_" + MenuConstants.Login
text: qsTr("Login")
}
Action {
id: quit
objectName: "HifiAction_" + MenuConstants.Quit
text: qsTr("Quit")
//shortcut: StandardKey.Quit
shortcut: "Ctrl+Q"
}
//
// Edit menu
//
Action {
id: undo
text: "Undo"
shortcut: StandardKey.Undo
}
Action {
id: redo
text: "Redo"
shortcut: StandardKey.Redo
}
Action {
id: animations
objectName: "HifiAction_" + MenuConstants.Animations
text: qsTr("Animations...")
}
Action {
id: attachments
text: qsTr("Attachments...")
}
Action {
id: explode
text: qsTr("Explode on quit")
checkable: true
checked: true
}
Action {
id: freeze
text: qsTr("Freeze on quit")
checkable: true
checked: false
}
ExclusiveGroup {
Action {
id: visibleToEveryone
objectName: "HifiAction_" + MenuConstants.VisibleToEveryone
text: qsTr("Everyone")
checkable: true
checked: true
}
Action {
id: visibleToFriends
objectName: "HifiAction_" + MenuConstants.VisibleToFriends
text: qsTr("Friends")
checkable: true
}
Action {
id: visibleToNoOne
objectName: "HifiAction_" + MenuConstants.VisibleToNoOne
text: qsTr("No one")
checkable: true
}
}
}
Menu {
objectName: "rootMenu";
Menu {
title: "File"
MenuItem { action: login }
MenuItem { action: explode }
MenuItem { action: freeze }
MenuItem { action: quit }
}
Menu {
title: "Tools"
Menu {
title: "I Am Visible To"
MenuItem { action: visibleToEveryone }
MenuItem { action: visibleToFriends }
MenuItem { action: visibleToNoOne }
}
MenuItem { action: animations }
}
Menu {
title: "Help"
MenuItem { action: aboutApp }
}
}
}

View file

@ -128,13 +128,16 @@ Hifi.VrMenu {
// visible: source.visible // visible: source.visible
Row { Row {
id: row id: row
spacing: 4 spacing: 2
anchors { anchors {
top: parent.top top: parent.top
topMargin: 2 topMargin: 2
} }
Spacer { size: 4 }
FontAwesome { FontAwesome {
id: check id: check
verticalAlignment: Text.AlignVCenter
y: 2
size: label.height size: label.height
text: checkText() text: checkText()
color: label.color color: label.color
@ -191,6 +194,7 @@ Hifi.VrMenu {
bottom: parent.bottom bottom: parent.bottom
left: parent.left left: parent.left
right: tag.right right: tag.right
rightMargin: -4
} }
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
hoverEnabled: true hoverEnabled: true
@ -263,7 +267,6 @@ Hifi.VrMenu {
function popColumn() { function popColumn() {
if (columns.length > 0) { if (columns.length > 0) {
var curColumn = columns.pop(); var curColumn = columns.pop();
console.log(curColumn);
curColumn.visible = false; curColumn.visible = false;
curColumn.destroy(); curColumn.destroy();
models.pop(); models.pop();

View file

@ -92,6 +92,7 @@ DialogBase {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
width: 16 width: 16
height: 16 height: 16
z: 1000
hoverEnabled: true hoverEnabled: true
onPressed: { onPressed: {
startX = mouseX startX = mouseX
@ -143,4 +144,15 @@ DialogBase {
} }
} }
} }
Keys.onPressed: {
switch(event.key) {
case Qt.Key_W:
if (event.modifiers == Qt.ControlModifier) {
event.accepted = true
enabled = false
}
break;
}
}
} }

View file

@ -22,11 +22,13 @@ Item {
readonly property alias titleWidth: titleBorder.width readonly property alias titleWidth: titleBorder.width
readonly property alias titleHeight: titleBorder.height readonly property alias titleHeight: titleBorder.height
// readonly property real borderWidth: hifi.styles.borderWidth
readonly property real borderWidth: 0
property alias clientBorder: clientBorder property alias clientBorder: clientBorder
readonly property real clientX: clientBorder.x + hifi.styles.borderWidth readonly property real clientX: clientBorder.x + borderWidth
readonly property real clientY: clientBorder.y + hifi.styles.borderWidth readonly property real clientY: clientBorder.y + borderWidth
readonly property real clientWidth: clientBorder.width - hifi.styles.borderWidth * 2 readonly property real clientWidth: clientBorder.width - borderWidth * 2
readonly property real clientHeight: clientBorder.height - hifi.styles.borderWidth * 2 readonly property real clientHeight: clientBorder.height - borderWidth * 2
/* /*
* Window decorations, with a title bar and frames * Window decorations, with a title bar and frames
@ -35,6 +37,7 @@ Item {
id: windowBorder id: windowBorder
anchors.fill: parent anchors.fill: parent
border.color: root.frameColor border.color: root.frameColor
border.width: 0
color: "#00000000" color: "#00000000"
Border { Border {
@ -43,11 +46,13 @@ Item {
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
border.color: root.frameColor border.color: root.frameColor
border.width: 0
clip: true clip: true
color: root.active ? color: root.active ?
hifi.colors.activeWindow.headerBackground : hifi.colors.activeWindow.headerBackground :
hifi.colors.inactiveWindow.headerBackground hifi.colors.inactiveWindow.headerBackground
Text { Text {
id: titleText id: titleText
color: root.active ? color: root.active ?
@ -60,8 +65,26 @@ Item {
} }
} // header border } // header border
// These two rectangles hide the curves between the title area
// and the client area
Rectangle {
y: titleBorder.height - titleBorder.radius
height: titleBorder.radius
width: titleBorder.width
color: titleBorder.color
visible: borderWidth == 0
}
Rectangle {
y: titleBorder.height
width: clientBorder.width
height: clientBorder.radius
color: clientBorder.color
}
Border { Border {
id: clientBorder id: clientBorder
border.width: 0
border.color: root.frameColor border.color: root.frameColor
color: root.backgroundColor color: root.backgroundColor
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@ -69,7 +92,6 @@ Item {
anchors.topMargin: -titleBorder.border.width anchors.topMargin: -titleBorder.border.width
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
clip: true
} // client border } // client border
} // window border } // window border

View file

@ -13,14 +13,14 @@ Original.TextInput {
font.family: hifi.fonts.fontFamily font.family: hifi.fonts.fontFamily
font.pointSize: hifi.fonts.fontSize font.pointSize: hifi.fonts.fontSize
/*
Original.Rectangle { Original.Rectangle {
// Render the rectangle as background // Render the rectangle as background
z: -1 z: -1
anchors.fill: parent anchors.fill: parent
color: hifi.colors.inputBackground color: hifi.colors.inputBackground
} }
*/
Text { Text {
anchors.fill: parent anchors.fill: parent
font.pointSize: parent.font.pointSize font.pointSize: parent.font.pointSize

View file

@ -766,6 +766,7 @@ void Application::initializeUi() {
AddressBarDialog::registerType(); AddressBarDialog::registerType();
LoginDialog::registerType(); LoginDialog::registerType();
MessageDialog::registerType(); MessageDialog::registerType();
VrMenu::registerType();
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->create(_glWidget->context()->contextHandle()); offscreenUi->create(_glWidget->context()->contextHandle());
@ -774,6 +775,8 @@ void Application::initializeUi() {
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
offscreenUi->load("Root.qml"); offscreenUi->load("Root.qml");
offscreenUi->load("RootMenu.qml"); offscreenUi->load("RootMenu.qml");
VrMenu::load();
VrMenu::executeQueuedLambdas();
offscreenUi->setMouseTranslator([this](const QPointF& p){ offscreenUi->setMouseTranslator([this](const QPointF& p){
if (OculusManager::isConnected()) { if (OculusManager::isConnected()) {
glm::vec2 pos = _applicationOverlay.screenToOverlay(toGlm(p)); glm::vec2 pos = _applicationOverlay.screenToOverlay(toGlm(p));
@ -4441,7 +4444,6 @@ void Application::checkSkeleton() {
_myAvatar->useBodyURL(DEFAULT_BODY_MODEL_URL, "Default"); _myAvatar->useBodyURL(DEFAULT_BODY_MODEL_URL, "Default");
} else { } else {
_myAvatar->updateCharacterController();
_physicsEngine.setCharacterController(_myAvatar->getCharacterController()); _physicsEngine.setCharacterController(_myAvatar->getCharacterController());
} }
} }

View file

@ -975,7 +975,9 @@ bool Menu::menuItemExists(const QString& menu, const QString& menuitem) {
MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) { MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) {
VrMenu::instance()->addMenu(menu); VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
vrMenu->addMenu(menu);
});
_backMap[menu] = this; _backMap[menu] = this;
} }
@ -997,18 +999,24 @@ void MenuWrapper::addSeparator() {
void MenuWrapper::addAction(QAction* action) { void MenuWrapper::addAction(QAction* action) {
_realMenu->addAction(action); _realMenu->addAction(action);
VrMenu::instance()->addAction(_realMenu, action); VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
vrMenu->addAction(_realMenu, action);
});
} }
QAction* MenuWrapper::addAction(const QString& menuName) { QAction* MenuWrapper::addAction(const QString& menuName) {
QAction* action = _realMenu->addAction(menuName); QAction* action = _realMenu->addAction(menuName);
VrMenu::instance()->addAction(_realMenu, action); VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
vrMenu->addAction(_realMenu, action);
});
return action; return action;
} }
QAction* MenuWrapper::addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut) { QAction* MenuWrapper::addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut) {
QAction* action = _realMenu->addAction(menuName, receiver, member, shortcut); QAction* action = _realMenu->addAction(menuName, receiver, member, shortcut);
VrMenu::instance()->addAction(_realMenu, action); VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
vrMenu->addAction(_realMenu, action);
});
return action; return action;
} }
@ -1018,7 +1026,9 @@ void MenuWrapper::removeAction(QAction* action) {
void MenuWrapper::insertAction(QAction* before, QAction* action) { void MenuWrapper::insertAction(QAction* before, QAction* action) {
_realMenu->insertAction(before, action); _realMenu->insertAction(before, action);
VrMenu::instance()->insertAction(before, action); VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
vrMenu->insertAction(before, action);
});
} }
QHash<QMenu*, MenuWrapper*> MenuWrapper::_backMap; QHash<QMenu*, MenuWrapper*> MenuWrapper::_backMap;

View file

@ -1058,3 +1058,20 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
} }
// virtual
void Avatar::rebuildSkeletonBody() {
/* TODO: implement this and remove override from MyAvatar (when we have AvatarMotionStates working)
if (_motionState) {
// compute localAABox
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
float radius = capsule.getRadius();
float height = 2.0f * (capsule.getHalfHeight() + radius);
glm::vec3 corner(-radius, -0.5f * height, -radius);
corner += _skeletonModel.getBoundingShapeOffset();
glm::vec3 scale(2.0f * radius, height, 2.0f * radius);
//_characterController.setLocalBoundingBox(corner, scale);
_motionState->setBoundingBox(corner, scale);
}
*/
}

View file

@ -162,6 +162,8 @@ public:
// (otherwise floating point error will cause problems at large positions). // (otherwise floating point error will cause problems at large positions).
void applyPositionDelta(const glm::vec3& delta); void applyPositionDelta(const glm::vec3& delta);
virtual void rebuildSkeletonBody();
signals: signals:
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision); void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
@ -228,6 +230,8 @@ private:
static int _jointConesID; static int _jointConesID;
int _voiceSphereID; int _voiceSphereID;
//AvatarMotionState* _motionState = nullptr;
}; };
#endif // hifi_Avatar_h #endif // hifi_Avatar_h

View file

@ -59,7 +59,7 @@ private:
AvatarSharedPointer newSharedAvatar(); AvatarSharedPointer newSharedAvatar();
// virtual override // virtual overrides
AvatarHash::iterator erase(const AvatarHash::iterator& iterator); AvatarHash::iterator erase(const AvatarHash::iterator& iterator);
QVector<AvatarSharedPointer> _avatarFades; QVector<AvatarSharedPointer> _avatarFades;

View file

@ -987,6 +987,13 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
} }
void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) { void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "useFullAvatarURL", Qt::BlockingQueuedConnection,
Q_ARG(const QUrl&, fullAvatarURL),
Q_ARG(const QString&, modelName));
return;
}
_useFullAvatar = true; _useFullAvatar = true;
if (_fullAvatarURLFromPreferences != fullAvatarURL) { if (_fullAvatarURLFromPreferences != fullAvatarURL) {
@ -1019,6 +1026,15 @@ void MyAvatar::useBodyURL(const QUrl& bodyURL, const QString& modelName) {
} }
void MyAvatar::useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, const QString& headName, const QString& bodyName) { void MyAvatar::useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, const QString& headName, const QString& bodyName) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "useFullAvatarURL", Qt::BlockingQueuedConnection,
Q_ARG(const QUrl&, headURL),
Q_ARG(const QUrl&, bodyURL),
Q_ARG(const QString&, headName),
Q_ARG(const QString&, bodyName));
return;
}
_useFullAvatar = false; _useFullAvatar = false;
if (_headURLFromPreferences != headURL) { if (_headURLFromPreferences != headURL) {
@ -1079,7 +1095,7 @@ glm::vec3 MyAvatar::getSkeletonPosition() const {
return Avatar::getPosition(); return Avatar::getPosition();
} }
void MyAvatar::updateCharacterController() { void MyAvatar::rebuildSkeletonBody() {
// compute localAABox // compute localAABox
const CapsuleShape& capsule = _skeletonModel.getBoundingShape(); const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
float radius = capsule.getRadius(); float radius = capsule.getRadius();

View file

@ -138,7 +138,6 @@ public:
virtual glm::vec3 getSkeletonPosition() const; virtual glm::vec3 getSkeletonPosition() const;
void updateLocalAABox(); void updateLocalAABox();
DynamicCharacterController* getCharacterController() { return &_characterController; } DynamicCharacterController* getCharacterController() { return &_characterController; }
void updateCharacterController();
void clearJointAnimationPriorities(); void clearJointAnimationPriorities();
@ -193,6 +192,8 @@ public slots:
void saveRecording(QString filename); void saveRecording(QString filename);
void loadLastRecording(); void loadLastRecording();
virtual void rebuildSkeletonBody();
signals: signals:
void transformChanged(); void transformChanged();

View file

@ -40,13 +40,14 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
_clampedFootPosition(0.0f), _clampedFootPosition(0.0f),
_headClipDistance(DEFAULT_NEAR_CLIP) _headClipDistance(DEFAULT_NEAR_CLIP)
{ {
assert(_owningAvatar);
} }
SkeletonModel::~SkeletonModel() { SkeletonModel::~SkeletonModel() {
} }
void SkeletonModel::setJointStates(QVector<JointState> states) { void SkeletonModel::initJointStates(QVector<JointState> states) {
Model::setJointStates(states); Model::initJointStates(states);
// Determine the default eye position for avatar scale = 1.0 // Determine the default eye position for avatar scale = 1.0
int headJointIndex = _geometry->getFBXGeometry().headJointIndex; int headJointIndex = _geometry->getFBXGeometry().headJointIndex;
@ -83,6 +84,7 @@ void SkeletonModel::setJointStates(QVector<JointState> states) {
_headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z); _headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z);
_headClipDistance = std::max(_headClipDistance, DEFAULT_NEAR_CLIP); _headClipDistance = std::max(_headClipDistance, DEFAULT_NEAR_CLIP);
_owningAvatar->rebuildSkeletonBody();
emit skeletonLoaded(); emit skeletonLoaded();
} }

View file

@ -28,7 +28,7 @@ public:
SkeletonModel(Avatar* owningAvatar, QObject* parent = NULL); SkeletonModel(Avatar* owningAvatar, QObject* parent = NULL);
~SkeletonModel(); ~SkeletonModel();
void setJointStates(QVector<JointState> states); virtual void initJointStates(QVector<JointState> states);
void simulate(float deltaTime, bool fullUpdate = true); void simulate(float deltaTime, bool fullUpdate = true);

View file

@ -680,6 +680,17 @@ void EntityItem::setMass(float mass) {
} }
} }
const float DEFAULT_ENTITY_RESTITUTION = 0.5f;
const float DEFAULT_ENTITY_FRICTION = 0.5f;
float EntityItem::getRestitution() const {
return DEFAULT_ENTITY_RESTITUTION;
}
float EntityItem::getFriction() const {
return DEFAULT_ENTITY_FRICTION;
}
void EntityItem::simulate(const quint64& now) { void EntityItem::simulate(const quint64& now) {
if (_lastSimulated == 0) { if (_lastSimulated == 0) {
_lastSimulated = now; _lastSimulated = now;
@ -840,11 +851,11 @@ glm::mat4 EntityItem::getWorldToEntityMatrix() const {
return glm::inverse(getEntityToWorldMatrix()); return glm::inverse(getEntityToWorldMatrix());
} }
glm::vec3 EntityItem::entityToWorld(const glm::vec3 point) const { glm::vec3 EntityItem::entityToWorld(const glm::vec3& point) const {
return glm::vec3(getEntityToWorldMatrix() * glm::vec4(point, 1.0f)); return glm::vec3(getEntityToWorldMatrix() * glm::vec4(point, 1.0f));
} }
glm::vec3 EntityItem::worldToEntity(const glm::vec3 point) const { glm::vec3 EntityItem::worldToEntity(const glm::vec3& point) const {
return glm::vec3(getWorldToEntityMatrix() * glm::vec4(point, 1.0f)); return glm::vec3(getWorldToEntityMatrix() * glm::vec4(point, 1.0f));
} }
@ -1164,7 +1175,7 @@ void EntityItem::updateVelocity(const glm::vec3& value) {
void EntityItem::updateDamping(float value) { void EntityItem::updateDamping(float value) {
if (fabsf(_damping - value) > MIN_DAMPING_DELTA) { if (fabsf(_damping - value) > MIN_DAMPING_DELTA) {
_damping = glm::clamp(value, 0.0f, 1.0f); _damping = glm::clamp(value, 0.0f, 1.0f);
_dirtyFlags |= EntityItem::DIRTY_VELOCITY; _dirtyFlags |= EntityItem::DIRTY_MATERIAL;
} }
} }
@ -1202,7 +1213,7 @@ void EntityItem::updateAngularVelocity(const glm::vec3& value) {
void EntityItem::updateAngularDamping(float value) { void EntityItem::updateAngularDamping(float value) {
if (fabsf(_angularDamping - value) > MIN_DAMPING_DELTA) { if (fabsf(_angularDamping - value) > MIN_DAMPING_DELTA) {
_angularDamping = glm::clamp(value, 0.0f, 1.0f); _angularDamping = glm::clamp(value, 0.0f, 1.0f);
_dirtyFlags |= EntityItem::DIRTY_VELOCITY; _dirtyFlags |= EntityItem::DIRTY_MATERIAL;
} }
} }

View file

@ -55,7 +55,8 @@ public:
DIRTY_SHAPE = 0x0020, DIRTY_SHAPE = 0x0020,
DIRTY_LIFETIME = 0x0040, DIRTY_LIFETIME = 0x0040,
DIRTY_UPDATEABLE = 0x0080, DIRTY_UPDATEABLE = 0x0080,
DIRTY_PHYSICS_NO_WAKE = 0x0100 // we want to update values in physics engine without "waking" the object up DIRTY_MATERIAL = 0x00100,
DIRTY_PHYSICS_NO_WAKE = 0x0200 // we want to update values in physics engine without "waking" the object up
}; };
DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly
@ -174,7 +175,7 @@ public:
float getDensity() const { return _density; } float getDensity() const { return _density; }
const glm::vec3 getVelocity() const { return _velocity; } /// get velocity in meters const glm::vec3& getVelocity() const { return _velocity; } /// get velocity in meters
void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in meters void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in meters
bool hasVelocity() const { return _velocity != ENTITY_ITEM_ZERO_VEC3; } bool hasVelocity() const { return _velocity != ENTITY_ITEM_ZERO_VEC3; }
@ -182,13 +183,16 @@ public:
void setGravity(const glm::vec3& value) { _gravity = value; } /// gravity in meters void setGravity(const glm::vec3& value) { _gravity = value; } /// gravity in meters
bool hasGravity() const { return _gravity != ENTITY_ITEM_ZERO_VEC3; } bool hasGravity() const { return _gravity != ENTITY_ITEM_ZERO_VEC3; }
const glm::vec3 getAcceleration() const { return _acceleration; } /// get acceleration in meters/second/second const glm::vec3& getAcceleration() const { return _acceleration; } /// get acceleration in meters/second/second
void setAcceleration(const glm::vec3& value) { _acceleration = value; } /// acceleration in meters/second/second void setAcceleration(const glm::vec3& value) { _acceleration = value; } /// acceleration in meters/second/second
bool hasAcceleration() const { return _acceleration != ENTITY_ITEM_ZERO_VEC3; } bool hasAcceleration() const { return _acceleration != ENTITY_ITEM_ZERO_VEC3; }
float getDamping() const { return _damping; } float getDamping() const { return _damping; }
void setDamping(float value) { _damping = value; } void setDamping(float value) { _damping = value; }
float getRestitution() const;
float getFriction() const;
// lifetime related properties. // lifetime related properties.
float getLifetime() const { return _lifetime; } /// get the lifetime in seconds for the entity float getLifetime() const { return _lifetime; } /// get the lifetime in seconds for the entity
void setLifetime(float value) { _lifetime = value; } /// set the lifetime in seconds for the entity void setLifetime(float value) { _lifetime = value; } /// set the lifetime in seconds for the entity
@ -298,8 +302,8 @@ public:
glm::mat4 getEntityToWorldMatrix() const; glm::mat4 getEntityToWorldMatrix() const;
glm::mat4 getWorldToEntityMatrix() const; glm::mat4 getWorldToEntityMatrix() const;
glm::vec3 worldToEntity(const glm::vec3 point) const; glm::vec3 worldToEntity(const glm::vec3& point) const;
glm::vec3 entityToWorld(const glm::vec3 point) const; glm::vec3 entityToWorld(const glm::vec3& point) const;
protected: protected:

View file

@ -16,6 +16,7 @@
#include "LightEntityItem.h" #include "LightEntityItem.h"
#include "ModelEntityItem.h" #include "ModelEntityItem.h"
#include "ZoneEntityItem.h" #include "ZoneEntityItem.h"
#include "EntitiesLogging.h"
EntityScriptingInterface::EntityScriptingInterface() : EntityScriptingInterface::EntityScriptingInterface() :
@ -78,19 +79,27 @@ EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& pro
EntityItemProperties propertiesWithSimID = properties; EntityItemProperties propertiesWithSimID = properties;
EntityItemID id(NEW_ENTITY, creatorTokenID, false ); EntityItemID id(NEW_ENTITY, creatorTokenID, false);
// If we have a local entity tree set, then also update it. // If we have a local entity tree set, then also update it.
bool success = true;
if (_entityTree) { if (_entityTree) {
_entityTree->lockForWrite(); _entityTree->lockForWrite();
EntityItem* entity = _entityTree->addEntity(id, propertiesWithSimID); EntityItem* entity = _entityTree->addEntity(id, propertiesWithSimID);
// This Node is creating a new object. If it's in motion, set this Node as the simulator. if (entity) {
setSimId(propertiesWithSimID, entity); // This Node is creating a new object. If it's in motion, set this Node as the simulator.
setSimId(propertiesWithSimID, entity);
} else {
qCDebug(entities) << "script failed to add new Entity to local Octree";
success = false;
}
_entityTree->unlock(); _entityTree->unlock();
} }
// queue the packet // queue the packet
queueEntityMessage(PacketTypeEntityAddOrEdit, id, propertiesWithSimID); if (success) {
queueEntityMessage(PacketTypeEntityAddOrEdit, id, propertiesWithSimID);
}
return id; return id;
} }

View file

@ -192,6 +192,14 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro
EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
EntityItem* result = NULL; EntityItem* result = NULL;
if (getIsClient()) {
// if our Node isn't allowed to create entities in this domain, don't try.
auto nodeList = DependencyManager::get<NodeList>();
if (!nodeList->getThisNodeCanRez()) {
return NULL;
}
}
// NOTE: This method is used in the client and the server tree. In the client, it's possible to create EntityItems // NOTE: This method is used in the client and the server tree. In the client, it's possible to create EntityItems
// that do not yet have known IDs. In the server tree however we don't want to have entities without known IDs. // that do not yet have known IDs. In the server tree however we don't want to have entities without known IDs.
bool recordCreationTime = false; bool recordCreationTime = false;

View file

@ -94,7 +94,7 @@ void DynamicCharacterController::preStep(btCollisionWorld* collisionWorld) {
} }
} }
void DynamicCharacterController::playerStep(btCollisionWorld* dynaWorld,btScalar dt) { void DynamicCharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) {
btVector3 actualVelocity = _rigidBody->getLinearVelocity(); btVector3 actualVelocity = _rigidBody->getLinearVelocity();
btScalar actualSpeed = actualVelocity.length(); btScalar actualSpeed = actualVelocity.length();
@ -315,10 +315,10 @@ void DynamicCharacterController::updateShapeIfNecessary() {
float mass = 1.0f; float mass = 1.0f;
btVector3 inertia(1.0f, 1.0f, 1.0f); btVector3 inertia(1.0f, 1.0f, 1.0f);
_rigidBody = new btRigidBody(mass, NULL, _shape, inertia); _rigidBody = new btRigidBody(mass, NULL, _shape, inertia);
_rigidBody->setSleepingThresholds (0.0f, 0.0f); _rigidBody->setSleepingThresholds(0.0f, 0.0f);
_rigidBody->setAngularFactor (0.0f); _rigidBody->setAngularFactor(0.0f);
_rigidBody->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), _rigidBody->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()),
glmToBullet(_avatarData->getPosition()))); glmToBullet(_avatarData->getPosition())));
if (_isHovering) { if (_isHovering) {
_rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f));
} else { } else {

View file

@ -51,7 +51,7 @@ EntityMotionState::~EntityMotionState() {
_entity = NULL; _entity = NULL;
} }
MotionType EntityMotionState::computeMotionType() const { MotionType EntityMotionState::computeObjectMotionType() const {
if (_entity->getCollisionsWillMove()) { if (_entity->getCollisionsWillMove()) {
return MOTION_TYPE_DYNAMIC; return MOTION_TYPE_DYNAMIC;
} }
@ -68,7 +68,7 @@ void EntityMotionState::stepKinematicSimulation(quint64 now) {
// which is different from physical kinematic motion (inside getWorldTransform()) // which is different from physical kinematic motion (inside getWorldTransform())
// which steps in physics simulation time. // which steps in physics simulation time.
_entity->simulate(now); _entity->simulate(now);
// TODO: we can't use ObjectMotionState::measureAcceleration() here because the entity // TODO: we can't use measureBodyAcceleration() here because the entity
// has no RigidBody and the timestep is a little bit out of sync with the physics simulation anyway. // has no RigidBody and the timestep is a little bit out of sync with the physics simulation anyway.
// Hence we must manually measure kinematic velocity and acceleration. // Hence we must manually measure kinematic velocity and acceleration.
} }
@ -101,7 +101,7 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
// This callback is invoked by the physics simulation at the end of each simulation step... // This callback is invoked by the physics simulation at the end of each simulation step...
// iff the corresponding RigidBody is DYNAMIC and has moved. // iff the corresponding RigidBody is DYNAMIC and has moved.
void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
measureAcceleration(); measureBodyAcceleration();
_entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset()); _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset());
_entity->setRotation(bulletToGLM(worldTrans.getRotation())); _entity->setRotation(bulletToGLM(worldTrans.getRotation()));
@ -138,20 +138,20 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
#endif #endif
} }
void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t step) { void EntityMotionState::updateBodyEasy(uint32_t flags, uint32_t step) {
if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY | EntityItem::DIRTY_PHYSICS_NO_WAKE)) { if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY | EntityItem::DIRTY_PHYSICS_NO_WAKE)) {
if (flags & EntityItem::DIRTY_POSITION) { if (flags & EntityItem::DIRTY_POSITION) {
_sentPosition = _entity->getPosition() - ObjectMotionState::getWorldOffset(); _sentPosition = getObjectPosition() - ObjectMotionState::getWorldOffset();
btTransform worldTrans; btTransform worldTrans;
worldTrans.setOrigin(glmToBullet(_sentPosition)); worldTrans.setOrigin(glmToBullet(_sentPosition));
_sentRotation = _entity->getRotation(); _sentRotation = getObjectRotation();
worldTrans.setRotation(glmToBullet(_sentRotation)); worldTrans.setRotation(glmToBullet(_sentRotation));
_body->setWorldTransform(worldTrans); _body->setWorldTransform(worldTrans);
} }
if (flags & EntityItem::DIRTY_VELOCITY) { if (flags & EntityItem::DIRTY_VELOCITY) {
updateObjectVelocities(); updateBodyVelocities();
} }
_sentStep = step; _sentStep = step;
@ -160,54 +160,54 @@ void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t step) {
} }
} }
// TODO: entity support for friction and restitution if (flags & EntityItem::DIRTY_MATERIAL) {
//_restitution = _entity->getRestitution(); updateBodyMaterialProperties();
_body->setRestitution(_restitution); }
//_friction = _entity->getFriction();
_body->setFriction(_friction);
_linearDamping = _entity->getDamping();
_angularDamping = _entity->getAngularDamping();
_body->setDamping(_linearDamping, _angularDamping);
if (flags & EntityItem::DIRTY_MASS) { if (flags & EntityItem::DIRTY_MASS) {
float mass = _entity->computeMass(); ShapeInfo shapeInfo;
_entity->computeShapeInfo(shapeInfo);
float mass = computeObjectMass(shapeInfo);
btVector3 inertia(0.0f, 0.0f, 0.0f); btVector3 inertia(0.0f, 0.0f, 0.0f);
_body->getCollisionShape()->calculateLocalInertia(mass, inertia); _body->getCollisionShape()->calculateLocalInertia(mass, inertia);
_body->setMassProps(mass, inertia); _body->setMassProps(mass, inertia);
_body->updateInertiaTensor(); _body->updateInertiaTensor();
} }
}; }
void EntityMotionState::updateObjectVelocities() { void EntityMotionState::updateBodyMaterialProperties() {
_body->setRestitution(getObjectRestitution());
_body->setFriction(getObjectFriction());
_body->setDamping(fabsf(btMin(getObjectLinearDamping(), 1.0f)), fabsf(btMin(getObjectAngularDamping(), 1.0f)));
}
void EntityMotionState::updateBodyVelocities() {
if (_body) { if (_body) {
_sentVelocity = _entity->getVelocity(); _sentVelocity = getObjectLinearVelocity();
setVelocity(_sentVelocity); setBodyVelocity(_sentVelocity);
_sentAngularVelocity = _entity->getAngularVelocity(); _sentAngularVelocity = getObjectAngularVelocity();
setAngularVelocity(_sentAngularVelocity); setBodyAngularVelocity(_sentAngularVelocity);
_sentGravity = _entity->getGravity(); _sentGravity = getObjectGravity();
setGravity(_sentGravity); setBodyGravity(_sentGravity);
_body->setActivationState(ACTIVE_TAG); _body->setActivationState(ACTIVE_TAG);
} }
} }
void EntityMotionState::computeShapeInfo(ShapeInfo& shapeInfo) { void EntityMotionState::computeObjectShapeInfo(ShapeInfo& shapeInfo) {
if (_entity->isReadyToComputeShape()) { if (_entity->isReadyToComputeShape()) {
_entity->computeShapeInfo(shapeInfo); _entity->computeShapeInfo(shapeInfo);
} }
} }
float EntityMotionState::computeMass(const ShapeInfo& shapeInfo) const { float EntityMotionState::computeObjectMass(const ShapeInfo& shapeInfo) const {
return _entity->computeMass(); return _entity->computeMass();
} }
bool EntityMotionState::shouldSendUpdate(uint32_t simulationFrame) { bool EntityMotionState::shouldSendUpdate(uint32_t simulationFrame) {
bool baseResult = this->ObjectMotionState::shouldSendUpdate(simulationFrame); if (!ObjectMotionState::shouldSendUpdate(simulationFrame)) {
if (!baseResult) {
return false; return false;
} }

View file

@ -31,12 +31,11 @@ public:
static void setOutgoingEntityList(QSet<EntityItem*>* list); static void setOutgoingEntityList(QSet<EntityItem*>* list);
static void enqueueOutgoingEntity(EntityItem* entity); static void enqueueOutgoingEntity(EntityItem* entity);
EntityMotionState() = delete; // prevent compiler from making default ctor
EntityMotionState(EntityItem* item); EntityMotionState(EntityItem* item);
virtual ~EntityMotionState(); virtual ~EntityMotionState();
/// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem /// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem
virtual MotionType computeMotionType() const; virtual MotionType computeObjectMotionType() const;
virtual void updateKinematicState(uint32_t substep); virtual void updateKinematicState(uint32_t substep);
virtual void stepKinematicSimulation(quint64 now); virtual void stepKinematicSimulation(quint64 now);
@ -50,11 +49,12 @@ public:
virtual void setWorldTransform(const btTransform& worldTrans); virtual void setWorldTransform(const btTransform& worldTrans);
// these relay incoming values to the RigidBody // these relay incoming values to the RigidBody
virtual void updateObjectEasy(uint32_t flags, uint32_t step); virtual void updateBodyEasy(uint32_t flags, uint32_t step);
virtual void updateObjectVelocities(); virtual void updateBodyMaterialProperties();
virtual void updateBodyVelocities();
virtual void computeShapeInfo(ShapeInfo& shapeInfo); virtual void computeObjectShapeInfo(ShapeInfo& shapeInfo);
virtual float computeMass(const ShapeInfo& shapeInfo) const; virtual float computeObjectMass(const ShapeInfo& shapeInfo) const;
virtual bool shouldSendUpdate(uint32_t simulationFrame); virtual bool shouldSendUpdate(uint32_t simulationFrame);
virtual void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step); virtual void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step);
@ -70,6 +70,17 @@ public:
virtual void setShouldClaimSimulationOwnership(bool value) { _shouldClaimSimulationOwnership = value; } virtual void setShouldClaimSimulationOwnership(bool value) { _shouldClaimSimulationOwnership = value; }
virtual bool getShouldClaimSimulationOwnership() { return _shouldClaimSimulationOwnership; } virtual bool getShouldClaimSimulationOwnership() { return _shouldClaimSimulationOwnership; }
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 const glm::vec3& getObjectPosition() const { return _entity->getPosition(); }
virtual const glm::quat& getObjectRotation() const { return _entity->getRotation(); }
virtual const glm::vec3& getObjectLinearVelocity() const { return _entity->getVelocity(); }
virtual const glm::vec3& getObjectAngularVelocity() const { return _entity->getAngularVelocity(); }
virtual const glm::vec3& getObjectGravity() const { return _entity->getGravity(); }
protected: protected:
EntityItem* _entity; EntityItem* _entity;
quint8 _accelerationNearlyGravityCount; quint8 _accelerationNearlyGravityCount;

View file

@ -36,17 +36,13 @@ const glm::vec3& ObjectMotionState::getWorldOffset() {
} }
// static // static
uint32_t _simulationStep = 0; uint32_t _worldSimulationStep = 0;
void ObjectMotionState::setSimulationStep(uint32_t step) { void ObjectMotionState::setWorldSimulationStep(uint32_t step) {
assert(step > _simulationStep); assert(step > _worldSimulationStep);
_simulationStep = step; _worldSimulationStep = step;
} }
ObjectMotionState::ObjectMotionState() : ObjectMotionState::ObjectMotionState() :
_friction(DEFAULT_FRICTION),
_restitution(DEFAULT_RESTITUTION),
_linearDamping(0.0f),
_angularDamping(0.0f),
_motionType(MOTION_TYPE_STATIC), _motionType(MOTION_TYPE_STATIC),
_body(NULL), _body(NULL),
_sentMoving(false), _sentMoving(false),
@ -69,52 +65,36 @@ ObjectMotionState::~ObjectMotionState() {
assert(_body == NULL); assert(_body == NULL);
} }
void ObjectMotionState::measureAcceleration() { void ObjectMotionState::measureBodyAcceleration() {
// try to manually measure the true acceleration of the object // try to manually measure the true acceleration of the object
uint32_t numSubsteps = _simulationStep - _lastSimulationStep; uint32_t numSubsteps = _worldSimulationStep - _lastSimulationStep;
if (numSubsteps > 0) { if (numSubsteps > 0) {
float dt = ((float)numSubsteps * PHYSICS_ENGINE_FIXED_SUBSTEP); float dt = ((float)numSubsteps * PHYSICS_ENGINE_FIXED_SUBSTEP);
float invDt = 1.0f / dt; float invDt = 1.0f / dt;
_lastSimulationStep = _simulationStep; _lastSimulationStep = _worldSimulationStep;
// Note: the integration equation for velocity uses damping: v1 = (v0 + a * dt) * (1 - D)^dt // Note: the integration equation for velocity uses damping: v1 = (v0 + a * dt) * (1 - D)^dt
// hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt // hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt
glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity()); glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity());
_measuredAcceleration = (velocity / powf(1.0f - _linearDamping, dt) - _lastVelocity) * invDt; _measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt;
_lastVelocity = velocity; _lastVelocity = velocity;
} }
} }
void ObjectMotionState::resetMeasuredAcceleration() { void ObjectMotionState::resetMeasuredBodyAcceleration() {
_lastSimulationStep = _simulationStep; _lastSimulationStep = _worldSimulationStep;
_lastVelocity = bulletToGLM(_body->getLinearVelocity()); _lastVelocity = bulletToGLM(_body->getLinearVelocity());
} }
void ObjectMotionState::setFriction(float friction) { void ObjectMotionState::setBodyVelocity(const glm::vec3& velocity) const {
_friction = btMax(btMin(fabsf(friction), MAX_FRICTION), 0.0f);
}
void ObjectMotionState::setRestitution(float restitution) {
_restitution = btMax(btMin(fabsf(restitution), 1.0f), 0.0f);
}
void ObjectMotionState::setLinearDamping(float damping) {
_linearDamping = btMax(btMin(fabsf(damping), 1.0f), 0.0f);
}
void ObjectMotionState::setAngularDamping(float damping) {
_angularDamping = btMax(btMin(fabsf(damping), 1.0f), 0.0f);
}
void ObjectMotionState::setVelocity(const glm::vec3& velocity) const {
_body->setLinearVelocity(glmToBullet(velocity)); _body->setLinearVelocity(glmToBullet(velocity));
} }
void ObjectMotionState::setAngularVelocity(const glm::vec3& velocity) const { void ObjectMotionState::setBodyAngularVelocity(const glm::vec3& velocity) const {
_body->setAngularVelocity(glmToBullet(velocity)); _body->setAngularVelocity(glmToBullet(velocity));
} }
void ObjectMotionState::setGravity(const glm::vec3& gravity) const { void ObjectMotionState::setBodyGravity(const glm::vec3& gravity) const {
_body->setGravity(glmToBullet(gravity)); _body->setGravity(glmToBullet(gravity));
} }
@ -181,7 +161,7 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationStep) {
// compute position error // compute position error
if (glm::length2(_sentVelocity) > 0.0f) { if (glm::length2(_sentVelocity) > 0.0f) {
_sentVelocity += _sentAcceleration * dt; _sentVelocity += _sentAcceleration * dt;
_sentVelocity *= powf(1.0f - _linearDamping, dt); _sentVelocity *= powf(1.0f - _body->getLinearDamping(), dt);
_sentPosition += dt * _sentVelocity; _sentPosition += dt * _sentVelocity;
} }
@ -206,7 +186,7 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationStep) {
if (glm::length2(_sentAngularVelocity) > 0.0f) { if (glm::length2(_sentAngularVelocity) > 0.0f) {
// compute rotation error // compute rotation error
float attenuation = powf(1.0f - _angularDamping, dt); float attenuation = powf(1.0f - _body->getAngularDamping(), dt);
_sentAngularVelocity *= attenuation; _sentAngularVelocity *= attenuation;
// Bullet caps the effective rotation velocity inside its rotation integration step, therefore // Bullet caps the effective rotation velocity inside its rotation integration step, therefore

View file

@ -36,7 +36,7 @@ enum MotionStateType {
// and re-added to the physics engine and "easy" which just updates the body properties. // and re-added to the physics engine and "easy" which just updates the body properties.
const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE); const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE);
const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY | const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY |
EntityItem::DIRTY_MASS | EntityItem::DIRTY_COLLISION_GROUP); EntityItem::DIRTY_MASS | EntityItem::DIRTY_COLLISION_GROUP | EntityItem::DIRTY_MATERIAL);
// These are the set of incoming flags that the PhysicsEngine needs to hear about: // These are the set of incoming flags that the PhysicsEngine needs to hear about:
const uint32_t DIRTY_PHYSICS_FLAGS = HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS; const uint32_t DIRTY_PHYSICS_FLAGS = HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS;
@ -59,32 +59,30 @@ public:
static void setWorldOffset(const glm::vec3& offset); static void setWorldOffset(const glm::vec3& offset);
static const glm::vec3& getWorldOffset(); static const glm::vec3& getWorldOffset();
static void setSimulationStep(uint32_t step); // The WorldSimulationStep is a cached copy of number of SubSteps of the simulation, used for local time measurements.
static void setWorldSimulationStep(uint32_t step);
ObjectMotionState(); ObjectMotionState();
~ObjectMotionState(); ~ObjectMotionState();
void measureAcceleration(); void measureBodyAcceleration();
void resetMeasuredAcceleration(); void resetMeasuredBodyAcceleration();
// An EASY update does not require the object to be removed and then reinserted into the PhysicsEngine // An EASY update does not require the object to be removed and then reinserted into the PhysicsEngine
virtual void updateObjectEasy(uint32_t flags, uint32_t frame) = 0; virtual void updateBodyEasy(uint32_t flags, uint32_t frame) = 0;
virtual void updateObjectVelocities() = 0;
virtual void updateBodyMaterialProperties() = 0;
virtual void updateBodyVelocities() = 0;
MotionStateType getType() const { return _type; } MotionStateType getType() const { return _type; }
virtual MotionType getMotionType() const { return _motionType; } virtual MotionType getMotionType() const { return _motionType; }
virtual void computeShapeInfo(ShapeInfo& info) = 0; virtual void computeObjectShapeInfo(ShapeInfo& info) = 0;
virtual float computeMass(const ShapeInfo& shapeInfo) const = 0; virtual float computeObjectMass(const ShapeInfo& shapeInfo) const = 0;
void setFriction(float friction); void setBodyVelocity(const glm::vec3& velocity) const;
void setRestitution(float restitution); void setBodyAngularVelocity(const glm::vec3& velocity) const;
void setLinearDamping(float damping); void setBodyGravity(const glm::vec3& gravity) const;
void setAngularDamping(float damping);
void setVelocity(const glm::vec3& velocity) const;
void setAngularVelocity(const glm::vec3& velocity) const;
void setGravity(const glm::vec3& gravity) const;
void getVelocity(glm::vec3& velocityOut) const; void getVelocity(glm::vec3& velocityOut) const;
void getAngularVelocity(glm::vec3& angularVelocityOut) const; void getAngularVelocity(glm::vec3& angularVelocityOut) const;
@ -97,7 +95,7 @@ public:
virtual bool shouldSendUpdate(uint32_t simulationStep); virtual bool shouldSendUpdate(uint32_t simulationStep);
virtual void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame) = 0; virtual void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame) = 0;
virtual MotionType computeMotionType() const = 0; virtual MotionType computeObjectMotionType() const = 0;
virtual void updateKinematicState(uint32_t substep) = 0; virtual void updateKinematicState(uint32_t substep) = 0;
@ -117,18 +115,26 @@ public:
virtual void setShouldClaimSimulationOwnership(bool value) { } virtual void setShouldClaimSimulationOwnership(bool value) { }
virtual bool getShouldClaimSimulationOwnership() { return false; } virtual bool getShouldClaimSimulationOwnership() { return false; }
// 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 = 0;
virtual float getObjectFriction() const = 0;
virtual float getObjectLinearDamping() const = 0;
virtual float getObjectAngularDamping() const = 0;
virtual const glm::vec3& getObjectPosition() const = 0;
virtual const glm::quat& getObjectRotation() const = 0;
virtual const glm::vec3& getObjectLinearVelocity() const = 0;
virtual const glm::vec3& getObjectAngularVelocity() const = 0;
virtual const glm::vec3& getObjectGravity() const = 0;
protected: protected:
void setRigidBody(btRigidBody* body); void setRigidBody(btRigidBody* body);
MotionStateType _type = MOTION_STATE_TYPE_UNKNOWN; MotionStateType _type = MOTION_STATE_TYPE_UNKNOWN; // type of MotionState
// TODO: move these materials properties outside of ObjectMotionState MotionType _motionType; // type of motion: KINEMATIC, DYNAMIC, or STATIC
float _friction;
float _restitution;
float _linearDamping;
float _angularDamping;
MotionType _motionType;
btRigidBody* _body; btRigidBody* _body;

View file

@ -15,6 +15,7 @@
#include "ShapeInfoUtil.h" #include "ShapeInfoUtil.h"
#include "PhysicsHelpers.h" #include "PhysicsHelpers.h"
#include "ThreadSafeDynamicsWorld.h" #include "ThreadSafeDynamicsWorld.h"
#include "PhysicsLogging.h"
static uint32_t _numSubsteps; static uint32_t _numSubsteps;
@ -172,9 +173,9 @@ void PhysicsEngine::relayIncomingChangesToSimulation() {
if (flags & HARD_DIRTY_PHYSICS_FLAGS) { if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
// a HARD update requires the body be pulled out of physics engine, changed, then reinserted // a HARD update requires the body be pulled out of physics engine, changed, then reinserted
// but it also handles all EASY changes // but it also handles all EASY changes
bool success = updateObjectHard(body, motionState, flags); bool success = updateBodyHard(body, motionState, flags);
if (!success) { if (!success) {
// NOTE: since updateObjectHard() failed we know that motionState has been removed // NOTE: since updateBodyHard() failed we know that motionState has been removed
// from simulation and body has been deleted. Depending on what else has changed // from simulation and body has been deleted. Depending on what else has changed
// we might need to remove motionState altogether... // we might need to remove motionState altogether...
if (flags & EntityItem::DIRTY_VELOCITY) { if (flags & EntityItem::DIRTY_VELOCITY) {
@ -194,10 +195,10 @@ void PhysicsEngine::relayIncomingChangesToSimulation() {
} else if (flags) { } else if (flags) {
// an EASY update does NOT require that the body be pulled out of physics engine // an EASY update does NOT require that the body be pulled out of physics engine
// hence the MotionState has all the knowledge and authority to perform the update. // hence the MotionState has all the knowledge and authority to perform the update.
motionState->updateObjectEasy(flags, _numSubsteps); motionState->updateBodyEasy(flags, _numSubsteps);
} }
if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) { if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) {
motionState->resetMeasuredAcceleration(); motionState->resetMeasuredBodyAcceleration();
} }
} else { } else {
// the only way we should ever get here (motionState exists but no body) is when the object // the only way we should ever get here (motionState exists but no body) is when the object
@ -207,7 +208,7 @@ void PhysicsEngine::relayIncomingChangesToSimulation() {
// it is possible that the changes are such that the object can now be added to the physical simulation // it is possible that the changes are such that the object can now be added to the physical simulation
if (flags & EntityItem::DIRTY_SHAPE) { if (flags & EntityItem::DIRTY_SHAPE) {
ShapeInfo shapeInfo; ShapeInfo shapeInfo;
motionState->computeShapeInfo(shapeInfo); motionState->computeObjectShapeInfo(shapeInfo);
btCollisionShape* shape = _shapeManager.getShape(shapeInfo); btCollisionShape* shape = _shapeManager.getShape(shapeInfo);
if (shape) { if (shape) {
addObject(shapeInfo, shape, motionState); addObject(shapeInfo, shape, motionState);
@ -341,7 +342,7 @@ void PhysicsEngine::stepSimulation() {
// lock on the tree before we re-lock ourselves. // lock on the tree before we re-lock ourselves.
// //
// TODO: untangle these lock sequences. // TODO: untangle these lock sequences.
ObjectMotionState::setSimulationStep(_numSubsteps); ObjectMotionState::setWorldSimulationStep(_numSubsteps);
_entityTree->lockForWrite(); _entityTree->lockForWrite();
lock(); lock();
_dynamicsWorld->synchronizeMotionStates(); _dynamicsWorld->synchronizeMotionStates();
@ -498,7 +499,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap
btVector3 inertia(0.0f, 0.0f, 0.0f); btVector3 inertia(0.0f, 0.0f, 0.0f);
float mass = 0.0f; float mass = 0.0f;
btRigidBody* body = NULL; btRigidBody* body = NULL;
switch(motionState->computeMotionType()) { switch(motionState->computeObjectMotionType()) {
case MOTION_TYPE_KINEMATIC: { case MOTION_TYPE_KINEMATIC: {
body = new btRigidBody(mass, motionState, shape, inertia); body = new btRigidBody(mass, motionState, shape, inertia);
body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
@ -511,13 +512,13 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap
break; break;
} }
case MOTION_TYPE_DYNAMIC: { case MOTION_TYPE_DYNAMIC: {
mass = motionState->computeMass(shapeInfo); mass = motionState->computeObjectMass(shapeInfo);
shape->calculateLocalInertia(mass, inertia); shape->calculateLocalInertia(mass, inertia);
body = new btRigidBody(mass, motionState, shape, inertia); body = new btRigidBody(mass, motionState, shape, inertia);
body->updateInertiaTensor(); body->updateInertiaTensor();
motionState->setRigidBody(body); motionState->setRigidBody(body);
motionState->setKinematic(false, _numSubsteps); motionState->setKinematic(false, _numSubsteps);
motionState->updateObjectVelocities(); motionState->updateBodyVelocities();
// NOTE: Bullet will deactivate any object whose velocity is below these thresholds for longer than 2 seconds. // NOTE: Bullet will deactivate any object whose velocity is below these thresholds for longer than 2 seconds.
// (the 2 seconds is determined by: static btRigidBody::gDeactivationTime // (the 2 seconds is determined by: static btRigidBody::gDeactivationTime
const float DYNAMIC_LINEAR_VELOCITY_THRESHOLD = 0.05f; // 5 cm/sec const float DYNAMIC_LINEAR_VELOCITY_THRESHOLD = 0.05f; // 5 cm/sec
@ -540,11 +541,10 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap
} }
} }
body->setFlags(BT_DISABLE_WORLD_GRAVITY); body->setFlags(BT_DISABLE_WORLD_GRAVITY);
body->setRestitution(motionState->_restitution); motionState->updateBodyMaterialProperties();
body->setFriction(motionState->_friction);
body->setDamping(motionState->_linearDamping, motionState->_angularDamping);
_dynamicsWorld->addRigidBody(body); _dynamicsWorld->addRigidBody(body);
motionState->resetMeasuredAcceleration(); motionState->resetMeasuredBodyAcceleration();
} }
void PhysicsEngine::bump(EntityItem* bumpEntity) { void PhysicsEngine::bump(EntityItem* bumpEntity) {
@ -608,8 +608,8 @@ void PhysicsEngine::removeObjectFromBullet(ObjectMotionState* motionState) {
} }
// private // private
bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) { bool PhysicsEngine::updateBodyHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) {
MotionType newType = motionState->computeMotionType(); MotionType newType = motionState->computeObjectMotionType();
// pull body out of physics engine // pull body out of physics engine
_dynamicsWorld->removeRigidBody(body); _dynamicsWorld->removeRigidBody(body);
@ -621,7 +621,7 @@ bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
// get new shape // get new shape
btCollisionShape* oldShape = body->getCollisionShape(); btCollisionShape* oldShape = body->getCollisionShape();
ShapeInfo shapeInfo; ShapeInfo shapeInfo;
motionState->computeShapeInfo(shapeInfo); motionState->computeObjectShapeInfo(shapeInfo);
btCollisionShape* newShape = _shapeManager.getShape(shapeInfo); btCollisionShape* newShape = _shapeManager.getShape(shapeInfo);
if (!newShape) { if (!newShape) {
// FAIL! we are unable to support these changes! // FAIL! we are unable to support these changes!
@ -640,7 +640,7 @@ bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
_shapeManager.releaseShape(oldShape); _shapeManager.releaseShape(oldShape);
// compute mass properties // compute mass properties
float mass = motionState->computeMass(shapeInfo); float mass = motionState->computeObjectMass(shapeInfo);
btVector3 inertia(0.0f, 0.0f, 0.0f); btVector3 inertia(0.0f, 0.0f, 0.0f);
body->getCollisionShape()->calculateLocalInertia(mass, inertia); body->getCollisionShape()->calculateLocalInertia(mass, inertia);
body->setMassProps(mass, inertia); body->setMassProps(mass, inertia);
@ -653,7 +653,7 @@ bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
} }
bool easyUpdate = flags & EASY_DIRTY_PHYSICS_FLAGS; bool easyUpdate = flags & EASY_DIRTY_PHYSICS_FLAGS;
if (easyUpdate) { if (easyUpdate) {
motionState->updateObjectEasy(flags, _numSubsteps); motionState->updateBodyEasy(flags, _numSubsteps);
} }
// update the motion parameters // update the motion parameters
@ -675,8 +675,8 @@ bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
if (! (flags & EntityItem::DIRTY_MASS)) { if (! (flags & EntityItem::DIRTY_MASS)) {
// always update mass properties when going dynamic (unless it's already been done above) // always update mass properties when going dynamic (unless it's already been done above)
ShapeInfo shapeInfo; ShapeInfo shapeInfo;
motionState->computeShapeInfo(shapeInfo); motionState->computeObjectShapeInfo(shapeInfo);
float mass = motionState->computeMass(shapeInfo); float mass = motionState->computeObjectMass(shapeInfo);
btVector3 inertia(0.0f, 0.0f, 0.0f); btVector3 inertia(0.0f, 0.0f, 0.0f);
body->getCollisionShape()->calculateLocalInertia(mass, inertia); body->getCollisionShape()->calculateLocalInertia(mass, inertia);
body->setMassProps(mass, inertia); body->setMassProps(mass, inertia);

View file

@ -39,8 +39,8 @@ public:
ContactKey(void* a, void* b) : _a(a), _b(b) {} ContactKey(void* a, void* b) : _a(a), _b(b) {}
bool operator<(const ContactKey& other) const { return _a < other._a || (_a == other._a && _b < other._b); } bool operator<(const ContactKey& other) const { return _a < other._a || (_a == other._a && _b < other._b); }
bool operator==(const ContactKey& other) const { return _a == other._a && _b == other._b; } bool operator==(const ContactKey& other) const { return _a == other._a && _b == other._b; }
void* _a; void* _a; // EntityMotionState pointer
void* _b; void* _b; // EntityMotionState pointer
}; };
typedef std::map<ContactKey, ContactInfo> ContactMap; typedef std::map<ContactKey, ContactInfo> ContactMap;
@ -100,7 +100,7 @@ private:
void doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB); void doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB);
// return 'true' of update was successful // return 'true' of update was successful
bool updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); bool updateBodyHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags);
void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags);
btClock _clock; btClock _clock;

View file

@ -426,12 +426,12 @@ bool Model::updateGeometry() {
_dilatedTextures.clear(); _dilatedTextures.clear();
_geometry = geometry; _geometry = geometry;
_meshGroupsKnown = false; _meshGroupsKnown = false;
setJointStates(newJointStates); initJointStates(newJointStates);
needToRebuild = true; needToRebuild = true;
} else if (_jointStates.isEmpty()) { } else if (_jointStates.isEmpty()) {
const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); const FBXGeometry& fbxGeometry = geometry->getFBXGeometry();
if (fbxGeometry.joints.size() > 0) { if (fbxGeometry.joints.size() > 0) {
setJointStates(createJointStates(fbxGeometry)); initJointStates(createJointStates(fbxGeometry));
needToRebuild = true; needToRebuild = true;
} }
} else if (!geometry->isLoaded()) { } else if (!geometry->isLoaded()) {
@ -469,7 +469,7 @@ bool Model::updateGeometry() {
} }
// virtual // virtual
void Model::setJointStates(QVector<JointState> states) { void Model::initJointStates(QVector<JointState> states) {
_jointStates = states; _jointStates = states;
initJointTransforms(); initJointTransforms();

View file

@ -244,7 +244,7 @@ protected:
// returns 'true' if needs fullUpdate after geometry change // returns 'true' if needs fullUpdate after geometry change
bool updateGeometry(); bool updateGeometry();
virtual void setJointStates(QVector<JointState> states); virtual void initJointStates(QVector<JointState> states);
void setScaleInternal(const glm::vec3& scale); void setScaleInternal(const glm::vec3& scale);
void scaleToFit(); void scaleToFit();

View file

@ -19,19 +19,10 @@
QString& PathUtils::resourcesPath() { QString& PathUtils::resourcesPath() {
#ifdef DEBUG
static QString staticResourcePath;
if (staticResourcePath.isEmpty()) {
QDir path(__FILE__);
path.cdUp();
staticResourcePath = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/";
}
#else
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/"; static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/";
#else #else
static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/resources/"; static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/resources/";
#endif
#endif #endif
return staticResourcePath; return staticResourcePath;

View file

@ -426,7 +426,6 @@ void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function<voi
item = _rootItem->findChild<QQuickItem*>(name); item = _rootItem->findChild<QQuickItem*>(name);
} }
if (item) { if (item) {
qDebug() << "Turning item " << !item->isEnabled();
item->setEnabled(!item->isEnabled()); item->setEnabled(!item->isEnabled());
} }
} }

View file

@ -42,7 +42,7 @@ private:
qmlObject->setUserData(USER_DATA_ID, this); qmlObject->setUserData(USER_DATA_ID, this);
qmlObject->setObjectName(uuid.toString()); qmlObject->setObjectName(uuid.toString());
// Make sure we can find it again in the future // Make sure we can find it again in the future
Q_ASSERT(VrMenu::instance()->findMenuObject(uuid.toString())); Q_ASSERT(VrMenu::_instance->findMenuObject(uuid.toString()));
} }
}; };
@ -57,14 +57,25 @@ HIFI_QML_DEF_LAMBDA(VrMenu, [&](QQmlContext* context, QObject* newItem) {
}); });
VrMenu* VrMenu::_instance{ nullptr }; VrMenu* VrMenu::_instance{ nullptr };
static QQueue<std::function<void(VrMenu*)>> queuedLambdas;
VrMenu* VrMenu::instance() { void VrMenu::executeOrQueue(std::function<void(VrMenu*)> f) {
if (!_instance) { if (_instance) {
VrMenu::registerType(); foreach(std::function<void(VrMenu*)> priorLambda, queuedLambdas) {
VrMenu::load(); priorLambda(_instance);
Q_ASSERT(_instance); }
f(_instance);
} else {
queuedLambdas.push_back(f);
} }
return _instance; }
void VrMenu::executeQueuedLambdas() {
Q_ASSERT(_instance);
foreach(std::function<void(VrMenu*)> f, queuedLambdas) {
f(_instance);
}
queuedLambdas.clear();
} }
VrMenu::VrMenu(QQuickItem* parent) : QQuickItem(parent) { VrMenu::VrMenu(QQuickItem* parent) : QQuickItem(parent) {
@ -72,6 +83,7 @@ VrMenu::VrMenu(QQuickItem* parent) : QQuickItem(parent) {
this->setEnabled(false); this->setEnabled(false);
} }
// QML helper functions // QML helper functions
QObject* addMenu(QObject* parent, const QString& text) { QObject* addMenu(QObject* parent, const QString& text) {
// FIXME add more checking here to ensure no name conflicts // FIXME add more checking here to ensure no name conflicts

View file

@ -26,7 +26,8 @@ class VrMenu : public QQuickItem {
HIFI_QML_DECL_LAMBDA HIFI_QML_DECL_LAMBDA
public: public:
static VrMenu* instance(); static void executeOrQueue(std::function<void(VrMenu*)> f);
static void executeQueuedLambdas();
VrMenu(QQuickItem* parent = nullptr); VrMenu(QQuickItem* parent = nullptr);
void addMenu(QMenu* menu); void addMenu(QMenu* menu);
void addAction(QMenu* parent, QAction* action); void addAction(QMenu* parent, QAction* action);

View file

@ -358,7 +358,7 @@ public:
#else #else
offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir())); offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir()));
offscreenUi->load(QUrl("TestRoot.qml")); offscreenUi->load(QUrl("TestRoot.qml"));
offscreenUi->load(QUrl("InterfaceMenu.qml")); offscreenUi->load(QUrl("TestMenu.qml"));
// Requires a root menu to have been loaded before it can load // Requires a root menu to have been loaded before it can load
VrMenu::load(); VrMenu::load();
#endif #endif
@ -481,7 +481,7 @@ void QTestWindow::renderQml() {
const char * LOG_FILTER_RULES = R"V0G0N( const char * LOG_FILTER_RULES = R"V0G0N(
*.debug=false hifi.offscreen.focus.debug=false
qt.quick.mouse.debug=false qt.quick.mouse.debug=false
)V0G0N"; )V0G0N";
@ -495,7 +495,7 @@ qt.quick.mouse.debug=false
int main(int argc, char** argv) { int main(int argc, char** argv) {
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
// QLoggingCategory::setFilterRules(LOG_FILTER_RULES); QLoggingCategory::setFilterRules(LOG_FILTER_RULES);
QTestWindow window; QTestWindow window;
app.exec(); app.exec();
return 0; return 0;