Merge branch 'fix-sync-period-calc' into correct-target-frame-rate

This commit is contained in:
howard-stearns 2015-12-08 17:32:53 -08:00
commit 9ae22800fe
197 changed files with 4953 additions and 1863 deletions

View file

@ -221,3 +221,36 @@ if (HIFI_MEMORY_DEBUGGING)
MESSAGE("-- Memory debugging is enabled")
endif (UNIX)
endif ()
include_application_version()
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE)
message(STATUS "+++++ Package for deployment will be generated on this build +++++")
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/full-stack-deployment)
set(ICONPATH_INTERFACE "$INSTDIR/${PATH_INSTALL_DATA}/interface.ico")
set(ICONPATH_STACK_MANAGER "$INSTDIR/${PATH_INSTALL_DATA}/stack-manager.ico")
string(REPLACE "/" "\\\\" ICONPATH_INTERFACE ${ICONPATH_INTERFACE})
string(REPLACE "/" "\\\\" ICONPATH_STACK_MANAGER ${ICONPATH_STACK_MANAGER})
set(CPACK_PACKAGE_NAME "High Fidelity")
set(CPACK_PACKAGE_VENDOR "High Fidelity, Inc")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "High Fidelity Interface and Stack")
set(CPACK_PACKAGE_VERSION "${BUILD_SEQ}")
set(CPACK_PACKAGE_VERSION_MAJOR "${BUILD_SEQ}")
set(CPACK_PACKAGE_VERSION_MINOR "0")
set(CPACK_PACKAGE_VERSION_PATCH "0")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "High Fidelity-${BUILD_SEQ}")
set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
set(CPACK_PACKAGE_EXECUTABLES
stack-manager "Stack Manager"
interface "Interface"
)
if (WIN32)
install(DIRECTORY ${CMAKE_BINARY_DIR}/full-stack-deployment/ DESTINATION "./")
endif (WIN32)
include(CPack)
endif ()

View file

@ -11,3 +11,4 @@ link_hifi_libraries(
include_application_version()
package_libraries_for_deployment()
consolidate_stack_components()

View file

@ -16,6 +16,7 @@
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <AssetClient.h>
#include <AvatarHashMap.h>
#include <AudioInjectorManager.h>
#include <AssetClient.h>
@ -43,8 +44,8 @@
static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 10;
Agent::Agent(NLPacket& packet) :
ThreadedAssignment(packet),
Agent::Agent(ReceivedMessage& message) :
ThreadedAssignment(message),
_entityEditSender(),
_receivedAudioStream(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES,
InboundAudioStream::Settings(0, false, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES, false,
@ -79,46 +80,46 @@ Agent::Agent(NLPacket& packet) :
packetReceiver.registerListener(PacketType::Jurisdiction, this, "handleJurisdictionPacket");
}
void Agent::handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
auto packetType = packet->getType();
void Agent::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
auto packetType = message->getType();
if (packetType == PacketType::OctreeStats) {
int statsMessageLength = OctreeHeadlessViewer::parseOctreeStats(packet, senderNode);
if (packet->getPayloadSize() > statsMessageLength) {
int statsMessageLength = OctreeHeadlessViewer::parseOctreeStats(message, senderNode);
if (message->getSize() > statsMessageLength) {
// pull out the piggybacked packet and create a new QSharedPointer<NLPacket> for it
int piggyBackedSizeWithHeader = packet->getPayloadSize() - statsMessageLength;
int piggyBackedSizeWithHeader = message->getSize() - statsMessageLength;
auto buffer = std::unique_ptr<char[]>(new char[piggyBackedSizeWithHeader]);
memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggyBackedSizeWithHeader);
memcpy(buffer.get(), message->getRawMessage() + statsMessageLength, piggyBackedSizeWithHeader);
auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggyBackedSizeWithHeader, packet->getSenderSockAddr());
packet = QSharedPointer<NLPacket>(newPacket.release());
auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggyBackedSizeWithHeader, message->getSenderSockAddr());
message = QSharedPointer<ReceivedMessage>::create(*newPacket);
} else {
return; // bail since no piggyback data
}
packetType = packet->getType();
packetType = message->getType();
} // fall through to piggyback message
if (packetType == PacketType::EntityData || packetType == PacketType::EntityErase) {
_entityViewer.processDatagram(*packet, senderNode);
_entityViewer.processDatagram(*message, senderNode);
}
}
void Agent::handleJurisdictionPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
void Agent::handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
NodeType_t nodeType;
packet->peekPrimitive(&nodeType);
message->peekPrimitive(&nodeType);
// PacketType_JURISDICTION, first byte is the node type...
if (nodeType == NodeType::EntityServer) {
DependencyManager::get<EntityScriptingInterface>()->getJurisdictionListener()->
queueReceivedPacket(packet, senderNode);
queueReceivedPacket(message, senderNode);
}
}
void Agent::handleAudioPacket(QSharedPointer<NLPacket> packet) {
_receivedAudioStream.parseData(*packet);
void Agent::handleAudioPacket(QSharedPointer<ReceivedMessage> message) {
_receivedAudioStream.parseData(*message);
_lastReceivedAudioLoudness = _receivedAudioStream.getNextOutputFrameLoudness();
@ -131,6 +132,7 @@ void Agent::run() {
// make sure we request our script once the agent connects to the domain
auto nodeList = DependencyManager::get<NodeList>();
connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript);
ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
@ -223,6 +225,7 @@ void Agent::executeScript() {
// call model URL setters with empty URLs so our avatar, if user, will have the default models
scriptedAvatar->setFaceModelURL(QUrl());
scriptedAvatar->setSkeletonModelURL(QUrl());
// give this AvatarData object to the script engine
_scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data());

View file

@ -39,7 +39,7 @@ class Agent : public ThreadedAssignment {
Q_PROPERTY(QUuid sessionUUID READ getSessionUUID)
public:
Agent(NLPacket& packet);
Agent(ReceivedMessage& message);
void setIsAvatar(bool isAvatar);
bool isAvatar() const { return _isAvatar; }
@ -63,9 +63,10 @@ private slots:
void scriptRequestFinished();
void executeScript();
void handleAudioPacket(QSharedPointer<NLPacket> packet);
void handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleJurisdictionPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleAudioPacket(QSharedPointer<ReceivedMessage> message);
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void processAgentAvatarAndAudio(float deltaTime);
private:

View file

@ -225,11 +225,11 @@ void AssignmentClient::sendAssignmentRequest() {
}
}
void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<NLPacket> packet) {
void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessage> message) {
qDebug() << "Received a PacketType::CreateAssignment - attempting to unpack.";
// construct the deployed assignment from the packet data
_currentAssignment = AssignmentFactory::unpackAssignment(*packet);
_currentAssignment = AssignmentFactory::unpackAssignment(*message);
if (_currentAssignment && !_isAssigned) {
qDebug() << "Received an assignment -" << *_currentAssignment;
@ -239,7 +239,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<NLPacket> pac
// switch our DomainHandler hostname and port to whoever sent us the assignment
nodeList->getDomainHandler().setSockAddr(packet->getSenderSockAddr(), _assignmentServerHostname);
nodeList->getDomainHandler().setSockAddr(message->getSenderSockAddr(), _assignmentServerHostname);
nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID());
qDebug() << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString();
@ -274,8 +274,8 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<NLPacket> pac
}
}
void AssignmentClient::handleStopNodePacket(QSharedPointer<NLPacket> packet) {
const HifiSockAddr& senderSockAddr = packet->getSenderSockAddr();
void AssignmentClient::handleStopNodePacket(QSharedPointer<ReceivedMessage> message) {
const HifiSockAddr& senderSockAddr = message->getSenderSockAddr();
if (senderSockAddr.getAddress() == QHostAddress::LocalHost ||
senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) {

View file

@ -38,8 +38,8 @@ public slots:
void aboutToQuit();
private slots:
void handleCreateAssignmentPacket(QSharedPointer<NLPacket> packet);
void handleStopNodePacket(QSharedPointer<NLPacket> packet);
void handleCreateAssignmentPacket(QSharedPointer<ReceivedMessage> message);
void handleStopNodePacket(QSharedPointer<ReceivedMessage> message);
private:
void setUpStatusToMonitor();

View file

@ -207,14 +207,14 @@ void AssignmentClientMonitor::checkSpares() {
}
}
void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<NLPacket> packet) {
void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<ReceivedMessage> message) {
// read out the sender ID
QUuid senderID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
QUuid senderID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer matchingNode = nodeList->nodeWithUUID(senderID);
const HifiSockAddr& senderSockAddr = packet->getSenderSockAddr();
const HifiSockAddr& senderSockAddr = message->getSenderSockAddr();
AssignmentClientChildData* childData = nullptr;
@ -251,7 +251,7 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<NLPacket> p
// get child's assignment type out of the packet
quint8 assignmentType;
packet->readPrimitive(&assignmentType);
message->readPrimitive(&assignmentType);
childData->setChildType((Assignment::Type) assignmentType);

View file

@ -36,7 +36,7 @@ public:
private slots:
void checkSpares();
void childProcessFinished();
void handleChildStatusPacket(QSharedPointer<NLPacket> packet);
void handleChildStatusPacket(QSharedPointer<ReceivedMessage> message);
public slots:
void aboutToQuit();

View file

@ -19,26 +19,26 @@
#include "assets/AssetServer.h"
#include "messages/MessagesMixer.h"
ThreadedAssignment* AssignmentFactory::unpackAssignment(NLPacket& packet) {
ThreadedAssignment* AssignmentFactory::unpackAssignment(ReceivedMessage& message) {
quint8 packedType;
packet.peekPrimitive(&packedType);
message.peekPrimitive(&packedType);
Assignment::Type unpackedType = (Assignment::Type) packedType;
switch (unpackedType) {
case Assignment::AudioMixerType:
return new AudioMixer(packet);
return new AudioMixer(message);
case Assignment::AvatarMixerType:
return new AvatarMixer(packet);
return new AvatarMixer(message);
case Assignment::AgentType:
return new Agent(packet);
return new Agent(message);
case Assignment::EntityServerType:
return new EntityServer(packet);
return new EntityServer(message);
case Assignment::AssetServerType:
return new AssetServer(packet);
return new AssetServer(message);
case Assignment::MessagesMixerType:
return new MessagesMixer(packet);
return new MessagesMixer(message);
default:
return NULL;
}

View file

@ -16,7 +16,7 @@
class AssignmentFactory {
public:
static ThreadedAssignment* unpackAssignment(NLPacket& packet);
static ThreadedAssignment* unpackAssignment(ReceivedMessage& message);
};
#endif // hifi_AssignmentFactory_h

View file

@ -27,8 +27,8 @@
const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server";
AssetServer::AssetServer(NLPacket& packet) :
ThreadedAssignment(packet),
AssetServer::AssetServer(ReceivedMessage& message) :
ThreadedAssignment(message),
_taskPool(this)
{
@ -40,7 +40,7 @@ AssetServer::AssetServer(NLPacket& packet) :
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::AssetGet, this, "handleAssetGet");
packetReceiver.registerListener(PacketType::AssetGetInfo, this, "handleAssetGetInfo");
packetReceiver.registerMessageListener(PacketType::AssetUpload, this, "handleAssetUpload");
packetReceiver.registerListener(PacketType::AssetUpload, this, "handleAssetUpload");
}
void AssetServer::run() {
@ -84,20 +84,20 @@ void AssetServer::run() {
}
}
void AssetServer::handleAssetGetInfo(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
void AssetServer::handleAssetGetInfo(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
QByteArray assetHash;
MessageID messageID;
uint8_t extensionLength;
if (packet->getPayloadSize() < qint64(SHA256_HASH_LENGTH + sizeof(messageID) + sizeof(extensionLength))) {
if (message->getSize() < qint64(SHA256_HASH_LENGTH + sizeof(messageID) + sizeof(extensionLength))) {
qDebug() << "ERROR bad file request";
return;
}
packet->readPrimitive(&messageID);
assetHash = packet->readWithoutCopy(SHA256_HASH_LENGTH);
packet->readPrimitive(&extensionLength);
QByteArray extension = packet->read(extensionLength);
message->readPrimitive(&messageID);
assetHash = message->readWithoutCopy(SHA256_HASH_LENGTH);
message->readPrimitive(&extensionLength);
QByteArray extension = message->read(extensionLength);
auto replyPacket = NLPacket::create(PacketType::AssetGetInfoReply);
@ -122,26 +122,26 @@ void AssetServer::handleAssetGetInfo(QSharedPointer<NLPacket> packet, SharedNode
nodeList->sendPacket(std::move(replyPacket), *senderNode);
}
void AssetServer::handleAssetGet(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
void AssetServer::handleAssetGet(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
auto minSize = qint64(sizeof(MessageID) + SHA256_HASH_LENGTH + sizeof(uint8_t) + sizeof(DataOffset) + sizeof(DataOffset));
if (packet->getPayloadSize() < minSize) {
if (message->getSize() < minSize) {
qDebug() << "ERROR bad file request";
return;
}
// Queue task
auto task = new SendAssetTask(packet, senderNode, _resourcesDirectory);
auto task = new SendAssetTask(message, senderNode, _resourcesDirectory);
_taskPool.start(task);
}
void AssetServer::handleAssetUpload(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
void AssetServer::handleAssetUpload(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
if (senderNode->getCanRez()) {
qDebug() << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(senderNode->getUUID());
auto task = new UploadAssetTask(packetList, senderNode, _resourcesDirectory);
auto task = new UploadAssetTask(message, senderNode, _resourcesDirectory);
_taskPool.start(task);
} else {
// this is a node the domain told us is not allowed to rez entities
@ -151,7 +151,7 @@ void AssetServer::handleAssetUpload(QSharedPointer<NLPacketList> packetList, Sha
auto permissionErrorPacket = NLPacket::create(PacketType::AssetUploadReply, sizeof(MessageID) + sizeof(AssetServerError));
MessageID messageID;
packetList->readPrimitive(&messageID);
message->readPrimitive(&messageID);
// write the message ID and a permission denied error
permissionErrorPacket->writePrimitive(messageID);

View file

@ -18,19 +18,20 @@
#include <QThreadPool>
#include "AssetUtils.h"
#include "ReceivedMessage.h"
class AssetServer : public ThreadedAssignment {
Q_OBJECT
public:
AssetServer(NLPacket& packet);
AssetServer(ReceivedMessage& message);
public slots:
void run();
private slots:
void handleAssetGetInfo(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleAssetGet(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleAssetUpload(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
void handleAssetGetInfo(QSharedPointer<ReceivedMessage> packet, SharedNodePointer senderNode);
void handleAssetGet(QSharedPointer<ReceivedMessage> packet, SharedNodePointer senderNode);
void handleAssetUpload(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer senderNode);
void sendStatsPacket();

View file

@ -22,9 +22,9 @@
#include "AssetUtils.h"
SendAssetTask::SendAssetTask(QSharedPointer<NLPacket> packet, const SharedNodePointer& sendToNode, const QDir& resourcesDir) :
SendAssetTask::SendAssetTask(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& sendToNode, const QDir& resourcesDir) :
QRunnable(),
_packet(packet),
_message(message),
_senderNode(sendToNode),
_resourcesDir(resourcesDir)
{
@ -36,16 +36,16 @@ void SendAssetTask::run() {
uint8_t extensionLength;
DataOffset start, end;
_packet->readPrimitive(&messageID);
QByteArray assetHash = _packet->read(SHA256_HASH_LENGTH);
_packet->readPrimitive(&extensionLength);
QByteArray extension = _packet->read(extensionLength);
_message->readPrimitive(&messageID);
QByteArray assetHash = _message->read(SHA256_HASH_LENGTH);
_message->readPrimitive(&extensionLength);
QByteArray extension = _message->read(extensionLength);
// `start` and `end` indicate the range of data to retrieve for the asset identified by `assetHash`.
// `start` is inclusive, `end` is exclusive. Requesting `start` = 1, `end` = 10 will retrieve 9 bytes of data,
// starting at index 1.
_packet->readPrimitive(&start);
_packet->readPrimitive(&end);
_message->readPrimitive(&start);
_message->readPrimitive(&end);
QString hexHash = assetHash.toHex();

View file

@ -25,12 +25,12 @@ class NLPacket;
class SendAssetTask : public QRunnable {
public:
SendAssetTask(QSharedPointer<NLPacket> packet, const SharedNodePointer& sendToNode, const QDir& resourcesDir);
SendAssetTask(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& sendToNode, const QDir& resourcesDir);
void run();
private:
QSharedPointer<NLPacket> _packet;
QSharedPointer<ReceivedMessage> _message;
SharedNodePointer _senderNode;
QDir _resourcesDir;
};

View file

@ -19,9 +19,9 @@
#include <NLPacketList.h>
UploadAssetTask::UploadAssetTask(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode,
UploadAssetTask::UploadAssetTask(QSharedPointer<ReceivedMessage> receivedMessage, SharedNodePointer senderNode,
const QDir& resourcesDir) :
_packetList(packetList),
_receivedMessage(receivedMessage),
_senderNode(senderNode),
_resourcesDir(resourcesDir)
{
@ -29,7 +29,7 @@ UploadAssetTask::UploadAssetTask(QSharedPointer<NLPacketList> packetList, Shared
}
void UploadAssetTask::run() {
auto data = _packetList->getMessage();
auto data = _receivedMessage->getMessage();
QBuffer buffer { &data };
buffer.open(QIODevice::ReadOnly);

View file

@ -19,17 +19,19 @@
#include <QtCore/QRunnable>
#include <QtCore/QSharedPointer>
#include "ReceivedMessage.h"
class NLPacketList;
class Node;
class UploadAssetTask : public QRunnable {
public:
UploadAssetTask(QSharedPointer<NLPacketList> packetList, QSharedPointer<Node> senderNode, const QDir& resourcesDir);
UploadAssetTask(QSharedPointer<ReceivedMessage> message, QSharedPointer<Node> senderNode, const QDir& resourcesDir);
void run();
private:
QSharedPointer<NLPacketList> _packetList;
QSharedPointer<ReceivedMessage> _receivedMessage;
QSharedPointer<Node> _senderNode;
QDir _resourcesDir;
};

View file

@ -75,8 +75,8 @@ bool AudioMixer::shouldMute(float quietestFrame) {
return (quietestFrame > _noiseMutingThreshold);
}
AudioMixer::AudioMixer(NLPacket& packet) :
ThreadedAssignment(packet),
AudioMixer::AudioMixer(ReceivedMessage& message) :
ThreadedAssignment(message),
_trailingSleepRatio(1.0f),
_minAudibilityThreshold(LOUDNESS_TO_DISTANCE_RATIO / 2.0f),
_performanceThrottlingRatio(0.0f),
@ -438,7 +438,6 @@ int AudioMixer::prepareMixForListeningNode(Node* node) {
AudioMixerClientData* listenerNodeData = static_cast<AudioMixerClientData*>(node->getLinkedData());
// zero out the client mix for this node
memset(_preMixSamples, 0, sizeof(_preMixSamples));
memset(_mixSamples, 0, sizeof(_mixSamples));
// loop through all other nodes that have sufficient audio to mix
@ -459,6 +458,9 @@ int AudioMixer::prepareMixForListeningNode(Node* node) {
if (otherNodeStream->getType() == PositionalAudioStream::Microphone) {
streamUUID = otherNode->getUUID();
}
// clear out the pre-mix samples before filling it up with this source
memset(_preMixSamples, 0, sizeof(_preMixSamples));
if (*otherNode != *node || otherNodeStream->shouldLoopbackForNode()) {
streamsMixed += addStreamToMixForListeningNodeWithStream(listenerNodeData, streamUUID,
@ -542,17 +544,17 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
}
}
void AudioMixer::handleNodeAudioPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
DependencyManager::get<NodeList>()->updateNodeWithDataFromPacket(packet, sendingNode);
void AudioMixer::handleNodeAudioPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
DependencyManager::get<NodeList>()->updateNodeWithDataFromPacket(message, sendingNode);
}
void AudioMixer::handleMuteEnvironmentPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
void AudioMixer::handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
auto nodeList = DependencyManager::get<NodeList>();
if (sendingNode->getCanAdjustLocks()) {
auto newPacket = NLPacket::create(PacketType::MuteEnvironment, packet->getPayloadSize());
auto newPacket = NLPacket::create(PacketType::MuteEnvironment, message->getSize());
// Copy payload
newPacket->write(packet->getPayload(), packet->getPayloadSize());
newPacket->write(message->getRawMessage(), message->getSize());
nodeList->eachNode([&](const SharedNodePointer& node){
if (node->getType() == NodeType::Agent && node->getActiveSocket() &&

View file

@ -28,7 +28,7 @@ const int READ_DATAGRAMS_STATS_WINDOW_SECONDS = 30;
class AudioMixer : public ThreadedAssignment {
Q_OBJECT
public:
AudioMixer(NLPacket& packet);
AudioMixer(ReceivedMessage& message);
void deleteLater() { qDebug() << "DELETE LATER CALLED?"; QObject::deleteLater(); }
public slots:
@ -41,8 +41,8 @@ public slots:
private slots:
void broadcastMixes();
void handleNodeAudioPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void handleMuteEnvironmentPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void handleNodeAudioPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
void handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
private:
void domainSettingsRequestComplete();

View file

@ -49,18 +49,18 @@ AvatarAudioStream* AudioMixerClientData::getAvatarAudioStream() const {
return NULL;
}
int AudioMixerClientData::parseData(NLPacket& packet) {
PacketType packetType = packet.getType();
int AudioMixerClientData::parseData(ReceivedMessage& message) {
PacketType packetType = message.getType();
if (packetType == PacketType::AudioStreamStats) {
// skip over header, appendFlag, and num stats packed
packet.seek(sizeof(quint8) + sizeof(quint16));
message.seek(sizeof(quint8) + sizeof(quint16));
// read the downstream audio stream stats
packet.readPrimitive(&_downstreamAudioStreamStats);
message.readPrimitive(&_downstreamAudioStreamStats);
return packet.pos();
return message.getPosition();
} else {
PositionalAudioStream* matchingStream = NULL;
@ -74,10 +74,10 @@ int AudioMixerClientData::parseData(NLPacket& packet) {
// we don't have a mic stream yet, so add it
// read the channel flag to see if our stream is stereo or not
packet.seek(sizeof(quint16));
message.seek(sizeof(quint16));
quint8 channelFlag;
packet.readPrimitive(&channelFlag);
message.readPrimitive(&channelFlag);
bool isStereo = channelFlag == 1;
@ -89,11 +89,11 @@ int AudioMixerClientData::parseData(NLPacket& packet) {
// this is injected audio
// grab the stream identifier for this injected audio
packet.seek(sizeof(quint16));
QUuid streamIdentifier = QUuid::fromRfc4122(packet.readWithoutCopy(NUM_BYTES_RFC4122_UUID));
message.seek(sizeof(quint16));
QUuid streamIdentifier = QUuid::fromRfc4122(message.readWithoutCopy(NUM_BYTES_RFC4122_UUID));
bool isStereo;
packet.readPrimitive(&isStereo);
message.readPrimitive(&isStereo);
if (!_audioStreams.contains(streamIdentifier)) {
// we don't have this injected stream yet, so add it
@ -105,9 +105,9 @@ int AudioMixerClientData::parseData(NLPacket& packet) {
}
// seek to the beginning of the packet so that the next reader is in the right spot
packet.seek(0);
message.seek(0);
return matchingStream->parseData(packet);
return matchingStream->parseData(message);
}
return 0;
}

View file

@ -42,7 +42,7 @@ public:
const QHash<QUuid, PositionalAudioStream*>& getAudioStreams() const { return _audioStreams; }
AvatarAudioStream* getAvatarAudioStream() const;
int parseData(NLPacket& packet);
int parseData(ReceivedMessage& message);
void checkBuffersBeforeFrameSend();

View file

@ -34,8 +34,8 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 60;
const unsigned int AVATAR_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) * 1000;
AvatarMixer::AvatarMixer(NLPacket& packet) :
ThreadedAssignment(packet),
AvatarMixer::AvatarMixer(ReceivedMessage& message) :
ThreadedAssignment(message),
_broadcastThread(),
_lastFrameTimestamp(QDateTime::currentMSecsSinceEpoch()),
_trailingSleepRatio(1.0f),
@ -424,19 +424,19 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
}
}
void AvatarMixer::handleAvatarDataPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
void AvatarMixer::handleAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
auto nodeList = DependencyManager::get<NodeList>();
nodeList->updateNodeWithDataFromPacket(packet, senderNode);
nodeList->updateNodeWithDataFromPacket(message, senderNode);
}
void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
if (senderNode->getLinkedData()) {
AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
if (nodeData != nullptr) {
AvatarData& avatar = nodeData->getAvatar();
// parse the identity packet and update the change timestamp if appropriate
if (avatar.hasIdentityChangedAfterParsing(*packet)) {
if (avatar.hasIdentityChangedAfterParsing(message->getMessage())) {
QMutexLocker nodeDataLocker(&nodeData->getMutex());
nodeData->setIdentityChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
}
@ -444,13 +444,13 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<NLPacket> packet, Sh
}
}
void AvatarMixer::handleAvatarBillboardPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
void AvatarMixer::handleAvatarBillboardPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
if (nodeData) {
AvatarData& avatar = nodeData->getAvatar();
// parse the billboard packet and update the change timestamp if appropriate
if (avatar.hasBillboardChangedAfterParsing(*packet)) {
if (avatar.hasBillboardChangedAfterParsing(message->getMessage())) {
QMutexLocker nodeDataLocker(&nodeData->getMutex());
nodeData->setBillboardChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
}
@ -458,8 +458,8 @@ void AvatarMixer::handleAvatarBillboardPacket(QSharedPointer<NLPacket> packet, S
}
}
void AvatarMixer::handleKillAvatarPacket(QSharedPointer<NLPacket> packet) {
DependencyManager::get<NodeList>()->processKillNode(*packet);
void AvatarMixer::handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message) {
DependencyManager::get<NodeList>()->processKillNode(*message);
}
void AvatarMixer::sendStatsPacket() {

View file

@ -21,7 +21,7 @@
class AvatarMixer : public ThreadedAssignment {
Q_OBJECT
public:
AvatarMixer(NLPacket& packet);
AvatarMixer(ReceivedMessage& message);
~AvatarMixer();
public slots:
/// runs the avatar mixer
@ -32,10 +32,10 @@ public slots:
void sendStatsPacket();
private slots:
void handleAvatarDataPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleAvatarIdentityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleAvatarBillboardPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleKillAvatarPacket(QSharedPointer<NLPacket> packet);
void handleAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleAvatarBillboardPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message);
void domainSettingsRequestComplete();
private:

View file

@ -13,12 +13,12 @@
#include "AvatarMixerClientData.h"
int AvatarMixerClientData::parseData(NLPacket& packet) {
int AvatarMixerClientData::parseData(ReceivedMessage& message) {
// pull the sequence number from the data first
packet.readPrimitive(&_lastReceivedSequenceNumber);
message.readPrimitive(&_lastReceivedSequenceNumber);
// compute the offset to the data payload
return _avatar.parseDataFromBuffer(packet.readWithoutCopy(packet.bytesLeftToRead()));
return _avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead()));
}
bool AvatarMixerClientData::checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid) {
@ -40,7 +40,7 @@ uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& node
}
void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
jsonObject["display_name"] = _avatar.getDisplayName();
jsonObject["display_name"] = _avatar->getDisplayName();
jsonObject["full_rate_distance"] = _fullRateDistance;
jsonObject["max_av_distance"] = _maxAvatarDistance;
jsonObject["num_avs_sent_last_frame"] = _numAvatarsSentLastFrame;
@ -49,7 +49,7 @@ void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends;
jsonObject[OUTBOUND_AVATAR_DATA_STATS_KEY] = getOutboundAvatarDataKbps();
jsonObject[INBOUND_AVATAR_DATA_STATS_KEY] = _avatar.getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
jsonObject[INBOUND_AVATAR_DATA_STATS_KEY] = _avatar->getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
jsonObject["av_data_receive_rate"] = _avatar.getReceiveRate();
jsonObject["av_data_receive_rate"] = _avatar->getReceiveRate();
}

View file

@ -33,8 +33,8 @@ const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps";
class AvatarMixerClientData : public NodeData {
Q_OBJECT
public:
int parseData(NLPacket& packet);
AvatarData& getAvatar() { return _avatar; }
int parseData(ReceivedMessage& message) override;
AvatarData& getAvatar() { return *_avatar; }
bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid);
@ -80,7 +80,7 @@ public:
void loadJSONStats(QJsonObject& jsonObject) const;
private:
AvatarData _avatar;
AvatarSharedPointer _avatar { new AvatarData() };
uint16_t _lastReceivedSequenceNumber { 0 };
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;

View file

@ -22,8 +22,8 @@ const char* MODEL_SERVER_NAME = "Entity";
const char* MODEL_SERVER_LOGGING_TARGET_NAME = "entity-server";
const char* LOCAL_MODELS_PERSIST_FILE = "resources/models.svo";
EntityServer::EntityServer(NLPacket& packet) :
OctreeServer(packet),
EntityServer::EntityServer(ReceivedMessage& message) :
OctreeServer(message),
_entitySimulation(NULL)
{
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
@ -41,9 +41,9 @@ EntityServer::~EntityServer() {
tree->removeNewlyCreatedHook(this);
}
void EntityServer::handleEntityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
void EntityServer::handleEntityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
if (_octreeInboundPacketProcessor) {
_octreeInboundPacketProcessor->queueReceivedPacket(packet, senderNode);
_octreeInboundPacketProcessor->queueReceivedPacket(message, senderNode);
}
}

View file

@ -30,7 +30,7 @@ struct ViewerSendingStats {
class EntityServer : public OctreeServer, public NewlyCreatedEntityHook {
Q_OBJECT
public:
EntityServer(NLPacket& packet);
EntityServer(ReceivedMessage& message);
~EntityServer();
// Subclasses must implement these methods
@ -62,7 +62,7 @@ protected:
virtual OctreePointer createTree() override;
private slots:
void handleEntityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleEntityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
private:
EntitySimulation* _entitySimulation;

View file

@ -20,13 +20,13 @@
const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer";
MessagesMixer::MessagesMixer(NLPacket& packet) : ThreadedAssignment(packet)
MessagesMixer::MessagesMixer(ReceivedMessage& message) : ThreadedAssignment(message)
{
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled);
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerMessageListener(PacketType::MessagesData, this, "handleMessages");
packetReceiver.registerMessageListener(PacketType::MessagesSubscribe, this, "handleMessagesSubscribe");
packetReceiver.registerMessageListener(PacketType::MessagesUnsubscribe, this, "handleMessagesUnsubscribe");
packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessages");
packetReceiver.registerListener(PacketType::MessagesSubscribe, this, "handleMessagesSubscribe");
packetReceiver.registerListener(PacketType::MessagesUnsubscribe, this, "handleMessagesUnsubscribe");
}
void MessagesMixer::nodeKilled(SharedNodePointer killedNode) {
@ -35,10 +35,10 @@ void MessagesMixer::nodeKilled(SharedNodePointer killedNode) {
}
}
void MessagesMixer::handleMessages(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
void MessagesMixer::handleMessages(QSharedPointer<ReceivedMessage> receivedMessage, SharedNodePointer senderNode) {
QString channel, message;
QUuid senderID;
MessagesClient::decodeMessagesPacket(packetList, channel, message, senderID);
MessagesClient::decodeMessagesPacket(receivedMessage, channel, message, senderID);
auto nodeList = DependencyManager::get<NodeList>();
@ -53,13 +53,13 @@ void MessagesMixer::handleMessages(QSharedPointer<NLPacketList> packetList, Shar
});
}
void MessagesMixer::handleMessagesSubscribe(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
QString channel = QString::fromUtf8(packetList->getMessage());
void MessagesMixer::handleMessagesSubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
QString channel = QString::fromUtf8(message->getMessage());
_channelSubscribers[channel] << senderNode->getUUID();
}
void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
QString channel = QString::fromUtf8(packetList->getMessage());
void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
QString channel = QString::fromUtf8(message->getMessage());
if (_channelSubscribers.contains(channel)) {
_channelSubscribers[channel].remove(senderNode->getUUID());
}

View file

@ -21,7 +21,7 @@
class MessagesMixer : public ThreadedAssignment {
Q_OBJECT
public:
MessagesMixer(NLPacket& packet);
MessagesMixer(ReceivedMessage& message);
public slots:
void run();
@ -29,9 +29,9 @@ public slots:
void sendStatsPacket();
private slots:
void handleMessages(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
void handleMessagesSubscribe(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
void handleMessagesUnsubscribe(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
void handleMessages(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleMessagesSubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleMessagesUnsubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
private:
QHash<QString,QSet<QUuid>> _channelSubscribers;

View file

@ -75,7 +75,7 @@ void OctreeInboundPacketProcessor::midProcess() {
}
}
void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
void OctreeInboundPacketProcessor::processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
if (_shuttingDown) {
qDebug() << "OctreeInboundPacketProcessor::processPacket() while shutting down... ignoring incoming packet";
return;
@ -85,22 +85,22 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
if (debugProcessPacket) {
qDebug("OctreeInboundPacketProcessor::processPacket() payload=%p payloadLength=%lld",
packet->getPayload(),
packet->getPayloadSize());
message->getRawMessage(),
message->getSize());
}
// Ask our tree subclass if it can handle the incoming packet...
PacketType packetType = packet->getType();
PacketType packetType = message->getType();
if (_myServer->getOctree()->handlesEditPacketType(packetType)) {
PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE", debugProcessPacket);
_receivedPacketCount++;
unsigned short int sequence;
packet->readPrimitive(&sequence);
message->readPrimitive(&sequence);
quint64 sentAt;
packet->readPrimitive(&sentAt);
message->readPrimitive(&sentAt);
quint64 arrivedAt = usecTimestampNow();
if (sentAt > arrivedAt) {
@ -118,7 +118,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
if (debugProcessPacket || _myServer->wantsDebugReceiving()) {
qDebug() << "PROCESSING THREAD: got '" << packetType << "' packet - " << _receivedPacketCount << " command from client";
qDebug() << " receivedBytes=" << packet->getDataSize();
qDebug() << " receivedBytes=" << message->getSize();
qDebug() << " sequence=" << sequence;
qDebug() << " sentAt=" << sentAt << " usecs";
qDebug() << " arrivedAt=" << arrivedAt << " usecs";
@ -132,29 +132,29 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
qDebug() << " numBytesPacketHeader=" << NLPacket::totalHeaderSize(packetType);
qDebug() << " sizeof(sequence)=" << sizeof(sequence);
qDebug() << " sizeof(sentAt)=" << sizeof(sentAt);
qDebug() << " atByte (in payload)=" << packet->pos();
qDebug() << " payload size=" << packet->getPayloadSize();
qDebug() << " atByte (in payload)=" << message->getPosition();
qDebug() << " payload size=" << message->getSize();
if (!packet->bytesLeftToRead()) {
if (!message->getBytesLeftToRead()) {
qDebug() << " ----- UNEXPECTED ---- got a packet without any edit details!!!! --------";
}
}
const unsigned char* editData = nullptr;
while (packet->bytesLeftToRead() > 0) {
while (message->getBytesLeftToRead() > 0) {
editData = reinterpret_cast<const unsigned char*>(packet->getPayload() + packet->pos());
editData = reinterpret_cast<const unsigned char*>(message->getRawMessage() + message->getPosition());
int maxSize = packet->bytesLeftToRead();
int maxSize = message->getBytesLeftToRead();
if (debugProcessPacket) {
qDebug() << " --- inside while loop ---";
qDebug() << " maxSize=" << maxSize;
qDebug("OctreeInboundPacketProcessor::processPacket() %hhu "
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld maxSize=%d",
packetType, packet->getPayload(), packet->getPayloadSize(), editData,
packet->pos(), maxSize);
packetType, message->getRawMessage(), message->getSize(), editData,
message->getPosition(), maxSize);
}
quint64 startProcess, startLock = usecTimestampNow();
@ -162,7 +162,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
_myServer->getOctree()->withWriteLock([&] {
startProcess = usecTimestampNow();
editDataBytesRead =
_myServer->getOctree()->processEditPacketData(*packet, editData, maxSize, sendingNode);
_myServer->getOctree()->processEditPacketData(*message, editData, maxSize, sendingNode);
});
quint64 endProcess = usecTimestampNow();
@ -178,12 +178,12 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
lockWaitTime += thisLockWaitTime;
// skip to next edit record in the packet
packet->seek(packet->pos() + editDataBytesRead);
message->seek(message->getPosition() + editDataBytesRead);
if (debugProcessPacket) {
qDebug() << " editDataBytesRead=" << editDataBytesRead;
qDebug() << " AFTER processEditPacketData payload position=" << packet->pos();
qDebug() << " AFTER processEditPacketData payload size=" << packet->getPayloadSize();
qDebug() << " AFTER processEditPacketData payload position=" << message->getPosition();
qDebug() << " AFTER processEditPacketData payload size=" << message->getSize();
}
}
@ -191,7 +191,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
if (debugProcessPacket) {
qDebug("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %hhu "
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld",
packetType, packet->getPayload(), packet->getPayloadSize(), editData, packet->pos());
packetType, message->getRawMessage(), message->getSize(), editData, message->getPosition());
}
// Make sure our Node and NodeList knows we've heard from this node.

View file

@ -78,7 +78,7 @@ public:
protected:
virtual void processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
virtual void processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
virtual unsigned long getMaxWait() const;
virtual void preProcess();

View file

@ -33,8 +33,6 @@ OctreeQueryNode::OctreeQueryNode() :
_lastTimeBagEmpty(0),
_viewFrustumChanging(false),
_viewFrustumJustStoppedChanging(true),
_currentPacketIsColor(true),
_currentPacketIsCompressed(false),
_octreeSendThread(NULL),
_lastClientBoundaryLevelAdjust(0),
_lastClientOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
@ -179,14 +177,9 @@ void OctreeQueryNode::resetOctreePacket() {
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
// the clients requested color state.
_currentPacketIsCompressed = getWantCompression();
OCTREE_PACKET_FLAGS flags = 0;
if (_currentPacketIsColor) {
setAtBit(flags, PACKET_IS_COLOR_BIT);
}
if (_currentPacketIsCompressed) {
setAtBit(flags, PACKET_IS_COMPRESSED_BIT);
}
setAtBit(flags, PACKET_IS_COLOR_BIT); // always color
setAtBit(flags, PACKET_IS_COMPRESSED_BIT); // always compressed
_octreePacket->reset();
@ -211,10 +204,9 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int by
// compressed packets include lead bytes which contain compressed size, this allows packing of
// multiple compressed portions together
if (_currentPacketIsCompressed) {
OCTREE_PACKET_INTERNAL_SECTION_SIZE sectionSize = bytes;
_octreePacket->writePrimitive(sectionSize);
}
OCTREE_PACKET_INTERNAL_SECTION_SIZE sectionSize = bytes;
_octreePacket->writePrimitive(sectionSize);
if (bytes <= _octreePacket->bytesAvailableForWrite()) {
_octreePacket->write(reinterpret_cast<const char*>(buffer), bytes);
_octreePacketWaiting = true;
@ -370,11 +362,11 @@ const NLPacket* OctreeQueryNode::getNextNackedPacket() {
return nullptr;
}
void OctreeQueryNode::parseNackPacket(NLPacket& packet) {
void OctreeQueryNode::parseNackPacket(ReceivedMessage& message) {
// read sequence numbers
while (packet.bytesLeftToRead()) {
while (message.getBytesLeftToRead()) {
OCTREE_PACKET_SEQUENCE sequenceNumber;
packet.readPrimitive(&sequenceNumber);
message.readPrimitive(&sequenceNumber);
_nackedSequenceNumbers.enqueue(sequenceNumber);
}
}

View file

@ -75,12 +75,6 @@ public:
quint64 getLastTimeBagEmpty() const { return _lastTimeBagEmpty; }
void setLastTimeBagEmpty() { _lastTimeBagEmpty = _sceneSendStartTime; }
bool getCurrentPacketIsColor() const { return _currentPacketIsColor; }
bool getCurrentPacketIsCompressed() const { return _currentPacketIsCompressed; }
bool getCurrentPacketFormatMatches() {
return (getCurrentPacketIsCompressed() == getWantCompression());
}
bool hasLodChanged() const { return _lodChanged; }
OctreeSceneStats stats;
@ -106,7 +100,7 @@ public:
OCTREE_PACKET_SEQUENCE getSequenceNumber() const { return _sequenceNumber; }
void parseNackPacket(NLPacket& packet);
void parseNackPacket(ReceivedMessage& message);
bool hasNextNackedPacket() const;
const NLPacket* getNextNackedPacket();
@ -133,8 +127,6 @@ private:
quint64 _lastTimeBagEmpty;
bool _viewFrustumChanging;
bool _viewFrustumJustStoppedChanging;
bool _currentPacketIsColor;
bool _currentPacketIsCompressed;
OctreeSendThread* _octreeSendThread;

View file

@ -309,36 +309,29 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
int truePacketsSent = 0;
int trueBytesSent = 0;
int packetsSentThisInterval = 0;
bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging())
bool isFullScene = ((!viewFrustumChanged) && nodeData->getViewFrustumJustStoppedChanging())
|| nodeData->hasLodChanged();
bool somethingToSend = true; // assume we have something
// FOR NOW... node tells us if it wants to receive only view frustum deltas
bool wantDelta = viewFrustumChanged && nodeData->getWantDelta();
// If our packet already has content in it, then we must use the color choice of the waiting packet.
// If we're starting a fresh packet, then...
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
// the clients requested color state.
bool wantCompression = nodeData->getWantCompression();
// If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color
// then let's just send that waiting packet.
if (!nodeData->getCurrentPacketFormatMatches()) {
if (nodeData->isPacketWaiting()) {
packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent);
} else {
nodeData->resetOctreePacket();
}
int targetSize = MAX_OCTREE_PACKET_DATA_SIZE;
if (wantCompression) {
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
}
_packetData.changeSettings(wantCompression, targetSize);
if (nodeData->isPacketWaiting()) {
packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent);
} else {
nodeData->resetOctreePacket();
}
int targetSize = MAX_OCTREE_PACKET_DATA_SIZE;
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL;
_packetData.changeSettings(true, targetSize); // FIXME - eventually support only compressed packets
const ViewFrustum* lastViewFrustum = viewFrustumChanged ? &nodeData->getLastKnownViewFrustum() : NULL;
// If the current view frustum has changed OR we have nothing to send, then search against
// the current view frustum for things to send.
@ -351,11 +344,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
}
}
if (!viewFrustumChanged && !nodeData->getWantDelta()) {
// only set our last sent time if we weren't resetting due to frustum change
nodeData->setLastTimeBagEmpty();
}
// track completed scenes and send out the stats packet accordingly
nodeData->stats.sceneCompleted();
nodeData->setLastRootTimestamp(_myServer->getOctree()->getRoot()->getLastChanged());
@ -452,11 +440,11 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
float octreeSizeScale = nodeData->getOctreeSizeScale();
int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust();
int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving()
? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
int boundaryLevelAdjust = boundaryLevelAdjustClient +
(viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(),
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
WANT_EXISTS_BITS, DONT_CHOP, viewFrustumChanged, lastViewFrustum,
boundaryLevelAdjust, octreeSizeScale,
nodeData->getLastTimeBagEmpty(),
isFullScene, &nodeData->stats, _myServer->getJurisdiction(),
@ -522,8 +510,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
// if for some reason the finalized size is greater than our available size, then probably the "compressed"
// form actually inflated beyond our padding, and in this case we will send the current packet, then
// write to out new packet...
unsigned int writtenSize = _packetData.getFinalizedSize()
+ (nodeData->getCurrentPacketIsCompressed() ? sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) : 0);
unsigned int writtenSize = _packetData.getFinalizedSize() + sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
if (writtenSize > nodeData->getAvailable()) {
packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent);
@ -539,8 +526,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
// the packet doesn't have enough space to bother attempting to pack more...
bool sendNow = true;
if (nodeData->getCurrentPacketIsCompressed() &&
nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING &&
if (nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING &&
extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) {
sendNow = false; // try to pack more
}
@ -552,9 +538,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
quint64 packetSendingEnd = usecTimestampNow();
packetSendingElapsedUsec = (float)(packetSendingEnd - packetSendingStart);
if (wantCompression) {
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
}
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
} else {
// If we're in compressed mode, then we want to see if we have room for more in this wire packet.
// but we've finalized the _packetData, so we want to start a new section, we will do that by
@ -564,7 +548,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
// a larger compressed size then uncompressed size
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING;
}
_packetData.changeSettings(nodeData->getWantCompression(), targetSize); // will do reset
_packetData.changeSettings(true, targetSize); // will do reset - NOTE: Always compressed
}
OctreeServer::trackTreeWaitTime(lockWaitElapsedUsec);

View file

@ -210,8 +210,8 @@ void OctreeServer::trackProcessWaitTime(float time) {
_averageProcessWaitTime.updateAverage(time);
}
OctreeServer::OctreeServer(NLPacket& packet) :
ThreadedAssignment(packet),
OctreeServer::OctreeServer(ReceivedMessage& message) :
ThreadedAssignment(message),
_argc(0),
_argv(NULL),
_parsedArgV(NULL),
@ -878,12 +878,12 @@ void OctreeServer::parsePayload() {
}
}
void OctreeServer::handleOctreeQueryPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
void OctreeServer::handleOctreeQueryPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
if (!_isFinished) {
// If we got a query packet, then we're talking to an agent, and we
// need to make sure we have it in our nodeList.
auto nodeList = DependencyManager::get<NodeList>();
nodeList->updateNodeWithDataFromPacket(packet, senderNode);
nodeList->updateNodeWithDataFromPacket(message, senderNode);
OctreeQueryNode* nodeData = dynamic_cast<OctreeQueryNode*>(senderNode->getLinkedData());
if (nodeData && !nodeData->isOctreeSendThreadInitalized()) {
@ -892,17 +892,17 @@ void OctreeServer::handleOctreeQueryPacket(QSharedPointer<NLPacket> packet, Shar
}
}
void OctreeServer::handleOctreeDataNackPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
void OctreeServer::handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
// If we got a nack packet, then we're talking to an agent, and we
// need to make sure we have it in our nodeList.
OctreeQueryNode* nodeData = dynamic_cast<OctreeQueryNode*>(senderNode->getLinkedData());
if (nodeData) {
nodeData->parseNackPacket(*packet);
nodeData->parseNackPacket(*message);
}
}
void OctreeServer::handleJurisdictionRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
_jurisdictionSender->queueReceivedPacket(packet, senderNode);
void OctreeServer::handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
_jurisdictionSender->queueReceivedPacket(message, senderNode);
}
bool OctreeServer::readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result) {

View file

@ -34,7 +34,7 @@ const int DEFAULT_PACKETS_PER_INTERVAL = 2000; // some 120,000 packets per secon
class OctreeServer : public ThreadedAssignment, public HTTPRequestHandler {
Q_OBJECT
public:
OctreeServer(NLPacket& packet);
OctreeServer(ReceivedMessage& message);
~OctreeServer();
/// allows setting of run arguments
@ -135,9 +135,9 @@ public slots:
private slots:
void domainSettingsRequestComplete();
void handleOctreeQueryPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleOctreeDataNackPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleJurisdictionRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleOctreeQueryPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
protected:
virtual OctreePointer createTree() = 0;

View file

@ -0,0 +1,27 @@
macro(CONSOLIDATE_STACK_COMPONENTS)
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE AND WIN32)
# Copy all the output for this target into the common deployment location
add_custom_command(
TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_directory $<TARGET_FILE_DIR:${TARGET_NAME}> ${CMAKE_BINARY_DIR}/full-stack-deployment
)
# Copy icon files for interface and stack manager
if (TARGET_NAME STREQUAL "interface" OR TARGET_NAME STREQUAL "stack-manager")
if (TARGET_NAME STREQUAL "interface")
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/icon/interface.ico")
set (ICON_DESTINATION_NAME "interface.ico")
elseif (TARGET_NAME STREQUAL "stack-manager")
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/assets/icon.ico")
set (ICON_DESTINATION_NAME "stack-manager.ico")
endif ()
add_custom_command(
TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy ${ICON_FILE_PATH} ${CMAKE_BINARY_DIR}/full-stack-deployment/${ICON_DESTINATION_NAME}
)
endif ()
endif ()
endmacro()

View file

@ -10,10 +10,15 @@
#
macro(INCLUDE_APPLICATION_VERSION)
#
# We are relying on Jenkins defined environment variables to determine the origin of this build
# and will only package if this is a PR or Release build
if (DEFINED ENV{JOB_ID})
set (DEPLOY_PACKAGE 1)
set (BUILD_SEQ $ENV{JOB_ID})
elseif (DEFINED ENV{ghprbPullId})
set (BUILD_SEQ "PR: $ENV{ghprbPullId} - Commit: $ENV{ghprbActualCommit}")
set (DEPLOY_PACKAGE 1)
set (BUILD_SEQ "PR-$ENV{ghprbPullId}")
else ()
set(BUILD_SEQ "dev")
endif ()

View file

@ -1,5 +1,5 @@
#
# CopyDllsBesideWindowsExecutable.cmake
# PackageLibrariesForDeployment.cmake
# cmake/macros
#
# Copyright 2015 High Fidelity, Inc.

View file

@ -107,7 +107,7 @@ if (WIN32 AND NOT CYGWIN)
select_library_configurations(SSL_EAY)
set(OPENSSL_LIBRARIES ${SSL_EAY_LIBRARY} ${LIB_EAY_LIBRARY})
find_path(OPENSSL_DLL_PATH NAMES ssleay32.dll PATH_SUFFIXES "bin" ${_OPENSSL_ROOT_HINTS_AND_PATHS})
elseif (MINGW)
@ -252,6 +252,15 @@ endif ()
if (WIN32)
add_paths_to_fixup_libs(${OPENSSL_DLL_PATH})
#
# For some reason fixup misses the following DLL and only copies libeay32. There's gotta be a better way to handle this
# but for now resorting to the following interm solution
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE)
add_custom_command(
TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy ${OPENSSL_DLL_PATH}/ssleay32.dll ${CMAKE_BINARY_DIR}/full-stack-deployment/
)
endif ()
endif ()
mark_as_advanced(OPENSSL_INCLUDE_DIR OPENSSL_LIBRARIES OPENSSL_SEARCH_DIRS)

View file

@ -53,4 +53,4 @@ else()
endif()
file(GLOB RUNTIME_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}")
fixup_bundle("${BUNDLE_EXECUTABLE}" "${RUNTIME_PLUGINS}" "@FIXUP_LIBS@")
fixup_bundle("${BUNDLE_EXECUTABLE}" "${RUNTIME_PLUGINS}" "@FIXUP_LIBS@")

View file

@ -38,3 +38,4 @@ endif (UNIX)
include_application_version()
package_libraries_for_deployment()
consolidate_stack_components()

View file

@ -51,15 +51,15 @@ const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
<< NodeType::AssetServer
<< NodeType::MessagesMixer;
void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> packet) {
if (packet->getPayloadSize() == 0) {
void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessage> message) {
if (message->getSize() == 0) {
return;
}
QDataStream packetStream(packet.data());
QDataStream packetStream(message->getMessage());
// read a NodeConnectionData object from the packet so we can pass around this data while we're inspecting it
NodeConnectionData nodeConnection = NodeConnectionData::fromDataStream(packetStream, packet->getSenderSockAddr());
NodeConnectionData nodeConnection = NodeConnectionData::fromDataStream(packetStream, message->getSenderSockAddr());
if (nodeConnection.localSockAddr.isNull() || nodeConnection.publicSockAddr.isNull()) {
qDebug() << "Unexpected data received for node local socket or public socket. Will not allow connection.";
@ -72,7 +72,7 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> pack
if (!VALID_NODE_TYPES.contains(nodeConnection.nodeType)) {
qDebug() << "Received an invalid node type with connect request. Will not allow connection from"
<< nodeConnection.senderSockAddr;
<< nodeConnection.senderSockAddr << ": " << nodeConnection.nodeType;
return;
}
@ -87,11 +87,11 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> pack
QString username;
QByteArray usernameSignature;
if (packet->bytesLeftToRead() > 0) {
if (message->getBytesLeftToRead() > 0) {
// read username from packet
packetStream >> username;
if (packet->bytesLeftToRead() > 0) {
if (message->getBytesLeftToRead() > 0) {
// read user signature from packet
packetStream >> usernameSignature;
}
@ -103,14 +103,14 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> pack
if (node) {
// set the sending sock addr and node interest set on this node
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
nodeData->setSendingSockAddr(packet->getSenderSockAddr());
nodeData->setSendingSockAddr(message->getSenderSockAddr());
nodeData->setNodeInterestSet(nodeConnection.interestList.toSet());
// signal that we just connected a node so the DomainServer can get it a list
// and broadcast its presence right away
emit connectedNode(node);
} else {
qDebug() << "Refusing connection from node at" << packet->getSenderSockAddr();
qDebug() << "Refusing connection from node at" << message->getSenderSockAddr();
}
}
@ -572,10 +572,10 @@ void DomainGatekeeper::handlePeerPingTimeout() {
}
}
void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointer<NLPacket> packet) {
void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointer<ReceivedMessage> message) {
// loop through the packet and pull out network peers
// any peer we don't have we add to the hash, otherwise we update
QDataStream iceResponseStream(packet.data());
QDataStream iceResponseStream(message->getMessage());
NetworkPeer* receivedPeer = new NetworkPeer;
iceResponseStream >> *receivedPeer;
@ -600,15 +600,15 @@ void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointer<NLPacket>
}
}
void DomainGatekeeper::processICEPingPacket(QSharedPointer<NLPacket> packet) {
void DomainGatekeeper::processICEPingPacket(QSharedPointer<ReceivedMessage> message) {
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*packet, limitedNodeList->getSessionUUID());
auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*message, limitedNodeList->getSessionUUID());
limitedNodeList->sendPacket(std::move(pingReplyPacket), packet->getSenderSockAddr());
limitedNodeList->sendPacket(std::move(pingReplyPacket), message->getSenderSockAddr());
}
void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer<NLPacket> packet) {
QDataStream packetStream(packet.data());
void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer<ReceivedMessage> message) {
QDataStream packetStream(message->getMessage());
QUuid nodeUUID;
packetStream >> nodeUUID;
@ -617,6 +617,6 @@ void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer<NLPacket> packet
if (sendingPeer) {
// we had this NetworkPeer in our connecting list - add the right sock addr to our connected list
sendingPeer->activateMatchingOrNewSymmetricSocket(packet->getSenderSockAddr());
sendingPeer->activateMatchingOrNewSymmetricSocket(message->getSenderSockAddr());
}
}

View file

@ -41,10 +41,10 @@ public:
void removeICEPeer(const QUuid& peerUUID) { _icePeers.remove(peerUUID); }
public slots:
void processConnectRequestPacket(QSharedPointer<NLPacket> packet);
void processICEPingPacket(QSharedPointer<NLPacket> packet);
void processICEPingReplyPacket(QSharedPointer<NLPacket> packet);
void processICEPeerInformationPacket(QSharedPointer<NLPacket> packet);
void processConnectRequestPacket(QSharedPointer<ReceivedMessage> message);
void processICEPingPacket(QSharedPointer<ReceivedMessage> message);
void processICEPingReplyPacket(QSharedPointer<ReceivedMessage> message);
void processICEPeerInformationPacket(QSharedPointer<ReceivedMessage> message);
void publicKeyJSONCallback(QNetworkReply& requestReply);

View file

@ -273,7 +273,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
packetReceiver.registerListener(PacketType::RequestAssignment, this, "processRequestAssignmentPacket");
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket");
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
packetReceiver.registerMessageListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
packetReceiver.registerListener(PacketType::DomainDisconnectRequest, this, "processNodeDisconnectRequestPacket");
// NodeList won't be available to the settings manager when it is created, so call registerListener here
@ -578,10 +578,10 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
}
}
void DomainServer::processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
QDataStream packetStream(packet.data());
NodeConnectionData nodeRequestData = NodeConnectionData::fromDataStream(packetStream, packet->getSenderSockAddr(), false);
QDataStream packetStream(message->getMessage());
NodeConnectionData nodeRequestData = NodeConnectionData::fromDataStream(packetStream, message->getSenderSockAddr(), false);
// update this node's sockets in case they have changed
sendingNode->setPublicSocket(nodeRequestData.publicSockAddr);
@ -591,7 +591,7 @@ void DomainServer::processListRequestPacket(QSharedPointer<NLPacket> packet, Sha
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(sendingNode->getLinkedData());
nodeData->setNodeInterestSet(nodeRequestData.interestList.toSet());
sendDomainListToNode(sendingNode, packet->getSenderSockAddr());
sendDomainListToNode(sendingNode, message->getSenderSockAddr());
}
unsigned int DomainServer::countConnectedUsers() {
@ -764,9 +764,9 @@ void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
);
}
void DomainServer::processRequestAssignmentPacket(QSharedPointer<NLPacket> packet) {
void DomainServer::processRequestAssignmentPacket(QSharedPointer<ReceivedMessage> message) {
// construct the requested assignment from the packet data
Assignment requestAssignment(*packet);
Assignment requestAssignment(*message);
// Suppress these for Assignment::AgentType to once per 5 seconds
static QElapsedTimer noisyMessageTimer;
@ -784,14 +784,14 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointer<NLPacket> packe
static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex
("Received a request for assignment type [^ ]+ from [^ ]+");
qDebug() << "Received a request for assignment type" << requestAssignment.getType()
<< "from" << packet->getSenderSockAddr();
<< "from" << message->getSenderSockAddr();
noisyMessageTimer.restart();
}
SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
if (assignmentToDeploy) {
qDebug() << "Deploying assignment -" << *assignmentToDeploy.data() << "- to" << packet->getSenderSockAddr();
qDebug() << "Deploying assignment -" << *assignmentToDeploy.data() << "- to" << message->getSenderSockAddr();
// give this assignment out, either the type matches or the requestor said they will take any
static std::unique_ptr<NLPacket> assignmentPacket;
@ -812,7 +812,7 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointer<NLPacket> packe
assignmentStream << uniqueAssignment;
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
limitedNodeList->sendUnreliablePacket(*assignmentPacket, packet->getSenderSockAddr());
limitedNodeList->sendUnreliablePacket(*assignmentPacket, message->getSenderSockAddr());
// give the information for that deployed assignment to the gatekeeper so it knows to that that node
// in when it comes back around
@ -824,7 +824,7 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointer<NLPacket> packe
static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex
("Unable to fulfill assignment request of type [^ ]+ from [^ ]+");
qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType()
<< "from" << packet->getSenderSockAddr();
<< "from" << message->getSenderSockAddr();
noisyMessageTimer.restart();
}
}
@ -993,7 +993,7 @@ void DomainServer::sendHeartbeatToIceServer() {
DependencyManager::get<LimitedNodeList>()->sendHeartbeatToIceServer(_iceServerSocket);
}
void DomainServer::processNodeJSONStatsPacket(QSharedPointer<NLPacketList> packetList, SharedNodePointer sendingNode) {
void DomainServer::processNodeJSONStatsPacket(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer sendingNode) {
auto nodeData = dynamic_cast<DomainServerNodeData*>(sendingNode->getLinkedData());
if (nodeData) {
nodeData->updateJSONStats(packetList->getMessage());
@ -1767,17 +1767,17 @@ void DomainServer::addStaticAssignmentsToQueue() {
}
}
void DomainServer::processPathQueryPacket(QSharedPointer<NLPacket> packet) {
void DomainServer::processPathQueryPacket(QSharedPointer<ReceivedMessage> message) {
// this is a query for the viewpoint resulting from a path
// first pull the query path from the packet
// figure out how many bytes the sender said this path is
quint16 numPathBytes;
packet->readPrimitive(&numPathBytes);
message->readPrimitive(&numPathBytes);
if (numPathBytes <= packet->bytesLeftToRead()) {
if (numPathBytes <= message->getBytesLeftToRead()) {
// the number of path bytes makes sense for the sent packet - pull out the path
QString pathQuery = QString::fromUtf8(packet->getPayload() + packet->pos(), numPathBytes);
QString pathQuery = QString::fromUtf8(message->getRawMessage() + message->getPosition(), numPathBytes);
// our settings contain paths that start with a leading slash, so make sure this query has that
if (!pathQuery.startsWith("/")) {
@ -1825,7 +1825,7 @@ void DomainServer::processPathQueryPacket(QSharedPointer<NLPacket> packet) {
// send off the packet - see if we can associate this outbound data to a particular node
// TODO: does this senderSockAddr always work for a punched DS client?
nodeList->sendPacket(std::move(pathResponsePacket), packet->getSenderSockAddr());
nodeList->sendPacket(std::move(pathResponsePacket), message->getSenderSockAddr());
}
}
@ -1837,11 +1837,11 @@ void DomainServer::processPathQueryPacket(QSharedPointer<NLPacket> packet) {
}
}
void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer<NLPacket> packet) {
void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer<ReceivedMessage> message) {
// This packet has been matched to a source node and they're asking not to be in the domain anymore
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
const QUuid& nodeUUID = packet->getSourceID();
const QUuid& nodeUUID = message->getSourceID();
qDebug() << "Received a disconnect request from node with UUID" << nodeUUID;

View file

@ -56,11 +56,11 @@ public slots:
void restart();
void processRequestAssignmentPacket(QSharedPointer<NLPacket> packet);
void processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processNodeJSONStatsPacket(QSharedPointer<NLPacketList> packetList, SharedNodePointer sendingNode);
void processPathQueryPacket(QSharedPointer<NLPacket> packet);
void processNodeDisconnectRequestPacket(QSharedPointer<NLPacket> packet);
void processRequestAssignmentPacket(QSharedPointer<ReceivedMessage> packet);
void processListRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
void processNodeJSONStatsPacket(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer sendingNode);
void processPathQueryPacket(QSharedPointer<ReceivedMessage> packet);
void processNodeDisconnectRequestPacket(QSharedPointer<ReceivedMessage> message);
private slots:
void aboutToQuit();

View file

@ -67,9 +67,9 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
}
void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<NLPacket> packet) {
void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message) {
Assignment::Type type;
packet->readPrimitive(&type);
message->readPrimitive(&type);
QJsonObject responseObject = responseObjectForType(QString::number(type));
auto json = QJsonDocument(responseObject).toJson();
@ -79,7 +79,7 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<NL
packetList->write(json);
auto nodeList = DependencyManager::get<LimitedNodeList>();
nodeList->sendPacketList(std::move(packetList), packet->getSenderSockAddr());
nodeList->sendPacketList(std::move(packetList), message->getSenderSockAddr());
}
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {

View file

@ -18,7 +18,7 @@
#include <HifiConfigVariantMap.h>
#include <HTTPManager.h>
#include <NLPacket.h>
#include <ReceivedMessage.h>
const QString SETTINGS_PATHS_KEY = "paths";
@ -42,7 +42,7 @@ public:
QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); }
private slots:
void processSettingsRequestPacket(QSharedPointer<NLPacket> packet);
void processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message);
private:
QJsonObject responseObjectForType(const QString& typeValue, bool isAuthenticated = false);

View file

@ -0,0 +1,105 @@
{
"assets": {
"crowd-boos.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/crowd-boos.wav",
"atp_url": "atp:c632c92b166ade60aa16b23ff1dfdf712856caeb83bd9311980b2d5edac821af.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"crowd-cheers-organ.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/crowd-cheers-organ.wav",
"atp_url": "atp:b8044401a846ed29f881a0b9b80cf1ba41f26327180c28fc9c70d144f9b70045.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"crowd-medium.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/crowd-medium.wav",
"atp_url": "atp:0821bf2ac60dd2f356dfdd948e8bb89c23984dc3584612f6c815765154f02cae.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"baseball-hitting-bat-1.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-1.wav",
"atp_url": "atp:6f0b691a0c9c9ece6557d97fe242b1faec4020fe26efc9c17327993b513c5fe5.wav",
"attribution": "CC BY 3.0 - Credit: https://www.freesound.org/people/SocializedArtist45/"
},
"baseball-hitting-bat-set-1.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-set-1.wav",
"atp_url": "atp:5be5806205158ebdc5c3623ceb7ae73315028b51ffeae24292aff7042e3fa6a9.wav",
"attribution": "CC BY 3.0 - Credit: CGEffex - https://www.freesound.org/people/CGEffex/"
},
"baseball-hitting-bat-set-2.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-set-2.wav",
"atp_url": "atp:e68661374e2145c480809c26134782aad11e0de456c7802170c7abccc4028873.wav",
"attribution": "CC BY 3.0 - Credit: CGEffex - https://www.freesound.org/people/CGEffex/"
},
"baseball-hitting-bat-set-3.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-set-3.wav",
"atp_url": "atp:787e3c9af17dd3929527787176ede83d6806260e63ddd5a4cef48cd22e32c6f7.wav",
"attribution": "CC BY 3.0 - Credit: CGEffex - https://www.freesound.org/people/CGEffex/"
},
"baseball-hitting-bat-set-4.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-set-4.wav",
"atp_url": "atp:fc65383431a6238c7a4749f0f6f061f75a604ed5e17d775ab1b2955609e67ebb.wav",
"attribution": "CC BY 3.0 - Credit: CGEffex - https://www.freesound.org/people/CGEffex/"
},
"chatter-loop.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/chatter-loop.wav",
"atp_url": "atp:d9978e693035d4e2b5c7b546c8cccfb2dde5677834d9eed5206ccb2da55b4732.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"zorba-organ.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/zorba-organ.wav",
"atp_url": "atp:1ee58f4d929fdef7c2989cd8be964952a24cdd653d80f57b6a89a2ae8e3029e1.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"charge-organ.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/charge-organ.wav",
"atp_url": "atp:cefaba2d5f1a378382ee046716bffcf6b6a40676649b23a1e81a996efe22d7d3.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"ball-game.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/ball-game.wav",
"atp_url": "atp:fb41e37f8f8f7b78e546ac78800df6e39edaa09b2df4bfa0afdd8d749dac38b8.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"clapping.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/clapping.wav",
"atp_url": "atp:44a83a788ccfd2924e35c902c34808b24dbd0309d000299ce01a355f91cf8115.wav",
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
},
"pop1.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/pop1.wav",
"atp_url": "atp:a2bf79c95fe74c2c6c9188acc7230f7cd1b0f6008f2c81954ecd93eca0497ec6.wav"
},
"pop2.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/pop2.wav",
"atp_url": "atp:901067ebc2cda4c0d86ec02fcca2ed901e85f9097ad68bbde78b4cad8eaf2ed7.wav"
},
"pop3.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/pop3.wav",
"atp_url": "atp:830312930577cb1ea36ba2d743e957debbacceb441b20addead5a6faa05a3771.wav"
},
"pop4.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/pop4.wav",
"atp_url": "atp:62e80d0a9f084cf731bcc66ca6e9020ee88587417071a281eee3167307b53560.wav"
},
"fire1.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/fire1.wav",
"atp_url": "atp:ee6afe565576c4546c6d6cd89c1af532484c9b60ab30574d6b40c2df022f7260.wav",
"attribution": "CC BY 3.0 - Credir: dcsimon - https://www.freesound.org/people/dcsimon/sounds/160720/"
},
"fire2.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/fire2.wav",
"atp_url": "atp:91ef19ba1c78be82d3fd06530cd05ceb90d1e75f4204c66819c208c55da049ef.wav",
"attribution": "CC BY 3.0 - Credit: dcsimon - https://www.freesound.org/people/dcsimon/sounds/160720/"
},
"fire3.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/fire3.wav",
"atp_url": "atp:ee56993daf775012cf49293bfd5971eec7e5c396642f8bfbea902ba8f47b56cd.wav",
"attribution": "CC BY 3.0 - Credit: dcsimon - https://www.freesound.org/people/dcsimon/sounds/160720/"
},
"fire4.wav": {
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/fire4.wav",
"atp_url": "atp:37775d267f00f82242a7e7f61f3f3d7bf64a54c5a3799e7f2540fa5f6b79bd02.wav",
"attribution": "CC BY 3.0 - Credit: dcsimon - https://www.freesound.org/people/dcsimon/sounds/160720/"
}
}
}

View file

@ -0,0 +1,43 @@
//
// baseballCrowd.js
// examples/acScripts
//
// Created by Stephen Birarda on 10/20/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var chatter = SoundCache.getSound("atp:d9978e693035d4e2b5c7b546c8cccfb2dde5677834d9eed5206ccb2da55b4732.wav");
var extras = [
SoundCache.getSound("atp:1ee58f4d929fdef7c2989cd8be964952a24cdd653d80f57b6a89a2ae8e3029e1.wav"), // zorba
SoundCache.getSound("atp:cefaba2d5f1a378382ee046716bffcf6b6a40676649b23a1e81a996efe22d7d3.wav"), // charge
SoundCache.getSound("atp:fb41e37f8f8f7b78e546ac78800df6e39edaa09b2df4bfa0afdd8d749dac38b8.wav"), // take me out to the ball game
SoundCache.getSound("atp:44a83a788ccfd2924e35c902c34808b24dbd0309d000299ce01a355f91cf8115.wav") // clapping
];
var CHATTER_VOLUME = 0.20
var EXTRA_VOLUME = 0.25
function playChatter() {
if (chatter.downloaded && !chatter.isPlaying) {
Audio.playSound(chatter, { loop: true, volume: CHATTER_VOLUME });
}
}
chatter.ready.connect(playChatter);
var currentInjector = null;
function playRandomExtras() {
if ((!currentInjector || !currentInjector.isPlaying) && (Math.random() < (1.0 / 1800.0))) {
// play a random extra sound about every 30s
currentInjector = Audio.playSound(
extras[Math.floor(Math.random() * extras.length)],
{ volume: EXTRA_VOLUME }
);
}
}
Script.update.connect(playRandomExtras);

43
examples/baseball/bat.js Normal file
View file

@ -0,0 +1,43 @@
//
// bat.js
// examples/baseball/
//
// Created by Ryan Huffman on Nov 9, 2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
Script.include("pitching.js");
var pitchingMachine = null;
this.pitchAndHideAvatar = function() {
if (!pitchingMachine) {
pitchingMachine = getOrCreatePitchingMachine();
Script.update.connect(function(dt) { pitchingMachine.update(dt); });
}
pitchingMachine.start();
MyAvatar.shouldRenderLocally = false;
};
this.startNearGrab = function() {
// send the avatar to the baseball location so that they're ready to bat
location = "/baseball"
this.pitchAndHideAvatar()
};
this.continueNearGrab = function() {
this.pitchAndHideAvatar()
};
this.releaseGrab = function() {
if (pitchingMachine) {
pitchingMachine.stop();
}
MyAvatar.shouldRenderLocally = true;
};
});

View file

@ -0,0 +1,76 @@
//
// createBatButton.js
// examples/baseball/moreBatsButton.js
//
// Created by Stephen Birarda on 10/28/2015.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function(){
this.clickReleaseOnEntity = function(entityID, mouseEvent) {
if (!mouseEvent.isLeftButton) {
return;
}
this.dropBats();
};
this.startNearTrigger = function() {
this.dropBats();
};
this.startFarTrigger = function() {
this.dropBats();
};
this.dropBats = function() {
// if the bat box is near us, grab it's position
var nearby = Entities.findEntities(this.position, 20);
nearby.forEach(function(id) {
var properties = Entities.getEntityProperties(id, ["name", "position"]);
if (properties.name && properties.name == "Bat Box") {
boxPosition = properties.position;
}
});
var BAT_DROP_HEIGHT = 2.0;
var dropPosition;
if (!boxPosition) {
// we got no bat box position, drop in front of the avatar instead
} else {
// drop the bat above the bat box
dropPosition = Vec3.sum(boxPosition, { x: 0.0, y: BAT_DROP_HEIGHT, z: 0.0});
}
var BAT_MODEL = "atp:c47deaae09cca927f6bc9cca0e8bbe77fc618f8c3f2b49899406a63a59f885cb.fbx";
var BAT_COLLISION_HULL = "atp:9eafceb7510c41d50661130090de7e0632aa4da236ebda84a0059a4be2130e0c.obj";
var SCRIPT_URL = "http://rawgit.com/birarda/hifi/baseball/examples/baseball/bat.js"
var batUserData = {
grabbableKey: {
spatialKey: {
leftRelativePosition: { x: 0.9, y: 0.05, z: -0.05 },
rightRelativePosition: { x: 0.9, y: 0.05, z: 0.05 },
relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, 45)
}
}
}
// add the fresh bat at the drop position
var bat = Entities.addEntity({
name: 'Bat',
type: "Model",
modelURL: BAT_MODEL,
position: dropPosition,
compoundShapeURL: BAT_COLLISION_HULL,
collisionsWillMove: true,
velocity: { x: 0, y: 0.05, z: 0}, // workaround for gravity not taking effect on add
gravity: { x: 0, y: -9.81, z: 0},
rotation: Quat.fromPitchYawRollDegrees(0.0, 0.0, -90.0),
script: SCRIPT_URL,
userData: JSON.stringify(batUserData)
});
};
});

View file

@ -0,0 +1,138 @@
//
// firework.js
// examples/baseball/
//
// Created by Ryan Huffman on Nov 9, 2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("utils.js");
var emitters = [];
var smokeTrailSettings = {
"name":"ParticlesTest Emitter",
"type": "ParticleEffect",
"color":{"red":205,"green":84.41176470588235,"blue":84.41176470588235},
"maxParticles":1000,
"velocity": { x: 0, y: 18.0, z: 0 },
"lifetime": 20,
"lifespan":3,
"emitRate":100,
"emitSpeed":0.5,
"speedSpread":0,
"emitOrientation":{"x":0,"y":0,"z":0,"w":1},
"emitDimensions":{"x":0,"y":0,"z":0},
"emitRadiusStart":0.5,
"polarStart":1,
"polarFinish":1,
"azimuthStart":0,
"azimuthFinish":0,
"emitAcceleration":{"x":0,"y":-0.70000001192092896,"z":0},
"accelerationSpread":{"x":0,"y":0,"z":0},
"particleRadius":0.03999999910593033,
"radiusSpread":0,
"radiusStart":0.13999999910593033,
"radiusFinish":0.14,
"colorSpread":{"red":0,"green":0,"blue":0},
"colorStart":{"red":255,"green":255,"blue":255},
"colorFinish":{"red":255,"green":255,"blue":255},
"alpha":1,
"alphaSpread":0,
"alphaStart":0,
"alphaFinish":1,
"textures":"https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png"
};
var fireworkSettings = {
"name":"ParticlesTest Emitter",
"type": "ParticleEffect",
"color":{"red":205,"green":84.41176470588235,"blue":84.41176470588235},
"maxParticles":1000,
"lifetime": 20,
"lifespan":4,
"emitRate":1000,
"emitSpeed":1.5,
"speedSpread":1.0,
"emitOrientation":{"x":-0.2,"y":0,"z":0,"w":0.7000000000000001},
"emitDimensions":{"x":0,"y":0,"z":0},
"emitRadiusStart":0.5,
"polarStart":1,
"polarFinish":1.2,
"azimuthStart":-Math.PI,
"azimuthFinish":Math.PI,
"emitAcceleration":{"x":0,"y":-0.70000001192092896,"z":0},
"accelerationSpread":{"x":0,"y":0,"z":0},
"particleRadius":0.03999999910593033,
"radiusSpread":0,
"radiusStart":0.13999999910593033,
"radiusFinish":0.14,
"colorSpread":{"red":0,"green":0,"blue":0},
"colorStart":{"red":255,"green":255,"blue":255},
"colorFinish":{"red":255,"green":255,"blue":255},
"alpha":1,
"alphaSpread":0,
"alphaStart":0,
"alphaFinish":1,
"textures":"https://hifi-public.s3.amazonaws.com/alan/Particles/spark_2.png",
};
var popSounds = getSounds([
"atp:a2bf79c95fe74c2c6c9188acc7230f7cd1b0f6008f2c81954ecd93eca0497ec6.wav",
"atp:901067ebc2cda4c0d86ec02fcca2ed901e85f9097ad68bbde78b4cad8eaf2ed7.wav",
"atp:830312930577cb1ea36ba2d743e957debbacceb441b20addead5a6faa05a3771.wav",
"atp:62e80d0a9f084cf731bcc66ca6e9020ee88587417071a281eee3167307b53560.wav"
]);
var launchSounds = getSounds([
"atp:ee6afe565576c4546c6d6cd89c1af532484c9b60ab30574d6b40c2df022f7260.wav",
"atp:91ef19ba1c78be82d3fd06530cd05ceb90d1e75f4204c66819c208c55da049ef.wav",
"atp:ee56993daf775012cf49293bfd5971eec7e5c396642f8bfbea902ba8f47b56cd.wav",
"atp:37775d267f00f82242a7e7f61f3f3d7bf64a54c5a3799e7f2540fa5f6b79bd02.wav"
]);
function playRandomSound(sounds, options) {
Audio.playSound(sounds[randomInt(sounds.length)], options);
}
function shootFirework(position, color, options) {
smokeTrailSettings.position = position;
smokeTrailSettings.velocity = randomVec3(-5, 5, 10, 20, 10, 15);
smokeTrailSettings.gravity = randomVec3(-5, 5, -9.8, -9.8, 20, 40);
playRandomSound(launchSounds, { position: {x: 0, y: 0 , z: 0}, volume: 3.0 });
var smokeID = Entities.addEntity(smokeTrailSettings);
Script.setTimeout(function() {
Entities.editEntity(smokeID, { emitRate: 0 });
var position = Entities.getEntityProperties(smokeID, ['position']).position;
fireworkSettings.position = position;
fireworkSettings.colorStart = color;
fireworkSettings.colorFinish = color;
var burstID = Entities.addEntity(fireworkSettings);
playRandomSound(popSounds, { position: {x: 0, y: 0 , z: 0}, volume: 3.0 });
Script.setTimeout(function() {
Entities.editEntity(burstID, { emitRate: 0 });
}, 500);
Script.setTimeout(function() {
Entities.deleteEntity(smokeID);
Entities.deleteEntity(burstID);
}, 10000);
}, 2000);
}
playFireworkShow = function(position, numberOfFireworks, duration) {
for (var i = 0; i < numberOfFireworks; i++) {
var randomOffset = randomVec3(-15, 15, -3, 3, -1, 1);
var randomPosition = Vec3.sum(position, randomOffset);
Script.setTimeout(function(position) {
return function() {
var color = randomColor(128, 255, 128, 255, 128, 255);
shootFirework(position, color, fireworkSettings);
}
}(randomPosition), Math.random() * duration)
}
}

165
examples/baseball/line.js Normal file
View file

@ -0,0 +1,165 @@
function info(message) {
print("[INFO] " + message);
}
function error(message) {
print("[ERROR] " + message);
}
/******************************************************************************
* PolyLine
*****************************************************************************/
var LINE_DIMENSIONS = { x: 2000, y: 2000, z: 2000 };
var MAX_LINE_LENGTH = 40; // This must be 2 or greater;
var PolyLine = function(position, color, defaultStrokeWidth) {
//info("Creating polyline");
//Vec3.print("New line at", position);
this.position = position;
this.color = color;
this.defaultStrokeWidth = 0.10;
this.points = [
{ x: 0, y: 0, z: 0 },
];
this.strokeWidths = [
this.defaultStrokeWidth,
]
this.normals = [
{ x: 1, y: 0, z: 0 },
]
this.entityID = Entities.addEntity({
type: "PolyLine",
position: position,
linePoints: this.points,
normals: this.normals,
strokeWidths: this.strokeWidths,
dimensions: LINE_DIMENSIONS,
color: color,
lifetime: 20,
});
};
PolyLine.prototype.enqueuePoint = function(position) {
if (this.isFull()) {
error("Hit max PolyLine size");
return;
}
//Vec3.print("pos", position);
//info("Number of points: " + this.points.length);
position = Vec3.subtract(position, this.position);
this.points.push(position);
this.normals.push({ x: 1, y: 0, z: 0 });
this.strokeWidths.push(this.defaultStrokeWidth);
Entities.editEntity(this.entityID, {
linePoints: this.points,
normals: this.normals,
strokeWidths: this.strokeWidths,
});
};
PolyLine.prototype.dequeuePoint = function() {
if (this.points.length == 0) {
error("Hit min PolyLine size");
return;
}
this.points = this.points.slice(1);
this.normals = this.normals.slice(1);
this.strokeWidths = this.strokeWidths.slice(1);
Entities.editEntity(this.entityID, {
linePoints: this.points,
normals: this.normals,
strokeWidths: this.strokeWidths,
});
};
PolyLine.prototype.getFirstPoint = function() {
return Vec3.sum(this.position, this.points[0]);
};
PolyLine.prototype.getLastPoint = function() {
return Vec3.sum(this.position, this.points[this.points.length - 1]);
};
PolyLine.prototype.getSize = function() {
return this.points.length;
}
PolyLine.prototype.isFull = function() {
return this.points.length >= MAX_LINE_LENGTH;
};
PolyLine.prototype.destroy = function() {
Entities.deleteEntity(this.entityID);
this.points = [];
};
/******************************************************************************
* InfiniteLine
*****************************************************************************/
InfiniteLine = function(position, color) {
this.position = position;
this.color = color;
this.lines = [new PolyLine(position, color)];
this.size = 0;
};
InfiniteLine.prototype.enqueuePoint = function(position) {
var currentLine;
if (this.lines.length == 0) {
currentLine = new PolyLine(position, this.color);
this.lines.push(currentLine);
} else {
currentLine = this.lines[this.lines.length - 1];
}
if (currentLine.isFull()) {
//info("Current line is full, creating new line");
//Vec3.print("Last line is", currentLine.getLastPoint());
//Vec3.print("New line is", position);
var newLine = new PolyLine(currentLine.getLastPoint(), this.color);
this.lines.push(newLine);
currentLine = newLine;
}
currentLine.enqueuePoint(position);
++this.size;
};
InfiniteLine.prototype.dequeuePoint = function() {
if (this.lines.length == 0) {
error("Trying to dequeue from InfiniteLine when no points are left");
return;
}
var lastLine = this.lines[0];
lastLine.dequeuePoint();
if (lastLine.getSize() <= 1) {
this.lines = this.lines.slice(1);
}
--this.size;
};
InfiniteLine.prototype.getFirstPoint = function() {
return this.lines.length > 0 ? this.lines[0].getFirstPoint() : null;
};
InfiniteLine.prototype.getLastPoint = function() {
return this.lines.length > 0 ? this.lines[lines.length - 1].getLastPoint() : null;
};
InfiniteLine.prototype.destroy = function() {
for (var i = 0; i < this.lines.length; ++i) {
this.lines[i].destroy();
}
this.size = 0;
};

View file

@ -0,0 +1,554 @@
//
// pitching.js
// examples/baseball/
//
// Created by Ryan Huffman on Nov 9, 2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
print("Loading pitching");
Script.include("../libraries/line.js");
Script.include("firework.js");
Script.include("utils.js");
var DISTANCE_BILLBOARD_NAME = "CurrentScore";
var HIGH_SCORE_BILLBOARD_NAME = "HighScore";
var distanceBillboardEntityID = null;
var highScoreBillboardEntityID = null;
function getDistanceBillboardEntityID() {
if (distanceBillboardEntityID === null) {
distanceBillboardEntityID = findEntity({name: DISTANCE_BILLBOARD_NAME }, 1000);
}
return distanceBillboardEntityID;
}
function getHighScoreBillboardEntityID() {
if (highScoreBillboardEntityID === null) {
highScoreBillboardEntityID = findEntity({name: HIGH_SCORE_BILLBOARD_NAME }, 1000);
}
return highScoreBillboardEntityID;
}
var METERS_TO_FEET = 3.28084;
var AUDIO = {
crowdBoos: [
SoundCache.getSound("atp:c632c92b166ade60aa16b23ff1dfdf712856caeb83bd9311980b2d5edac821af.wav", false)
],
crowdCheers: [
SoundCache.getSound("atp:0821bf2ac60dd2f356dfdd948e8bb89c23984dc3584612f6c815765154f02cae.wav", false),
SoundCache.getSound("atp:b8044401a846ed29f881a0b9b80cf1ba41f26327180c28fc9c70d144f9b70045.wav", false),
],
batHit: [
SoundCache.getSound("atp:6f0b691a0c9c9ece6557d97fe242b1faec4020fe26efc9c17327993b513c5fe5.wav", false),
SoundCache.getSound("atp:5be5806205158ebdc5c3623ceb7ae73315028b51ffeae24292aff7042e3fa6a9.wav", false),
SoundCache.getSound("atp:e68661374e2145c480809c26134782aad11e0de456c7802170c7abccc4028873.wav", false),
SoundCache.getSound("atp:787e3c9af17dd3929527787176ede83d6806260e63ddd5a4cef48cd22e32c6f7.wav", false),
SoundCache.getSound("atp:fc65383431a6238c7a4749f0f6f061f75a604ed5e17d775ab1b2955609e67ebb.wav", false),
],
strike: [
SoundCache.getSound("atp:2a258076a85fffde4ba04b5ddc1de9034c7ae7d2af8c5d93d4fed0bcdef3472a.wav", false),
SoundCache.getSound("atp:518363524af3ed9b9ae4ca2ceee61f01aecd37e266a51c5a5f5487efe2520fd5.wav", false),
SoundCache.getSound("atp:d51d38b089574acbdfdf53ef733bfb3ab41d848fb8c0b6659c7790a785240009.wav", false),
],
foul: [
SoundCache.getSound("atp:316fa18ff9eef457f670452b449a8dc5a41ccabd4e948781c50aaafaae63b0ab.wav", false),
SoundCache.getSound("atp:c84d88352d38437edd7414b26dc74e567618712caeb59fec70822398b0c5a279.wav", false),
]
}
var PITCH_THUNK_SOUND_URL = "http://hifi-public.s3.amazonaws.com/sounds/ping_pong_gun/pong_sound.wav";
var pitchSound = SoundCache.getSound(PITCH_THUNK_SOUND_URL, false);
updateBillboard("");
var PITCHING_MACHINE_URL = "atp:87d4879530b698741ecc45f6f31789aac11f7865a2c3bec5fe9b061a182c80d4.fbx";
// This defines an offset to pitch a ball from with respect to the machine's position. The offset is a
// percentage of the machine's dimensions. So, { x: 0.5, y: -1.0, z: 0.0 } would offset on 50% on the
// machine's x axis, -100% on the y axis, and 0% on the z-axis. For the dimensions { x: 100, y: 100, z: 100 },
// that would result in an offset of { x: 50, y: -100, z: 0 }. This makes it easy to calculate an offset if
// the machine's dimensions change.
var PITCHING_MACHINE_OUTPUT_OFFSET_PCT = {
x: 0.0,
y: 0.25,
z: -1.05,
};
var PITCHING_MACHINE_PROPERTIES = {
name: "Pitching Machine",
type: "Model",
position: {
x: -0.93,
y: 0.8,
z: -19.8
},
velocity: {
x: 0,
y: -0.01,
z: 0
},
gravity: {
x: 0.0,
y: -9.8,
z: 0.0
},
registrationPoint: {
x: 0.5,
y: 0.5,
z: 0.5
},
rotation: Quat.fromPitchYawRollDegrees(0, 180, 0),
modelURL: PITCHING_MACHINE_URL,
dimensions: {
x: 0.4,
y: 0.61,
z: 0.39
},
collisionsWillMove: false,
shapeType: "Box"
};
PITCHING_MACHINE_PROPERTIES.dimensions = Vec3.multiply(2.5, PITCHING_MACHINE_PROPERTIES.dimensions);
var DISTANCE_FROM_PLATE = PITCHING_MACHINE_PROPERTIES.position.z;
var PITCH_RATE = 5000;
getOrCreatePitchingMachine = function() {
// Search for pitching machine
var entities = findEntities({ name: PITCHING_MACHINE_PROPERTIES.name }, 1000);
var pitchingMachineID = null;
// Create if it doesn't exist
if (entities.length == 0) {
pitchingMachineID = Entities.addEntity(PITCHING_MACHINE_PROPERTIES);
} else {
pitchingMachineID = entities[0];
}
// Wrap with PitchingMachine object and return
return new PitchingMachine(pitchingMachineID);
}
// The pitching machine wraps an entity ID and uses it's position & rotation to determin where to
// pitch the ball from and in which direction, and uses the dimensions to determine the scale of them ball.
function PitchingMachine(pitchingMachineID) {
this.pitchingMachineID = pitchingMachineID;
this.enabled = false;
this.baseball = null;
this.injector = null;
}
PitchingMachine.prototype = {
pitchBall: function() {
cleanupTrail();
if (!this.enabled) {
return;
}
print("Pitching ball");
var machineProperties = Entities.getEntityProperties(this.pitchingMachineID, ["dimensions", "position", "rotation"]);
var pitchFromPositionBase = machineProperties.position;
var pitchFromOffset = vec3Mult(machineProperties.dimensions, PITCHING_MACHINE_OUTPUT_OFFSET_PCT);
pitchFromOffset = Vec3.multiplyQbyV(machineProperties.rotation, pitchFromOffset);
var pitchFromPosition = Vec3.sum(pitchFromPositionBase, pitchFromOffset);
var pitchDirection = Quat.getFront(machineProperties.rotation);
var ballScale = machineProperties.dimensions.x / PITCHING_MACHINE_PROPERTIES.dimensions.x;
var speed = randomFloat(BASEBALL_MIN_SPEED, BASEBALL_MAX_SPEED);
var velocity = Vec3.multiply(speed, pitchDirection);
this.baseball = new Baseball(pitchFromPosition, velocity, ballScale);
if (!this.injector) {
this.injector = Audio.playSound(pitchSound, {
position: pitchFromPosition,
volume: 1.0
});
} else {
this.injector.restart();
}
},
start: function() {
if (this.enabled) {
return;
}
print("Starting Pitching Machine");
this.enabled = true;
this.pitchBall();
},
stop: function() {
if (!this.enabled) {
return;
}
print("Stopping Pitching Machine");
this.enabled = false;
},
update: function(dt) {
if (this.baseball) {
this.baseball.update(dt);
if (this.baseball.finished()) {
this.baseball = null;
var self = this;
Script.setTimeout(function() { self.pitchBall(); }, 3000);
}
}
}
};
var BASEBALL_MODEL_URL = "atp:7185099f1f650600ca187222573a88200aeb835454bd2f578f12c7fb4fd190fa.fbx";
var BASEBALL_MIN_SPEED = 7.0;
var BASEBALL_MAX_SPEED = 15.0;
var BASEBALL_RADIUS = 0.07468;
var BASEBALL_PROPERTIES = {
name: "Baseball",
type: "Model",
modelURL: BASEBALL_MODEL_URL,
shapeType: "Sphere",
position: {
x: 0,
y: 0,
z: 0
},
dimensions: {
x: BASEBALL_RADIUS,
y: BASEBALL_RADIUS,
z: BASEBALL_RADIUS
},
collisionsWillMove: true,
angularVelocity: {
x: 17.0,
y: 0,
z: -8.0,
x: 0.0,
y: 0,
z: 0.0,
},
angularDamping: 0.0,
damping: 0.0,
restitution: 0.5,
friction: 0.0,
friction: 0.5,
lifetime: 20,
gravity: {
x: 0,
y: 0,
z: 0
}
};
var BASEBALL_STATE = {
PITCHING: 0,
HIT: 1,
HIT_LANDED: 2,
STRIKE: 3,
FOUL: 4
};
function vec3Mult(a, b) {
return {
x: a.x * b.x,
y: a.y * b.y,
z: a.z * b.z,
};
}
function map(value, min1, max1, min2, max2) {
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
}
function orientationOf(vector) {
var RAD_TO_DEG = 180.0 / Math.PI;
var Y_AXIS = { x: 0, y: 1, z: 0 };
var X_AXIS = { x: 1, y: 0, z: 0 };
var direction = Vec3.normalize(vector);
var yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS);
var pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS);
return Quat.multiply(yaw, pitch);
}
var ACCELERATION_SPREAD = 0.35;
var TRAIL_COLOR = { red: 128, green: 255, blue: 89 };
var TRAIL_LIFETIME = 20;
var trail = null;
var trailInterval = null;
function cleanupTrail() {
if (trail) {
Script.clearInterval(this.trailInterval);
trailInterval = null;
trail.destroy();
trail = null;
}
}
function setupTrail(entityID, position) {
cleanupTrail();
var lastPosition = position;
trail = new InfiniteLine(position, { red: 128, green: 255, blue: 89 }, 20);
trailInterval = Script.setInterval(function() {
var properties = Entities.getEntityProperties(entityID, ['position']);
if (Vec3.distance(properties.position, lastPosition)) {
var strokeWidth = Math.log(1 + trail.size) * 0.05;
trail.enqueuePoint(properties.position, strokeWidth);
lastPosition = properties.position;
}
}, 50);
}
function Baseball(position, velocity, ballScale) {
var self = this;
this.state = BASEBALL_STATE.PITCHING;
// Setup entity properties
var properties = shallowCopy(BASEBALL_PROPERTIES);
properties.position = position;
properties.velocity = velocity;
properties.dimensions = Vec3.multiply(ballScale, properties.dimensions);
/*
properties.gravity = {
x: randomFloat(-ACCELERATION_SPREAD, ACCELERATION_SPREAD),
y: randomFloat(-ACCELERATION_SPREAD, ACCELERATION_SPREAD),
z: 0.0,
};
*/
// Create entity
this.entityID = Entities.addEntity(properties);
this.timeSincePitched = 0;
this.timeSinceHit = 0;
this.hitBallAtPosition = null;
this.distanceTravelled = 0;
this.wasHighScore = false;
this.landed = false;
// Listen for collision for the lifetime of the entity
Script.addEventHandler(this.entityID, "collisionWithEntity", function(entityA, entityB, collision) {
self.collisionCallback(entityA, entityB, collision);
});
if (Math.random() < 0.5) {
for (var i = 0; i < 50; i++) {
Script.setTimeout(function() {
Entities.editEntity(entityID, {
gravity: {
x: randomFloat(-ACCELERATION_SPREAD, ACCELERATION_SPREAD),
y: randomFloat(-ACCELERATION_SPREAD, ACCELERATION_SPREAD),
z: 0.0,
}
})
}, i * 100);
}
}
}
// Update the stadium billboard with the current distance or a text message and update the high score
// if it has been beaten.
function updateBillboard(distanceOrMessage) {
var distanceBillboardEntityID = getDistanceBillboardEntityID();
if (distanceBillboardEntityID) {
Entities.editEntity(distanceBillboardEntityID, {
text: distanceOrMessage
});
}
var highScoreBillboardEntityID = getHighScoreBillboardEntityID();
// If a number was passed in, let's see if it is larger than the current high score
// and update it if so.
if (!isNaN(distanceOrMessage) && highScoreBillboardEntityID) {
var properties = Entities.getEntityProperties(highScoreBillboardEntityID, ["text"]);
var bestDistance = parseInt(properties.text);
if (distanceOrMessage >= bestDistance) {
Entities.editEntity(highScoreBillboardEntityID, {
text: distanceOrMessage,
});
return true;
}
}
return false;
}
var FIREWORKS_SHOW_POSITION = { x: 0, y: 0, z: -78.0 };
var FIREWORK_PER_X_FEET = 100;
var MAX_FIREWORKS = 10;
Baseball.prototype = {
finished: function() {
return this.state == BASEBALL_STATE.FOUL
|| this.state == BASEBALL_STATE.STRIKE
|| this.state == BASEBALL_STATE.HIT_LANDED;
},
update: function(dt) {
this.timeSincePitched += dt;
if (this.state == BASEBALL_STATE.HIT) {
this.timeSinceHit += dt;
var myProperties = Entities.getEntityProperties(this.entityID, ['position', 'velocity']);
var speed = Vec3.length(myProperties.velocity);
this.distanceTravelled = Vec3.distance(this.hitBallAtPosition, myProperties.position) * METERS_TO_FEET;
var wasHighScore = updateBillboard(Math.ceil(this.distanceTravelled));
if (this.landed || this.timeSinceHit > 10 || speed < 1) {
this.wasHighScore = wasHighScore;
this.ballLanded();
}
} else if (this.state == BASEBALL_STATE.PITCHING) {
if (this.timeSincePitched > 10) {
print("TIMED OUT WHILE PITCHING");
this.state = BASEBALL_STATE.STRIKE;
}
}
},
ballLanded: function() {
this.state = BASEBALL_STATE.HIT_LANDED;
var numberOfFireworks = Math.floor(this.distanceTravelled / FIREWORK_PER_X_FEET);
if (numberOfFireworks > 0) {
numberOfFireworks = Math.min(MAX_FIREWORKS, numberOfFireworks);
playFireworkShow(FIREWORKS_SHOW_POSITION, numberOfFireworks, 2000);
}
print("Ball took " + this.timeSinceHit.toFixed(3) + " seconds to land");
print("Ball travelled " + this.distanceTravelled + " feet")
},
collisionCallback: function(entityA, entityB, collision) {
var self = this;
var myProperties = Entities.getEntityProperties(this.entityID, ['position', 'velocity']);
var myPosition = myProperties.position;
var myVelocity = myProperties.velocity;
// Activate gravity
Entities.editEntity(self.entityID, {
gravity: { x: 0, y: -9.8, z: 0 }
});
var name = Entities.getEntityProperties(entityB, ["name"]).name;
if (name == "Bat") {
if (this.state == BASEBALL_STATE.PITCHING) {
print("HIT");
var FOUL_MIN_YAW = -135.0;
var FOUL_MAX_YAW = 135.0;
var yaw = Math.atan2(myVelocity.x, myVelocity.z) * 180 / Math.PI;
var foul = yaw > FOUL_MIN_YAW && yaw < FOUL_MAX_YAW;
var speedMultiplier = 2;
if (foul && myVelocity.z > 0) {
var TUNNELED_PITCH_RANGE = 15.0;
var xzDist = Math.sqrt(myVelocity.x * myVelocity.x + myVelocity.z * myVelocity.z);
var pitch = Math.atan2(myVelocity.y, xzDist) * 180 / Math.PI;
print("Pitch: ", pitch);
// If the pitch is going straight out the back and has a pitch in the range TUNNELED_PITCH_RANGE,
// let's assume the ball tunneled through the bat and reverse its direction.
if (Math.abs(pitch) < TUNNELED_PITCH_RANGE) {
print("Reversing hit");
myVelocity.x *= -1;
myVelocity.y *= -1;
myVelocity.z *= -1;
yaw = Math.atan2(myVelocity.x, myVelocity.z) * 180 / Math.PI;
foul = yaw > FOUL_MIN_YAW && yaw < FOUL_MAX_YAW;
speedMultiplier = 3;
}
}
// Update ball velocity
Entities.editEntity(self.entityID, {
velocity: Vec3.multiply(speedMultiplier, myVelocity)
});
// Setup line update interval
setupTrail(self.entityID, myPosition);
// Setup bat hit sound
playRandomSound(AUDIO.batHit, {
position: myPosition,
volume: 2.0
});
// Setup crowd reaction sound
var speed = Vec3.length(myVelocity);
Script.setTimeout(function() {
playRandomSound((speed < 5.0) ? AUDIO.crowdBoos : AUDIO.crowdCheers, {
position: { x: 0 ,y: 0, z: 0 },
volume: 1.0
});
}, 500);
if (foul) {
print("FOUL, yaw: ", yaw);
updateBillboard("FOUL");
this.state = BASEBALL_STATE.FOUL;
playRandomSound(AUDIO.foul, {
position: myPosition,
volume: 2.0
});
} else {
print("HIT ", yaw);
this.state = BASEBALL_STATE.HIT;
}
}
} else if (name == "stadium") {
//entityCollisionWithGround(entityB, this.entityID, collision);
this.landed = true;
} else if (name == "backstop") {
if (this.state == BASEBALL_STATE.PITCHING) {
print("STRIKE");
this.state = BASEBALL_STATE.STRIKE;
updateBillboard("STRIKE");
playRandomSound(AUDIO.strike, {
position: myPosition,
volume: 2.0
});
}
}
}
}
function entityCollisionWithGround(ground, entity, collision) {
var ZERO_VEC = { x: 0, y: 0, z: 0 };
var dVelocityMagnitude = Vec3.length(collision.velocityChange);
var position = Entities.getEntityProperties(entity, "position").position;
var particleRadius = 0.3;
var speed = map(dVelocityMagnitude, 0.05, 3, 0.02, 0.09);
var displayTime = 400;
var orientationChange = orientationOf(collision.velocityChange);
var dustEffect = Entities.addEntity({
type: "ParticleEffect",
name: "Dust-Puff",
position: position,
color: {red: 195, green: 170, blue: 185},
lifespan: 3,
lifetime: 2,//displayTime/1000 * 2, //So we can fade particle system out gracefully
emitRate: 5,
emitSpeed: speed,
emitAcceleration: ZERO_VEC,
accelerationSpread: ZERO_VEC,
isEmitting: true,
polarStart: Math.PI/2,
polarFinish: Math.PI/2,
emitOrientation: orientationChange,
radiusSpread: 0.1,
radiusStart: particleRadius,
radiusFinish: particleRadius + particleRadius / 2,
particleRadius: particleRadius,
alpha: 0.45,
alphaFinish: 0.001,
textures: "https://hifi-public.s3.amazonaws.com/alan/Playa/Particles/Particle-Sprite-Gen.png"
});
}
Script.scriptEnding.connect(function() {
cleanupTrail();
Entities.deleteEntity(pitchingMachineID);
});

View file

@ -0,0 +1,92 @@
//
// utils.js
// examples/baseball/
//
// Created by Ryan Huffman on Nov 9, 2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
randomInt = function(low, high) {
return Math.floor(randomFloat(low, high));
};
randomFloat = function(low, high) {
if (high === undefined) {
high = low;
low = 0;
}
return low + Math.random() * (high - low);
};
randomColor = function(redMin, redMax, greenMin, greenMax, blueMin, blueMax) {
return {
red: Math.ceil(randomFloat(redMin, redMax)),
green: Math.ceil(randomFloat(greenMin, greenMax)),
blue: Math.ceil(randomFloat(blueMin, blueMax)),
}
};
randomVec3 = function(xMin, xMax, yMin, yMax, zMin, zMax) {
return {
x: randomFloat(xMin, xMax),
y: randomFloat(yMin, yMax),
z: randomFloat(zMin, zMax),
}
};
getSounds = function(soundURLs) {
var sounds = [];
for (var i = 0; i < soundURLs.length; ++i) {
sounds.push(SoundCache.getSound(soundURLs[i], false));
}
return sounds;
};
playRandomSound = function(sounds, options) {
if (options === undefined) {
options = {
volume: 1.0,
position: MyAvatar.position,
}
}
return Audio.playSound(sounds[randomInt(sounds.length)], options);
}
shallowCopy = function(obj) {
var copy = {}
for (var key in obj) {
copy[key] = obj[key];
}
return copy;
}
findEntity = function(properties, searchRadius) {
var entities = findEntities(properties, searchRadius);
return entities.length > 0 ? entities[0] : null;
}
// Return all entities with properties `properties` within radius `searchRadius`
findEntities = function(properties, searchRadius) {
var entities = Entities.findEntities(MyAvatar.position, searchRadius);
var matchedEntities = [];
var keys = Object.keys(properties);
for (var i = 0; i < entities.length; ++i) {
var match = true;
var candidateProperties = Entities.getEntityProperties(entities[i], keys);
for (var key in properties) {
if (candidateProperties[key] != properties[key]) {
// This isn't a match, move to next entity
match = false;
break;
}
}
if (match) {
matchedEntities.push(entities[i]);
}
}
return matchedEntities;
}

View file

@ -201,6 +201,33 @@ function entityIsGrabbedByOther(entityID) {
return false;
}
function getSpatialOffsetPosition(hand, spatialKey) {
if (hand !== RIGHT_HAND && spatialKey.leftRelativePosition) {
return spatialKey.leftRelativePosition;
}
if (hand === RIGHT_HAND && spatialKey.rightRelativePosition) {
return spatialKey.rightRelativePosition;
}
if (spatialKey.relativePosition) {
return spatialKey.relativePosition;
}
return Vec3.ZERO;
}
function getSpatialOffsetRotation(hand, spatialKey) {
if (hand !== RIGHT_HAND && spatialKey.leftRelativeRotation) {
return spatialKey.leftRelativeRotation;
}
if (hand === RIGHT_HAND && spatialKey.rightRelativeRotation) {
return spatialKey.rightRelativeRotation;
}
if (spatialKey.relativeRotation) {
return spatialKey.relativeRotation;
}
return Quat.IDENTITY;
}
function MyController(hand) {
this.hand = hand;
@ -224,20 +251,11 @@ function MyController(hand) {
this.triggerValue = 0; // rolling average of trigger value
this.rawTriggerValue = 0;
this.rawBumperValue = 0;
this.overlayLine = null;
this.offsetPosition = {
x: 0.0,
y: 0.0,
z: 0.0
};
this.offsetRotation = {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0
};
this.offsetPosition = Vec3.ZERO;
this.offsetRotation = Quat.IDENTITY;
var _this = this;
@ -835,12 +853,8 @@ function MyController(hand) {
if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) {
// if an object is "equipped" and has a spatialKey, use it.
if (grabbableData.spatialKey.relativePosition) {
this.offsetPosition = grabbableData.spatialKey.relativePosition;
}
if (grabbableData.spatialKey.relativeRotation) {
this.offsetRotation = grabbableData.spatialKey.relativeRotation;
}
this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey);
this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey);
} else {
var objectRotation = grabbedProperties.rotation;
this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
@ -966,23 +980,8 @@ function MyController(hand) {
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
// use a spring to pull the object to where it will be when equipped
var relativeRotation = {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0
};
var relativePosition = {
x: 0.0,
y: 0.0,
z: 0.0
};
if (grabbableData.spatialKey.relativePosition) {
relativePosition = grabbableData.spatialKey.relativePosition;
}
if (grabbableData.spatialKey.relativeRotation) {
relativeRotation = grabbableData.spatialKey.relativeRotation;
}
var relativeRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey);
var relativePosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey);
var handRotation = this.getHandRotation();
var handPosition = this.getHandPosition();
var targetRotation = Quat.multiply(handRotation, relativeRotation);

View file

@ -20,6 +20,9 @@ var leapHands = (function () {
hasHandAndWristJoints,
handToWristOffset = [], // For avatars without a wrist joint we control an estimate of a proper hand joint position
HAND_OFFSET = 0.4, // Relative distance of wrist to hand versus wrist to index finger knuckle
handAnimationStateHandlers,
handAnimationStateFunctions,
handAnimationStateProperties,
hands,
wrists,
NUM_HANDS = 2, // 0 = left; 1 = right
@ -37,7 +40,14 @@ var leapHands = (function () {
avatarScale,
avatarFaceModelURL,
avatarSkeletonModelURL,
settingsTimer;
settingsTimer,
HMD_CAMERA_TO_AVATAR_ROTATION = [
Quat.angleAxis(180.0, { x: 0, y: 0, z: 1 }),
Quat.angleAxis(-180.0, { x: 0, y: 0, z: 1 })
],
DESKTOP_CAMERA_TO_AVATAR_ROTATION =
Quat.multiply(Quat.angleAxis(180.0, { x: 0, y: 1, z: 0 }), Quat.angleAxis(90.0, { x: 0, y: 0, z: 1 })),
LEAP_THUMB_ROOT_ADJUST = [Quat.fromPitchYawRollDegrees(0, 0, 20), Quat.fromPitchYawRollDegrees(0, 0, -20)];
function printSkeletonJointNames() {
var jointNames,
@ -125,6 +135,26 @@ var leapHands = (function () {
*/
}
function animateLeftHand() {
var ROTATION_AND_POSITION = 0;
return {
leftHandType: ROTATION_AND_POSITION,
leftHandPosition: hands[0].position,
leftHandRotation: hands[0].rotation
};
}
function animateRightHand() {
var ROTATION_AND_POSITION = 0;
return {
rightHandType: ROTATION_AND_POSITION,
rightHandPosition: hands[1].position,
rightHandRotation: hands[1].rotation
};
}
function finishCalibration() {
var avatarPosition,
handPosition,
@ -166,10 +196,8 @@ var leapHands = (function () {
MyAvatar.clearJointData("LeftHand");
MyAvatar.clearJointData("LeftForeArm");
MyAvatar.clearJointData("LeftArm");
MyAvatar.clearJointData("RightHand");
MyAvatar.clearJointData("RightForeArm");
MyAvatar.clearJointData("RightArm");
calibrationStatus = CALIBRATED;
print("Leap Motion: Calibrated");
@ -193,12 +221,10 @@ var leapHands = (function () {
}
// Set avatar arms vertical, forearms horizontal, as "zero" position for calibration
MyAvatar.setJointRotation("LeftArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 0.0));
MyAvatar.setJointRotation("LeftForeArm", Quat.fromPitchYawRollDegrees(0.0, 90.0, 90.0));
MyAvatar.setJointRotation("LeftHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
MyAvatar.setJointRotation("RightArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 0.0));
MyAvatar.setJointRotation("RightForeArm", Quat.fromPitchYawRollDegrees(0.0, -90.0, -90.0));
MyAvatar.setJointRotation("RightHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
MyAvatar.setJointRotation("LeftForeArm", Quat.fromPitchYawRollDegrees(0.0, 0.0, 90.0));
MyAvatar.setJointRotation("LeftHand", Quat.fromPitchYawRollDegrees(0.0, 90.0, 0.0));
MyAvatar.setJointRotation("RightForeArm", Quat.fromPitchYawRollDegrees(0.0, 0.0, -90.0));
MyAvatar.setJointRotation("RightHand", Quat.fromPitchYawRollDegrees(0.0, -90.0, 0.0));
// Wait for arms to assume their positions before calculating
Script.setTimeout(finishCalibration, CALIBRATION_TIME);
@ -319,6 +345,13 @@ var leapHands = (function () {
]
];
handAnimationStateHandlers = [null, null];
handAnimationStateFunctions = [animateLeftHand, animateRightHand];
handAnimationStateProperties = [
["leftHandType", "leftHandPosition", "leftHandRotation"],
["rightHandType", "rightHandPosition", "rightHandPosition"]
];
setIsOnHMD();
settingsTimer = Script.setInterval(checkSettings, 2000);
@ -348,6 +381,12 @@ var leapHands = (function () {
return;
}
// Hand animation handlers ...
if (handAnimationStateHandlers[h] === null) {
handAnimationStateHandlers[h] = MyAvatar.addAnimationStateHandler(handAnimationStateFunctions[h],
handAnimationStateProperties[h]);
}
// Hand position ...
handOffset = hands[h].controller.getAbsTranslation();
handRotation = hands[h].controller.getAbsRotation();
@ -362,37 +401,41 @@ var leapHands = (function () {
// Hand offset in camera coordinates ...
handOffset = {
x: hands[h].zeroPosition.x - handOffset.x,
y: hands[h].zeroPosition.y - handOffset.z,
z: hands[h].zeroPosition.z + handOffset.y
x: -handOffset.x,
y: -handOffset.z,
z: -handOffset.y - hands[h].zeroPosition.z
};
handOffset.z = -handOffset.z;
// Hand offset in world coordinates ...
cameraOrientation = Camera.getOrientation();
handOffset = Vec3.sum(Camera.getPosition(), Vec3.multiplyQbyV(cameraOrientation, handOffset));
// Hand offset in avatar coordinates ...
// Hand offset in avatar coordinates ...
inverseAvatarOrientation = Quat.inverse(MyAvatar.orientation);
handOffset = Vec3.subtract(handOffset, MyAvatar.position);
handOffset = Vec3.multiplyQbyV(inverseAvatarOrientation, handOffset);
handOffset.z = -handOffset.z;
handOffset.x = -handOffset.x;
// Hand rotation in camera coordinates ...
handRotation = {
x: -handRotation.x,
x: -handRotation.y,
y: -handRotation.z,
z: -handRotation.y,
z: -handRotation.x,
w: handRotation.w
};
// Hand rotation in avatar coordinates ...
handRotation = Quat.multiply(Quat.angleAxis(180.0, { x: 0, y: 1, z: 0 }), handRotation);
cameraOrientation.x = -cameraOrientation.x;
cameraOrientation.z = -cameraOrientation.z;
handRotation = Quat.multiply(cameraOrientation, handRotation);
handRotation = Quat.multiply(inverseAvatarOrientation, handRotation);
handRotation = Quat.multiply(HMD_CAMERA_TO_AVATAR_ROTATION[h], handRotation);
cameraOrientation = {
x: cameraOrientation.z,
y: cameraOrientation.y,
z: cameraOrientation.x,
w: cameraOrientation.w
};
cameraOrientation = Quat.multiply(cameraOrientation, Quat.inverse(MyAvatar.orientation));
handRotation = Quat.multiply(handRotation, cameraOrientation); // Works!!!
} else {
@ -411,18 +454,19 @@ var leapHands = (function () {
// Hand rotation in camera coordinates ...
handRotation = {
x: -handRotation.x,
y: -handRotation.z,
z: -handRotation.y,
x: handRotation.z,
y: handRotation.y,
z: handRotation.x,
w: handRotation.w
};
// Hand rotation in avatar coordinates ...
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
handRotation = Quat.multiply(DESKTOP_CAMERA_TO_AVATAR_ROTATION, handRotation);
}
// Set hand position and orientation ...
MyAvatar.setJointModelPositionAndOrientation(hands[h].jointName, handOffset, handRotation, true);
// Set hand position and orientation for animation state handler ...
hands[h].position = handOffset;
hands[h].rotation = handRotation;
// Set finger joints ...
for (i = 0; i < NUM_FINGERS; i += 1) {
@ -436,6 +480,10 @@ var leapHands = (function () {
z: side * -locRotation.x,
w: locRotation.w
};
if (j === 0) {
// Adjust avatar thumb root joint rotation to make avatar hands look better
locRotation = Quat.multiply(LEAP_THUMB_ROOT_ADJUST[h], locRotation);
}
} else {
locRotation = {
x: -locRotation.x,
@ -458,14 +506,9 @@ var leapHands = (function () {
hands[h].inactiveCount += 1;
if (hands[h].inactiveCount === MAX_HAND_INACTIVE_COUNT) {
if (h === 0) {
MyAvatar.clearJointData("LeftHand");
MyAvatar.clearJointData("LeftForeArm");
MyAvatar.clearJointData("LeftArm");
} else {
MyAvatar.clearJointData("RightHand");
MyAvatar.clearJointData("RightForeArm");
MyAvatar.clearJointData("RightArm");
if (handAnimationStateHandlers[h] !== null) {
MyAvatar.removeAnimationStateHandler(handAnimationStateHandlers[h]);
handAnimationStateHandlers[h] = null;
}
}
}
@ -483,6 +526,9 @@ var leapHands = (function () {
for (h = 0; h < NUM_HANDS; h += 1) {
Controller.releaseInputController(hands[h].controller);
Controller.releaseInputController(wrists[h].controller);
if (handAnimationStateHandlers[h] !== null) {
MyAvatar.removeAnimationStateHandler(handAnimationStateHandlers[h]);
}
for (i = 0; i < NUM_FINGERS; i += 1) {
for (j = 0; j < NUM_FINGER_JOINTS; j += 1) {
if (fingers[h][i][j].controller !== null) {

View file

@ -0,0 +1,226 @@
// earthquakes_live.js
//
// exploratory implementation in prep for abstract latlong to earth graphing tool for VR
// shows all of the quakes in the past 24 hours reported by the USGS
//
// created by james b. pollack @imgntn on 12/5/2015
// Copyright 2015 High Fidelity, Inc.
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// working notes: maybe try doing markers as boxes,rotated to the sphere normal, and with the height representing some value
Script.include('../libraries/promise.js');
var Promise = loadPromise();
Script.include('../libraries/tinyColor.js');
var tinyColor = loadTinyColor();
//you could make it the size of the actual earth.
var EARTH_SPHERE_RADIUS = 6371;
var EARTH_SPHERE_RADIUS = 2;
var EARTH_CENTER_POSITION = Vec3.sum(Vec3.sum(MyAvatar.position, {
x: 0,
y: 0.5,
z: 0
}), Vec3.multiply(EARTH_SPHERE_RADIUS, Quat.getFront(Camera.getOrientation())));
var EARTH_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/earthquakes_live/models/earth-noclouds.fbx';
var SHOULD_SPIN=false;
var POLL_FOR_CHANGES = true;
//USGS updates the data every five minutes
var CHECK_QUAKE_FREQUENCY = 5 * 60 * 1000;
var QUAKE_MARKER_DIMENSIONS = {
x: 0.01,
y: 0.01,
z: 0.01
};
function createEarth() {
var earthProperties = {
name: 'Earth',
type: 'Model',
modelURL: EARTH_MODEL_URL,
position: EARTH_CENTER_POSITION,
dimensions: {
x: EARTH_SPHERE_RADIUS,
y: EARTH_SPHERE_RADIUS,
z: EARTH_SPHERE_RADIUS
},
rotation: Quat.fromPitchYawRollDegrees(0, 90, 0),
// collisionsWillMove: true,
//if you have a shapetype it blocks the smaller markers
// shapeType:'sphere'
// userData: JSON.stringify({
// grabbableKey: {
// grabbable: false
// }
// })
}
return Entities.addEntity(earthProperties)
}
function latLongToVector3(lat, lon, radius, height) {
var phi = (lat) * Math.PI / 180;
var theta = (lon - 180) * Math.PI / 180;
var x = -(radius + height) * Math.cos(phi) * Math.cos(theta);
var y = (radius + height) * Math.sin(phi);
var z = (radius + height) * Math.cos(phi) * Math.sin(theta);
return {
x: x,
y: y,
z: z
};
}
function getQuakePosition(earthquake) {
var longitude = earthquake.geometry.coordinates[0];
var latitude = earthquake.geometry.coordinates[1];
var depth = earthquake.geometry.coordinates[2];
var latlng = latLongToVector3(latitude, longitude, EARTH_SPHERE_RADIUS / 2, 0);
var position = EARTH_CENTER_POSITION;
var finalPosition = Vec3.sum(position, latlng);
//print('finalpos::' + JSON.stringify(finalPosition))
return finalPosition
}
var QUAKE_URL = 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson'
function get(url) {
print('getting' + url)
// Return a new promise.
return new Promise(function(resolve, reject) {
// Do the usual XHR stuff
var req = new XMLHttpRequest();
req.open('GET', url);
req.onreadystatechange = function() {
print('req status:: ' + JSON.stringify(req.status))
if (req.readyState == 4 && req.status == 200) {
var myArr = JSON.parse(req.responseText);
resolve(myArr);
}
};
req.send();
});
}
function createQuakeMarker(earthquake) {
var markerProperties = {
name: earthquake.properties.place,
type: 'Sphere',
parentID:earth,
dimensions: QUAKE_MARKER_DIMENSIONS,
position: getQuakePosition(earthquake),
ignoreForCollisions:true,
lifetime: 6000,
color: getQuakeMarkerColor(earthquake)
}
// print('marker properties::' + JSON.stringify(markerProperties))
return Entities.addEntity(markerProperties);
}
function getQuakeMarkerColor(earthquake) {
var color = {};
var magnitude = earthquake.properties.mag;
//realistic but will never get full red coloring and will probably be pretty dull for most. must experiment
var sValue = scale(magnitude, 0, 10, 0, 100);
var HSL_string = "hsl(0, " + sValue + "%, 50%)"
var color = tinyColor(HSL_string);
var finalColor = {
red: color._r,
green: color._g,
blue: color._b
}
return finalColor
}
function scale(value, min1, max1, min2, max2) {
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
}
function processQuakes(earthquakes) {
print('quakers length' + earthquakes.length)
earthquakes.forEach(function(quake) {
// print('PROCESSING A QUAKE')
var marker = createQuakeMarker(quake);
markers.push(marker);
})
print('markers length:' + markers.length)
}
var quakes;
var markers = [];
var earth = createEarth();
function getThenProcessQuakes() {
get(QUAKE_URL).then(function(response) {
print('got it::' + response.features.length)
quakes = response.features;
processQuakes(quakes);
//print("Success!" + JSON.stringify(response));
}, function(error) {
print('error getting quakes')
});
}
function cleanupMarkers() {
print('CLEANING UP MARKERS')
while (markers.length > 0) {
Entities.deleteEntity(markers.pop());
}
}
function cleanupEarth() {
Entities.deleteEntity(earth);
Script.update.disconnect(spinEarth);
}
function cleanupInterval() {
if (pollingInterval !== null) {
Script.clearInterval(pollingInterval)
}
}
Script.scriptEnding.connect(cleanupMarkers);
Script.scriptEnding.connect(cleanupEarth);
Script.scriptEnding.connect(cleanupInterval);
getThenProcessQuakes();
var pollingInterval = null;
if (POLL_FOR_CHANGES === true) {
pollingInterval = Script.setInterval(function() {
cleanupMarkers();
getThenProcessQuakes()
}, CHECK_QUAKE_FREQUENCY)
}
function spinEarth(){
Entities.editEntity(earth,{
angularVelocity:{
x:0,
y:0.25,
z:0
}
})
}
if(SHOULD_SPIN===true){
Script.update.connect(spinEarth);
}

View file

@ -392,6 +392,11 @@ var toolBar = (function() {
url,
file;
if (!event.isLeftButton) {
// if another mouse button than left is pressed ignore it
return false;
}
clickedOverlay = Overlays.getOverlayAtPoint({
x: event.x,
y: event.y

View file

@ -226,6 +226,7 @@
var elRescaleDimensionsButton = document.getElementById("dimension-rescale-button");
var elParentID = document.getElementById("property-parent-id");
var elParentJointIndex = document.getElementById("property-parent-joint-index");
var elRegistrationX = document.getElementById("property-reg-x");
var elRegistrationY = document.getElementById("property-reg-y");
@ -456,6 +457,7 @@
elDimensionsZ.value = properties.dimensions.z.toFixed(2);
elParentID.value = properties.parentID;
elParentJointIndex.value = properties.parentJointIndex;
elRegistrationX.value = properties.registrationPoint.x.toFixed(2);
elRegistrationY.value = properties.registrationPoint.y.toFixed(2);
@ -671,6 +673,7 @@
elDimensionsZ.addEventListener('change', dimensionsChangeFunction);
elParentID.addEventListener('change', createEmitTextPropertyUpdateFunction('parentID'));
elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex'));
var registrationChangeFunction = createEmitVec3PropertyUpdateFunction(
'registrationPoint', elRegistrationX, elRegistrationY, elRegistrationZ);
@ -1067,6 +1070,12 @@
<input type="text" id="property-parent-id">
</div>
</div>
<div class="property">
<span class="label" style="float: left; margin-right: 6px">ParentJointIndex</span>
<div class="value" style="overflow: hidden;">
<input type="text" id="property-parent-joint-index">
</div>
</div>
<div class="property">
<div class="label">Registration</div>

View file

@ -2340,6 +2340,11 @@ SelectionDisplay = (function () {
that.mousePressEvent = function(event) {
if (!event.isLeftButton) {
// if another mouse button than left is pressed ignore it
return false;
}
var somethingClicked = false;
var pickRay = Camera.computePickRay(event.x, event.y);

File diff suppressed because it is too large Load diff

View file

@ -67,12 +67,17 @@
}
var BOW_SPATIAL_KEY = {
relativePosition: {
x: 0,
leftRelativePosition: {
x: 0.05,
y: 0.06,
z: 0.11
z: -0.05
},
relativeRotation: Quat.fromPitchYawRollDegrees(0, -90, 90)
rightRelativePosition: {
x: -0.05,
y: 0.06,
z: -0.05
},
relativeRotation: Quat.fromPitchYawRollDegrees(0, 90, -90)
}

View file

@ -48,12 +48,17 @@ var bow = Entities.addEntity({
grabbableKey: {
invertSolidWhileHeld: true,
spatialKey: {
relativePosition: {
x: 0,
leftRelativePosition: {
x: 0.05,
y: 0.06,
z: 0.11
z: -0.05
},
relativeRotation: Quat.fromPitchYawRollDegrees(0, -90, 90)
rightRelativePosition: {
x: -0.05,
y: 0.06,
z: -0.05
},
relativeRotation: Quat.fromPitchYawRollDegrees(0, 90, -90)
}
}
})

View file

@ -12,31 +12,57 @@
Script.include("cookies.js");
var audioOptions = new AudioEffectOptions({
maxRoomSize: 50,
bandwidth: 10000,
preDelay: 20,
lateDelay: 0,
reverbTime: 2,
earlyDiffusion: 100,
lateDiffusion: 100,
roomSize: 50,
reverbTime: 4,
damping: 0.50,
inputBandwidth: 0.8,
earlyLevel: 0,
tailLevel: 0,
dryLevel: -6,
wetLevel: -6
density: 100,
bassMult: 1.5,
bassFreq: 250,
highGain: -6,
highFreq: 3000,
modRate: 2.3,
modDepth: 50,
earlyGain: 0,
lateGain: 0,
earlyMixLeft: 20,
earlyMixRight: 20,
lateMixLeft: 90,
lateMixRight: 90,
wetDryMix: 50,
});
AudioDevice.setReverbOptions(audioOptions);
AudioDevice.setReverb(true);
print("Reverb is ON.");
var panel = new Panel(10, 200);
var panel = new Panel(10, 160);
var parameters = [
{ name: "roomSize", min: 0, max: 100, units: " feet" },
{ name: "reverbTime", min: 0, max: 10, units: " sec" },
{ name: "damping", min: 0, max: 1, units: " " },
{ name: "inputBandwidth", min: 0, max: 1, units: " " },
{ name: "earlyLevel", min: -48, max: 0, units: " dB" },
{ name: "tailLevel", min: -48, max: 0, units: " dB" },
{ name: "wetLevel", min: -48, max: 0, units: " dB" },
{ name: "bandwidth", min: 1000, max: 12000, units: " Hz" },
{ name: "preDelay", min: 0, max: 333, units: " ms" },
{ name: "lateDelay", min: 0, max: 166, units: " ms" },
{ name: "reverbTime", min: 0.1, max: 10, units: " seconds" },
{ name: "earlyDiffusion", min: 0, max: 100, units: " percent" },
{ name: "lateDiffusion", min: 0, max: 100, units: " percent" },
{ name: "roomSize", min: 0, max: 100, units: " percent" },
{ name: "density", min: 0, max: 100, units: " percent" },
{ name: "bassMult", min: 0.1, max: 4, units: " ratio" },
{ name: "bassFreq", min: 10, max: 500, units: " Hz" },
{ name: "highGain", min: -24, max: 0, units: " dB" },
{ name: "highFreq", min: 1000, max: 12000, units: " Hz" },
{ name: "modRate", min: 0.1, max: 10, units: " Hz" },
{ name: "modDepth", min: 0, max: 100, units: " percent" },
{ name: "earlyGain", min: -96, max: 24, units: " dB" },
{ name: "lateGain", min: -96, max: 24, units: " dB" },
{ name: "earlyMixLeft", min: 0, max: 100, units: " percent" },
{ name: "earlyMixRight", min: 0, max: 100, units: " percent" },
{ name: "lateMixLeft", min: 0, max: 100, units: " percent" },
{ name: "lateMixRight", min: 0, max: 100, units: " percent" },
{ name: "wetDryMix", min: 0, max: 100, units: " percent" },
]
function setter(name) {
@ -48,7 +74,7 @@ function getter(name) {
}
function displayer(units) {
return function(value) { return (value).toFixed(1) + units; };
return function(value) { return (value).toFixed(1) + units; }
}
// create a slider for each parameter

View file

@ -194,7 +194,6 @@ else (APPLE)
# link target to external libraries
if (WIN32)
# target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib)
target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib)
else (WIN32)
# Nothing else required on linux apparently
@ -202,3 +201,4 @@ else (APPLE)
endif (APPLE)
package_libraries_for_deployment()
consolidate_stack_components()

View file

@ -45,7 +45,7 @@
#include <QtNetwork/QNetworkDiskCache>
#include <gl/Config.h>
#include <QtGui/QOpenGLContext>
#include <QOpenGLContextWrapper.h>
#include <AccountManager.h>
#include <AddressManager.h>
@ -1077,6 +1077,11 @@ void Application::initializeUi() {
}
void Application::paintGL() {
// paintGL uses a queued connection, so we can get messages from the queue even after we've quit
// and the plugins have shutdown
if (_aboutToQuit) {
return;
}
_frameCount++;
// update fps moving average
@ -1117,29 +1122,6 @@ void Application::paintGL() {
_inPaint = true;
Finally clearFlagLambda([this] { _inPaint = false; });
// Some LOD-like controls need to know a smoothly varying "potential" frame rate that doesn't
// include time waiting for vsync, and which can report a number above target if we've got the headroom.
// For example, if we're shooting for 75fps and paintWait is 3.3333ms (= 75% * 13.33ms), our deducedNonVSyncFps
// would be 100fps. In principle, a paintWait of zero would have deducedNonVSyncFps=75.
// Here we make a guess for deducedNonVSyncFps = 1 / deducedNonVSyncPeriod.
//
// Time between previous paintGL call and this one, which can vary not only with vSync misses, but also with QT timing.
// We're using this as a proxy for the time between vsync and displayEnd, below. (Not exact, but tends to be the same over time.)
// This is not the same as update(deltaTime), because the latter attempts to throttle to 60hz and also clamps to 1/4 second.
const float actualPeriod = diff / (float)USECS_PER_SECOND; // same as 1/instantaneousFps but easier for compiler to optimize
// Note that _lastPaintWait (stored at end of last call) is for the same paint cycle.
float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait + _marginForDeducedFramePeriod; // plus a some non-zero time for machinery we can't measure
// We don't know how much time to allow for that, but if we went over the target period, we know it's at least the portion
// of paintWait up to the next vSync. This gives us enough of a penalty so that when actualPeriod crosses two cycles,
// the key part (and not an exagerated part) of _lastPaintWait is accounted for.
const float targetPeriod = getTargetFramePeriod();
if (_lastPaintWait > EPSILON && actualPeriod > targetPeriod) {
// Don't use C++ remainder(). It's authors are mathematically insane.
deducedNonVSyncPeriod += fmod(actualPeriod, _lastPaintWait);
}
_lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod;
_lastInstantaneousFps = instantaneousFps;
auto displayPlugin = getActiveDisplayPlugin();
// FIXME not needed anymore?
_offscreenContext->makeCurrent();
@ -1396,18 +1378,14 @@ void Application::paintGL() {
Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));
_lockedFramebufferMap[finalTexture] = scratchFramebuffer;
uint64_t displayStart = usecTimestampNow();
Q_ASSERT(QOpenGLContext::currentContext() == _offscreenContext->getContext());
Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
{
PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene");
PerformanceTimer perfTimer("pluginSubmitScene");
displayPlugin->submitSceneTexture(_frameCount, finalTexture, toGlm(size));
}
Q_ASSERT(QOpenGLContext::currentContext() == _offscreenContext->getContext());
Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
uint64_t displayEnd = usecTimestampNow();
const float displayPeriodUsec = (float)(displayEnd - displayStart); // usecs
_lastPaintWait = displayPeriodUsec / (float)USECS_PER_SECOND;
}
{
@ -1418,6 +1396,14 @@ void Application::paintGL() {
batch.resetStages();
});
}
// Some LOD-like controls need to know a smoothly varying "potential" frame rate that doesn't
// include time waiting for sync, and which can report a number above target if we've got the headroom.
// In my tests, the following is mostly less than 0.5ms, and never more than 3ms. I don't think its worth measuring during runtime.
static const float paintWaitAndQTTimerAllowance = 0.001; // seconds
// Store both values now for use by next cycle.
_lastInstantaneousFps = instantaneousFps;
_lastUnsynchronizedFps = 1.0f / (((usecTimestampNow() - now) / (float)USECS_PER_SECOND) + paintWaitAndQTTimerAllowance);
}
void Application::runTests() {
@ -2414,6 +2400,9 @@ bool Application::exportEntities(const QString& filename, const QVector<EntityIt
exportTree->addEntity(entityItem->getEntityItemID(), properties);
}
// remap IDs on export so that we aren't publishing the IDs of entities in our domain
exportTree->remapIDs();
exportTree->writeToJSONFile(filename.toLocal8Bit().constData());
// restore the main window's active state
@ -2436,6 +2425,10 @@ bool Application::exportEntities(const QString& filename, float x, float y, floa
properties.setPosition(properties.getPosition() - root);
exportTree->addEntity(id, properties);
}
// remap IDs on export so that we aren't publishing the IDs of entities in our domain
exportTree->remapIDs();
exportTree->writeToSVOFile(filename.toLocal8Bit().constData());
} else {
qCDebug(interfaceapp) << "No models were selected";
@ -2480,6 +2473,7 @@ bool Application::importEntities(const QString& urlOrFilename) {
bool success = _entityClipboard->readFromURL(url.toString());
if (success) {
_entityClipboard->remapIDs();
_entityClipboard->reaverageOctreeElements();
}
return success;
@ -3073,11 +3067,6 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
//qCDebug(interfaceapp) << ">>> inside... queryOctree()... _viewFrustum.getFieldOfView()=" << _viewFrustum.getFieldOfView();
bool wantExtraDebugging = getLogger()->extraDebugging();
// These will be the same for all servers, so we can set them up once and then reuse for each server we send to.
_octreeQuery.setWantLowResMoving(true);
_octreeQuery.setWantDelta(true);
_octreeQuery.setWantCompression(true);
_octreeQuery.setCameraPosition(_viewFrustum.getPosition());
_octreeQuery.setCameraOrientation(_viewFrustum.getOrientation());
_octreeQuery.setCameraFov(_viewFrustum.getFieldOfView());
@ -3783,12 +3772,11 @@ void Application::domainChanged(const QString& domainHostname) {
_domainConnectionRefusals.clear();
}
void Application::handleDomainConnectionDeniedPacket(QSharedPointer<NLPacket> packet) {
void Application::handleDomainConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message) {
// Read deny reason from packet
quint16 reasonSize;
packet->readPrimitive(&reasonSize);
QString reason = QString::fromUtf8(packet->getPayload() + packet->pos(), reasonSize);
packet->seek(packet->pos() + reasonSize);
message->readPrimitive(&reasonSize);
QString reason = QString::fromUtf8(message->readWithoutCopy(reasonSize));
// output to the log so the user knows they got a denied connection request
// and check and signal for an access token so that we can make sure they are logged in
@ -3874,9 +3862,7 @@ void Application::nodeKilled(SharedNodePointer node) {
Menu::getInstance()->getActionForOption(MenuOption::UploadAsset)->setEnabled(false);
}
}
void Application::trackIncomingOctreePacket(NLPacket& packet, SharedNodePointer sendingNode, bool wasStatsPacket) {
void Application::trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket) {
// Attempt to identify the sender from its address.
if (sendingNode) {
const QUuid& nodeUUID = sendingNode->getUUID();
@ -3885,13 +3871,13 @@ void Application::trackIncomingOctreePacket(NLPacket& packet, SharedNodePointer
_octreeServerSceneStats.withWriteLock([&] {
if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) {
OctreeSceneStats& stats = _octreeServerSceneStats[nodeUUID];
stats.trackIncomingOctreePacket(packet, wasStatsPacket, sendingNode->getClockSkewUsec());
stats.trackIncomingOctreePacket(message, wasStatsPacket, sendingNode->getClockSkewUsec());
}
});
}
}
int Application::processOctreeStats(NLPacket& packet, SharedNodePointer sendingNode) {
int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode) {
// But, also identify the sender, and keep track of the contained jurisdiction root for this server
// parse the incoming stats datas stick it in a temporary object for now, while we
@ -3903,7 +3889,7 @@ int Application::processOctreeStats(NLPacket& packet, SharedNodePointer sendingN
// now that we know the node ID, let's add these stats to the stats for that node...
_octreeServerSceneStats.withWriteLock([&] {
OctreeSceneStats& octreeStats = _octreeServerSceneStats[nodeUUID];
statsMessageLength = octreeStats.unpackFromPacket(packet);
statsMessageLength = octreeStats.unpackFromPacket(message);
// see if this is the first we've heard of this node...
NodeToJurisdictionMap* jurisdiction = NULL;
@ -4082,7 +4068,7 @@ bool Application::canAcceptURL(const QString& urlString) {
QString lowerPath = url.path().toLower();
while (i.hasNext()) {
i.next();
if (lowerPath.endsWith(i.key())) {
if (lowerPath.endsWith(i.key(), Qt::CaseInsensitive)) {
return true;
}
}
@ -4102,7 +4088,7 @@ bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
QString lowerPath = url.path().toLower();
while (i.hasNext()) {
i.next();
if (lowerPath.endsWith(i.key())) {
if (lowerPath.endsWith(i.key(), Qt::CaseInsensitive)) {
AcceptURLMethod method = i.value();
return (this->*method)(urlString);
}
@ -4202,7 +4188,7 @@ bool Application::askToUploadAsset(const QString& filename) {
messageBox.setDefaultButton(QMessageBox::Ok);
// Option to drop model in world for models
if (filename.endsWith(FBX_EXTENSION) || filename.endsWith(OBJ_EXTENSION)) {
if (filename.endsWith(FBX_EXTENSION, Qt::CaseInsensitive) || filename.endsWith(OBJ_EXTENSION, Qt::CaseInsensitive)) {
auto checkBox = new QCheckBox(&messageBox);
checkBox->setText("Add to scene");
messageBox.setCheckBox(checkBox);
@ -4237,7 +4223,8 @@ void Application::modelUploadFinished(AssetUpload* upload, const QString& hash)
auto filename = QFileInfo(upload->getFilename()).fileName();
if ((upload->getError() == AssetUpload::NoError) &&
(filename.endsWith(FBX_EXTENSION) || filename.endsWith(OBJ_EXTENSION))) {
(upload->getExtension().endsWith(FBX_EXTENSION, Qt::CaseInsensitive) ||
upload->getExtension().endsWith(OBJ_EXTENSION, Qt::CaseInsensitive))) {
auto entities = DependencyManager::get<EntityScriptingInterface>();

View file

@ -162,11 +162,8 @@ public:
uint32_t getFrameCount() { return _frameCount; }
float getFps() const { return _fps; }
float getTargetFrameRate(); // frames/second
float getTargetFramePeriod(); // seconds
float getLastInstanteousFps() const { return _lastInstantaneousFps; }
float getLastPaintWait() const { return _lastPaintWait; };
float getLastDeducedNonVSyncFps() const { return _lastDeducedNonVSyncFps; }
void setMarginForDeducedFramePeriod(float newValue) { _marginForDeducedFramePeriod = newValue; }
float getLastUnsynchronizedFps() const { return _lastUnsynchronizedFps; }
float getFieldOfView() { return _fieldOfView.get(); }
void setFieldOfView(float fov);
@ -328,7 +325,7 @@ private slots:
void activeChanged(Qt::ApplicationState state);
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
void handleDomainConnectionDeniedPacket(QSharedPointer<NLPacket> packet);
void handleDomainConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message);
void notifyPacketVersionMismatch();
@ -394,8 +391,8 @@ private:
bool importSVOFromURL(const QString& urlString);
int processOctreeStats(NLPacket& packet, SharedNodePointer sendingNode);
void trackIncomingOctreePacket(NLPacket& packet, SharedNodePointer sendingNode, bool wasStatsPacket);
int processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode);
void trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket);
void resizeEvent(QResizeEvent* size);
@ -441,9 +438,7 @@ private:
QElapsedTimer _timerStart;
QElapsedTimer _lastTimeUpdated;
float _lastInstantaneousFps { 0.0f };
float _lastPaintWait { 0.0f };
float _lastDeducedNonVSyncFps { 0.0f };
float _marginForDeducedFramePeriod{ 0.002f }; // 2ms, adjustable
float _lastUnsynchronizedFps { 0.0f };
ShapeManager _shapeManager;
PhysicalEntitySimulation _entitySimulation;

View file

@ -21,17 +21,17 @@
EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) {
switch (type) {
case ACTION_TYPE_NONE:
return nullptr;
return EntityActionPointer();
case ACTION_TYPE_OFFSET:
return (EntityActionPointer) new ObjectActionOffset(id, ownerEntity);
return std::make_shared<ObjectActionOffset>(id, ownerEntity);
case ACTION_TYPE_SPRING:
return (EntityActionPointer) new ObjectActionSpring(id, ownerEntity);
return std::make_shared<ObjectActionSpring>(id, ownerEntity);
case ACTION_TYPE_HOLD:
return (EntityActionPointer) new AvatarActionHold(id, ownerEntity);
return std::make_shared<AvatarActionHold>(id, ownerEntity);
}
assert(false);
return nullptr;
Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity action type");
return EntityActionPointer();
}

View file

@ -201,6 +201,7 @@ void Avatar::simulate(float deltaTime) {
_skeletonModel.getRig()->copyJointsFromJointData(_jointData);
_skeletonModel.simulate(deltaTime, _hasNewJointRotations || _hasNewJointTranslations);
simulateAttachments(deltaTime);
locationChanged(); // joints changed, so if there are any children, update them.
_hasNewJointRotations = false;
_hasNewJointTranslations = false;
}
@ -857,23 +858,28 @@ QVector<glm::quat> Avatar::getJointRotations() const {
}
glm::quat Avatar::getJointRotation(int index) const {
if (QThread::currentThread() != thread()) {
return AvatarData::getJointRotation(index);
}
glm::quat rotation;
_skeletonModel.getJointRotation(index, rotation);
return rotation;
}
glm::vec3 Avatar::getJointTranslation(int index) const {
if (QThread::currentThread() != thread()) {
return AvatarData::getJointTranslation(index);
}
glm::vec3 translation;
_skeletonModel.getJointTranslation(index, translation);
return translation;
}
glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
glm::quat rotation;
_skeletonModel.getAbsoluteJointRotationInRigFrame(index, rotation);
return Quaternions::Y_180 * rotation;
}
glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
glm::vec3 translation;
_skeletonModel.getAbsoluteJointTranslationInRigFrame(index, translation);
return Quaternions::Y_180 * translation;
}
int Avatar::getJointIndex(const QString& name) const {
if (QThread::currentThread() != thread()) {
@ -1151,12 +1157,12 @@ glm::quat Avatar::getRightPalmRotation() {
return rightRotation;
}
void Avatar::setPosition(const glm::vec3 position) {
void Avatar::setPosition(const glm::vec3& position) {
AvatarData::setPosition(position);
updateAttitude();
}
void Avatar::setOrientation(const glm::quat orientation) {
void Avatar::setOrientation(const glm::quat& orientation) {
AvatarData::setOrientation(orientation);
updateAttitude();
}

View file

@ -108,6 +108,9 @@ public:
virtual int getJointIndex(const QString& name) const;
virtual QStringList getJointNames() const;
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
virtual void setFaceModelURL(const QUrl& faceModelURL);
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
@ -155,8 +158,8 @@ public:
void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; }
AvatarMotionState* getMotionState() { return _motionState; }
virtual void setPosition(glm::vec3 position);
virtual void setOrientation(glm::quat orientation);
virtual void setPosition(const glm::vec3& position) override;
virtual void setOrientation(const glm::quat& orientation) override;
public slots:

View file

@ -9,63 +9,72 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "QVariantGLM.h"
#include "avatar/AvatarManager.h"
#include "AvatarActionHold.h"
#include <QVariantGLM.h>
#include "avatar/AvatarManager.h"
const uint16_t AvatarActionHold::holdVersion = 1;
AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity) :
ObjectActionSpring(id, ownerEntity),
_relativePosition(glm::vec3(0.0f)),
_relativeRotation(glm::quat()),
_hand("right"),
_holderID(QUuid()) {
ObjectActionSpring(id, ownerEntity)
{
_type = ACTION_TYPE_HOLD;
#if WANT_DEBUG
#if WANT_DEBUG
qDebug() << "AvatarActionHold::AvatarActionHold";
#endif
#endif
}
AvatarActionHold::~AvatarActionHold() {
#if WANT_DEBUG
#if WANT_DEBUG
qDebug() << "AvatarActionHold::~AvatarActionHold";
#endif
#endif
}
std::shared_ptr<Avatar> AvatarActionHold::getTarget(glm::quat& rotation, glm::vec3& position) {
std::shared_ptr<Avatar> holdingAvatar = nullptr;
auto avatarManager = DependencyManager::get<AvatarManager>();
auto holdingAvatar = std::static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(_holderID));
if (!holdingAvatar) {
return holdingAvatar;
}
withTryReadLock([&]{
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
AvatarSharedPointer holdingAvatarData = avatarManager->getAvatarBySessionID(_holderID);
holdingAvatar = std::static_pointer_cast<Avatar>(holdingAvatarData);
if (holdingAvatar) {
glm::vec3 offset;
glm::vec3 palmPosition;
glm::quat palmRotation;
if (_hand == "right") {
bool isRightHand = (_hand == "right");
glm::vec3 palmPosition { Vectors::ZERO };
glm::quat palmRotation { Quaternions::IDENTITY };
if (_ignoreIK && holdingAvatar->isMyAvatar()) {
// We cannot ignore other avatars IK and this is not the point of this option
// This is meant to make the grabbing behavior more reactive.
if (isRightHand) {
palmPosition = holdingAvatar->getHand()->getCopyOfPalmData(HandData::RightHand).getPosition();
palmRotation = holdingAvatar->getHand()->getCopyOfPalmData(HandData::RightHand).getRotation();
} else {
palmPosition = holdingAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand).getPosition();
palmRotation = holdingAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand).getRotation();
}
} else {
if (isRightHand) {
palmPosition = holdingAvatar->getRightPalmPosition();
palmRotation = holdingAvatar->getRightPalmRotation();
} else {
palmPosition = holdingAvatar->getLeftPalmPosition();
palmRotation = holdingAvatar->getLeftPalmRotation();
}
rotation = palmRotation * _relativeRotation;
offset = rotation * _relativePosition;
position = palmPosition + offset;
}
rotation = palmRotation * _relativeRotation;
position = palmPosition + rotation * _relativePosition;
});
return holdingAvatar;
}
void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
glm::quat rotation;
glm::vec3 position;
glm::quat rotation { Quaternions::IDENTITY };
glm::vec3 position { Vectors::ZERO };
bool valid = false;
int holdCount = 0;
@ -168,6 +177,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
QUuid holderID;
bool kinematic;
bool kinematicSetVelocity;
bool ignoreIK;
bool needUpdate = false;
bool somethingChanged = ObjectAction::updateArguments(arguments);
@ -203,14 +213,20 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
ok = true;
kinematic = EntityActionInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false);
if (!ok) {
_kinematic = false;
kinematic = _kinematic;
}
ok = true;
kinematicSetVelocity = EntityActionInterface::extractBooleanArgument("hold", arguments,
"kinematicSetVelocity", ok, false);
if (!ok) {
_kinematicSetVelocity = false;
kinematicSetVelocity = _kinematicSetVelocity;
}
ok = true;
ignoreIK = EntityActionInterface::extractBooleanArgument("hold", arguments, "ignoreIK", ok, false);
if (!ok) {
ignoreIK = _ignoreIK;
}
if (somethingChanged ||
@ -220,7 +236,8 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
hand != _hand ||
holderID != _holderID ||
kinematic != _kinematic ||
kinematicSetVelocity != _kinematicSetVelocity) {
kinematicSetVelocity != _kinematicSetVelocity ||
ignoreIK != _ignoreIK) {
needUpdate = true;
}
});
@ -236,6 +253,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
_holderID = holderID;
_kinematic = kinematic;
_kinematicSetVelocity = kinematicSetVelocity;
_ignoreIK = ignoreIK;
_active = true;
auto ownerEntity = _ownerEntity.lock();
@ -260,6 +278,7 @@ QVariantMap AvatarActionHold::getArguments() {
arguments["hand"] = _hand;
arguments["kinematic"] = _kinematic;
arguments["kinematicSetVelocity"] = _kinematicSetVelocity;
arguments["ignoreIK"] = _ignoreIK;
});
return arguments;
}

View file

@ -38,17 +38,19 @@ public:
std::shared_ptr<Avatar> getTarget(glm::quat& rotation, glm::vec3& position);
private:
void doKinematicUpdate(float deltaTimeStep);
static const uint16_t holdVersion;
glm::vec3 _relativePosition;
glm::quat _relativeRotation;
QString _hand;
glm::vec3 _relativePosition { Vectors::ZERO };
glm::quat _relativeRotation { Quaternions::IDENTITY };
QString _hand { "right" };
QUuid _holderID;
void doKinematicUpdate(float deltaTimeStep);
bool _kinematic { false };
bool _kinematicSetVelocity { false };
bool _previousSet { false };
bool _ignoreIK { false };
glm::vec3 _previousPositionalTarget;
glm::quat _previousRotationalTarget;

View file

@ -111,7 +111,6 @@ void AvatarManager::init() {
_renderDistanceController.setKP(0.0008f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0.
_renderDistanceController.setKI(0.0006f); // Big enough to bring us to target with the above KP.
_renderDistanceController.setKD(0.000001f); // A touch of kd increases the speed by which we get there.
}
void AvatarManager::updateMyAvatar(float deltaTime) {
@ -153,7 +152,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
// The measured value is frame rate. When the controlled value (1 / render cutoff distance)
// goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate
// goes up.
const float deduced = qApp->getLastDeducedNonVSyncFps();
const float deduced = qApp->getLastUnsynchronizedFps();
distance = 1.0f / _renderDistanceController.update(deduced, deltaTime);
} else {
// We could keep the controller running when not vsync'd, if getLastDeducedNonVSyncFps is still meaningful.
@ -417,7 +416,7 @@ void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) {
AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) {
if (sessionID == _myAvatar->getSessionUUID()) {
return std::static_pointer_cast<Avatar>(_myAvatar);
return _myAvatar;
}
return findAvatar(sessionID);

View file

@ -560,7 +560,7 @@ void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
if (!_shouldRender) {
return; // exit early
}
Avatar::render(renderArgs, cameraPosition);
}
@ -967,8 +967,6 @@ void MyAvatar::clearJointData(int index) {
QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index));
return;
}
// HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority
_rig->setJointState(index, false, glm::quat(), glm::vec3(), 0.0f);
_rig->clearJointAnimationPriority(index);
}
@ -1188,7 +1186,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl
if (!_skeletonModel.isRenderable()) {
return; // wait until all models are loaded
}
fixupModelsInScene();
// Render head so long as the camera isn't inside it

View file

@ -254,6 +254,7 @@ public slots:
void setEnableDebugDrawDefaultPose(bool isEnabled);
void setEnableDebugDrawAnimPose(bool isEnabled);
void setEnableDebugDrawPosition(bool isEnabled);
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
void setEnableMeshVisible(bool isEnabled);
Q_INVOKABLE void setAnimGraphUrl(const QUrl& url);
@ -276,7 +277,7 @@ private:
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f) override;
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); }
bool getShouldRenderLocally() const { return _shouldRender; }
bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; };
bool isMyAvatar() const override { return true; }

View file

@ -23,11 +23,11 @@ OctreePacketProcessor::OctreePacketProcessor() {
this, "handleOctreePacket");
}
void OctreePacketProcessor::handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
queueReceivedPacket(packet, senderNode);
void OctreePacketProcessor::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
queueReceivedPacket(message, senderNode);
}
void OctreePacketProcessor::processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"OctreePacketProcessor::processPacket()");
@ -39,41 +39,42 @@ void OctreePacketProcessor::processPacket(QSharedPointer<NLPacket> packet, Share
bool wasStatsPacket = false;
PacketType octreePacketType = packet->getType();
PacketType octreePacketType = message->getType();
// note: PacketType_OCTREE_STATS can have PacketType_VOXEL_DATA
// immediately following them inside the same packet. So, we process the PacketType_OCTREE_STATS first
// then process any remaining bytes as if it was another packet
if (octreePacketType == PacketType::OctreeStats) {
int statsMessageLength = qApp->processOctreeStats(*packet, sendingNode);
int statsMessageLength = qApp->processOctreeStats(*message, sendingNode);
wasStatsPacket = true;
int piggybackBytes = packet->getPayloadSize() - statsMessageLength;
int piggybackBytes = message->getSize() - statsMessageLength;
if (piggybackBytes) {
// construct a new packet from the piggybacked one
auto buffer = std::unique_ptr<char[]>(new char[piggybackBytes]);
memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggybackBytes);
memcpy(buffer.get(), message->getRawMessage() + statsMessageLength, piggybackBytes);
auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggybackBytes, packet->getSenderSockAddr());
packet = QSharedPointer<NLPacket>(newPacket.release());
auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggybackBytes, message->getSenderSockAddr());
message = QSharedPointer<ReceivedMessage>::create(*newPacket.release());
} else {
// Note... stats packets don't have sequence numbers, so we don't want to send those to trackIncomingVoxelPacket()
return; // bail since no piggyback data
}
} // fall through to piggyback message
PacketType packetType = packet->getType();
PacketType packetType = message->getType();
// check version of piggyback packet against expected version
if (packet->getVersion() != versionForPacketType(packet->getType())) {
if (message->getVersion() != versionForPacketType(message->getType())) {
static QMultiMap<QUuid, PacketType> versionDebugSuppressMap;
const QUuid& senderUUID = packet->getSourceID();
const QUuid& senderUUID = message->getSourceID();
if (!versionDebugSuppressMap.contains(senderUUID, packetType)) {
qDebug() << "Was stats packet? " << wasStatsPacket;
qDebug() << "OctreePacketProcessor - piggyback packet version mismatch on" << packetType << "- Sender"
<< senderUUID << "sent" << (int) packet->getVersion() << "but"
<< senderUUID << "sent" << (int) message->getVersion() << "but"
<< (int) versionForPacketType(packetType) << "expected.";
emit packetVersionMismatch();
@ -83,21 +84,21 @@ void OctreePacketProcessor::processPacket(QSharedPointer<NLPacket> packet, Share
return; // bail since piggyback version doesn't match
}
qApp->trackIncomingOctreePacket(*packet, sendingNode, wasStatsPacket);
qApp->trackIncomingOctreePacket(*message, sendingNode, wasStatsPacket);
// seek back to beginning of packet after tracking
packet->seek(0);
message->seek(0);
switch(packetType) {
case PacketType::EntityErase: {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
qApp->getEntities()->processEraseMessage(*packet, sendingNode);
qApp->getEntities()->processEraseMessage(*message, sendingNode);
}
} break;
case PacketType::EntityData: {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
qApp->getEntities()->processDatagram(*packet, sendingNode);
qApp->getEntities()->processDatagram(*message, sendingNode);
}
} break;

View file

@ -13,6 +13,7 @@
#define hifi_OctreePacketProcessor_h
#include <ReceivedPacketProcessor.h>
#include <ReceivedMessage.h>
/// Handles processing of incoming voxel packets for the interface application. As with other ReceivedPacketProcessor classes
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
@ -25,9 +26,9 @@ signals:
void packetVersionMismatch();
protected:
virtual void processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
virtual void processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override;
private slots:
void handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
};
#endif // hifi_OctreePacketProcessor_h

View file

@ -622,7 +622,8 @@ QScriptValue WindowScriptingInterface::showBrowse(const QString& title, const QS
fileDialog.setAcceptMode(acceptMode);
QUrl fileUrl(directory);
if (acceptMode == QFileDialog::AcceptSave) {
fileDialog.setFileMode(QFileDialog::Directory);
// TODO -- Setting this breaks the dialog on Linux. Does it help something on other platforms?
// fileDialog.setFileMode(QFileDialog::Directory);
fileDialog.selectFile(fileUrl.fileName());
}
if (fileDialog.exec()) {

View file

@ -33,8 +33,8 @@
const int PREFERENCES_HEIGHT_PADDING = 20;
PreferencesDialog::PreferencesDialog(QWidget* parent) :
QDialog(parent) {
QDialog(parent)
{
setAttribute(Qt::WA_DeleteOnClose);
ui.setupUi(this);
@ -48,10 +48,8 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) :
connect(ui.buttonReloadDefaultScripts, &QPushButton::clicked, qApp, &Application::loadDefaultScripts);
connect(ui.buttonChangeAppearance, &QPushButton::clicked, this, &PreferencesDialog::openFullAvatarModelBrowser);
connect(ui.appearanceDescription, &QLineEdit::textChanged, this, [this](const QString& url) {
DependencyManager::get<AvatarManager>()->getMyAvatar()->useFullAvatarURL(url, "");
this->fullAvatarURLChanged(url, "");
});
connect(ui.appearanceDescription, &QLineEdit::editingFinished, this, &PreferencesDialog::changeFullAvatarURL);
connect(qApp, &Application::fullAvatarURLChanged, this, &PreferencesDialog::fullAvatarURLChanged);
// move dialog to left side
@ -61,6 +59,11 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) :
UIUtil::scaleWidgetFontSizes(this);
}
void PreferencesDialog::changeFullAvatarURL() {
DependencyManager::get<AvatarManager>()->getMyAvatar()->useFullAvatarURL(ui.appearanceDescription->text(), "");
this->fullAvatarURLChanged(ui.appearanceDescription->text(), "");
}
void PreferencesDialog::fullAvatarURLChanged(const QString& newValue, const QString& modelName) {
ui.appearanceDescription->setText(newValue);
const QString APPEARANCE_LABEL_TEXT("Appearance: ");
@ -69,9 +72,17 @@ void PreferencesDialog::fullAvatarURLChanged(const QString& newValue, const QStr
void PreferencesDialog::accept() {
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
// if there is an attempted change to the full avatar URL, apply it now
if (QUrl(ui.appearanceDescription->text()) != myAvatar->getFullAvatarURLFromPreferences()) {
changeFullAvatarURL();
}
_lastGoodAvatarURL = myAvatar->getFullAvatarURLFromPreferences();
_lastGoodAvatarName = myAvatar->getFullAvatarModelName();
savePreferences();
close();
delete _marketplaceWindow;
_marketplaceWindow = NULL;

View file

@ -49,6 +49,7 @@ private slots:
void openFullAvatarModelBrowser();
void openSnapshotLocationBrowser();
void openScriptsLocationBrowser();
void changeFullAvatarURL();
void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
};

View file

@ -12,6 +12,7 @@
#include <GeometryCache.h>
#include <RegisteredMetaTypes.h>
#include <DeferredLightingEffect.h>
QString const Line3DOverlay::TYPE = "line3d";
@ -53,6 +54,7 @@ void Line3DOverlay::render(RenderArgs* args) {
auto batch = args->_batch;
if (batch) {
batch->setModelTransform(_transform);
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch);
if (getIsDashedLine()) {
// TODO: add support for color to renderDashedLine()

View file

@ -14,6 +14,8 @@
#include <glm/gtx/vector_angle.hpp>
#include <queue>
#include <QScriptValueIterator>
#include <QWriteLocker>
#include <QReadLocker>
#include <NumericalConstants.h>
#include <DebugDraw.h>
@ -158,10 +160,10 @@ void Rig::destroyAnimGraph() {
_animSkeleton.reset();
_animLoader.reset();
_animNode.reset();
_relativePoses.clear();
_absolutePoses.clear();
_overridePoses.clear();
_overrideFlags.clear();
_internalPoseSet._relativePoses.clear();
_internalPoseSet._absolutePoses.clear();
_internalPoseSet._overridePoses.clear();
_internalPoseSet._overrideFlags.clear();
}
void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset) {
@ -173,16 +175,16 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff
computeEyesInRootFrame(_animSkeleton->getRelativeDefaultPoses());
_relativePoses.clear();
_relativePoses = _animSkeleton->getRelativeDefaultPoses();
_internalPoseSet._relativePoses.clear();
_internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
buildAbsoluteRigPoses(_relativePoses, _absolutePoses);
buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses);
_overridePoses.clear();
_overridePoses = _animSkeleton->getRelativeDefaultPoses();
_internalPoseSet._overridePoses.clear();
_internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses();
_overrideFlags.clear();
_overrideFlags.resize(_animSkeleton->getNumJoints(), false);
_internalPoseSet._overrideFlags.clear();
_internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false);
buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses);
@ -201,16 +203,16 @@ void Rig::reset(const FBXGeometry& geometry) {
computeEyesInRootFrame(_animSkeleton->getRelativeDefaultPoses());
_relativePoses.clear();
_relativePoses = _animSkeleton->getRelativeDefaultPoses();
_internalPoseSet._relativePoses.clear();
_internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
buildAbsoluteRigPoses(_relativePoses, _absolutePoses);
buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses);
_overridePoses.clear();
_overridePoses = _animSkeleton->getRelativeDefaultPoses();
_internalPoseSet._overridePoses.clear();
_internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses();
_overrideFlags.clear();
_overrideFlags.resize(_animSkeleton->getNumJoints(), false);
_internalPoseSet._overrideFlags.clear();
_internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false);
buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses);
@ -228,11 +230,11 @@ void Rig::reset(const FBXGeometry& geometry) {
}
bool Rig::jointStatesEmpty() {
return _relativePoses.empty();
return _internalPoseSet._relativePoses.empty();
}
int Rig::getJointStateCount() const {
return _relativePoses.size();
return _internalPoseSet._relativePoses.size();
}
int Rig::indexOfJoint(const QString& jointName) const {
@ -262,7 +264,7 @@ void Rig::setModelOffset(const glm::mat4& modelOffsetMat) {
bool Rig::getJointStateRotation(int index, glm::quat& rotation) const {
if (isIndexValid(index)) {
rotation = _relativePoses[index].rot;
rotation = _internalPoseSet._relativePoses[index].rot;
return !isEqual(rotation, _animSkeleton->getRelativeDefaultPose(index).rot);
} else {
return false;
@ -271,7 +273,7 @@ bool Rig::getJointStateRotation(int index, glm::quat& rotation) const {
bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const {
if (isIndexValid(index)) {
translation = _relativePoses[index].trans;
translation = _internalPoseSet._relativePoses[index].trans;
return !isEqual(translation, _animSkeleton->getRelativeDefaultPose(index).trans);
} else {
return false;
@ -280,46 +282,47 @@ bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const {
void Rig::clearJointState(int index) {
if (isIndexValid(index)) {
_overrideFlags[index] = false;
_internalPoseSet._overrideFlags[index] = false;
}
}
void Rig::clearJointStates() {
_overrideFlags.clear();
_overrideFlags.resize(_animSkeleton->getNumJoints());
_internalPoseSet._overrideFlags.clear();
_internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints());
}
void Rig::clearJointAnimationPriority(int index) {
if (isIndexValid(index)) {
_overrideFlags[index] = false;
_internalPoseSet._overrideFlags[index] = false;
_internalPoseSet._overridePoses[index] = _animSkeleton->getRelativeDefaultPose(index);
}
}
void Rig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) {
if (isIndexValid(index)) {
if (valid) {
assert(_overrideFlags.size() == _overridePoses.size());
_overrideFlags[index] = true;
_overridePoses[index].trans = translation;
assert(_internalPoseSet._overrideFlags.size() == _internalPoseSet._overridePoses.size());
_internalPoseSet._overrideFlags[index] = true;
_internalPoseSet._overridePoses[index].trans = translation;
}
}
}
void Rig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) {
if (isIndexValid(index)) {
assert(_overrideFlags.size() == _overridePoses.size());
_overrideFlags[index] = true;
_overridePoses[index].rot = rotation;
_overridePoses[index].trans = translation;
assert(_internalPoseSet._overrideFlags.size() == _internalPoseSet._overridePoses.size());
_internalPoseSet._overrideFlags[index] = true;
_internalPoseSet._overridePoses[index].rot = rotation;
_internalPoseSet._overridePoses[index].trans = translation;
}
}
void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) {
if (isIndexValid(index)) {
if (valid) {
ASSERT(_overrideFlags.size() == _overridePoses.size());
_overrideFlags[index] = true;
_overridePoses[index].rot = rotation;
ASSERT(_internalPoseSet._overrideFlags.size() == _internalPoseSet._overridePoses.size());
_internalPoseSet._overrideFlags[index] = true;
_internalPoseSet._overridePoses[index].rot = rotation;
}
}
}
@ -336,7 +339,7 @@ void Rig::restoreJointTranslation(int index, float fraction, float priority) {
bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const {
if (isIndexValid(jointIndex)) {
position = (rotation * _absolutePoses[jointIndex].trans) + translation;
position = (rotation * _internalPoseSet._absolutePoses[jointIndex].trans) + translation;
return true;
} else {
return false;
@ -345,7 +348,7 @@ bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm:
bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const {
if (isIndexValid(jointIndex)) {
position = _absolutePoses[jointIndex].trans;
position = _internalPoseSet._absolutePoses[jointIndex].trans;
return true;
} else {
return false;
@ -354,7 +357,7 @@ bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const {
bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const {
if (isIndexValid(jointIndex)) {
result = rotation * _absolutePoses[jointIndex].rot;
result = rotation * _internalPoseSet._absolutePoses[jointIndex].rot;
return true;
} else {
return false;
@ -362,8 +365,19 @@ bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const
}
bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const {
if (isIndexValid(jointIndex)) {
rotation = _relativePoses[jointIndex].rot;
QReadLocker readLock(&_externalPoseSetLock);
if (jointIndex >= 0 && jointIndex < (int)_externalPoseSet._relativePoses.size()) {
rotation = _externalPoseSet._relativePoses[jointIndex].rot;
return true;
} else {
return false;
}
}
bool Rig::getAbsoluteJointRotationInRigFrame(int jointIndex, glm::quat& rotation) const {
QReadLocker readLock(&_externalPoseSetLock);
if (jointIndex >= 0 && jointIndex < (int)_externalPoseSet._absolutePoses.size()) {
rotation = _externalPoseSet._absolutePoses[jointIndex].rot;
return true;
} else {
return false;
@ -371,8 +385,19 @@ bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const {
}
bool Rig::getJointTranslation(int jointIndex, glm::vec3& translation) const {
if (isIndexValid(jointIndex)) {
translation = _relativePoses[jointIndex].trans;
QReadLocker readLock(&_externalPoseSetLock);
if (jointIndex >= 0 && jointIndex < (int)_externalPoseSet._relativePoses.size()) {
translation = _externalPoseSet._relativePoses[jointIndex].trans;
return true;
} else {
return false;
}
}
bool Rig::getAbsoluteJointTranslationInRigFrame(int jointIndex, glm::vec3& translation) const {
QReadLocker readLock(&_externalPoseSetLock);
if (jointIndex >= 0 && jointIndex < (int)_externalPoseSet._absolutePoses.size()) {
translation = _externalPoseSet._absolutePoses[jointIndex].trans;
return true;
} else {
return false;
@ -708,21 +733,27 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
// evaluate the animation
AnimNode::Triggers triggersOut;
_relativePoses = _animNode->evaluate(_animVars, deltaTime, triggersOut);
if ((int)_relativePoses.size() != _animSkeleton->getNumJoints()) {
_internalPoseSet._relativePoses = _animNode->evaluate(_animVars, deltaTime, triggersOut);
if ((int)_internalPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) {
// animations haven't fully loaded yet.
_relativePoses = _animSkeleton->getRelativeDefaultPoses();
_internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
}
_animVars.clearTriggers();
for (auto& trigger : triggersOut) {
_animVars.setTrigger(trigger);
}
computeEyesInRootFrame(_relativePoses);
computeEyesInRootFrame(_internalPoseSet._relativePoses);
}
applyOverridePoses();
buildAbsoluteRigPoses(_relativePoses, _absolutePoses);
buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses);
// copy internal poses to external poses
{
QWriteLocker writeLock(&_externalPoseSetLock);
_externalPoseSet = _internalPoseSet;
}
}
void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority,
@ -884,7 +915,7 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
if (isIndexValid(index)) {
glm::mat4 rigToWorld = createMatFromQuatAndPos(modelRotation, modelTranslation);
glm::mat4 worldToRig = glm::inverse(rigToWorld);
glm::vec3 zAxis = glm::normalize(_absolutePoses[index].trans - transformPoint(worldToRig, lookAtSpot));
glm::vec3 zAxis = glm::normalize(_internalPoseSet._absolutePoses[index].trans - transformPoint(worldToRig, lookAtSpot));
glm::quat q = rotationBetween(IDENTITY_FRONT, zAxis);
// limit rotation
@ -892,7 +923,7 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
q = glm::angleAxis(glm::clamp(glm::angle(q), -MAX_ANGLE, MAX_ANGLE), glm::axis(q));
// directly set absolutePose rotation
_absolutePoses[index].rot = q;
_internalPoseSet._absolutePoses[index].rot = q;
}
}
@ -989,13 +1020,13 @@ void Rig::applyOverridePoses() {
return;
}
ASSERT(_animSkeleton->getNumJoints() == (int)_relativePoses.size());
ASSERT(_animSkeleton->getNumJoints() == (int)_overrideFlags.size());
ASSERT(_animSkeleton->getNumJoints() == (int)_overridePoses.size());
ASSERT(_animSkeleton->getNumJoints() == (int)_internalPoseSet._relativePoses.size());
ASSERT(_animSkeleton->getNumJoints() == (int)_internalPoseSet._overrideFlags.size());
ASSERT(_animSkeleton->getNumJoints() == (int)_internalPoseSet._overridePoses.size());
for (size_t i = 0; i < _overrideFlags.size(); i++) {
if (_overrideFlags[i]) {
_relativePoses[i] = _overridePoses[i];
for (size_t i = 0; i < _internalPoseSet._overrideFlags.size(); i++) {
if (_internalPoseSet._overrideFlags[i]) {
_internalPoseSet._relativePoses[i] = _internalPoseSet._overridePoses[i];
}
}
}
@ -1020,14 +1051,14 @@ void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& a
// transform all absolute poses into rig space.
AnimPose geometryToRigTransform(_geometryToRigTransform);
for (int i = 0; i < (int)_absolutePoses.size(); i++) {
for (int i = 0; i < (int)absolutePosesOut.size(); i++) {
absolutePosesOut[i] = geometryToRigTransform * absolutePosesOut[i];
}
}
glm::mat4 Rig::getJointTransform(int jointIndex) const {
if (isIndexValid(jointIndex)) {
return _absolutePoses[jointIndex];
return _internalPoseSet._absolutePoses[jointIndex];
} else {
return glm::mat4();
}

View file

@ -19,6 +19,7 @@
#include <QScriptValue>
#include <vector>
#include <JointData.h>
#include <QReadWriteLock>
#include "AnimNode.h"
#include "AnimNodeLoader.h"
@ -27,6 +28,9 @@
class Rig;
typedef std::shared_ptr<Rig> RigPointer;
// Rig instances are reentrant.
// However only specific methods thread-safe. Noted below.
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
public:
struct StateHandler {
@ -123,12 +127,14 @@ public:
// if rotation is identity, result will be in rig space
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const;
// geometry space
// geometry space (thread-safe)
bool getJointRotation(int jointIndex, glm::quat& rotation) const;
// geometry space
bool getJointTranslation(int jointIndex, glm::vec3& translation) const;
// rig space (thread-safe)
bool getAbsoluteJointRotationInRigFrame(int jointIndex, glm::quat& rotation) const;
bool getAbsoluteJointTranslationInRigFrame(int jointIndex, glm::vec3& translation) const;
// legacy
bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const;
@ -217,10 +223,19 @@ public:
AnimPose _modelOffset; // model to rig space
AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets)
AnimPoseVec _relativePoses; // geometry space relative to parent.
AnimPoseVec _absolutePoses; // rig space, not relative to parent.
AnimPoseVec _overridePoses; // geometry space relative to parent.
std::vector<bool> _overrideFlags;
struct PoseSet {
AnimPoseVec _relativePoses; // geometry space relative to parent.
AnimPoseVec _absolutePoses; // rig space, not relative to parent.
AnimPoseVec _overridePoses; // geometry space relative to parent.
std::vector<bool> _overrideFlags;
};
// Only accessed by the main thread
PoseSet _internalPoseSet;
// Copy of the _poseSet for external threads.
PoseSet _externalPoseSet;
mutable QReadWriteLock _externalPoseSetLock;
AnimPoseVec _absoluteDefaultPoses; // rig space, not relative to parent.

View file

@ -462,24 +462,24 @@ void AudioClient::stop() {
}
}
void AudioClient::handleAudioEnvironmentDataPacket(QSharedPointer<NLPacket> packet) {
void AudioClient::handleAudioEnvironmentDataPacket(QSharedPointer<ReceivedMessage> message) {
char bitset;
packet->readPrimitive(&bitset);
message->readPrimitive(&bitset);
bool hasReverb = oneAtBit(bitset, HAS_REVERB_BIT);
if (hasReverb) {
float reverbTime, wetLevel;
packet->readPrimitive(&reverbTime);
packet->readPrimitive(&wetLevel);
message->readPrimitive(&reverbTime);
message->readPrimitive(&wetLevel);
_receivedAudioStream.setReverb(reverbTime, wetLevel);
} else {
_receivedAudioStream.clearReverb();
}
}
void AudioClient::handleAudioDataPacket(QSharedPointer<NLPacket> packet) {
void AudioClient::handleAudioDataPacket(QSharedPointer<ReceivedMessage> message) {
auto nodeList = DependencyManager::get<NodeList>();
nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket);
@ -493,11 +493,11 @@ void AudioClient::handleAudioDataPacket(QSharedPointer<NLPacket> packet) {
}
// Audio output must exist and be correctly set up if we're going to process received audio
_receivedAudioStream.parseData(*packet);
_receivedAudioStream.parseData(*message);
}
}
void AudioClient::handleNoisyMutePacket(QSharedPointer<NLPacket> packet) {
void AudioClient::handleNoisyMutePacket(QSharedPointer<ReceivedMessage> message) {
if (!_muted) {
toggleMute();
@ -506,12 +506,12 @@ void AudioClient::handleNoisyMutePacket(QSharedPointer<NLPacket> packet) {
}
}
void AudioClient::handleMuteEnvironmentPacket(QSharedPointer<NLPacket> packet) {
void AudioClient::handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> message) {
glm::vec3 position;
float radius;
packet->readPrimitive(&position);
packet->readPrimitive(&radius);
message->readPrimitive(&position);
message->readPrimitive(&radius);
emit muteEnvironmentRequested(position, radius);
}
@ -541,26 +541,40 @@ bool AudioClient::switchOutputToAudioDevice(const QString& outputDeviceName) {
void AudioClient::configureReverb() {
ReverbParameters p;
_listenerReverb.getParameters(&p);
// for now, reuse the gverb parameters
p.sampleRate = _outputFormat.sampleRate();
p.roomSize = _reverbOptions->getRoomSize();
p.bandwidth = _reverbOptions->getBandwidth();
p.preDelay = _reverbOptions->getPreDelay();
p.lateDelay = _reverbOptions->getLateDelay();
p.reverbTime = _reverbOptions->getReverbTime();
p.highGain = -24.0f * (1.0f - _reverbOptions->getDamping());
p.bandwidth = 10000.0f * _reverbOptions->getInputBandwidth();
p.earlyGain = _reverbOptions->getEarlyLevel();
p.lateGain = _reverbOptions->getTailLevel();
p.wetDryMix = 100.0f * powf(10.0f, _reverbOptions->getWetLevel() * (1/20.0f));
p.earlyDiffusion = _reverbOptions->getEarlyDiffusion();
p.lateDiffusion = _reverbOptions->getLateDiffusion();
p.roomSize = _reverbOptions->getRoomSize();
p.density = _reverbOptions->getDensity();
p.bassMult = _reverbOptions->getBassMult();
p.bassFreq = _reverbOptions->getBassFreq();
p.highGain = _reverbOptions->getHighGain();
p.highFreq = _reverbOptions->getHighFreq();
p.modRate = _reverbOptions->getModRate();
p.modDepth = _reverbOptions->getModDepth();
p.earlyGain = _reverbOptions->getEarlyGain();
p.lateGain = _reverbOptions->getLateGain();
p.earlyMixLeft = _reverbOptions->getEarlyMixLeft();
p.earlyMixRight = _reverbOptions->getEarlyMixRight();
p.lateMixLeft = _reverbOptions->getLateMixLeft();
p.lateMixRight = _reverbOptions->getLateMixRight();
p.wetDryMix = _reverbOptions->getWetDryMix();
_listenerReverb.setParameters(&p);
// used for adding self-reverb to loopback audio
// used only for adding self-reverb to loopback audio
p.wetDryMix = 100.0f;
p.preDelay = 0.0f;
p.earlyGain = -96.0f; // disable ER
p.lateGain -= 12.0f; // quieter than listener reverb
p.lateGain -= 12.0f; // quieter than listener reverb
p.lateMixLeft = 0.0f;
p.lateMixRight = 0.0f;
_sourceReverb.setParameters(&p);
}
@ -572,10 +586,10 @@ void AudioClient::updateReverbOptions() {
_zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime());
reverbChanged = true;
}
if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) {
_zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel());
reverbChanged = true;
}
//if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) {
// _zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel());
// reverbChanged = true;
//}
if (_reverbOptions != &_zoneReverbOptions) {
_reverbOptions = &_zoneReverbOptions;
@ -602,17 +616,27 @@ void AudioClient::setReverb(bool reverb) {
void AudioClient::setReverbOptions(const AudioEffectOptions* options) {
// Save the new options
_scriptReverbOptions.setMaxRoomSize(options->getMaxRoomSize());
_scriptReverbOptions.setRoomSize(options->getRoomSize());
_scriptReverbOptions.setBandwidth(options->getBandwidth());
_scriptReverbOptions.setPreDelay(options->getPreDelay());
_scriptReverbOptions.setLateDelay(options->getLateDelay());
_scriptReverbOptions.setReverbTime(options->getReverbTime());
_scriptReverbOptions.setDamping(options->getDamping());
_scriptReverbOptions.setSpread(options->getSpread());
_scriptReverbOptions.setInputBandwidth(options->getInputBandwidth());
_scriptReverbOptions.setEarlyLevel(options->getEarlyLevel());
_scriptReverbOptions.setTailLevel(options->getTailLevel());
_scriptReverbOptions.setDryLevel(options->getDryLevel());
_scriptReverbOptions.setWetLevel(options->getWetLevel());
_scriptReverbOptions.setEarlyDiffusion(options->getEarlyDiffusion());
_scriptReverbOptions.setLateDiffusion(options->getLateDiffusion());
_scriptReverbOptions.setRoomSize(options->getRoomSize());
_scriptReverbOptions.setDensity(options->getDensity());
_scriptReverbOptions.setBassMult(options->getBassMult());
_scriptReverbOptions.setBassFreq(options->getBassFreq());
_scriptReverbOptions.setHighGain(options->getHighGain());
_scriptReverbOptions.setHighFreq(options->getHighFreq());
_scriptReverbOptions.setModRate(options->getModRate());
_scriptReverbOptions.setModDepth(options->getModDepth());
_scriptReverbOptions.setEarlyGain(options->getEarlyGain());
_scriptReverbOptions.setLateGain(options->getLateGain());
_scriptReverbOptions.setEarlyMixLeft(options->getEarlyMixLeft());
_scriptReverbOptions.setEarlyMixRight(options->getEarlyMixRight());
_scriptReverbOptions.setLateMixLeft(options->getLateMixLeft());
_scriptReverbOptions.setLateMixRight(options->getLateMixRight());
_scriptReverbOptions.setWetDryMix(options->getWetDryMix());
if (_reverbOptions == &_scriptReverbOptions) {
// Apply them to the reverb instances

View file

@ -140,10 +140,10 @@ public slots:
void start();
void stop();
void handleAudioEnvironmentDataPacket(QSharedPointer<NLPacket> packet);
void handleAudioDataPacket(QSharedPointer<NLPacket> packet);
void handleNoisyMutePacket(QSharedPointer<NLPacket> packet);
void handleMuteEnvironmentPacket(QSharedPointer<NLPacket> packet);
void handleAudioEnvironmentDataPacket(QSharedPointer<ReceivedMessage> message);
void handleAudioDataPacket(QSharedPointer<ReceivedMessage> message);
void handleNoisyMutePacket(QSharedPointer<ReceivedMessage> message);
void handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> message);
void sendDownstreamAudioStatsPacket() { _stats.sendDownstreamAudioStatsPacket(); }
void handleAudioInput();

View file

@ -63,11 +63,12 @@ void AudioIOStats::sentPacket() {
_lastSentAudioPacket = now;
}
}
void AudioIOStats::processStreamStatsPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
void AudioIOStats::processStreamStatsPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
// parse the appendFlag, clear injected audio stream stats if 0
quint8 appendFlag;
packet->readPrimitive(&appendFlag);
message->readPrimitive(&appendFlag);
if (!appendFlag) {
_mixerInjectedStreamStatsMap.clear();
@ -75,12 +76,12 @@ void AudioIOStats::processStreamStatsPacket(QSharedPointer<NLPacket> packet, Sha
// parse the number of stream stats structs to follow
quint16 numStreamStats;
packet->readPrimitive(&numStreamStats);
message->readPrimitive(&numStreamStats);
// parse the stream stats
AudioStreamStats streamStats;
for (quint16 i = 0; i < numStreamStats; i++) {
packet->readPrimitive(&streamStats);
message->readPrimitive(&streamStats);
if (streamStats._streamType == PositionalAudioStream::Microphone) {
_mixerAvatarStreamStats = streamStats;

View file

@ -45,7 +45,7 @@ public:
void sendDownstreamAudioStatsPacket();
public slots:
void processStreamStatsPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processStreamStatsPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
private:
MixedProcessedAudioStream* _receivedAudioStream;

View file

@ -10,58 +10,76 @@
#include "AudioEffectOptions.h"
static const QString MAX_ROOM_SIZE_HANDLE = "maxRoomSize";
static const QString ROOM_SIZE_HANDLE = "roomSize";
static const QString BANDWIDTH_HANDLE = "bandwidth";
static const QString PRE_DELAY_HANDLE = "preDelay";
static const QString LATE_DELAY_HANDLE = "lateDelay";
static const QString REVERB_TIME_HANDLE = "reverbTime";
static const QString DAMPIMG_HANDLE = "damping";
static const QString SPREAD_HANDLE = "spread";
static const QString INPUT_BANDWIDTH_HANDLE = "inputBandwidth";
static const QString EARLY_LEVEL_HANDLE = "earlyLevel";
static const QString TAIL_LEVEL_HANDLE = "tailLevel";
static const QString DRY_LEVEL_HANDLE = "dryLevel";
static const QString WET_LEVEL_HANDLE = "wetLevel";
static const QString EARLY_DIFFUSION_HANDLE = "earlyDiffusion";
static const QString LATE_DIFFUSION_HANDLE = "lateDiffusion";
static const QString ROOM_SIZE_HANDLE = "roomSize";
static const QString DENSITY_HANDLE = "density";
static const QString BASS_MULT_HANDLE = "bassMult";
static const QString BASS_FREQ_HANDLE = "bassFreq";
static const QString HIGH_GAIN_HANDLE = "highGain";
static const QString HIGH_FREQ_HANDLE = "highFreq";
static const QString MOD_RATE_HANDLE = "modRate";
static const QString MOD_DEPTH_HANDLE = "modDepth";
static const QString EARLY_GAIN_HANDLE = "earlyGain";
static const QString LATE_GAIN_HANDLE = "lateGain";
static const QString EARLY_MIX_LEFT_HANDLE = "earlyMixLeft";
static const QString EARLY_MIX_RIGHT_HANDLE = "earlyMixRight";
static const QString LATE_MIX_LEFT_HANDLE = "lateMixLeft";
static const QString LATE_MIX_RIGHT_HANDLE = "lateMixRight";
static const QString WET_DRY_MIX_HANDLE = "wetDryMix";
AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) :
_maxRoomSize(50.0f),
_roomSize(50.0f),
_reverbTime(4.0f),
_damping(0.5f),
_spread(15.0f),
_inputBandwidth(0.75f),
_earlyLevel(-12.0f),
_tailLevel(-18.0f),
_dryLevel(0.0f),
_wetLevel(0.0f) {
if (arguments.property(MAX_ROOM_SIZE_HANDLE).isNumber()) {
_maxRoomSize = arguments.property(MAX_ROOM_SIZE_HANDLE).toNumber();
}
if (arguments.property(ROOM_SIZE_HANDLE).isNumber()) {
_roomSize = arguments.property(ROOM_SIZE_HANDLE).toNumber();
}
if (arguments.property(REVERB_TIME_HANDLE).isNumber()) {
_reverbTime = arguments.property(REVERB_TIME_HANDLE).toNumber();
}
if (arguments.property(DAMPIMG_HANDLE).isNumber()) {
_damping = arguments.property(DAMPIMG_HANDLE).toNumber();
}
if (arguments.property(SPREAD_HANDLE).isNumber()) {
_spread = arguments.property(SPREAD_HANDLE).toNumber();
}
if (arguments.property(INPUT_BANDWIDTH_HANDLE).isNumber()) {
_inputBandwidth = arguments.property(INPUT_BANDWIDTH_HANDLE).toNumber();
}
if (arguments.property(EARLY_LEVEL_HANDLE).isNumber()) {
_earlyLevel = arguments.property(EARLY_LEVEL_HANDLE).toNumber();
}
if (arguments.property(TAIL_LEVEL_HANDLE).isNumber()) {
_tailLevel = arguments.property(TAIL_LEVEL_HANDLE).toNumber();
}
if (arguments.property(DRY_LEVEL_HANDLE).isNumber()) {
_dryLevel = arguments.property(DRY_LEVEL_HANDLE).toNumber();
}
if (arguments.property(WET_LEVEL_HANDLE).isNumber()) {
_wetLevel = arguments.property(WET_LEVEL_HANDLE).toNumber();
}
static const float BANDWIDTH_DEFAULT = 10000.0f;
static const float PRE_DELAY_DEFAULT = 20.0f;
static const float LATE_DELAY_DEFAULT = 0.0f;
static const float REVERB_TIME_DEFAULT = 2.0f;
static const float EARLY_DIFFUSION_DEFAULT = 100.0f;
static const float LATE_DIFFUSION_DEFAULT = 100.0f;
static const float ROOM_SIZE_DEFAULT = 50.0f;
static const float DENSITY_DEFAULT = 100.0f;
static const float BASS_MULT_DEFAULT = 1.5f;
static const float BASS_FREQ_DEFAULT = 250.0f;
static const float HIGH_GAIN_DEFAULT = -6.0f;
static const float HIGH_FREQ_DEFAULT = 3000.0f;
static const float MOD_RATE_DEFAULT = 2.3f;
static const float MOD_DEPTH_DEFAULT = 50.0f;
static const float EARLY_GAIN_DEFAULT = 0.0f;
static const float LATE_GAIN_DEFAULT = 0.0f;
static const float EARLY_MIX_LEFT_DEFAULT = 20.0f;
static const float EARLY_MIX_RIGHT_DEFAULT = 20.0f;
static const float LATE_MIX_LEFT_DEFAULT = 90.0f;
static const float LATE_MIX_RIGHT_DEFAULT = 90.0f;
static const float WET_DRY_MIX_DEFAULT = 50.0f;
static void setOption(QScriptValue arguments, const QString name, float defaultValue, float& variable) {
variable = arguments.property(name).isNumber() ? arguments.property(name).toNumber() : defaultValue;
}
AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) {
setOption(arguments, BANDWIDTH_HANDLE, BANDWIDTH_DEFAULT, _bandwidth);
setOption(arguments, PRE_DELAY_HANDLE, PRE_DELAY_DEFAULT, _preDelay);
setOption(arguments, LATE_DELAY_HANDLE, LATE_DELAY_DEFAULT, _lateDelay);
setOption(arguments, REVERB_TIME_HANDLE, REVERB_TIME_DEFAULT, _reverbTime);
setOption(arguments, EARLY_DIFFUSION_HANDLE, EARLY_DIFFUSION_DEFAULT, _earlyDiffusion);
setOption(arguments, LATE_DIFFUSION_HANDLE, LATE_DIFFUSION_DEFAULT, _lateDiffusion);
setOption(arguments, ROOM_SIZE_HANDLE, ROOM_SIZE_DEFAULT, _roomSize);
setOption(arguments, DENSITY_HANDLE, DENSITY_DEFAULT, _density);
setOption(arguments, BASS_MULT_HANDLE, BASS_MULT_DEFAULT, _bassMult);
setOption(arguments, BASS_FREQ_HANDLE, BASS_FREQ_DEFAULT, _bassFreq);
setOption(arguments, HIGH_GAIN_HANDLE, HIGH_GAIN_DEFAULT, _highGain);
setOption(arguments, HIGH_FREQ_HANDLE, HIGH_FREQ_DEFAULT, _highFreq);
setOption(arguments, MOD_RATE_HANDLE, MOD_RATE_DEFAULT, _modRate);
setOption(arguments, MOD_DEPTH_HANDLE, MOD_DEPTH_DEFAULT, _modDepth);
setOption(arguments, EARLY_GAIN_HANDLE, EARLY_GAIN_DEFAULT, _earlyGain);
setOption(arguments, LATE_GAIN_HANDLE, LATE_GAIN_DEFAULT, _lateGain);
setOption(arguments, EARLY_MIX_LEFT_HANDLE, EARLY_MIX_LEFT_DEFAULT, _earlyMixLeft);
setOption(arguments, EARLY_MIX_RIGHT_HANDLE, EARLY_MIX_RIGHT_DEFAULT, _earlyMixRight);
setOption(arguments, LATE_MIX_LEFT_HANDLE, LATE_MIX_LEFT_DEFAULT, _lateMixLeft);
setOption(arguments, LATE_MIX_RIGHT_HANDLE, LATE_MIX_RIGHT_DEFAULT, _lateMixRight);
setOption(arguments, WET_DRY_MIX_HANDLE, WET_DRY_MIX_DEFAULT, _wetDryMix);
}
AudioEffectOptions::AudioEffectOptions(const AudioEffectOptions &other) : QObject() {
@ -69,17 +87,28 @@ AudioEffectOptions::AudioEffectOptions(const AudioEffectOptions &other) : QObjec
}
AudioEffectOptions& AudioEffectOptions::operator=(const AudioEffectOptions &other) {
_maxRoomSize = other._maxRoomSize;
_roomSize = other._roomSize;
_bandwidth = other._bandwidth;
_preDelay = other._preDelay;
_lateDelay = other._lateDelay;
_reverbTime = other._reverbTime;
_damping = other._damping;
_spread = other._spread;
_inputBandwidth = other._inputBandwidth;
_earlyLevel = other._earlyLevel;
_tailLevel = other._tailLevel;
_dryLevel = other._dryLevel;
_wetLevel = other._wetLevel;
_earlyDiffusion = other._earlyDiffusion;
_lateDiffusion = other._lateDiffusion;
_roomSize = other._roomSize;
_density = other._density;
_bassMult = other._bassMult;
_bassFreq = other._bassFreq;
_highGain = other._highGain;
_highFreq = other._highFreq;
_modRate = other._modRate;
_modDepth = other._modDepth;
_earlyGain = other._earlyGain;
_lateGain = other._lateGain;
_earlyMixLeft = other._earlyMixLeft;
_earlyMixRight = other._earlyMixRight;
_lateMixLeft = other._lateMixLeft;
_lateMixRight = other._lateMixRight;
_wetDryMix = other._wetDryMix;
return *this;
}

View file

@ -15,32 +15,30 @@
#include <QtScript/QScriptContext>
#include <QtScript/QScriptEngine>
#include "AudioReverb.h"
class AudioEffectOptions : public QObject {
Q_OBJECT
// Meters Square
Q_PROPERTY(float maxRoomSize READ getMaxRoomSize WRITE setMaxRoomSize)
Q_PROPERTY(float roomSize READ getRoomSize WRITE setRoomSize)
// Seconds
Q_PROPERTY(float bandwidth READ getBandwidth WRITE setBandwidth)
Q_PROPERTY(float preDelay READ getPreDelay WRITE setPreDelay)
Q_PROPERTY(float lateDelay READ getLateDelay WRITE setLateDelay)
Q_PROPERTY(float reverbTime READ getReverbTime WRITE setReverbTime)
// Ratio between 0 and 1
Q_PROPERTY(float damping READ getDamping WRITE setDamping)
// (?) Does not appear to be set externally very often
Q_PROPERTY(float spread READ getSpread WRITE setSpread)
// Ratio between 0 and 1
Q_PROPERTY(float inputBandwidth READ getInputBandwidth WRITE setInputBandwidth)
// in dB
Q_PROPERTY(float earlyLevel READ getEarlyLevel WRITE setEarlyLevel)
Q_PROPERTY(float tailLevel READ getTailLevel WRITE setTailLevel)
Q_PROPERTY(float dryLevel READ getDryLevel WRITE setDryLevel)
Q_PROPERTY(float wetLevel READ getWetLevel WRITE setWetLevel)
Q_PROPERTY(float earlyDiffusion READ getEarlyDiffusion WRITE setEarlyDiffusion)
Q_PROPERTY(float lateDiffusion READ getLateDiffusion WRITE setLateDiffusion)
Q_PROPERTY(float roomSize READ getRoomSize WRITE setRoomSize)
Q_PROPERTY(float density READ getDensity WRITE setDensity)
Q_PROPERTY(float bassMult READ getBassMult WRITE setBassMult)
Q_PROPERTY(float bassFreq READ getBassFreq WRITE setBassFreq)
Q_PROPERTY(float highGain READ getHighGain WRITE setHighGain)
Q_PROPERTY(float highFreq READ getHighFreq WRITE setHighFreq)
Q_PROPERTY(float modRate READ getModRate WRITE setModRate)
Q_PROPERTY(float modDepth READ getModDepth WRITE setModDepth)
Q_PROPERTY(float earlyGain READ getEarlyGain WRITE setEarlyGain)
Q_PROPERTY(float lateGain READ getLateGain WRITE setLateGain)
Q_PROPERTY(float earlyMixLeft READ getEarlyMixLeft WRITE setEarlyMixLeft)
Q_PROPERTY(float earlyMixRight READ getEarlyMixRight WRITE setEarlyMixRight)
Q_PROPERTY(float lateMixLeft READ getLateMixLeft WRITE setLateMixLeft)
Q_PROPERTY(float lateMixRight READ getLateMixRight WRITE setLateMixRight)
Q_PROPERTY(float wetDryMix READ getWetDryMix WRITE setWetDryMix)
public:
AudioEffectOptions(QScriptValue arguments = QScriptValue());
@ -49,60 +47,100 @@ public:
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
float getRoomSize() const { return _roomSize; }
void setRoomSize(float roomSize ) { _roomSize = roomSize; }
float getBandwidth() const { return _bandwidth; }
void setBandwidth(float bandwidth) { _bandwidth = bandwidth; }
float getMaxRoomSize() const { return _maxRoomSize; }
void setMaxRoomSize(float maxRoomSize ) { _maxRoomSize = maxRoomSize; }
float getPreDelay() const { return _preDelay; }
void setPreDelay(float preDelay) { _preDelay = preDelay; }
float getLateDelay() const { return _lateDelay; }
void setLateDelay(float lateDelay) { _lateDelay = lateDelay; }
float getReverbTime() const { return _reverbTime; }
void setReverbTime(float reverbTime ) { _reverbTime = reverbTime; }
void setReverbTime(float reverbTime) { _reverbTime = reverbTime; }
float getDamping() const { return _damping; }
void setDamping(float damping ) { _damping = damping; }
float getEarlyDiffusion() const { return _earlyDiffusion; }
void setEarlyDiffusion(float earlyDiffusion) { _earlyDiffusion = earlyDiffusion; }
float getSpread() const { return _spread; }
void setSpread(float spread ) { _spread = spread; }
float getLateDiffusion() const { return _lateDiffusion; }
void setLateDiffusion(float lateDiffusion) { _lateDiffusion = lateDiffusion; }
float getInputBandwidth() const { return _inputBandwidth; }
void setInputBandwidth(float inputBandwidth ) { _inputBandwidth = inputBandwidth; }
float getRoomSize() const { return _roomSize; }
void setRoomSize(float roomSize) { _roomSize = roomSize; }
float getEarlyLevel() const { return _earlyLevel; }
void setEarlyLevel(float earlyLevel ) { _earlyLevel = earlyLevel; }
float getDensity() const { return _density; }
void setDensity(float density) { _density = density; }
float getTailLevel() const { return _tailLevel; }
void setTailLevel(float tailLevel ) { _tailLevel = tailLevel; }
float getBassMult() const { return _bassMult; }
void setBassMult(float bassMult) { _bassMult = bassMult; }
float getDryLevel() const { return _dryLevel; }
void setDryLevel(float dryLevel) { _dryLevel = dryLevel; }
float getBassFreq() const { return _bassFreq; }
void setBassFreq(float bassFreq) { _bassFreq = bassFreq; }
float getWetLevel() const { return _wetLevel; }
void setWetLevel(float wetLevel) { _wetLevel = wetLevel; }
float getHighGain() const { return _highGain; }
void setHighGain(float highGain) { _highGain = highGain; }
float getHighFreq() const { return _highFreq; }
void setHighFreq(float highFreq) { _highFreq = highFreq; }
float getModRate() const { return _modRate; }
void setModRate(float modRate) { _modRate = modRate; }
float getModDepth() const { return _modDepth; }
void setModDepth(float modDepth) { _modDepth = modDepth; }
float getEarlyGain() const { return _earlyGain; }
void setEarlyGain(float earlyGain) { _earlyGain = earlyGain; }
float getLateGain() const { return _lateGain; }
void setLateGain(float lateGain) { _lateGain = lateGain; }
float getEarlyMixLeft() const { return _earlyMixLeft; }
void setEarlyMixLeft(float earlyMixLeft) { _earlyMixLeft = earlyMixLeft; }
float getEarlyMixRight() const { return _earlyMixRight; }
void setEarlyMixRight(float earlyMixRight) { _earlyMixRight = earlyMixRight; }
float getLateMixLeft() const { return _lateMixLeft; }
void setLateMixLeft(float lateMixLeft) { _lateMixLeft = lateMixLeft; }
float getLateMixRight() const { return _lateMixRight; }
void setLateMixRight(float lateMixRight) { _lateMixRight = lateMixRight; }
float getWetDryMix() const { return _wetDryMix; }
void setWetDryMix(float wetDryMix) { _wetDryMix = wetDryMix; }
private:
// http://wiki.audacityteam.org/wiki/GVerb#Instant_Reverberb_settings
float _bandwidth; // [20, 24000] Hz
// Meters Square
float _maxRoomSize;
float _roomSize;
float _preDelay; // [0, 333] ms
float _lateDelay; // [0, 166] ms
// Seconds
float _reverbTime;
float _reverbTime; // [0.1, 100] seconds
// Ratio between 0 and 1
float _damping;
float _earlyDiffusion; // [0, 100] percent
float _lateDiffusion; // [0, 100] percent
// ? (Does not appear to be set externally very often)
float _spread;
float _roomSize; // [0, 100] percent
float _density; // [0, 100] percent
// Ratio between 0 and 1
float _inputBandwidth;
float _bassMult; // [0.1, 10] ratio
float _bassFreq; // [10, 500] Hz
float _highGain; // [-24, 0] dB
float _highFreq; // [1000, 12000] Hz
// dB
float _earlyLevel;
float _tailLevel;
float _dryLevel;
float _wetLevel;
float _modRate; // [0.1, 10] Hz
float _modDepth; // [0, 100] percent
float _earlyGain; // [-96, +24] dB
float _lateGain; // [-96, +24] dB
float _earlyMixLeft; // [0, 100] percent
float _earlyMixRight; // [0, 100] percent
float _lateMixLeft; // [0, 100] percent
float _lateMixRight; // [0, 100] percent
float _wetDryMix; // [0, 100] percent
};
#endif // hifi_AudioEffectOptions_h

View file

@ -98,22 +98,22 @@ void InboundAudioStream::perSecondCallbackForUpdatingStats() {
_timeGapStatsForStatsPacket.currentIntervalComplete();
}
int InboundAudioStream::parseData(NLPacket& packet) {
int InboundAudioStream::parseData(ReceivedMessage& message) {
// parse sequence number and track it
quint16 sequence;
packet.readPrimitive(&sequence);
message.readPrimitive(&sequence);
SequenceNumberStats::ArrivalInfo arrivalInfo = _incomingSequenceNumberStats.sequenceNumberReceived(sequence,
packet.getSourceID());
message.getSourceID());
packetReceivedUpdateTimingStats();
int networkSamples;
// parse the info after the seq number and before the audio data (the stream properties)
int prePropertyPosition = packet.pos();
int propertyBytes = parseStreamProperties(packet.getType(), packet.readWithoutCopy(packet.bytesLeftToRead()), networkSamples);
packet.seek(prePropertyPosition + propertyBytes);
int prePropertyPosition = message.getPosition();
int propertyBytes = parseStreamProperties(message.getType(), message.readWithoutCopy(message.getBytesLeftToRead()), networkSamples);
message.seek(prePropertyPosition + propertyBytes);
// handle this packet based on its arrival status.
switch (arrivalInfo._status) {
@ -128,10 +128,10 @@ int InboundAudioStream::parseData(NLPacket& packet) {
}
case SequenceNumberStats::OnTime: {
// Packet is on time; parse its data to the ringbuffer
if (packet.getType() == PacketType::SilentAudioFrame) {
if (message.getType() == PacketType::SilentAudioFrame) {
writeDroppableSilentSamples(networkSamples);
} else {
parseAudioData(packet.getType(), packet.readWithoutCopy(packet.bytesLeftToRead()), networkSamples);
parseAudioData(message.getType(), message.readWithoutCopy(message.getBytesLeftToRead()), networkSamples);
}
break;
}
@ -161,7 +161,7 @@ int InboundAudioStream::parseData(NLPacket& packet) {
framesAvailableChanged();
return packet.pos();
return message.getPosition();
}
int InboundAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {

View file

@ -15,6 +15,7 @@
#include <NodeData.h>
#include <NumericalConstants.h>
#include <udt/PacketHeaders.h>
#include <ReceivedMessage.h>
#include <StDev.h>
#include "AudioRingBuffer.h"
@ -107,7 +108,7 @@ public:
virtual void resetStats();
void clearBuffer();
virtual int parseData(NLPacket& packet);
virtual int parseData(ReceivedMessage& packet) override;
int popFrames(int maxFrames, bool allOrNothing, bool starveIfNoFramesPopped = true);
int popSamples(int maxSamples, bool allOrNothing, bool starveIfNoSamplesPopped = true);

View file

@ -985,8 +985,8 @@ void AvatarData::clearJointsData() {
}
}
bool AvatarData::hasIdentityChangedAfterParsing(NLPacket& packet) {
QDataStream packetStream(&packet);
bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray& data) {
QDataStream packetStream(data);
QUuid avatarUUID;
QUrl faceModelURL, skeletonModelURL;
@ -1030,12 +1030,11 @@ QByteArray AvatarData::identityByteArray() {
return identityData;
}
bool AvatarData::hasBillboardChangedAfterParsing(NLPacket& packet) {
QByteArray newBillboard = packet.readAll();
if (newBillboard == _billboard) {
bool AvatarData::hasBillboardChangedAfterParsing(const QByteArray& data) {
if (data == _billboard) {
return false;
}
_billboard = newBillboard;
_billboard = data;
return true;
}
@ -1626,10 +1625,10 @@ void AvatarData::setBodyRoll(float bodyRoll) {
setOrientation(glm::quat(glm::radians(eulerAngles)));
}
void AvatarData::setPosition(const glm::vec3 position) {
void AvatarData::setPosition(const glm::vec3& position) {
SpatiallyNestable::setPosition(position);
}
void AvatarData::setOrientation(const glm::quat orientation) {
void AvatarData::setOrientation(const glm::quat& orientation) {
SpatiallyNestable::setOrientation(orientation);
}

View file

@ -201,8 +201,8 @@ public:
float getBodyRoll() const;
void setBodyRoll(float bodyRoll);
virtual void setPosition(glm::vec3 position);
virtual void setOrientation(glm::quat orientation);
virtual void setPosition(const glm::vec3& position) override;
virtual void setOrientation(const glm::quat& orientation) override;
void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time.
void startCapture(); // start/end of the period in which the latest values are about to be captured for camera, etc.
@ -284,10 +284,10 @@ public:
const HeadData* getHeadData() const { return _headData; }
const HandData* getHandData() const { return _handData; }
bool hasIdentityChangedAfterParsing(NLPacket& packet);
bool hasIdentityChangedAfterParsing(const QByteArray& data);
QByteArray identityByteArray();
bool hasBillboardChangedAfterParsing(NLPacket& packet);
bool hasBillboardChangedAfterParsing(const QByteArray& data);
const QUrl& getFaceModelURL() const { return _faceModelURL; }
QString getFaceModelURLString() const { return _faceModelURL.toString(); }

View file

@ -71,35 +71,34 @@ AvatarSharedPointer AvatarHashMap::findAvatar(const QUuid& sessionUUID) {
return nullptr;
}
void AvatarHashMap::processAvatarDataPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
void AvatarHashMap::processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
// enumerate over all of the avatars in this packet
// only add them if mixerWeakPointer points to something (meaning that mixer is still around)
while (packet->bytesLeftToRead()) {
QUuid sessionUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
while (message->getBytesLeftToRead()) {
QUuid sessionUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
int positionBeforeRead = packet->pos();
int positionBeforeRead = message->getPosition();
QByteArray byteArray = packet->readWithoutCopy(packet->bytesLeftToRead());
QByteArray byteArray = message->readWithoutCopy(message->getBytesLeftToRead());
if (sessionUUID != _lastOwnerSessionUUID) {
auto avatar = newOrExistingAvatar(sessionUUID, sendingNode);
// have the matching (or new) avatar parse the data from the packet
int bytesRead = avatar->parseDataFromBuffer(byteArray);
packet->seek(positionBeforeRead + bytesRead);
message->seek(positionBeforeRead + bytesRead);
} else {
// create a dummy AvatarData class to throw this data on the ground
AvatarData dummyData;
int bytesRead = dummyData.parseDataFromBuffer(byteArray);
packet->seek(positionBeforeRead + bytesRead);
message->seek(positionBeforeRead + bytesRead);
}
}
}
void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
// setup a data stream to parse the packet
QDataStream identityStream(packet.data());
QDataStream identityStream(message->getMessage());
QUuid sessionUUID;
@ -131,20 +130,20 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<NLPacket> packet,
}
}
void AvatarHashMap::processAvatarBillboardPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
QUuid sessionUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
void AvatarHashMap::processAvatarBillboardPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
QUuid sessionUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
auto avatar = newOrExistingAvatar(sessionUUID, sendingNode);
QByteArray billboard = packet->read(packet->bytesLeftToRead());
QByteArray billboard = message->read(message->getBytesLeftToRead());
if (avatar->getBillboard() != billboard) {
avatar->setBillboard(billboard);
}
}
void AvatarHashMap::processKillAvatar(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
void AvatarHashMap::processKillAvatar(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
// read the node id
QUuid sessionUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
QUuid sessionUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
removeAvatar(sessionUUID);
}

View file

@ -45,10 +45,10 @@ public slots:
private slots:
void sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID);
void processAvatarDataPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processAvatarIdentityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processAvatarBillboardPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processKillAvatar(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
void processAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
void processAvatarBillboardPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
void processKillAvatar(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
protected:
AvatarHashMap();

View file

@ -14,7 +14,6 @@
#include <QtCore/QTimer>
#include <QtOpenGL/QGLWidget>
#include <QtGui/QOpenGLContext>
#include <QtGui/QImage>
#include <gl/GLWidget.h>
@ -31,6 +30,12 @@ class PresentThread : public QThread, public Dependency {
using Lock = std::unique_lock<Mutex>;
public:
PresentThread() {
connect(qApp, &QCoreApplication::aboutToQuit, [this]{
_shutdown = true;
});
}
~PresentThread() {
_shutdown = true;
wait();
@ -46,7 +51,6 @@ public:
// Extra code because of the widget 'wrapper' context
_context = context;
_context->moveToThread(this);
_context->contextHandle()->moveToThread(this);
}
virtual void run() override {
@ -57,7 +61,6 @@ public:
Lock lock(_mutex);
// Move the context to the main thread
_context->moveToThread(qApp->thread());
_context->contextHandle()->moveToThread(qApp->thread());
_pendingMainThreadOperation = false;
// Release the main thread to do it's action
_condition.notify_one();
@ -102,9 +105,12 @@ public:
_context->doneCurrent();
}
_context->makeCurrent();
if (_activePlugin) {
_activePlugin->uncustomizeContext();
}
_context->doneCurrent();
_context->moveToThread(qApp->thread());
_context->contextHandle()->moveToThread(qApp->thread());
}
void withMainThreadContext(std::function<void()> f) {
@ -120,7 +126,6 @@ public:
// Move the context back to the presentation thread
_context->moveToThread(this);
_context->contextHandle()->moveToThread(this);
// restore control of the context to the presentation thread and signal
// the end of the operation
@ -157,7 +162,14 @@ OpenGLDisplayPlugin::OpenGLDisplayPlugin() {
});
connect(&_timer, &QTimer::timeout, this, [&] {
#ifdef Q_OS_MAC
// On Mac, QT thread timing is such that we can miss one or even two cycles quite often, giving a render rate (including update/simulate)
// far lower than what we want. This hack keeps that rate more natural, at the expense of some wasted rendering.
// This is likely to be mooted by further planned changes.
if (_active && _sceneTextureEscrow.depth() <= 1) {
#else
if (_active && _sceneTextureEscrow.depth() < 1) {
#endif
emit requestRender();
}
});

View file

@ -408,8 +408,8 @@ int EntityTreeRenderer::getBoundaryLevelAdjust() const {
}
void EntityTreeRenderer::processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode) {
std::static_pointer_cast<EntityTree>(_tree)->processEraseMessage(packet, sourceNode);
void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
std::static_pointer_cast<EntityTree>(_tree)->processEraseMessage(message, sourceNode);
}
Model* EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl) {

View file

@ -49,7 +49,7 @@ public:
EntityTreePointer getTree() { return std::static_pointer_cast<EntityTree>(_tree); }
void processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode);
void processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode);
virtual void init();

View file

@ -41,6 +41,7 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
Q_ASSERT(getType() == EntityTypes::Box);
Q_ASSERT(args->_batch);
if (!_procedural) {
_procedural.reset(new Procedural(this->getUserData()));
_procedural->_vertexSource = simple_vert;
@ -64,4 +65,6 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
} else {
DependencyManager::get<DeferredLightingEffect>()->renderSolidCubeInstance(batch, getTransformToCenter(), cubeColor);
}
};
static const auto triCount = DependencyManager::get<GeometryCache>()->getCubeTriangleCount();
args->_details._trianglesRendered += triCount;
}

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