Merge branch 'master' into M17901

This commit is contained in:
David Rowe 2018-09-06 09:30:10 +12:00
commit 097b4c177a
83 changed files with 1261 additions and 389 deletions

View file

@ -46,6 +46,7 @@ This can either be entered directly into your shell session before you build or
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/Qt5.10.1/5.10.1/gcc_64/lib/cmake
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.10.1/clang_64/lib/cmake/
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.10.1/lib/cmake
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake

View file

@ -6,13 +6,20 @@ Please read the [general build guide](BUILD.md) for information on dependencies
Should you choose not to install Qt5 via a package manager that handles dependencies for you, you may be missing some Qt5 dependencies. On Ubuntu, for example, the following additional packages are required:
libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev
libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev zlib1g-dev
## Ubuntu 16.04 specific build guide
## Ubuntu 16.04/18.04 specific build guide
### Ubuntu 18.04 only
Add the universe repository:
_(This is not enabled by default on the server edition)_
```bash
sudo add-apt-repository universe
sudo apt-get update
```
### Prepare environment
hifiqt5.10.1
Install qt:
Install Qt 5.10.1:
```bash
wget http://debian.highfidelity.com/pool/h/hi/hifiqt5.10.1_5.10.1_amd64.deb
sudo dpkg -i hifiqt5.10.1_5.10.1_amd64.deb
@ -20,19 +27,20 @@ sudo dpkg -i hifiqt5.10.1_5.10.1_amd64.deb
Install build dependencies:
```bash
sudo apt-get install libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev
sudo apt-get install libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev zlib1g-dev
```
To compile interface in a server you must install:
```bash
sudo apt -y install libpulse0 libnss3 libnspr4 libfontconfig1 libxcursor1 libxcomposite1 libxtst6 libxslt1.1
sudo apt-get -y install libpulse0 libnss3 libnspr4 libfontconfig1 libxcursor1 libxcomposite1 libxtst6 libxslt1.1
```
Install build tools:
```bash
sudo apt install cmake
sudo apt-get install cmake
```
### Get code and checkout the tag you need
Clone this repository:
@ -48,12 +56,7 @@ git tags
Then checkout last tag with:
```bash
git checkout tags/RELEASE-6819
```
Or go to the highfidelity download page (https://highfidelity.com/download) to get the release version. For example, if there is a BETA 6731 type:
```bash
git checkout tags/RELEASE-6731
git checkout tags/v0.71.0
```
### Compiling
@ -66,15 +69,20 @@ cd hifi/build
Prepare makefiles:
```bash
cmake -DQT_CMAKE_PREFIX_PATH=/usr/local/Qt5.10.1/5.10/gcc_64/lib/cmake ..
cmake -DQT_CMAKE_PREFIX_PATH=/usr/local/Qt5.10.1/5.10.1/gcc_64/lib/cmake ..
```
Start compilation and get a cup of coffee:
Start compilation of the server and get a cup of coffee:
```bash
make domain-server assignment-client interface
make domain-server assignment-client
```
In a server does not make sense to compile interface
To compile interface:
```bash
make interface
```
In a server, it does not make sense to compile interface
### Running the software
@ -93,4 +101,4 @@ Running interface:
./interface/interface
```
Go to localhost in running interface.
Go to localhost in the running interface.

View file

@ -329,6 +329,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,
AvatarData::_avatarSortCoefficientAge);
sortedAvatars.reserve(avatarsToSort.size());
// ignore or sort
const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer();
@ -429,9 +430,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
int remainingAvatars = (int)sortedAvatars.size();
auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true);
while (!sortedAvatars.empty()) {
const auto avatarData = sortedAvatars.top().getAvatar();
sortedAvatars.pop();
const auto& sortedAvatarVector = sortedAvatars.getSortedVector();
for (const auto& sortedAvatar : sortedAvatarVector) {
const auto& avatarData = sortedAvatar.getAvatar();
remainingAvatars--;
auto otherNode = avatarDataToNodes[avatarData];

View file

@ -5820,15 +5820,13 @@ void Application::update(float deltaTime) {
auto t5 = std::chrono::high_resolution_clock::now();
workload::Timings timings(6);
timings[0] = (t4 - t0);
timings[1] = (t5 - t4);
timings[2] = (t4 - t3);
timings[3] = (t3 - t2);
timings[4] = (t2 - t1);
timings[5] = (t1 - t0);
timings[0] = t1 - t0; // prePhysics entities
timings[1] = t2 - t1; // prePhysics avatars
timings[2] = t3 - t2; // stepPhysics
timings[3] = t4 - t3; // postPhysics
timings[4] = t5 - t4; // non-physical kinematics
timings[5] = workload::Timing_ns((int32_t)(NSECS_PER_SECOND * deltaTime)); // game loop duration
_gameWorkload.updateSimulationTimings(timings);
}
}
} else {
@ -6366,7 +6364,6 @@ void Application::clearDomainOctreeDetails() {
}
void Application::clearDomainAvatars() {
getMyAvatar()->setAvatarEntityDataChanged(true); // to recreate worn entities
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
}

View file

@ -187,16 +187,17 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
AvatarSharedPointer _avatar;
};
auto avatarMap = getHashCopy();
AvatarHash::iterator itr = avatarMap.begin();
const auto& views = qApp->getConicalViews();
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(views,
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,
AvatarData::_avatarSortCoefficientAge);
sortedAvatars.reserve(avatarMap.size() - 1); // don't include MyAvatar
// sort
auto avatarMap = getHashCopy();
AvatarHash::iterator itr = avatarMap.begin();
while (itr != avatarMap.end()) {
const auto& avatar = std::static_pointer_cast<Avatar>(*itr);
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
@ -206,6 +207,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
}
++itr;
}
const auto& sortedAvatarVector = sortedAvatars.getSortedVector();
// process in sorted order
uint64_t startTime = usecTimestampNow();
@ -216,8 +218,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
render::Transaction renderTransaction;
workload::Transaction workloadTransaction;
while (!sortedAvatars.empty()) {
const SortableAvatar& sortData = sortedAvatars.top();
for (auto it = sortedAvatarVector.begin(); it != sortedAvatarVector.end(); ++it) {
const SortableAvatar& sortData = *it;
const auto avatar = std::static_pointer_cast<OtherAvatar>(sortData.getAvatar());
// TODO: to help us scale to more avatars it would be nice to not have to poll orb state here
@ -231,7 +233,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID());
if (ignoring) {
sortedAvatars.pop();
continue;
}
@ -260,26 +261,19 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
// --> some avatar velocity measurements may be a little off
// no time to simulate, but we take the time to count how many were tragically missed
bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
if (!inView) {
break;
}
if (inView && avatar->hasNewJointData()) {
numAVatarsNotUpdated++;
}
sortedAvatars.pop();
while (inView && !sortedAvatars.empty()) {
const SortableAvatar& newSortData = sortedAvatars.top();
while (it != sortedAvatarVector.end()) {
const SortableAvatar& newSortData = *it;
const auto newAvatar = std::static_pointer_cast<Avatar>(newSortData.getAvatar());
inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
if (inView && newAvatar->hasNewJointData()) {
numAVatarsNotUpdated++;
bool inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
// Once we reach an avatar that's not in view, all avatars after it will also be out of view
if (!inView) {
break;
}
sortedAvatars.pop();
numAVatarsNotUpdated += (int)(newAvatar->hasNewJointData());
++it;
}
break;
}
sortedAvatars.pop();
}
if (_shouldRender) {

View file

@ -4313,7 +4313,8 @@ glm::mat4 MyAvatar::getCenterEyeCalibrationMat() const {
auto centerEyeRot = Quaternions::Y_180;
return createMatFromQuatAndPos(centerEyeRot, centerEyePos / getSensorToWorldScale());
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_MIDDLE_EYE_ROT, DEFAULT_AVATAR_MIDDLE_EYE_POS / getSensorToWorldScale());
glm::mat4 headMat = getHeadCalibrationMat();
return createMatFromQuatAndPos(DEFAULT_AVATAR_MIDDLE_EYE_ROT, extractTranslation(headMat) + DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET);
}
}
@ -4323,9 +4324,10 @@ glm::mat4 MyAvatar::getHeadCalibrationMat() const {
if (headIndex >= 0) {
auto headPos = getAbsoluteDefaultJointTranslationInObjectFrame(headIndex);
auto headRot = getAbsoluteDefaultJointRotationInObjectFrame(headIndex);
return createMatFromQuatAndPos(headRot, headPos / getSensorToWorldScale());
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_HEAD_ROT, DEFAULT_AVATAR_HEAD_POS / getSensorToWorldScale());
return createMatFromQuatAndPos(DEFAULT_AVATAR_HEAD_ROT, DEFAULT_AVATAR_HEAD_POS);
}
}
@ -4337,7 +4339,7 @@ glm::mat4 MyAvatar::getSpine2CalibrationMat() const {
auto spine2Rot = getAbsoluteDefaultJointRotationInObjectFrame(spine2Index);
return createMatFromQuatAndPos(spine2Rot, spine2Pos / getSensorToWorldScale());
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_SPINE2_ROT, DEFAULT_AVATAR_SPINE2_POS / getSensorToWorldScale());
return createMatFromQuatAndPos(DEFAULT_AVATAR_SPINE2_ROT, DEFAULT_AVATAR_SPINE2_POS);
}
}
@ -4349,7 +4351,7 @@ glm::mat4 MyAvatar::getHipsCalibrationMat() const {
auto hipsRot = getAbsoluteDefaultJointRotationInObjectFrame(hipsIndex);
return createMatFromQuatAndPos(hipsRot, hipsPos / getSensorToWorldScale());
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_HIPS_ROT, DEFAULT_AVATAR_HIPS_POS / getSensorToWorldScale());
return createMatFromQuatAndPos(DEFAULT_AVATAR_HIPS_ROT, DEFAULT_AVATAR_HIPS_POS);
}
}
@ -4361,7 +4363,7 @@ glm::mat4 MyAvatar::getLeftFootCalibrationMat() const {
auto leftFootRot = getAbsoluteDefaultJointRotationInObjectFrame(leftFootIndex);
return createMatFromQuatAndPos(leftFootRot, leftFootPos / getSensorToWorldScale());
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTFOOT_ROT, DEFAULT_AVATAR_LEFTFOOT_POS / getSensorToWorldScale());
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTFOOT_ROT, DEFAULT_AVATAR_LEFTFOOT_POS);
}
}
@ -4373,11 +4375,10 @@ glm::mat4 MyAvatar::getRightFootCalibrationMat() const {
auto rightFootRot = getAbsoluteDefaultJointRotationInObjectFrame(rightFootIndex);
return createMatFromQuatAndPos(rightFootRot, rightFootPos / getSensorToWorldScale());
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTFOOT_ROT, DEFAULT_AVATAR_RIGHTFOOT_POS / getSensorToWorldScale());
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTFOOT_ROT, DEFAULT_AVATAR_RIGHTFOOT_POS);
}
}
glm::mat4 MyAvatar::getRightArmCalibrationMat() const {
int rightArmIndex = _skeletonModel->getRig().indexOfJoint("RightArm");
if (rightArmIndex >= 0) {
@ -4385,7 +4386,7 @@ glm::mat4 MyAvatar::getRightArmCalibrationMat() const {
auto rightArmRot = getAbsoluteDefaultJointRotationInObjectFrame(rightArmIndex);
return createMatFromQuatAndPos(rightArmRot, rightArmPos / getSensorToWorldScale());
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTARM_ROT, DEFAULT_AVATAR_RIGHTARM_POS / getSensorToWorldScale());
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTARM_ROT, DEFAULT_AVATAR_RIGHTARM_POS);
}
}
@ -4396,7 +4397,7 @@ glm::mat4 MyAvatar::getLeftArmCalibrationMat() const {
auto leftArmRot = getAbsoluteDefaultJointRotationInObjectFrame(leftArmIndex);
return createMatFromQuatAndPos(leftArmRot, leftArmPos / getSensorToWorldScale());
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTARM_ROT, DEFAULT_AVATAR_LEFTARM_POS / getSensorToWorldScale());
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTARM_ROT, DEFAULT_AVATAR_LEFTARM_POS);
}
}
@ -4407,7 +4408,7 @@ glm::mat4 MyAvatar::getRightHandCalibrationMat() const {
auto rightHandRot = getAbsoluteDefaultJointRotationInObjectFrame(rightHandIndex);
return createMatFromQuatAndPos(rightHandRot, rightHandPos / getSensorToWorldScale());
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTHAND_ROT, DEFAULT_AVATAR_RIGHTHAND_POS / getSensorToWorldScale());
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTHAND_ROT, DEFAULT_AVATAR_RIGHTHAND_POS);
}
}
@ -4418,7 +4419,7 @@ glm::mat4 MyAvatar::getLeftHandCalibrationMat() const {
auto leftHandRot = getAbsoluteDefaultJointRotationInObjectFrame(leftHandIndex);
return createMatFromQuatAndPos(leftHandRot, leftHandPos / getSensorToWorldScale());
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTHAND_ROT, DEFAULT_AVATAR_LEFTHAND_POS / getSensorToWorldScale());
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTHAND_ROT, DEFAULT_AVATAR_LEFTHAND_POS);
}
}

View file

@ -1034,7 +1034,7 @@ public:
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
// all calibration matrices are in absolute avatar space.
// all calibration matrices are in absolute sensor space.
glm::mat4 getCenterEyeCalibrationMat() const;
glm::mat4 getHeadCalibrationMat() const;
glm::mat4 getSpine2CalibrationMat() const;

View file

@ -0,0 +1,23 @@
//
// Created by Sabrina Shanman 8/14/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "MyAvatarHeadTransformNode.h"
#include "DependencyManager.h"
#include "AvatarManager.h"
#include "MyAvatar.h"
Transform MyAvatarHeadTransformNode::getTransform() {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
glm::vec3 pos = myAvatar->getHeadPosition();
glm::quat headOri = myAvatar->getHeadOrientation();
glm::quat ori = headOri * glm::angleAxis(-PI / 2.0f, Vectors::RIGHT);
return Transform(ori, glm::vec3(1.0f), pos);
}

View file

@ -0,0 +1,19 @@
//
// Created by Sabrina Shanman 8/14/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_MyAvatarHeadTransformNode_h
#define hifi_MyAvatarHeadTransformNode_h
#include "TransformNode.h"
class MyAvatarHeadTransformNode : public TransformNode {
public:
MyAvatarHeadTransformNode() { }
Transform getTransform() override;
};
#endif // hifi_MyAvatarHeadTransformNode_h

View file

@ -29,20 +29,23 @@ OtherAvatar::~OtherAvatar() {
}
void OtherAvatar::removeOrb() {
if (qApp->getOverlays().isAddedOverlay(_otherAvatarOrbMeshPlaceholderID)) {
if (!_otherAvatarOrbMeshPlaceholderID.isNull()) {
qApp->getOverlays().deleteOverlay(_otherAvatarOrbMeshPlaceholderID);
_otherAvatarOrbMeshPlaceholderID = UNKNOWN_OVERLAY_ID;
}
}
void OtherAvatar::updateOrbPosition() {
if (_otherAvatarOrbMeshPlaceholder != nullptr) {
_otherAvatarOrbMeshPlaceholder->setWorldPosition(getHead()->getPosition());
if (_otherAvatarOrbMeshPlaceholderID.isNull()) {
_otherAvatarOrbMeshPlaceholderID = qApp->getOverlays().addOverlay(_otherAvatarOrbMeshPlaceholder);
}
}
}
void OtherAvatar::createOrb() {
if (_otherAvatarOrbMeshPlaceholderID == UNKNOWN_OVERLAY_ID ||
!qApp->getOverlays().isAddedOverlay(_otherAvatarOrbMeshPlaceholderID)) {
if (_otherAvatarOrbMeshPlaceholderID.isNull()) {
_otherAvatarOrbMeshPlaceholder = std::make_shared<Sphere3DOverlay>();
_otherAvatarOrbMeshPlaceholder->setAlpha(1.0f);
_otherAvatarOrbMeshPlaceholder->setColor({ 0xFF, 0x00, 0xFF });

View file

@ -40,11 +40,9 @@ QmlCommerce::QmlCommerce() {
connect(ledger.data(), &Ledger::transferAssetToUsernameResult, this, &QmlCommerce::transferAssetToUsernameResult);
connect(ledger.data(), &Ledger::availableUpdatesResult, this, &QmlCommerce::availableUpdatesResult);
connect(ledger.data(), &Ledger::updateItemResult, this, &QmlCommerce::updateItemResult);
auto accountManager = DependencyManager::get<AccountManager>();
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() {
setPassphrase("");
});
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { setPassphrase(""); });
_appsPath = PathUtils::getAppDataPath() + "Apps/";
}
@ -105,7 +103,11 @@ void QmlCommerce::balance() {
}
}
void QmlCommerce::inventory(const QString& editionFilter, const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage) {
void QmlCommerce::inventory(const QString& editionFilter,
const QString& typeFilter,
const QString& titleFilter,
const int& page,
const int& perPage) {
auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>();
QStringList cachedPublicKeys = wallet->listPublicKeys();
@ -166,24 +168,30 @@ void QmlCommerce::certificateInfo(const QString& certificateId) {
ledger->certificateInfo(certificateId);
}
void QmlCommerce::transferAssetToNode(const QString& nodeID, const QString& certificateID, const int& amount, const QString& optionalMessage) {
void QmlCommerce::transferAssetToNode(const QString& nodeID,
const QString& certificateID,
const int& amount,
const QString& optionalMessage) {
auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>();
QStringList keys = wallet->listPublicKeys();
if (keys.count() == 0) {
QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } };
QJsonObject result{ { "status", "fail" }, { "message", "Uninitialized Wallet." } };
return emit transferAssetToNodeResult(result);
}
QString key = keys[0];
ledger->transferAssetToNode(key, nodeID, certificateID, amount, optionalMessage);
}
void QmlCommerce::transferAssetToUsername(const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage) {
void QmlCommerce::transferAssetToUsername(const QString& username,
const QString& certificateID,
const int& amount,
const QString& optionalMessage) {
auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>();
QStringList keys = wallet->listPublicKeys();
if (keys.count() == 0) {
QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } };
QJsonObject result{ { "status", "fail" }, { "message", "Uninitialized Wallet." } };
return emit transferAssetToUsernameResult(result);
}
QString key = keys[0];
@ -194,10 +202,7 @@ void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& cert
auto ledger = DependencyManager::get<Ledger>();
ledger->updateLocation(certificateID, DependencyManager::get<AddressManager>()->getPlaceName(), true);
qApp->replaceDomainContent(itemHref);
QJsonObject messageProperties = {
{ "status", "SuccessfulRequestToReplaceContent" },
{ "content_set_url", itemHref }
};
QJsonObject messageProperties = { { "status", "SuccessfulRequestToReplaceContent" }, { "content_set_url", itemHref } };
UserActivityLogger::getInstance().logAction("replace_domain_content", messageProperties);
emit contentSetChanged(itemHref);
@ -214,10 +219,7 @@ QString QmlCommerce::getInstalledApps(const QString& justInstalledAppID) {
QDir directory(_appsPath);
QStringList apps = directory.entryList(QStringList("*.app.json"));
foreach(QString appFileName, apps) {
installedAppsFromMarketplace += appFileName;
installedAppsFromMarketplace += ",";
foreach (QString appFileName, apps) {
// If we were supplied a "justInstalledAppID" argument, that means we're entering this function
// to get the new list of installed apps immediately after installing an app.
// In that case, the app we installed may not yet have its associated script running -
@ -243,10 +245,12 @@ QString QmlCommerce::getInstalledApps(const QString& justInstalledAppID) {
// delete the .app.json from the user's local disk.
if (!runningScripts.contains(scriptURL)) {
if (!appFile.remove()) {
qCWarning(commerce)
<< "Couldn't delete local .app.json file (app's script isn't running). App filename is:"
<< appFileName;
qCWarning(commerce) << "Couldn't delete local .app.json file (app's script isn't running). App filename is:"
<< appFileName;
}
} else {
installedAppsFromMarketplace += appFileName;
installedAppsFromMarketplace += ",";
}
} else {
qCDebug(commerce) << "Couldn't open local .app.json file for reading.";
@ -317,7 +321,9 @@ bool QmlCommerce::uninstallApp(const QString& itemHref) {
// Read from the file to know what .js script to stop
QFile appFile(_appsPath + "/" + appHref.fileName());
if (!appFile.open(QIODevice::ReadOnly)) {
qCDebug(commerce) << "Couldn't open local .app.json file for deletion. Cannot continue with app uninstallation. App filename is:" << appHref.fileName();
qCDebug(commerce)
<< "Couldn't open local .app.json file for deletion. Cannot continue with app uninstallation. App filename is:"
<< appHref.fileName();
return false;
}
QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(appFile.readAll());
@ -325,13 +331,15 @@ bool QmlCommerce::uninstallApp(const QString& itemHref) {
QString scriptUrl = appFileJsonObject["scriptURL"].toString();
if (!DependencyManager::get<ScriptEngines>()->stopScript(scriptUrl.trimmed(), false)) {
qCWarning(commerce) << "Couldn't stop script during app uninstall. Continuing anyway. ScriptURL is:" << scriptUrl.trimmed();
qCWarning(commerce) << "Couldn't stop script during app uninstall. Continuing anyway. ScriptURL is:"
<< scriptUrl.trimmed();
}
// Delete the .app.json from the filesystem
// remove() closes the file first.
if (!appFile.remove()) {
qCWarning(commerce) << "Couldn't delete local .app.json file during app uninstall. Continuing anyway. App filename is:" << appHref.fileName();
qCWarning(commerce) << "Couldn't delete local .app.json file during app uninstall. Continuing anyway. App filename is:"
<< appHref.fileName();
}
QFileInfo appFileInfo(appFile);
@ -352,7 +360,8 @@ bool QmlCommerce::openApp(const QString& itemHref) {
QJsonObject appFileJsonObject = appFileJsonDocument.object();
QString homeUrl = appFileJsonObject["homeURL"].toString();
auto tablet = dynamic_cast<TabletProxy*>(DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system"));
auto tablet = dynamic_cast<TabletProxy*>(
DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system"));
if (homeUrl.contains(".qml", Qt::CaseInsensitive)) {
tablet->loadQMLSource(homeUrl);
} else if (homeUrl.contains(".html", Qt::CaseInsensitive)) {
@ -377,7 +386,7 @@ void QmlCommerce::updateItem(const QString& certificateId) {
auto wallet = DependencyManager::get<Wallet>();
QStringList keys = wallet->listPublicKeys();
if (keys.count() == 0) {
QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } };
QJsonObject result{ { "status", "fail" }, { "message", "Uninitialized Wallet." } };
return emit updateItemResult(result);
}
QString key = keys[0];

View file

@ -32,9 +32,6 @@ PickResultPointer CollisionPickResult::compareAndProcessNewResult(const PickResu
}
intersects = entityIntersections.size() || avatarIntersections.size();
if (newCollisionResult->loadState == LOAD_STATE_NOT_LOADED || loadState == LOAD_STATE_UNKNOWN) {
loadState = (LoadState)newCollisionResult->loadState;
}
return std::make_shared<CollisionPickResult>(*this);
}
@ -80,23 +77,42 @@ QVariantMap CollisionPickResult::toVariantMap() const {
}
variantMap["intersectingObjects"] = qIntersectingObjects;
variantMap["loaded"] = (loadState == LOAD_STATE_LOADED);
variantMap["collisionRegion"] = pickVariant;
return variantMap;
}
bool CollisionPick::isShapeInfoReady() {
bool CollisionPick::isLoaded() const {
return !_mathPick.shouldComputeShapeInfo() || (_cachedResource && _cachedResource->isLoaded());
}
bool CollisionPick::getShapeInfoReady() {
if (_mathPick.shouldComputeShapeInfo()) {
if (_cachedResource && _cachedResource->isLoaded()) {
computeShapeInfo(_mathPick, *_mathPick.shapeInfo, _cachedResource);
return true;
_mathPick.loaded = true;
} else {
_mathPick.loaded = false;
}
return false;
} else {
computeShapeInfoDimensionsOnly(_mathPick, *_mathPick.shapeInfo, _cachedResource);
_mathPick.loaded = true;
}
return true;
return _mathPick.loaded;
}
void CollisionPick::computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource) {
ShapeType type = shapeInfo.getType();
glm::vec3 dimensions = pick.transform.getScale();
QString modelURL = (resource ? resource->getURL().toString() : "");
if (type == SHAPE_TYPE_COMPOUND) {
shapeInfo.setParams(type, dimensions, modelURL);
} else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) {
shapeInfo.setParams(type, 0.5f * dimensions, modelURL);
} else {
shapeInfo.setParams(type, 0.5f * dimensions, modelURL);
}
}
void CollisionPick::computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource) {
@ -328,8 +344,25 @@ void CollisionPick::computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo
}
}
CollisionPick::CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine) :
Pick(filter, maxDistance, enabled),
_mathPick(collisionRegion),
_physicsEngine(physicsEngine) {
if (collisionRegion.shouldComputeShapeInfo()) {
_cachedResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(collisionRegion.modelURL);
}
_mathPick.loaded = isLoaded();
}
CollisionRegion CollisionPick::getMathematicalPick() const {
return _mathPick;
CollisionRegion mathPick = _mathPick;
mathPick.loaded = isLoaded();
if (!parentTransform) {
return mathPick;
} else {
mathPick.transform = parentTransform->getTransform().worldTransform(mathPick.transform);
return mathPick;
}
}
void CollisionPick::filterIntersections(std::vector<ContactTestResult>& intersections) const {
@ -356,31 +389,37 @@ void CollisionPick::filterIntersections(std::vector<ContactTestResult>& intersec
}
PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pick) {
if (!isShapeInfoReady()) {
if (!pick.loaded) {
// Cannot compute result
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
}
getShapeInfoReady();
auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform);
auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold);
filterIntersections(entityIntersections);
return std::make_shared<CollisionPickResult>(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::vector<ContactTestResult>());
return std::make_shared<CollisionPickResult>(pick, entityIntersections, std::vector<ContactTestResult>());
}
PickResultPointer CollisionPick::getOverlayIntersection(const CollisionRegion& pick) {
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
}
PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pick) {
if (!isShapeInfoReady()) {
if (!pick.loaded) {
// Cannot compute result
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
}
getShapeInfoReady();
auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform);
auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold);
filterIntersections(avatarIntersections);
return std::make_shared<CollisionPickResult>(pick, CollisionPickResult::LOAD_STATE_LOADED, std::vector<ContactTestResult>(), avatarIntersections);
return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), avatarIntersections);
}
PickResultPointer CollisionPick::getHUDIntersection(const CollisionRegion& pick) {
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
}
Transform CollisionPick::getResultTransform() const {
return Transform(getMathematicalPick().transform);
}

View file

@ -11,35 +11,28 @@
#include <PhysicsEngine.h>
#include <model-networking/ModelCache.h>
#include <RegisteredMetaTypes.h>
#include <TransformNode.h>
#include <Pick.h>
class CollisionPickResult : public PickResult {
public:
enum LoadState {
LOAD_STATE_UNKNOWN,
LOAD_STATE_NOT_LOADED,
LOAD_STATE_LOADED
};
CollisionPickResult() {}
CollisionPickResult(const QVariantMap& pickVariant) : PickResult(pickVariant) {}
CollisionPickResult(const CollisionRegion& searchRegion, LoadState loadState, const std::vector<ContactTestResult>& entityIntersections, const std::vector<ContactTestResult>& avatarIntersections) :
CollisionPickResult(const CollisionRegion& searchRegion, const std::vector<ContactTestResult>& entityIntersections, const std::vector<ContactTestResult>& avatarIntersections) :
PickResult(searchRegion.toVariantMap()),
loadState(loadState),
intersects(entityIntersections.size() || avatarIntersections.size()),
entityIntersections(entityIntersections),
avatarIntersections(avatarIntersections) {
avatarIntersections(avatarIntersections)
{
}
CollisionPickResult(const CollisionPickResult& collisionPickResult) : PickResult(collisionPickResult.pickVariant) {
avatarIntersections = collisionPickResult.avatarIntersections;
entityIntersections = collisionPickResult.entityIntersections;
intersects = collisionPickResult.intersects;
loadState = collisionPickResult.loadState;
}
LoadState loadState { LOAD_STATE_UNKNOWN };
bool intersects { false };
std::vector<ContactTestResult> entityIntersections;
std::vector<ContactTestResult> avatarIntersections;
@ -54,28 +47,24 @@ public:
class CollisionPick : public Pick<CollisionRegion> {
public:
CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine) :
Pick(filter, maxDistance, enabled),
_mathPick(collisionRegion),
_physicsEngine(physicsEngine) {
if (collisionRegion.shouldComputeShapeInfo()) {
_cachedResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(collisionRegion.modelURL);
}
}
CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine);
CollisionRegion getMathematicalPick() const override;
PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override {
return std::make_shared<CollisionPickResult>(pickVariant, CollisionPickResult::LOAD_STATE_UNKNOWN, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
return std::make_shared<CollisionPickResult>(pickVariant, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
}
PickResultPointer getEntityIntersection(const CollisionRegion& pick) override;
PickResultPointer getOverlayIntersection(const CollisionRegion& pick) override;
PickResultPointer getAvatarIntersection(const CollisionRegion& pick) override;
PickResultPointer getHUDIntersection(const CollisionRegion& pick) override;
Transform getResultTransform() const override;
protected:
// Returns true if pick.shapeInfo is valid. Otherwise, attempts to get the shapeInfo ready for use.
bool isShapeInfoReady();
// Returns true if the resource for _mathPick.shapeInfo is loaded or if a resource is not needed.
bool isLoaded() const;
// Returns true if _mathPick.shapeInfo is valid. Otherwise, attempts to get the _mathPick ready for use.
bool getShapeInfoReady();
void computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
void computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
void filterIntersections(std::vector<ContactTestResult>& intersections) const;
CollisionRegion _mathPick;

View file

@ -35,6 +35,37 @@ void LaserPointer::editRenderStatePath(const std::string& state, const QVariant&
}
}
QVariantMap LaserPointer::toVariantMap() const {
QVariantMap qVariantMap;
QVariantMap qRenderStates;
for (auto iter = _renderStates.cbegin(); iter != _renderStates.cend(); iter++) {
auto renderState = iter->second;
QVariantMap qRenderState;
qRenderState["start"] = renderState->getStartID();
qRenderState["path"] = std::static_pointer_cast<RenderState>(renderState)->getPathID();
qRenderState["end"] = renderState->getEndID();
qRenderStates[iter->first.c_str()] = qRenderState;
}
qVariantMap["renderStates"] = qRenderStates;
QVariantMap qDefaultRenderStates;
for (auto iter = _defaultRenderStates.cbegin(); iter != _defaultRenderStates.cend(); iter++) {
float distance = iter->second.first;
auto defaultRenderState = iter->second.second;
QVariantMap qDefaultRenderState;
qDefaultRenderState["distance"] = distance;
qDefaultRenderState["start"] = defaultRenderState->getStartID();
qDefaultRenderState["path"] = std::static_pointer_cast<RenderState>(defaultRenderState)->getPathID();
qDefaultRenderState["end"] = defaultRenderState->getEndID();
qDefaultRenderStates[iter->first.c_str()] = qDefaultRenderState;
}
qVariantMap["defaultRenderStates"] = qDefaultRenderStates;
return qVariantMap;
}
glm::vec3 LaserPointer::getPickOrigin(const PickResultPointer& pickResult) const {
auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult);
return (rayPickResult ? vec3FromVariant(rayPickResult->pickVariant["origin"]) : glm::vec3(0.0f));

View file

@ -42,6 +42,8 @@ public:
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers,
bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled);
QVariantMap toVariantMap() const override;
static std::shared_ptr<StartEndRenderState> buildRenderState(const QVariantMap& propMap);
protected:

View file

@ -0,0 +1,27 @@
//
// Created by Sabrina Shanman 8/14/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "MouseTransformNode.h"
#include "Application.h"
#include "display-plugins/CompositorHelper.h"
#include "RayPick.h"
Transform MouseTransformNode::getTransform() {
QVariant position = qApp->getApplicationCompositor().getReticleInterface()->getPosition();
if (position.isValid()) {
Transform transform;
QVariantMap posMap = position.toMap();
PickRay pickRay = qApp->getCamera().computePickRay(posMap["x"].toFloat(), posMap["y"].toFloat());
transform.setTranslation(pickRay.origin);
transform.setRotation(rotationBetween(Vectors::UP, pickRay.direction));
return transform;
}
return Transform();
}

View file

@ -0,0 +1,18 @@
//
// Created by Sabrina Shanman 8/14/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_MouseTransformNode_h
#define hifi_MouseTransformNode_h
#include "TransformNode.h"
class MouseTransformNode : public TransformNode {
public:
Transform getTransform() override;
};
#endif // hifi_MouseTransformNode_h

View file

@ -70,4 +70,16 @@ glm::vec3 ParabolaPick::getAcceleration() const {
return scale * (DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldOrientation() * _accelerationAxis);
}
return scale * _accelerationAxis;
}
Transform ParabolaPick::getResultTransform() const {
PickResultPointer result = getPrevPickResult();
if (!result) {
return Transform();
}
auto parabolaResult = std::static_pointer_cast<ParabolaPickResult>(result);
Transform transform;
transform.setTranslation(parabolaResult->intersection);
return transform;
}

View file

@ -83,6 +83,7 @@ public:
PickResultPointer getOverlayIntersection(const PickParabola& pick) override;
PickResultPointer getAvatarIntersection(const PickParabola& pick) override;
PickResultPointer getHUDIntersection(const PickParabola& pick) override;
Transform getResultTransform() const override;
protected:
float _speed;

View file

@ -60,6 +60,35 @@ void ParabolaPointer::editRenderStatePath(const std::string& state, const QVaria
}
}
QVariantMap ParabolaPointer::toVariantMap() const {
QVariantMap qVariantMap;
QVariantMap qRenderStates;
for (auto iter = _renderStates.cbegin(); iter != _renderStates.cend(); iter++) {
auto renderState = iter->second;
QVariantMap qRenderState;
qRenderState["start"] = renderState->getStartID();
qRenderState["end"] = renderState->getEndID();
qRenderStates[iter->first.c_str()] = qRenderState;
}
qVariantMap["renderStates"] = qRenderStates;
QVariantMap qDefaultRenderStates;
for (auto iter = _defaultRenderStates.cbegin(); iter != _defaultRenderStates.cend(); iter++) {
float distance = iter->second.first;
auto defaultRenderState = iter->second.second;
QVariantMap qDefaultRenderState;
qDefaultRenderState["distance"] = distance;
qDefaultRenderState["start"] = defaultRenderState->getStartID();
qDefaultRenderState["end"] = defaultRenderState->getEndID();
qDefaultRenderStates[iter->first.c_str()] = qDefaultRenderState;
}
qVariantMap["defaultRenderStates"] = qDefaultRenderStates;
return qVariantMap;
}
glm::vec3 ParabolaPointer::getPickOrigin(const PickResultPointer& pickResult) const {
auto parabolaPickResult = std::static_pointer_cast<ParabolaPickResult>(pickResult);
return (parabolaPickResult ? vec3FromVariant(parabolaPickResult->pickVariant["origin"]) : glm::vec3(0.0f));

View file

@ -97,6 +97,8 @@ public:
ParabolaPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers,
bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled);
QVariantMap toVariantMap() const override;
static std::shared_ptr<StartEndRenderState> buildRenderState(const QVariantMap& propMap);
protected:

View file

@ -23,6 +23,13 @@
#include "MouseParabolaPick.h"
#include "CollisionPick.h"
#include "SpatialParentFinder.h"
#include "NestableTransformNode.h"
#include "PickTransformNode.h"
#include "MouseTransformNode.h"
#include "avatar/MyAvatarHeadTransformNode.h"
#include "avatar/AvatarManager.h"
#include <ScriptEngine.h>
unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) {
@ -276,8 +283,10 @@ unsigned int PickScriptingInterface::createCollisionPick(const QVariant& propert
}
CollisionRegion collisionRegion(propMap);
auto collisionPick = std::make_shared<CollisionPick>(filter, maxDistance, enabled, collisionRegion, qApp->getPhysicsEngine());
collisionPick->parentTransform = createTransformNode(propMap);
return DependencyManager::get<PickManager>()->addPick(PickQuery::Collision, std::make_shared<CollisionPick>(filter, maxDistance, enabled, collisionRegion, qApp->getPhysicsEngine()));
return DependencyManager::get<PickManager>()->addPick(PickQuery::Collision, collisionPick);
}
void PickScriptingInterface::enablePick(unsigned int uid) {
@ -351,3 +360,43 @@ unsigned int PickScriptingInterface::getPerFrameTimeBudget() const {
void PickScriptingInterface::setPerFrameTimeBudget(unsigned int numUsecs) {
DependencyManager::get<PickManager>()->setPerFrameTimeBudget(numUsecs);
}
std::shared_ptr<TransformNode> PickScriptingInterface::createTransformNode(const QVariantMap& propMap) {
if (propMap["parentID"].isValid()) {
QUuid parentUuid = propMap["parentID"].toUuid();
if (!parentUuid.isNull()) {
// Infer object type from parentID
// For now, assume a QUuuid is a SpatiallyNestable. This should change when picks are converted over to QUuids.
bool success;
std::weak_ptr<SpatiallyNestable> nestablePointer = DependencyManager::get<SpatialParentFinder>()->find(parentUuid, success, nullptr);
int parentJointIndex = 0;
if (propMap["parentJointIndex"].isValid()) {
parentJointIndex = propMap["parentJointIndex"].toInt();
}
auto sharedNestablePointer = nestablePointer.lock();
if (success && sharedNestablePointer) {
return std::make_shared<NestableTransformNode>(nestablePointer, parentJointIndex);
}
}
unsigned int pickID = propMap["parentID"].toUInt();
if (pickID != 0) {
return std::make_shared<PickTransformNode>(pickID);
}
}
if (propMap["joint"].isValid()) {
QString joint = propMap["joint"].toString();
if (joint == "Mouse") {
return std::make_shared<MouseTransformNode>();
} else if (joint == "Avatar") {
return std::make_shared<MyAvatarHeadTransformNode>();
} else if (!joint.isNull()) {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
int jointIndex = myAvatar->getJointIndex(joint);
return std::make_shared<NestableTransformNode>(myAvatar, jointIndex);
}
}
return std::shared_ptr<TransformNode>();
}

View file

@ -152,9 +152,6 @@ public:
* @property {CollisionRegion} collisionRegion The CollisionRegion that was used. Valid even if there was no intersection.
*/
// TODO: Add this to the CollisionPickResult jsdoc once model collision picks are working
//* @property {boolean} loaded If the CollisionRegion was successfully loaded (may be false if a model was used)
/**jsdoc
* Information about the Collision Pick's intersection with an object
*
@ -320,6 +317,9 @@ public slots:
* @returns {number}
*/
static constexpr unsigned int INTERSECTED_HUD() { return IntersectionType::HUD; }
protected:
static std::shared_ptr<TransformNode> createTransformNode(const QVariantMap& propMap);
};
#endif // hifi_PickScriptingInterface_h

View file

@ -76,16 +76,19 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties)
* @property {number} distance The distance at which to render the end of this Ray Pointer, if one is defined.
*/
/**jsdoc
* A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something.
* A set of properties which define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something.
*
* @typedef {object} Pointers.RayPointerRenderState
* @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState}
* @property {Overlays.OverlayProperties} [start] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
* An overlay to represent the beginning of the Ray Pointer, if desired.
* @property {Overlays.OverlayProperties} [path] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field), which <b>must</b> be <code>"line3d"</code>.
* An overlay to represent the path of the Ray Pointer, if desired.
* @property {Overlays.OverlayProperties} [end] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
* An overlay to represent the end of the Ray Pointer, if desired.
* @property {string} name When using {@link Pointers.createPointer}, the name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState}
* @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the beginning of the Ray Pointer,
* using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise.
* @property {Overlays.OverlayProperties|QUuid} [path] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the path of the Ray Pointer,
* using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field), which <b>must</b> be <code>"line3d"</code>.
* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise.
* @property {Overlays.OverlayProperties|QUuid} [end] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the end of the Ray Pointer,
* using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise.
*/
/**jsdoc
* A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Contains the relevant {@link Picks.PickProperties} to define the underlying Pick.
@ -99,8 +102,12 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties)
* @property {number} [followNormalStrength=0.0] The strength of the interpolation between the real normal and the visual normal if followNormal is true. <code>0-1</code>. If 0 or 1,
* the normal will follow exactly.
* @property {boolean} [enabled=false]
* @property {Pointers.RayPointerRenderState[]} [renderStates] A list of different visual states to switch between.
* @property {Pointers.DefaultRayPointerRenderState[]} [defaultRenderStates] A list of different visual states to use if there is no intersection.
* @property {Pointers.RayPointerRenderState[]|Object.<string, Pointers.RayPointerRenderState>} [renderStates] A collection of different visual states to switch between.
* When using {@link Pointers.createPointer}, a list of RayPointerRenderStates.
* When returned from {@link Pointers.getPointerProperties}, a map between render state names and RayPointRenderStates.
* @property {Pointers.DefaultRayPointerRenderState[]|Object.<string, Pointers.DefaultRayPointerRenderState>} [defaultRenderStates] A collection of different visual states to use if there is no intersection.
* When using {@link Pointers.createPointer}, a list of DefaultRayPointerRenderStates.
* When returned from {@link Pointers.getPointerProperties}, a map between render state names and DefaultRayPointRenderStates.
* @property {boolean} [hover=false] If this Pointer should generate hover events.
* @property {Pointers.Trigger[]} [triggers] A list of different triggers mechanisms that control this Pointer's click event generation.
*/
@ -224,12 +231,15 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
* A set of properties used to define the visual aspect of a Parabola Pointer in the case that the Pointer is intersecting something.
*
* @typedef {object} Pointers.ParabolaPointerRenderState
* @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState}
* @property {Overlays.OverlayProperties} [start] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
* An overlay to represent the beginning of the Parabola Pointer, if desired.
* @property {Pointers.ParabolaProperties} [path] The rendering properties of the parabolic path defined by the Parabola Pointer.
* @property {Overlays.OverlayProperties} [end] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
* An overlay to represent the end of the Parabola Pointer, if desired.
* @property {string} name When using {@link Pointers.createPointer}, the name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState}
* @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the beginning of the Parabola Pointer,
* using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise.
* @property {Pointers.ParabolaProperties} [path] When using {@link Pointers.createPointer}, the optionally defined rendering properties of the parabolic path defined by the Parabola Pointer.
* Not defined in {@link Pointers.getPointerProperties}.
* @property {Overlays.OverlayProperties|QUuid} [end] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the end of the Parabola Pointer,
* using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise.
*/
/**jsdoc
* A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Contains the relevant {@link Picks.PickProperties} to define the underlying Pick.
@ -243,8 +253,12 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
* @property {number} [followNormalStrength=0.0] The strength of the interpolation between the real normal and the visual normal if followNormal is true. <code>0-1</code>. If 0 or 1,
* the normal will follow exactly.
* @property {boolean} [enabled=false]
* @property {Pointers.ParabolaPointerRenderState[]} [renderStates] A list of different visual states to switch between.
* @property {Pointers.DefaultParabolaPointerRenderState[]} [defaultRenderStates] A list of different visual states to use if there is no intersection.
* @property {Pointers.ParabolaPointerRenderState[]|Object.<string, Pointers.ParabolaPointerRenderState>} [renderStates] A collection of different visual states to switch between.
* When using {@link Pointers.createPointer}, a list of ParabolaPointerRenderStates.
* When returned from {@link Pointers.getPointerProperties}, a map between render state names and ParabolaPointerRenderStates.
* @property {Pointers.DefaultParabolaPointerRenderState[]|Object.<string, Pointers.DefaultParabolaPointerRenderState>} [defaultRenderStates] A collection of different visual states to use if there is no intersection.
* When using {@link Pointers.createPointer}, a list of DefaultParabolaPointerRenderStates.
* When returned from {@link Pointers.getPointerProperties}, a map between render state names and DefaultParabolaPointerRenderStates.
* @property {boolean} [hover=false] If this Pointer should generate hover events.
* @property {Pointers.Trigger[]} [triggers] A list of different triggers mechanisms that control this Pointer's click event generation.
*/
@ -375,4 +389,8 @@ QVariantMap PointerScriptingInterface::getPrevPickResult(unsigned int uid) const
result = pickResult->toVariantMap();
}
return result;
}
QVariantMap PointerScriptingInterface::getPointerProperties(unsigned int uid) const {
return DependencyManager::get<PointerManager>()->getPointerProperties(uid);
}

View file

@ -203,6 +203,14 @@ public:
*/
Q_INVOKABLE bool isMouse(unsigned int uid) { return DependencyManager::get<PointerManager>()->isMouse(uid); }
/**jsdoc
* Returns information about an existing Pointer
* @function Pointers.getPointerState
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
* @returns {Pointers.LaserPointerProperties|Pointers.StylusPointerProperties|Pointers.ParabolaPointerProperties} The information about the Pointer.
* Currently only includes renderStates and defaultRenderStates with associated overlay IDs.
*/
Q_INVOKABLE QVariantMap getPointerProperties(unsigned int uid) const;
};
#endif // hifi_PointerScriptingInterface_h

