mirror of
https://github.com/lubosz/overte.git
synced 2025-04-19 17:03:43 +02:00
Merge branch 'fix-sync-period-calc' into correct-target-frame-rate
This commit is contained in:
commit
9ae22800fe
197 changed files with 4953 additions and 1863 deletions
|
@ -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 ()
|
||||
|
|
|
@ -11,3 +11,4 @@ link_hifi_libraries(
|
|||
|
||||
include_application_version()
|
||||
package_libraries_for_deployment()
|
||||
consolidate_stack_components()
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
class AssignmentFactory {
|
||||
public:
|
||||
static ThreadedAssignment* unpackAssignment(NLPacket& packet);
|
||||
static ThreadedAssignment* unpackAssignment(ReceivedMessage& message);
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentFactory_h
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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() &&
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
27
cmake/macros/ConsolidateStackComponents.cmake
Normal file
27
cmake/macros/ConsolidateStackComponents.cmake
Normal 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()
|
|
@ -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 ()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# CopyDllsBesideWindowsExecutable.cmake
|
||||
# PackageLibrariesForDeployment.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
|
@ -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)
|
||||
|
|
|
@ -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@")
|
||||
|
|
|
@ -38,3 +38,4 @@ endif (UNIX)
|
|||
|
||||
include_application_version()
|
||||
package_libraries_for_deployment()
|
||||
consolidate_stack_components()
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
105
examples/baseball/assets.json
Normal file
105
examples/baseball/assets.json
Normal 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/"
|
||||
}
|
||||
}
|
||||
}
|
43
examples/baseball/baseballCrowd.js
Normal file
43
examples/baseball/baseballCrowd.js
Normal 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
43
examples/baseball/bat.js
Normal 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;
|
||||
};
|
||||
});
|
76
examples/baseball/createBatButton.js
Normal file
76
examples/baseball/createBatButton.js
Normal 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)
|
||||
});
|
||||
};
|
||||
});
|
138
examples/baseball/firework.js
Normal file
138
examples/baseball/firework.js
Normal 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
165
examples/baseball/line.js
Normal 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;
|
||||
};
|
554
examples/baseball/pitching.js
Normal file
554
examples/baseball/pitching.js
Normal 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);
|
||||
});
|
92
examples/baseball/utils.js
Normal file
92
examples/baseball/utils.js
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
226
examples/data_visualization/earthquakes_live.js
Normal file
226
examples/data_visualization/earthquakes_live.js
Normal 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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
1155
examples/libraries/tinyColor.js
Normal file
1155
examples/libraries/tinyColor.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -49,6 +49,7 @@ private slots:
|
|||
void openFullAvatarModelBrowser();
|
||||
void openSnapshotLocationBrowser();
|
||||
void openScriptsLocationBrowser();
|
||||
void changeFullAvatarURL();
|
||||
void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
|
||||
};
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue