Merge branch 'master' into M17217-b

This commit is contained in:
David Rowe 2018-08-17 10:35:52 +12:00
commit 7e46a99400
207 changed files with 7250 additions and 3792 deletions

View file

@ -1,3 +1,10 @@
### OS Specific Build Guides
* [BUILD_WIN.md](BUILD_WIN.md) - complete instructions for Windows.
* [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X.
* [BUILD_LINUX.md](BUILD_LINUX.md) - additional instructions for Linux.
* [BUILD_ANDROID.md](BUILD_ANDROID.md) - additional instructions for Android
### Dependencies
- [cmake](https://cmake.org/download/): 3.9
@ -27,14 +34,7 @@ These are not placed in your normal build tree when doing an out of source build
If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DUSE\_LOCAL\_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project.
### OS Specific Build Guides
* [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X.
* [BUILD_LINUX.md](BUILD_LINUX.md) - additional instructions for Linux.
* [BUILD_WIN.md](BUILD_WIN.md) - additional instructions for Windows.
* [BUILD_ANDROID.md](BUILD_ANDROID.md) - additional instructions for Android
### CMake
#### CMake
Hifi uses CMake to generate build files and project files for your platform.
@ -80,6 +80,7 @@ In the examples below the variable $NAME would be replaced by the name of the de
* $NAME_ROOT_DIR - set this variable in your ENV
* HIFI_LIB_DIR - set this variable in your ENV to your High Fidelity lib folder, should contain a folder '$name'
### Optional Components
#### Devices

View file

@ -82,8 +82,6 @@ Agent::Agent(ReceivedMessage& message) :
DependencyManager::set<recording::ClipCache>();
DependencyManager::set<ScriptCache>();
DependencyManager::set<ScriptEngines>(ScriptEngine::AGENT_SCRIPT);
DependencyManager::set<RecordingScriptingInterface>();
DependencyManager::set<UsersScriptingInterface>();
@ -162,6 +160,8 @@ void Agent::handleAudioPacket(QSharedPointer<ReceivedMessage> message) {
static const QString AGENT_LOGGING_NAME = "agent";
void Agent::run() {
// Create ScriptEngines on threaded-assignment thread then move to main thread.
DependencyManager::set<ScriptEngines>(ScriptEngine::AGENT_SCRIPT)->moveToThread(qApp->thread());
// make sure we request our script once the agent connects to the domain
auto nodeList = DependencyManager::get<NodeList>();
@ -497,7 +497,6 @@ void Agent::executeScript() {
Frame::clearFrameHandler(AVATAR_FRAME_TYPE);
DependencyManager::destroy<RecordingScriptingInterface>();
setFinished(true);
}
@ -516,7 +515,7 @@ void Agent::setIsListeningToAudioStream(bool isListeningToAudioStream) {
auto nodeList = DependencyManager::get<NodeList>();
nodeList->eachMatchingNode(
[&](const SharedNodePointer& node)->bool {
[](const SharedNodePointer& node)->bool {
return (node->getType() == NodeType::AudioMixer) && node->getActiveSocket();
},
[&](const SharedNodePointer& node) {
@ -847,7 +846,7 @@ void Agent::aboutToFinish() {
DependencyManager::destroy<recording::Deck>();
DependencyManager::destroy<recording::Recorder>();
DependencyManager::destroy<recording::ClipCache>();
DependencyManager::destroy<ScriptEngine>();
QMetaObject::invokeMethod(&_avatarAudioTimer, "stop");
// cleanup codec & encoder

View file

@ -945,22 +945,14 @@ void AssetServer::sendStatsPacket() {
upstreamStats["2. Sent Packets"] = stat.second.sentPackets;
upstreamStats["3. Recvd ACK"] = events[Events::ReceivedACK];
upstreamStats["4. Procd ACK"] = events[Events::ProcessedACK];
upstreamStats["5. Recvd LACK"] = events[Events::ReceivedLightACK];
upstreamStats["6. Recvd NAK"] = events[Events::ReceivedNAK];
upstreamStats["7. Recvd TNAK"] = events[Events::ReceivedTimeoutNAK];
upstreamStats["8. Sent ACK2"] = events[Events::SentACK2];
upstreamStats["9. Retransmitted"] = events[Events::Retransmission];
upstreamStats["5. Retransmitted"] = events[Events::Retransmission];
nodeStats["Upstream Stats"] = upstreamStats;
QJsonObject downstreamStats;
downstreamStats["1. Recvd (P/s)"] = stat.second.receiveRate;
downstreamStats["2. Recvd Packets"] = stat.second.receivedPackets;
downstreamStats["3. Sent ACK"] = events[Events::SentACK];
downstreamStats["4. Sent LACK"] = events[Events::SentLightACK];
downstreamStats["5. Sent NAK"] = events[Events::SentNAK];
downstreamStats["6. Sent TNAK"] = events[Events::SentTimeoutNAK];
downstreamStats["7. Recvd ACK2"] = events[Events::ReceivedACK2];
downstreamStats["8. Duplicates"] = events[Events::Duplicate];
downstreamStats["4. Duplicates"] = events[Events::Duplicate];
nodeStats["Downstream Stats"] = downstreamStats;
QString uuid;

View file

@ -447,18 +447,21 @@ void AvatarMixer::handleAvatarKilled(SharedNodePointer avatarNode) {
// send a kill packet for it to our other nodes
nodeList->eachMatchingNode([&](const SharedNodePointer& node) {
// we relay avatar kill packets to agents that are not upstream
// and downstream avatar mixers, if the node that was just killed was being replicated
return (node->getType() == NodeType::Agent && !node->isUpstream()) ||
(avatarNode->isReplicated() && shouldReplicateTo(*avatarNode, *node));
// and downstream avatar mixers, if the node that was just killed was being replicatedConnectedAgent
return node->getActiveSocket() &&
((node->getType() == NodeType::Agent && !node->isUpstream()) ||
(avatarNode->isReplicated() && shouldReplicateTo(*avatarNode, *node)));
}, [&](const SharedNodePointer& node) {
if (node->getType() == NodeType::Agent) {
if (!killPacket) {
killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason));
killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true);
killPacket->write(avatarNode->getUUID().toRfc4122());
killPacket->writePrimitive(KillAvatarReason::AvatarDisconnected);
}
nodeList->sendUnreliablePacket(*killPacket, *node);
auto killPacketCopy = NLPacket::createCopy(*killPacket);
nodeList->sendPacket(std::move(killPacketCopy), *node);
} else {
// send a replicated kill packet to the downstream avatar mixer
if (!replicatedKillPacket) {

View file

@ -108,7 +108,7 @@ uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& node
void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointer other) {
if (!isRadiusIgnoring(other->getUUID())) {
addToRadiusIgnoringSet(other->getUUID());
auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason));
auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true);
killPacket->write(other->getUUID().toRfc4122());
if (self->isIgnoreRadiusEnabled()) {
killPacket->writePrimitive(KillAvatarReason::TheirAvatarEnteredYourBubble);
@ -116,7 +116,7 @@ void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointe
killPacket->writePrimitive(KillAvatarReason::YourAvatarEnteredTheirBubble);
}
setLastBroadcastTime(other->getUUID(), 0);
DependencyManager::get<NodeList>()->sendUnreliablePacket(*killPacket, *self);
DependencyManager::get<NodeList>()->sendPacket(std::move(killPacket), *self);
}
}

View file

@ -264,7 +264,8 @@ void DomainGatekeeper::updateNodePermissions() {
QList<SharedNodePointer> nodesToKill;
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
limitedNodeList->eachNode([this, limitedNodeList, &nodesToKill](const SharedNodePointer& node){
QWeakPointer<LimitedNodeList> limitedNodeListWeak = limitedNodeList;
limitedNodeList->eachNode([this, limitedNodeListWeak, &nodesToKill](const SharedNodePointer& node){
// the id and the username in NodePermissions will often be the same, but id is set before
// authentication and verifiedUsername is only set once they user's key has been confirmed.
QString verifiedUsername = node->getPermissions().getVerifiedUserName();
@ -296,7 +297,8 @@ void DomainGatekeeper::updateNodePermissions() {
machineFingerprint = nodeData->getMachineFingerprint();
auto sendingAddress = nodeData->getSendingSockAddr().getAddress();
isLocalUser = (sendingAddress == limitedNodeList->getLocalSockAddr().getAddress() ||
auto nodeList = limitedNodeListWeak.lock();
isLocalUser = ((nodeList && sendingAddress == nodeList->getLocalSockAddr().getAddress()) ||
sendingAddress == QHostAddress::LocalHost);
}
@ -458,7 +460,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
// in case this is a node that's failing to connect
// double check we don't have the same node whose sockets match exactly already in the list
limitedNodeList->eachNodeBreakable([&](const SharedNodePointer& node){
limitedNodeList->eachNodeBreakable([nodeConnection, username, &existingNodeID](const SharedNodePointer& node){
if (node->getPublicSocket() == nodeConnection.publicSockAddr && node->getLocalSocket() == nodeConnection.localSockAddr) {
// we have a node that already has these exact sockets - this can occur if a node
@ -1009,7 +1011,7 @@ void DomainGatekeeper::refreshGroupsCache() {
getDomainOwnerFriendsList();
auto nodeList = DependencyManager::get<LimitedNodeList>();
nodeList->eachNode([&](const SharedNodePointer& node) {
nodeList->eachNode([this](const SharedNodePointer& node) {
if (!node->getPermissions().isAssignment) {
// this node is an agent
const QString& verifiedUserName = node->getPermissions().getVerifiedUserName();

View file

@ -1078,7 +1078,7 @@ bool DomainServer::isInInterestSet(const SharedNodePointer& nodeA, const SharedN
unsigned int DomainServer::countConnectedUsers() {
unsigned int result = 0;
auto nodeList = DependencyManager::get<LimitedNodeList>();
nodeList->eachNode([&](const SharedNodePointer& node){
nodeList->eachNode([&result](const SharedNodePointer& node){
// only count unassigned agents (i.e., users)
if (node->getType() == NodeType::Agent) {
auto nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
@ -1181,7 +1181,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
// DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
if (nodeData->isAuthenticated()) {
// if this authenticated node has any interest types, send back those nodes as well
limitedNodeList->eachNode([&](const SharedNodePointer& otherNode) {
limitedNodeList->eachNode([this, node, &domainListPackets, &domainListStream](const SharedNodePointer& otherNode) {
if (otherNode->getUUID() != node->getUUID() && isInInterestSet(node, otherNode)) {
// since we're about to add a node to the packet we start a segment
domainListPackets->startSegment();
@ -1230,6 +1230,7 @@ QUuid DomainServer::connectionSecretForNodes(const SharedNodePointer& nodeA, con
void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
QWeakPointer<LimitedNodeList> limitedNodeListWeak = limitedNodeList;
auto addNodePacket = NLPacket::create(PacketType::DomainServerAddedNode);
@ -1241,7 +1242,7 @@ void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
int connectionSecretIndex = addNodePacket->pos();
limitedNodeList->eachMatchingNode(
[&](const SharedNodePointer& node)->bool {
[this, addedNode](const SharedNodePointer& node)->bool {
if (node->getLinkedData() && node->getActiveSocket() && node != addedNode) {
// is the added Node in this node's interest list?
return isInInterestSet(node, addedNode);
@ -1249,16 +1250,19 @@ void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
return false;
}
},
[&](const SharedNodePointer& node) {
addNodePacket->seek(connectionSecretIndex);
QByteArray rfcConnectionSecret = connectionSecretForNodes(node, addedNode).toRfc4122();
// replace the bytes at the end of the packet for the connection secret between these nodes
addNodePacket->write(rfcConnectionSecret);
[this, &addNodePacket, connectionSecretIndex, addedNode, limitedNodeListWeak](const SharedNodePointer& node) {
// send off this packet to the node
limitedNodeList->sendUnreliablePacket(*addNodePacket, *node);
auto limitedNodeList = limitedNodeListWeak.lock();
if (limitedNodeList) {
addNodePacket->seek(connectionSecretIndex);
QByteArray rfcConnectionSecret = connectionSecretForNodes(node, addedNode).toRfc4122();
// replace the bytes at the end of the packet for the connection secret between these nodes
addNodePacket->write(rfcConnectionSecret);
limitedNodeList->sendUnreliablePacket(*addNodePacket, *node);
}
}
);
}
@ -2864,7 +2868,7 @@ void DomainServer::updateReplicationNodes(ReplicationServerDirection direction)
auto serversSettings = replicationSettings.value(serversKey).toList();
std::vector<HifiSockAddr> knownReplicationNodes;
nodeList->eachNode([&](const SharedNodePointer& otherNode) {
nodeList->eachNode([direction, &knownReplicationNodes](const SharedNodePointer& otherNode) {
if ((direction == Upstream && NodeType::isUpstream(otherNode->getType()))
|| (direction == Downstream && NodeType::isDownstream(otherNode->getType()))) {
knownReplicationNodes.push_back(otherNode->getPublicSocket());
@ -2902,7 +2906,7 @@ void DomainServer::updateReplicationNodes(ReplicationServerDirection direction)
// collect them in a vector to separately remove them with handleKillNode (since eachNode has a read lock and
// we cannot recursively take the write lock required by handleKillNode)
std::vector<SharedNodePointer> nodesToKill;
nodeList->eachNode([&](const SharedNodePointer& otherNode) {
nodeList->eachNode([this, direction, replicationNodesInSettings, replicationDirection, &nodesToKill](const SharedNodePointer& otherNode) {
if ((direction == Upstream && NodeType::isUpstream(otherNode->getType()))
|| (direction == Downstream && NodeType::isDownstream(otherNode->getType()))) {
bool nodeInSettings = find(replicationNodesInSettings.cbegin(), replicationNodesInSettings.cend(),

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -585,8 +585,9 @@
"states": [
{
"id": "idle",
"interpTarget": 10,
"interpDuration": 10,
"interpTarget": 0,
"interpDuration": 4,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isMovingForward", "state": "idleToWalkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
@ -598,13 +599,16 @@
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" }
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "idleToWalkFwd",
"interpTarget": 3,
"interpDuration": 3,
"interpTarget": 10,
"interpDuration": 4,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "idleToWalkFwdOnDone", "state": "walkFwd" },
{ "var": "isNotMoving", "state": "idle" },
@ -617,6 +621,30 @@
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "idleSettle",
"interpTarget": 10,
"interpDuration": 10,
"interpType": "snapshotPrev",
"transitions": [
{"var": "idleSettleOnDone", "state": "idle" },
{"var": "isMovingForward", "state": "idleToWalkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" }
]
},
@ -624,8 +652,9 @@
"id": "walkFwd",
"interpTarget": 16,
"interpDuration": 6,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotMoving", "state": "idle" },
{ "var": "isNotMoving", "state": "idleSettle" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
@ -635,15 +664,18 @@
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" }
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "walkBwd",
"interpTarget": 6,
"interpTarget": 8,
"interpDuration": 6,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotMoving", "state": "idle" },
{ "var": "isNotMoving", "state": "idleSettle" },
{ "var": "isMovingForward", "state": "walkFwd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
@ -653,15 +685,18 @@
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" }
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "strafeRight",
"interpTarget": 6,
"interpDuration": 6,
"interpTarget": 5,
"interpDuration": 8,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotMoving", "state": "idle" },
{ "var": "isNotMoving", "state": "idleSettle" },
{ "var": "isMovingForward", "state": "walkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
@ -671,18 +706,65 @@
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "strafeLeft",
"interpTarget": 5,
"interpDuration": 8,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotMoving", "state": "idleSettle" },
{ "var": "isMovingForward", "state": "walkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "strafeRightHmd",
"interpTarget": 5,
"interpDuration": 8,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotMoving", "state": "idleSettle" },
{ "var": "isMovingForward", "state": "walkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" }
]
},
{
"id": "strafeLeft",
"interpTarget": 6,
"interpDuration": 6,
"id": "strafeLeftHmd",
"interpTarget": 5,
"interpDuration": 8,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotMoving", "state": "idle" },
{ "var": "isNotMoving", "state": "idleSettle" },
{ "var": "isMovingForward", "state": "walkFwd" },
{ "var": "isMovingBackward", "state": "walkBwd" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isFlying", "state": "fly" },
@ -695,7 +777,8 @@
{
"id": "turnRight",
"interpTarget": 6,
"interpDuration": 6,
"interpDuration": 8,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotTurning", "state": "idle" },
{ "var": "isMovingForward", "state": "walkFwd" },
@ -707,13 +790,16 @@
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" }
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
"id": "turnLeft",
"interpTarget": 6,
"interpDuration": 6,
"interpDuration": 8,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isNotTurning", "state": "idle" },
{ "var": "isMovingForward", "state": "walkFwd" },
@ -725,7 +811,9 @@
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" }
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
@ -733,7 +821,7 @@
"interpTarget": 6,
"interpDuration": 6,
"transitions": [
{ "var": "isNotFlying", "state": "idle" }
{ "var": "isNotFlying", "state": "idleSettle" }
]
},
{
@ -797,7 +885,9 @@
{ "var": "isTakeoffRun", "state": "takeoffRun" },
{ "var": "isInAirStand", "state": "inAirStand" },
{ "var": "isInAirRun", "state": "inAirRun" },
{ "var": "landStandOnDone", "state": "idle" }
{ "var": "landStandOnDone", "state": "idle" },
{ "var": "isMovingRightHmd", "state": "strafeRightHmd" },
{ "var": "isMovingLeftHmd", "state": "strafeLeftHmd" }
]
},
{
@ -871,13 +961,13 @@
"data": {
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0.5, 1.4, 4.5],
"characteristicSpeeds": [0.5, 1.5, 2.5, 3.2, 4.5],
"alphaVar": "moveForwardAlpha",
"desiredSpeedVar": "moveForwardSpeed"
},
"children": [
{
"id": "walkFwdShort",
"id": "walkFwdShort_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/walk_short_fwd.fbx",
@ -889,7 +979,7 @@
"children": []
},
{
"id": "walkFwdNormal",
"id": "walkFwdNormal_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/walk_fwd.fbx",
@ -901,7 +991,31 @@
"children": []
},
{
"id": "walkFwdRun",
"id": "walkFwdFast_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/walk_fwd_fast.fbx",
"startFrame": 0.0,
"endFrame": 25.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "walkFwdJog_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/jog_fwd.fbx",
"startFrame": 0.0,
"endFrame": 25.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "walkFwdRun_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/run_fwd.fbx",
@ -926,13 +1040,25 @@
},
"children": []
},
{
"id": "idleSettle",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/settle_to_idle.fbx",
"startFrame": 1.0,
"endFrame": 48.0,
"timeScale": 1.0,
"loopFlag": false
},
"children": []
},
{
"id": "walkBwd",
"type": "blendLinearMove",
"data": {
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0.6, 1.45],
"characteristicSpeeds": [0.6, 1.7],
"alphaVar": "moveBackwardAlpha",
"desiredSpeedVar": "moveBackwardSpeed"
},
@ -953,9 +1079,9 @@
"id": "walkBwdNormal",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/walk_bwd.fbx",
"url": "qrc:///avatar/animations/walk_bwd_fast.fbx",
"startFrame": 0.0,
"endFrame": 36.0,
"endFrame": 27.0,
"timeScale": 1.0,
"loopFlag": true
},
@ -969,7 +1095,7 @@
"data": {
"url": "qrc:///avatar/animations/turn_left.fbx",
"startFrame": 0.0,
"endFrame": 28.0,
"endFrame": 32.0,
"timeScale": 1.0,
"loopFlag": true
},
@ -981,7 +1107,7 @@
"data": {
"url": "qrc:///avatar/animations/turn_left.fbx",
"startFrame": 0.0,
"endFrame": 30.0,
"endFrame": 32.0,
"timeScale": 1.0,
"loopFlag": true,
"mirrorFlag": true
@ -994,30 +1120,66 @@
"data": {
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0.2, 0.65],
"characteristicSpeeds": [0, 0.5, 1.5, 2.6, 3.0],
"alphaVar": "moveLateralAlpha",
"desiredSpeedVar": "moveLateralSpeed"
},
"children": [
{
"id": "strafeLeftShort",
"id": "strafeLeftShort_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/side_step_short_left.fbx",
"startFrame": 0.0,
"endFrame": 28.0,
"endFrame": 29.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "strafeLeftNormal",
"id": "strafeLeft_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/side_step_left.fbx",
"startFrame": 0.0,
"endFrame": 30.0,
"endFrame": 20.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "strafeLeftAnim_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/walk_left.fbx",
"startFrame": 0.0,
"endFrame": 33.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "strafeLeftFast_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/walk_left_fast.fbx",
"startFrame": 0.0,
"endFrame": 21.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "strafeLeftJog_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/jog_left.fbx",
"startFrame": 0.0,
"endFrame": 24.0,
"timeScale": 1.0,
"loopFlag": true
},
@ -1031,34 +1193,175 @@
"data": {
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0.2, 0.65],
"characteristicSpeeds": [0, 0.5, 1.5, 2.6, 3.0],
"alphaVar": "moveLateralAlpha",
"desiredSpeedVar": "moveLateralSpeed"
},
"children": [ {
"id": "stepRightShort_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/side_step_short_left.fbx",
"startFrame": 0.0,
"endFrame": 29.0,
"timeScale": 1.0,
"loopFlag": true,
"mirrorFlag": true
},
"children": []
},
{
"id": "stepRight_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/side_step_left.fbx",
"startFrame": 0.0,
"endFrame": 20.0,
"timeScale": 1.0,
"loopFlag": true,
"mirrorFlag": true
},
"children": []
},
{
"id": "strafeRight_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/walk_left.fbx",
"startFrame": 0.0,
"endFrame": 33.0,
"timeScale": 1.0,
"loopFlag": true,
"mirrorFlag": true
},
"children": []
},
{
"id": "strafeRightFast_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/walk_left_fast.fbx",
"startFrame": 0.0,
"endFrame": 21.0,
"timeScale": 1.0,
"loopFlag": true,
"mirrorFlag": true
},
"children": []
},
{
"id": "strafeRightJog_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/jog_left.fbx",
"startFrame": 0.0,
"endFrame": 24.0,
"timeScale": 1.0,
"loopFlag": true,
"mirrorFlag": true
},
"children": []
}
]
},
{
"id": "strafeLeftHmd",
"type": "blendLinearMove",
"data": {
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0, 0.5, 2.5],
"alphaVar": "moveLateralAlpha",
"desiredSpeedVar": "moveLateralSpeed"
},
"children": [
{
"id": "strafeRightShort",
"id": "stepLeftShort_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/side_step_short_right.fbx",
"url": "qrc:///avatar/animations/side_step_short_left.fbx",
"startFrame": 0.0,
"endFrame": 28.0,
"endFrame": 29.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "strafeRightNormal",
"id": "stepLeft_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/side_step_right.fbx",
"url": "qrc:///avatar/animations/side_step_left.fbx",
"startFrame": 0.0,
"endFrame": 30.0,
"endFrame": 20.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "strafeLeftAnim_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/side_step_left_fast.fbx",
"startFrame": 0.0,
"endFrame": 16.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
}
]
},
{
"id": "strafeRightHmd",
"type": "blendLinearMove",
"data": {
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0, 0.5, 2.5],
"alphaVar": "moveLateralAlpha",
"desiredSpeedVar": "moveLateralSpeed"
},
"children": [
{
"id": "stepRightShort_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/side_step_short_left.fbx",
"startFrame": 0.0,
"endFrame": 29.0,
"timeScale": 1.0,
"loopFlag": true,
"mirrorFlag": true
},
"children": []
},
{
"id": "stepRight_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/side_step_left.fbx",
"startFrame": 0.0,
"endFrame": 20.0,
"timeScale": 1.0,
"loopFlag": true,
"mirrorFlag": true
},
"children": []
},
{
"id": "strafeRightAnim_c",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/side_step_left_fast.fbx",
"startFrame": 0.0,
"endFrame": 16.0,
"timeScale": 1.0,
"loopFlag": true,
"mirrorFlag": true
},
"children": []
}
]
},

View file

@ -380,15 +380,21 @@
{
"properties": {
"acceleration": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"actionData": "",
"age": 14.011327743530273,
"ageAsText": "0 hours 0 minutes 14 seconds",
"age": 321.8835144042969,
"ageAsText": "0 hours 5 minutes 21 seconds",
"angularDamping": 0.39346998929977417,
"angularVelocity": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
@ -406,24 +412,36 @@
},
"boundingBox": {
"brn": {
"x": -0.20154684782028198,
"y": 0.03644842654466629,
"z": -0.2641940414905548
"blue": -0.03950843587517738,
"green": 0.20785385370254517,
"red": -0.04381325840950012,
"x": -0.04381325840950012,
"y": 0.20785385370254517,
"z": -0.03950843587517738
},
"center": {
"x": -0.030000001192092896,
"y": 0.12999820709228516,
"z": -0.07000023126602173
"blue": 0,
"green": 0.23000000417232513,
"red": 0,
"x": 0,
"y": 0.23000000417232513,
"z": 0
},
"dimensions": {
"x": 0.3430936932563782,
"y": 0.18709957599639893,
"z": 0.38838762044906616
"blue": 0.07901687175035477,
"green": 0.044292300939559937,
"red": 0.08762651681900024,
"x": 0.08762651681900024,
"y": 0.044292300939559937,
"z": 0.07901687175035477
},
"tfl": {
"x": 0.1415468454360962,
"y": 0.22354799509048462,
"z": 0.12419357895851135
"blue": 0.03950843587517738,
"green": 0.2521461546421051,
"red": 0.04381325840950012,
"x": 0.04381325840950012,
"y": 0.2521461546421051,
"z": 0.03950843587517738
}
},
"canCastShadow": true,
@ -441,189 +459,14 @@
"collisionless": false,
"collisionsWillMove": false,
"compoundShapeURL": "",
"created": "2018-06-06T17:25:42Z",
"damping": 0.39346998929977417,
"density": 1000,
"description": "",
"dimensions": {
"x": 0.33466479182243347,
"y": 0.16981728374958038,
"z": 0.38838762044906616
},
"dynamic": false,
"editionNumber": 19,
"entityInstanceNumber": 0,
"friction": 0.5,
"gravity": {
"x": 0,
"y": 0,
"z": 0
},
"href": "",
"id": "{6b0a2b08-e8e3-4d43-95cc-dfc4f7a4b0c9}",
"ignoreForCollisions": false,
"itemArtist": "jyoum",
"itemCategories": "Wearables",
"itemDescription": "A stylish and classic piece of headwear for your avatar.",
"itemLicense": "",
"itemName": "Fedora",
"jointRotations": [
],
"jointRotationsSet": [
],
"jointTranslations": [
],
"jointTranslationsSet": [
],
"lastEdited": 1528306032827319,
"lastEditedBy": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
"lifetime": -1,
"limitedRun": 4294967295,
"localPosition": {
"x": -0.030000008642673492,
"y": 0.12999820709228516,
"z": -0.07000023126602173
},
"localRotation": {
"w": 0.9996573328971863,
"x": 0,
"y": 0,
"z": 0.026176949962973595
},
"locked": false,
"marketplaceID": "11c4208d-15d7-4449-9758-a08da6dbd3dc",
"modelURL": "http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx",
"name": "",
"naturalDimensions": {
"x": 0.2765824794769287,
"y": 0.14034485816955566,
"z": 0.320981502532959
},
"naturalPosition": {
"x": 0.000143393874168396,
"y": 1.7460365295410156,
"z": 0.022502630949020386
},
"originalTextures": "{\n \"file5\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Base_Color.png\",\n \"file7\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Roughness.png\"\n}\n",
"owningAvatarID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
"parentID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
"parentJointIndex": 64,
"position": {
"x": -0.030000008642673492,
"y": 0.12999820709228516,
"z": -0.07000023126602173
},
"queryAACube": {
"scale": 1.6202316284179688,
"x": -0.5601736903190613,
"y": -10.668098449707031,
"z": -0.8933582305908203
},
"registrationPoint": {
"x": 0.5,
"y": 0.5,
"z": 0.5
},
"relayParentJoints": false,
"renderInfo": {
"drawCalls": 1,
"hasTransparent": false,
"texturesCount": 2,
"texturesSize": 327680,
"verticesCount": 719
},
"restitution": 0.5,
"rotation": {
"w": 0.9996573328971863,
"x": 0,
"y": 0,
"z": 0.026176949962973595
},
"script": "",
"scriptTimestamp": 0,
"serverScripts": "",
"shapeType": "box",
"staticCertificateVersion": 0,
"textures": "",
"type": "Model",
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
"velocity": {
"x": 0,
"y": 0,
"z": 0
},
"visible": true
}
},
{
"properties": {
"acceleration": {
"x": 0,
"y": 0,
"z": 0
},
"actionData": "",
"age": 14.011027336120605,
"ageAsText": "0 hours 0 minutes 14 seconds",
"angularDamping": 0.39346998929977417,
"angularVelocity": {
"x": 0,
"y": 0,
"z": 0
},
"animation": {
"allowTranslation": true,
"currentFrame": 0,
"firstFrame": 0,
"fps": 30,
"hold": false,
"lastFrame": 100000,
"loop": true,
"running": false,
"url": ""
},
"boundingBox": {
"brn": {
"x": -0.04381517320871353,
"y": 0.20789726078510284,
"z": -0.0394962802529335
},
"center": {
"x": -1.9073486328125e-06,
"y": 0.2300434112548828,
"z": 1.2159347534179688e-05
},
"dimensions": {
"x": 0.08762653172016144,
"y": 0.04429228603839874,
"z": 0.07901687920093536
},
"tfl": {
"x": 0.043811358511447906,
"y": 0.2521895468235016,
"z": 0.03952059894800186
}
},
"canCastShadow": true,
"certificateID": "",
"clientOnly": true,
"cloneAvatarEntity": false,
"cloneDynamic": false,
"cloneLifetime": 300,
"cloneLimit": 0,
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
"cloneable": false,
"collidesWith": "",
"collisionMask": 0,
"collisionSoundURL": "",
"collisionless": false,
"collisionsWillMove": false,
"compoundShapeURL": "",
"created": "2018-06-06T17:25:42Z",
"created": "2018-07-26T23:56:46Z",
"damping": 0.39346998929977417,
"density": 1000,
"description": "",
"dimensions": {
"blue": 0.07229919731616974,
"green": 0.06644226610660553,
"red": 0.03022606298327446,
"x": 0.03022606298327446,
"y": 0.06644226610660553,
"z": 0.07229919731616974
@ -633,12 +476,15 @@
"entityInstanceNumber": 0,
"friction": 0.5,
"gravity": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"href": "",
"id": "{d018c6ea-b2f4-441e-85e1-d17373ae6f34}",
"id": "{03053239-bb37-4c51-a013-a1772baaeed5}",
"ignoreForCollisions": false,
"itemArtist": "jyoum",
"itemCategories": "Wearables",
@ -653,51 +499,66 @@
],
"jointTranslationsSet": [
],
"lastEdited": 1528306032505220,
"lastEditedBy": "{b46f9c9e-4cd3-4964-96d6-cf3954abb908}",
"lastEdited": 1532649569894305,
"lastEditedBy": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
"lifetime": -1,
"limitedRun": 4294967295,
"localPosition": {
"x": -1.9073486328125e-06,
"y": 0.2300434112548828,
"z": 1.2159347534179688e-05
"blue": 0,
"green": 0.23000000417232513,
"red": 0,
"x": 0,
"y": 0.23000000417232513,
"z": 0
},
"localRotation": {
"w": 0.5910987257957458,
"x": -0.48726412653923035,
"y": -0.4088631868362427,
"z": 0.49599069356918335
"w": 0.5910986065864563,
"x": -0.48726415634155273,
"y": -0.4088630974292755,
"z": 0.49599072337150574
},
"locked": false,
"marketplaceID": "0685794d-fddb-4bad-a608-6d7789ceda90",
"modelURL": "http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx",
"name": "Scifi Watch by Jimi",
"naturalDimensions": {
"blue": 0.055614765733480453,
"green": 0.0511094331741333,
"red": 0.023250818252563477,
"x": 0.023250818252563477,
"y": 0.0511094331741333,
"z": 0.055614765733480453
},
"naturalPosition": {
"blue": -0.06031447649002075,
"green": 1.4500460624694824,
"red": 0.6493338942527771,
"x": 0.6493338942527771,
"y": 1.4500460624694824,
"z": -0.06031447649002075
},
"originalTextures": "{\n \"file4\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Base_Color.png\",\n \"file5\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Normal_OpenGL.png\",\n \"file6\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Metallic.png\",\n \"file7\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Roughness.png\",\n \"file8\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Emissive.png\"\n}\n",
"owningAvatarID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
"parentID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
"owningAvatarID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
"parentID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
"parentJointIndex": 16,
"position": {
"x": -1.9073486328125e-06,
"y": 0.2300434112548828,
"z": 1.2159347534179688e-05
"blue": 0,
"green": 0.23000000417232513,
"red": 0,
"x": 0,
"y": 0.23000000417232513,
"z": 0
},
"queryAACube": {
"scale": 0.3082179129123688,
"x": -0.19203892350196838,
"y": -10.429610252380371,
"z": -0.4076632857322693
"x": 495.7716979980469,
"y": 498.345703125,
"z": 498.52044677734375
},
"registrationPoint": {
"blue": 0.5,
"green": 0.5,
"red": 0.5,
"x": 0.5,
"y": 0.5,
"z": 0.5
@ -712,10 +573,10 @@
},
"restitution": 0.5,
"rotation": {
"w": 0.5910987257957458,
"x": -0.48726412653923035,
"y": -0.4088631868362427,
"z": 0.49599069356918335
"w": 0.5910986065864563,
"x": -0.48726415634155273,
"y": -0.4088630974292755,
"z": 0.49599072337150574
},
"script": "",
"scriptTimestamp": 0,
@ -726,6 +587,229 @@
"type": "Model",
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"[LR]ForeArm\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
"velocity": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"visible": true
}
},
{
"properties": {
"acceleration": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"actionData": "",
"age": 308.8044128417969,
"ageAsText": "0 hours 5 minutes 8 seconds",
"angularDamping": 0.39346998929977417,
"angularVelocity": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"animation": {
"allowTranslation": true,
"currentFrame": 0,
"firstFrame": 0,
"fps": 30,
"hold": false,
"lastFrame": 100000,
"loop": true,
"running": false,
"url": ""
},
"boundingBox": {
"brn": {
"blue": -0.2340194433927536,
"green": -0.07067721337080002,
"red": -0.17002610862255096,
"x": -0.17002610862255096,
"y": -0.07067721337080002,
"z": -0.2340194433927536
},
"center": {
"blue": -0.039825439453125,
"green": 0.02001953125,
"red": 0.0001678466796875,
"x": 0.0001678466796875,
"y": 0.02001953125,
"z": -0.039825439453125
},
"dimensions": {
"blue": 0.3883880078792572,
"green": 0.18139348924160004,
"red": 0.34038791060447693,
"x": 0.34038791060447693,
"y": 0.18139348924160004,
"z": 0.3883880078792572
},
"tfl": {
"blue": 0.1543685644865036,
"green": 0.11071627587080002,
"red": 0.17036180198192596,
"x": 0.17036180198192596,
"y": 0.11071627587080002,
"z": 0.1543685644865036
}
},
"canCastShadow": true,
"certificateID": "",
"clientOnly": true,
"cloneAvatarEntity": false,
"cloneDynamic": false,
"cloneLifetime": 300,
"cloneLimit": 0,
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
"cloneable": false,
"collidesWith": "",
"collisionMask": 0,
"collisionSoundURL": "",
"collisionless": false,
"collisionsWillMove": false,
"compoundShapeURL": "",
"created": "2018-07-26T23:56:46Z",
"damping": 0.39346998929977417,
"density": 1000,
"description": "",
"dimensions": {
"blue": 0.38838762044906616,
"green": 0.16981728374958038,
"red": 0.33466479182243347,
"x": 0.33466479182243347,
"y": 0.16981728374958038,
"z": 0.38838762044906616
},
"dynamic": false,
"editionNumber": 18,
"entityInstanceNumber": 0,
"friction": 0.5,
"gravity": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"href": "",
"id": "{1bf231ce-3913-4c53-be3c-b1f4094dac51}",
"ignoreForCollisions": false,
"itemArtist": "jyoum",
"itemCategories": "Wearables",
"itemDescription": "A stylish and classic piece of headwear for your avatar.",
"itemLicense": "",
"itemName": "Fedora",
"jointRotations": [
],
"jointRotationsSet": [
],
"jointTranslations": [
],
"jointTranslationsSet": [
],
"lastEdited": 1532649698129709,
"lastEditedBy": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
"lifetime": -1,
"limitedRun": 4294967295,
"localPosition": {
"blue": -0.039825439453125,
"green": 0.02001953125,
"red": 0.0001678466796875,
"x": 0.0001678466796875,
"y": 0.02001953125,
"z": -0.039825439453125
},
"localRotation": {
"w": 0.9998477101325989,
"x": -9.898545982878204e-09,
"y": 5.670873406415922e-07,
"z": 0.017452405765652657
},
"locked": false,
"marketplaceID": "11c4208d-15d7-4449-9758-a08da6dbd3dc",
"modelURL": "http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx",
"name": "",
"naturalDimensions": {
"blue": 0.320981502532959,
"green": 0.14034485816955566,
"red": 0.2765824794769287,
"x": 0.2765824794769287,
"y": 0.14034485816955566,
"z": 0.320981502532959
},
"naturalPosition": {
"blue": 0.022502630949020386,
"green": 1.7460365295410156,
"red": 0.000143393874168396,
"x": 0.000143393874168396,
"y": 1.7460365295410156,
"z": 0.022502630949020386
},
"originalTextures": "{\n \"file5\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Base_Color.png\",\n \"file7\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Roughness.png\"\n}\n",
"owningAvatarID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
"parentID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
"parentJointIndex": 66,
"position": {
"blue": -0.039825439453125,
"green": 0.02001953125,
"red": 0.0001678466796875,
"x": 0.0001678466796875,
"y": 0.02001953125,
"z": -0.039825439453125
},
"queryAACube": {
"scale": 1.6202316284179688,
"x": 495.21051025390625,
"y": 498.5577697753906,
"z": 497.6370849609375
},
"registrationPoint": {
"blue": 0.5,
"green": 0.5,
"red": 0.5,
"x": 0.5,
"y": 0.5,
"z": 0.5
},
"relayParentJoints": false,
"renderInfo": {
"drawCalls": 1,
"hasTransparent": false,
"texturesCount": 2,
"texturesSize": 327680,
"verticesCount": 719
},
"restitution": 0.5,
"rotation": {
"w": 0.9998477101325989,
"x": -9.898545982878204e-09,
"y": 5.670873406415922e-07,
"z": 0.017452405765652657
},
"script": "",
"scriptTimestamp": 0,
"serverScripts": "",
"shapeType": "box",
"staticCertificateVersion": 0,
"textures": "",
"type": "Model",
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
"velocity": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0

View file

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 100 100.8" style="enable-background:new 0 0 100 100.8;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M26.7,83.9c7.3,1.2,14.8,1.8,22.1,1.8c0.4,0,0.8,0,1.2,0c7.8-0.1,15.6-0.8,23.4-2.2l0,0
c5.7-1.1,11.3-6.6,12.5-12.3C87.3,64.2,88,57,88,50s-0.7-14.2-2.1-21.2c-1.2-5.6-6.8-11.1-12.5-12.2c-7.7-1.4-15.6-2.2-23.4-2.2
c-7.7-0.1-15.6,0.5-23.4,1.8c-5.7,1-11.4,6.5-12.6,12.3c-1.4,7.2-2.1,14.4-2.1,21.6s0.7,14.4,2.1,21.7
C15.3,77.4,20.9,82.9,26.7,83.9z M20.9,29.8c0.6-2.9,4-6.3,6.9-6.8c7-1.1,14-1.7,21-1.7c0.4,0,0.8,0,1.2,0
c7.4,0.1,14.8,0.8,22.1,2.1c2.9,0.6,6.4,3.9,6.9,6.7c1.3,6.6,1.9,13.3,1.9,19.9c0,6.6-0.6,13.3-1.9,19.8c-0.6,2.8-4,6.2-6.9,6.8
c-7.3,1.3-14.8,2.1-22.1,2.1c-7.4,0.1-14.8-0.5-22.1-1.7c-2.9-0.5-6.3-3.9-6.9-6.7c-1.3-6.7-2-13.5-2-20.3
C19,43.3,19.6,36.4,20.9,29.8z"/>
<path class="st0" d="M32.3,61.4c-0.5,1.3-0.1,2.8,0.9,3.8c0.3,0.3,7.2,6.6,15.9,6.6c0.8,0,1.7-0.1,2.6-0.2
c9.8-1.5,15.5-11.1,15.8-11.5c0.7-1.2,0.6-2.8-0.2-3.9c-0.9-1.1-2.3-1.6-3.7-1.3c-9.2,2.5-18.6,3.9-28.1,4.2
C34,59.1,32.8,60,32.3,61.4z"/>
<circle class="st0" cx="36.5" cy="42.8" r="9"/>
<path class="st0" d="M61.4,44.1h6.1c1.9,0,3.3-1.5,3.3-3.3c0-1.9-1.5-3.3-3.3-3.3h-6.1c-1.9,0-3.3,1.5-3.3,3.3
C58.1,42.7,59.6,44.1,61.4,44.1z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<g>
<path d="M39.8,10.7c0.8,0.1,1.5,0.7,1.8,1.4s0.2,1.6-0.2,2.2c-0.3,0.4-0.2,1,0.3,1.4c0.2,0.1,0.4,0.1,0.5,0.1
c0.3,0,0.6-0.1,0.6-0.2l0.1-0.1l0.1-0.1c0.8-1.2,1-2.8,0.4-4.1C42.8,10,41.6,9,40.1,8.8c-0.3-0.1-0.5,0-0.7,0.1
C39.2,9,39,9.3,39,9.5C38.8,10.1,39.2,10.6,39.8,10.7z"/>
<path d="M42.5,8.1c1.2,0.2,2.2,1,2.7,2.1c0.4,1.1,0.3,2.4-0.3,3.3c-0.2,0.2-0.2,0.5-0.2,0.7s0.2,0.5,0.5,0.7
c0.2,0.1,0.4,0.1,0.5,0.1c0.4,0,0.7-0.2,0.8-0.5c1-1.5,1.2-3.4,0.5-5.1s-2.3-3-4.2-3.3c-0.5-0.1-1,0.2-1.1,0.8
C41.6,7.5,41.9,8,42.5,8.1z M42.1,7L42.1,7L42.1,7L42.1,7z"/>
<path d="M6.7,14.3c-0.4-0.6-0.5-1.5-0.2-2.2c0.3-0.8,1-1.3,1.9-1.4c0.6-0.1,0.9-0.6,0.8-1.1C9.1,9.3,9,9,8.7,8.9
C8.5,8.8,8.3,8.7,8,8.8C6.5,9,5.3,10,4.7,11.4s-0.4,2.9,0.4,4.1l0.1,0.1l0.1,0.1c0.1,0,0.4,0.2,0.6,0.2c0.1,0,0.3,0,0.6-0.2
C6.9,15.4,7,14.8,6.7,14.3z"/>
<path d="M5.6,8.1C5.9,8,6.1,7.9,6.3,7.7C6.5,7.5,6.5,7.2,6.5,7C6.4,6.7,6.2,6.4,6,6.3C5.8,6.1,5.6,6.1,5.3,6.1
C3.5,6.5,1.9,7.7,1.1,9.5c-0.7,1.7-0.5,3.6,0.5,5.1c0.1,0.3,0.4,0.5,0.8,0.5c0.1,0,0.3,0,0.6-0.2c0.2-0.2,0.4-0.4,0.4-0.6
c0-0.3,0-0.5-0.2-0.7c-0.6-1-0.8-2.2-0.3-3.3C3.4,9.1,4.4,8.3,5.6,8.1z"/>
<path d="M48.8,25.1c-0.7-0.7-1.8-0.7-2.6-0.2c-0.4,0.3-0.7,0.5-1,0.8L44.9,26c-0.3,0.3-0.6,0.5-0.9,0.8c-0.6,0.6-1.2,1.1-1.9,1.6
c-1.2,0.8-2.7,1-4.1,0.5c-1.3-0.5-2.3-1.6-2.6-3c-0.1-0.6-0.2-1.3-0.2-2c-0.2-2.8-0.3-5.6-0.5-8.5l-0.3-5.2c0-0.7-0.1-1.4-0.2-2.2
c-0.1-0.8-0.5-1.5-1.1-1.8s-1.2-0.3-1.8,0c-1,0.5-1.1,1.6-1.1,2.1c-0.1,1.4-0.2,2.8-0.2,4.2c0,0.9-0.1,1.8-0.1,2.7
c-0.1,2.4-0.3,4.8-0.4,7.2v0.2c0,0.8-0.1,0.9-0.5,0.9s-0.4-0.1-0.6-0.9L27,16.4c-0.8-3.3-1.5-6.7-2.3-10c-0.3-1.3-1.2-1.9-2.4-1.7
c-1.1,0.2-1.7,1.2-1.6,2.4C20.7,8,20.9,9,21,9.9c0.1,0.6,0.2,1.3,0.2,1.9c0.5,3.8,0.9,7.6,1.4,11.3l0.1,1.1
c0.1,0.8-0.1,0.9-0.4,0.9c-0.3,0.1-0.4,0.1-0.7-0.6L20,20.7c-1.2-2.9-2.5-5.7-3.7-8.6c-0.8-1.9-2-1.9-2.8-1.5
c-0.6,0.2-1.5,0.9-0.9,2.9c0.3,1,0.6,1.9,0.9,2.9c0.3,0.9,0.5,1.8,0.8,2.7c0.5,1.8,1,3.5,1.6,5.3l1.1,3.7c0.2,0.6,0,0.7-0.2,0.8
c-0.1,0.1-0.3,0.1-0.7-0.4c-0.1-0.1-0.2-0.2-0.2-0.3l-3.8-5.7c-0.5-0.7-0.9-1.4-1.4-2.1c-0.6-0.8-1.4-1-2.3-0.6s-1.3,1.1-1.2,2
c0.1,0.5,0.3,0.9,0.4,1.2c0.8,1.6,1.6,3.2,2.4,4.8c2.1,4.2,4.2,8.5,6.4,12.8c2.3,4.5,6.2,7,11.5,7.3l0,0l0,0
c4.7-0.2,8.2-1.9,10.7-5.2c2.1-2.8,4.2-5.7,6.2-8.5c0.9-1.3,1.8-2.5,2.7-3.7c0.2-0.3,0.4-0.5,0.6-0.8c0.4-0.6,0.8-1.1,1.2-1.7
C49.5,26.7,49.5,25.8,48.8,25.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M39.8,10.7c0.8,0.1,1.5,0.7,1.8,1.4s0.2,1.6-0.2,2.2c-0.3,0.4-0.2,1,0.3,1.4c0.2,0.1,0.4,0.1,0.5,0.1
c0.3,0,0.6-0.1,0.6-0.2l0.1-0.1l0.1-0.1c0.8-1.2,1-2.8,0.4-4.1C42.8,10,41.6,9,40.1,8.8c-0.3-0.1-0.5,0-0.7,0.1
C39.2,9,39,9.3,39,9.5C38.8,10.1,39.2,10.6,39.8,10.7z"/>
<path class="st0" d="M42.5,8.1c1.2,0.2,2.2,1,2.7,2.1c0.4,1.1,0.3,2.4-0.3,3.3c-0.2,0.2-0.2,0.5-0.2,0.7s0.2,0.5,0.5,0.7
c0.2,0.1,0.4,0.1,0.5,0.1c0.4,0,0.7-0.2,0.8-0.5c1-1.5,1.2-3.4,0.5-5.1s-2.3-3-4.2-3.3c-0.5-0.1-1,0.2-1.1,0.8
C41.6,7.5,41.9,8,42.5,8.1z M42.1,7L42.1,7L42.1,7L42.1,7z"/>
<path class="st0" d="M6.7,14.3c-0.4-0.6-0.5-1.5-0.2-2.2c0.3-0.8,1-1.3,1.9-1.4c0.6-0.1,0.9-0.6,0.8-1.1C9.1,9.3,9,9,8.7,8.9
C8.5,8.8,8.3,8.7,8,8.8C6.5,9,5.3,10,4.7,11.4s-0.4,2.9,0.4,4.1l0.1,0.1l0.1,0.1c0.1,0,0.4,0.2,0.6,0.2c0.1,0,0.3,0,0.6-0.2
C6.9,15.4,7,14.8,6.7,14.3z"/>
<path class="st0" d="M5.6,8.1C5.9,8,6.1,7.9,6.3,7.7C6.5,7.5,6.5,7.2,6.5,7C6.4,6.7,6.2,6.4,6,6.3C5.8,6.1,5.6,6.1,5.3,6.1
C3.5,6.5,1.9,7.7,1.1,9.5c-0.7,1.7-0.5,3.6,0.5,5.1c0.1,0.3,0.4,0.5,0.8,0.5c0.1,0,0.3,0,0.6-0.2c0.2-0.2,0.4-0.4,0.4-0.6
c0-0.3,0-0.5-0.2-0.7c-0.6-1-0.8-2.2-0.3-3.3C3.4,9.1,4.4,8.3,5.6,8.1z"/>
<path class="st0" d="M48.8,25.1c-0.7-0.7-1.8-0.7-2.6-0.2c-0.4,0.3-0.7,0.5-1,0.8L44.9,26c-0.3,0.3-0.6,0.5-0.9,0.8
c-0.6,0.6-1.2,1.1-1.9,1.6c-1.2,0.8-2.7,1-4.1,0.5c-1.3-0.5-2.3-1.6-2.6-3c-0.1-0.6-0.2-1.3-0.2-2c-0.2-2.8-0.3-5.6-0.5-8.5
l-0.3-5.2c0-0.7-0.1-1.4-0.2-2.2c-0.1-0.8-0.5-1.5-1.1-1.8s-1.2-0.3-1.8,0c-1,0.5-1.1,1.6-1.1,2.1c-0.1,1.4-0.2,2.8-0.2,4.2
c0,0.9-0.1,1.8-0.1,2.7c-0.1,2.4-0.3,4.8-0.4,7.2v0.2c0,0.8-0.1,0.9-0.5,0.9s-0.4-0.1-0.6-0.9L27,16.4c-0.8-3.3-1.5-6.7-2.3-10
c-0.3-1.3-1.2-1.9-2.4-1.7c-1.1,0.2-1.7,1.2-1.6,2.4C20.7,8,20.9,9,21,9.9c0.1,0.6,0.2,1.3,0.2,1.9c0.5,3.8,0.9,7.6,1.4,11.3
l0.1,1.1c0.1,0.8-0.1,0.9-0.4,0.9c-0.3,0.1-0.4,0.1-0.7-0.6L20,20.7c-1.2-2.9-2.5-5.7-3.7-8.6c-0.8-1.9-2-1.9-2.8-1.5
c-0.6,0.2-1.5,0.9-0.9,2.9c0.3,1,0.6,1.9,0.9,2.9c0.3,0.9,0.5,1.8,0.8,2.7c0.5,1.8,1,3.5,1.6,5.3l1.1,3.7c0.2,0.6,0,0.7-0.2,0.8
c-0.1,0.1-0.3,0.1-0.7-0.4c-0.1-0.1-0.2-0.2-0.2-0.3l-3.8-5.7c-0.5-0.7-0.9-1.4-1.4-2.1c-0.6-0.8-1.4-1-2.3-0.6s-1.3,1.1-1.2,2
c0.1,0.5,0.3,0.9,0.4,1.2c0.8,1.6,1.6,3.2,2.4,4.8c2.1,4.2,4.2,8.5,6.4,12.8c2.3,4.5,6.2,7,11.5,7.3l0,0l0,0
c4.7-0.2,8.2-1.9,10.7-5.2c2.1-2.8,4.2-5.7,6.2-8.5c0.9-1.3,1.8-2.5,2.7-3.7c0.2-0.3,0.4-0.5,0.6-0.8c0.4-0.6,0.8-1.1,1.2-1.7
C49.5,26.7,49.5,25.8,48.8,25.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -173,6 +173,21 @@ Item {
StatText {
text: "Yaw: " + root.yaw.toFixed(1)
}
StatText {
visible: root.animStackNames.length > 0;
text: "Anim Stack Names:"
}
ListView {
width: geoCol.width
height: root.animStackNames.length * 15
visible: root.animStackNames.length > 0;
model: root.animStackNames
delegate: StatText {
text: modelData.length > 30
? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22)
: modelData
}
}
StatText {
visible: root.expanded;
text: "Avatar Mixer In: " + root.avatarMixerInKbps + " kbps, " +

View file

@ -471,7 +471,6 @@ TabletModalWindow {
bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height
}
headerVisible: !selectDirectory
onClicked: navigateToRow(row);
onDoubleClicked: navigateToRow(row);
focus: true
Keys.onReturnPressed: navigateToCurrentRow();

View file

@ -186,6 +186,8 @@ Windows.ScrollingWindow {
return;
}
var grabbable = MenuInterface.isOptionChecked("Create Entities As Grabbable (except Zones, Particles, and Lights)");
if (defaultURL.endsWith(".jpg") || defaultURL.endsWith(".png")) {
var name = assetProxyModel.data(treeView.selection.currentIndex);
var modelURL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx";
@ -195,7 +197,7 @@ Windows.ScrollingWindow {
var collisionless = true;
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)));
var gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0);
Entities.addModelEntity(name, modelURL, textures, shapeType, dynamic, collisionless, position, gravity);
Entities.addModelEntity(name, modelURL, textures, shapeType, dynamic, collisionless, grabbable, position, gravity);
} else {
var SHAPE_TYPE_NONE = 0;
var SHAPE_TYPE_SIMPLE_HULL = 1;
@ -281,7 +283,7 @@ Windows.ScrollingWindow {
print("Asset browser - adding asset " + url + " (" + name + ") to world.");
// Entities.addEntity doesn't work from QML, so we use this.
Entities.addModelEntity(name, url, "", shapeType, dynamic, collisionless, addPosition, gravity);
Entities.addModelEntity(name, url, "", shapeType, dynamic, collisionless, grabbable, addPosition, gravity);
}
}
});

View file

@ -41,6 +41,8 @@ Column {
property var goFunction: null;
property var http: null;
property bool autoScrollTimerEnabled: false;
HifiConstants { id: hifi }
Component.onCompleted: suggestions.getFirstPage();
HifiModels.PSFListModel {
@ -88,7 +90,9 @@ Column {
online_users: data.details.connections || data.details.concurrency || 0,
// Server currently doesn't give isStacked (undefined). Could give bool.
drillDownToPlace: data.is_stacked || (data.action === 'concurrency'),
isStacked: !!data.is_stacked
isStacked: !!data.is_stacked,
time_before_autoscroll_ms: data.hold_time || 3000
};
}
@ -102,9 +106,12 @@ Column {
id: scroll;
model: suggestions;
orientation: ListView.Horizontal;
highlightFollowsCurrentItem: false
highlightMoveDuration: -1;
highlightMoveVelocity: -1;
highlightFollowsCurrentItem: true;
preferredHighlightBegin: 0;
preferredHighlightEnd: cardWidth;
highlightRangeMode: ListView.StrictlyEnforceRange;
highlightMoveDuration: 800;
highlightMoveVelocity: 1;
currentIndex: -1;
spacing: 12;
@ -134,8 +141,49 @@ Column {
textSizeSmall: root.textSizeSmall;
stackShadowNarrowing: root.stackShadowNarrowing;
shadowHeight: root.stackedCardShadowHeight;
hoverThunk: function () { hovered = true }
unhoverThunk: function () { hovered = false }
hoverThunk: function () {
hovered = true;
if(root.autoScrollTimerEnabled) {
autoScrollTimer.stop();
}
}
unhoverThunk: function () {
hovered = false;
if(root.autoScrollTimerEnabled) {
autoScrollTimer.start();
}
}
}
onCountChanged: {
if (scroll.currentIndex === -1 && scroll.count > 0 && root.autoScrollTimerEnabled) {
scroll.currentIndex = 0;
autoScrollTimer.interval = suggestions.get(scroll.currentIndex).time_before_autoscroll_ms;
autoScrollTimer.start();
}
}
onCurrentIndexChanged: {
if (root.autoScrollTimerEnabled) {
autoScrollTimer.interval = suggestions.get(scroll.currentIndex).time_before_autoscroll_ms;
autoScrollTimer.start();
}
}
}
Timer {
id: autoScrollTimer;
interval: 3000;
running: false;
repeat: false;
onTriggered: {
if (scroll.currentIndex !== -1) {
if (scroll.currentIndex === scroll.count - 1) {
scroll.currentIndex = 0;
} else {
scroll.currentIndex++;
}
}
}
}
}

View file

@ -526,11 +526,13 @@ Item {
anchors.left: nameCardVUMeter.left;
// Properties
visible: (isMyCard || (selected && pal.activeTab == "nearbyTab")) && isPresent;
value: Users.getAvatarGain(uuid)
minimumValue: -60.0
maximumValue: 20.0
stepSize: 5
updateValueWhileDragging: true
Component.onCompleted: {
value = Users.getAvatarGain(uuid);
}
onValueChanged: {
updateGainFromQML(uuid, value, false);
}
@ -587,9 +589,11 @@ Item {
}
function updateGainFromQML(avatarUuid, sliderValue, isReleased) {
Users.setAvatarGain(avatarUuid, sliderValue);
if (isReleased) {
UserActivityLogger.palAction("avatar_gain_changed", avatarUuid);
if (Users.getAvatarGain(avatarUuid) != sliderValue) {
Users.setAvatarGain(avatarUuid, sliderValue);
if (isReleased) {
UserActivityLogger.palAction("avatar_gain_changed", avatarUuid);
}
}
}

View file

@ -1046,12 +1046,13 @@ Rectangle {
enabled: myData.userName !== "Unknown user" && !userInfoViewer.visible;
hoverEnabled: true;
onClicked: {
// TODO: Change language from "Happening Now" to something else (or remove entirely)
popupComboDialog("Set your availability:",
availabilityComboBox.availabilityStrings,
["Your username will be visible in everyone's 'Nearby' list. Anyone will be able to jump to your location from within the 'Nearby' list.",
"Your location will be visible in the 'Connections' list only for those with whom you are connected or friends. They'll be able to jump to your location if the domain allows.",
"Your location will be visible in the 'Connections' list only for those with whom you are friends. They'll be able to jump to your location if the domain allows. You will only receive 'Happening Now' notifications in 'Go To' from friends.",
"You will appear offline in the 'Connections' list, and you will not receive 'Happening Now' notifications in 'Go To'."],
"Your location will be visible in the 'Connections' list only for those with whom you are connected or friends. They'll be able to jump to your location if the domain allows, and you will see 'Snaps' Blasts from them in 'Go To'.",
"Your location will be visible in the 'Connections' list only for those with whom you are friends. They'll be able to jump to your location if the domain allows, and you will see 'Snaps' Blasts from them in 'Go To'",
"You will appear offline in the 'Connections' list, and you will not receive Snaps Blasts from connections or friends in 'Go To'."],
["all", "connections", "friends", "none"]);
}
onEntered: availabilityComboBox.color = hifi.colors.lightGrayText;

View file

@ -41,8 +41,8 @@ MessageBox {
popup.button1text = 'CANCEL'
popup.titleText = 'Get Wearables'
popup.bodyText = 'Buy wearables from <b><a href="app://marketplace">Marketplace.</a></b>' + '<br/>' +
'Wear wearables from <b><a href="app://purchases">My Purchases.</a></b>' + '<br/>' + '<br/>' +
'Visit “AvatarIsland” to get wearables'
'Use wearables in <b><a href="app://purchases">My Purchases.</a></b>' + '<br/>' + '<br/>' +
'Visit “AvatarIsland” to get wearables.'
popup.imageSource = getWearablesUrl;
popup.onButton2Clicked = function() {
@ -102,7 +102,7 @@ MessageBox {
popup.titleText = 'Get Avatars'
popup.bodyText = 'Buy avatars from <b><a href="app://marketplace">Marketplace.</a></b>' + '<br/>' +
'Wear avatars from <b><a href="app://purchases">My Purchases.</a></b>' + '<br/>' + '<br/>' +
'Wear avatars in <b><a href="app://purchases">My Purchases.</a></b>' + '<br/>' + '<br/>' +
'Visit “BodyMart” to get free avatars.'
popup.imageSource = getAvatarsUrl;

View file

@ -186,6 +186,8 @@ Rectangle {
return;
}
var grabbable = MenuInterface.isOptionChecked("Create Entities As Grabbable (except Zones, Particles, and Lights)");
if (defaultURL.endsWith(".jpg") || defaultURL.endsWith(".png")) {
var name = assetProxyModel.data(treeView.selection.currentIndex);
var modelURL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx";
@ -195,7 +197,7 @@ Rectangle {
var collisionless = true;
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)));
var gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0);
Entities.addModelEntity(name, modelURL, textures, shapeType, dynamic, collisionless, position, gravity);
Entities.addModelEntity(name, modelURL, textures, shapeType, dynamic, collisionless, grabbable, position, gravity);
} else {
var SHAPE_TYPE_NONE = 0;
var SHAPE_TYPE_SIMPLE_HULL = 1;
@ -281,7 +283,7 @@ Rectangle {
print("Asset browser - adding asset " + url + " (" + name + ") to world.");
// Entities.addEntity doesn't work from QML, so we use this.
Entities.addModelEntity(name, url, "", shapeType, dynamic, collisionless, addPosition, gravity);
Entities.addModelEntity(name, url, "", shapeType, dynamic, collisionless, grabbable, addPosition, gravity);
}
}
});

View file

@ -57,9 +57,9 @@ ListModel {
// Not normally set directly, but rather by giving a truthy argument to getFirstPage(true);
property bool delayedClear: false;
function resetModel() {
if (!delayedClear) { root.clear(); }
currentPageToRetrieve = 1;
retrievedAtLeastOnePage = false;
if (!delayedClear) { root.clear(); }
totalPages = 0;
totalEntries = 0;
}
@ -94,11 +94,13 @@ ListModel {
function needsMoreHorizontalResults() {
return flickable
&& currentPageToRetrieve > 0
&& retrievedAtLeastOnePage
&& flickable.contentWidth < flickable.width;
}
function needsMoreVerticalResults() {
return flickable
&& currentPageToRetrieve > 0
&& retrievedAtLeastOnePage
&& flickable.contentHeight < flickable.height;
}
function getNextPageIfNotEnoughHorizontalResults() {

View file

@ -320,11 +320,12 @@ StackView {
width: parent.width;
cardWidth: 312 + (2 * 4);
cardHeight: 163 + (2 * 4);
labelText: 'HAPPENING NOW';
labelText: 'FEATURED';
actions: 'announcement';
filter: addressLine.text;
goFunction: goCard;
http: http;
autoScrollTimerEnabled: true;
}
Feed {
id: places;

View file

@ -45,9 +45,7 @@ QString AboutUtil::getQtVersion() const {
}
void AboutUtil::openUrl(const QString& url) const {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system");
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
auto hmd = DependencyManager::get<HMDScriptingInterface>();
auto offscreenUi = DependencyManager::get<OffscreenUi>();

View file

@ -1139,8 +1139,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// setup a timer for domain-server check ins
QTimer* domainCheckInTimer = new QTimer(this);
connect(domainCheckInTimer, &QTimer::timeout, [this, nodeList] {
if (!isServerlessMode()) {
QWeakPointer<NodeList> nodeListWeak = nodeList;
connect(domainCheckInTimer, &QTimer::timeout, [this, nodeListWeak] {
auto nodeList = nodeListWeak.lock();
if (!isServerlessMode() && nodeList) {
nodeList->sendDomainServerCheckIn();
}
});
@ -1150,33 +1152,34 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
domainCheckInTimer->deleteLater();
});
{
auto audioIO = DependencyManager::get<AudioClient>().data();
audioIO->setPositionGetter([] {
auto avatarManager = DependencyManager::get<AvatarManager>();
auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;
auto audioIO = DependencyManager::get<AudioClient>();
audioIO->setPositionGetter([]{
auto avatarManager = DependencyManager::get<AvatarManager>();
auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;
return myAvatar ? myAvatar->getPositionForAudio() : Vectors::ZERO;
});
audioIO->setOrientationGetter([] {
auto avatarManager = DependencyManager::get<AvatarManager>();
auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;
return myAvatar ? myAvatar->getPositionForAudio() : Vectors::ZERO;
});
audioIO->setOrientationGetter([]{
auto avatarManager = DependencyManager::get<AvatarManager>();
auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;
return myAvatar ? myAvatar->getOrientationForAudio() : Quaternions::IDENTITY;
});
return myAvatar ? myAvatar->getOrientationForAudio() : Quaternions::IDENTITY;
});
recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [&audioIO](recording::Frame::ConstPointer frame) {
audioIO->handleRecordedAudioInput(frame->data);
});
recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [=](recording::Frame::ConstPointer frame) {
audioIO->handleRecordedAudioInput(frame->data);
});
connect(audioIO.data(), &AudioClient::inputReceived, [](const QByteArray& audio){
static auto recorder = DependencyManager::get<recording::Recorder>();
if (recorder->isRecording()) {
static const recording::FrameType AUDIO_FRAME_TYPE = recording::Frame::registerFrameType(AudioConstants::getAudioFrameName());
recorder->recordFrame(AUDIO_FRAME_TYPE, audio);
}
});
audioIO->startThread();
connect(audioIO, &AudioClient::inputReceived, [](const QByteArray& audio) {
static auto recorder = DependencyManager::get<recording::Recorder>();
if (recorder->isRecording()) {
static const recording::FrameType AUDIO_FRAME_TYPE = recording::Frame::registerFrameType(AudioConstants::getAudioFrameName());
recorder->recordFrame(AUDIO_FRAME_TYPE, audio);
}
});
audioIO->startThread();
}
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
@ -1275,27 +1278,29 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Inititalize sample before registering
_sampleSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/sample.wav"));
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
scriptEngines->registerScriptInitializer([this](ScriptEnginePointer engine){
registerScriptEngineWithApplicationServices(engine);
});
{
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
scriptEngines->registerScriptInitializer([this](ScriptEnginePointer engine) {
registerScriptEngineWithApplicationServices(engine);
});
connect(scriptEngines, &ScriptEngines::scriptCountChanged, scriptEngines, [this] {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
if (scriptEngines->getRunningScripts().isEmpty()) {
getMyAvatar()->clearScriptableSettings();
}
}, Qt::QueuedConnection);
connect(scriptEngines, &ScriptEngines::scriptCountChanged, this, [this] {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
if (scriptEngines->getRunningScripts().isEmpty()) {
getMyAvatar()->clearScriptableSettings();
}
}, Qt::QueuedConnection);
connect(scriptEngines, &ScriptEngines::scriptsReloading, scriptEngines, [this] {
getEntities()->reloadEntityScripts();
loadAvatarScripts(getMyAvatar()->getScriptUrls());
}, Qt::QueuedConnection);
connect(scriptEngines, &ScriptEngines::scriptsReloading, this, [this] {
getEntities()->reloadEntityScripts();
loadAvatarScripts(getMyAvatar()->getScriptUrls());
}, Qt::QueuedConnection);
connect(scriptEngines, &ScriptEngines::scriptLoadError,
scriptEngines, [](const QString& filename, const QString& error){
OffscreenUi::asyncWarning(nullptr, "Error Loading Script", filename + " failed to load.");
}, Qt::QueuedConnection);
connect(scriptEngines, &ScriptEngines::scriptLoadError,
this, [](const QString& filename, const QString& error) {
OffscreenUi::asyncWarning(nullptr, "Error Loading Script", filename + " failed to load.");
}, Qt::QueuedConnection);
}
#ifdef _WIN32
WSADATA WsaData;
@ -1365,10 +1370,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// so we defer the setup of the `scripting::Audio` class until this point
{
auto audioScriptingInterface = DependencyManager::set<AudioScriptingInterface, scripting::Audio>();
connect(audioIO.data(), &AudioClient::mutedByMixer, audioScriptingInterface.data(), &AudioScriptingInterface::mutedByMixer);
connect(audioIO.data(), &AudioClient::receivedFirstPacket, audioScriptingInterface.data(), &AudioScriptingInterface::receivedFirstPacket);
connect(audioIO.data(), &AudioClient::disconnected, audioScriptingInterface.data(), &AudioScriptingInterface::disconnected);
connect(audioIO.data(), &AudioClient::muteEnvironmentRequested, [](glm::vec3 position, float radius) {
auto audioIO = DependencyManager::get<AudioClient>().data();
connect(audioIO, &AudioClient::mutedByMixer, audioScriptingInterface.data(), &AudioScriptingInterface::mutedByMixer);
connect(audioIO, &AudioClient::receivedFirstPacket, audioScriptingInterface.data(), &AudioScriptingInterface::receivedFirstPacket);
connect(audioIO, &AudioClient::disconnected, audioScriptingInterface.data(), &AudioScriptingInterface::disconnected);
connect(audioIO, &AudioClient::muteEnvironmentRequested, [](glm::vec3 position, float radius) {
auto audioClient = DependencyManager::get<AudioClient>();
auto audioScriptingInterface = DependencyManager::get<AudioScriptingInterface>();
auto myAvatarPosition = DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldPosition();
@ -1697,23 +1703,26 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
userInputMapper->registerDevice(_touchscreenVirtualPadDevice->getInputDevice());
}
// this will force the model the look at the correct directory (weird order of operations issue)
scriptEngines->reloadLocalFiles();
{
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
// this will force the model the look at the correct directory (weird order of operations issue)
scriptEngines->reloadLocalFiles();
// do this as late as possible so that all required subsystems are initialized
// If we've overridden the default scripts location, just load default scripts
// otherwise, load 'em all
// do this as late as possible so that all required subsystems are initialized
// If we've overridden the default scripts location, just load default scripts
// otherwise, load 'em all
// we just want to see if --scripts was set, we've already parsed it and done
// the change in PathUtils. Rather than pass that in the constructor, lets just
// look (this could be debated)
QString scriptsSwitch = QString("--").append(SCRIPTS_SWITCH);
QDir defaultScriptsLocation(getCmdOption(argc, constArgv, scriptsSwitch.toStdString().c_str()));
if (!defaultScriptsLocation.exists()) {
scriptEngines->loadDefaultScripts();
scriptEngines->defaultScriptsLocationOverridden(true);
} else {
scriptEngines->loadScripts();
// we just want to see if --scripts was set, we've already parsed it and done
// the change in PathUtils. Rather than pass that in the constructor, lets just
// look (this could be debated)
QString scriptsSwitch = QString("--").append(SCRIPTS_SWITCH);
QDir defaultScriptsLocation(getCmdOption(argc, constArgv, scriptsSwitch.toStdString().c_str()));
if (!defaultScriptsLocation.exists()) {
scriptEngines->loadDefaultScripts();
scriptEngines->defaultScriptsLocationOverridden(true);
} else {
scriptEngines->loadScripts();
}
}
// Make sure we don't time out during slow operations at startup
@ -1763,13 +1772,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
cameraMenuChanged();
}
// set the local loopback interface for local sounds
AudioInjector::setLocalAudioInterface(audioIO.data());
auto audioScriptingInterface = DependencyManager::get<AudioScriptingInterface>();
audioScriptingInterface->setLocalAudioInterface(audioIO.data());
connect(audioIO.data(), &AudioClient::noiseGateOpened, audioScriptingInterface.data(), &AudioScriptingInterface::noiseGateOpened);
connect(audioIO.data(), &AudioClient::noiseGateClosed, audioScriptingInterface.data(), &AudioScriptingInterface::noiseGateClosed);
connect(audioIO.data(), &AudioClient::inputReceived, audioScriptingInterface.data(), &AudioScriptingInterface::inputReceived);
{
auto audioIO = DependencyManager::get<AudioClient>().data();
// set the local loopback interface for local sounds
AudioInjector::setLocalAudioInterface(audioIO);
auto audioScriptingInterface = DependencyManager::get<AudioScriptingInterface>();
audioScriptingInterface->setLocalAudioInterface(audioIO);
connect(audioIO, &AudioClient::noiseGateOpened, audioScriptingInterface.data(), &AudioScriptingInterface::noiseGateOpened);
connect(audioIO, &AudioClient::noiseGateClosed, audioScriptingInterface.data(), &AudioScriptingInterface::noiseGateClosed);
connect(audioIO, &AudioClient::inputReceived, audioScriptingInterface.data(), &AudioScriptingInterface::inputReceived);
}
this->installEventFilter(this);
@ -1826,13 +1838,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
});
connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, [=](const EntityItemID& entityItemID) {
connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, [this](const EntityItemID& entityItemID) {
if (entityItemID == _keyboardFocusedEntity.get()) {
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
}
});
connect(getEntities()->getTree().get(), &EntityTree::deletingEntity, [=](const EntityItemID& entityItemID) {
connect(getEntities()->getTree().get(), &EntityTree::deletingEntity, [](const EntityItemID& entityItemID) {
auto avatarManager = DependencyManager::get<AvatarManager>();
auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;
if (myAvatar) {
@ -1840,7 +1852,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
});
EntityTree::setAddMaterialToEntityOperator([&](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
EntityTree::setAddMaterialToEntityOperator([this](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
// try to find the renderable
auto renderable = getEntities()->renderableForEntityId(entityID);
if (renderable) {
@ -1855,7 +1867,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
return false;
});
EntityTree::setRemoveMaterialFromEntityOperator([&](const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
EntityTree::setRemoveMaterialFromEntityOperator([this](const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
// try to find the renderable
auto renderable = getEntities()->renderableForEntityId(entityID);
if (renderable) {
@ -1890,7 +1902,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
return false;
});
EntityTree::setAddMaterialToOverlayOperator([&](const QUuid& overlayID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
EntityTree::setAddMaterialToOverlayOperator([this](const QUuid& overlayID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
auto overlay = _overlays.getOverlay(overlayID);
if (overlay) {
overlay->addMaterial(material, parentMaterialName);
@ -1898,7 +1910,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
return false;
});
EntityTree::setRemoveMaterialFromOverlayOperator([&](const QUuid& overlayID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
EntityTree::setRemoveMaterialFromOverlayOperator([this](const QUuid& overlayID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
auto overlay = _overlays.getOverlay(overlayID);
if (overlay) {
overlay->removeMaterial(material, parentMaterialName);
@ -1909,13 +1921,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Keyboard focus handling for Web overlays.
auto overlays = &(qApp->getOverlays());
connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) {
connect(overlays, &Overlays::overlayDeleted, [this](const OverlayID& overlayID) {
if (overlayID == _keyboardFocusedOverlay.get()) {
setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
}
});
connect(this, &Application::aboutToQuit, [=]() {
connect(this, &Application::aboutToQuit, [this]() {
setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
});
@ -2184,23 +2196,22 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
QVariant testProperty = property(hifi::properties::TEST);
qDebug() << testProperty;
if (testProperty.isValid()) {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
const auto testScript = property(hifi::properties::TEST).toUrl();
// Set last parameter to exit interface when the test script finishes, if so requested
scriptEngines->loadScript(testScript, false, false, false, false, quitWhenFinished);
DependencyManager::get<ScriptEngines>()->loadScript(testScript, false, false, false, false, quitWhenFinished);
// This is done so we don't get a "connection time-out" message when we haven't passed in a URL.
if (arguments().contains("--url")) {
auto reply = SandboxUtils::getStatus();
connect(reply, &QNetworkReply::finished, this, [=] {
connect(reply, &QNetworkReply::finished, this, [this, reply] {
handleSandboxStatus(reply);
});
}
} else {
PROFILE_RANGE(render, "GetSandboxStatus");
auto reply = SandboxUtils::getStatus();
connect(reply, &QNetworkReply::finished, this, [=] {
connect(reply, &QNetworkReply::finished, this, [this, reply] {
handleSandboxStatus(reply);
});
}
@ -2227,8 +2238,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged);
DependencyManager::get<PickManager>()->setShouldPickHUDOperator([&]() { return DependencyManager::get<HMDScriptingInterface>()->isHMDMode(); });
DependencyManager::get<PickManager>()->setCalculatePos2DFromHUDOperator([&](const glm::vec3& intersection) {
DependencyManager::get<PickManager>()->setShouldPickHUDOperator([]() { return DependencyManager::get<HMDScriptingInterface>()->isHMDMode(); });
DependencyManager::get<PickManager>()->setCalculatePos2DFromHUDOperator([this](const glm::vec3& intersection) {
const glm::vec2 MARGIN(25.0f);
glm::vec2 maxPos = _controllerScriptingInterface->getViewportDimensions() - MARGIN;
glm::vec2 pos2D = DependencyManager::get<HMDScriptingInterface>()->overlayFromWorldPoint(intersection);
@ -2238,7 +2249,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Setup the mouse ray pick and related operators
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickID(DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, std::make_shared<MouseRayPick>(
PickFilter(PickScriptingInterface::PICK_ENTITIES() | PickScriptingInterface::PICK_INCLUDE_NONCOLLIDABLE()), 0.0f, true)));
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickResultOperator([&](unsigned int rayPickID) {
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickResultOperator([](unsigned int rayPickID) {
RayToEntityIntersectionResult entityResult;
entityResult.intersects = false;
auto pickResult = DependencyManager::get<PickManager>()->getPrevPickResultTyped<RayPickResult>(rayPickID);
@ -2254,7 +2265,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
return entityResult;
});
DependencyManager::get<EntityTreeRenderer>()->setSetPrecisionPickingOperator([&](unsigned int rayPickID, bool value) {
DependencyManager::get<EntityTreeRenderer>()->setSetPrecisionPickingOperator([](unsigned int rayPickID, bool value) {
DependencyManager::get<PickManager>()->setPrecisionPicking(rayPickID, value);
});
@ -2453,21 +2464,35 @@ void Application::cleanupBeforeQuit() {
_keyboardFocusHighlight = nullptr;
}
auto nodeList = DependencyManager::get<NodeList>();
{
auto nodeList = DependencyManager::get<NodeList>();
// send the domain a disconnect packet, force stoppage of domain-server check-ins
nodeList->getDomainHandler().disconnect();
nodeList->setIsShuttingDown(true);
// send the domain a disconnect packet, force stoppage of domain-server check-ins
nodeList->getDomainHandler().disconnect();
nodeList->setIsShuttingDown(true);
// tell the packet receiver we're shutting down, so it can drop packets
nodeList->getPacketReceiver().setShouldDropPackets(true);
// tell the packet receiver we're shutting down, so it can drop packets
nodeList->getPacketReceiver().setShouldDropPackets(true);
}
getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
// Clear any queued processing (I/O, FBX/OBJ/Texture parsing)
QThreadPool::globalInstance()->clear();
DependencyManager::destroy<RecordingScriptingInterface>();
// FIXME: Something is still holding on to the ScriptEnginePointers contained in ScriptEngines, and they hold backpointers to ScriptEngines,
// so this doesn't shut down properly
DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts
// These classes hold ScriptEnginePointers, so they must be destroyed before ScriptEngines
// Must be done after shutdownScripting in case any scripts try to access these things
{
DependencyManager::destroy<StandAloneJSConsole>();
EntityTreePointer tree = getEntities()->getTree();
tree->setSimulation(nullptr);
DependencyManager::destroy<EntityTreeRenderer>();
}
DependencyManager::destroy<ScriptEngines>();
_displayPlugin.reset();
@ -2504,6 +2529,8 @@ void Application::cleanupBeforeQuit() {
DependencyManager::destroy<EyeTracker>();
#endif
DependencyManager::destroy<ContextOverlayInterface>(); // Must be destroyed before TabletScriptingInterface
// stop QML
DependencyManager::destroy<TabletScriptingInterface>();
DependencyManager::destroy<ToolbarScriptingInterface>();
@ -2515,10 +2542,6 @@ void Application::cleanupBeforeQuit() {
_snapshotSoundInjector->stop();
}
// FIXME: something else is holding a reference to AudioClient,
// so it must be explicitly synchronously stopped here
DependencyManager::get<AudioClient>()->cleanupBeforeQuit();
// destroy Audio so it and its threads have a chance to go down safely
// this must happen after QML, as there are unexplained audio crashes originating in qtwebengine
DependencyManager::destroy<AudioClient>();
@ -2558,9 +2581,6 @@ Application::~Application() {
_entityClipboard->eraseAllOctreeElements();
_entityClipboard.reset();
EntityTreePointer tree = getEntities()->getTree();
tree->setSimulation(nullptr);
_octreeProcessor.terminate();
_entityEditSender.terminate();
@ -2924,6 +2944,15 @@ void Application::initializeUi() {
// Pre-create a couple of Web3D overlays to speed up tablet UI
auto offscreenSurfaceCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
offscreenSurfaceCache->setOnRootContextCreated([&](const QString& rootObject, QQmlContext* surfaceContext) {
if (rootObject == TabletScriptingInterface::QML) {
// in Qt 5.10.0 there is already an "Audio" object in the QML context
// though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface"
surfaceContext->setContextProperty("AudioScriptingInterface", DependencyManager::get<AudioScriptingInterface>().data());
surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
}
});
offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1);
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
@ -3265,8 +3294,7 @@ void Application::showHelp() {
QUrlQuery queryString;
queryString.addQueryItem("handControllerName", handControllerName);
queryString.addQueryItem("defaultTab", defaultTab);
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet(SYSTEM_TABLET));
TabletProxy* tablet = dynamic_cast<TabletProxy*>(DependencyManager::get<TabletScriptingInterface>()->getTablet(SYSTEM_TABLET));
tablet->gotoWebScreen(PathUtils::resourcesUrl() + INFO_HELP_PATH + "?" + queryString.toString());
DependencyManager::get<HMDScriptingInterface>()->openTablet();
//InfoView::show(INFO_HELP_PATH, false, queryString.toString());
@ -3579,6 +3607,10 @@ static void dumpEventQueue(QThread* thread) {
bool Application::event(QEvent* event) {
if (_aboutToQuit) {
return false;
}
if (!Menu::getInstance()) {
return false;
}
@ -3696,7 +3728,10 @@ static bool _altPressed{ false };
void Application::keyPressEvent(QKeyEvent* event) {
_altPressed = event->key() == Qt::Key_Alt;
_keysPressed.insert(event->key());
if (!event->isAutoRepeat()) {
_keysPressed.insert(event->key(), *event);
}
_controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
@ -3907,7 +3942,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
void Application::keyReleaseEvent(QKeyEvent* event) {
_keysPressed.remove(event->key());
if (!event->isAutoRepeat()) {
_keysPressed.remove(event->key());
}
#if defined(Q_OS_ANDROID)
if (event->key() == Qt::Key_Back) {
@ -3943,11 +3980,14 @@ void Application::focusOutEvent(QFocusEvent* event) {
#endif
// synthesize events for keys currently pressed, since we may not get their release events
foreach (int key, _keysPressed) {
QKeyEvent keyEvent(QEvent::KeyRelease, key, Qt::NoModifier);
keyReleaseEvent(&keyEvent);
// Because our key event handlers may manipulate _keysPressed, lets swap the keys pressed into a local copy,
// clearing the existing list.
QHash<int, QKeyEvent> keysPressed;
std::swap(keysPressed, _keysPressed);
for (auto& ev : keysPressed) {
QKeyEvent synthesizedEvent { QKeyEvent::KeyRelease, ev.key(), Qt::NoModifier, ev.text() };
keyReleaseEvent(&synthesizedEvent);
}
_keysPressed.clear();
}
void Application::maybeToggleMenuVisible(QMouseEvent* event) const {
@ -3975,10 +4015,6 @@ void Application::maybeToggleMenuVisible(QMouseEvent* event) const {
void Application::mouseMoveEvent(QMouseEvent* event) {
PROFILE_RANGE(app_input_mouse, __FUNCTION__);
if (_aboutToQuit) {
return;
}
maybeToggleMenuVisible(event);
auto& compositor = getApplicationCompositor();
@ -4043,11 +4079,9 @@ void Application::mousePressEvent(QMouseEvent* event) {
event->screenPos(), event->button(),
event->buttons(), event->modifiers());
if (!_aboutToQuit) {
getOverlays().mousePressEvent(&mappedEvent);
if (!_controllerScriptingInterface->areEntityClicksCaptured()) {
getEntities()->mousePressEvent(&mappedEvent);
}
getOverlays().mousePressEvent(&mappedEvent);
if (!_controllerScriptingInterface->areEntityClicksCaptured()) {
getEntities()->mousePressEvent(&mappedEvent);
}
_controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts
@ -4084,14 +4118,11 @@ void Application::mouseDoublePressEvent(QMouseEvent* event) {
event->screenPos(), event->button(),
event->buttons(), event->modifiers());
if (!_aboutToQuit) {
getOverlays().mouseDoublePressEvent(&mappedEvent);
if (!_controllerScriptingInterface->areEntityClicksCaptured()) {
getEntities()->mouseDoublePressEvent(&mappedEvent);
}
getOverlays().mouseDoublePressEvent(&mappedEvent);
if (!_controllerScriptingInterface->areEntityClicksCaptured()) {
getEntities()->mouseDoublePressEvent(&mappedEvent);
}
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface->isMouseCaptured()) {
return;
@ -4110,10 +4141,8 @@ void Application::mouseReleaseEvent(QMouseEvent* event) {
event->screenPos(), event->button(),
event->buttons(), event->modifiers());
if (!_aboutToQuit) {
getOverlays().mouseReleaseEvent(&mappedEvent);
getEntities()->mouseReleaseEvent(&mappedEvent);
}
getOverlays().mouseReleaseEvent(&mappedEvent);
getEntities()->mouseReleaseEvent(&mappedEvent);
_controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
@ -4264,7 +4293,6 @@ bool Application::shouldPaint() const {
return false;
}
auto displayPlugin = getActiveDisplayPlugin();
#ifdef DEBUG_PAINT_DELAY
@ -4744,7 +4772,7 @@ bool Application::exportEntities(const QString& filename,
exportTree->createRootElement();
glm::vec3 root(TREE_SCALE, TREE_SCALE, TREE_SCALE);
bool success = true;
entityTree->withReadLock([&] {
entityTree->withReadLock([entityIDs, entityTree, givenOffset, myAvatarID, &root, &entities, &success, &exportTree] {
for (auto entityID : entityIDs) { // Gather entities and properties.
auto entityItem = entityTree->findEntityByEntityItemID(entityID);
if (!entityItem) {
@ -5493,6 +5521,10 @@ static bool domainLoadingInProgress = false;
void Application::update(float deltaTime) {
PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_renderFrameCount + 1);
if (_aboutToQuit) {
return;
}
if (!_physicsEnabled) {
if (!domainLoadingInProgress) {
PROFILE_ASYNC_BEGIN(app, "Scene Loading", "");
@ -5761,15 +5793,13 @@ void Application::update(float deltaTime) {
_entitySimulation->handleDeactivatedMotionStates(deactivations);
});
if (!_aboutToQuit) {
// handleCollisionEvents() AFTER handleChangedMotionStates()
{
PROFILE_RANGE(simulation_physics, "CollisionEvents");
avatarManager->handleCollisionEvents(collisionEvents);
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
// deadlock.)
_entitySimulation->handleCollisionEvents(collisionEvents);
}
// handleCollisionEvents() AFTER handleChangedMotionStates()
{
PROFILE_RANGE(simulation_physics, "CollisionEvents");
avatarManager->handleCollisionEvents(collisionEvents);
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
// deadlock.)
_entitySimulation->handleCollisionEvents(collisionEvents);
}
{
@ -5787,11 +5817,9 @@ void Application::update(float deltaTime) {
}
auto t4 = std::chrono::high_resolution_clock::now();
if (!_aboutToQuit) {
// NOTE: the getEntities()->update() call below will wait for lock
// and will provide non-physical entity motion
getEntities()->update(true); // update the models...
}
// NOTE: the getEntities()->update() call below will wait for lock
// and will provide non-physical entity motion
getEntities()->update(true); // update the models...
auto t5 = std::chrono::high_resolution_clock::now();
@ -6357,7 +6385,6 @@ void Application::domainURLChanged(QUrl domainURL) {
void Application::resettingDomain() {
_notifiedPacketVersionMismatchThisDomain = false;
auto nodeList = DependencyManager::get<NodeList>();
clearDomainOctreeDetails();
}
@ -6708,19 +6735,16 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
registerInteractiveWindowMetaType(scriptEngine.data());
DependencyManager::get<PickScriptingInterface>()->registerMetaTypes(scriptEngine.data());
auto pickScriptingInterface = DependencyManager::get<PickScriptingInterface>();
pickScriptingInterface->registerMetaTypes(scriptEngine.data());
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
connect(scriptEngine.data(), &ScriptEngine::printedMessage,
DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onPrintedMessage);
connect(scriptEngine.data(), &ScriptEngine::errorMessage,
DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onErrorMessage);
connect(scriptEngine.data(), &ScriptEngine::warningMessage,
DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onWarningMessage);
connect(scriptEngine.data(), &ScriptEngine::infoMessage,
DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onInfoMessage);
connect(scriptEngine.data(), &ScriptEngine::clearDebugWindow,
DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onClearDebugWindow);
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
connect(scriptEngine.data(), &ScriptEngine::printedMessage, scriptEngines, &ScriptEngines::onPrintedMessage);
connect(scriptEngine.data(), &ScriptEngine::errorMessage, scriptEngines, &ScriptEngines::onErrorMessage);
connect(scriptEngine.data(), &ScriptEngine::warningMessage, scriptEngines, &ScriptEngines::onWarningMessage);
connect(scriptEngine.data(), &ScriptEngine::infoMessage, scriptEngines, &ScriptEngines::onInfoMessage);
connect(scriptEngine.data(), &ScriptEngine::clearDebugWindow, scriptEngines, &ScriptEngines::onClearDebugWindow);
}
@ -7028,10 +7052,9 @@ void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const
}
void Application::showScriptLogs() {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/debugging/debugWindow.js");
scriptEngines->loadScript(defaultScriptsLoc.toString());
DependencyManager::get<ScriptEngines>()->loadScript(defaultScriptsLoc.toString());
}
void Application::showAssetServerWidget(QString filePath) {
@ -7284,6 +7307,8 @@ void Application::addAssetToWorldAddEntity(QString filePath, QString mapping) {
}
properties.setCollisionless(true); // Temporarily set so that doesn't collide with avatar.
properties.setVisible(false); // Temporarily set so that don't see at large unresized dimensions.
bool grabbable = (Menu::getInstance()->isOptionChecked(MenuOption::CreateEntitiesGrabbable));
properties.setUserData(grabbable ? GRABBABLE_USER_DATA : NOT_GRABBABLE_USER_DATA);
glm::vec3 positionOffset = getMyAvatar()->getWorldOrientation() * (getMyAvatar()->getSensorToWorldScale() * glm::vec3(0.0f, 0.0f, -2.0f));
properties.setPosition(getMyAvatar()->getWorldPosition() + positionOffset);
properties.setRotation(getMyAvatar()->getWorldOrientation());
@ -7326,7 +7351,6 @@ void Application::addAssetToWorldCheckModelSize() {
auto name = properties.getName();
auto dimensions = properties.getDimensions();
const QString GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":true}}";
bool doResize = false;
const glm::vec3 DEFAULT_DIMENSIONS = glm::vec3(0.1f, 0.1f, 0.1f);
@ -7370,7 +7394,8 @@ void Application::addAssetToWorldCheckModelSize() {
if (!name.toLower().endsWith(PNG_EXTENSION) && !name.toLower().endsWith(JPG_EXTENSION)) {
properties.setCollisionless(false);
}
properties.setUserData(GRABBABLE_USER_DATA);
bool grabbable = (Menu::getInstance()->isOptionChecked(MenuOption::CreateEntitiesGrabbable));
properties.setUserData(grabbable ? GRABBABLE_USER_DATA : NOT_GRABBABLE_USER_DATA);
properties.setLastEdited(usecTimestampNow());
entityScriptingInterface->editEntity(entityID, properties);
}
@ -7595,7 +7620,6 @@ void Application::openUrl(const QUrl& url) const {
}
void Application::loadDialog() {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
ModalDialogListener* dlg = OffscreenUi::getOpenFileNameAsync(_glWidget, tr("Open Script"),
getPreviousScriptLocation(),
tr("JavaScript Files (*.js)"));

View file

@ -621,7 +621,7 @@ private:
float _mirrorYawOffset;
float _raiseMirror;
QSet<int> _keysPressed;
QHash<int, QKeyEvent> _keysPressed;
bool _enableProcessOctreeThread;

View file

@ -64,7 +64,7 @@ void addAvatarEntities(const QVariantList& avatarEntities) {
EntityItemID id = EntityItemID(QUuid::createUuid());
bool success = true;
entityTree->withWriteLock([&] {
entityTree->withWriteLock([&entityTree, id, &entityProperties, &success] {
EntityItemPointer entity = entityTree->addEntity(id, entityProperties);
if (entity) {
if (entityProperties.queryAACubeRelatedPropertyChanged()) {
@ -171,6 +171,13 @@ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) {
if (bookmarkEntry != _bookmarks.end()) {
QVariantMap bookmark = bookmarkEntry.value().toMap();
if (bookmark.empty()) { // compatibility with bookmarks like this: "Wooden Doll": "http://mpassets.highfidelity.com/7fe80a1e-f445-4800-9e89-40e677b03bee-v1/mannequin.fst?noDownload=false",
auto avatarUrl = bookmarkEntry.value().toString();
if (!avatarUrl.isEmpty()) {
bookmark.insert(ENTRY_AVATAR_URL, avatarUrl);
}
}
if (!bookmark.empty()) {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();

View file

@ -147,9 +147,11 @@ Menu::Menu() {
auto assetServerAction = addActionToQMenuAndActionHash(editMenu, MenuOption::AssetServer,
Qt::CTRL | Qt::SHIFT | Qt::Key_A,
qApp, SLOT(showAssetServerWidget()));
auto nodeList = DependencyManager::get<NodeList>();
QObject::connect(nodeList.data(), &NodeList::canWriteAssetsChanged, assetServerAction, &QAction::setEnabled);
assetServerAction->setEnabled(nodeList->getThisNodeCanWriteAssets());
{
auto nodeList = DependencyManager::get<NodeList>();
QObject::connect(nodeList.data(), &NodeList::canWriteAssetsChanged, assetServerAction, &QAction::setEnabled);
assetServerAction->setEnabled(nodeList->getThisNodeCanWriteAssets());
}
// Edit > Package Model as .fst...
addActionToQMenuAndActionHash(editMenu, MenuOption::PackageModel, 0,
@ -620,8 +622,11 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::SendWrongProtocolVersion, 0, false,
qApp, SLOT(sendWrongProtocolVersionsSignature(bool)));
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::SendWrongDSConnectVersion, 0, false,
nodeList.data(), SLOT(toggleSendNewerDSConnectVersion(bool)));
{
auto nodeList = DependencyManager::get<NodeList>();
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::SendWrongDSConnectVersion, 0, false,
nodeList.data(), SLOT(toggleSendNewerDSConnectVersion(bool)));
}
#endif
@ -655,10 +660,9 @@ Menu::Menu() {
action = addActionToQMenuAndActionHash(audioDebugMenu, "Stats...");
connect(action, &QAction::triggered, [] {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/audio/stats.js");
scriptEngines->loadScript(defaultScriptsLoc.toString());
DependencyManager::get<ScriptEngines>()->loadScript(defaultScriptsLoc.toString());
});
action = addActionToQMenuAndActionHash(audioDebugMenu, "Buffers...");
@ -667,16 +671,14 @@ Menu::Menu() {
QString("hifi/tablet/TabletAudioBuffers.qml"), "AudioBuffersDialog");
});
auto audioIO = DependencyManager::get<AudioClient>();
addActionToQMenuAndActionHash(audioDebugMenu, MenuOption::MuteEnvironment, 0,
audioIO.data(), SLOT(sendMuteEnvironmentPacket()));
DependencyManager::get<AudioClient>().data(), SLOT(sendMuteEnvironmentPacket()));
action = addActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope);
connect(action, &QAction::triggered, [] {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/audio/audioScope.js");
scriptEngines->loadScript(defaultScriptsLoc.toString());
DependencyManager::get<ScriptEngines>()->loadScript(defaultScriptsLoc.toString());
});
// Developer > Physics >>>
@ -756,10 +758,9 @@ Menu::Menu() {
// Developer > API Debugger
action = addActionToQMenuAndActionHash(developerMenu, "API Debugger");
connect(action, &QAction::triggered, [] {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/tools/currentAPI.js");
scriptEngines->loadScript(defaultScriptsLoc.toString());
DependencyManager::get<ScriptEngines>()->loadScript(defaultScriptsLoc.toString());
});
// Developer > Log...
@ -767,11 +768,14 @@ Menu::Menu() {
qApp, SLOT(toggleLogDialog()));
auto essLogAction = addActionToQMenuAndActionHash(developerMenu, MenuOption::EntityScriptServerLog, 0,
qApp, SLOT(toggleEntityScriptServerLogDialog()));
QObject::connect(nodeList.data(), &NodeList::canRezChanged, essLogAction, [essLogAction] {
{
auto nodeList = DependencyManager::get<NodeList>();
QObject::connect(nodeList.data(), &NodeList::canRezChanged, essLogAction, [essLogAction] {
auto nodeList = DependencyManager::get<NodeList>();
essLogAction->setEnabled(nodeList->getThisNodeCanRez());
});
essLogAction->setEnabled(nodeList->getThisNodeCanRez());
});
essLogAction->setEnabled(nodeList->getThisNodeCanRez());
}
addActionToQMenuAndActionHash(developerMenu, "Script Log (HMD friendly)...", Qt::NoButton,
qApp, SLOT(showScriptLogs()));

View file

@ -76,6 +76,7 @@ namespace MenuOption {
const QString CrashOutOfBoundsVectorAccessThreaded = "Out of Bounds Vector Access (threaded)";
const QString CrashNewFault = "New Fault";
const QString CrashNewFaultThreaded = "New Fault (threaded)";
const QString CreateEntitiesGrabbable = "Create Entities As Grabbable (except Zones, Particles, and Lights)";
const QString DeadlockInterface = "Deadlock Interface";
const QString UnresponsiveInterface = "Unresponsive Interface";
const QString DecreaseAvatarSize = "Decrease Avatar Size";

View file

@ -205,7 +205,9 @@ void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inp
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1);
assert(items.canCast<RenderFetchCullSortTask::Output>());
if (isDeferred) {
task.addJob<RenderDeferredTask>("RenderDeferredTask", items, false);
const render::Varying cascadeSceneBBoxes;
const auto renderInput = RenderDeferredTask::Input(items, cascadeSceneBBoxes).asVarying();
task.addJob<RenderDeferredTask>("RenderDeferredTask", renderInput, false);
} else {
task.addJob<RenderForwardTask>("Forward", items);
}

View file

@ -79,7 +79,7 @@ AvatarManager::AvatarManager(QObject* parent) :
// when we hear that the user has ignored an avatar by session UUID
// immediately remove that avatar instead of waiting for the absence of packets from avatar mixer
connect(nodeList.data(), &NodeList::ignoredNode, this, [=](const QUuid& nodeID, bool enabled) {
connect(nodeList.data(), &NodeList::ignoredNode, this, [this](const QUuid& nodeID, bool enabled) {
if (enabled) {
removeAvatar(nodeID, KillAvatarReason::AvatarIgnored);
}
@ -277,21 +277,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
sortedAvatars.pop();
}
if (_shouldRender) {
if (!_avatarsToFade.empty()) {
QReadLocker lock(&_hashLock);
QVector<AvatarSharedPointer>::iterator itr = _avatarsToFade.begin();
while (itr != _avatarsToFade.end() && usecTimestampNow() > updateExpiry) {
auto avatar = std::static_pointer_cast<Avatar>(*itr);
avatar->animateScaleChanges(deltaTime);
avatar->simulate(deltaTime, true);
avatar->updateRenderItem(transaction);
++itr;
}
}
qApp->getMain3DScene()->enqueueTransaction(transaction);
}
_numAvatarsUpdated = numAvatarsUpdated;
_numAvatarsNotUpdated = numAVatarsNotUpdated;
@ -333,16 +318,21 @@ void AvatarManager::postUpdate(float deltaTime, const render::ScenePointer& scen
void AvatarManager::sendIdentityRequest(const QUuid& avatarID) const {
auto nodeList = DependencyManager::get<NodeList>();
QWeakPointer<NodeList> nodeListWeak = nodeList;
nodeList->eachMatchingNode(
[&](const SharedNodePointer& node)->bool {
return node->getType() == NodeType::AvatarMixer && node->getActiveSocket();
},
[&](const SharedNodePointer& node) {
auto packet = NLPacket::create(PacketType::AvatarIdentityRequest, NUM_BYTES_RFC4122_UUID, true);
packet->write(avatarID.toRfc4122());
nodeList->sendPacket(std::move(packet), *node);
++_identityRequestsSent;
});
[](const SharedNodePointer& node)->bool {
return node->getType() == NodeType::AvatarMixer && node->getActiveSocket();
},
[this, avatarID, nodeListWeak](const SharedNodePointer& node) {
auto nodeList = nodeListWeak.lock();
if (nodeList) {
auto packet = NLPacket::create(PacketType::AvatarIdentityRequest, NUM_BYTES_RFC4122_UUID, true);
packet->write(avatarID.toRfc4122());
nodeList->sendPacket(std::move(packet), *node);
++_identityRequestsSent;
}
}
);
}
void AvatarManager::simulateAvatarFades(float deltaTime) {
@ -365,8 +355,6 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
}
avatarItr = _avatarsToFade.erase(avatarItr);
} else {
const bool inView = true; // HACK
avatar->simulate(deltaTime, inView);
++avatarItr;
}
}

View file

@ -139,6 +139,12 @@ MyAvatar::MyAvatar(QThread* thread) :
auto geometry = getSkeletonModel()->getFBXGeometry();
qApp->loadAvatarScripts(geometry.scripts);
_shouldLoadScripts = false;
}
// Load and convert old attachments to avatar entities
if (_oldAttachmentData.size() > 0) {
setAttachmentData(_oldAttachmentData);
_oldAttachmentData.clear();
_attachmentData.clear();
}
});
connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady);
@ -492,14 +498,14 @@ void MyAvatar::update(float deltaTime) {
// Get audio loudness data from audio input device
// Also get the AudioClient so we can update the avatar bounding box data
// on the AudioClient side.
auto audio = DependencyManager::get<AudioClient>();
auto audio = DependencyManager::get<AudioClient>().data();
setAudioLoudness(audio->getLastInputLoudness());
setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
glm::vec3 halfBoundingBoxDimensions(_characterController.getCapsuleRadius(), _characterController.getCapsuleHalfHeight(), _characterController.getCapsuleRadius());
// This might not be right! Isn't the capsule local offset in avatar space? -HRS 5/26/17
halfBoundingBoxDimensions += _characterController.getCapsuleLocalOffset();
QMetaObject::invokeMethod(audio.data(), "setAvatarBoundingBoxParameters",
QMetaObject::invokeMethod(audio, "setAvatarBoundingBoxParameters",
Q_ARG(glm::vec3, (getWorldPosition() - halfBoundingBoxDimensions)),
Q_ARG(glm::vec3, (halfBoundingBoxDimensions*2.0f)));
@ -1249,7 +1255,6 @@ void MyAvatar::loadData() {
useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName);
QVector<AttachmentData> attachmentData;
int attachmentCount = settings.beginReadArray("attachmentData");
for (int i = 0; i < attachmentCount; i++) {
settings.setArrayIndex(i);
@ -1266,10 +1271,10 @@ void MyAvatar::loadData() {
attachment.rotation = glm::quat(eulers);
attachment.scale = loadSetting(settings, "scale", 1.0f);
attachment.isSoft = settings.value("isSoft").toBool();
attachmentData.append(attachment);
// old attachments are stored and loaded/converted later when rig is ready
_oldAttachmentData.append(attachment);
}
settings.endArray();
setAttachmentData(attachmentData);
int avatarEntityCount = settings.beginReadArray("avatarEntityData");
for (int i = 0; i < avatarEntityCount; i++) {
@ -1494,50 +1499,126 @@ void MyAvatar::setJointRotations(const QVector<glm::quat>& jointRotations) {
}
void MyAvatar::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setJointData", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation),
Q_ARG(const glm::vec3&, translation));
return;
switch (index) {
case FARGRAB_RIGHTHAND_INDEX: {
_farGrabRightMatrixCache.set(createMatFromQuatAndPos(rotation, translation));
break;
}
case FARGRAB_LEFTHAND_INDEX: {
_farGrabLeftMatrixCache.set(createMatFromQuatAndPos(rotation, translation));
break;
}
case FARGRAB_MOUSE_INDEX: {
_farGrabMouseMatrixCache.set(createMatFromQuatAndPos(rotation, translation));
break;
}
default: {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setJointData", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation),
Q_ARG(const glm::vec3&, translation));
return;
}
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
_skeletonModel->getRig().setJointState(index, true, rotation, translation, SCRIPT_PRIORITY);
}
}
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
_skeletonModel->getRig().setJointState(index, true, rotation, translation, SCRIPT_PRIORITY);
}
void MyAvatar::setJointRotation(int index, const glm::quat& rotation) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setJointRotation", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation));
return;
switch (index) {
case FARGRAB_RIGHTHAND_INDEX: {
glm::mat4 prevMat = _farGrabRightMatrixCache.get();
glm::vec3 previousTranslation = extractTranslation(prevMat);
_farGrabRightMatrixCache.set(createMatFromQuatAndPos(rotation, previousTranslation));
break;
}
case FARGRAB_LEFTHAND_INDEX: {
glm::mat4 prevMat = _farGrabLeftMatrixCache.get();
glm::vec3 previousTranslation = extractTranslation(prevMat);
_farGrabLeftMatrixCache.set(createMatFromQuatAndPos(rotation, previousTranslation));
break;
}
case FARGRAB_MOUSE_INDEX: {
glm::mat4 prevMat = _farGrabMouseMatrixCache.get();
glm::vec3 previousTranslation = extractTranslation(prevMat);
_farGrabMouseMatrixCache.set(createMatFromQuatAndPos(rotation, previousTranslation));
break;
}
default: {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setJointRotation", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation));
return;
}
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
_skeletonModel->getRig().setJointRotation(index, true, rotation, SCRIPT_PRIORITY);
}
}
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
_skeletonModel->getRig().setJointRotation(index, true, rotation, SCRIPT_PRIORITY);
}
void MyAvatar::setJointTranslation(int index, const glm::vec3& translation) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setJointTranslation", Q_ARG(int, index), Q_ARG(const glm::vec3&, translation));
return;
switch (index) {
case FARGRAB_RIGHTHAND_INDEX: {
glm::mat4 prevMat = _farGrabRightMatrixCache.get();
glm::quat previousRotation = extractRotation(prevMat);
_farGrabRightMatrixCache.set(createMatFromQuatAndPos(previousRotation, translation));
break;
}
case FARGRAB_LEFTHAND_INDEX: {
glm::mat4 prevMat = _farGrabLeftMatrixCache.get();
glm::quat previousRotation = extractRotation(prevMat);
_farGrabLeftMatrixCache.set(createMatFromQuatAndPos(previousRotation, translation));
break;
}
case FARGRAB_MOUSE_INDEX: {
glm::mat4 prevMat = _farGrabMouseMatrixCache.get();
glm::quat previousRotation = extractRotation(prevMat);
_farGrabMouseMatrixCache.set(createMatFromQuatAndPos(previousRotation, translation));
break;
}
default: {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setJointTranslation",
Q_ARG(int, index), Q_ARG(const glm::vec3&, translation));
return;
}
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
_skeletonModel->getRig().setJointTranslation(index, true, translation, SCRIPT_PRIORITY);
}
}
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
_skeletonModel->getRig().setJointTranslation(index, true, translation, SCRIPT_PRIORITY);
}
void MyAvatar::clearJointData(int index) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index));
return;
switch (index) {
case FARGRAB_RIGHTHAND_INDEX: {
_farGrabRightMatrixCache.invalidate();
break;
}
case FARGRAB_LEFTHAND_INDEX: {
_farGrabLeftMatrixCache.invalidate();
break;
}
case FARGRAB_MOUSE_INDEX: {
_farGrabMouseMatrixCache.invalidate();
break;
}
default: {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index));
return;
}
_skeletonModel->getRig().clearJointAnimationPriority(index);
}
}
_skeletonModel->getRig().clearJointAnimationPriority(index);
}
void MyAvatar::setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setJointData", Q_ARG(QString, name), Q_ARG(const glm::quat&, rotation),
Q_ARG(const glm::vec3&, translation));
Q_ARG(const glm::vec3&, translation));
return;
}
writeLockWithNamedJointIndex(name, [&](int index) {
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
_skeletonModel->getRig().setJointState(index, true, rotation, translation, SCRIPT_PRIORITY);
setJointData(index, rotation, translation);
});
}
@ -1547,8 +1628,7 @@ void MyAvatar::setJointRotation(const QString& name, const glm::quat& rotation)
return;
}
writeLockWithNamedJointIndex(name, [&](int index) {
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
_skeletonModel->getRig().setJointRotation(index, true, rotation, SCRIPT_PRIORITY);
setJointRotation(index, rotation);
});
}
@ -1558,8 +1638,7 @@ void MyAvatar::setJointTranslation(const QString& name, const glm::vec3& transla
return;
}
writeLockWithNamedJointIndex(name, [&](int index) {
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
_skeletonModel->getRig().setJointTranslation(index, true, translation, SCRIPT_PRIORITY);
setJointTranslation(index, translation);
});
}
@ -1569,7 +1648,7 @@ void MyAvatar::clearJointData(const QString& name) {
return;
}
writeLockWithNamedJointIndex(name, [&](int index) {
_skeletonModel->getRig().clearJointAnimationPriority(index);
clearJointData(index);
});
}
@ -1578,6 +1657,9 @@ void MyAvatar::clearJointsData() {
QMetaObject::invokeMethod(this, "clearJointsData");
return;
}
_farGrabRightMatrixCache.invalidate();
_farGrabLeftMatrixCache.invalidate();
_farGrabMouseMatrixCache.invalidate();
_skeletonModel->getRig().clearJointStates();
}
@ -1688,16 +1770,6 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
markIdentityDataChanged();
}
void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "setAttachmentData",
Q_ARG(const QVector<AttachmentData>, attachmentData));
return;
}
Avatar::setAttachmentData(attachmentData);
emit attachmentsChanged();
}
glm::vec3 MyAvatar::getSkeletonPosition() const {
CameraMode mode = qApp->getCamera().getMode();
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
@ -1968,20 +2040,164 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName,
float scale, bool isSoft,
bool allowDuplicates, bool useSaved) {
if (QThread::currentThread() != thread()) {
Avatar::attach(modelURL, jointName, translation, rotation, scale, isSoft, allowDuplicates, useSaved);
BLOCKING_INVOKE_METHOD(this, "attach",
Q_ARG(const QString&, modelURL),
Q_ARG(const QString&, jointName),
Q_ARG(const glm::vec3&, translation),
Q_ARG(const glm::quat&, rotation),
Q_ARG(float, scale),
Q_ARG(bool, isSoft),
Q_ARG(bool, allowDuplicates),
Q_ARG(bool, useSaved)
);
return;
}
if (useSaved) {
AttachmentData attachment = loadAttachmentData(modelURL, jointName);
if (attachment.isValid()) {
Avatar::attach(modelURL, attachment.jointName,
attachment.translation, attachment.rotation,
attachment.scale, attachment.isSoft,
allowDuplicates, useSaved);
return;
AttachmentData data;
data.modelURL = modelURL;
data.jointName = jointName;
data.translation = translation;
data.rotation = rotation;
data.scale = scale;
data.isSoft = isSoft;
EntityItemProperties properties;
attachmentDataToEntityProperties(data, properties);
DependencyManager::get<EntityScriptingInterface>()->addEntity(properties, true);
emit attachmentsChanged();
}
void MyAvatar::detachOne(const QString& modelURL, const QString& jointName) {
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "detachOne",
Q_ARG(const QString&, modelURL),
Q_ARG(const QString&, jointName)
);
return;
}
QUuid entityID;
if (findAvatarEntity(modelURL, jointName, entityID)) {
DependencyManager::get<EntityScriptingInterface>()->deleteEntity(entityID);
}
emit attachmentsChanged();
}
void MyAvatar::detachAll(const QString& modelURL, const QString& jointName) {
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "detachAll",
Q_ARG(const QString&, modelURL),
Q_ARG(const QString&, jointName)
);
return;
}
QUuid entityID;
while (findAvatarEntity(modelURL, jointName, entityID)) {
DependencyManager::get<EntityScriptingInterface>()->deleteEntity(entityID);
}
emit attachmentsChanged();
}
void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "setAttachmentData",
Q_ARG(const QVector<AttachmentData>&, attachmentData));
return;
}
std::vector<EntityItemProperties> newEntitiesProperties;
for (auto& data : attachmentData) {
QUuid entityID;
EntityItemProperties properties;
if (findAvatarEntity(data.modelURL.toString(), data.jointName, entityID)) {
properties = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(entityID);
}
attachmentDataToEntityProperties(data, properties);
newEntitiesProperties.push_back(properties);
}
removeAvatarEntities();
for (auto& properties : newEntitiesProperties) {
DependencyManager::get<EntityScriptingInterface>()->addEntity(properties, true);
}
emit attachmentsChanged();
}
QVector<AttachmentData> MyAvatar::getAttachmentData() const {
QVector<AttachmentData> avatarData;
auto avatarEntities = getAvatarEntityData();
AvatarEntityMap::const_iterator dataItr = avatarEntities.begin();
while (dataItr != avatarEntities.end()) {
QUuid entityID = dataItr.key();
auto properties = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(entityID);
AttachmentData data = entityPropertiesToAttachmentData(properties);
avatarData.append(data);
dataItr++;
}
return avatarData;
}
QVariantList MyAvatar::getAttachmentsVariant() const {
QVariantList result;
for (const auto& attachment : getAttachmentData()) {
result.append(attachment.toVariant());
}
return result;
}
void MyAvatar::setAttachmentsVariant(const QVariantList& variant) {
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "setAttachmentsVariant",
Q_ARG(const QVariantList&, variant));
return;
}
QVector<AttachmentData> newAttachments;
newAttachments.reserve(variant.size());
for (const auto& attachmentVar : variant) {
AttachmentData attachment;
if (attachment.fromVariant(attachmentVar)) {
newAttachments.append(attachment);
}
}
Avatar::attach(modelURL, jointName, translation, rotation, scale, isSoft, allowDuplicates, useSaved);
setAttachmentData(newAttachments);
}
bool MyAvatar::findAvatarEntity(const QString& modelURL, const QString& jointName, QUuid& entityID) {
auto avatarEntities = getAvatarEntityData();
AvatarEntityMap::const_iterator dataItr = avatarEntities.begin();
while (dataItr != avatarEntities.end()) {
entityID = dataItr.key();
auto props = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(entityID);
if (props.getModelURL() == modelURL &&
(jointName.isEmpty() || props.getParentJointIndex() == getJointIndex(jointName))) {
return true;
}
dataItr++;
}
return false;
}
AttachmentData MyAvatar::entityPropertiesToAttachmentData(const EntityItemProperties& properties) const {
AttachmentData data;
data.modelURL = properties.getModelURL();
data.translation = properties.getLocalPosition();
data.rotation = properties.getLocalRotation();
data.isSoft = properties.getRelayParentJoints();
int jointIndex = (int)properties.getParentJointIndex();
if (jointIndex > -1 && jointIndex < getJointNames().size()) {
data.jointName = getJointNames()[jointIndex];
}
return data;
}
void MyAvatar::attachmentDataToEntityProperties(const AttachmentData& data, EntityItemProperties& properties) {
QString url = data.modelURL.toString();
properties.setName(QFileInfo(url).baseName());
properties.setType(EntityTypes::Model);
properties.setParentID(AVATAR_SELF_ID);
properties.setLocalPosition(data.translation);
properties.setLocalRotation(data.rotation);
if (!data.isSoft) {
properties.setParentJointIndex(getJointIndex(data.jointName));
} else {
properties.setRelayParentJoints(true);
}
properties.setModelURL(url);
}
void MyAvatar::initHeadBones() {
@ -2336,6 +2552,23 @@ void MyAvatar::updateOrientation(float deltaTime) {
}
}
static float scaleSpeedByDirection(const glm::vec2 velocityDirection, const float forwardSpeed, const float backwardSpeed) {
// for the elipse function --> (x^2)/(backwardSpeed*backwardSpeed) + y^2/(forwardSpeed*forwardSpeed) = 1, scale == y^2 when x is 0
float fwdScale = forwardSpeed * forwardSpeed;
float backScale = backwardSpeed * backwardSpeed;
float scaledX = velocityDirection.x * backwardSpeed;
float scaledSpeed = forwardSpeed;
if (velocityDirection.y < 0.0f) {
if (backScale > 0.0f) {
float yValue = sqrtf(fwdScale * (1.0f - ((scaledX * scaledX) / backScale)));
scaledSpeed = sqrtf((scaledX * scaledX) + (yValue * yValue));
}
} else {
scaledSpeed = backwardSpeed;
}
return scaledSpeed;
}
void MyAvatar::updateActionMotor(float deltaTime) {
bool thrustIsPushing = (glm::length2(_thrust) > EPSILON);
bool scriptedMotorIsPushing = (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED)
@ -2397,7 +2630,10 @@ void MyAvatar::updateActionMotor(float deltaTime) {
_actionMotorVelocity = motorSpeed * direction;
} else {
// we're interacting with a floor --> simple horizontal speed and exponential decay
_actionMotorVelocity = getSensorToWorldScale() * (_walkSpeed.get() * _walkSpeedScalar) * direction;
const glm::vec2 currentVel = { direction.x, direction.z };
float scaledSpeed = scaleSpeedByDirection(currentVel, _walkSpeed.get(), _walkBackwardSpeed.get());
// _walkSpeedScalar is a multiplier if we are in sprint mode, otherwise 1.0
_actionMotorVelocity = getSensorToWorldScale() * (scaledSpeed * _walkSpeedScalar) * direction;
}
float previousBoomLength = _boomLength;
@ -3449,18 +3685,34 @@ float MyAvatar::getWalkSpeed() const {
return _walkSpeed.get() * _walkSpeedScalar;
}
float MyAvatar::getWalkBackwardSpeed() const {
return _walkSpeed.get() * _walkSpeedScalar;
}
bool MyAvatar::isReadyForPhysics() const {
return qApp->isServerlessMode() || _haveReceivedHeightLimitsFromDomain;
}
void MyAvatar::setSprintMode(bool sprint) {
_walkSpeedScalar = sprint ? AVATAR_SPRINT_SPEED_SCALAR : AVATAR_WALK_SPEED_SCALAR;
_walkSpeedScalar = sprint ? _sprintSpeed.get() : AVATAR_WALK_SPEED_SCALAR;
}
void MyAvatar::setWalkSpeed(float value) {
_walkSpeed.set(value);
}
void MyAvatar::setWalkBackwardSpeed(float value) {
_walkBackwardSpeed.set(value);
}
void MyAvatar::setSprintSpeed(float value) {
_sprintSpeed.set(value);
}
float MyAvatar::getSprintSpeed() const {
return _sprintSpeed.get();
}
QVector<QString> MyAvatar::getScriptUrls() {
QVector<QString> scripts = _skeletonModel->isLoaded() ? _skeletonModel->getFBXGeometry().scripts : QVector<QString>();
return scripts;

View file

@ -138,12 +138,14 @@ class MyAvatar : public Avatar {
* where MyAvatar.sessionUUID is not available (e.g., if not connected to a domain). Note: Likely to be deprecated.
* <em>Read-only.</em>
* @property {number} walkSpeed
* @property {number} walkBackwardSpeed
* @property {number} sprintSpeed
*
* @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the
* registration point of the 3D model.
*
* @property {Vec3} position
* @property {number} scale
* @property {number} scale - Returns the clamped scale of the avatar.
* @property {number} density <em>Read-only.</em>
* @property {Vec3} handPosition
* @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar.
@ -233,6 +235,8 @@ class MyAvatar : public Avatar {
Q_PROPERTY(QUuid SELF_ID READ getSelfID CONSTANT)
Q_PROPERTY(float walkSpeed READ getWalkSpeed WRITE setWalkSpeed);
Q_PROPERTY(float walkBackwardSpeed READ getWalkBackwardSpeed WRITE setWalkBackwardSpeed);
Q_PROPERTY(float sprintSpeed READ getSprintSpeed WRITE setSprintSpeed);
const QString DOMINANT_LEFT_HAND = "left";
const QString DOMINANT_RIGHT_HAND = "right";
@ -865,8 +869,6 @@ public:
void resetFullAvatarURL();
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData) override;
MyCharacterController* getCharacterController() { return &_characterController; }
const MyCharacterController* getCharacterController() const { return &_characterController; }
@ -982,7 +984,7 @@ public:
/**jsdoc
* @function MyAvatar.getAvatarScale
* @returns {number}
* @returns {number}
*/
Q_INVOKABLE float getAvatarScale();
@ -1074,6 +1076,10 @@ public:
void setWalkSpeed(float value);
float getWalkSpeed() const;
void setWalkBackwardSpeed(float value);
float getWalkBackwardSpeed() const;
void setSprintSpeed(float value);
float getSprintSpeed() const;
QVector<QString> getScriptUrls();
@ -1082,6 +1088,12 @@ public:
float computeStandingHeightMode(const controller::Pose& head);
glm::quat computeAverageHeadRotation(const controller::Pose& head);
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData) override;
virtual QVector<AttachmentData> getAttachmentData() const override;
virtual QVariantList getAttachmentsVariant() const override;
virtual void setAttachmentsVariant(const QVariantList& variant) override;
public slots:
/**jsdoc
@ -1520,11 +1532,21 @@ private:
void setScriptedMotorTimescale(float timescale);
void setScriptedMotorFrame(QString frame);
void setScriptedMotorMode(QString mode);
// Attachments
virtual void attach(const QString& modelURL, const QString& jointName = QString(),
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(),
float scale = 1.0f, bool isSoft = false,
bool allowDuplicates = false, bool useSaved = true) override;
virtual void detachOne(const QString& modelURL, const QString& jointName = QString()) override;
virtual void detachAll(const QString& modelURL, const QString& jointName = QString()) override;
// Attachments/Avatar Entity
void attachmentDataToEntityProperties(const AttachmentData& data, EntityItemProperties& properties);
AttachmentData entityPropertiesToAttachmentData(const EntityItemProperties& properties) const;
bool findAvatarEntity(const QString& modelURL, const QString& jointName, QUuid& entityID);
bool cameraInsideHead(const glm::vec3& cameraPosition) const;
void updateEyeContactTarget(float deltaTime);
@ -1745,6 +1767,8 @@ private:
// max unscaled forward movement speed
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
ThreadSafeValueCache<float> _walkBackwardSpeed { DEFAULT_AVATAR_MAX_WALKING_BACKWARD_SPEED };
ThreadSafeValueCache<float> _sprintSpeed { AVATAR_SPRINT_SPEED_SCALAR };
float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR };
// load avatar scripts once when rig is ready

View file

@ -352,8 +352,7 @@ bool QmlCommerce::openApp(const QString& itemHref) {
QJsonObject appFileJsonObject = appFileJsonDocument.object();
QString homeUrl = appFileJsonObject["homeURL"].toString();
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(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)) {

View file

@ -328,7 +328,7 @@ Wallet::Wallet() {
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnershipPacket");
packetReceiver.registerListener(PacketType::ChallengeOwnershipRequest, this, "handleChallengeOwnershipPacket");
connect(ledger.data(), &Ledger::accountResult, this, [&](QJsonObject result) {
connect(ledger.data(), &Ledger::accountResult, this, [](QJsonObject result) {
auto wallet = DependencyManager::get<Wallet>();
auto walletScriptingInterface = DependencyManager::get<WalletScriptingInterface>();
uint status;

View file

@ -0,0 +1,353 @@
//
// Created by Sabrina Shanman 7/16/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 "CollisionPick.h"
#include <QtCore/QDebug>
#include <glm/gtx/transform.hpp>
#include "ScriptEngineLogging.h"
#include "UUIDHasher.h"
void buildObjectIntersectionsMap(IntersectionType intersectionType, const std::vector<ContactTestResult>& objectIntersections, std::unordered_map<QUuid, QVariantMap>& intersections, std::unordered_map<QUuid, QVariantList>& collisionPointPairs) {
for (auto& objectIntersection : objectIntersections) {
auto at = intersections.find(objectIntersection.foundID);
if (at == intersections.end()) {
QVariantMap intersectingObject;
intersectingObject["id"] = objectIntersection.foundID;
intersectingObject["type"] = intersectionType;
intersections[objectIntersection.foundID] = intersectingObject;
collisionPointPairs[objectIntersection.foundID] = QVariantList();
}
QVariantMap collisionPointPair;
collisionPointPair["pointOnPick"] = vec3toVariant(objectIntersection.testCollisionPoint);
collisionPointPair["pointOnObject"] = vec3toVariant(objectIntersection.foundCollisionPoint);
collisionPointPairs[objectIntersection.foundID].append(collisionPointPair);
}
}
QVariantMap CollisionPickResult::toVariantMap() const {
QVariantMap variantMap;
variantMap["intersects"] = intersects;
std::unordered_map<QUuid, QVariantMap> intersections;
std::unordered_map<QUuid, QVariantList> collisionPointPairs;
buildObjectIntersectionsMap(ENTITY, entityIntersections, intersections, collisionPointPairs);
buildObjectIntersectionsMap(AVATAR, avatarIntersections, intersections, collisionPointPairs);
QVariantList qIntersectingObjects;
for (auto& intersectionKeyVal : intersections) {
const QUuid& id = intersectionKeyVal.first;
QVariantMap& intersection = intersectionKeyVal.second;
intersection["collisionContacts"] = collisionPointPairs[id];
qIntersectingObjects.append(intersection);
}
variantMap["intersectingObjects"] = qIntersectingObjects;
variantMap["loaded"] = (loadState == LOAD_STATE_LOADED);
variantMap["collisionRegion"] = pickVariant;
return variantMap;
}
bool CollisionPick::isShapeInfoReady() {
if (_mathPick.shouldComputeShapeInfo()) {
if (_cachedResource && _cachedResource->isLoaded()) {
computeShapeInfo(_mathPick, *_mathPick.shapeInfo, _cachedResource);
return true;
}
return false;
}
return true;
}
void CollisionPick::computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource) {
// This code was copied and modified from RenderableModelEntityItem::computeShapeInfo
// TODO: Move to some shared code area (in entities-renderer? model-networking?)
// after we verify this is working and do a diff comparison with RenderableModelEntityItem::computeShapeInfo
// to consolidate the code.
// We may also want to make computeShapeInfo always abstract away from the gpu model mesh, like it does here.
const uint32_t TRIANGLE_STRIDE = 3;
const uint32_t QUAD_STRIDE = 4;
ShapeType type = shapeInfo.getType();
glm::vec3 dimensions = pick.transform.getScale();
if (type == SHAPE_TYPE_COMPOUND) {
// should never fall in here when collision model not fully loaded
// TODO: assert that all geometries exist and are loaded
//assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded());
const FBXGeometry& collisionGeometry = resource->getFBXGeometry();
ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection();
pointCollection.clear();
uint32_t i = 0;
// the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect
// to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case.
foreach (const FBXMesh& mesh, collisionGeometry.meshes) {
// each meshPart is a convex hull
foreach (const FBXMeshPart &meshPart, mesh.parts) {
pointCollection.push_back(QVector<glm::vec3>());
ShapeInfo::PointList& pointsInPart = pointCollection[i];
// run through all the triangles and (uniquely) add each point to the hull
uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size();
// TODO: assert rather than workaround after we start sanitizing FBXMesh higher up
//assert(numIndices % TRIANGLE_STRIDE == 0);
numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader
for (uint32_t j = 0; j < numIndices; j += TRIANGLE_STRIDE) {
glm::vec3 p0 = mesh.vertices[meshPart.triangleIndices[j]];
glm::vec3 p1 = mesh.vertices[meshPart.triangleIndices[j + 1]];
glm::vec3 p2 = mesh.vertices[meshPart.triangleIndices[j + 2]];
if (!pointsInPart.contains(p0)) {
pointsInPart << p0;
}
if (!pointsInPart.contains(p1)) {
pointsInPart << p1;
}
if (!pointsInPart.contains(p2)) {
pointsInPart << p2;
}
}
// run through all the quads and (uniquely) add each point to the hull
numIndices = (uint32_t)meshPart.quadIndices.size();
// TODO: assert rather than workaround after we start sanitizing FBXMesh higher up
//assert(numIndices % QUAD_STRIDE == 0);
numIndices -= numIndices % QUAD_STRIDE; // WORKAROUND lack of sanity checking in FBXReader
for (uint32_t j = 0; j < numIndices; j += QUAD_STRIDE) {
glm::vec3 p0 = mesh.vertices[meshPart.quadIndices[j]];
glm::vec3 p1 = mesh.vertices[meshPart.quadIndices[j + 1]];
glm::vec3 p2 = mesh.vertices[meshPart.quadIndices[j + 2]];
glm::vec3 p3 = mesh.vertices[meshPart.quadIndices[j + 3]];
if (!pointsInPart.contains(p0)) {
pointsInPart << p0;
}
if (!pointsInPart.contains(p1)) {
pointsInPart << p1;
}
if (!pointsInPart.contains(p2)) {
pointsInPart << p2;
}
if (!pointsInPart.contains(p3)) {
pointsInPart << p3;
}
}
if (pointsInPart.size() == 0) {
qCDebug(scriptengine) << "Warning -- meshPart has no faces";
pointCollection.pop_back();
continue;
}
++i;
}
}
// We expect that the collision model will have the same units and will be displaced
// from its origin in the same way the visual model is. The visual model has
// been centered and probably scaled. We take the scaling and offset which were applied
// to the visual model and apply them to the collision model (without regard for the
// collision model's extents).
glm::vec3 scaleToFit = dimensions / resource->getFBXGeometry().getUnscaledMeshExtents().size();
// multiply each point by scale
for (int32_t i = 0; i < pointCollection.size(); i++) {
for (int32_t j = 0; j < pointCollection[i].size(); j++) {
// back compensate for registration so we can apply that offset to the shapeInfo later
pointCollection[i][j] = scaleToFit * pointCollection[i][j];
}
}
shapeInfo.setParams(type, dimensions, resource->getURL().toString());
} else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) {
const FBXGeometry& fbxGeometry = resource->getFBXGeometry();
int numFbxMeshes = fbxGeometry.meshes.size();
int totalNumVertices = 0;
for (int i = 0; i < numFbxMeshes; i++) {
const FBXMesh& mesh = fbxGeometry.meshes.at(i);
totalNumVertices += mesh.vertices.size();
}
const int32_t MAX_VERTICES_PER_STATIC_MESH = 1e6;
if (totalNumVertices > MAX_VERTICES_PER_STATIC_MESH) {
qWarning() << "model" << resource->getURL() << "has too many vertices" << totalNumVertices << "and will collide as a box.";
shapeInfo.setParams(SHAPE_TYPE_BOX, 0.5f * dimensions);
return;
}
auto& meshes = resource->getFBXGeometry().meshes;
int32_t numMeshes = (int32_t)(meshes.size());
const int MAX_ALLOWED_MESH_COUNT = 1000;
if (numMeshes > MAX_ALLOWED_MESH_COUNT) {
// too many will cause the deadlock timer to throw...
shapeInfo.setParams(SHAPE_TYPE_BOX, 0.5f * dimensions);
return;
}
ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection();
pointCollection.clear();
if (type == SHAPE_TYPE_SIMPLE_COMPOUND) {
pointCollection.resize(numMeshes);
} else {
pointCollection.resize(1);
}
ShapeInfo::TriangleIndices& triangleIndices = shapeInfo.getTriangleIndices();
triangleIndices.clear();
Extents extents;
int32_t meshCount = 0;
int32_t pointListIndex = 0;
for (auto& mesh : meshes) {
if (!mesh.vertices.size()) {
continue;
}
QVector<glm::vec3> vertices = mesh.vertices;
ShapeInfo::PointList& points = pointCollection[pointListIndex];
// reserve room
int32_t sizeToReserve = (int32_t)(vertices.count());
if (type == SHAPE_TYPE_SIMPLE_COMPOUND) {
// a list of points for each mesh
pointListIndex++;
} else {
// only one list of points
sizeToReserve += (int32_t)points.size();
}
points.reserve(sizeToReserve);
// copy points
const glm::vec3* vertexItr = vertices.cbegin();
while (vertexItr != vertices.cend()) {
glm::vec3 point = *vertexItr;
points.push_back(point);
extents.addPoint(point);
++vertexItr;
}
if (type == SHAPE_TYPE_STATIC_MESH) {
// copy into triangleIndices
size_t triangleIndicesCount = 0;
for (const FBXMeshPart& meshPart : mesh.parts) {
triangleIndicesCount += meshPart.triangleIndices.count();
}
triangleIndices.reserve((int)triangleIndicesCount);
for (const FBXMeshPart& meshPart : mesh.parts) {
const int* indexItr = meshPart.triangleIndices.cbegin();
while (indexItr != meshPart.triangleIndices.cend()) {
triangleIndices.push_back(*indexItr);
++indexItr;
}
}
} else if (type == SHAPE_TYPE_SIMPLE_COMPOUND) {
// for each mesh copy unique part indices, separated by special bogus (flag) index values
for (const FBXMeshPart& meshPart : mesh.parts) {
// collect unique list of indices for this part
std::set<int32_t> uniqueIndices;
auto numIndices = meshPart.triangleIndices.count();
// TODO: assert rather than workaround after we start sanitizing FBXMesh higher up
//assert(numIndices% TRIANGLE_STRIDE == 0);
numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader
auto indexItr = meshPart.triangleIndices.cbegin();
while (indexItr != meshPart.triangleIndices.cend()) {
uniqueIndices.insert(*indexItr);
++indexItr;
}
// store uniqueIndices in triangleIndices
triangleIndices.reserve(triangleIndices.size() + (int32_t)uniqueIndices.size());
for (auto index : uniqueIndices) {
triangleIndices.push_back(index);
}
// flag end of part
triangleIndices.push_back(END_OF_MESH_PART);
}
// flag end of mesh
triangleIndices.push_back(END_OF_MESH);
}
++meshCount;
}
// scale and shift
glm::vec3 extentsSize = extents.size();
glm::vec3 scaleToFit = dimensions / extentsSize;
for (int32_t i = 0; i < 3; ++i) {
if (extentsSize[i] < 1.0e-6f) {
scaleToFit[i] = 1.0f;
}
}
for (auto points : pointCollection) {
for (int32_t i = 0; i < points.size(); ++i) {
points[i] = (points[i] * scaleToFit);
}
}
shapeInfo.setParams(type, 0.5f * dimensions, resource->getURL().toString());
}
}
CollisionRegion CollisionPick::getMathematicalPick() const {
return _mathPick;
}
const std::vector<ContactTestResult> CollisionPick::filterIntersections(const std::vector<ContactTestResult>& intersections) const {
std::vector<ContactTestResult> filteredIntersections;
const QVector<QUuid>& ignoreItems = getIgnoreItems();
const QVector<QUuid>& includeItems = getIncludeItems();
bool isWhitelist = includeItems.size();
for (const auto& intersection : intersections) {
const QUuid& id = intersection.foundID;
if (!ignoreItems.contains(id) && (!isWhitelist || includeItems.contains(id))) {
filteredIntersections.push_back(intersection);
}
}
return filteredIntersections;
}
PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pick) {
if (!isShapeInfoReady()) {
// Cannot compute result
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
}
const auto& entityIntersections = filterIntersections(_physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_ENTITY, *pick.shapeInfo, pick.transform));
return std::make_shared<CollisionPickResult>(pick, CollisionPickResult::LOAD_STATE_LOADED, 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>());
}
PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pick) {
if (!isShapeInfoReady()) {
// Cannot compute result
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
}
const auto& avatarIntersections = filterIntersections(_physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_AVATAR, *pick.shapeInfo, pick.transform));
return std::make_shared<CollisionPickResult>(pick, CollisionPickResult::LOAD_STATE_LOADED, 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>());
}

View file

@ -0,0 +1,102 @@
//
// Created by Sabrina Shanman 7/11/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_CollisionPick_h
#define hifi_CollisionPick_h
#include <PhysicsEngine.h>
#include <model-networking/ModelCache.h>
#include <RegisteredMetaTypes.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) :
PickResult(searchRegion.toVariantMap()),
loadState(loadState),
intersects(entityIntersections.size() || avatarIntersections.size()),
entityIntersections(entityIntersections),
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;
QVariantMap toVariantMap() const override;
bool doesIntersect() const override { return intersects; }
bool checkOrFilterAgainstMaxDistance(float maxDistance) override { return true; }
PickResultPointer compareAndProcessNewResult(const PickResultPointer& newRes) override {
const std::shared_ptr<CollisionPickResult> newCollisionResult = std::static_pointer_cast<CollisionPickResult>(newRes);
for (ContactTestResult& entityIntersection : newCollisionResult->entityIntersections) {
entityIntersections.push_back(entityIntersection);
}
for (ContactTestResult& avatarIntersection : newCollisionResult->avatarIntersections) {
avatarIntersections.push_back(avatarIntersection);
}
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);
}
};
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);
}
}
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>());
}
PickResultPointer getEntityIntersection(const CollisionRegion& pick) override;
PickResultPointer getOverlayIntersection(const CollisionRegion& pick) override;
PickResultPointer getAvatarIntersection(const CollisionRegion& pick) override;
PickResultPointer getHUDIntersection(const CollisionRegion& pick) override;
protected:
// Returns true if pick.shapeInfo is valid. Otherwise, attempts to get the shapeInfo ready for use.
bool isShapeInfoReady();
void computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
const std::vector<ContactTestResult> filterIntersections(const std::vector<ContactTestResult>& intersections) const;
CollisionRegion _mathPick;
PhysicsEnginePointer _physicsEngine;
QSharedPointer<GeometryResource> _cachedResource;
};
#endif // hifi_CollisionPick_h

View file

@ -11,6 +11,7 @@
#include <QVariant>
#include "GLMHelpers.h"
#include "Application.h"
#include <PickManager.h>
#include "StaticRayPick.h"
@ -20,6 +21,7 @@
#include "StaticParabolaPick.h"
#include "JointParabolaPick.h"
#include "MouseParabolaPick.h"
#include "CollisionPick.h"
#include <ScriptEngine.h>
@ -31,6 +33,8 @@ unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type,
return createStylusPick(properties);
case PickQuery::PickType::Parabola:
return createParabolaPick(properties);
case PickQuery::PickType::Collision:
return createCollisionPick(properties);
default:
return PickManager::INVALID_PICK_ID;
}
@ -234,6 +238,48 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti
return PickManager::INVALID_PICK_ID;
}
/**jsdoc
* A Shape defines a physical volume.
*
* @typedef {object} Shape
* @property {string} shapeType The type of shape to use. Can be one of the following: "box", "sphere", "capsule-x", "capsule-y", "capsule-z", "cylinder-x", "cylinder-y", "cylinder-z"
* @property {Vec3} dimensions - The size to scale the shape to.
*/
// TODO: Add this property to the Shape jsdoc above once model picks work properly
// * @property {string} modelURL - If shapeType is one of: "compound", "simple-hull", "simple-compound", or "static-mesh", this defines the model to load to generate the collision volume.
/**jsdoc
* A set of properties that can be passed to {@link Picks.createPick} to create a new Collision Pick.
* @typedef {object} Picks.CollisionPickProperties
* @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.
*/
unsigned int PickScriptingInterface::createCollisionPick(const QVariant& properties) {
QVariantMap propMap = properties.toMap();
bool enabled = false;
if (propMap["enabled"].isValid()) {
enabled = propMap["enabled"].toBool();
}
PickFilter filter = PickFilter();
if (propMap["filter"].isValid()) {
filter = PickFilter(propMap["filter"].toUInt());
}
float maxDistance = 0.0f;
if (propMap["maxDistance"].isValid()) {
maxDistance = propMap["maxDistance"].toFloat();
}
CollisionRegion collisionRegion(propMap);
return DependencyManager::get<PickManager>()->addPick(PickQuery::Collision, std::make_shared<CollisionPick>(filter, maxDistance, enabled, collisionRegion, qApp->getPhysicsEngine()));
}
void PickScriptingInterface::enablePick(unsigned int uid) {
DependencyManager::get<PickManager>()->enablePick(uid);
}

View file

@ -12,6 +12,7 @@
#include <RegisteredMetaTypes.h>
#include <DependencyManager.h>
#include <PhysicsEngine.h>
#include <Pick.h>
/**jsdoc
@ -62,6 +63,7 @@ class PickScriptingInterface : public QObject, public Dependency {
public:
unsigned int createRayPick(const QVariant& properties);
unsigned int createStylusPick(const QVariant& properties);
unsigned int createCollisionPick(const QVariant& properties);
unsigned int createParabolaPick(const QVariant& properties);
void registerMetaTypes(QScriptEngine* engine);
@ -72,7 +74,7 @@ public:
* with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pick, a Mouse Ray Pick, or a Joint Ray Pick.
* @function Picks.createPick
* @param {PickType} type A PickType that specifies the method of picking to use
* @param {Picks.RayPickProperties|Picks.StylusPickProperties|Picks.ParabolaPickProperties} properties A PickProperties object, containing all the properties for initializing this Pick
* @param {Picks.RayPickProperties|Picks.StylusPickProperties|Picks.ParabolaPickProperties|Picks.CollisionPickProperties} properties A PickProperties object, containing all the properties for initializing this Pick
* @returns {number} The ID of the created Pick. Used for managing the Pick. 0 if invalid.
*/
Q_INVOKABLE unsigned int createPick(const PickQuery::PickType type, const QVariant& properties);
@ -141,11 +143,40 @@ public:
* @property {PickParabola} parabola The PickParabola that was used. Valid even if there was no intersection.
*/
/**jsdoc
* An intersection result for a Collision Pick.
*
* @typedef {object} CollisionPickResult
* @property {boolean} intersects If there was at least one valid intersection (intersectingObjects.length > 0)
* @property {IntersectingObject[]} intersectingObjects The collision information of each object which intersect with the CollisionRegion.
* @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
*
* @typedef {object} IntersectingObject
* @property {QUuid} id The ID of the object.
* @property {number} type The type of the object, either Picks.INTERSECTED_ENTITY() or Picks.INTERSECTED_AVATAR()
* @property {CollisionContact[]} collisionContacts Pairs of points representing penetration information between the pick and the object
*/
/**jsdoc
* A pair of points that represents part of an overlap between a Collision Pick and an object in the physics engine. Points which are further apart represent deeper overlap
*
* @typedef {object} CollisionContact
* @property {Vec3} pointOnPick A point representing a penetration of the object's surface into the volume of the pick, in world space.
* @property {Vec3} pointOnObject A point representing a penetration of the pick's surface into the volume of the found object, in world space.
*/
/**jsdoc
* Get the most recent pick result from this Pick. This will be updated as long as the Pick is enabled.
* @function Picks.getPrevPickResult
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
* @returns {RayPickResult|StylusPickResult} The most recent intersection result. This will be different for different PickTypes.
* @returns {RayPickResult|StylusPickResult|ParabolaPickResult|CollisionPickResult} The most recent intersection result. This will be different for different PickTypes.
*/
Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid);

View file

@ -60,23 +60,20 @@ Audio::Audio() : _devices(_contextIsHMD) {
}
bool Audio::startRecording(const QString& filepath) {
auto client = DependencyManager::get<AudioClient>().data();
return resultWithWriteLock<bool>([&] {
return client->startRecording(filepath);
return DependencyManager::get<AudioClient>()->startRecording(filepath);
});
}
bool Audio::getRecording() {
auto client = DependencyManager::get<AudioClient>().data();
return resultWithReadLock<bool>([&] {
return client->getRecording();
return DependencyManager::get<AudioClient>()->getRecording();
});
}
void Audio::stopRecording() {
auto client = DependencyManager::get<AudioClient>().data();
withWriteLock([&] {
client->stopRecording();
DependencyManager::get<AudioClient>()->stopRecording();
});
}

View file

@ -360,11 +360,11 @@ void AudioInputDeviceList::onPeakValueListChanged(const QList<float>& peakValueL
}
AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) {
auto client = DependencyManager::get<AudioClient>();
auto client = DependencyManager::get<AudioClient>().data();
connect(client.data(), &AudioClient::deviceChanged, this, &AudioDevices::onDeviceChanged, Qt::QueuedConnection);
connect(client.data(), &AudioClient::devicesChanged, this, &AudioDevices::onDevicesChanged, Qt::QueuedConnection);
connect(client.data(), &AudioClient::peakValueListChanged, &_inputs, &AudioInputDeviceList::onPeakValueListChanged, Qt::QueuedConnection);
connect(client, &AudioClient::deviceChanged, this, &AudioDevices::onDeviceChanged, Qt::QueuedConnection);
connect(client, &AudioClient::devicesChanged, this, &AudioDevices::onDevicesChanged, Qt::QueuedConnection);
connect(client, &AudioClient::peakValueListChanged, &_inputs, &AudioInputDeviceList::onPeakValueListChanged, Qt::QueuedConnection);
_inputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioInput), contextIsHMD);
_outputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioOutput), contextIsHMD);
@ -446,7 +446,7 @@ void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceI
static std::once_flag once;
std::call_once(once, [&] {
//readout settings
auto client = DependencyManager::get<AudioClient>();
auto client = DependencyManager::get<AudioClient>().data();
_inputs._hmdSavedDeviceName = getTargetDevice(true, QAudio::AudioInput);
_inputs._desktopSavedDeviceName = getTargetDevice(false, QAudio::AudioInput);
@ -494,9 +494,9 @@ void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceI
void AudioDevices::chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD) {
//check if current context equals device to change
if (_contextIsHMD == isHMD) {
auto client = DependencyManager::get<AudioClient>();
auto client = DependencyManager::get<AudioClient>().data();
_requestedInputDevice = device;
QMetaObject::invokeMethod(client.data(), "switchAudioDevice",
QMetaObject::invokeMethod(client, "switchAudioDevice",
Q_ARG(QAudio::Mode, QAudio::AudioInput),
Q_ARG(const QAudioDeviceInfo&, device));
} else {
@ -511,9 +511,9 @@ void AudioDevices::chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD)
void AudioDevices::chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD) {
//check if current context equals device to change
if (_contextIsHMD == isHMD) {
auto client = DependencyManager::get<AudioClient>();
auto client = DependencyManager::get<AudioClient>().data();
_requestedOutputDevice = device;
QMetaObject::invokeMethod(client.data(), "switchAudioDevice",
QMetaObject::invokeMethod(client, "switchAudioDevice",
Q_ARG(QAudio::Mode, QAudio::AudioOutput),
Q_ARG(const QAudioDeviceInfo&, device));
} else {

View file

@ -312,7 +312,7 @@ JSConsole::~JSConsole() {
delete _ui;
}
void JSConsole::setScriptEngine(const ScriptEnginePointer& scriptEngine) {
void JSConsole::setScriptEngine(const ScriptEnginePointer& scriptEngine) {
if (_scriptEngine == scriptEngine && scriptEngine != nullptr) {
return;
}

View file

@ -162,9 +162,7 @@ void LoginDialog::createAccountFromStream(QString username) {
}
void LoginDialog::openUrl(const QString& url) const {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
auto tablet = dynamic_cast<TabletProxy*>(DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system"));
auto hmd = DependencyManager::get<HMDScriptingInterface>();
auto offscreenUi = DependencyManager::get<OffscreenUi>();

View file

@ -27,28 +27,27 @@
void setupPreferences() {
auto preferences = DependencyManager::get<Preferences>();
auto nodeList = DependencyManager::get<NodeList>();
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
static const QString AVATAR_BASICS { "Avatar Basics" };
{
auto getter = [=]()->QString { return myAvatar->getDisplayName(); };
auto setter = [=](const QString& value) { myAvatar->setDisplayName(value); };
auto getter = [myAvatar]()->QString { return myAvatar->getDisplayName(); };
auto setter = [myAvatar](const QString& value) { myAvatar->setDisplayName(value); };
auto preference = new EditPreference(AVATAR_BASICS, "Avatar display name (optional)", getter, setter);
preference->setPlaceholderText("Not showing a name");
preferences->addPreference(preference);
}
{
auto getter = [=]()->QString { return myAvatar->getCollisionSoundURL(); };
auto setter = [=](const QString& value) { myAvatar->setCollisionSoundURL(value); };
auto getter = [myAvatar]()->QString { return myAvatar->getCollisionSoundURL(); };
auto setter = [myAvatar](const QString& value) { myAvatar->setCollisionSoundURL(value); };
auto preference = new EditPreference(AVATAR_BASICS, "Avatar collision sound URL (optional)", getter, setter);
preference->setPlaceholderText("Enter the URL of a sound to play when you bump into something");
preferences->addPreference(preference);
}
{
auto getter = [=]()->QString { return myAvatar->getFullAvatarURLFromPreferences().toString(); };
auto setter = [=](const QString& value) { myAvatar->useFullAvatarURL(value, ""); qApp->clearAvatarOverrideUrl(); };
auto getter = [myAvatar]()->QString { return myAvatar->getFullAvatarURLFromPreferences().toString(); };
auto setter = [myAvatar](const QString& value) { myAvatar->useFullAvatarURL(value, ""); qApp->clearAvatarOverrideUrl(); };
auto preference = new AvatarPreference(AVATAR_BASICS, "Appearance", getter, setter);
preferences->addPreference(preference);
}
@ -163,8 +162,8 @@ void setupPreferences() {
static const QString VIEW_CATEGORY{ "View" };
{
auto getter = [=]()->float { return myAvatar->getRealWorldFieldOfView(); };
auto setter = [=](float value) { myAvatar->setRealWorldFieldOfView(value); };
auto getter = [myAvatar]()->float { return myAvatar->getRealWorldFieldOfView(); };
auto setter = [myAvatar](float value) { myAvatar->setRealWorldFieldOfView(value); };
auto preference = new SpinnerPreference(VIEW_CATEGORY, "Real world vertical field of view (angular size of monitor)", getter, setter);
preference->setMin(1);
preference->setMax(180);
@ -219,13 +218,13 @@ void setupPreferences() {
static const QString AVATAR_TUNING { "Avatar Tuning" };
{
auto getter = [=]()->QString { return myAvatar->getDominantHand(); };
auto setter = [=](const QString& value) { myAvatar->setDominantHand(value); };
auto getter = [myAvatar]()->QString { return myAvatar->getDominantHand(); };
auto setter = [myAvatar](const QString& value) { myAvatar->setDominantHand(value); };
preferences->addPreference(new PrimaryHandPreference(AVATAR_TUNING, "Dominant Hand", getter, setter));
}
{
auto getter = [=]()->float { return myAvatar->getTargetScale(); };
auto setter = [=](float value) { myAvatar->setTargetScale(value); };
auto getter = [myAvatar]()->float { return myAvatar->getTargetScale(); };
auto setter = [myAvatar](float value) { myAvatar->setTargetScale(value); };
auto preference = new SpinnerSliderPreference(AVATAR_TUNING, "Avatar Scale", getter, setter);
preference->setMin(0.25);
preference->setMax(4);
@ -240,16 +239,16 @@ void setupPreferences() {
}
{
auto getter = [=]()->QString { return myAvatar->getAnimGraphOverrideUrl().toString(); };
auto setter = [=](const QString& value) { myAvatar->setAnimGraphOverrideUrl(QUrl(value)); };
auto getter = [myAvatar]()->QString { return myAvatar->getAnimGraphOverrideUrl().toString(); };
auto setter = [myAvatar](const QString& value) { myAvatar->setAnimGraphOverrideUrl(QUrl(value)); };
auto preference = new EditPreference(AVATAR_TUNING, "Avatar animation JSON", getter, setter);
preference->setPlaceholderText("default");
preferences->addPreference(preference);
}
{
auto getter = [=]()->bool { return myAvatar->getCollisionsEnabled(); };
auto setter = [=](bool value) { myAvatar->setCollisionsEnabled(value); };
auto getter = [myAvatar]()->bool { return myAvatar->getCollisionsEnabled(); };
auto setter = [myAvatar](bool value) { myAvatar->setCollisionsEnabled(value); };
auto preference = new CheckPreference(AVATAR_TUNING, "Enable Avatar collisions", getter, setter);
preferences->addPreference(preference);
}
@ -270,20 +269,20 @@ void setupPreferences() {
{
static const QString movementsControlChannel = QStringLiteral("Hifi-Advanced-Movement-Disabler");
auto getter = [=]()->bool { return myAvatar->useAdvancedMovementControls(); };
auto setter = [=](bool value) { myAvatar->setUseAdvancedMovementControls(value); };
auto getter = [myAvatar]()->bool { return myAvatar->useAdvancedMovementControls(); };
auto setter = [myAvatar](bool value) { myAvatar->setUseAdvancedMovementControls(value); };
preferences->addPreference(new CheckPreference(VR_MOVEMENT,
QStringLiteral("Advanced movement for hand controllers"),
getter, setter));
}
{
auto getter = [=]()->bool { return myAvatar->getFlyingHMDPref(); };
auto setter = [=](bool value) { myAvatar->setFlyingHMDPref(value); };
auto getter = [myAvatar]()->bool { return myAvatar->getFlyingHMDPref(); };
auto setter = [myAvatar](bool value) { myAvatar->setFlyingHMDPref(value); };
preferences->addPreference(new CheckPreference(VR_MOVEMENT, "Flying & jumping (HMD)", getter, setter));
}
{
auto getter = [=]()->int { return myAvatar->getSnapTurn() ? 0 : 1; };
auto setter = [=](int value) { myAvatar->setSnapTurn(value == 0); };
auto getter = [myAvatar]()->int { return myAvatar->getSnapTurn() ? 0 : 1; };
auto setter = [myAvatar](int value) { myAvatar->setSnapTurn(value == 0); };
auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Snap turn / Smooth turn", getter, setter);
QStringList items;
items << "Snap turn" << "Smooth turn";
@ -309,8 +308,8 @@ void setupPreferences() {
static const QString AVATAR_CAMERA{ "Mouse Sensitivity" };
{
auto getter = [=]()->float { return myAvatar->getPitchSpeed(); };
auto setter = [=](float value) { myAvatar->setPitchSpeed(value); };
auto getter = [myAvatar]()->float { return myAvatar->getPitchSpeed(); };
auto setter = [myAvatar](float value) { myAvatar->setPitchSpeed(value); };
auto preference = new SpinnerSliderPreference(AVATAR_CAMERA, "Y input:", getter, setter);
preference->setMin(1.0f);
preference->setMax(360.0f);
@ -319,8 +318,8 @@ void setupPreferences() {
preferences->addPreference(preference);
}
{
auto getter = [=]()->float { return myAvatar->getYawSpeed(); };
auto setter = [=](float value) { myAvatar->setYawSpeed(value); };
auto getter = [myAvatar]()->float { return myAvatar->getYawSpeed(); };
auto setter = [myAvatar](float value) { myAvatar->setYawSpeed(value); };
auto preference = new SpinnerSliderPreference(AVATAR_CAMERA, "X input:", getter, setter);
preference->setMin(1.0f);
preference->setMax(360.0f);
@ -381,12 +380,24 @@ void setupPreferences() {
{
static const QString NETWORKING("Networking");
auto nodelist = DependencyManager::get<NodeList>();
QWeakPointer<NodeList> nodeListWeak = DependencyManager::get<NodeList>();
{
static const int MIN_PORT_NUMBER { 0 };
static const int MAX_PORT_NUMBER { 65535 };
auto getter = [nodelist] { return static_cast<int>(nodelist->getSocketLocalPort()); };
auto setter = [nodelist](int preset) { nodelist->setSocketLocalPort(static_cast<quint16>(preset)); };
auto getter = [nodeListWeak] {
auto nodeList = nodeListWeak.lock();
if (nodeList) {
return static_cast<int>(nodeList->getSocketLocalPort());
} else {
return -1;
}
};
auto setter = [nodeListWeak](int preset) {
auto nodeList = nodeListWeak.lock();
if (nodeList) {
nodeList->setSocketLocalPort(static_cast<quint16>(preset));
}
};
auto preference = new IntSpinnerPreference(NETWORKING, "Listening Port", getter, setter);
preference->setMin(MIN_PORT_NUMBER);
preference->setMax(MAX_PORT_NUMBER);

View file

@ -174,7 +174,7 @@ void Stats::updateStats(bool force) {
int octreeServerCount = 0;
int pingOctreeMax = 0;
int totalEntityKbps = 0;
nodeList->eachNode([&](const SharedNodePointer& node) {
nodeList->eachNode([&totalPingOctree, &totalEntityKbps, &octreeServerCount, &pingOctreeMax](const SharedNodePointer& node) {
// TODO: this should also support entities
if (node->getType() == NodeType::EntityServer) {
totalPingOctree += node->getPingMs();
@ -191,6 +191,14 @@ void Stats::updateStats(bool force) {
// Third column, avatar stats
auto myAvatar = avatarManager->getMyAvatar();
auto animStack = myAvatar->getSkeletonModel()->getRig().getAnimStack();
_animStackNames.clear();
for (auto animStackIterator = animStack.begin(); animStackIterator != animStack.end(); ++animStackIterator) {
_animStackNames << animStackIterator->first + ": " + QString::number(animStackIterator->second,'f',3);
}
emit animStackNamesChanged();
glm::vec3 avatarPos = myAvatar->getWorldPosition();
STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z));
STAT_UPDATE_FLOAT(speed, glm::length(myAvatar->getWorldVelocity()), 0.01f);
@ -211,7 +219,7 @@ void Stats::updateStats(bool force) {
STAT_UPDATE_FLOAT(myAvatarSendRate, avatarManager->getMyAvatarSendRate(), 0.1f);
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
auto audioClient = DependencyManager::get<AudioClient>();
auto audioClient = DependencyManager::get<AudioClient>().data();
if (audioMixerNode || force) {
STAT_UPDATE(audioMixerKbps, (int)roundf(
bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) +
@ -251,7 +259,6 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount());
STAT_UPDATE(processing, DependencyManager::get<StatTracker>()->getStat("Processing").toInt());
STAT_UPDATE(processingPending, DependencyManager::get<StatTracker>()->getStat("PendingProcessing").toInt());
// See if the active download urls have changed
bool shouldUpdateUrls = _downloads != _downloadUrls.size();
@ -346,7 +353,6 @@ void Stats::updateStats(bool force) {
auto config = qApp->getRenderEngine()->getConfiguration().get();
STAT_UPDATE(engineFrameTime, (float) config->getCPURunTime());
STAT_UPDATE(avatarSimulationTime, (float)avatarManager->getAvatarSimulationTime());
STAT_UPDATE(gpuBuffers, (int)gpu::Context::getBufferGPUCount());
STAT_UPDATE(gpuBufferMemory, (int)BYTES_TO_MB(gpu::Context::getBufferGPUMemSize()));
@ -431,7 +437,7 @@ void Stats::updateStats(bool force) {
// a new Map sorted by average time...
bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
QMap<float, QString> sortedRecords;
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
auto allRecords = PerformanceTimer::getAllTimerRecords();
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
while (i.hasNext()) {
@ -479,7 +485,7 @@ void Stats::updateStats(bool force) {
bool operator<(const SortableStat& other) const { return priority < other.priority; }
};
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
auto allRecords = PerformanceTimer::getAllTimerRecords();
std::priority_queue<SortableStat> idleUpdateStats;
auto itr = allRecords.find("/idle/update");
if (itr != allRecords.end()) {
@ -496,7 +502,7 @@ void Stats::updateStats(bool force) {
};
for (int32_t j = 0; j < categories.size(); ++j) {
QString recordKey = "/idle/update/" + categories[j];
auto record = PerformanceTimer::getTimerRecord(recordKey);
auto& record = allRecords[recordKey];
if (record.getCount()) {
float dt = (float) record.getMovingAverage() / (float)USECS_PER_MSEC;
QString message = QString("\n %1 = %2").arg(categories[j]).arg(dt);

View file

@ -135,6 +135,7 @@ private: \
* @property {number} batchFrameTime - <em>Read-only.</em>
* @property {number} engineFrameTime - <em>Read-only.</em>
* @property {number} avatarSimulationTime - <em>Read-only.</em>
* @property {string[]} animStackNames - <em>Read-only.</em>
*
*
* @property {number} x
@ -282,6 +283,7 @@ class Stats : public QQuickItem {
STATS_PROPERTY(float, batchFrameTime, 0)
STATS_PROPERTY(float, engineFrameTime, 0)
STATS_PROPERTY(float, avatarSimulationTime, 0)
Q_PROPERTY(QStringList animStackNames READ animStackNames NOTIFY animStackNamesChanged)
public:
static Stats* getInstance();
@ -306,6 +308,7 @@ public:
}
QStringList downloadUrls () { return _downloadUrls; }
QStringList animStackNames() { return _animStackNames; }
public slots:
void forceUpdateStats() { updateStats(true); }
@ -999,6 +1002,13 @@ signals:
*/
void avatarSimulationTimeChanged();
/**jsdoc
* Triggered when the value of the <code>animStackNames</code> property changes.
* @function Stats.animStackNamesChanged
* @returns {Signal}
*/
void animStackNamesChanged();
/**jsdoc
* Triggered when the value of the <code>rectifiedTextureCount</code> property changes.
* @function Stats.rectifiedTextureCountChanged
@ -1244,6 +1254,7 @@ private:
QString _monospaceFont;
const AudioIOStats* _audioStats;
QStringList _downloadUrls = QStringList();
QStringList _animStackNames = QStringList();
};
#endif // hifi_Stats_h

View file

@ -23,8 +23,7 @@ TestingDialog::TestingDialog(QWidget* parent) :
_console->setFixedHeight(TESTING_CONSOLE_HEIGHT);
auto _engines = DependencyManager::get<ScriptEngines>();
_engine = _engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath);
_engine = DependencyManager::get<ScriptEngines>()->loadScript(qApp->applicationDirPath() + testRunnerRelativePath);
_console->setScriptEngine(_engine);
connect(_engine.data(), &ScriptEngine::finished, this, &TestingDialog::onTestingFinished);
}

View file

@ -184,9 +184,11 @@ void Web3DOverlay::buildWebSurface() {
_webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
} else {
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), qmlSurfaceDeleter);
connect(_webSurface.data(), &hifi::qml::OffscreenSurface::rootContextCreated, [this](QQmlContext* surfaceContext) {
setupQmlSurface(_url == TabletScriptingInterface::QML);
});
_webSurface->load(_url);
_cachedWebSurface = false;
setupQmlSurface();
}
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getWorldPosition()));
onResizeWebSurface();
@ -214,7 +216,7 @@ bool Web3DOverlay::isWebContent() const {
return false;
}
void Web3DOverlay::setupQmlSurface() {
void Web3DOverlay::setupQmlSurface(bool isTablet) {
_webSurface->getSurfaceContext()->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
@ -225,7 +227,7 @@ void Web3DOverlay::setupQmlSurface() {
_webSurface->getSurfaceContext()->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("Snapshot", DependencyManager::get<Snapshot>().data());
if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") {
if (isTablet) {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto flags = tabletScriptingInterface->getFlags();

View file

@ -79,7 +79,7 @@ protected:
Transform evalRenderTransform() override;
private:
void setupQmlSurface();
void setupQmlSurface(bool isTablet);
void rebuildWebSurface();
bool isWebContent() const;

View file

@ -97,7 +97,7 @@ namespace render {
}
}
GameWorkloadRenderItem::GameWorkloadRenderItem() : _key(render::ItemKey::Builder::opaqueShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1)) {
GameWorkloadRenderItem::GameWorkloadRenderItem() : _key(render::ItemKey::Builder::opaqueShape().withoutShadowCaster().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1)) {
}
render::ItemKey GameWorkloadRenderItem::getKey() const {

View file

@ -27,6 +27,7 @@ AnimBlendLinear::~AnimBlendLinear() {
const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
_alpha = animVars.lookup(_alphaVar, _alpha);
float parentAlpha = _animStack[_id];
if (_children.size() == 0) {
for (auto&& pose : _poses) {
@ -34,16 +35,27 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, con
}
} else if (_children.size() == 1) {
_poses = _children[0]->evaluate(animVars, context, dt, triggersOut);
_animStack[_children[0]->getID()] = parentAlpha;
} else {
float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
size_t prevPoseIndex = glm::floor(clampedAlpha);
size_t nextPoseIndex = glm::ceil(clampedAlpha);
float alpha = glm::fract(clampedAlpha);
auto alpha = glm::fract(clampedAlpha);
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt);
}
// weights are for animation stack debug purposes only.
float weight1 = 0.0f;
float weight2 = 0.0f;
if (prevPoseIndex == nextPoseIndex) {
weight2 = 1.0f;
_animStack[_children[nextPoseIndex]->getID()] = weight2 * parentAlpha;
} else {
weight2 = alpha;
weight1 = 1.0f - weight2;
_animStack[_children[prevPoseIndex]->getID()] = weight1 * parentAlpha;
_animStack[_children[nextPoseIndex]->getID()] = weight2 * parentAlpha;
}
}
processOutputJoints(triggersOut);
return _poses;

View file

@ -26,13 +26,46 @@ AnimBlendLinearMove::~AnimBlendLinearMove() {
}
static float calculateAlpha(const float speed, const std::vector<float>& characteristicSpeeds) {
assert(characteristicSpeeds.size() > 0);
// calculate alpha from linear combination of referenceSpeeds.
float alpha = 0.0f;
if (speed <= characteristicSpeeds.front()) {
alpha = 0.0f;
} else if (speed > characteristicSpeeds.back()) {
alpha = (float)(characteristicSpeeds.size() - 1);
} else {
for (size_t i = 0; i < characteristicSpeeds.size() - 1; i++) {
if (characteristicSpeeds[i] < speed && speed < characteristicSpeeds[i + 1]) {
alpha = (float)i + ((speed - characteristicSpeeds[i]) / (characteristicSpeeds[i + 1] - characteristicSpeeds[i]));
break;
}
}
}
return alpha;
}
const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
assert(_children.size() == _characteristicSpeeds.size());
_alpha = animVars.lookup(_alphaVar, _alpha);
_desiredSpeed = animVars.lookup(_desiredSpeedVar, _desiredSpeed);
float speed = 0.0f;
if (_alphaVar.contains("Lateral")) {
speed = animVars.lookup("moveLateralSpeed", speed);
} else if (_alphaVar.contains("Backward")) {
speed = animVars.lookup("moveBackwardSpeed", speed);
} else {
//this is forward movement
speed = animVars.lookup("moveForwardSpeed", speed);
}
_alpha = calculateAlpha(speed, _characteristicSpeeds);
float parentAlpha = _animStack[_id];
_animStack["speed"] = speed;
if (_children.size() == 0) {
for (auto&& pose : _poses) {
pose = AnimPose::identity;
@ -44,8 +77,8 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars,
float prevDeltaTime, nextDeltaTime;
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
_animStack[_children[0]->getID()] = parentAlpha;
} else {
auto clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
auto prevPoseIndex = glm::floor(clampedAlpha);
auto nextPoseIndex = glm::ceil(clampedAlpha);
@ -53,6 +86,19 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars,
float prevDeltaTime, nextDeltaTime;
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
// weights are for animation stack debug purposes only.
float weight1 = 0.0f;
float weight2 = 0.0f;
if (prevPoseIndex == nextPoseIndex) {
weight2 = 1.0f;
_animStack[_children[nextPoseIndex]->getID()] = weight2 * parentAlpha;
} else {
weight2 = alpha;
weight1 = 1.0f - weight2;
_animStack[_children[prevPoseIndex]->getID()] = weight1 * parentAlpha;
_animStack[_children[nextPoseIndex]->getID()] = weight2 * parentAlpha;
}
}
processOutputJoints(triggersOut);

View file

@ -12,6 +12,10 @@
#include <QtGlobal>
std::map<QString, float> AnimNode::_animStack = {
{"none", 0.0f}
};
AnimNode::Pointer AnimNode::getParent() {
return _parent.lock();
}

View file

@ -84,6 +84,7 @@ public:
}
void setCurrentFrame(float frame);
const std::map<QString, float> getAnimStack() { return _animStack; }
template <typename F>
bool traverse(F func) {
@ -126,6 +127,9 @@ protected:
std::weak_ptr<AnimNode> _parent;
std::vector<QString> _outputJointNames;
// global available to Stats.h
static std::map<QString, float> _animStack;
// no copies
AnimNode(const AnimNode&) = delete;
AnimNode& operator=(const AnimNode&) = delete;

View file

@ -661,7 +661,7 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
return false;
}
AnimStateMachine::InterpType interpTypeEnum = AnimStateMachine::InterpType::SnapshotBoth; // default value
AnimStateMachine::InterpType interpTypeEnum = AnimStateMachine::InterpType::SnapshotPrev; // default value
if (!interpType.isEmpty()) {
interpTypeEnum = stringToInterpType(interpType);
if (interpTypeEnum == AnimStateMachine::InterpType::NumTypes) {

View file

@ -23,12 +23,18 @@ AnimStateMachine::~AnimStateMachine() {
const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
if (_id.contains("userAnimStateMachine")) {
_animStack.clear();
}
QString desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID());
if (_currentState->getID() != desiredStateID) {
// switch states
bool foundState = false;
for (auto& state : _states) {
if (state->getID() == desiredStateID) {
// parenthesis means previous state, which is a snapshot.
_previousStateID = "(" + _currentState->getID() + ")";
switchState(animVars, context, state);
foundState = true;
break;
@ -42,6 +48,8 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
// evaluate currentState transitions
auto desiredState = evaluateTransitions(animVars);
if (desiredState != _currentState) {
// parenthesis means previous state, which is a snapshot.
_previousStateID = "(" + _currentState->getID() + ")";
switchState(animVars, context, desiredState);
}
@ -49,8 +57,17 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
auto currentStateNode = _children[_currentState->getChildIndex()];
assert(currentStateNode);
if (!_previousStateID.contains("none")) {
_animStack[_previousStateID] = 1.0f - _alpha;
}
if (_duringInterp) {
_alpha += _alphaVel * dt;
if (_alpha > 1.0f) {
_animStack[_currentState->getID()] = 1.0f;
} else {
_animStack[_currentState->getID()] = _alpha;
}
if (_alpha < 1.0f) {
AnimPoseVec* nextPoses = nullptr;
AnimPoseVec* prevPoses = nullptr;
@ -68,20 +85,23 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
} else {
assert(false);
}
if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) {
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), _alpha, &_poses[0]);
}
} else {
_duringInterp = false;
if (_animStack.count(_previousStateID) > 0) {
_animStack.erase(_previousStateID);
}
_previousStateID = "none";
_prevPoses.clear();
_nextPoses.clear();
}
}
if (!_duringInterp) {
_animStack[_currentState->getID()] = 1.0f;
_poses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
}
processOutputJoints(triggersOut);
return _poses;

View file

@ -133,11 +133,12 @@ protected:
// interpolation state
bool _duringInterp = false;
InterpType _interpType { InterpType::SnapshotBoth };
InterpType _interpType { InterpType::SnapshotPrev };
float _alphaVel = 0.0f;
float _alpha = 0.0f;
AnimPoseVec _prevPoses;
AnimPoseVec _nextPoses;
QString _previousStateID { "none" };
State::Pointer _currentState;
std::vector<State::Pointer> _states;

View file

@ -590,28 +590,6 @@ bool Rig::getAbsoluteJointPoseInRigFrame(int jointIndex, AnimPose& returnPose) c
}
}
void Rig::calcAnimAlpha(float speed, const std::vector<float>& referenceSpeeds, float* alphaOut) const {
ASSERT(referenceSpeeds.size() > 0);
// calculate alpha from linear combination of referenceSpeeds.
float alpha = 0.0f;
if (speed <= referenceSpeeds.front()) {
alpha = 0.0f;
} else if (speed > referenceSpeeds.back()) {
alpha = (float)(referenceSpeeds.size() - 1);
} else {
for (size_t i = 0; i < referenceSpeeds.size() - 1; i++) {
if (referenceSpeeds[i] < speed && speed < referenceSpeeds[i + 1]) {
alpha = (float)i + ((speed - referenceSpeeds[i]) / (referenceSpeeds[i + 1] - referenceSpeeds[i]));
break;
}
}
}
*alphaOut = alpha;
}
void Rig::setEnableInverseKinematics(bool enable) {
_enableInverseKinematics = enable;
}
@ -651,11 +629,6 @@ bool Rig::getRelativeDefaultJointTranslation(int index, glm::vec3& translationOu
}
}
// animation reference speeds.
static const std::vector<float> FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s
static const std::vector<float> BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s
static const std::vector<float> LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState) {
glm::vec3 forward = worldRotation * IDENTITY_FORWARD;
@ -675,24 +648,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
// sine wave LFO var for testing.
static float t = 0.0f;
_animVars.set("sine", 2.0f * 0.5f * sinf(t) + 0.5f);
float moveForwardAlpha = 0.0f;
float moveBackwardAlpha = 0.0f;
float moveLateralAlpha = 0.0f;
// calcuate the animation alpha and timeScale values based on current speeds and animation reference speeds.
calcAnimAlpha(_averageForwardSpeed.getAverage(), FORWARD_SPEEDS, &moveForwardAlpha);
calcAnimAlpha(-_averageForwardSpeed.getAverage(), BACKWARD_SPEEDS, &moveBackwardAlpha);
calcAnimAlpha(fabsf(_averageLateralSpeed.getAverage()), LATERAL_SPEEDS, &moveLateralAlpha);
_animVars.set("moveForwardSpeed", _averageForwardSpeed.getAverage());
_animVars.set("moveForwardAlpha", moveForwardAlpha);
_animVars.set("moveBackwardSpeed", -_averageForwardSpeed.getAverage());
_animVars.set("moveBackwardAlpha", moveBackwardAlpha);
_animVars.set("moveLateralSpeed", fabsf(_averageLateralSpeed.getAverage()));
_animVars.set("moveLateralAlpha", moveLateralAlpha);
const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec
const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec
@ -777,6 +735,8 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
_animVars.set("isMovingBackward", false);
_animVars.set("isMovingRight", false);
_animVars.set("isMovingLeft", false);
_animVars.set("isMovingRightHmd", false);
_animVars.set("isMovingLeftHmd", false);
_animVars.set("isNotMoving", false);
} else {
@ -785,28 +745,48 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
_animVars.set("isMovingForward", false);
_animVars.set("isMovingRight", false);
_animVars.set("isMovingLeft", false);
_animVars.set("isMovingRightHmd", false);
_animVars.set("isMovingLeftHmd", false);
_animVars.set("isNotMoving", false);
}
} else {
if (lateralSpeed > 0.0f) {
// right
_animVars.set("isMovingRight", true);
_animVars.set("isMovingLeft", false);
if (!_headEnabled) {
_animVars.set("isMovingRight", true);
_animVars.set("isMovingLeft", false);
_animVars.set("isMovingRightHmd", false);
_animVars.set("isMovingLeftHmd", false);
} else {
_animVars.set("isMovingRight", false);
_animVars.set("isMovingLeft", false);
_animVars.set("isMovingRightHmd", true);
_animVars.set("isMovingLeftHmd", false);
}
_animVars.set("isMovingForward", false);
_animVars.set("isMovingBackward", false);
_animVars.set("isNotMoving", false);
} else {
// left
_animVars.set("isMovingLeft", true);
_animVars.set("isMovingRight", false);
if (!_headEnabled) {
_animVars.set("isMovingRight", false);
_animVars.set("isMovingLeft", true);
_animVars.set("isMovingRightHmd", false);
_animVars.set("isMovingLeftHmd", false);
} else {
_animVars.set("isMovingRight", false);
_animVars.set("isMovingLeft", false);
_animVars.set("isMovingRightHmd", false);
_animVars.set("isMovingLeftHmd", true);
}
_animVars.set("isMovingForward", false);
_animVars.set("isMovingBackward", false);
_animVars.set("isNotMoving", false);
}
}
}
_animVars.set("isTurningLeft", false);
_animVars.set("isTurningRight", false);
_animVars.set("isTurningLeft", false);
_animVars.set("isNotTurning", true);
_animVars.set("isFlying", false);
_animVars.set("isNotFlying", true);
@ -825,14 +805,16 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
_animVars.set("isNotTurning", false);
} else {
// turning left
_animVars.set("isTurningLeft", true);
_animVars.set("isTurningRight", false);
_animVars.set("isTurningLeft", true);
_animVars.set("isNotTurning", false);
}
_animVars.set("isMovingForward", false);
_animVars.set("isMovingBackward", false);
_animVars.set("isMovingRight", false);
_animVars.set("isMovingLeft", false);
_animVars.set("isMovingRightHmd", false);
_animVars.set("isMovingLeftHmd", false);
_animVars.set("isNotMoving", true);
_animVars.set("isFlying", false);
_animVars.set("isNotFlying", true);
@ -843,15 +825,17 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
_animVars.set("isInAirRun", false);
_animVars.set("isNotInAir", true);
} else if (_state == RigRole::Idle ) {
} else if (_state == RigRole::Idle) {
// default anim vars to notMoving and notTurning
_animVars.set("isMovingForward", false);
_animVars.set("isMovingBackward", false);
_animVars.set("isMovingLeft", false);
_animVars.set("isMovingRight", false);
_animVars.set("isMovingLeft", false);
_animVars.set("isMovingRightHmd", false);
_animVars.set("isMovingLeftHmd", false);
_animVars.set("isNotMoving", true);
_animVars.set("isTurningLeft", false);
_animVars.set("isTurningRight", false);
_animVars.set("isTurningLeft", false);
_animVars.set("isNotTurning", true);
_animVars.set("isFlying", false);
_animVars.set("isNotFlying", true);
@ -866,11 +850,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
// flying.
_animVars.set("isMovingForward", false);
_animVars.set("isMovingBackward", false);
_animVars.set("isMovingLeft", false);
_animVars.set("isMovingRight", false);
_animVars.set("isMovingLeft", false);
_animVars.set("isMovingRightHmd", false);
_animVars.set("isMovingLeftHmd", false);
_animVars.set("isNotMoving", true);
_animVars.set("isTurningLeft", false);
_animVars.set("isTurningRight", false);
_animVars.set("isTurningLeft", false);
_animVars.set("isNotTurning", true);
_animVars.set("isFlying", true);
_animVars.set("isNotFlying", false);
@ -885,11 +871,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
// jumping in-air
_animVars.set("isMovingForward", false);
_animVars.set("isMovingBackward", false);
_animVars.set("isMovingLeft", false);
_animVars.set("isMovingRight", false);
_animVars.set("isMovingLeft", false);
_animVars.set("isMovingRightHmd", false);
_animVars.set("isMovingLeftHmd", false);
_animVars.set("isNotMoving", true);
_animVars.set("isTurningLeft", false);
_animVars.set("isTurningRight", false);
_animVars.set("isTurningLeft", false);
_animVars.set("isNotTurning", true);
_animVars.set("isFlying", false);
_animVars.set("isNotFlying", true);
@ -912,11 +900,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
// jumping in-air
_animVars.set("isMovingForward", false);
_animVars.set("isMovingBackward", false);
_animVars.set("isMovingLeft", false);
_animVars.set("isMovingRight", false);
_animVars.set("isMovingLeft", false);
_animVars.set("isMovingRightHmd", false);
_animVars.set("isMovingLeftHmd", false);
_animVars.set("isNotMoving", true);
_animVars.set("isTurningLeft", false);
_animVars.set("isTurningRight", false);
_animVars.set("isTurningLeft", false);
_animVars.set("isNotTurning", true);
_animVars.set("isFlying", false);
_animVars.set("isNotFlying", true);
@ -1623,7 +1613,7 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
_animVars.set("isTalking", params.isTalking);
_animVars.set("notIsTalking", !params.isTalking);
bool headEnabled = params.primaryControllerFlags[PrimaryControllerType_Head] & (uint8_t)ControllerFlags::Enabled;
_headEnabled = params.primaryControllerFlags[PrimaryControllerType_Head] & (uint8_t)ControllerFlags::Enabled;
bool leftHandEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftHand] & (uint8_t)ControllerFlags::Enabled;
bool rightHandEnabled = params.primaryControllerFlags[PrimaryControllerType_RightHand] & (uint8_t)ControllerFlags::Enabled;
bool hipsEnabled = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Enabled;
@ -1635,18 +1625,18 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
bool rightArmEnabled = params.secondaryControllerFlags[SecondaryControllerType_RightArm] & (uint8_t)ControllerFlags::Enabled;
glm::mat4 sensorToRigMatrix = glm::inverse(params.rigToSensorMatrix);
updateHead(headEnabled, hipsEnabled, params.primaryControllerPoses[PrimaryControllerType_Head]);
updateHead(_headEnabled, hipsEnabled, params.primaryControllerPoses[PrimaryControllerType_Head]);
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, hipsEstimated, leftArmEnabled, rightArmEnabled, headEnabled, dt,
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, hipsEstimated, leftArmEnabled, rightArmEnabled, _headEnabled, dt,
params.primaryControllerPoses[PrimaryControllerType_LeftHand], params.primaryControllerPoses[PrimaryControllerType_RightHand],
params.hipsShapeInfo, params.spineShapeInfo, params.spine1ShapeInfo, params.spine2ShapeInfo,
params.rigToSensorMatrix, sensorToRigMatrix);
updateFeet(leftFootEnabled, rightFootEnabled, headEnabled,
updateFeet(leftFootEnabled, rightFootEnabled, _headEnabled,
params.primaryControllerPoses[PrimaryControllerType_LeftFoot], params.primaryControllerPoses[PrimaryControllerType_RightFoot],
params.rigToSensorMatrix, sensorToRigMatrix);
if (headEnabled) {
if (_headEnabled) {
// Blend IK chains toward the joint limit centers, this should stablize head and hand ik.
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToLimitCenterPoses);
} else {

View file

@ -221,6 +221,9 @@ public:
// input assumed to be in rig space
void computeHeadFromHMD(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut) const;
const std::map<QString, float> getAnimStack() { return _animNode->getAnimStack(); }
void toggleSmoothPoleVectors() { _smoothPoleVectors = !_smoothPoleVectors; };
signals:
void onLoadComplete();
@ -298,6 +301,7 @@ protected:
std::shared_ptr<AnimSkeleton> _animSkeleton;
std::unique_ptr<AnimNodeLoader> _animLoader;
AnimVariantMap _animVars;
enum class RigRole {
Idle = 0,
Turn,
@ -383,6 +387,7 @@ protected:
bool _smoothPoleVectors { false };
int _rigId;
bool _headEnabled { false };
};
#endif /* defined(__hifi__Rig__) */

View file

@ -53,6 +53,7 @@
#include "AudioHelpers.h"
#if defined(Q_OS_ANDROID)
#define VOICE_RECOGNITION "voicerecognition"
#include <QtAndroidExtras/QAndroidJniObject>
#endif
@ -273,31 +274,14 @@ AudioClient::~AudioClient() {
}
void AudioClient::customDeleter() {
deleteLater();
}
void AudioClient::cleanupBeforeQuit() {
// FIXME: this should be put in customDeleter, but there is still a reference to this when it is called,
// so this must be explicitly, synchronously stopped
static ConditionalGuard guard;
if (QThread::currentThread() != thread()) {
// This will likely be called from the main thread, but we don't want to do blocking queued calls
// from the main thread, so we use a normal auto-connection invoke, and then use a conditional to wait
// for completion
// The effect is the same, yes, but we actually want to avoid the use of Qt::BlockingQueuedConnection
// in the code
QMetaObject::invokeMethod(this, "cleanupBeforeQuit");
guard.wait();
return;
}
#if defined(Q_OS_ANDROID)
_shouldRestartInputSetup = false;
#endif
stop();
_checkDevicesTimer->stop();
_checkPeakValuesTimer->stop();
guard.trigger();
deleteLater();
}
void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) {
@ -465,7 +449,16 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
return getNamedAudioDeviceForMode(mode, deviceName);
#endif
#if defined (Q_OS_ANDROID)
if (mode == QAudio::AudioInput) {
auto inputDevices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
for (auto inputDevice : inputDevices) {
if (inputDevice.deviceName() == VOICE_RECOGNITION) {
return inputDevice;
}
}
}
#endif
// fallback for failed lookup is the default device
return (mode == QAudio::AudioInput) ? QAudioDeviceInfo::defaultInputDevice() : QAudioDeviceInfo::defaultOutputDevice();
}
@ -486,15 +479,6 @@ bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
audioFormat.setSampleType(QAudioFormat::SignedInt);
audioFormat.setByteOrder(QAudioFormat::LittleEndian);
#if defined(Q_OS_ANDROID)
// Using the HW sample rate (AUDIO_INPUT_FLAG_FAST) in some samsung phones causes a low volume at input stream
// Changing the sample rate forces a resampling that (in samsung) amplifies +18 dB
QAndroidJniObject brand = QAndroidJniObject::getStaticObjectField<jstring>("android/os/Build", "BRAND");
if (audioDevice == QAudioDeviceInfo::defaultInputDevice() && brand.toString().contains("samsung", Qt::CaseInsensitive)) {
audioFormat.setSampleRate(24000);
}
#endif
if (!audioDevice.isFormatSupported(audioFormat)) {
qCWarning(audioclient) << "The native format is" << audioFormat << "but isFormatSupported() failed.";
return false;
@ -1848,7 +1832,9 @@ const float AudioClient::CALLBACK_ACCELERATOR_RATIO = IsWindows8OrGreater() ? 1.
const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 2.0f;
#endif
#ifdef Q_OS_LINUX
#ifdef Q_OS_ANDROID
const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 0.5f;
#elif defined(Q_OS_LINUX)
const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 2.0f;
#endif

View file

@ -171,7 +171,6 @@ public:
public slots:
void start();
void stop();
void cleanupBeforeQuit();
void handleAudioEnvironmentDataPacket(QSharedPointer<ReceivedMessage> message);
void handleAudioDataPacket(QSharedPointer<ReceivedMessage> message);

View file

@ -95,8 +95,6 @@ void AudioIOStats::processStreamStatsPacket(QSharedPointer<ReceivedMessage> mess
}
void AudioIOStats::publish() {
auto audioIO = DependencyManager::get<AudioClient>();
// call _receivedAudioStream's per-second callback
_receivedAudioStream->perSecondCallbackForUpdatingStats();

View file

@ -1184,6 +1184,15 @@ glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
}
return Quaternions::Y_180 * rotation * Quaternions::Y_180;
}
case FARGRAB_RIGHTHAND_INDEX: {
return extractRotation(_farGrabRightMatrixCache.get());
}
case FARGRAB_LEFTHAND_INDEX: {
return extractRotation(_farGrabLeftMatrixCache.get());
}
case FARGRAB_MOUSE_INDEX: {
return extractRotation(_farGrabMouseMatrixCache.get());
}
default: {
glm::quat rotation;
_skeletonModel->getAbsoluteJointRotationInRigFrame(index, rotation);
@ -1224,6 +1233,15 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
}
return Quaternions::Y_180 * translation * Quaternions::Y_180;
}
case FARGRAB_RIGHTHAND_INDEX: {
return extractTranslation(_farGrabRightMatrixCache.get());
}
case FARGRAB_LEFTHAND_INDEX: {
return extractTranslation(_farGrabLeftMatrixCache.get());
}
case FARGRAB_MOUSE_INDEX: {
return extractTranslation(_farGrabMouseMatrixCache.get());
}
default: {
glm::vec3 translation;
_skeletonModel->getAbsoluteJointTranslationInRigFrame(index, translation);

View file

@ -62,7 +62,7 @@ size_t AvatarDataPacket::maxFaceTrackerInfoSize(size_t numBlendshapeCoefficients
return FACE_TRACKER_INFO_SIZE + numBlendshapeCoefficients * sizeof(float);
}
size_t AvatarDataPacket::maxJointDataSize(size_t numJoints) {
size_t AvatarDataPacket::maxJointDataSize(size_t numJoints, bool hasGrabJoints) {
const size_t validityBitsSize = (size_t)std::ceil(numJoints / (float)BITS_IN_BYTE);
size_t totalSize = sizeof(uint8_t); // numJoints
@ -73,7 +73,8 @@ size_t AvatarDataPacket::maxJointDataSize(size_t numJoints) {
totalSize += numJoints * sizeof(SixByteTrans); // Translations
size_t NUM_FAUX_JOINT = 2;
totalSize += NUM_FAUX_JOINT * (sizeof(SixByteQuat) + sizeof(SixByteTrans)); // faux joints
size_t num_grab_joints = (hasGrabJoints ? 2 : 0);
totalSize += (NUM_FAUX_JOINT + num_grab_joints) * (sizeof(SixByteQuat) + sizeof(SixByteTrans)); // faux joints
return totalSize;
}
@ -227,7 +228,8 @@ QByteArray AvatarData::toByteArrayStateful(AvatarDataDetail dataDetail, bool dro
&_outboundDataRate);
}
QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSentTime, const QVector<JointData>& lastSentJointData,
QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSentTime,
const QVector<JointData>& lastSentJointData,
AvatarDataPacket::HasFlags& hasFlagsOut, bool dropFaceTracking, bool distanceAdjust,
glm::vec3 viewerPosition, QVector<JointData>* sentJointDataOut, AvatarDataRate* outboundDataRateOut) const {
@ -284,6 +286,11 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
bool hasFaceTrackerInfo = false;
bool hasJointData = false;
bool hasJointDefaultPoseFlags = false;
bool hasGrabJoints = false;
glm::mat4 leftFarGrabMatrix;
glm::mat4 rightFarGrabMatrix;
glm::mat4 mouseFarGrabMatrix;
if (sendPALMinimum) {
hasAudioLoudness = true;
@ -304,12 +311,30 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
(sendAll || faceTrackerInfoChangedSince(lastSentTime));
hasJointData = sendAll || !sendMinimum;
hasJointDefaultPoseFlags = hasJointData;
if (hasJointData) {
bool leftValid;
leftFarGrabMatrix = _farGrabLeftMatrixCache.get(leftValid);
if (!leftValid) {
leftFarGrabMatrix = glm::mat4();
}
bool rightValid;
rightFarGrabMatrix = _farGrabRightMatrixCache.get(rightValid);
if (!rightValid) {
rightFarGrabMatrix = glm::mat4();
}
bool mouseValid;
mouseFarGrabMatrix = _farGrabMouseMatrixCache.get(mouseValid);
if (!mouseValid) {
mouseFarGrabMatrix = glm::mat4();
}
hasGrabJoints = (leftValid || rightValid || mouseValid);
}
}
const size_t byteArraySize = AvatarDataPacket::MAX_CONSTANT_HEADER_SIZE +
(hasFaceTrackerInfo ? AvatarDataPacket::maxFaceTrackerInfoSize(_headData->getBlendshapeCoefficients().size()) : 0) +
(hasJointData ? AvatarDataPacket::maxJointDataSize(_jointData.size()) : 0) +
(hasJointData ? AvatarDataPacket::maxJointDataSize(_jointData.size(), hasGrabJoints) : 0) +
(hasJointDefaultPoseFlags ? AvatarDataPacket::maxJointDefaultPoseFlagsSize(_jointData.size()) : 0);
QByteArray avatarDataByteArray((int)byteArraySize, 0);
@ -330,7 +355,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
| (hasAvatarLocalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION : 0)
| (hasFaceTrackerInfo ? AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO : 0)
| (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0)
| (hasJointDefaultPoseFlags ? AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS : 0);
| (hasJointDefaultPoseFlags ? AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS : 0)
| (hasGrabJoints ? AvatarDataPacket::PACKET_HAS_GRAB_JOINTS : 0);
memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags));
destinationBuffer += sizeof(packetStateFlags);
@ -668,6 +694,53 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerRightHandTransform.getTranslation(),
TRANSLATION_COMPRESSION_RADIX);
if (hasGrabJoints) {
// the far-grab joints may range further than 3 meters, so we can't use packFloatVec3ToSignedTwoByteFixed etc
auto startSection = destinationBuffer;
auto data = reinterpret_cast<AvatarDataPacket::FarGrabJoints*>(destinationBuffer);
glm::vec3 leftFarGrabPosition = extractTranslation(leftFarGrabMatrix);
glm::quat leftFarGrabRotation = extractRotation(leftFarGrabMatrix);
glm::vec3 rightFarGrabPosition = extractTranslation(rightFarGrabMatrix);
glm::quat rightFarGrabRotation = extractRotation(rightFarGrabMatrix);
glm::vec3 mouseFarGrabPosition = extractTranslation(mouseFarGrabMatrix);
glm::quat mouseFarGrabRotation = extractRotation(mouseFarGrabMatrix);
data->leftFarGrabPosition[0] = leftFarGrabPosition.x;
data->leftFarGrabPosition[1] = leftFarGrabPosition.y;
data->leftFarGrabPosition[2] = leftFarGrabPosition.z;
data->leftFarGrabRotation[0] = leftFarGrabRotation.w;
data->leftFarGrabRotation[1] = leftFarGrabRotation.x;
data->leftFarGrabRotation[2] = leftFarGrabRotation.y;
data->leftFarGrabRotation[3] = leftFarGrabRotation.z;
data->rightFarGrabPosition[0] = rightFarGrabPosition.x;
data->rightFarGrabPosition[1] = rightFarGrabPosition.y;
data->rightFarGrabPosition[2] = rightFarGrabPosition.z;
data->rightFarGrabRotation[0] = rightFarGrabRotation.w;
data->rightFarGrabRotation[1] = rightFarGrabRotation.x;
data->rightFarGrabRotation[2] = rightFarGrabRotation.y;
data->rightFarGrabRotation[3] = rightFarGrabRotation.z;
data->mouseFarGrabPosition[0] = mouseFarGrabPosition.x;
data->mouseFarGrabPosition[1] = mouseFarGrabPosition.y;
data->mouseFarGrabPosition[2] = mouseFarGrabPosition.z;
data->mouseFarGrabRotation[0] = mouseFarGrabRotation.w;
data->mouseFarGrabRotation[1] = mouseFarGrabRotation.x;
data->mouseFarGrabRotation[2] = mouseFarGrabRotation.y;
data->mouseFarGrabRotation[3] = mouseFarGrabRotation.z;
destinationBuffer += sizeof(AvatarDataPacket::FarGrabJoints);
int numBytes = destinationBuffer - startSection;
if (outboundDataRateOut) {
outboundDataRateOut->farGrabJointRate.increment(numBytes);
}
}
#ifdef WANT_DEBUG
if (sendAll) {
qCDebug(avatars) << "AvatarData::toByteArray" << cullSmallChanges << sendAll
@ -834,6 +907,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
bool hasFaceTrackerInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO);
bool hasJointData = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DATA);
bool hasJointDefaultPoseFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS);
bool hasGrabJoints = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_GRAB_JOINTS);
quint64 now = usecTimestampNow();
@ -1195,6 +1269,34 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
int numBytesRead = sourceBuffer - startSection;
_jointDataRate.increment(numBytesRead);
_jointDataUpdateRate.increment();
if (hasGrabJoints) {
auto startSection = sourceBuffer;
PACKET_READ_CHECK(FarGrabJoints, sizeof(AvatarDataPacket::FarGrabJoints));
auto data = reinterpret_cast<const AvatarDataPacket::FarGrabJoints*>(sourceBuffer);
glm::vec3 leftFarGrabPosition = glm::vec3(data->leftFarGrabPosition[0], data->leftFarGrabPosition[1],
data->leftFarGrabPosition[2]);
glm::quat leftFarGrabRotation = glm::quat(data->leftFarGrabRotation[0], data->leftFarGrabRotation[1],
data->leftFarGrabRotation[2], data->leftFarGrabRotation[3]);
glm::vec3 rightFarGrabPosition = glm::vec3(data->rightFarGrabPosition[0], data->rightFarGrabPosition[1],
data->rightFarGrabPosition[2]);
glm::quat rightFarGrabRotation = glm::quat(data->rightFarGrabRotation[0], data->rightFarGrabRotation[1],
data->rightFarGrabRotation[2], data->rightFarGrabRotation[3]);
glm::vec3 mouseFarGrabPosition = glm::vec3(data->mouseFarGrabPosition[0], data->mouseFarGrabPosition[1],
data->mouseFarGrabPosition[2]);
glm::quat mouseFarGrabRotation = glm::quat(data->mouseFarGrabRotation[0], data->mouseFarGrabRotation[1],
data->mouseFarGrabRotation[2], data->mouseFarGrabRotation[3]);
_farGrabLeftMatrixCache.set(createMatFromQuatAndPos(leftFarGrabRotation, leftFarGrabPosition));
_farGrabRightMatrixCache.set(createMatFromQuatAndPos(rightFarGrabRotation, rightFarGrabPosition));
_farGrabMouseMatrixCache.set(createMatFromQuatAndPos(mouseFarGrabRotation, mouseFarGrabPosition));
sourceBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition);
int numBytesRead = sourceBuffer - startSection;
_farGrabJointRate.increment(numBytesRead);
_farGrabJointUpdateRate.increment();
}
}
if (hasJointDefaultPoseFlags) {
@ -1261,6 +1363,8 @@ float AvatarData::getDataRate(const QString& rateName) const {
return _jointDataRate.rate() / BYTES_PER_KILOBIT;
} else if (rateName == "jointDefaultPoseFlagsRate") {
return _jointDefaultPoseFlagsRate.rate() / BYTES_PER_KILOBIT;
} else if (rateName == "farGrabJointRate") {
return _farGrabJointRate.rate() / BYTES_PER_KILOBIT;
} else if (rateName == "globalPositionOutbound") {
return _outboundDataRate.globalPositionRate.rate() / BYTES_PER_KILOBIT;
} else if (rateName == "localPositionOutbound") {
@ -1318,6 +1422,8 @@ float AvatarData::getUpdateRate(const QString& rateName) const {
return _faceTrackerUpdateRate.rate();
} else if (rateName == "jointData") {
return _jointDataUpdateRate.rate();
} else if (rateName == "farGrabJointData") {
return _farGrabJointUpdateRate.rate();
}
return 0.0f;
}
@ -1344,7 +1450,7 @@ void AvatarData::setRawJointData(QVector<JointData> data) {
}
void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) {
if (index == -1) {
if (index < 0 || index >= LOWEST_PSEUDO_JOINT_INDEX) {
return;
}
QWriteLocker writeLock(&_jointDataLock);
@ -1359,7 +1465,7 @@ void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::v
}
void AvatarData::clearJointData(int index) {
if (index == -1) {
if (index < 0 || index >= LOWEST_PSEUDO_JOINT_INDEX) {
return;
}
QWriteLocker writeLock(&_jointDataLock);
@ -1371,27 +1477,72 @@ void AvatarData::clearJointData(int index) {
}
bool AvatarData::isJointDataValid(int index) const {
if (index == -1) {
return false;
switch (index) {
case FARGRAB_RIGHTHAND_INDEX: {
bool valid;
_farGrabRightMatrixCache.get(valid);
return valid;
}
case FARGRAB_LEFTHAND_INDEX: {
bool valid;
_farGrabLeftMatrixCache.get(valid);
return valid;
}
case FARGRAB_MOUSE_INDEX: {
bool valid;
_farGrabMouseMatrixCache.get(valid);
return valid;
}
default: {
if (index < 0 || index >= LOWEST_PSEUDO_JOINT_INDEX) {
return false;
}
QReadLocker readLock(&_jointDataLock);
return index < _jointData.size();
}
}
QReadLocker readLock(&_jointDataLock);
return index < _jointData.size();
}
glm::quat AvatarData::getJointRotation(int index) const {
if (index == -1) {
return glm::quat();
switch (index) {
case FARGRAB_RIGHTHAND_INDEX: {
return extractRotation(_farGrabRightMatrixCache.get());
}
case FARGRAB_LEFTHAND_INDEX: {
return extractRotation(_farGrabLeftMatrixCache.get());
}
case FARGRAB_MOUSE_INDEX: {
return extractRotation(_farGrabMouseMatrixCache.get());
}
default: {
if (index < 0 || index >= LOWEST_PSEUDO_JOINT_INDEX) {
return glm::quat();
}
QReadLocker readLock(&_jointDataLock);
return index < _jointData.size() ? _jointData.at(index).rotation : glm::quat();
}
}
QReadLocker readLock(&_jointDataLock);
return index < _jointData.size() ? _jointData.at(index).rotation : glm::quat();
}
glm::vec3 AvatarData::getJointTranslation(int index) const {
if (index == -1) {
return glm::vec3();
switch (index) {
case FARGRAB_RIGHTHAND_INDEX: {
return extractTranslation(_farGrabRightMatrixCache.get());
}
case FARGRAB_LEFTHAND_INDEX: {
return extractTranslation(_farGrabLeftMatrixCache.get());
}
case FARGRAB_MOUSE_INDEX: {
return extractTranslation(_farGrabMouseMatrixCache.get());
}
default: {
if (index < 0 || index >= LOWEST_PSEUDO_JOINT_INDEX) {
return glm::vec3();
}
QReadLocker readLock(&_jointDataLock);
return index < _jointData.size() ? _jointData.at(index).translation : glm::vec3();
}
}
QReadLocker readLock(&_jointDataLock);
return index < _jointData.size() ? _jointData.at(index).translation : glm::vec3();
}
glm::vec3 AvatarData::getJointTranslation(const QString& name) const {
@ -1400,6 +1551,7 @@ glm::vec3 AvatarData::getJointTranslation(const QString& name) const {
// return getJointTranslation(getJointIndex(name));
return readLockWithNamedJointIndex<glm::vec3>(name, [this](int index) {
return _jointData.at(index).translation;
return getJointTranslation(index);
});
}
@ -1437,7 +1589,7 @@ void AvatarData::setJointTranslation(const QString& name, const glm::vec3& trans
}
void AvatarData::setJointRotation(int index, const glm::quat& rotation) {
if (index == -1) {
if (index < 0 || index >= LOWEST_PSEUDO_JOINT_INDEX) {
return;
}
QWriteLocker writeLock(&_jointDataLock);
@ -1450,7 +1602,7 @@ void AvatarData::setJointRotation(int index, const glm::quat& rotation) {
}
void AvatarData::setJointTranslation(int index, const glm::vec3& translation) {
if (index == -1) {
if (index < 0 || index >= LOWEST_PSEUDO_JOINT_INDEX) {
return;
}
QWriteLocker writeLock(&_jointDataLock);
@ -1567,6 +1719,15 @@ int AvatarData::getFauxJointIndex(const QString& name) const {
if (name == "_CAMERA_MATRIX") {
return CAMERA_MATRIX_INDEX;
}
if (name == "_FARGRAB_RIGHTHAND") {
return FARGRAB_RIGHTHAND_INDEX;
}
if (name == "_FARGRAB_LEFTHAND") {
return FARGRAB_LEFTHAND_INDEX;
}
if (name == "_FARGRAB_MOUSE") {
return FARGRAB_MOUSE_INDEX;
}
return -1;
}
@ -1908,7 +2069,7 @@ void AvatarData::sendIdentityPacket() {
auto packetList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
packetList->write(identityData);
nodeList->eachMatchingNode(
[&](const SharedNodePointer& node)->bool {
[](const SharedNodePointer& node)->bool {
return node->getType() == NodeType::AvatarMixer && node->getActiveSocket();
},
[&](const SharedNodePointer& node) {

View file

@ -138,6 +138,7 @@ namespace AvatarDataPacket {
const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 10;
const HasFlags PACKET_HAS_JOINT_DATA = 1U << 11;
const HasFlags PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS = 1U << 12;
const HasFlags PACKET_HAS_GRAB_JOINTS = 1U << 13;
const size_t AVATAR_HAS_FLAGS_SIZE = 2;
using SixByteQuat = uint8_t[6];
@ -273,7 +274,7 @@ namespace AvatarDataPacket {
SixByteTrans rightHandControllerTranslation;
};
*/
size_t maxJointDataSize(size_t numJoints);
size_t maxJointDataSize(size_t numJoints, bool hasGrabJoints);
/*
struct JointDefaultPoseFlags {
@ -283,6 +284,17 @@ namespace AvatarDataPacket {
};
*/
size_t maxJointDefaultPoseFlagsSize(size_t numJoints);
PACKED_BEGIN struct FarGrabJoints {
float leftFarGrabPosition[3]; // left controller far-grab joint position
float leftFarGrabRotation[4]; // left controller far-grab joint rotation
float rightFarGrabPosition[3]; // right controller far-grab joint position
float rightFarGrabRotation[4]; // right controller far-grab joint rotation
float mouseFarGrabPosition[3]; // mouse far-grab joint position
float mouseFarGrabRotation[4]; // mouse far-grab joint rotation
} PACKED_END;
const size_t FAR_GRAB_JOINTS_SIZE = 84;
static_assert(sizeof(FarGrabJoints) == FAR_GRAB_JOINTS_SIZE, "AvatarDataPacket::FarGrabJoints size doesn't match.");
}
const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation
@ -347,6 +359,7 @@ public:
RateCounter<> faceTrackerRate;
RateCounter<> jointDataRate;
RateCounter<> jointDefaultPoseFlagsRate;
RateCounter<> farGrabJointRate;
};
class AvatarPriority {
@ -363,7 +376,7 @@ class AvatarData : public QObject, public SpatiallyNestable {
// The following properties have JSDoc in MyAvatar.h and ScriptableAvatar.h
Q_PROPERTY(glm::vec3 position READ getWorldPosition WRITE setPositionViaScript)
Q_PROPERTY(float scale READ getTargetScale WRITE setTargetScale)
Q_PROPERTY(float scale READ getDomainLimitedScale WRITE setTargetScale)
Q_PROPERTY(float density READ getDensity)
Q_PROPERTY(glm::vec3 handPosition READ getHandPosition WRITE setHandPosition)
Q_PROPERTY(float bodyYaw READ getBodyYaw WRITE setBodyYaw)
@ -895,14 +908,14 @@ public:
* @returns {object}
*/
// FIXME: Can this name be improved? Can it be deprecated?
Q_INVOKABLE QVariantList getAttachmentsVariant() const;
Q_INVOKABLE virtual QVariantList getAttachmentsVariant() const;
/**jsdoc
* @function MyAvatar.setAttachmentsVariant
* @param {object} variant
*/
// FIXME: Can this name be improved? Can it be deprecated?
Q_INVOKABLE void setAttachmentsVariant(const QVariantList& variant);
Q_INVOKABLE virtual void setAttachmentsVariant(const QVariantList& variant);
/**jsdoc
@ -969,7 +982,7 @@ public:
* print (attachments[i].modelURL);
* }
*/
Q_INVOKABLE QVector<AttachmentData> getAttachmentData() const;
Q_INVOKABLE virtual QVector<AttachmentData> getAttachmentData() const;
/**jsdoc
* Set all models currently attached to your avatar. For example, if you retrieve attachment data using
@ -1040,7 +1053,7 @@ public:
* @param {string} [jointName=""] - The name of the joint to detach the model from. If <code>""</code>, then the most
* recently attached model is removed from which ever joint it was attached to.
*/
Q_INVOKABLE void detachOne(const QString& modelURL, const QString& jointName = QString());
Q_INVOKABLE virtual void detachOne(const QString& modelURL, const QString& jointName = QString());
/**jsdoc
* Detach all instances of a particular model from either a specific joint or all joints.
@ -1049,7 +1062,7 @@ public:
* @param {string} [jointName=""] - The name of the joint to detach the model from. If <code>""</code>, then the model is
* detached from all joints.
*/
Q_INVOKABLE void detachAll(const QString& modelURL, const QString& jointName = QString());
Q_INVOKABLE virtual void detachAll(const QString& modelURL, const QString& jointName = QString());
QString getSkeletonModelURLFromScript() const { return _skeletonModelURL.toString(); }
void setSkeletonModelURLFromScript(const QString& skeletonModelString) { setSkeletonModelURL(QUrl(skeletonModelString)); }
@ -1317,6 +1330,7 @@ protected:
bool _firstSkeletonCheck { true };
QUrl _skeletonFBXURL;
QVector<AttachmentData> _attachmentData;
QVector<AttachmentData> _oldAttachmentData;
QString _displayName;
QString _sessionDisplayName { };
bool _lookAtSnappingEnabled { true };
@ -1369,6 +1383,7 @@ protected:
RateCounter<> _faceTrackerRate;
RateCounter<> _jointDataRate;
RateCounter<> _jointDefaultPoseFlagsRate;
RateCounter<> _farGrabJointRate;
// Some rate data for incoming data updates
RateCounter<> _parseBufferUpdateRate;
@ -1385,6 +1400,7 @@ protected:
RateCounter<> _faceTrackerUpdateRate;
RateCounter<> _jointDataUpdateRate;
RateCounter<> _jointDefaultPoseFlagsUpdateRate;
RateCounter<> _farGrabJointUpdateRate;
// Some rate data for outgoing data
AvatarDataRate _outboundDataRate;
@ -1403,6 +1419,10 @@ protected:
ThreadSafeValueCache<glm::mat4> _controllerLeftHandMatrixCache { glm::mat4() };
ThreadSafeValueCache<glm::mat4> _controllerRightHandMatrixCache { glm::mat4() };
ThreadSafeValueCache<glm::mat4> _farGrabRightMatrixCache { glm::mat4() };
ThreadSafeValueCache<glm::mat4> _farGrabLeftMatrixCache { glm::mat4() };
ThreadSafeValueCache<glm::mat4> _farGrabMouseMatrixCache { glm::mat4() };
int getFauxJointIndex(const QString& name) const;
float _audioLoudness { 0.0f };
@ -1560,5 +1580,11 @@ const int CONTROLLER_LEFTHAND_INDEX = 65532; // -4
const int CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX = 65531; // -5
const int CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX = 65530; // -6
const int CAMERA_MATRIX_INDEX = 65529; // -7
const int FARGRAB_RIGHTHAND_INDEX = 65528; // -8
const int FARGRAB_LEFTHAND_INDEX = 65527; // -9
const int FARGRAB_MOUSE_INDEX = 65526; // -10
const int LOWEST_PSEUDO_JOINT_INDEX = 65526;
#endif // hifi_AvatarData_h

View file

@ -138,7 +138,7 @@ void TextureBaker::processTexture() {
// IMPORTANT: _originalTexture is empty past this point
_originalTexture.clear();
_outputFiles.push_back(originalCopyFilePath);
meta.original = _metaTexturePathPrefix +_textureURL.fileName();
meta.original = _metaTexturePathPrefix + _textureURL.fileName();
}
auto buffer = std::static_pointer_cast<QIODevice>(std::make_shared<QFile>(originalCopyFilePath));
@ -149,49 +149,56 @@ void TextureBaker::processTexture() {
// Compressed KTX
if (_compressionEnabled) {
auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(),
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true, _abortProcessing);
if (!processedTexture) {
handleError("Could not process texture " + _textureURL.toString());
return;
}
processedTexture->setSourceHash(hash);
constexpr std::array<gpu::BackendTarget, 2> BACKEND_TARGETS {{
gpu::BackendTarget::GL45,
gpu::BackendTarget::GLES32
}};
for (auto target : BACKEND_TARGETS) {
auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(),
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true,
target, _abortProcessing);
if (!processedTexture) {
handleError("Could not process texture " + _textureURL.toString());
return;
}
processedTexture->setSourceHash(hash);
if (shouldStop()) {
return;
}
if (shouldStop()) {
return;
}
auto memKTX = gpu::Texture::serialize(*processedTexture);
if (!memKTX) {
handleError("Could not serialize " + _textureURL.toString() + " to KTX");
return;
}
auto memKTX = gpu::Texture::serialize(*processedTexture);
if (!memKTX) {
handleError("Could not serialize " + _textureURL.toString() + " to KTX");
return;
}
const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat());
if (name == nullptr) {
handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString());
return;
}
const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat());
if (name == nullptr) {
handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString());
return;
}
const char* data = reinterpret_cast<const char*>(memKTX->_storage->data());
const size_t length = memKTX->_storage->size();
const char* data = reinterpret_cast<const char*>(memKTX->_storage->data());
const size_t length = memKTX->_storage->size();
auto fileName = _baseFilename + "_" + name + ".ktx";
auto filePath = _outputDirectory.absoluteFilePath(fileName);
QFile bakedTextureFile { filePath };
if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) {
handleError("Could not write baked texture for " + _textureURL.toString());
return;
auto fileName = _baseFilename + "_" + name + ".ktx";
auto filePath = _outputDirectory.absoluteFilePath(fileName);
QFile bakedTextureFile { filePath };
if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) {
handleError("Could not write baked texture for " + _textureURL.toString());
return;
}
_outputFiles.push_back(filePath);
meta.availableTextureTypes[memKTX->_header.getGLInternaFormat()] = _metaTexturePathPrefix + fileName;
}
_outputFiles.push_back(filePath);
meta.availableTextureTypes[memKTX->_header.getGLInternaFormat()] = _metaTexturePathPrefix + fileName;
}
// Uncompressed KTX
if (_textureType == image::TextureUsage::Type::CUBE_TEXTURE) {
buffer->reset();
auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(),
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, _abortProcessing);
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, gpu::BackendTarget::GL45, _abortProcessing);
if (!processedTexture) {
handleError("Could not process texture " + _textureURL.toString());
return;

View file

@ -40,7 +40,7 @@
#include <PointerManager.h>
std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction;
std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction = []() { return true; };
QString resolveScriptURL(const QString& scriptUrl) {
auto normalizedScriptUrl = DependencyManager::get<ResourceManager>()->normalizeURL(scriptUrl);

View file

@ -14,7 +14,6 @@
#include <ObjectMotionState.h>
#include "EntityTreeRenderer.h"
#include "RenderableLightEntityItem.h"
#include "RenderableLineEntityItem.h"
#include "RenderableModelEntityItem.h"
@ -44,8 +43,6 @@ enum class RenderItemStatusIcon {
NONE = 255
};
std::function<bool()> EntityRenderer::_entitiesShouldFadeFunction = []() { return true; };
void EntityRenderer::initEntityRenderers() {
REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(PolyVox, RenderablePolyVoxEntityItem::factory)
@ -385,13 +382,13 @@ void EntityRenderer::updateModelTransformAndBound() {
void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) {
DETAILED_PROFILE_RANGE(simulation_physics, __FUNCTION__);
withWriteLock([&] {
if (isFading()) {
auto transparent = isTransparent();
auto fading = isFading();
if (fading || _prevIsTransparent != transparent) {
emit requestRenderUpdate();
}
auto transparent = isTransparent();
if (_prevIsTransparent && !transparent) {
_isFading = false;
if (fading) {
_isFading = Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f;
}
_prevIsTransparent = transparent;

View file

@ -19,6 +19,7 @@
#include "EntitiesRendererLogging.h"
#include <graphics-scripting/Forward.h>
#include <RenderHifi.h>
#include "EntityTreeRenderer.h"
class EntityTreeRenderer;
@ -96,7 +97,7 @@ protected:
// Called by the `render` method after `needsRenderUpdate`
virtual void doRender(RenderArgs* args) = 0;
bool isFading() const { return _isFading; }
virtual bool isFading() const { return _isFading; }
void updateModelTransformAndBound();
virtual bool isTransparent() const { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; }
inline bool isValidRenderItem() const { return _renderItemID != Item::INVALID_ITEM_ID; }
@ -121,7 +122,6 @@ protected:
static void makeStatusGetters(const EntityItemPointer& entity, Item::Status::Getters& statusGetters);
static std::function<bool()> _entitiesShouldFadeFunction;
const Transform& getModelTransform() const;
Item::Bound _bound;
@ -131,7 +131,7 @@ protected:
ItemIDs _subRenderItemIDs;
uint64_t _fadeStartTime{ usecTimestampNow() };
uint64_t _updateTime{ usecTimestampNow() }; // used when sorting/throttling render updates
bool _isFading{ _entitiesShouldFadeFunction() };
bool _isFading { EntityTreeRenderer::getEntitiesShouldFadeFunction()() };
bool _prevIsTransparent { false };
bool _visible { false };
bool _isVisibleInSecondaryCamera { false };

View file

@ -146,6 +146,9 @@ public:
void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override;
void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) override;
// FIXME: model mesh parts should fade individually
bool isFading() const override { return false; }
protected:
virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction) override;
virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override;

View file

@ -29,18 +29,12 @@ using namespace render::entities;
static uint8_t CUSTOM_PIPELINE_NUMBER { 0 };
static const int32_t PAINTSTROKE_TEXTURE_SLOT { 0 };
// FIXME: This is interfering with the uniform buffers in DeferredLightingEffect.cpp, so use 12 to avoid collisions
static const int32_t PAINTSTROKE_UNIFORM_SLOT { 12 };
static gpu::Stream::FormatPointer polylineFormat;
static gpu::PipelinePointer polylinePipeline;
#ifdef POLYLINE_ENTITY_USE_FADE_EFFECT
static gpu::PipelinePointer polylineFadePipeline;
#endif
struct PolyLineUniforms {
glm::vec3 color;
};
static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch) {
if (!polylinePipeline) {
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::entities_renderer::program::paintStroke);
@ -84,8 +78,6 @@ PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity)
polylineFormat->setAttribute(gpu::Stream::COLOR, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB), offsetof(Vertex, color));
});
PolyLineUniforms uniforms;
_uniformBuffer = std::make_shared<gpu::Buffer>(sizeof(PolyLineUniforms), (const gpu::Byte*) &uniforms);
_verticesBuffer = std::make_shared<gpu::Buffer>();
}
@ -126,9 +118,6 @@ void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer&
}
void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
PolyLineUniforms uniforms;
uniforms.color = toGlm(entity->getXColor());
memcpy(&_uniformBuffer.edit<PolyLineUniforms>(), &uniforms, sizeof(PolyLineUniforms));
auto pointsChanged = entity->pointsChanged();
auto strokeWidthsChanged = entity->strokeWidthsChanged();
auto normalsChanged = entity->normalsChanged();
@ -274,7 +263,6 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) {
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(_polylineTransform);
batch.setUniformBuffer(PAINTSTROKE_UNIFORM_SLOT, _uniformBuffer);
if (_texture && _texture->isLoaded()) {
batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, _texture->getGPUTexture());

View file

@ -65,7 +65,6 @@ protected:
QVector<glm::vec3> _lastStrokeColors;
QVector<float> _lastStrokeWidths;
gpu::BufferPointer _verticesBuffer;
gpu::BufferView _uniformBuffer;
uint32_t _numVertices { 0 };
bool _empty{ true };

View file

@ -14,7 +14,6 @@
<@include DeferredBufferWrite.slh@>
// the albedo texture
layout(binding=0) uniform sampler2D originalTexture;
@ -23,17 +22,7 @@ layout(location=0) in vec3 interpolatedNormal;
layout(location=1) in vec2 varTexcoord;
layout(location=2) in vec4 varColor;
struct PolyLineUniforms {
vec3 color;
};
layout(binding=0) uniform polyLineBuffer {
PolyLineUniforms polyline;
};
void main(void) {
vec4 texel = texture(originalTexture, varTexcoord);
int frontCondition = 1 -int(gl_FrontFacing) * 2;
vec3 color = varColor.rgb;

View file

@ -26,13 +26,11 @@ layout(location=1) out vec2 varTexcoord;
layout(location=2) out vec4 varColor;
void main(void) {
varTexcoord = inTexCoord0.st;
// pass along the diffuse color
varColor = color_sRGBAToLinear(inColor);
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();

View file

@ -38,6 +38,8 @@
#include <EntityScriptClient.h>
#include <Profile.h>
const QString GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":true}}";
const QString NOT_GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":false}}";
EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership) :
_entityTree(NULL),
@ -303,7 +305,7 @@ bool EntityScriptingInterface::addLocalEntityCopy(EntityItemProperties& properti
}
QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& textures,
const QString& shapeType, bool dynamic, bool collisionless,
const QString& shapeType, bool dynamic, bool collisionless, bool grabbable,
const glm::vec3& position, const glm::vec3& gravity) {
_activityTracking.addedEntityCount++;
@ -314,6 +316,7 @@ QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QStrin
properties.setShapeTypeFromString(shapeType);
properties.setDynamic(dynamic);
properties.setCollisionless(collisionless);
properties.setUserData(grabbable ? GRABBABLE_USER_DATA : NOT_GRABBABLE_USER_DATA);
properties.setPosition(position);
properties.setGravity(gravity);
if (!textures.isEmpty()) {
@ -1249,7 +1252,7 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID,
EntityItemPointer entity;
bool doTransmit = false;
_entityTree->withWriteLock([&] {
_entityTree->withWriteLock([this, &entity, entityID, myNodeID, &doTransmit, actor, &properties] {
EntitySimulationPointer simulation = _entityTree->getSimulation();
entity = _entityTree->findEntityByEntityItemID(entityID);
if (!entity) {

View file

@ -39,6 +39,9 @@
class EntityTree;
class MeshProxy;
extern const QString GRABBABLE_USER_DATA;
extern const QString NOT_GRABBABLE_USER_DATA;
// helper factory to compose standardized, async metadata queries for "magic" Entity properties
// like .script and .serverScripts. This is used for automated testing of core scripting features
// as well as to provide early adopters a self-discoverable, consistent way to diagnose common
@ -237,7 +240,7 @@ public slots:
/// temporary method until addEntity can be used from QJSEngine
/// Deliberately not adding jsdoc, only used internally.
Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& textures, const QString& shapeType, bool dynamic,
bool collisionless, const glm::vec3& position, const glm::vec3& gravity);
bool collisionless, bool grabbable, const glm::vec3& position, const glm::vec3& gravity);
/**jsdoc
* Create a clone of an entity. A clone can be created by a client that doesn't have rez permissions in the current domain.

View file

@ -1435,7 +1435,7 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt
QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
connect(networkReply, &QNetworkReply::finished, [=]() {
connect(networkReply, &QNetworkReply::finished, [this, networkReply, entityItemID, certID, senderNode]() {
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
jsonObject = jsonObject["data"].toObject();

View file

@ -371,7 +371,7 @@ void ModelEntityItem::setAnimationFPS(float value) {
// virtual
bool ModelEntityItem::shouldBePhysical() const {
return !isDead() && getShapeType() != SHAPE_TYPE_NONE;
return !isDead() && getShapeType() != SHAPE_TYPE_NONE && QUrl(_modelURL).isValid();
}
void ModelEntityItem::resizeJointArrays(int newSize) {

View file

@ -65,20 +65,24 @@ GLTexture* GLESBackend::syncGPUObject(const TexturePointer& texturePointer) {
object = new GLESAttachmentTexture(shared_from_this(), texture);
break;
case TextureUsageType::RESOURCE:
// FIXME disabling variable allocation textures for now, while debugging android rendering
// and crashes
#if 0
qCDebug(gpugllogging) << "variable / Strict texture " << texture.source().c_str();
object = new GLESResourceTexture(shared_from_this(), texture);
GLVariableAllocationSupport::addMemoryManagedTexture(texturePointer);
break;
#endif
case TextureUsageType::STRICT_RESOURCE:
qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str();
object = new GLESStrictResourceTexture(shared_from_this(), texture);
break;
case TextureUsageType::RESOURCE: {
auto &transferEngine = _textureManagement._transferEngine;
if (transferEngine->allowCreate()) {
object = new GLESResourceTexture(shared_from_this(), texture);
transferEngine->addMemoryManagedTexture(texturePointer);
} else {
auto fallback = texturePointer->getFallbackTexture();
if (fallback) {
object = static_cast<GLESTexture *>(syncGPUObject(fallback));
}
}
break;
}
default:
Q_UNREACHABLE();
}
@ -195,7 +199,6 @@ Size GLESTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const
glTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
}
} else {
// TODO: implement for android
assert(false);
amountCopied = 0;
}
@ -385,7 +388,6 @@ void GLESVariableAllocationTexture::allocateStorage(uint16 allocatedMip) {
const auto totalMips = _gpuObject.getNumMips();
const auto mips = totalMips - _allocatedMip;
withPreservedTexture([&] {
// FIXME technically GL 4.2, but OSX includes the ARB_texture_storage extension
glTexStorage2D(_target, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); CHECK_GL_ERROR();
});
auto mipLevels = _gpuObject.getNumMips();
@ -426,139 +428,26 @@ void GLESVariableAllocationTexture::syncSampler() const {
});
}
void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
// DestID must be bound to the GLESBackend::RESOURCE_TRANSFER_TEX_UNIT
GLuint fbo { 0 };
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
uint16_t mips = numMips;
// copy pre-existing mips
for (uint16_t mip = populatedMips; mip < mips; ++mip) {
void copyTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
auto mipDimensions = texture.evalMipDimensions(mip);
uint16_t targetMip = mip - destMipOffset;
uint16_t sourceMip = mip - srcMipOffset;
for (GLenum target : GLTexture::getFaceTargets(texTarget)) {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, srcId, sourceMip);
(void)CHECK_GL_ERROR();
glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y);
auto faces = GLTexture::getFaceCount(texTarget);
for (uint8_t face = 0; face < faces; ++face) {
glCopyImageSubData(
srcId, texTarget, sourceMip, 0, 0, face,
destId, texTarget, targetMip, 0, 0, face,
mipDimensions.x, mipDimensions.y, 1
);
(void)CHECK_GL_ERROR();
}
}
// destroy the transfer framebuffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo);
}
void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
// DestID must be bound to the GLESBackend::RESOURCE_TRANSFER_TEX_UNIT
struct MipDesc {
GLint _faceSize;
GLint _size;
GLint _offset;
GLint _width;
GLint _height;
};
std::vector<MipDesc> sourceMips(numMips);
std::vector<GLubyte> bytes;
glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT);
glBindTexture(texTarget, srcId);
const auto& faceTargets = GLTexture::getFaceTargets(texTarget);
GLint internalFormat { 0 };
// Collect the mip description from the source texture
GLint bufferOffset { 0 };
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
auto& sourceMip = sourceMips[mip];
uint16_t sourceLevel = mip - srcMipOffset;
// Grab internal format once
if (internalFormat == 0) {
glGetTexLevelParameteriv(faceTargets[0], sourceLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat);
}
// Collect the size of the first face, and then compute the total size offset needed for this mip level
auto mipDimensions = texture.evalMipDimensions(mip);
sourceMip._width = mipDimensions.x;
sourceMip._height = mipDimensions.y;
#ifdef DEBUG_COPY
glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_WIDTH, &sourceMip._width);
glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_HEIGHT, &sourceMip._height);
#endif
// TODO: retrieve the size of a compressed image
assert(false);
//glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize);
sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize;
sourceMip._offset = bufferOffset;
bufferOffset += sourceMip._size;
}
(void)CHECK_GL_ERROR();
// Allocate the PBO to accomodate for all the mips to copy
GLuint pbo { 0 };
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STATIC_COPY);
(void)CHECK_GL_ERROR();
// Transfer from source texture to pbo
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
auto& sourceMip = sourceMips[mip];
uint16_t sourceLevel = mip - srcMipOffset;
for (GLint f = 0; f < (GLint)faceTargets.size(); f++) {
// TODO: implement for android
//glGetCompressedTexImage(faceTargets[f], sourceLevel, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize));
}
(void)CHECK_GL_ERROR();
}
// Now populate the new texture from the pbo
glBindTexture(texTarget, 0);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_TEX_UNIT);
// Transfer from pbo to new texture
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
auto& sourceMip = sourceMips[mip];
uint16_t destLevel = mip - destMipOffset;
for (GLint f = 0; f < (GLint)faceTargets.size(); f++) {
#ifdef DEBUG_COPY
GLint destWidth, destHeight, destSize;
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_WIDTH, &destWidth);
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_HEIGHT, &destHeight);
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &destSize);
#endif
glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat,
sourceMip._faceSize, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize));
}
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glDeleteBuffers(1, &pbo);
}
void GLESVariableAllocationTexture::copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
uint16_t numMips = _gpuObject.getNumMips();
withPreservedTexture([&] {
if (_texelFormat.isCompressed()) {
copyCompressedTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips);
} else {
copyUncompressedTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips);
}
});
copyTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips);
}
size_t GLESVariableAllocationTexture::promote() {

Some files were not shown because too many files have changed in this diff Show more