View file

@ -53,6 +53,18 @@ PickResultPointer RayPick::getHUDIntersection(const PickRay& pick) {
return std::make_shared<RayPickResult>(IntersectionType::HUD, QUuid(), glm::distance(pick.origin, hudRes), hudRes, pick);
}
Transform RayPick::getResultTransform() const {
PickResultPointer result = getPrevPickResult();
if (!result) {
return Transform();
}
auto rayResult = std::static_pointer_cast<RayPickResult>(result);
Transform transform;
transform.setTranslation(rayResult->intersection);
return transform;
}
glm::vec3 RayPick::intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat& rotation, const glm::vec3& registration) {
// TODO: take into account registration
glm::vec3 n = rotation * Vectors::FRONT;

View file

@ -77,6 +77,7 @@ public:
PickResultPointer getOverlayIntersection(const PickRay& pick) override;
PickResultPointer getAvatarIntersection(const PickRay& pick) override;
PickResultPointer getHUDIntersection(const PickRay& pick) override;
Transform getResultTransform() const override;
// These are helper functions for projecting and intersecting rays
static glm::vec3 intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction);

View file

@ -225,4 +225,16 @@ PickResultPointer StylusPick::getAvatarIntersection(const StylusTip& pick) {
PickResultPointer StylusPick::getHUDIntersection(const StylusTip& pick) {
return std::make_shared<StylusPickResult>(pick.toVariantMap());
}
Transform StylusPick::getResultTransform() const {
PickResultPointer result = getPrevPickResult();
if (!result) {
return Transform();
}
auto stylusResult = std::static_pointer_cast<StylusPickResult>(result);
Transform transform;
transform.setTranslation(stylusResult->intersection);
return transform;
}

View file

@ -66,6 +66,7 @@ public:
PickResultPointer getOverlayIntersection(const StylusTip& pick) override;
PickResultPointer getAvatarIntersection(const StylusTip& pick) override;
PickResultPointer getHUDIntersection(const StylusTip& pick) override;
Transform getResultTransform() const override;
bool isLeftHand() const override { return _side == Side::Left; }
bool isRightHand() const override { return _side == Side::Right; }

View file

@ -207,6 +207,10 @@ void StylusPointer::setRenderState(const std::string& state) {
}
}
QVariantMap StylusPointer::toVariantMap() const {
return QVariantMap();
}
glm::vec3 StylusPointer::findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction) {
switch (pickedObject.type) {
case ENTITY:

View file

@ -33,6 +33,8 @@ public:
void setRenderState(const std::string& state) override;
void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) override {}
QVariantMap toVariantMap() const override;
static OverlayID buildStylusOverlay(const QVariantMap& properties);
protected:

View file

@ -926,7 +926,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
// compute blend based on velocity
const float JUMP_SPEED = 3.5f;
float alpha = glm::clamp(-_lastVelocity.y / JUMP_SPEED, -1.0f, 1.0f) + 1.0f;
float alpha = glm::clamp(-workingVelocity.y / JUMP_SPEED, -1.0f, 1.0f) + 1.0f;
_animVars.set("inAirAlpha", alpha);
}

View file

@ -240,7 +240,7 @@ static void FIR_1x4_SSE(float* src, float* dst0, float* dst1, float* dst2, float
float* ps = &src[i - HRTF_TAPS + 1]; // process forwards
assert(HRTF_TAPS % 4 == 0);
static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4");
for (int k = 0; k < HRTF_TAPS; k += 4) {
@ -276,23 +276,8 @@ static void FIR_1x4_SSE(float* src, float* dst0, float* dst1, float* dst2, float
}
}
//
// Runtime CPU dispatch
//
#include "CPUDetect.h"
void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames);
void FIR_1x4_AVX512(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames);
static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) {
static auto f = cpuSupportsAVX512() ? FIR_1x4_AVX512 : (cpuSupportsAVX2() ? FIR_1x4_AVX2 : FIR_1x4_SSE);
(*f)(src, dst0, dst1, dst2, dst3, coef, numFrames); // dispatch
}
// 4 channel planar to interleaved
static void interleave_4x4(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames) {
static void interleave_4x4_SSE(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames) {
assert(numFrames % 4 == 0);
@ -323,7 +308,7 @@ static void interleave_4x4(float* src0, float* src1, float* src2, float* src3, f
// process 2 cascaded biquads on 4 channels (interleaved)
// biquads computed in parallel, by adding one sample of delay
static void biquad2_4x4(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) {
static void biquad2_4x4_SSE(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) {
// enable flush-to-zero mode to prevent denormals
unsigned int ftz = _MM_GET_FLUSH_ZERO_MODE();
@ -388,7 +373,7 @@ static void biquad2_4x4(float* src, float* dst, float coef[5][8], float state[3]
}
// crossfade 4 inputs into 2 outputs with accumulation (interleaved)
static void crossfade_4x2(float* src, float* dst, const float* win, int numFrames) {
static void crossfade_4x2_SSE(float* src, float* dst, const float* win, int numFrames) {
assert(numFrames % 4 == 0);
@ -435,12 +420,12 @@ static void crossfade_4x2(float* src, float* dst, const float* win, int numFrame
}
// linear interpolation with gain
static void interpolate(float* dst, const float* src0, const float* src1, float frac, float gain) {
static void interpolate_SSE(const float* src0, const float* src1, float* dst, float frac, float gain) {
__m128 f0 = _mm_set1_ps(gain * (1.0f - frac));
__m128 f1 = _mm_set1_ps(gain * frac);
assert(HRTF_TAPS % 4 == 0);
static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4");
for (int k = 0; k < HRTF_TAPS; k += 4) {
@ -453,6 +438,44 @@ static void interpolate(float* dst, const float* src0, const float* src1, float
}
}
//
// Runtime CPU dispatch
//
#include "CPUDetect.h"
void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames);
void FIR_1x4_AVX512(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames);
void interleave_4x4_AVX2(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames);
void biquad2_4x4_AVX2(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames);
void crossfade_4x2_AVX2(float* src, float* dst, const float* win, int numFrames);
void interpolate_AVX2(const float* src0, const float* src1, float* dst, float frac, float gain);
static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) {
static auto f = cpuSupportsAVX512() ? FIR_1x4_AVX512 : (cpuSupportsAVX2() ? FIR_1x4_AVX2 : FIR_1x4_SSE);
(*f)(src, dst0, dst1, dst2, dst3, coef, numFrames); // dispatch
}
static void interleave_4x4(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames) {
static auto f = cpuSupportsAVX2() ? interleave_4x4_AVX2 : interleave_4x4_SSE;
(*f)(src0, src1, src2, src3, dst, numFrames); // dispatch
}
static void biquad2_4x4(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) {
static auto f = cpuSupportsAVX2() ? biquad2_4x4_AVX2 : biquad2_4x4_SSE;
(*f)(src, dst, coef, state, numFrames); // dispatch
}
static void crossfade_4x2(float* src, float* dst, const float* win, int numFrames) {
static auto f = cpuSupportsAVX2() ? crossfade_4x2_AVX2 : crossfade_4x2_SSE;
(*f)(src, dst, win, numFrames); // dispatch
}
static void interpolate(const float* src0, const float* src1, float* dst, float frac, float gain) {
static auto f = cpuSupportsAVX2() ? interpolate_AVX2 : interpolate_SSE;
(*f)(src0, src1, dst, frac, gain); // dispatch
}
#else // portable reference code
// 1 channel input, 4 channel output
@ -489,7 +512,7 @@ static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* ds
float* ps = &src[i - HRTF_TAPS + 1]; // process forwards
assert(HRTF_TAPS % 4 == 0);
static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4");
for (int k = 0; k < HRTF_TAPS; k += 4) {
@ -715,7 +738,7 @@ static void crossfade_4x2(float* src, float* dst, const float* win, int numFrame
}
// linear interpolation with gain
static void interpolate(float* dst, const float* src0, const float* src1, float frac, float gain) {
static void interpolate(const float* src0, const float* src1, float* dst, float frac, float gain) {
float f0 = gain * (1.0f - frac);
float f1 = gain * frac;
@ -967,8 +990,8 @@ static void setFilters(float firCoef[4][HRTF_TAPS], float bqCoef[5][8], int dela
azimuthToIndex(azimuth, az0, az1, frac);
// interpolate FIR
interpolate(firCoef[channel+0], ir_table_table[index][azL0][0], ir_table_table[index][azL1][0], fracL, gain * gainL);
interpolate(firCoef[channel+1], ir_table_table[index][azR0][1], ir_table_table[index][azR1][1], fracR, gain * gainR);
interpolate(ir_table_table[index][azL0][0], ir_table_table[index][azL1][0], firCoef[channel+0], fracL, gain * gainL);
interpolate(ir_table_table[index][azR0][1], ir_table_table[index][azR1][1], firCoef[channel+1], fracR, gain * gainR);
// interpolate ITD
float itd = (1.0f - frac) * itd_table_table[index][az0] + frac * itd_table_table[index][az1];

View file

@ -44,7 +44,7 @@ void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3
float* ps = &src[i - HRTF_TAPS + 1]; // process forwards
assert(HRTF_TAPS % 4 == 0);
static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4");
for (int k = 0; k < HRTF_TAPS; k += 4) {
@ -87,4 +87,165 @@ void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3
_mm256_zeroupper();
}
// 4 channel planar to interleaved
void interleave_4x4_AVX2(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames) {
assert(numFrames % 8 == 0);
for (int i = 0; i < numFrames; i += 8) {
__m256 x0 = _mm256_loadu_ps(&src0[i]);
__m256 x1 = _mm256_loadu_ps(&src1[i]);
__m256 x2 = _mm256_loadu_ps(&src2[i]);
__m256 x3 = _mm256_loadu_ps(&src3[i]);
// interleave (4x4 matrix transpose)
__m256 t0 = _mm256_unpacklo_ps(x0, x1);
__m256 t1 = _mm256_unpackhi_ps(x0, x1);
__m256 t2 = _mm256_unpacklo_ps(x2, x3);
__m256 t3 = _mm256_unpackhi_ps(x2, x3);
x0 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(1,0,1,0));
x1 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(3,2,3,2));
x2 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(1,0,1,0));
x3 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(3,2,3,2));
t0 = _mm256_permute2f128_ps(x0, x1, 0x20);
t1 = _mm256_permute2f128_ps(x2, x3, 0x20);
t2 = _mm256_permute2f128_ps(x0, x1, 0x31);
t3 = _mm256_permute2f128_ps(x2, x3, 0x31);
_mm256_storeu_ps(&dst[4*i+0], t0);
_mm256_storeu_ps(&dst[4*i+8], t1);
_mm256_storeu_ps(&dst[4*i+16], t2);
_mm256_storeu_ps(&dst[4*i+24], t3);
}
_mm256_zeroupper();
}
// process 2 cascaded biquads on 4 channels (interleaved)
// biquads are computed in parallel, by adding one sample of delay
void biquad2_4x4_AVX2(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) {
// enable flush-to-zero mode to prevent denormals
unsigned int ftz = _MM_GET_FLUSH_ZERO_MODE();
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
// restore state
__m256 x0 = _mm256_setzero_ps();
__m256 y0 = _mm256_loadu_ps(state[0]);
__m256 w1 = _mm256_loadu_ps(state[1]);
__m256 w2 = _mm256_loadu_ps(state[2]);
// biquad coefs
__m256 b0 = _mm256_loadu_ps(coef[0]);
__m256 b1 = _mm256_loadu_ps(coef[1]);
__m256 b2 = _mm256_loadu_ps(coef[2]);
__m256 a1 = _mm256_loadu_ps(coef[3]);
__m256 a2 = _mm256_loadu_ps(coef[4]);
for (int i = 0; i < numFrames; i++) {
// x0 = (first biquad output << 128) | input
x0 = _mm256_insertf128_ps(_mm256_permute2f128_ps(y0, y0, 0x01), _mm_loadu_ps(&src[4*i]), 0);
// transposed Direct Form II
y0 = _mm256_fmadd_ps(x0, b0, w1);
w1 = _mm256_fmadd_ps(x0, b1, w2);
w2 = _mm256_mul_ps(x0, b2);
w1 = _mm256_fnmadd_ps(y0, a1, w1);
w2 = _mm256_fnmadd_ps(y0, a2, w2);
_mm_storeu_ps(&dst[4*i], _mm256_extractf128_ps(y0, 1)); // second biquad output
}
// save state
_mm256_storeu_ps(state[0], y0);
_mm256_storeu_ps(state[1], w1);
_mm256_storeu_ps(state[2], w2);
_MM_SET_FLUSH_ZERO_MODE(ftz);
_mm256_zeroupper();
}
// crossfade 4 inputs into 2 outputs with accumulation (interleaved)
void crossfade_4x2_AVX2(float* src, float* dst, const float* win, int numFrames) {
assert(numFrames % 8 == 0);
for (int i = 0; i < numFrames; i += 8) {
__m256 f0 = _mm256_loadu_ps(&win[i]);
__m256 x0 = _mm256_castps128_ps256(_mm_loadu_ps(&src[4*i+0]));
__m256 x1 = _mm256_castps128_ps256(_mm_loadu_ps(&src[4*i+4]));
__m256 x2 = _mm256_castps128_ps256(_mm_loadu_ps(&src[4*i+8]));
__m256 x3 = _mm256_castps128_ps256(_mm_loadu_ps(&src[4*i+12]));
x0 = _mm256_insertf128_ps(x0, _mm_loadu_ps(&src[4*i+16]), 1);
x1 = _mm256_insertf128_ps(x1, _mm_loadu_ps(&src[4*i+20]), 1);
x2 = _mm256_insertf128_ps(x2, _mm_loadu_ps(&src[4*i+24]), 1);
x3 = _mm256_insertf128_ps(x3, _mm_loadu_ps(&src[4*i+28]), 1);
__m256 y0 = _mm256_loadu_ps(&dst[2*i+0]);
__m256 y1 = _mm256_loadu_ps(&dst[2*i+8]);
// deinterleave (4x4 matrix transpose)
__m256 t0 = _mm256_unpacklo_ps(x0, x1);
__m256 t1 = _mm256_unpackhi_ps(x0, x1);
__m256 t2 = _mm256_unpacklo_ps(x2, x3);
__m256 t3 = _mm256_unpackhi_ps(x2, x3);
x0 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(1,0,1,0));
x1 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(3,2,3,2));
x2 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(1,0,1,0));
x3 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(3,2,3,2));
// crossfade
x0 = _mm256_sub_ps(x0, x2);
x1 = _mm256_sub_ps(x1, x3);
x2 = _mm256_fmadd_ps(f0, x0, x2);
x3 = _mm256_fmadd_ps(f0, x1, x3);
// interleave
t0 = _mm256_unpacklo_ps(x2, x3);
t1 = _mm256_unpackhi_ps(x2, x3);
x0 = _mm256_permute2f128_ps(t0, t1, 0x20);
x1 = _mm256_permute2f128_ps(t0, t1, 0x31);
// accumulate
y0 = _mm256_add_ps(y0, x0);
y1 = _mm256_add_ps(y1, x1);
_mm256_storeu_ps(&dst[2*i+0], y0);
_mm256_storeu_ps(&dst[2*i+8], y1);
}
_mm256_zeroupper();
}
// linear interpolation with gain
void interpolate_AVX2(const float* src0, const float* src1, float* dst, float frac, float gain) {
__m256 f0 = _mm256_set1_ps(gain * (1.0f - frac));
__m256 f1 = _mm256_set1_ps(gain * frac);
static_assert(HRTF_TAPS % 8 == 0, "HRTF_TAPS must be a multiple of 8");
for (int k = 0; k < HRTF_TAPS; k += 8) {
__m256 x0 = _mm256_loadu_ps(&src0[k]);
__m256 x1 = _mm256_loadu_ps(&src1[k]);
x0 = _mm256_mul_ps(f0, x0);
x0 = _mm256_fmadd_ps(f1, x1, x0);
_mm256_storeu_ps(&dst[k], x0);
}
_mm256_zeroupper();
}
#endif

View file

@ -44,7 +44,7 @@ void FIR_1x4_AVX512(float* src, float* dst0, float* dst1, float* dst2, float* ds
float* ps = &src[i - HRTF_TAPS + 1]; // process forwards
assert(HRTF_TAPS % 4 == 0);
static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4");
for (int k = 0; k < HRTF_TAPS; k += 4) {

View file

@ -139,7 +139,6 @@ Avatar::~Avatar() {
}
});
}
auto geometryCache = DependencyManager::get<GeometryCache>();
if (geometryCache) {
geometryCache->releaseID(_nameRectGeometryID);

View file

@ -1861,7 +1861,9 @@ qint64 AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice
}
qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID traitInstanceID,
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) {
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion,
AvatarTraits::TraitInstanceID wireInstanceID) {
qint64 bytesWritten = 0;
bytesWritten += destination.writePrimitive(traitType);
@ -1870,7 +1872,11 @@ qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTr
bytesWritten += destination.writePrimitive(traitVersion);
}
bytesWritten += destination.write(traitInstanceID.toRfc4122());
if (!wireInstanceID.isNull()) {
bytesWritten += destination.write(wireInstanceID.toRfc4122());
} else {
bytesWritten += destination.write(traitInstanceID.toRfc4122());
}
if (traitType == AvatarTraits::AvatarEntity) {
// grab a read lock on the avatar entities and check for entity data for the given ID
@ -1895,6 +1901,16 @@ qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTr
return bytesWritten;
}
void AvatarData::prepareResetTraitInstances() {
if (_clientTraitsHandler) {
_avatarEntitiesLock.withReadLock([this]{
foreach (auto entityID, _avatarEntityData.keys()) {
_clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID);
}
});
}
}
void AvatarData::processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData) {
if (traitType == AvatarTraits::SkeletonModelURL) {
// get the URL from the binary data
@ -2792,7 +2808,7 @@ void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) {
if (_clientTraitsHandler) {
// if we have a client traits handler, flag any updated or created entities
// so that we send changes for them next frame
foreach (auto entityID, _avatarEntityData) {
foreach (auto entityID, _avatarEntityData.keys()) {
_clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID);
}
}

View file

@ -962,7 +962,10 @@ public:
qint64 packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination,
AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION);
qint64 packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID,
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION);
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION,
AvatarTraits::TraitInstanceID wireInstanceID = AvatarTraits::TraitInstanceID());
void prepareResetTraitInstances();
void processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData);
void processTraitInstance(AvatarTraits::TraitType traitType,
@ -1190,6 +1193,9 @@ public:
void setReplicaIndex(int replicaIndex) { _replicaIndex = replicaIndex; }
int getReplicaIndex() { return _replicaIndex; }
const AvatarTraits::TraitInstanceID getTraitInstanceXORID() const { return _traitInstanceXORID; }
void cycleTraitInstanceXORID() { _traitInstanceXORID = QUuid::createUuid(); }
signals:
/**jsdoc
@ -1496,6 +1502,8 @@ private:
// privatize the copy constructor and assignment operator so they cannot be called
AvatarData(const AvatarData&);
AvatarData& operator= (const AvatarData&);
AvatarTraits::TraitInstanceID _traitInstanceXORID { QUuid::createUuid() };
};
Q_DECLARE_METATYPE(AvatarData*)

View file

@ -85,7 +85,8 @@ void AvatarReplicas::processDeletedTraitInstance(const QUuid& parentID, AvatarTr
if (_replicasMap.find(parentID) != _replicasMap.end()) {
auto &replicas = _replicasMap[parentID];
for (auto avatar : replicas) {
avatar->processDeletedTraitInstance(traitType, instanceID);
avatar->processDeletedTraitInstance(traitType,
AvatarTraits::xoredInstanceID(instanceID, avatar->getTraitInstanceXORID()));
}
}
}
@ -94,7 +95,9 @@ void AvatarReplicas::processTraitInstance(const QUuid& parentID, AvatarTraits::T
if (_replicasMap.find(parentID) != _replicasMap.end()) {
auto &replicas = _replicasMap[parentID];
for (auto avatar : replicas) {
avatar->processTraitInstance(traitType, instanceID, traitBinaryData);
avatar->processTraitInstance(traitType,
AvatarTraits::xoredInstanceID(instanceID, avatar->getTraitInstanceXORID()),
traitBinaryData);
}
}
}
@ -335,16 +338,28 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> mess
AvatarTraits::TraitInstanceID traitInstanceID =
QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
// XOR the incoming trait instance ID with this avatar object's personal XOR ID
// this ensures that we have separate entity instances in the local tree
// if we briefly end up with two Avatar objects for this node
// (which can occur if the shared pointer for the
// previous instance of an avatar hasn't yet gone out of scope before the
// new instance is created)
auto xoredInstanceID = AvatarTraits::xoredInstanceID(traitInstanceID, avatar->getTraitInstanceXORID());
message->readPrimitive(&traitBinarySize);
auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID);
if (packetTraitVersion > processedInstanceVersion) {
// in order to handle re-connections to the avatar mixer when the other
if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) {
avatar->processDeletedTraitInstance(traitType, traitInstanceID);
avatar->processDeletedTraitInstance(traitType, xoredInstanceID);
_replicas.processDeletedTraitInstance(avatarID, traitType, traitInstanceID);
} else {
auto traitData = message->read(traitBinarySize);
avatar->processTraitInstance(traitType, traitInstanceID, traitData);
avatar->processTraitInstance(traitType, xoredInstanceID, traitData);
_replicas.processTraitInstance(avatarID, traitType, traitInstanceID, traitData);
}
processedInstanceVersion = packetTraitVersion;

View file

@ -41,7 +41,8 @@ namespace AvatarTraits {
const TraitWireSize DELETED_TRAIT_SIZE = -1;
inline qint64 packInstancedTraitDelete(TraitType traitType, TraitInstanceID instanceID, ExtendedIODevice& destination,
TraitVersion traitVersion = NULL_TRAIT_VERSION) {
TraitVersion traitVersion = NULL_TRAIT_VERSION,
TraitInstanceID xoredInstanceID = TraitInstanceID()) {
qint64 bytesWritten = 0;
bytesWritten += destination.writePrimitive(traitType);
@ -50,12 +51,28 @@ namespace AvatarTraits {
bytesWritten += destination.writePrimitive(traitVersion);
}
bytesWritten += destination.write(instanceID.toRfc4122());
if (xoredInstanceID.isNull()) {
bytesWritten += destination.write(instanceID.toRfc4122());
} else {
bytesWritten += destination.write(xoredInstanceID.toRfc4122());
}
bytesWritten += destination.writePrimitive(DELETED_TRAIT_SIZE);
return bytesWritten;
}
inline TraitInstanceID xoredInstanceID(TraitInstanceID localInstanceID, TraitInstanceID xorKeyID) {
QByteArray xoredInstanceID { NUM_BYTES_RFC4122_UUID, 0 };
auto xorKeyIDBytes = xorKeyID.toRfc4122();
auto localInstanceIDBytes = localInstanceID.toRfc4122();
for (auto i = 0; i < localInstanceIDBytes.size(); ++i) {
xoredInstanceID[i] = localInstanceIDBytes[i] ^ xorKeyIDBytes[i];
}
return QUuid::fromRfc4122(xoredInstanceID);
}
};
#endif // hifi_AvatarTraits_h

View file

@ -37,6 +37,15 @@ void ClientTraitsHandler::resetForNewMixer() {
// mark that all traits should be sent next time
_shouldPerformInitialSend = true;
// reset the trait statuses
_traitStatuses.reset();
// pre-fill the instanced statuses that we will need to send next frame
_owningAvatar->prepareResetTraitInstances();
// reset the trait XOR ID since we're resetting for a new avatar mixer
_owningAvatar->cycleTraitInstanceXORID();
}
void ClientTraitsHandler::sendChangedTraitsToMixer() {
@ -87,11 +96,19 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() {
|| instanceIDValuePair.value == Updated) {
// this is a changed trait we need to send or we haven't send out trait information yet
// ask the owning avatar to pack it
_owningAvatar->packTraitInstance(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList);
// since this is going to the mixer, use the XORed instance ID (to anonymize trait instance IDs
// that would typically persist across sessions)
_owningAvatar->packTraitInstance(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList,
AvatarTraits::NULL_TRAIT_VERSION,
AvatarTraits::xoredInstanceID(instanceIDValuePair.id,
_owningAvatar->getTraitInstanceXORID()));
} else if (!_shouldPerformInitialSend && instanceIDValuePair.value == Deleted) {
// pack delete for this trait instance
AvatarTraits::packInstancedTraitDelete(instancedIt->traitType, instanceIDValuePair.id,
*traitsPacketList);
*traitsPacketList, AvatarTraits::NULL_TRAIT_VERSION,
AvatarTraits::xoredInstanceID(instanceIDValuePair.id,
_owningAvatar->getTraitInstanceXORID()));
}
}

View file

@ -30,9 +30,9 @@ public:
void markTraitUpdated(AvatarTraits::TraitType updatedTrait)
{ _traitStatuses[updatedTrait] = Updated; _hasChangedTraits = true; }
void markInstancedTraitUpdated(AvatarTraits::TraitType traitType, QUuid updatedInstanceID)
void markInstancedTraitUpdated(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID updatedInstanceID)
{ _traitStatuses.instanceInsert(traitType, updatedInstanceID, Updated); _hasChangedTraits = true; }
void markInstancedTraitDeleted(AvatarTraits::TraitType traitType, QUuid deleteInstanceID)
void markInstancedTraitDeleted(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID deleteInstanceID)
{ _traitStatuses.instanceInsert(traitType, deleteInstanceID, Deleted); _hasChangedTraits = true; }
void resetForNewMixer();

View file

@ -19,16 +19,16 @@ struct InputCalibrationData {
glm::mat4 sensorToWorldMat; // sensor to world
glm::mat4 avatarMat; // avatar to world
glm::mat4 hmdSensorMat; // hmd pos and orientation in sensor space
glm::mat4 defaultCenterEyeMat; // default pose for the center of the eyes in avatar space.
glm::mat4 defaultHeadMat; // default pose for head joint in avatar space
glm::mat4 defaultSpine2; // default pose for spine2 joint in avatar space
glm::mat4 defaultHips; // default pose for hips joint in avatar space
glm::mat4 defaultLeftFoot; // default pose for leftFoot joint in avatar space
glm::mat4 defaultRightFoot; // default pose for rightFoot joint in avatar space
glm::mat4 defaultRightArm; // default pose for rightArm joint in avatar space
glm::mat4 defaultLeftArm; // default pose for leftArm joint in avatar space
glm::mat4 defaultRightHand; // default pose for rightHand joint in avatar space
glm::mat4 defaultLeftHand; // default pose for leftHand joint in avatar space
glm::mat4 defaultCenterEyeMat; // default pose for the center of the eyes in sensor space.
glm::mat4 defaultHeadMat; // default pose for head joint in sensor space
glm::mat4 defaultSpine2; // default pose for spine2 joint in sensor space
glm::mat4 defaultHips; // default pose for hips joint in sensor space
glm::mat4 defaultLeftFoot; // default pose for leftFoot joint in sensor space
glm::mat4 defaultRightFoot; // default pose for rightFoot joint in sensor space
glm::mat4 defaultRightArm; // default pose for rightArm joint in sensor space
glm::mat4 defaultLeftArm; // default pose for leftArm joint in sensor space
glm::mat4 defaultRightHand; // default pose for rightHand joint in sensor space
glm::mat4 defaultLeftHand; // default pose for leftHand joint in sensor space
};
enum class ChannelType {

View file

@ -527,8 +527,8 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) {
}
// If the source hasn't been written yet, defer processing of this route
auto source = route->source;
auto sourceInput = source->getInput();
auto& source = route->source;
auto& sourceInput = source->getInput();
if (sourceInput.device == STANDARD_DEVICE && !force && source->writeable()) {
if (debugRoutes && route->debug) {
qCDebug(controllers) << "Source not yet written, deferring";
@ -559,7 +559,7 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) {
return true;
}
auto destination = route->destination;
auto& destination = route->destination;
// THis could happen if the route destination failed to create
// FIXME: Maybe do not create the route if the destination failed and avoid this case ?
if (!destination) {

View file

@ -382,6 +382,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
const auto& views = _viewState->getConicalViews();
PrioritySortUtil::PriorityQueue<SortableRenderer> sortedRenderables(views);
sortedRenderables.reserve(_renderablesToUpdate.size());
{
PROFILE_RANGE_EX(simulation_physics, "SortRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size());
std::unordered_map<EntityItemID, EntityRendererPointer>::iterator itr = _renderablesToUpdate.begin();
@ -405,11 +406,14 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
// process the sorted renderables
size_t numSorted = sortedRenderables.size();
while (!sortedRenderables.empty() && usecTimestampNow() < expiry) {
const auto renderable = sortedRenderables.top().getRenderer();
const auto& sortedRenderablesVector = sortedRenderables.getSortedVector();
for (const auto& sortedRenderable : sortedRenderablesVector) {
if (usecTimestampNow() > expiry) {
break;
}
const auto& renderable = sortedRenderable.getRenderer();
renderable->updateInScene(scene, transaction);
_renderablesToUpdate.erase(renderable->getEntity()->getID());
sortedRenderables.pop();
}
// compute average per-renderable update cost

View file

@ -263,15 +263,19 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
default:
materials[matName] = currentMaterial;
#ifdef WANT_DEBUG
qCDebug(modelformat) << "OBJ Reader Last material illumination model:" << currentMaterial.illuminationModel <<
" shininess:" << currentMaterial.shininess << " opacity:" << currentMaterial.opacity <<
" diffuse color:" << currentMaterial.diffuseColor << " specular color:" <<
currentMaterial.specularColor << " emissive color:" << currentMaterial.emissiveColor <<
" diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" <<
currentMaterial.specularTextureFilename << " emissive texture:" <<
currentMaterial.emissiveTextureFilename << " bump texture:" <<
currentMaterial.bumpTextureFilename;
#endif
qCDebug(modelformat) <<
"OBJ Reader Last material illumination model:" << currentMaterial.illuminationModel <<
" shininess:" << currentMaterial.shininess <<
" opacity:" << currentMaterial.opacity <<
" diffuse color:" << currentMaterial.diffuseColor <<
" specular color:" << currentMaterial.specularColor <<
" emissive color:" << currentMaterial.emissiveColor <<
" diffuse texture:" << currentMaterial.diffuseTextureFilename <<
" specular texture:" << currentMaterial.specularTextureFilename <<
" emissive texture:" << currentMaterial.emissiveTextureFilename <<
" bump texture:" << currentMaterial.bumpTextureFilename <<
" opacity texture:" << currentMaterial.opacityTextureFilename;
#endif
return;
}
QByteArray token = tokenizer.getDatum();
@ -289,6 +293,8 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
currentMaterial.emissiveTextureFilename = "";
currentMaterial.specularTextureFilename = "";
currentMaterial.bumpTextureFilename = "";
currentMaterial.opacityTextureFilename = "";
} else if (token == "Ns") {
currentMaterial.shininess = tokenizer.getFloat();
} else if (token == "Ni") {
@ -321,7 +327,7 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
currentMaterial.emissiveColor = tokenizer.getVec3();
} else if (token == "Ks") {
currentMaterial.specularColor = tokenizer.getVec3();
} else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks") || (token == "map_bump") || (token == "bump")) {
} else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks") || (token == "map_bump") || (token == "bump") || (token == "map_d")) {
const QByteArray textureLine = tokenizer.getLineAsDatum();
QByteArray filename;
OBJMaterialTextureOptions textureOptions;
@ -341,6 +347,8 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
} else if ((token == "map_bump") || (token == "bump")) {
currentMaterial.bumpTextureFilename = filename;
currentMaterial.bumpTextureOptions = textureOptions;
} else if (token == "map_d") {
currentMaterial.opacityTextureFilename = filename;
}
}
}
@ -900,6 +908,9 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m
fbxMaterial.normalTexture.isBumpmap = true;
fbxMaterial.bumpMultiplier = objMaterial.bumpTextureOptions.bumpMultiplier;
}
if (!objMaterial.opacityTextureFilename.isEmpty()) {
fbxMaterial.opacityTexture.filename = objMaterial.opacityTextureFilename;
}
modelMaterial->setEmissive(fbxMaterial.emissiveColor);
modelMaterial->setAlbedo(fbxMaterial.diffuseColor);

View file

@ -66,6 +66,8 @@ public:
QByteArray specularTextureFilename;
QByteArray emissiveTextureFilename;
QByteArray bumpTextureFilename;
QByteArray opacityTextureFilename;
OBJMaterialTextureOptions bumpTextureOptions;
int illuminationModel;
bool used { false };

View file

@ -13,6 +13,7 @@
#include <glm/detail/type_vec.hpp>
#include "GpuHelpers.h"
#include "GLMHelpers.h"
namespace graphics {
class Mesh;
@ -55,18 +56,16 @@ namespace buffer_helpers {
tangent = glm::clamp(tangent, -1.0f, 1.0f);
normal *= 511.0f;
tangent *= 511.0f;
normal = glm::round(normal);
tangent = glm::round(tangent);
glm::detail::i10i10i10i2 normalStruct;
glm::detail::i10i10i10i2 tangentStruct;
normalStruct.data.x = int(normal.x);
normalStruct.data.y = int(normal.y);
normalStruct.data.z = int(normal.z);
normalStruct.data.x = fastLrintf(normal.x);
normalStruct.data.y = fastLrintf(normal.y);
normalStruct.data.z = fastLrintf(normal.z);
normalStruct.data.w = 0;
tangentStruct.data.x = int(tangent.x);
tangentStruct.data.y = int(tangent.y);
tangentStruct.data.z = int(tangent.z);
tangentStruct.data.x = fastLrintf(tangent.x);
tangentStruct.data.y = fastLrintf(tangent.y);
tangentStruct.data.z = fastLrintf(tangent.z);
tangentStruct.data.w = 0;
packedNormal = normalStruct.pack;
packedTangent = tangentStruct.pack;

View file

@ -778,8 +778,10 @@ void EntityMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& ma
bool EntityMotionState::shouldSendBid() const {
// NOTE: this method is only ever called when the entity's simulation is NOT locally owned
return _body->isActive() && (_region == workload::Region::R1) &&
glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) >= _entity->getSimulationPriority();
return _body->isActive()
&& (_region == workload::Region::R1)
&& glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) >= _entity->getSimulationPriority()
&& !_entity->getLocked();
}
uint8_t EntityMotionState::computeFinalBidPriority() const {

View file

@ -898,11 +898,12 @@ void PhysicsEngine::setShowBulletConstraintLimits(bool value) {
}
struct AllContactsCallback : public btCollisionWorld::ContactResultCallback {
AllContactsCallback(int32_t mask, int32_t group, const ShapeInfo& shapeInfo, const Transform& transform, btCollisionObject* myAvatarCollisionObject) :
AllContactsCallback(int32_t mask, int32_t group, const ShapeInfo& shapeInfo, const Transform& transform, btCollisionObject* myAvatarCollisionObject, float threshold) :
btCollisionWorld::ContactResultCallback(),
collisionObject(),
contacts(),
myAvatarCollisionObject(myAvatarCollisionObject) {
myAvatarCollisionObject(myAvatarCollisionObject),
threshold(threshold) {
const btCollisionShape* collisionShape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
collisionObject.setCollisionShape(const_cast<btCollisionShape*>(collisionShape));
@ -924,8 +925,13 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback {
btCollisionObject collisionObject;
std::vector<ContactTestResult> contacts;
btCollisionObject* myAvatarCollisionObject;
btScalar threshold;
btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0, int partId0, int index0, const btCollisionObjectWrapper* colObj1, int partId1, int index1) override {
if (cp.m_distance1 > -threshold) {
return 0;
}
const btCollisionObject* otherBody;
btVector3 penetrationPoint;
btVector3 otherPenetrationPoint;
@ -968,14 +974,14 @@ protected:
}
};
std::vector<ContactTestResult> PhysicsEngine::contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group) const {
std::vector<ContactTestResult> PhysicsEngine::contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group, float threshold) const {
// TODO: Give MyAvatar a motion state so we don't have to do this
btCollisionObject* myAvatarCollisionObject = nullptr;
if ((mask & USER_COLLISION_GROUP_MY_AVATAR) && _myAvatarController) {
myAvatarCollisionObject = _myAvatarController->getCollisionObject();
}
auto contactCallback = AllContactsCallback((int32_t)mask, (int32_t)group, regionShapeInfo, regionTransform, myAvatarCollisionObject);
auto contactCallback = AllContactsCallback((int32_t)mask, (int32_t)group, regionShapeInfo, regionTransform, myAvatarCollisionObject, threshold);
_dynamicsWorld->contactTest(&contactCallback.collisionObject, contactCallback);
return contactCallback.contacts;

View file

@ -142,7 +142,7 @@ public:
// Function for getting colliding objects in the world of specified type
// See PhysicsCollisionGroups.h for mask flags.
std::vector<ContactTestResult> contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC) const;
std::vector<ContactTestResult> contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC, float threshold = 0.0f) const;
private:
QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body);

View file

@ -17,6 +17,7 @@
#include <QVariant>
#include <shared/ReadWriteLockable.h>
#include <TransformNode.h>
enum IntersectionType {
NONE = 0,
@ -213,6 +214,10 @@ public:
virtual bool isRightHand() const { return false; }
virtual bool isMouse() const { return false; }
virtual Transform getResultTransform() const = 0;
std::shared_ptr<TransformNode> parentTransform;
private:
PickFilter _filter;
const float _maxDistance;

View file

@ -57,8 +57,8 @@ bool PickCacheOptimizer<T>::checkAndCompareCachedResults(T& pick, PickCache& cac
}
template<typename T>
void PickCacheOptimizer<T>::cacheResult(const bool intersects, const PickResultPointer& resTemp, const PickCacheKey& key, PickResultPointer& res, T& mathPick, PickCache& cache, const std::shared_ptr<Pick<T>> pick) {
if (intersects) {
void PickCacheOptimizer<T>::cacheResult(const bool needToCompareResults, const PickResultPointer& resTemp, const PickCacheKey& key, PickResultPointer& res, T& mathPick, PickCache& cache, const std::shared_ptr<Pick<T>> pick) {
if (needToCompareResults) {
cache[mathPick][key] = resTemp;
res = res->compareAndProcessNewResult(resTemp);
} else {

View file

@ -90,6 +90,14 @@ void PickManager::setIncludeItems(unsigned int uid, const QVector<QUuid>& includ
}
}
Transform PickManager::getResultTransform(unsigned int uid) const {
auto pick = findPick(uid);
if (pick) {
return pick->getResultTransform();
}
return Transform();
}
void PickManager::update() {
uint64_t expiry = usecTimestampNow() + _perFrameTimeBudget;
std::unordered_map<PickQuery::PickType, std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>> cachedPicks;

View file

@ -43,6 +43,8 @@ public:
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignore) const;
void setIncludeItems(unsigned int uid, const QVector<QUuid>& include) const;
Transform getResultTransform(unsigned int uid) const;
bool isLeftHand(unsigned int uid);
bool isRightHand(unsigned int uid);
bool isMouse(unsigned int uid);

View file

@ -0,0 +1,26 @@
//
// Created by Sabrina Shanman 8/22/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "PickTransformNode.h"
#include "DependencyManager.h"
#include "PickManager.h"
PickTransformNode::PickTransformNode(unsigned int uid) :
_uid(uid)
{
}
Transform PickTransformNode::getTransform() {
auto pickManager = DependencyManager::get<PickManager>();
if (!pickManager) {
return Transform();
}
return pickManager->getResultTransform(_uid);
}

View file

@ -0,0 +1,23 @@
//
// Created by Sabrina Shanman 8/22/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_PickTransformNode_h
#define hifi_PickTransformNode_h
#include "TransformNode.h"
// TODO: Remove this class when Picks are converted to SpatiallyNestables
class PickTransformNode : public TransformNode {
public:
PickTransformNode(unsigned int uid);
Transform getTransform() override;
protected:
unsigned int _uid;
};
#endif // hifi_PickTransformNode_h

View file

@ -50,6 +50,8 @@ public:
virtual void setRenderState(const std::string& state) = 0;
virtual void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) = 0;
virtual QVariantMap toVariantMap() const = 0;
virtual void setPrecisionPicking(bool precisionPicking);
virtual void setIgnoreItems(const QVector<QUuid>& ignoreItems) const;
virtual void setIncludeItems(const QVector<QUuid>& includeItems) const;

View file

@ -77,6 +77,15 @@ PickResultPointer PointerManager::getPrevPickResult(unsigned int uid) const {
return result;
}
QVariantMap PointerManager::getPointerProperties(unsigned int uid) const {
auto pointer = find(uid);
if (pointer) {
return pointer->toVariantMap();
} else {
return QVariantMap();
}
}
void PointerManager::update() {
auto cachedPointers = resultWithReadLock<std::unordered_map<unsigned int, std::shared_ptr<Pointer>>>([&] {
return _pointers;

View file

@ -30,6 +30,7 @@ public:
void setRenderState(unsigned int uid, const std::string& renderState) const;
void editRenderState(unsigned int uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const;
PickResultPointer getPrevPickResult(unsigned int uid) const;
QVariantMap getPointerProperties(unsigned int uid) const;
void setPrecisionPicking(unsigned int uid, bool precisionPicking) const;
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignoreEntities) const;

View file

@ -86,9 +86,8 @@ void CauterizedModel::createRenderItemSet() {
// Create the render payloads
int numParts = (int)mesh->getNumParts();
for (int partIndex = 0; partIndex < numParts; partIndex++) {
if (!fbxGeometry.meshes[i].blendshapes.empty() && !_blendedVertexBuffers[i]) {
_blendedVertexBuffers[i] = std::make_shared<gpu::Buffer>();
_blendedVertexBuffers[i]->resize(fbxGeometry.meshes[i].vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType)));
if (!fbxGeometry.meshes[i].blendshapes.empty()) {
initializeBlendshapes(fbxGeometry.meshes[i], i);
}
auto ptr = std::make_shared<CauterizedMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
_modelMeshRenderItems << std::static_pointer_cast<ModelMeshPartPayload>(ptr);
@ -103,7 +102,7 @@ void CauterizedModel::createRenderItemSet() {
}
}
void CauterizedModel::updateClusterMatrices() {
void CauterizedModel::updateClusterMatrices(bool triggerBlendshapes) {
PerformanceTimer perfTimer("CauterizedModel::updateClusterMatrices");
if (!_needsUpdateClusterMatrices || !isLoaded()) {
@ -176,7 +175,7 @@ void CauterizedModel::updateClusterMatrices() {
// post the blender if we're not currently waiting for one to finish
auto modelBlender = DependencyManager::get<ModelBlender>();
if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
if (triggerBlendshapes && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
modelBlender->noteRequiresBlend(getThisPointer());
}

View file

@ -33,7 +33,7 @@ public:
void createRenderItemSet() override;
virtual void updateClusterMatrices() override;
virtual void updateClusterMatrices(bool triggerBlendshapes = true) override;
void updateRenderItems() override;
const Model::MeshState& getCauterizeMeshState(int index) const;

View file

@ -302,52 +302,11 @@ bool Model::updateGeometry() {
assert(_meshStates.empty());
const FBXGeometry& fbxGeometry = getFBXGeometry();
int i = 0;
foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
MeshState state;
state.clusterDualQuaternions.resize(mesh.clusters.size());
state.clusterMatrices.resize(mesh.clusters.size());
_meshStates.push_back(state);
if (!mesh.blendshapes.isEmpty()) {
if (!_blendedVertexBuffers[i]) {
_blendedVertexBuffers[i] = std::make_shared<gpu::Buffer>();
}
const auto& buffer = _blendedVertexBuffers[i];
QVector<NormalType> normalsAndTangents;
normalsAndTangents.resize(2 * mesh.normals.size());
// Interleave normals and tangents
// Parallel version for performance
tbb::parallel_for(tbb::blocked_range<int>(0, mesh.normals.size()), [&](const tbb::blocked_range<int>& range) {
auto normalsRange = std::make_pair(mesh.normals.begin() + range.begin(), mesh.normals.begin() + range.end());
auto tangentsRange = std::make_pair(mesh.tangents.begin() + range.begin(), mesh.tangents.begin() + range.end());
auto normalsAndTangentsIt = normalsAndTangents.begin() + 2 * range.begin();
for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first;
normalIt != normalsRange.second;
++normalIt, ++tangentIt) {
#if FBX_PACK_NORMALS
glm::uint32 finalNormal;
glm::uint32 finalTangent;
buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent);
#else
const auto& finalNormal = *normalIt;
const auto& finalTangent = *tangentIt;
#endif
*normalsAndTangentsIt = finalNormal;
++normalsAndTangentsIt;
*normalsAndTangentsIt = finalTangent;
++normalsAndTangentsIt;
}
});
const auto verticesSize = mesh.vertices.size() * sizeof(glm::vec3);
buffer->resize(mesh.vertices.size() * sizeof(glm::vec3) + normalsAndTangents.size() * sizeof(NormalType));
buffer->setSubData(0, verticesSize, (const gpu::Byte*) mesh.vertices.constData());
buffer->setSubData(verticesSize, 2 * mesh.normals.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data());
mesh.normalsAndTangents = normalsAndTangents;
}
i++;
}
needFullUpdate = true;
emit rigReady();
@ -1017,7 +976,7 @@ bool Model::addToScene(const render::ScenePointer& scene,
render::Transaction& transaction,
render::Item::Status::Getters& statusGetters) {
if (!_addedToScene && isLoaded()) {
updateClusterMatrices();
updateClusterMatrices(false);
if (_modelMeshRenderItems.empty()) {
createRenderItemSet();
}
@ -1527,7 +1486,7 @@ void Model::computeMeshPartLocalBounds() {
}
// virtual
void Model::updateClusterMatrices() {
void Model::updateClusterMatrices(bool triggerBlendshapes) {
DETAILED_PERFORMANCE_TIMER("Model::updateClusterMatrices");
if (!_needsUpdateClusterMatrices || !isLoaded()) {
@ -1556,7 +1515,7 @@ void Model::updateClusterMatrices() {
// post the blender if we're not currently waiting for one to finish
auto modelBlender = DependencyManager::get<ModelBlender>();
if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
if (triggerBlendshapes && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
modelBlender->noteRequiresBlend(getThisPointer());
}
@ -1596,10 +1555,10 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo
assert(buffer);
buffer->resize(mesh.vertices.size() * sizeof(glm::vec3) + mesh.normalsAndTangents.size() * sizeof(NormalType));
buffer->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + index * sizeof(glm::vec3));
buffer->setSubData(verticesSize, 2 * mesh.normals.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data() + normalAndTangentIndex * sizeof(NormalType));
buffer->setSubData(verticesSize, mesh.normalsAndTangents.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data() + normalAndTangentIndex * sizeof(NormalType));
index += vertexCount;
normalAndTangentIndex += 2 * mesh.normals.size();
normalAndTangentIndex += mesh.normalsAndTangents.size();
}
}
@ -1637,6 +1596,42 @@ const render::ItemIDs& Model::fetchRenderItemIDs() const {
return _modelMeshRenderItemIDs;
}
void Model::initializeBlendshapes(const FBXMesh& mesh, int index) {
QVector<NormalType> normalsAndTangents;
normalsAndTangents.resize(2 * mesh.normals.size());
// Interleave normals and tangents
// Parallel version for performance
tbb::parallel_for(tbb::blocked_range<int>(0, mesh.normals.size()), [&](const tbb::blocked_range<int>& range) {
auto normalsRange = std::make_pair(mesh.normals.begin() + range.begin(), mesh.normals.begin() + range.end());
auto tangentsRange = std::make_pair(mesh.tangents.begin() + range.begin(), mesh.tangents.begin() + range.end());
auto normalsAndTangentsIt = normalsAndTangents.begin() + 2 * range.begin();
for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first;
normalIt != normalsRange.second;
++normalIt, ++tangentIt) {
#if FBX_PACK_NORMALS
glm::uint32 finalNormal;
glm::uint32 finalTangent;
buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent);
#else
const auto& finalNormal = *normalIt;
const auto& finalTangent = *tangentIt;
#endif
*normalsAndTangentsIt = finalNormal;
++normalsAndTangentsIt;
*normalsAndTangentsIt = finalTangent;
++normalsAndTangentsIt;
}
});
const auto verticesSize = mesh.vertices.size() * sizeof(glm::vec3);
_blendedVertexBuffers[index] = std::make_shared<gpu::Buffer>();
_blendedVertexBuffers[index]->resize(mesh.vertices.size() * sizeof(glm::vec3) + normalsAndTangents.size() * sizeof(NormalType));
_blendedVertexBuffers[index]->setSubData(0, verticesSize, (const gpu::Byte*) mesh.vertices.constData());
_blendedVertexBuffers[index]->setSubData(verticesSize, normalsAndTangents.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data());
mesh.normalsAndTangents = normalsAndTangents;
}
void Model::createRenderItemSet() {
assert(isLoaded());
const auto& meshes = _renderGeometry->getMeshes();
@ -1665,7 +1660,7 @@ void Model::createRenderItemSet() {
// Run through all of the meshes, and place them into their segregated, but unsorted buckets
int shapeID = 0;
uint32_t numMeshes = (uint32_t)meshes.size();
auto fbxGeometry = getFBXGeometry();
auto& fbxGeometry = getFBXGeometry();
for (uint32_t i = 0; i < numMeshes; i++) {
const auto& mesh = meshes.at(i);
if (!mesh) {
@ -1675,8 +1670,8 @@ void Model::createRenderItemSet() {
// Create the render payloads
int numParts = (int)mesh->getNumParts();
for (int partIndex = 0; partIndex < numParts; partIndex++) {
if (fbxGeometry.meshes[i].blendshapes.empty() && !_blendedVertexBuffers[i]) {
_blendedVertexBuffers[i] = std::make_shared<gpu::Buffer>();
if (!fbxGeometry.meshes[i].blendshapes.empty()) {
initializeBlendshapes(fbxGeometry.meshes[i], i);
}
_modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
auto material = getGeometry()->getShapeMaterial(shapeID);

View file

@ -159,7 +159,7 @@ public:
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
virtual void simulate(float deltaTime, bool fullUpdate = true);
virtual void updateClusterMatrices();
virtual void updateClusterMatrices(bool triggerBlendshapes = true);
/// Returns a reference to the shared geometry.
const Geometry::Pointer& getGeometry() const { return _renderGeometry; }
@ -492,6 +492,8 @@ protected:
bool shouldInvalidatePayloadShapeKey(int meshIndex);
void initializeBlendshapes(const FBXMesh& mesh, int index);
private:
float _loadingPriority { 0.0f };

View file

@ -31,7 +31,7 @@ int SoftAttachmentModel::getJointIndexOverride(int i) const {
// virtual
// use the _rigOverride matrices instead of the Model::_rig
void SoftAttachmentModel::updateClusterMatrices() {
void SoftAttachmentModel::updateClusterMatrices(bool triggerBlendshapes) {
if (!_needsUpdateClusterMatrices) {
return;
}
@ -78,7 +78,7 @@ void SoftAttachmentModel::updateClusterMatrices() {
// post the blender if we're not currently waiting for one to finish
auto modelBlender = DependencyManager::get<ModelBlender>();
if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
if (triggerBlendshapes && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
modelBlender->noteRequiresBlend(getThisPointer());
}

View file

@ -27,7 +27,7 @@ public:
~SoftAttachmentModel();
void updateRig(float deltaTime, glm::mat4 parentTransform) override;
void updateClusterMatrices() override;
void updateClusterMatrices(bool triggerBlendshapes = true) override;
protected:
int getJointIndexOverride(int i) const;

View file

@ -79,33 +79,43 @@ void AABox::setBox(const glm::vec3& corner, const glm::vec3& scale) {
glm::vec3 AABox::getFarthestVertex(const glm::vec3& normal) const {
glm::vec3 result = _corner;
if (normal.x > 0.0f) {
result.x += _scale.x;
}
if (normal.y > 0.0f) {
result.y += _scale.y;
}
if (normal.z > 0.0f) {
result.z += _scale.z;
}
// This is a branchless version of:
//if (normal.x > 0.0f) {
// result.x += _scale.x;
//}
//if (normal.y > 0.0f) {
// result.y += _scale.y;
//}
//if (normal.z > 0.0f) {
// result.z += _scale.z;
//}
float blend = (float)(normal.x > 0.0f);
result.x += blend * _scale.x + (1.0f - blend) * 0.0f;
blend = (float)(normal.y > 0.0f);
result.y += blend * _scale.y + (1.0f - blend) * 0.0f;
blend = (float)(normal.z > 0.0f);
result.z += blend * _scale.z + (1.0f - blend) * 0.0f;
return result;
}
glm::vec3 AABox::getNearestVertex(const glm::vec3& normal) const {
glm::vec3 result = _corner;
if (normal.x < 0.0f) {
result.x += _scale.x;
}
if (normal.y < 0.0f) {
result.y += _scale.y;
}
if (normal.z < 0.0f) {
result.z += _scale.z;
}
// This is a branchless version of:
//if (normal.x < 0.0f) {
// result.x += _scale.x;
//}
//if (normal.y < 0.0f) {
// result.y += _scale.y;
//}
//if (normal.z < 0.0f) {
// result.z += _scale.z;
//}
float blend = (float)(normal.x < 0.0f);
result.x += blend * _scale.x + (1.0f - blend) * 0.0f;
blend = (float)(normal.y < 0.0f);
result.y += blend * _scale.y + (1.0f - blend) * 0.0f;
blend = (float)(normal.z < 0.0f);
result.z += blend * _scale.z + (1.0f - blend) * 0.0f;
return result;
}
@ -459,28 +469,6 @@ AABox AABox::clamp(float min, float max) const {
return AABox(clampedCorner, clampedScale);
}
AABox& AABox::operator += (const glm::vec3& point) {
if (isInvalid()) {
_corner = glm::min(_corner, point);
} else {
glm::vec3 maximum(_corner + _scale);
_corner = glm::min(_corner, point);
maximum = glm::max(maximum, point);
_scale = maximum - _corner;
}
return (*this);
}
AABox& AABox::operator += (const AABox& box) {
if (!box.isInvalid()) {
(*this) += box._corner;
(*this) += box.calcTopFarLeft();
}
return (*this);
}
void AABox::embiggen(float scale) {
_corner += scale * (-0.5f * _scale);
_scale *= scale;

View file

@ -85,8 +85,23 @@ public:
AABox clamp(const glm::vec3& min, const glm::vec3& max) const;
AABox clamp(float min, float max) const;
AABox& operator += (const glm::vec3& point);
AABox& operator += (const AABox& box);
inline AABox& operator+=(const glm::vec3& point) {
bool valid = !isInvalid();
glm::vec3 maximum = glm::max(_corner + _scale, point);
_corner = glm::min(_corner, point);
if (valid) {
_scale = maximum - _corner;
}
return (*this);
}
inline AABox& operator+=(const AABox& box) {
if (!box.isInvalid()) {
(*this) += box._corner;
(*this) += box.calcTopFarLeft();
}
return (*this);
}
// Translate the AABox just moving the corner
void translate(const glm::vec3& translation) { _corner += translation; }
@ -114,7 +129,7 @@ public:
static const glm::vec3 INFINITY_VECTOR;
bool isInvalid() const { return _corner == INFINITY_VECTOR; }
bool isInvalid() const { return _corner.x == std::numeric_limits<float>::infinity(); }
void clear() { _corner = INFINITY_VECTOR; _scale = glm::vec3(0.0f); }

View file

@ -44,7 +44,7 @@ const float DEFAULT_AVATAR_RIGHTHAND_MASS = 2.0f;
// Used when avatar is missing joints... (avatar space)
const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 };
const glm::vec3 DEFAULT_AVATAR_MIDDLE_EYE_POS { 0.0f, 0.6f, 0.0f };
const glm::vec3 DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET = { 0.0f, 0.06f, -0.09f };
const glm::vec3 DEFAULT_AVATAR_HEAD_POS { 0.0f, 0.53f, 0.0f };
const glm::quat DEFAULT_AVATAR_HEAD_ROT { Quaternions::Y_180 };
const glm::vec3 DEFAULT_AVATAR_RIGHTARM_POS { -0.134824f, 0.396348f, -0.0515777f };

View file

@ -316,4 +316,17 @@ inline void glm_mat4u_mul(const glm::mat4& m1, const glm::mat4& m2, glm::mat4& r
#endif
}
// convert float to int, using round-to-nearest-even (undefined on overflow)
inline int fastLrintf(float x) {
#if GLM_ARCH & GLM_ARCH_SSE2_BIT
return _mm_cvt_ss2si(_mm_set_ss(x));
#else
// return lrintf(x);
static_assert(std::numeric_limits<double>::is_iec559, "Requires IEEE-754 double precision format");
union { double d; int64_t i; } bits = { (double)x };
bits.d += (3ULL << 51);
return (int)bits.i;
#endif
}
#endif // hifi_GLMHelpers_h

View file

@ -0,0 +1,31 @@
//
// Created by Sabrina Shanman 8/14/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "NestableTransformNode.h"
NestableTransformNode::NestableTransformNode(SpatiallyNestableWeakPointer spatiallyNestable, int jointIndex) :
_spatiallyNestable(spatiallyNestable),
_jointIndex(jointIndex)
{
}
Transform NestableTransformNode::getTransform() {
auto nestable = _spatiallyNestable.lock();
if (!nestable) {
return Transform();
}
bool success;
Transform jointWorldTransform = nestable->getTransform(_jointIndex, success);
if (success) {
return jointWorldTransform;
} else {
return Transform();
}
}

View file

@ -0,0 +1,25 @@
//
// Created by Sabrina Shanman 8/14/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_NestableTransformNode_h
#define hifi_NestableTransformNode_h
#include "TransformNode.h"
#include "SpatiallyNestable.h"
class NestableTransformNode : public TransformNode {
public:
NestableTransformNode(SpatiallyNestableWeakPointer spatiallyNestable, int jointIndex);
Transform getTransform() override;
protected:
SpatiallyNestableWeakPointer _spatiallyNestable;
int _jointIndex;
};
#endif // hifi_NestableTransformNode_h

View file

@ -36,6 +36,7 @@ const float METERS_PER_MILLIMETER = 0.001f;
const float MILLIMETERS_PER_METER = 1000.0f;
const quint64 NSECS_PER_USEC = 1000;
const quint64 NSECS_PER_MSEC = 1000000;
const quint64 NSECS_PER_SECOND = 1e9;
const quint64 USECS_PER_MSEC = 1000;
const quint64 MSECS_PER_SECOND = 1000;
const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;

View file

@ -12,7 +12,6 @@
#define hifi_PrioritySortUtil_h
#include <glm/glm.hpp>
#include <queue>
#include "NumericalConstants.h"
#include "shared/ConicalViewFrustum.h"
@ -75,7 +74,6 @@ namespace PrioritySortUtil {
void setPriority(float priority) { _priority = priority; }
float getPriority() const { return _priority; }
bool operator<(const Sortable& other) const { return _priority < other._priority; }
private:
float _priority { 0.0f };
};
@ -97,14 +95,18 @@ namespace PrioritySortUtil {
_ageWeight = ageWeight;
}
size_t size() const { return _queue.size(); }
size_t size() const { return _vector.size(); }
void push(T thing) {
thing.setPriority(computePriority(thing));
_queue.push(thing);
_vector.push_back(thing);
}
void reserve(size_t num) {
_vector.reserve(num);
}
const std::vector<T>& getSortedVector() {
std::sort(_vector.begin(), _vector.end(), [](const T& left, const T& right) { return left.getPriority() > right.getPriority(); });
return _vector;
}
const T& top() const { return _queue.top(); }
void pop() { return _queue.pop(); }
bool empty() const { return _queue.empty(); }
private:
@ -153,7 +155,7 @@ namespace PrioritySortUtil {
}
ConicalViewFrustums _views;
std::priority_queue<T> _queue;
std::vector<T> _vector;
float _angularWeight { DEFAULT_ANGULAR_COEF };
float _centerWeight { DEFAULT_CENTER_COEF };
float _ageWeight { DEFAULT_AGE_COEF };

View file

@ -253,18 +253,36 @@ public:
}
};
/**jsdoc
* A CollisionPick defines a volume for checking collisions in the physics simulation.
// TODO: Add "loaded" to CollisionRegion jsdoc once model collision picks are supported.
* @typedef {object} CollisionPick
/**jsdoc
* A CollisionRegion defines a volume for checking collisions in the physics simulation.
* @typedef {object} CollisionRegion
* @property {Shape} shape - The information about the collision region's size and shape.
* @property {Vec3} position - The position of the collision region.
* @property {Quat} orientation - The orientation of the collision region.
* @property {Vec3} position - The position of the collision region, relative to a parent if defined.
* @property {Quat} orientation - The orientation of the collision region, relative to a parent if defined.
* @property {float} threshold - The approximate minimum penetration depth for a test object to be considered in contact with the collision region.
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or an overlay.
* @property {number} parentJointIndex - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar.
*/
class CollisionRegion : public MathPick {
public:
CollisionRegion() { }
CollisionRegion(const CollisionRegion& collisionRegion) :
loaded(collisionRegion.loaded),
modelURL(collisionRegion.modelURL),
shapeInfo(std::make_shared<ShapeInfo>()),
transform(collisionRegion.transform),
threshold(collisionRegion.threshold)
{
shapeInfo->setParams(collisionRegion.shapeInfo->getType(), collisionRegion.shapeInfo->getHalfExtents(), collisionRegion.modelURL.toString());
}
CollisionRegion(const QVariantMap& pickVariant) {
// "loaded" is not deserialized here because there is no way to know if the shape is actually loaded
if (pickVariant["shape"].isValid()) {
auto shape = pickVariant["shape"].toMap();
if (!shape.empty()) {
@ -287,6 +305,10 @@ public:
}
}
if (pickVariant["threshold"].isValid()) {
threshold = glm::max(0.0f, pickVariant["threshold"].toFloat());
}
if (pickVariant["position"].isValid()) {
transform.setTranslation(vec3FromVariant(pickVariant["position"]));
}
@ -304,6 +326,9 @@ public:
shape["dimensions"] = vec3toVariant(transform.getScale());
collisionRegion["shape"] = shape;
collisionRegion["loaded"] = loaded;
collisionRegion["threshold"] = threshold;
collisionRegion["position"] = vec3toVariant(transform.getTranslation());
collisionRegion["orientation"] = quatToVariant(transform.getRotation());
@ -312,13 +337,16 @@ public:
}
operator bool() const override {
return !(glm::any(glm::isnan(transform.getTranslation())) ||
return !std::isnan(threshold) &&
!(glm::any(glm::isnan(transform.getTranslation())) ||
glm::any(glm::isnan(transform.getRotation())) ||
shapeInfo->getType() == SHAPE_TYPE_NONE);
}
bool operator==(const CollisionRegion& other) const {
return glm::all(glm::equal(transform.getTranslation(), other.transform.getTranslation())) &&
return loaded == other.loaded &&
threshold == other.threshold &&
glm::all(glm::equal(transform.getTranslation(), other.transform.getTranslation())) &&
glm::all(glm::equal(transform.getRotation(), other.transform.getRotation())) &&
glm::all(glm::equal(transform.getScale(), other.transform.getScale())) &&
shapeInfo->getType() == other.shapeInfo->getType() &&
@ -337,11 +365,13 @@ public:
}
// We can't load the model here because it would create a circular dependency, so we delegate that responsibility to the owning CollisionPick
bool loaded { false };
QUrl modelURL;
// We can't compute the shapeInfo here without loading the model first, so we delegate that responsibility to the owning CollisionPick
std::shared_ptr<ShapeInfo> shapeInfo = std::make_shared<ShapeInfo>();
Transform transform;
float threshold;
};
namespace std {

View file

@ -0,0 +1,18 @@
//
// Created by Sabrina Shanman 8/14/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_TransformNode_h
#define hifi_TransformNode_h
#include "Transform.h"
class TransformNode {
public:
virtual Transform getTransform() = 0;
};
#endif // hifi_TransformNode_h

View file

@ -156,10 +156,12 @@ bool FileUtils::canCreateFile(const QString& fullPath) {
qDebug(shared) << "unable to overwrite file '" << fullPath << "'";
return false;
}
QDir dir(fileInfo.absolutePath());
QString absolutePath = fileInfo.absolutePath();
QDir dir(absolutePath);
if (!dir.exists()) {
if (!dir.mkpath(fullPath)) {
qDebug(shared) << "unable to create dir '" << dir.absolutePath() << "'";
if (!dir.mkpath(absolutePath)) {
qDebug(shared) << "unable to create dir '" << absolutePath << "'";
return false;
}
}

View file

@ -70,7 +70,8 @@ private:
using SpacePointer = std::shared_ptr<Space>;
using Changes = std::vector<Space::Change>;
using IndexVectors = std::vector<IndexVector>;
using Timings = std::vector<std::chrono::nanoseconds>;
using Timing_ns = std::chrono::nanoseconds;
using Timings = std::vector<Timing_ns>;
} // namespace workload

View file

@ -102,9 +102,16 @@ void ControlViews::run(const workload::WorkloadContextPointer& runContext, const
// Export the ranges and timings for debuging
if (inTimings.size()) {
_dataExport.timings[workload::Region::R1] = std::chrono::duration<float, std::milli>(inTimings[0]).count();
// NOTE for reference:
// inTimings[0] = prePhysics entities
// inTimings[1] = prePhysics avatars
// inTimings[2] = stepPhysics
// inTimings[3] = postPhysics
// inTimings[4] = non-physical kinematics
// inTimings[5] = game loop
_dataExport.timings[workload::Region::R1] = std::chrono::duration<float, std::milli>(inTimings[2] + inTimings[3]).count();
_dataExport.timings[workload::Region::R2] = _dataExport.timings[workload::Region::R1];
_dataExport.timings[workload::Region::R3] = std::chrono::duration<float, std::milli>(inTimings[1]).count();
_dataExport.timings[workload::Region::R3] = std::chrono::duration<float, std::milli>(inTimings[4]).count();
doExport = true;
}
@ -115,11 +122,29 @@ void ControlViews::run(const workload::WorkloadContextPointer& runContext, const
}
}
glm::vec2 Regulator::run(const Timing_ns& regulationDuration, const Timing_ns& measured, const glm::vec2& current) {
// Regulate next value based on current moving toward the goal budget
float error_ms = std::chrono::duration<float, std::milli>(_budget - measured).count();
float coef = glm::clamp(error_ms / std::chrono::duration<float, std::milli>(regulationDuration).count(), -1.0f, 1.0f);
return current * (1.0f + coef * (error_ms < 0.0f ? _relativeStepDown : _relativeStepUp));
glm::vec2 Regulator::run(const Timing_ns& deltaTime, const Timing_ns& measuredTime, const glm::vec2& currentFrontBack) {
// measure signal: average and noise
const float FILTER_TIMESCALE = 0.5f * (float)NSECS_PER_SECOND;
float del = deltaTime.count() / FILTER_TIMESCALE;
if (del > 1.0f) {
del = 1.0f; // clamp for stability
}
_measuredTimeAverage = (1.0f - del) * _measuredTimeAverage + del * measuredTime.count();
float diff = measuredTime.count() - _measuredTimeAverage;
_measuredTimeNoiseSquared = (1.0f - del) * _measuredTimeNoiseSquared + del * diff * diff;
float noise = sqrtf(_measuredTimeNoiseSquared);
// check budget
float offsetFromTarget = _budget.count() - _measuredTimeAverage;
if (fabsf(offsetFromTarget) < noise) {
// budget is within the noise --> do nothing
return currentFrontBack;
}
// compute response
glm::vec2 stepDelta = offsetFromTarget < 0.0f ? -_relativeStepDown : _relativeStepUp;
stepDelta *= glm::min(1.0f, (fabsf(offsetFromTarget) - noise) / noise); // ease out of "do nothing"
return currentFrontBack * (1.0f + stepDelta);
}
glm::vec2 Regulator::clamp(const glm::vec2& backFront) const {
@ -128,17 +153,24 @@ glm::vec2 Regulator::clamp(const glm::vec2& backFront) const {
}
void ControlViews::regulateViews(workload::Views& outViews, const workload::Timings& timings) {
for (auto& outView : outViews) {
for (int32_t r = 0; r < workload::Region::NUM_VIEW_REGIONS; r++) {
outView.regionBackFronts[r] = regionBackFronts[r];
}
}
auto loopDuration = std::chrono::nanoseconds{ std::chrono::milliseconds(16) };
regionBackFronts[workload::Region::R1] = regionRegulators[workload::Region::R1].run(loopDuration, timings[0], regionBackFronts[workload::Region::R1]);
regionBackFronts[workload::Region::R2] = regionRegulators[workload::Region::R2].run(loopDuration, timings[0], regionBackFronts[workload::Region::R2]);
regionBackFronts[workload::Region::R3] = regionRegulators[workload::Region::R3].run(loopDuration, timings[1], regionBackFronts[workload::Region::R3]);
// Note: for reference:
// timings[0] = prePhysics entities
// timings[1] = prePhysics avatars
// timings[2] = stepPhysics
// timings[3] = postPhysics
// timings[4] = non-physical kinematics
// timings[5] = game loop
auto loopDuration = timings[5];
regionBackFronts[workload::Region::R1] = regionRegulators[workload::Region::R1].run(loopDuration, timings[2] + timings[3], regionBackFronts[workload::Region::R1]);
regionBackFronts[workload::Region::R2] = regionRegulators[workload::Region::R2].run(loopDuration, timings[2] + timings[3], regionBackFronts[workload::Region::R2]);
regionBackFronts[workload::Region::R3] = regionRegulators[workload::Region::R3].run(loopDuration, timings[4], regionBackFronts[workload::Region::R3]);
enforceRegionContainment();
for (auto& outView : outViews) {

View file

@ -37,8 +37,8 @@ namespace workload {
{ 250.0f, 16000.0f }
};
const float RELATIVE_STEP_DOWN = 0.05f;
const float RELATIVE_STEP_UP = 0.04f;
const float RELATIVE_STEP_DOWN = 0.11f;
const float RELATIVE_STEP_UP = 0.09f;
class SetupViewsConfig : public Job::Config{
Q_OBJECT
@ -212,20 +212,31 @@ namespace workload {
};
struct Regulator {
using Timing_ns = std::chrono::nanoseconds;
Timing_ns _budget{ std::chrono::milliseconds(2) };
glm::vec2 _minRange{ MIN_VIEW_BACK_FRONTS[0] };
glm::vec2 _maxRange{ MAX_VIEW_BACK_FRONTS[0] };
glm::vec2 _relativeStepDown{ RELATIVE_STEP_DOWN };
glm::vec2 _relativeStepUp{ RELATIVE_STEP_UP };
Timing_ns _budget{ std::chrono::milliseconds(2) };
float _measuredTimeAverage { 0.0f };
float _measuredTimeNoiseSquared { 0.0f };
Regulator() {}
Regulator(const Timing_ns& budget_ns, const glm::vec2& minRange, const glm::vec2& maxRange, const glm::vec2& relativeStepDown, const glm::vec2& relativeStepUp) :
_budget(budget_ns), _minRange(minRange), _maxRange(maxRange), _relativeStepDown(relativeStepDown), _relativeStepUp(relativeStepUp) {}
Regulator(const Timing_ns& budget_ns,
const glm::vec2& minRange,
const glm::vec2& maxRange,
const glm::vec2& relativeStepDown,
const glm::vec2& relativeStepUp) :
_minRange(minRange),
_maxRange(maxRange),
_relativeStepDown(relativeStepDown),
_relativeStepUp(relativeStepUp),
_budget(budget_ns),
_measuredTimeAverage(budget_ns.count()),
_measuredTimeNoiseSquared(0.0f)
{}
glm::vec2 run(const Timing_ns& regulationDuration, const Timing_ns& measured, const glm::vec2& current);
void setBudget(const Timing_ns& budget) { _budget = budget; }
glm::vec2 run(const Timing_ns& deltaTime, const Timing_ns& measuredTime, const glm::vec2& currentFrontBack);
glm::vec2 clamp(const glm::vec2& backFront) const;
};

View file

@ -168,12 +168,13 @@ void AACubeTests::rayVsParabolaPerformance() {
glm::vec3 origin(0.0f);
glm::vec3 direction = glm::normalize(glm::vec3(1.0f));
glm::vec3 invDirection = 1.0f / direction;
float distance;
BoxFace face;
glm::vec3 normal;
auto start = std::chrono::high_resolution_clock::now();
for (auto& cube : cubes) {
if (cube.findRayIntersection(origin, direction, distance, face, normal)) {
if (cube.findRayIntersection(origin, direction, invDirection, distance, face, normal)) {
numRayHits++;
}
}

View file

@ -214,3 +214,39 @@ void GLMHelpersTests::testGenerateBasisVectors() {
QCOMPARE_WITH_ABS_ERROR(w, z, EPSILON);
}
}
void GLMHelpersTests::roundPerf() {
const int NUM_VECS = 1000000;
const float MAX_VEC = 500.0f;
std::vector<glm::vec3> vecs;
vecs.reserve(NUM_VECS);
for (int i = 0; i < NUM_VECS; i++) {
vecs.emplace_back(randFloatInRange(-MAX_VEC, MAX_VEC), randFloatInRange(-MAX_VEC, MAX_VEC), randFloatInRange(-MAX_VEC, MAX_VEC));
}
std::vector<glm::vec3> vecs2 = vecs;
std::vector<glm::vec3> originalVecs = vecs;
auto start = std::chrono::high_resolution_clock::now();
for (auto& vec : vecs) {
vec = glm::round(vec);
}
auto glmTime = std::chrono::high_resolution_clock::now() - start;
start = std::chrono::high_resolution_clock::now();
for (auto& vec : vecs2) {
vec = glm::vec3(fastLrintf(vec.x), fastLrintf(vec.y), fastLrintf(vec.z));
}
auto manualTime = std::chrono::high_resolution_clock::now() - start;
bool identical = true;
for (int i = 0; i < vecs.size(); i++) {
identical &= vecs[i] == vecs2[i];
if (vecs[i] != vecs2[i]) {
qDebug() << "glm: " << vecs[i].x << vecs[i].y << vecs[i].z << ", manual: " << vecs2[i].x << vecs2[i].y << vecs2[i].z;
qDebug() << "original: " << originalVecs[i].x << originalVecs[i].y << originalVecs[i].z;
break;
}
}
qDebug() << "ratio: " << (float)glmTime.count() / (float)manualTime.count() << ", identical: " << identical;
}

View file

@ -22,6 +22,7 @@ private slots:
void testSixByteOrientationCompression();
void testSimd();
void testGenerateBasisVectors();
void roundPerf();
};
float getErrorDifference(const float& a, const float& b);