merge upstream/master into avatar-interaction

This commit is contained in:
Andrew Meadows 2014-04-01 12:53:47 -07:00
commit b65855a384
48 changed files with 898 additions and 458 deletions

View file

@ -3,7 +3,7 @@ Dependencies
* [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 2.8.11 * [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 2.8.11
* [Qt](http://qt-project.org/downloads) ~> 5.2.0 * [Qt](http://qt-project.org/downloads) ~> 5.2.0
* [zLib](http://www.zlib.net/) ~> 1.2.8 * [zLib](http://www.zlib.net/) ~> 1.2.8
* [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.0 * [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.2
* [qxmpp](https://code.google.com/p/qxmpp/) ~> 0.7.6 * [qxmpp](https://code.google.com/p/qxmpp/) ~> 0.7.6
#####Linux only #####Linux only
@ -142,4 +142,4 @@ If you need to debug Interface, you can run interface from within Visual Studio
####Debugging Interface ####Debugging Interface
* In the Solution Explorer, right click interface and click Set as StartUp Project * In the Solution Explorer, right click interface and click Set as StartUp Project
* Set the "Working Directory" for the Interface debugging sessions to the Debug output directory so that your application can load resources. Do this: right click interface and click Properties, choose Debugging from Configuration Properties, set Working Directory to .\Debug * Set the "Working Directory" for the Interface debugging sessions to the Debug output directory so that your application can load resources. Do this: right click interface and click Properties, choose Debugging from Configuration Properties, set Working Directory to .\Debug
* Now you can run and debug interface through Visual Studio * Now you can run and debug interface through Visual Studio

View file

@ -23,7 +23,7 @@ Running Interface
When you launch interface, you will automatically connect to our default domain: "root.highfidelity.io". When you launch interface, you will automatically connect to our default domain: "root.highfidelity.io".
If you don't see anything, make sure your preferences are pointing to If you don't see anything, make sure your preferences are pointing to
root.highfidelity.io, if you still have no luck it's possible our servers are root.highfidelity.io (set your domain via Cmnd+D/Cntrl+D), if you still have no luck it's possible our servers are
simply down; if you're experiencing a major bug, let us know by adding an issue to this repository. simply down; if you're experiencing a major bug, let us know by adding an issue to this repository.
Make sure to include details about your computer and how to reproduce the bug. Make sure to include details about your computer and how to reproduce the bug.

View file

@ -138,7 +138,11 @@ void Agent::run() {
ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
NodeList* nodeList = NodeList::getInstance(); NodeList* nodeList = NodeList::getInstance();
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer); nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet()
<< NodeType::AudioMixer
<< NodeType::AvatarMixer
<< NodeType::VoxelServer
<< NodeType::ParticleServer);
// figure out the URL for the script for this agent assignment // figure out the URL for the script for this agent assignment
QString scriptURLString("http://%1:8080/assignment/%2"); QString scriptURLString("http://%1:8080/assignment/%2");

View file

@ -18,6 +18,7 @@
#include <SharedUtil.h> #include <SharedUtil.h>
#include "AssignmentFactory.h" #include "AssignmentFactory.h"
#include "AssignmentThread.h"
#include "AssignmentClient.h" #include "AssignmentClient.h"
@ -28,7 +29,7 @@ int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
AssignmentClient::AssignmentClient(int &argc, char **argv) : AssignmentClient::AssignmentClient(int &argc, char **argv) :
QCoreApplication(argc, argv), QCoreApplication(argc, argv),
_currentAssignment(NULL) _currentAssignment()
{ {
setOrganizationName("High Fidelity"); setOrganizationName("High Fidelity");
setOrganizationDomain("highfidelity.io"); setOrganizationDomain("highfidelity.io");
@ -124,7 +125,7 @@ void AssignmentClient::readPendingDatagrams() {
if (nodeList->packetVersionAndHashMatch(receivedPacket)) { if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) { if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) {
// construct the deployed assignment from the packet data // construct the deployed assignment from the packet data
_currentAssignment = AssignmentFactory::unpackAssignment(receivedPacket); _currentAssignment = SharedAssignmentPointer(AssignmentFactory::unpackAssignment(receivedPacket));
if (_currentAssignment) { if (_currentAssignment) {
qDebug() << "Received an assignment -" << *_currentAssignment; qDebug() << "Received an assignment -" << *_currentAssignment;
@ -137,14 +138,13 @@ void AssignmentClient::readPendingDatagrams() {
qDebug() << "Destination IP for assignment is" << nodeList->getDomainInfo().getIP().toString(); qDebug() << "Destination IP for assignment is" << nodeList->getDomainInfo().getIP().toString();
// start the deployed assignment // start the deployed assignment
QThread* workerThread = new QThread(this); AssignmentThread* workerThread = new AssignmentThread(_currentAssignment, this);
connect(workerThread, SIGNAL(started()), _currentAssignment, SLOT(run())); connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run);
connect(_currentAssignment.data(), &ThreadedAssignment::finished, workerThread, &QThread::quit);
connect(_currentAssignment, SIGNAL(finished()), this, SLOT(assignmentCompleted())); connect(_currentAssignment.data(), &ThreadedAssignment::finished,
connect(_currentAssignment, SIGNAL(finished()), workerThread, SLOT(quit())); this, &AssignmentClient::assignmentCompleted);
connect(_currentAssignment, SIGNAL(finished()), _currentAssignment, SLOT(deleteLater())); connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
_currentAssignment->moveToThread(workerThread); _currentAssignment->moveToThread(workerThread);
@ -153,7 +153,7 @@ void AssignmentClient::readPendingDatagrams() {
// let the assignment handle the incoming datagrams for its duration // let the assignment handle the incoming datagrams for its duration
disconnect(&nodeList->getNodeSocket(), 0, this, 0); disconnect(&nodeList->getNodeSocket(), 0, this, 0);
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _currentAssignment, connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _currentAssignment.data(),
&ThreadedAssignment::readPendingDatagrams); &ThreadedAssignment::readPendingDatagrams);
// Starts an event loop, and emits workerThread->started() // Starts an event loop, and emits workerThread->started()
@ -202,10 +202,12 @@ void AssignmentClient::assignmentCompleted() {
NodeList* nodeList = NodeList::getInstance(); NodeList* nodeList = NodeList::getInstance();
// have us handle incoming NodeList datagrams again // have us handle incoming NodeList datagrams again
disconnect(&nodeList->getNodeSocket(), 0, _currentAssignment, 0); disconnect(&nodeList->getNodeSocket(), 0, _currentAssignment.data(), 0);
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams); connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams);
_currentAssignment = NULL; // clear our current assignment shared pointer now that we're done with it
// if the assignment thread is still around it has its own shared pointer to the assignment
_currentAssignment.clear();
// reset our NodeList by switching back to unassigned and clearing the list // reset our NodeList by switching back to unassigned and clearing the list
nodeList->setOwnerType(NodeType::Unassigned); nodeList->setOwnerType(NodeType::Unassigned);

View file

@ -24,7 +24,7 @@ private slots:
void handleAuthenticationRequest(); void handleAuthenticationRequest();
private: private:
Assignment _requestAssignment; Assignment _requestAssignment;
ThreadedAssignment* _currentAssignment; SharedAssignmentPointer _currentAssignment;
}; };
#endif /* defined(__hifi__AssignmentClient__) */ #endif /* defined(__hifi__AssignmentClient__) */

View file

@ -0,0 +1,16 @@
//
// AssignmentThread.cpp
// hifi
//
// Created by Stephen Birarda on 2014-03-28.
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
//
#include "AssignmentThread.h"
AssignmentThread::AssignmentThread(const SharedAssignmentPointer& assignment, QObject* parent) :
QThread(parent),
_assignment(assignment)
{
}

View file

@ -0,0 +1,23 @@
//
// AssignmentThread.h
// hifi
//
// Created by Stephen Birarda on 2014-03-28.
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
//
#ifndef __hifi__AssignmentThread__
#define __hifi__AssignmentThread__
#include <QtCore/QThread>
#include <ThreadedAssignment.h>
class AssignmentThread : public QThread {
public:
AssignmentThread(const SharedAssignmentPointer& assignment, QObject* parent);
private:
SharedAssignmentPointer _assignment;
};
#endif /* defined(__hifi__AssignmentThread__) */

View file

@ -21,8 +21,9 @@ quint64 endSceneSleepTime = 0;
OctreeSendThread::OctreeSendThread(OctreeServer* myServer, SharedNodePointer node) : OctreeSendThread::OctreeSendThread(OctreeServer* myServer, SharedNodePointer node) :
_myServer(myServer), _myServer(myServer),
_node(node), _nodeUUID(node->getUUID()),
_packetData(), _packetData(),
_nodeMissingCount(0),
_processLock(), _processLock(),
_isShuttingDown(false) _isShuttingDown(false)
{ {
@ -43,46 +44,68 @@ OctreeSendThread::~OctreeSendThread() {
} }
qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client disconnected " qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client disconnected "
"- ending sending thread [" << this << "]"; "- ending sending thread [" << this << "]";
_node.clear();
OctreeServer::clientDisconnected(); OctreeServer::clientDisconnected();
} }
void OctreeSendThread::setIsShuttingDown() { void OctreeSendThread::setIsShuttingDown() {
QMutexLocker locker(&_processLock); // this will cause us to wait till the process loop is complete
_isShuttingDown = true; _isShuttingDown = true;
OctreeServer::stopTrackingThread(this); OctreeServer::stopTrackingThread(this);
// this will cause us to wait till the process loop is complete, we do this after we change _isShuttingDown
QMutexLocker locker(&_processLock);
} }
bool OctreeSendThread::process() { bool OctreeSendThread::process() {
if (_isShuttingDown) {
return false; // exit early if we're shutting down
}
OctreeServer::didProcess(this); OctreeServer::didProcess(this);
float lockWaitElapsedUsec = OctreeServer::SKIP_TIME; float lockWaitElapsedUsec = OctreeServer::SKIP_TIME;
quint64 lockWaitStart = usecTimestampNow(); quint64 lockWaitStart = usecTimestampNow();
QMutexLocker locker(&_processLock); _processLock.lock();
quint64 lockWaitEnd = usecTimestampNow(); quint64 lockWaitEnd = usecTimestampNow();
lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart); lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart);
OctreeServer::trackProcessWaitTime(lockWaitElapsedUsec); OctreeServer::trackProcessWaitTime(lockWaitElapsedUsec);
if (_isShuttingDown) {
return false; // exit early if we're shutting down
}
quint64 start = usecTimestampNow(); quint64 start = usecTimestampNow();
// don't do any send processing until the initial load of the octree is complete... // don't do any send processing until the initial load of the octree is complete...
if (_myServer->isInitialLoadComplete()) { if (_myServer->isInitialLoadComplete()) {
if (!_node.isNull()) { SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(_nodeUUID, false);
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(_node->getLinkedData()); if (node) {
_nodeMissingCount = 0;
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
// Sometimes the node data has not yet been linked, in which case we can't really do anything // Sometimes the node data has not yet been linked, in which case we can't really do anything
if (nodeData && !nodeData->isShuttingDown()) { if (nodeData && !nodeData->isShuttingDown()) {
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
packetDistributor(_node, nodeData, viewFrustumChanged); packetDistributor(node, nodeData, viewFrustumChanged);
}
} else {
_nodeMissingCount++;
const int MANY_FAILED_LOCKS = 1;
if (_nodeMissingCount >= MANY_FAILED_LOCKS) {
QString safeServerName("Octree");
if (_myServer) {
safeServerName = _myServer->getMyServerName();
}
qDebug() << qPrintable(safeServerName) << "server: sending thread [" << this << "]"
<< "failed to get nodeWithUUID() " << _nodeUUID <<". Failed:" << _nodeMissingCount << "times";
} }
} }
} }
_processLock.unlock();
if (_isShuttingDown) {
return false; // exit early if we're shutting down
}
// Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap // Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap
if (isStillRunning()) { if (isStillRunning()) {
// dynamically sleep until we need to fire off the next set of octree elements // dynamically sleep until we need to fire off the next set of octree elements

View file

@ -38,14 +38,15 @@ protected:
virtual bool process(); virtual bool process();
private: private:
SharedNodePointer _node;
OctreeServer* _myServer; OctreeServer* _myServer;
QUuid _nodeUUID;
int handlePacketSend(const SharedNodePointer& node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent); int handlePacketSend(const SharedNodePointer& node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent);
int packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged); int packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged);
OctreePacketData _packetData; OctreePacketData _packetData;
int _nodeMissingCount;
QMutex _processLock; // don't allow us to have our nodeData, or our thread to be deleted while we're processing QMutex _processLock; // don't allow us to have our nodeData, or our thread to be deleted while we're processing
bool _isShuttingDown; bool _isShuttingDown;
}; };

View file

@ -1160,7 +1160,6 @@ QString OctreeServer::getStatusLink() {
} }
void OctreeServer::sendStatsPacket() { void OctreeServer::sendStatsPacket() {
// TODO: we have too many stats to fit in a single MTU... so for now, we break it into multiple JSON objects and // TODO: we have too many stats to fit in a single MTU... so for now, we break it into multiple JSON objects and
// send them separately. What we really should do is change the NodeList::sendStatsToDomainServer() to handle the // send them separately. What we really should do is change the NodeList::sendStatsToDomainServer() to handle the
// the following features: // the following features:
@ -1241,59 +1240,78 @@ QMap<OctreeSendThread*, quint64> OctreeServer::_threadsDidPacketDistributor;
QMap<OctreeSendThread*, quint64> OctreeServer::_threadsDidHandlePacketSend; QMap<OctreeSendThread*, quint64> OctreeServer::_threadsDidHandlePacketSend;
QMap<OctreeSendThread*, quint64> OctreeServer::_threadsDidCallWriteDatagram; QMap<OctreeSendThread*, quint64> OctreeServer::_threadsDidCallWriteDatagram;
QMutex OctreeServer::_threadsDidProcessMutex;
QMutex OctreeServer::_threadsDidPacketDistributorMutex;
QMutex OctreeServer::_threadsDidHandlePacketSendMutex;
QMutex OctreeServer::_threadsDidCallWriteDatagramMutex;
void OctreeServer::didProcess(OctreeSendThread* thread) { void OctreeServer::didProcess(OctreeSendThread* thread) {
QMutexLocker locker(&_threadsDidProcessMutex);
_threadsDidProcess[thread] = usecTimestampNow(); _threadsDidProcess[thread] = usecTimestampNow();
} }
void OctreeServer::didPacketDistributor(OctreeSendThread* thread) { void OctreeServer::didPacketDistributor(OctreeSendThread* thread) {
QMutexLocker locker(&_threadsDidPacketDistributorMutex);
_threadsDidPacketDistributor[thread] = usecTimestampNow(); _threadsDidPacketDistributor[thread] = usecTimestampNow();
} }
void OctreeServer::didHandlePacketSend(OctreeSendThread* thread) { void OctreeServer::didHandlePacketSend(OctreeSendThread* thread) {
QMutexLocker locker(&_threadsDidHandlePacketSendMutex);
_threadsDidHandlePacketSend[thread] = usecTimestampNow(); _threadsDidHandlePacketSend[thread] = usecTimestampNow();
} }
void OctreeServer::didCallWriteDatagram(OctreeSendThread* thread) { void OctreeServer::didCallWriteDatagram(OctreeSendThread* thread) {
QMutexLocker locker(&_threadsDidCallWriteDatagramMutex);
_threadsDidCallWriteDatagram[thread] = usecTimestampNow(); _threadsDidCallWriteDatagram[thread] = usecTimestampNow();
} }
void OctreeServer::stopTrackingThread(OctreeSendThread* thread) { void OctreeServer::stopTrackingThread(OctreeSendThread* thread) {
QMutexLocker lockerA(&_threadsDidProcessMutex);
QMutexLocker lockerB(&_threadsDidPacketDistributorMutex);
QMutexLocker lockerC(&_threadsDidHandlePacketSendMutex);
QMutexLocker lockerD(&_threadsDidCallWriteDatagramMutex);
_threadsDidProcess.remove(thread); _threadsDidProcess.remove(thread);
_threadsDidPacketDistributor.remove(thread); _threadsDidPacketDistributor.remove(thread);
_threadsDidHandlePacketSend.remove(thread); _threadsDidHandlePacketSend.remove(thread);
_threadsDidCallWriteDatagram.remove(thread);
} }
int howManyThreadsDidSomething(QMap<OctreeSendThread*, quint64>& something, quint64 since) { int howManyThreadsDidSomething(QMutex& mutex, QMap<OctreeSendThread*, quint64>& something, quint64 since) {
if (since == 0) {
return something.size();
}
int count = 0; int count = 0;
QMap<OctreeSendThread*, quint64>::const_iterator i = something.constBegin(); if (mutex.tryLock()) {
while (i != something.constEnd()) { if (since == 0) {
if (i.value() > since) { count = something.size();
count++; } else {
QMap<OctreeSendThread*, quint64>::const_iterator i = something.constBegin();
while (i != something.constEnd()) {
if (i.value() > since) {
count++;
}
++i;
}
} }
++i; mutex.unlock();
} }
return count; return count;
} }
int OctreeServer::howManyThreadsDidProcess(quint64 since) { int OctreeServer::howManyThreadsDidProcess(quint64 since) {
return howManyThreadsDidSomething(_threadsDidProcess, since); return howManyThreadsDidSomething(_threadsDidProcessMutex, _threadsDidProcess, since);
} }
int OctreeServer::howManyThreadsDidPacketDistributor(quint64 since) { int OctreeServer::howManyThreadsDidPacketDistributor(quint64 since) {
return howManyThreadsDidSomething(_threadsDidPacketDistributor, since); return howManyThreadsDidSomething(_threadsDidPacketDistributorMutex, _threadsDidPacketDistributor, since);
} }
int OctreeServer::howManyThreadsDidHandlePacketSend(quint64 since) { int OctreeServer::howManyThreadsDidHandlePacketSend(quint64 since) {
return howManyThreadsDidSomething(_threadsDidHandlePacketSend, since); return howManyThreadsDidSomething(_threadsDidHandlePacketSendMutex, _threadsDidHandlePacketSend, since);
} }
int OctreeServer::howManyThreadsDidCallWriteDatagram(quint64 since) { int OctreeServer::howManyThreadsDidCallWriteDatagram(quint64 since) {
return howManyThreadsDidSomething(_threadsDidCallWriteDatagram, since); return howManyThreadsDidSomething(_threadsDidCallWriteDatagramMutex, _threadsDidCallWriteDatagram, since);
} }

View file

@ -211,6 +211,10 @@ protected:
static QMap<OctreeSendThread*, quint64> _threadsDidHandlePacketSend; static QMap<OctreeSendThread*, quint64> _threadsDidHandlePacketSend;
static QMap<OctreeSendThread*, quint64> _threadsDidCallWriteDatagram; static QMap<OctreeSendThread*, quint64> _threadsDidCallWriteDatagram;
static QMutex _threadsDidProcessMutex;
static QMutex _threadsDidPacketDistributorMutex;
static QMutex _threadsDidHandlePacketSendMutex;
static QMutex _threadsDidCallWriteDatagramMutex;
}; };
#endif // __octree_server__OctreeServer__ #endif // __octree_server__OctreeServer__

View file

@ -23,10 +23,10 @@ function printVector(string, vector) {
} }
var CHANCE_OF_MOVING = 0.005; var CHANCE_OF_MOVING = 0.005;
var CHANCE_OF_SOUND = 0.005; var CHANCE_OF_SOUND = 0.000;
var CHANCE_OF_HEAD_TURNING = 0.05; var CHANCE_OF_HEAD_TURNING = 0.05;
var CHANCE_OF_BIG_MOVE = 0.1; var CHANCE_OF_BIG_MOVE = 0.1;
var CHANCE_OF_WAVING = 0.005; // Currently this isn't working var CHANCE_OF_WAVING = 0.009;
var shouldReceiveVoxels = true; var shouldReceiveVoxels = true;
var VOXEL_FPS = 60.0; var VOXEL_FPS = 60.0;
@ -39,12 +39,16 @@ var isWaving = false;
var waveFrequency = 0.0; var waveFrequency = 0.0;
var waveAmplitude = 0.0; var waveAmplitude = 0.0;
var X_MIN = 0.0; var X_MIN = 20.0;
var X_MAX = 5.0; var X_MAX = 25.0;
var Z_MIN = 0.0; var Z_MIN = 20.0;
var Z_MAX = 5.0; var Z_MAX = 25.0;
var Y_PELVIS = 2.5; var Y_PELVIS = 2.5;
var SHOULDER_JOINT_NUMBER = 15; var SPINE_JOINT_NUMBER = 13;
var SHOULDER_JOINT_NUMBER = 17;
var ELBOW_JOINT_NUMBER = 18;
var JOINT_R_HIP = 1;
var JOINT_R_KNEE = 2;
var MOVE_RANGE_SMALL = 0.5; var MOVE_RANGE_SMALL = 0.5;
var MOVE_RANGE_BIG = Math.max(X_MAX - X_MIN, Z_MAX - Z_MIN) / 2.0; var MOVE_RANGE_BIG = Math.max(X_MAX - X_MIN, Z_MAX - Z_MIN) / 2.0;
@ -61,6 +65,9 @@ var targetDirection = { x: 0, y: 0, z: 0, w: 0 };
var currentDirection = { x: 0, y: 0, z: 0, w: 0 }; var currentDirection = { x: 0, y: 0, z: 0, w: 0 };
var targetHeadPitch = 0.0; var targetHeadPitch = 0.0;
var walkFrequency = 5.0;
var walkAmplitude = 45.0;
var cumulativeTime = 0.0; var cumulativeTime = 0.0;
var sounds = []; var sounds = [];
@ -115,12 +122,30 @@ printVector("New bot, position = ", Avatar.position);
function stopWaving() { function stopWaving() {
isWaving = false; isWaving = false;
Avatar.clearJointData(SHOULDER_JOINT_NUMBER); Avatar.clearJointData(SHOULDER_JOINT_NUMBER);
Avatar.clearJointData(ELBOW_JOINT_NUMBER);
Avatar.clearJointData(SPINE_JOINT_NUMBER);
}
function keepWalking() {
Avatar.setJointData(JOINT_R_HIP, Quat.fromPitchYawRollDegrees(walkAmplitude * Math.sin(cumulativeTime * walkFrequency), 0.0, 0.0));
Avatar.setJointData(JOINT_R_KNEE, Quat.fromPitchYawRollDegrees(walkAmplitude * Math.sin(cumulativeTime * walkFrequency), 0.0, 0.0));
}
function stopWalking() {
Avatar.clearJointData(JOINT_R_HIP);
Avatar.clearJointData(JOINT_R_KNEE);
} }
function updateBehavior(deltaTime) { function updateBehavior(deltaTime) {
cumulativeTime += deltaTime; cumulativeTime += deltaTime;
// Hack - right now you need to set the avatar position a bit after the avatar is made to make sure it's there.
if (CHANCE_OF_MOVING == 0.000) {
Avatar.position = firstPosition;
}
if (shouldReceiveVoxels && ((cumulativeTime - lastVoxelQueryTime) > (1.0 / VOXEL_FPS))) { if (shouldReceiveVoxels && ((cumulativeTime - lastVoxelQueryTime) > (1.0 / VOXEL_FPS))) {
VoxelViewer.setPosition(Avatar.position); VoxelViewer.setPosition(Avatar.position);
VoxelViewer.setOrientation(Avatar.orientation); VoxelViewer.setOrientation(Avatar.orientation);
@ -134,13 +159,18 @@ function updateBehavior(deltaTime) {
if (!isWaving && (Math.random() < CHANCE_OF_WAVING)) { if (!isWaving && (Math.random() < CHANCE_OF_WAVING)) {
isWaving = true; isWaving = true;
waveFrequency = 1.0 + Math.random() * 5.0; waveFrequency = 3.0 + Math.random() * 5.0;
waveAmplitude = 5.0 + Math.random() * 60.0; waveAmplitude = 5.0 + Math.random() * 60.0;
Script.setTimeout(stopWaving, 1000 + Math.random() * 2000); Script.setTimeout(stopWaving, 1000 + Math.random() * 2000);
Avatar.setJointData(ELBOW_JOINT_NUMBER, Quat.fromPitchYawRollDegrees(0.0, 45, 0.0)); // Initially turn the palm outward
} else if (isWaving) { } else if (isWaving) {
Avatar.setJointData(SHOULDER_JOINT_NUMBER, Quat.fromPitchYawRollDegrees(0.0, 0.0, waveAmplitude * Math.sin(cumulativeTime * waveFrequency))); Avatar.setJointData(SHOULDER_JOINT_NUMBER, Quat.fromPitchYawRollDegrees(0.0, 0.0, 60 + waveAmplitude * Math.sin((cumulativeTime - 0.25) * waveFrequency)));
Avatar.setJointData(ELBOW_JOINT_NUMBER, Quat.fromPitchYawRollDegrees(0.0, 0.0, 25 + waveAmplitude/2.0 * Math.sin(cumulativeTime * 1.2 * waveFrequency)));
Avatar.setJointData(SPINE_JOINT_NUMBER, Quat.fromPitchYawRollDegrees(0.0, 0.0, 60 + waveAmplitude/4.0 * Math.sin(cumulativeTime * waveFrequency)));
} }
if (Math.random() < CHANCE_OF_SOUND) { if (Math.random() < CHANCE_OF_SOUND) {
playRandomSound(); playRandomSound();
} }
@ -168,11 +198,13 @@ function updateBehavior(deltaTime) {
targetPosition.y = Y_PELVIS; targetPosition.y = Y_PELVIS;
isMoving = true; isMoving = true;
} else { } else if (isMoving) {
keepWalking();
Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(Vec3.subtract(targetPosition, Avatar.position), MOVE_RATE)); Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(Vec3.subtract(targetPosition, Avatar.position), MOVE_RATE));
Avatar.orientation = Quat.mix(Avatar.orientation, targetDirection, TURN_RATE); Avatar.orientation = Quat.mix(Avatar.orientation, targetDirection, TURN_RATE);
if (Vec3.length(Vec3.subtract(Avatar.position, targetPosition)) < STOP_TOLERANCE) { if (Vec3.length(Vec3.subtract(Avatar.position, targetPosition)) < STOP_TOLERANCE) {
isMoving = false; isMoving = false;
stopWalking();
} }
} }
} }

View file

@ -1326,7 +1326,7 @@ function wheelEvent(event) {
} }
} }
Controller.wheelEvent.connect(wheelEvent); // Controller.wheelEvent.connect(wheelEvent);
Controller.mousePressEvent.connect(mousePressEvent); Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mouseMoveEvent.connect(mouseMoveEvent);

View file

@ -27,6 +27,9 @@ var BULLET_VELOCITY = 5.0;
var MIN_THROWER_DELAY = 1000; var MIN_THROWER_DELAY = 1000;
var MAX_THROWER_DELAY = 1000; var MAX_THROWER_DELAY = 1000;
var LEFT_BUTTON_3 = 3; var LEFT_BUTTON_3 = 3;
var RELOAD_INTERVAL = 9;
var showScore = false;
// Load some sound to use for loading and firing // Load some sound to use for loading and firing
var fireSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guns/GUN-SHOT2.raw"); var fireSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guns/GUN-SHOT2.raw");
@ -38,6 +41,8 @@ var targetLaunchSound = new Sound("http://highfidelity-public.s3-us-west-1.amazo
var audioOptions = new AudioInjectionOptions(); var audioOptions = new AudioInjectionOptions();
audioOptions.volume = 0.9; audioOptions.volume = 0.9;
var shotsFired = 0;
var shotTime = new Date(); var shotTime = new Date();
// initialize our triggers // initialize our triggers
@ -63,7 +68,8 @@ var reticle = Overlays.addOverlay("image", {
alpha: 1 alpha: 1
}); });
var text = Overlays.addOverlay("text", { if (showScore) {
var text = Overlays.addOverlay("text", {
x: screenSize.x / 2 - 100, x: screenSize.x / 2 - 100,
y: screenSize.y / 2 - 50, y: screenSize.y / 2 - 50,
width: 150, width: 150,
@ -74,6 +80,8 @@ var text = Overlays.addOverlay("text", {
leftMargin: 4, leftMargin: 4,
text: "Score: " + score text: "Score: " + score
}); });
}
function printVector(string, vector) { function printVector(string, vector) {
@ -94,6 +102,10 @@ function shootBullet(position, velocity) {
// Play firing sounds // Play firing sounds
audioOptions.position = position; audioOptions.position = position;
Audio.playSound(fireSound, audioOptions); Audio.playSound(fireSound, audioOptions);
shotsFired++;
if ((shotsFired % RELOAD_INTERVAL) == 0) {
Audio.playSound(loadSound, audioOptions);
}
} }
function shootTarget() { function shootTarget() {
@ -147,12 +159,15 @@ function particleCollisionWithVoxel(particle, voxel, penetration) {
Voxels.eraseVoxel(position.x, position.y, position.z, HOLE_SIZE); Voxels.eraseVoxel(position.x, position.y, position.z, HOLE_SIZE);
//audioOptions.position = position; //audioOptions.position = position;
audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
Audio.playSound(targetHitSound, audioOptions); Audio.playSound(impactSound, audioOptions);
} }
function particleCollisionWithParticle(particle1, particle2) { function particleCollisionWithParticle(particle1, particle2) {
score++; score++;
Overlays.editOverlay(text, { text: "Score: " + score } ); if (showScore) {
Overlays.editOverlay(text, { text: "Score: " + score } );
}
// Sort out which particle is which // Sort out which particle is which
// Record shot time // Record shot time
@ -171,12 +186,12 @@ function keyPressEvent(event) {
if (event.text == "t") { if (event.text == "t") {
var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY;
Script.setTimeout(shootTarget, time); Script.setTimeout(shootTarget, time);
} if (event.text == ".") {
shootFromMouse();
} }
} }
function update(deltaTime) { function update(deltaTime) {
// Check for mouseLook movement, update rotation // Check for mouseLook movement, update rotation
// rotate body yaw for yaw received from mouse // rotate body yaw for yaw received from mouse
var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Radians( { x: 0, y: yawFromMouse, z: 0 } )); var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Radians( { x: 0, y: yawFromMouse, z: 0 } ));
@ -257,18 +272,21 @@ function mousePressEvent(event) {
isMouseDown = true; isMouseDown = true;
lastX = event.x; lastX = event.x;
lastY = event.y; lastY = event.y;
audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); //audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
Audio.playSound(loadSound, audioOptions); //Audio.playSound(loadSound, audioOptions);
} }
function mouseReleaseEvent(event) { function shootFromMouse() {
// position
var DISTANCE_FROM_CAMERA = 2.0; var DISTANCE_FROM_CAMERA = 2.0;
var camera = Camera.getPosition(); var camera = Camera.getPosition();
var forwardVector = Quat.getFront(Camera.getOrientation()); var forwardVector = Quat.getFront(Camera.getOrientation());
var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA)); var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA));
var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY); var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY);
shootBullet(newPosition, velocity); shootBullet(newPosition, velocity);
}
function mouseReleaseEvent(event) {
// position
isMouseDown = false; isMouseDown = false;
} }

View file

@ -162,11 +162,11 @@ function flyWithHydra(deltaTime) {
if (thrustMultiplier < MAX_THRUST_MULTIPLIER) { if (thrustMultiplier < MAX_THRUST_MULTIPLIER) {
thrustMultiplier *= 1 + (deltaTime * THRUST_INCREASE_RATE); thrustMultiplier *= 1 + (deltaTime * THRUST_INCREASE_RATE);
} }
var currentOrientation = MyAvatar.orientation; var headOrientation = MyAvatar.headOrientation;
var front = Quat.getFront(currentOrientation); var front = Quat.getFront(headOrientation);
var right = Quat.getRight(currentOrientation); var right = Quat.getRight(headOrientation);
var up = Quat.getUp(currentOrientation); var up = Quat.getUp(headOrientation);
var thrustFront = Vec3.multiply(front, MyAvatar.scale * THRUST_MAG_HAND_JETS * var thrustFront = Vec3.multiply(front, MyAvatar.scale * THRUST_MAG_HAND_JETS *
thrustJoystickPosition.y * thrustMultiplier * deltaTime); thrustJoystickPosition.y * thrustMultiplier * deltaTime);

View file

@ -4,22 +4,22 @@
<context> <context>
<name>Application</name> <name>Application</name>
<message> <message>
<location filename="src/Application.cpp" line="1380"/> <location filename="src/Application.cpp" line="1382"/>
<source>Export Voxels</source> <source>Export Voxels</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="src/Application.cpp" line="1381"/> <location filename="src/Application.cpp" line="1383"/>
<source>Sparse Voxel Octree Files (*.svo)</source> <source>Sparse Voxel Octree Files (*.svo)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="src/Application.cpp" line="3608"/> <location filename="src/Application.cpp" line="3711"/>
<source>Open Script</source> <source>Open Script</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="src/Application.cpp" line="3609"/> <location filename="src/Application.cpp" line="3712"/>
<source>JavaScript Files (*.js)</source> <source>JavaScript Files (*.js)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -27,56 +27,49 @@
<context> <context>
<name>ChatWindow</name> <name>ChatWindow</name>
<message> <message>
<location filename="ui/chatWindow.ui" line="20"/> <location filename="ui/chatWindow.ui" line="29"/>
<location filename="../build/interface/ui_chatWindow.h" line="143"/> <location filename="../build/interface/ui_chatWindow.h" line="153"/>
<source>Chat</source> <source>Chat</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="ui/chatWindow.ui" line="50"/> <location filename="ui/chatWindow.ui" line="57"/>
<location filename="../build/interface/ui_chatWindow.h" line="144"/> <location filename="../build/interface/ui_chatWindow.h" line="154"/>
<source>Connecting to XMPP...</source> <source>Connecting to XMPP...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="ui/chatWindow.ui" line="71"/> <location filename="ui/chatWindow.ui" line="78"/>
<location filename="../build/interface/ui_chatWindow.h" line="145"/> <location filename="../build/interface/ui_chatWindow.h" line="155"/>
<source> online now:</source> <source> online now:</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message numerus="yes"> <message numerus="yes">
<location filename="src/ui/ChatWindow.cpp" line="128"/> <location filename="src/ui/ChatWindow.cpp" line="135"/>
<source>day</source> <source>day</source>
<translation> <translation type="unfinished">
<numerusform>%n day</numerusform> <numerusform></numerusform>
<numerusform>%n days</numerusform> <numerusform></numerusform>
</translation> </translation>
</message> </message>
<message numerus="yes"> <message numerus="yes">
<location filename="src/ui/ChatWindow.cpp" line="128"/> <location filename="src/ui/ChatWindow.cpp" line="135"/>
<source>hour</source> <source>hour</source>
<translation> <translation type="unfinished">
<numerusform>%n hour</numerusform> <numerusform></numerusform>
<numerusform>%n hours</numerusform> <numerusform></numerusform>
</translation> </translation>
</message> </message>
<message numerus="yes"> <message numerus="yes">
<location filename="src/ui/ChatWindow.cpp" line="128"/> <location filename="src/ui/ChatWindow.cpp" line="135"/>
<source>minute</source> <source>minute</source>
<translation> <translation type="unfinished">
<numerusform>%n minute</numerusform> <numerusform></numerusform>
<numerusform>%n minutes</numerusform> <numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>second</source>
<translation type="vanished">
<numerusform>%n second</numerusform>
<numerusform>%n seconds</numerusform>
</translation> </translation>
</message> </message>
<message> <message>
<location filename="src/ui/ChatWindow.cpp" line="183"/> <location filename="src/ui/ChatWindow.cpp" line="191"/>
<source>%1 online now:</source> <source>%1 online now:</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -113,18 +106,18 @@
<context> <context>
<name>Menu</name> <name>Menu</name>
<message> <message>
<location filename="src/Menu.cpp" line="460"/> <location filename="src/Menu.cpp" line="464"/>
<source>Open .ini config file</source> <source>Open .ini config file</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="src/Menu.cpp" line="462"/> <location filename="src/Menu.cpp" line="466"/>
<location filename="src/Menu.cpp" line="474"/> <location filename="src/Menu.cpp" line="478"/>
<source>Text files (*.ini)</source> <source>Text files (*.ini)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="src/Menu.cpp" line="472"/> <location filename="src/Menu.cpp" line="476"/>
<source>Save .ini config file</source> <source>Save .ini config file</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>

View file

@ -10,7 +10,8 @@
uniform sampler2DShadow shadowMap; uniform sampler2DShadow shadowMap;
varying vec4 shadowColor;
void main(void) { void main(void) {
gl_FragColor = gl_Color * mix(vec4(0.8, 0.8, 0.8, 1.0), vec4(1.0, 1.0, 1.0, 1.0), gl_FragColor = mix(shadowColor, gl_Color, shadow2D(shadowMap, gl_TexCoord[0].stp));
shadow2D(shadowMap, gl_TexCoord[0].stp));
} }

View file

@ -0,0 +1,28 @@
#version 120
//
// shadow_map.vert
// vertex shader
//
// Created by Andrzej Kapolka on 3/27/14.
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
//
varying vec4 shadowColor;
void main(void) {
// the shadow color includes only the ambient terms
shadowColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient);
// the normal color includes diffuse
vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0));
gl_FrontColor = shadowColor + gl_Color * (gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position)));
// generate the shadow texture coordinate using the eye position
vec4 eyePosition = gl_ModelViewMatrix * gl_Vertex;
gl_TexCoord[0] = vec4(dot(gl_EyePlaneS[0], eyePosition), dot(gl_EyePlaneT[0], eyePosition),
dot(gl_EyePlaneR[0], eyePosition), 1.0);
// use the fixed function transform
gl_Position = ftransform();
}

View file

@ -151,6 +151,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_lastQueriedViewFrustum(), _lastQueriedViewFrustum(),
_lastQueriedTime(usecTimestampNow()), _lastQueriedTime(usecTimestampNow()),
_audioScope(256, 200, true), _audioScope(256, 200, true),
_trailingAudioLoudness(0.f),
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
_mouseX(0), _mouseX(0),
_mouseY(0), _mouseY(0),
@ -169,6 +170,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_bytesPerSecond(0), _bytesPerSecond(0),
_recentMaxPackets(0), _recentMaxPackets(0),
_resetRecentMaxPacketsSoon(true), _resetRecentMaxPacketsSoon(true),
_previousScriptLocation(),
_logger(new FileLogger(this)) _logger(new FileLogger(this))
{ {
// read the ApplicationInfo.ini file for Name/Version/Domain information // read the ApplicationInfo.ini file for Name/Version/Domain information
@ -1636,6 +1638,8 @@ void Application::updateLOD() {
// adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD) && !isThrottleRendering()) { if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD) && !isThrottleRendering()) {
Menu::getInstance()->autoAdjustLOD(_fps); Menu::getInstance()->autoAdjustLOD(_fps);
} else {
Menu::getInstance()->resetLODAdjust();
} }
} }
@ -1842,15 +1846,6 @@ void Application::updateDialogs(float deltaTime) {
} }
} }
void Application::updateAudio(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateAudio()");
// Update audio stats for procedural sounds
_audio.setLastAcceleration(_myAvatar->getThrust());
_audio.setLastVelocity(_myAvatar->getVelocity());
}
void Application::updateCursor(float deltaTime) { void Application::updateCursor(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateCursor()"); PerformanceWarning warn(showWarnings, "Application::updateCursor()");
@ -1899,7 +1894,6 @@ void Application::update(float deltaTime) {
updateMetavoxels(deltaTime); // update metavoxels updateMetavoxels(deltaTime); // update metavoxels
updateCamera(deltaTime); // handle various camera tweaks like off axis projection updateCamera(deltaTime); // handle various camera tweaks like off axis projection
updateDialogs(deltaTime); // update various stats dialogs if present updateDialogs(deltaTime); // update various stats dialogs if present
updateAudio(deltaTime); // Update audio stats for procedural sounds
updateCursor(deltaTime); // Handle cursor updates updateCursor(deltaTime); // Handle cursor updates
_particles.update(); // update the particles... _particles.update(); // update the particles...
@ -2187,19 +2181,26 @@ void Application::updateShadowMap() {
(_viewFrustum.getFarClip() - _viewFrustum.getNearClip()); (_viewFrustum.getFarClip() - _viewFrustum.getNearClip());
loadViewFrustum(_myCamera, _viewFrustum); loadViewFrustum(_myCamera, _viewFrustum);
glm::vec3 points[] = { glm::vec3 points[] = {
inverseRotation * (glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), nearScale)), glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), nearScale),
inverseRotation * (glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), nearScale)), glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), nearScale),
inverseRotation * (glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), nearScale)), glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), nearScale),
inverseRotation * (glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), nearScale)), glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), nearScale),
inverseRotation * (glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), farScale)), glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), farScale),
inverseRotation * (glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), farScale)), glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), farScale),
inverseRotation * (glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), farScale)), glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), farScale),
inverseRotation * (glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), farScale)) }; glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), farScale) };
glm::vec3 minima(FLT_MAX, FLT_MAX, FLT_MAX), maxima(-FLT_MAX, -FLT_MAX, -FLT_MAX); glm::vec3 center;
for (size_t i = 0; i < sizeof(points) / sizeof(points[0]); i++) { for (size_t i = 0; i < sizeof(points) / sizeof(points[0]); i++) {
minima = glm::min(minima, points[i]); center += points[i];
maxima = glm::max(maxima, points[i]);
} }
center /= (float)(sizeof(points) / sizeof(points[0]));
float radius = 0.0f;
for (size_t i = 0; i < sizeof(points) / sizeof(points[0]); i++) {
radius = qMax(radius, glm::distance(points[i], center));
}
center = inverseRotation * center;
glm::vec3 minima(center.x - radius, center.y - radius, center.z - radius);
glm::vec3 maxima(center.x + radius, center.y + radius, center.z + radius);
// stretch out our extents in z so that we get all of the avatars // stretch out our extents in z so that we get all of the avatars
minima.z -= _viewFrustum.getFarClip() * 0.5f; minima.z -= _viewFrustum.getFarClip() * 0.5f;
@ -2494,14 +2495,103 @@ void Application::displayOverlay() {
renderCollisionOverlay(_glWidget->width(), _glWidget->height(), _audio.getCollisionSoundMagnitude()); renderCollisionOverlay(_glWidget->width(), _glWidget->height(), _audio.getCollisionSoundMagnitude());
} }
} }
// Audio Scope
const int AUDIO_SCOPE_Y_OFFSET = 135;
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
_audio.renderMuteIcon(1, _glWidget->height() - 50);
if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) { if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) {
int oscilloscopeTop = _glWidget->height() - 135; int oscilloscopeTop = _glWidget->height() - AUDIO_SCOPE_Y_OFFSET;
_audioScope.render(25, oscilloscopeTop); _audioScope.render(MIRROR_VIEW_LEFT_PADDING, oscilloscopeTop);
} }
} }
// Audio VU Meter and Mute Icon
const int MUTE_ICON_SIZE = 24;
const int AUDIO_METER_INSET = 2;
const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - AUDIO_METER_INSET;
const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 * AUDIO_METER_INSET;
const int AUDIO_METER_HEIGHT = 8;
const int AUDIO_METER_Y_GAP = 8;
const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_INSET;
int audioMeterY;
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_Y_GAP;
} else {
audioMeterY = AUDIO_METER_Y_GAP;
}
_audio.renderMuteIcon(MIRROR_VIEW_LEFT_PADDING, audioMeterY);
const float AUDIO_METER_BLUE[] = {0.0, 0.0, 1.0};
const float AUDIO_METER_GREEN[] = {0.0, 1.0, 0.0};
const float AUDIO_METER_RED[] = {1.0, 0.0, 0.0};
const float AUDIO_GREEN_START = 0.25 * AUDIO_METER_SCALE_WIDTH;
const float AUDIO_RED_START = 0.80 * AUDIO_METER_SCALE_WIDTH;
const float CLIPPING_INDICATOR_TIME = 1.0f;
const float AUDIO_METER_AVERAGING = 0.5;
const float LOG2 = log(2.f);
const float MAX_LOG2_SAMPLE = 15.f;
float audioLevel = 0.f;
float loudness = _audio.getLastInputLoudness() + 1.f;
_trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.f - AUDIO_METER_AVERAGING) * loudness;
float log2loudness = log(_trailingAudioLoudness) / LOG2;
audioLevel = log2loudness / MAX_LOG2_SAMPLE * AUDIO_METER_SCALE_WIDTH;
bool isClipping = ((_audio.getTimeSinceLastClip() > 0.f) && (_audio.getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME));
glBegin(GL_QUADS);
if (isClipping) {
glColor3f(1, 0, 0);
} else {
glColor3f(0, 0, 0);
}
// Draw audio meter background Quad
glVertex2i(AUDIO_METER_X, audioMeterY);
glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, audioMeterY);
glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, audioMeterY + AUDIO_METER_HEIGHT);
glVertex2i(AUDIO_METER_X, audioMeterY + AUDIO_METER_HEIGHT);
if (audioLevel > AUDIO_RED_START) {
if (!isClipping) {
glColor3fv(AUDIO_METER_RED);
} else {
glColor3f(1, 1, 1);
}
// Draw Red Quad
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
audioLevel = AUDIO_RED_START;
}
if (audioLevel > AUDIO_GREEN_START) {
if (!isClipping) {
glColor3fv(AUDIO_METER_GREEN);
} else {
glColor3f(1, 1, 1);
}
// Draw Green Quad
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
audioLevel = AUDIO_GREEN_START;
}
// Draw Blue Quad
if (!isClipping) {
glColor3fv(AUDIO_METER_BLUE);
} else {
glColor3f(1, 1, 1);
}
// Draw Blue (low level) quad
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
glEnd();
if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) { if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) {
_myAvatar->renderHeadMouse(); _myAvatar->renderHeadMouse();
@ -3523,13 +3613,21 @@ void Application::reloadAllScripts() {
} }
} }
void Application::uploadFST() { void Application::uploadFST(bool isHead) {
FstReader reader; FstReader reader(isHead);
if (reader.zip()) { if (reader.zip()) {
reader.send(); reader.send();
} }
} }
void Application::uploadHead() {
uploadFST(true);
}
void Application::uploadSkeleton() {
uploadFST(false);
}
void Application::removeScriptName(const QString& fileNameString) { void Application::removeScriptName(const QString& fileNameString) {
_activeScripts.removeOne(fileNameString); _activeScripts.removeOne(fileNameString);
} }
@ -3601,12 +3699,20 @@ void Application::loadScript(const QString& scriptName) {
} }
void Application::loadDialog() { void Application::loadDialog() {
// shut down and stop any existing script QString suggestedName;
QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
QString suggestedName = desktopLocation.append("/script.js"); if (_previousScriptLocation.isEmpty()) {
QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
suggestedName = desktopLocation.append("/script.js");
} else {
suggestedName = _previousScriptLocation;
}
QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"), suggestedName, QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"), suggestedName,
tr("JavaScript Files (*.js)")); tr("JavaScript Files (*.js)"));
if (!fileNameString.isEmpty()) {
_previousScriptLocation = fileNameString;
}
loadScript(fileNameString); loadScript(fileNameString);
} }

View file

@ -261,7 +261,9 @@ public slots:
void stopAllScripts(); void stopAllScripts();
void reloadAllScripts(); void reloadAllScripts();
void uploadFST(); void uploadFST(bool isHead);
void uploadHead();
void uploadSkeleton();
private slots: private slots:
void timer(); void timer();
@ -312,7 +314,6 @@ private:
void updateMetavoxels(float deltaTime); void updateMetavoxels(float deltaTime);
void updateCamera(float deltaTime); void updateCamera(float deltaTime);
void updateDialogs(float deltaTime); void updateDialogs(float deltaTime);
void updateAudio(float deltaTime);
void updateCursor(float deltaTime); void updateCursor(float deltaTime);
Avatar* findLookatTargetAvatar(glm::vec3& eyePosition, QUuid &nodeUUID); Avatar* findLookatTargetAvatar(glm::vec3& eyePosition, QUuid &nodeUUID);
@ -397,6 +398,7 @@ private:
quint64 _lastQueriedTime; quint64 _lastQueriedTime;
Oscilloscope _audioScope; Oscilloscope _audioScope;
float _trailingAudioLoudness;
OctreeQuery _octreeQuery; // NodeData derived class for querying voxels from voxel server OctreeQuery _octreeQuery; // NodeData derived class for querying voxels from voxel server
@ -483,6 +485,8 @@ private:
ControllerScriptingInterface _controllerScriptingInterface; ControllerScriptingInterface _controllerScriptingInterface;
QPointer<LogDialog> _logDialog; QPointer<LogDialog> _logDialog;
QString _previousScriptLocation;
FileLogger* _logger; FileLogger* _logger;
void checkVersion(); void checkVersion();

View file

@ -60,15 +60,16 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* p
_measuredJitter(0), _measuredJitter(0),
_jitterBufferSamples(initialJitterBufferSamples), _jitterBufferSamples(initialJitterBufferSamples),
_lastInputLoudness(0), _lastInputLoudness(0),
_timeSinceLastClip(-1.0),
_dcOffset(0), _dcOffset(0),
_noiseGateMeasuredFloor(0), _noiseGateMeasuredFloor(0),
_noiseGateSampleCounter(0), _noiseGateSampleCounter(0),
_noiseGateOpen(false), _noiseGateOpen(false),
_noiseGateEnabled(true), _noiseGateEnabled(true),
_toneInjectionEnabled(false),
_noiseGateFramesToClose(0), _noiseGateFramesToClose(0),
_lastVelocity(0),
_lastAcceleration(0),
_totalPacketsReceived(0), _totalPacketsReceived(0),
_totalInputAudioSamples(0),
_collisionSoundMagnitude(0.0f), _collisionSoundMagnitude(0.0f),
_collisionSoundFrequency(0.0f), _collisionSoundFrequency(0.0f),
_collisionSoundNoise(0.0f), _collisionSoundNoise(0.0f),
@ -391,7 +392,7 @@ void Audio::handleAudioInput() {
inputSamplesRequired, inputSamplesRequired,
NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL, NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL,
_inputFormat, _desiredInputFormat); _inputFormat, _desiredInputFormat);
// //
// Impose Noise Gate // Impose Noise Gate
// //
@ -420,13 +421,24 @@ void Audio::handleAudioInput() {
const int NOISE_GATE_CLOSE_FRAME_DELAY = 5; const int NOISE_GATE_CLOSE_FRAME_DELAY = 5;
const int NOISE_GATE_FRAMES_TO_AVERAGE = 5; const int NOISE_GATE_FRAMES_TO_AVERAGE = 5;
const float DC_OFFSET_AVERAGING = 0.99f; const float DC_OFFSET_AVERAGING = 0.99f;
const float CLIPPING_THRESHOLD = 0.90f;
//
// Check clipping, adjust DC offset, and check if should open noise gate
//
float measuredDcOffset = 0.f; float measuredDcOffset = 0.f;
// Increment the time since the last clip
if (_timeSinceLastClip >= 0.0f) {
_timeSinceLastClip += (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float) SAMPLE_RATE;
}
for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) { for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
measuredDcOffset += monoAudioSamples[i]; measuredDcOffset += monoAudioSamples[i];
monoAudioSamples[i] -= (int16_t) _dcOffset; monoAudioSamples[i] -= (int16_t) _dcOffset;
thisSample = fabsf(monoAudioSamples[i]); thisSample = fabsf(monoAudioSamples[i]);
if (thisSample > (32767.f * CLIPPING_THRESHOLD)) {
_timeSinceLastClip = 0.0f;
}
loudness += thisSample; loudness += thisSample;
// Noise Reduction: Count peaks above the average loudness // Noise Reduction: Count peaks above the average loudness
if (thisSample > (_noiseGateMeasuredFloor * NOISE_GATE_HEIGHT)) { if (thisSample > (_noiseGateMeasuredFloor * NOISE_GATE_HEIGHT)) {
@ -481,6 +493,16 @@ void Audio::handleAudioInput() {
_lastInputLoudness = 0; _lastInputLoudness = 0;
} }
} }
//
// Add tone injection if enabled
//
const float TONE_FREQ = 220.f / SAMPLE_RATE * TWO_PI;
const float QUARTER_VOLUME = 8192.f;
if (_toneInjectionEnabled) {
for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
monoAudioSamples[i] = QUARTER_VOLUME * sinf(TONE_FREQ * (float)(i + _proceduralEffectSample));
}
}
// add input data just written to the scope // add input data just written to the scope
QMetaObject::invokeMethod(_scope, "addSamples", Qt::QueuedConnection, QMetaObject::invokeMethod(_scope, "addSamples", Qt::QueuedConnection,
@ -675,7 +697,9 @@ void Audio::toggleAudioNoiseReduction() {
_noiseGateEnabled = !_noiseGateEnabled; _noiseGateEnabled = !_noiseGateEnabled;
} }
void Audio::toggleToneInjection() {
_toneInjectionEnabled = !_toneInjectionEnabled;
}
// Take a pointer to the acquired microphone input samples and add procedural sounds // Take a pointer to the acquired microphone input samples and add procedural sounds
void Audio::addProceduralSounds(int16_t* monoInput, int numSamples) { void Audio::addProceduralSounds(int16_t* monoInput, int numSamples) {

View file

@ -47,13 +47,11 @@ public:
Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* parent = 0); Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* parent = 0);
float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGateMeasuredFloor, 0.f); } float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGateMeasuredFloor, 0.f); }
float getTimeSinceLastClip() const { return _timeSinceLastClip; }
float getAudioAverageInputLoudness() const { return _lastInputLoudness; } float getAudioAverageInputLoudness() const { return _lastInputLoudness; }
void setNoiseGateEnabled(bool noiseGateEnabled) { _noiseGateEnabled = noiseGateEnabled; } void setNoiseGateEnabled(bool noiseGateEnabled) { _noiseGateEnabled = noiseGateEnabled; }
void setLastAcceleration(const glm::vec3 lastAcceleration) { _lastAcceleration = lastAcceleration; }
void setLastVelocity(const glm::vec3 lastVelocity) { _lastVelocity = lastVelocity; }
void setJitterBufferSamples(int samples) { _jitterBufferSamples = samples; } void setJitterBufferSamples(int samples) { _jitterBufferSamples = samples; }
int getJitterBufferSamples() { return _jitterBufferSamples; } int getJitterBufferSamples() { return _jitterBufferSamples; }
@ -83,6 +81,7 @@ public slots:
void reset(); void reset();
void toggleMute(); void toggleMute();
void toggleAudioNoiseReduction(); void toggleAudioNoiseReduction();
void toggleToneInjection();
virtual void handleAudioByteArray(const QByteArray& audioByteArray); virtual void handleAudioByteArray(const QByteArray& audioByteArray);
@ -130,16 +129,17 @@ private:
float _measuredJitter; float _measuredJitter;
int16_t _jitterBufferSamples; int16_t _jitterBufferSamples;
float _lastInputLoudness; float _lastInputLoudness;
float _timeSinceLastClip;
float _dcOffset; float _dcOffset;
float _noiseGateMeasuredFloor; float _noiseGateMeasuredFloor;
float* _noiseSampleFrames; float* _noiseSampleFrames;
int _noiseGateSampleCounter; int _noiseGateSampleCounter;
bool _noiseGateOpen; bool _noiseGateOpen;
bool _noiseGateEnabled; bool _noiseGateEnabled;
bool _toneInjectionEnabled;
int _noiseGateFramesToClose; int _noiseGateFramesToClose;
glm::vec3 _lastVelocity;
glm::vec3 _lastAcceleration;
int _totalPacketsReceived; int _totalPacketsReceived;
int _totalInputAudioSamples;
float _collisionSoundMagnitude; float _collisionSoundMagnitude;
float _collisionSoundFrequency; float _collisionSoundFrequency;

View file

@ -146,7 +146,8 @@ Menu::Menu() :
SLOT(goTo())); SLOT(goTo()));
addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model"); addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model");
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadFST, 0, Application::getInstance(), SLOT(uploadFST())); addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, Application::getInstance(), SLOT(uploadHead()));
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadSkeleton, 0, Application::getInstance(), SLOT(uploadSkeleton()));
addDisabledActionAndSeparator(fileMenu, "Settings"); addDisabledActionAndSeparator(fileMenu, "Settings");
addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings())); addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings()));
@ -241,7 +242,7 @@ Menu::Menu() :
addDisabledActionAndSeparator(viewMenu, "Stats"); addDisabledActionAndSeparator(viewMenu, "Stats");
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash);
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, appInstance, SLOT(toggleLogDialog())); addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, appInstance, SLOT(toggleLogDialog()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Oscilloscope, 0, true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Oscilloscope, 0, false);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true);
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails())); addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails()));
addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, this, SLOT(octreeStatsDetails())); addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, this, SLOT(octreeStatsDetails()));
@ -361,6 +362,12 @@ Menu::Menu() :
false, false,
appInstance->getAudio(), appInstance->getAudio(),
SLOT(toggleMute())); SLOT(toggleMute()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioToneInjection,
0,
false,
appInstance->getAudio(),
SLOT(toggleToneInjection()));
addActionToQMenuAndActionHash(developerMenu, MenuOption::PasteToVoxel, addActionToQMenuAndActionHash(developerMenu, MenuOption::PasteToVoxel,
Qt::CTRL | Qt::SHIFT | Qt::Key_V, Qt::CTRL | Qt::SHIFT | Qt::Key_V,
@ -394,8 +401,6 @@ void Menu::loadSettings(QSettings* settings) {
_maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM); _maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM);
_maxVoxelPacketsPerSecond = loadSetting(settings, "maxVoxelsPPS", DEFAULT_MAX_VOXEL_PPS); _maxVoxelPacketsPerSecond = loadSetting(settings, "maxVoxelsPPS", DEFAULT_MAX_VOXEL_PPS);
_voxelSizeScale = loadSetting(settings, "voxelSizeScale", DEFAULT_OCTREE_SIZE_SCALE); _voxelSizeScale = loadSetting(settings, "voxelSizeScale", DEFAULT_OCTREE_SIZE_SCALE);
_avatarLODDistanceMultiplier = loadSetting(settings, "avatarLODDistanceMultiplier",
DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER);
_boundaryLevelAdjust = loadSetting(settings, "boundaryLevelAdjust", 0); _boundaryLevelAdjust = loadSetting(settings, "boundaryLevelAdjust", 0);
settings->beginGroup("View Frustum Offset Camera"); settings->beginGroup("View Frustum Offset Camera");
@ -435,7 +440,6 @@ void Menu::saveSettings(QSettings* settings) {
settings->setValue("maxVoxels", _maxVoxels); settings->setValue("maxVoxels", _maxVoxels);
settings->setValue("maxVoxelsPPS", _maxVoxelPacketsPerSecond); settings->setValue("maxVoxelsPPS", _maxVoxelPacketsPerSecond);
settings->setValue("voxelSizeScale", _voxelSizeScale); settings->setValue("voxelSizeScale", _voxelSizeScale);
settings->setValue("avatarLODDistanceMultiplier", _avatarLODDistanceMultiplier);
settings->setValue("boundaryLevelAdjust", _boundaryLevelAdjust); settings->setValue("boundaryLevelAdjust", _boundaryLevelAdjust);
settings->beginGroup("View Frustum Offset Camera"); settings->beginGroup("View Frustum Offset Camera");
settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffset.yaw); settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffset.yaw);
@ -928,14 +932,17 @@ void Menu::goTo() {
int dialogReturn = gotoDialog.exec(); int dialogReturn = gotoDialog.exec();
if (dialogReturn == QDialog::Accepted && !gotoDialog.textValue().isEmpty()) { if (dialogReturn == QDialog::Accepted && !gotoDialog.textValue().isEmpty()) {
LocationManager* manager = &LocationManager::getInstance(); goToUser(gotoDialog.textValue());
manager->goTo(gotoDialog.textValue());
connect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision);
} }
sendFakeEnterEvent(); sendFakeEnterEvent();
} }
void Menu::goToUser(const QString& user) {
LocationManager* manager = &LocationManager::getInstance();
manager->goTo(user);
connect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision);
}
void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJsonObject& placeData) { void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJsonObject& placeData) {
QMessageBox msgBox; QMessageBox msgBox;
msgBox.setText("Both user and location exists with same name"); msgBox.setText("Both user and location exists with same name");
@ -1103,13 +1110,28 @@ void Menu::showMetavoxelEditor() {
} }
void Menu::showChat() { void Menu::showChat() {
if (!_chatAction->isEnabled()) {
// Don't do anything if chat is disabled (No
// QXMPP library or xmpp is disconnected).
return;
}
QMainWindow* mainWindow = Application::getInstance()->getWindow();
if (!_chatWindow) { if (!_chatWindow) {
Application::getInstance()->getWindow()->addDockWidget(Qt::RightDockWidgetArea, _chatWindow = new ChatWindow()); mainWindow->addDockWidget(Qt::NoDockWidgetArea, _chatWindow = new ChatWindow());
}
} else { if (!_chatWindow->toggleViewAction()->isChecked()) {
if (!_chatWindow->toggleViewAction()->isChecked()) { int width = _chatWindow->width();
_chatWindow->toggleViewAction()->trigger(); int y = qMax((mainWindow->height() - _chatWindow->height()) / 2, 0);
} _chatWindow->move(mainWindow->width(), y);
_chatWindow->resize(0, _chatWindow->height());
_chatWindow->toggleViewAction()->trigger();
QPropertyAnimation* slideAnimation = new QPropertyAnimation(_chatWindow, "geometry", _chatWindow);
slideAnimation->setStartValue(_chatWindow->geometry());
slideAnimation->setEndValue(QRect(mainWindow->width() - width, _chatWindow->y(),
width, _chatWindow->height()));
slideAnimation->setDuration(250);
slideAnimation->start(QAbstractAnimation::DeleteWhenStopped);
} }
} }
@ -1204,7 +1226,10 @@ void Menu::autoAdjustLOD(float currentFPS) {
if (now - _lastAvatarDetailDrop > ADJUST_AVATAR_LOD_DOWN_DELAY) { if (now - _lastAvatarDetailDrop > ADJUST_AVATAR_LOD_DOWN_DELAY) {
// attempt to lower the detail in proportion to the fps difference // attempt to lower the detail in proportion to the fps difference
float targetFps = (ADJUST_LOD_DOWN_FPS + ADJUST_LOD_UP_FPS) * 0.5f; float targetFps = (ADJUST_LOD_DOWN_FPS + ADJUST_LOD_UP_FPS) * 0.5f;
_avatarLODDistanceMultiplier *= (targetFps / _fastFPSAverage.getAverage()); float averageFps = _fastFPSAverage.getAverage();
const float MAXIMUM_MULTIPLIER_SCALE = 2.0f;
_avatarLODDistanceMultiplier *= (averageFps < EPSILON) ? MAXIMUM_MULTIPLIER_SCALE :
qMin(MAXIMUM_MULTIPLIER_SCALE, targetFps / averageFps);
_lastAvatarDetailDrop = now; _lastAvatarDetailDrop = now;
} }
} else if (_fastFPSAverage.getAverage() > ADJUST_LOD_UP_FPS) { } else if (_fastFPSAverage.getAverage() > ADJUST_LOD_UP_FPS) {
@ -1250,6 +1275,12 @@ void Menu::autoAdjustLOD(float currentFPS) {
} }
} }
void Menu::resetLODAdjust() {
_fpsAverage.reset();
_fastFPSAverage.reset();
_lastAvatarDetailDrop = _lastAdjust = usecTimestampNow();
}
void Menu::setVoxelSizeScale(float sizeScale) { void Menu::setVoxelSizeScale(float sizeScale) {
_voxelSizeScale = sizeScale; _voxelSizeScale = sizeScale;
} }

View file

@ -85,6 +85,7 @@ public:
// User Tweakable LOD Items // User Tweakable LOD Items
QString getLODFeedbackText(); QString getLODFeedbackText();
void autoAdjustLOD(float currentFPS); void autoAdjustLOD(float currentFPS);
void resetLODAdjust();
void setVoxelSizeScale(float sizeScale); void setVoxelSizeScale(float sizeScale);
float getVoxelSizeScale() const { return _voxelSizeScale; } float getVoxelSizeScale() const { return _voxelSizeScale; }
float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; } float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; }
@ -121,6 +122,7 @@ public slots:
void importSettings(); void importSettings();
void exportSettings(); void exportSettings();
void goTo(); void goTo();
void goToUser(const QString& user);
void pasteToVoxel(); void pasteToVoxel();
void toggleLoginMenuItem(); void toggleLoginMenuItem();
@ -240,6 +242,7 @@ namespace MenuOption {
const QString FilterSixense = "Smooth Sixense Movement"; const QString FilterSixense = "Smooth Sixense Movement";
const QString Enable3DTVMode = "Enable 3DTV Mode"; const QString Enable3DTVMode = "Enable 3DTV Mode";
const QString AudioNoiseReduction = "Audio Noise Reduction"; const QString AudioNoiseReduction = "Audio Noise Reduction";
const QString AudioToneInjection = "Inject Test Tone";
const QString EchoServerAudio = "Echo Server Audio"; const QString EchoServerAudio = "Echo Server Audio";
const QString EchoLocalAudio = "Echo Local Audio"; const QString EchoLocalAudio = "Echo Local Audio";
const QString MuteAudio = "Mute Microphone"; const QString MuteAudio = "Mute Microphone";
@ -300,7 +303,8 @@ namespace MenuOption {
const QString StopAllScripts = "Stop All Scripts"; const QString StopAllScripts = "Stop All Scripts";
const QString TestPing = "Test Ping"; const QString TestPing = "Test Ping";
const QString TransmitterDrive = "Transmitter Drive"; const QString TransmitterDrive = "Transmitter Drive";
const QString UploadFST = "Upload FST file"; const QString UploadHead = "Upload Head Model";
const QString UploadSkeleton = "Upload Skeleton Model";
const QString Visage = "Visage"; const QString Visage = "Visage";
const QString Quit = "Quit"; const QString Quit = "Quit";
const QString Voxels = "Voxels"; const QString Voxels = "Voxels";

View file

@ -252,7 +252,8 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
const float DISPLAYNAME_DISTANCE = 10.0f; const float DISPLAYNAME_DISTANCE = 10.0f;
setShowDisplayName(renderMode == NORMAL_RENDER_MODE && distanceToTarget < DISPLAYNAME_DISTANCE); setShowDisplayName(renderMode == NORMAL_RENDER_MODE && distanceToTarget < DISPLAYNAME_DISTANCE);
if (renderMode != NORMAL_RENDER_MODE) { if (renderMode != NORMAL_RENDER_MODE || (isMyAvatar() &&
Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON)) {
return; return;
} }
renderDisplayName(); renderDisplayName();
@ -318,9 +319,7 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
void Avatar::renderBody(RenderMode renderMode) { void Avatar::renderBody(RenderMode renderMode) {
if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
// render the billboard until both models are loaded // render the billboard until both models are loaded
if (renderMode != SHADOW_RENDER_MODE) { renderBillboard();
renderBillboard();
}
return; return;
} }
_skeletonModel.render(1.0f, renderMode == SHADOW_RENDER_MODE); _skeletonModel.render(1.0f, renderMode == SHADOW_RENDER_MODE);
@ -403,9 +402,13 @@ void Avatar::renderDisplayName() {
glPushMatrix(); glPushMatrix();
glm::vec3 textPosition; glm::vec3 textPosition;
getSkeletonModel().getNeckPosition(textPosition); if (getSkeletonModel().getNeckPosition(textPosition)) {
textPosition += getBodyUpDirection() * getHeadHeight() * 1.1f; textPosition += getBodyUpDirection() * getHeadHeight() * 1.1f;
} else {
const float HEAD_PROPORTION = 0.75f;
textPosition = _position + getBodyUpDirection() * (getBillboardSize() * HEAD_PROPORTION);
}
glTranslatef(textPosition.x, textPosition.y, textPosition.z); glTranslatef(textPosition.x, textPosition.y, textPosition.z);
// we need "always facing camera": we must remove the camera rotation from the stack // we need "always facing camera": we must remove the camera rotation from the stack

View file

@ -156,7 +156,8 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
//palm.addToPenetration(averagePenetration); //palm.addToPenetration(averagePenetration);
} else { } else {
// someone else's hand against MyAvatar // someone else's hand against MyAvatar
averageContactPoint /= float(handCollisions.size()); // TODO: submit collision info to MyAvatar which should lean accordingly
averageContactPoint /= (float)handCollisions.size();
avatar->applyCollision(averageContactPoint, totalPenetration); avatar->applyCollision(averageContactPoint, totalPenetration);
} }
} }

View file

@ -82,6 +82,7 @@ void MyAvatar::reset() {
// TODO? resurrect headMouse stuff? // TODO? resurrect headMouse stuff?
//_headMouseX = _glWidget->width() / 2; //_headMouseX = _glWidget->width() / 2;
//_headMouseY = _glWidget->height() / 2; //_headMouseY = _glWidget->height() / 2;
_skeletonModel.reset();
getHead()->reset(); getHead()->reset();
getHand()->reset(); getHand()->reset();

View file

@ -156,7 +156,7 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
direction += fingerVector / length; direction += fingerVector / length;
} }
fingerVector = glm::inverse(palmRotation) * fingerVector * -sign; fingerVector = glm::inverse(palmRotation) * fingerVector * -sign;
IndexValue indexValue = { int(i), atan2f(fingerVector.z, fingerVector.x) }; IndexValue indexValue = { (int)i, atan2f(fingerVector.z, fingerVector.x) };
fingerIndices.append(indexValue); fingerIndices.append(indexValue);
} }
qSort(fingerIndices.begin(), fingerIndices.end()); qSort(fingerIndices.begin(), fingerIndices.end());

View file

@ -1264,7 +1264,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
remainingModels.insert(model.key()); remainingModels.insert(model.key());
} }
while (!remainingModels.isEmpty()) { while (!remainingModels.isEmpty()) {
QString topID = getTopModelID(parentMap, models, *remainingModels.constBegin()); QString first = *remainingModels.constBegin();
foreach (const QString& id, remainingModels) {
if (id < first) {
first = id;
}
}
QString topID = getTopModelID(parentMap, models, first);
appendModelIDs(parentMap.value(topID), childMap, models, remainingModels, modelIDs); appendModelIDs(parentMap.value(topID), childMap, models, remainingModels, modelIDs);
} }
@ -1586,7 +1592,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
int numVertices = extracted.mesh.vertices.size(); int numVertices = extracted.mesh.vertices.size();
jointShapeInfo.numVertices = numVertices; jointShapeInfo.numVertices = numVertices;
if (numVertices > 0) { if (numVertices > 0) {
averageVertex /= float(jointShapeInfo.numVertices); averageVertex /= (float)jointShapeInfo.numVertices;
float averageRadius = 0.f; float averageRadius = 0.f;
foreach (const glm::vec3& vertex, extracted.mesh.vertices) { foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
averageRadius += glm::distance(vertex, averageVertex); averageRadius += glm::distance(vertex, averageVertex);
@ -1617,7 +1623,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
} else { } else {
// collide the joint like a sphere // collide the joint like a sphere
if (jointShapeInfo.numVertices > 0) { if (jointShapeInfo.numVertices > 0) {
jointShapeInfo.averageVertex /= float(jointShapeInfo.numVertices); jointShapeInfo.averageVertex /= (float)jointShapeInfo.numVertices;
joint.shapePosition = jointShapeInfo.averageVertex; joint.shapePosition = jointShapeInfo.averageVertex;
} else { } else {
joint.shapePosition = glm::vec3(0.f); joint.shapePosition = glm::vec3(0.f);
@ -1627,7 +1633,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
&& jointShapeInfo.numVertices > 0) { && jointShapeInfo.numVertices > 0) {
// the bone projection algorithm was not able to compute the joint radius // the bone projection algorithm was not able to compute the joint radius
// so we use an alternative measure // so we use an alternative measure
jointShapeInfo.averageRadius /= float(jointShapeInfo.numVertices); jointShapeInfo.averageRadius /= (float)jointShapeInfo.numVertices;
joint.boneRadius = jointShapeInfo.averageRadius; joint.boneRadius = jointShapeInfo.averageRadius;
} }
} }

View file

@ -31,11 +31,11 @@ void GeometryCache::renderHemisphere(int slices, int stacks) {
GLfloat* vertexData = new GLfloat[vertices * 3]; GLfloat* vertexData = new GLfloat[vertices * 3];
GLfloat* vertex = vertexData; GLfloat* vertex = vertexData;
for (int i = 0; i < stacks - 1; i++) { for (int i = 0; i < stacks - 1; i++) {
float phi = PI_OVER_TWO * float(i) / float(stacks - 1); float phi = PI_OVER_TWO * (float)i / (float)(stacks - 1);
float z = sinf(phi), radius = cosf(phi); float z = sinf(phi), radius = cosf(phi);
for (int j = 0; j < slices; j++) { for (int j = 0; j < slices; j++) {
float theta = TWO_PI * float(j) / float(slices); float theta = TWO_PI * (float)j / (float)slices;
*(vertex++) = sinf(theta) * radius; *(vertex++) = sinf(theta) * radius;
*(vertex++) = cosf(theta) * radius; *(vertex++) = cosf(theta) * radius;
@ -181,7 +181,7 @@ void GeometryCache::renderHalfCylinder(int slices, int stacks) {
float y = (float)i / (stacks - 1); float y = (float)i / (stacks - 1);
for (int j = 0; j <= slices; j++) { for (int j = 0; j <= slices; j++) {
float theta = 3.f * PI_OVER_TWO + PI * float(j) / float(slices); float theta = 3.f * PI_OVER_TWO + PI * (float)j / (float)slices;
//normals //normals
*(vertex++) = sinf(theta); *(vertex++) = sinf(theta);

View file

@ -170,9 +170,16 @@ void Model::init() {
} }
void Model::reset() { void Model::reset() {
if (_jointStates.isEmpty()) {
return;
}
foreach (Model* attachment, _attachments) { foreach (Model* attachment, _attachments) {
attachment->reset(); attachment->reset();
} }
const FBXGeometry& geometry = _geometry->getFBXGeometry();
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].rotation = geometry.joints.at(i).rotation;
}
} }
// return 'true' if geometry is up to date // return 'true' if geometry is up to date

View file

@ -160,7 +160,7 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) {
// Center of coordinate system -> upper left of bar // Center of coordinate system -> upper left of bar
glPushMatrix(); glPushMatrix();
glTranslatef(float(barX), float(y), 0.0f); glTranslatef((float)barX, (float)y, 0.0f);
// Render captions // Render captions
setColorRGBA(COLOR_TEXT); setColorRGBA(COLOR_TEXT);
@ -202,7 +202,7 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) {
// Render scale indicators // Render scale indicators
setColorRGBA(COLOR_INDICATOR); setColorRGBA(COLOR_INDICATOR);
for (int j = NUMBER_OF_MARKERS; --j > 0;) { for (int j = NUMBER_OF_MARKERS; --j > 0;) {
renderVerticalLine(int(barWidth * j / NUMBER_OF_MARKERS), 0, h); renderVerticalLine((barWidth * j) / NUMBER_OF_MARKERS, 0, h);
} }
// Render bars // Render bars
@ -210,8 +210,8 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) {
for (size_t i = 0; i < N_CHANNELS; ++i) { for (size_t i = 0; i < N_CHANNELS; ++i) {
ChannelIndex chIdx = ChannelIndex(i); ChannelIndex chIdx = ChannelIndex(i);
int wIn = int(barWidth * inputStream(chIdx).getValue() * UNIT_SCALE / scaleMax); int wIn = (int)(barWidth * inputStream(chIdx).getValue() * UNIT_SCALE / scaleMax);
int wOut = int(barWidth * outputStream(chIdx).getValue() * UNIT_SCALE / scaleMax); int wOut = (int)(barWidth * outputStream(chIdx).getValue() * UNIT_SCALE / scaleMax);
setColorRGBA(channelInfo(chIdx).colorRGBA); setColorRGBA(channelInfo(chIdx).colorRGBA);

View file

@ -6,13 +6,12 @@
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
// //
#include <QFormLayout> #include <QGridLayout>
#include <QFrame> #include <QFrame>
#include <QLayoutItem> #include <QLayoutItem>
#include <QPalette> #include <QPalette>
#include <QScrollBar> #include <QScrollBar>
#include <QSizePolicy> #include <QSizePolicy>
#include <QTextDocument>
#include <QTimer> #include <QTimer>
#include "Application.h" #include "Application.h"
@ -31,17 +30,18 @@ ChatWindow::ChatWindow() :
ui(new Ui::ChatWindow), ui(new Ui::ChatWindow),
numMessagesAfterLastTimeStamp(0) numMessagesAfterLastTimeStamp(0)
{ {
QWidget* widget = new QWidget(); ui->setupUi(this);
setWidget(widget);
// remove the title bar (see the Qt docs on setTitleBarWidget)
ui->setupUi(widget); setTitleBarWidget(new QWidget());
FlowLayout* flowLayout = new FlowLayout(0, 4, 4); FlowLayout* flowLayout = new FlowLayout(0, 4, 4);
ui->usersWidget->setLayout(flowLayout); ui->usersWidget->setLayout(flowLayout);
ui->messagePlainTextEdit->installEventFilter(this); ui->messagesGridLayout->setColumnStretch(0, 1);
ui->messagesGridLayout->setColumnStretch(1, 3);
ui->closeButton->hide(); ui->messagePlainTextEdit->installEventFilter(this);
#ifdef HAVE_QXMPP #ifdef HAVE_QXMPP
const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient();
@ -76,41 +76,48 @@ ChatWindow::~ChatWindow() {
} }
void ChatWindow::keyPressEvent(QKeyEvent* event) { void ChatWindow::keyPressEvent(QKeyEvent* event) {
QWidget::keyPressEvent(event); QDockWidget::keyPressEvent(event);
if (event->key() == Qt::Key_Escape) { if (event->key() == Qt::Key_Escape) {
hide(); hide();
} }
} }
void ChatWindow::showEvent(QShowEvent* event) { void ChatWindow::showEvent(QShowEvent* event) {
QWidget::showEvent(event); QDockWidget::showEvent(event);
if (!event->spontaneous()) { if (!event->spontaneous()) {
activateWindow();
ui->messagePlainTextEdit->setFocus(); ui->messagePlainTextEdit->setFocus();
} }
} }
bool ChatWindow::eventFilter(QObject* sender, QEvent* event) { bool ChatWindow::eventFilter(QObject* sender, QEvent* event) {
Q_UNUSED(sender); if (sender == ui->messagePlainTextEdit) {
if (event->type() != QEvent::KeyPress) {
if (event->type() != QEvent::KeyPress) { return false;
return false;
}
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) &&
(keyEvent->modifiers() & Qt::ShiftModifier) == 0) {
QString messageText = ui->messagePlainTextEdit->document()->toPlainText().trimmed();
if (!messageText.isEmpty()) {
#ifdef HAVE_QXMPP
const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom();
QXmppMessage message;
message.setTo(publicChatRoom->jid());
message.setType(QXmppMessage::GroupChat);
message.setBody(messageText);
XmppClient::getInstance().getXMPPClient().sendPacket(message);
#endif
ui->messagePlainTextEdit->document()->clear();
} }
return true; QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) &&
(keyEvent->modifiers() & Qt::ShiftModifier) == 0) {
QString messageText = ui->messagePlainTextEdit->document()->toPlainText().trimmed();
if (!messageText.isEmpty()) {
#ifdef HAVE_QXMPP
const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom();
QXmppMessage message;
message.setTo(publicChatRoom->jid());
message.setType(QXmppMessage::GroupChat);
message.setBody(messageText);
XmppClient::getInstance().getXMPPClient().sendPacket(message);
#endif
ui->messagePlainTextEdit->document()->clear();
}
return true;
}
} else {
if (event->type() != QEvent::MouseButtonRelease) {
return false;
}
QString user = sender->property("user").toString();
Menu::getInstance()->goToUser(user);
} }
return false; return false;
} }
@ -138,8 +145,9 @@ void ChatWindow::addTimeStamp() {
timeLabel->setStyleSheet("color: palette(shadow);" timeLabel->setStyleSheet("color: palette(shadow);"
"background-color: palette(highlight);" "background-color: palette(highlight);"
"padding: 4px;"); "padding: 4px;");
timeLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
timeLabel->setAlignment(Qt::AlignHCenter); timeLabel->setAlignment(Qt::AlignHCenter);
ui->messagesFormLayout->addRow(timeLabel); ui->messagesGridLayout->addWidget(timeLabel, ui->messagesGridLayout->rowCount(), 0, 1, 2);
numMessagesAfterLastTimeStamp = 0; numMessagesAfterLastTimeStamp = 0;
} }
} }
@ -187,13 +195,21 @@ void ChatWindow::participantsChanged() {
delete item; delete item;
} }
foreach (const QString& participant, participants) { foreach (const QString& participant, participants) {
QLabel* userLabel = new QLabel(getParticipantName(participant)); QString participantName = getParticipantName(participant);
QLabel* userLabel = new QLabel();
userLabel->setText(participantName);
userLabel->setStyleSheet("background-color: palette(light);" userLabel->setStyleSheet("background-color: palette(light);"
"border-radius: 5px;" "border-radius: 5px;"
"color: #267077;" "color: #267077;"
"padding: 2px;" "padding-top: 3px;"
"padding-right: 2px;"
"padding-bottom: 2px;"
"padding-left: 2px;"
"border: 1px solid palette(shadow);" "border: 1px solid palette(shadow);"
"font-weight: bold"); "font-weight: bold");
userLabel->setProperty("user", participantName);
userLabel->setCursor(Qt::PointingHandCursor);
userLabel->installEventFilter(this);
ui->usersWidget->layout()->addWidget(userLabel); ui->usersWidget->layout()->addWidget(userLabel);
} }
} }
@ -204,19 +220,25 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
} }
QLabel* userLabel = new QLabel(getParticipantName(message.from())); QLabel* userLabel = new QLabel(getParticipantName(message.from()));
userLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); userLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
userLabel->setStyleSheet("padding: 2px; font-weight: bold"); userLabel->setStyleSheet("padding: 2px; font-weight: bold");
userLabel->setAlignment(Qt::AlignTop); userLabel->setAlignment(Qt::AlignTop | Qt::AlignRight);
QLabel* messageLabel = new QLabel(message.body().replace(regexLinks, "<a href=\"\\1\">\\1</a>")); QLabel* messageLabel = new QLabel(message.body().replace(regexLinks, "<a href=\"\\1\">\\1</a>"));
messageLabel->setWordWrap(true); messageLabel->setWordWrap(true);
messageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); messageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
messageLabel->setOpenExternalLinks(true); messageLabel->setOpenExternalLinks(true);
messageLabel->setStyleSheet("padding: 2px; margin-right: 20px"); messageLabel->setStyleSheet("padding-bottom: 2px; padding-left: 2px; padding-top: 2px; padding-right: 20px");
messageLabel->setAlignment(Qt::AlignTop); messageLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
ui->messagesFormLayout->addRow(userLabel, messageLabel); if (getParticipantName(message.from()) == AccountManager::getInstance().getUsername()) {
ui->messagesFormLayout->parentWidget()->updateGeometry(); userLabel->setStyleSheet(userLabel->styleSheet() + "; background-color: #e1e8ea");
messageLabel->setStyleSheet(messageLabel->styleSheet() + "; background-color: #e1e8ea");
}
ui->messagesGridLayout->addWidget(userLabel, ui->messagesGridLayout->rowCount(), 0);
ui->messagesGridLayout->addWidget(messageLabel, ui->messagesGridLayout->rowCount() - 1, 1);
ui->messagesGridLayout->parentWidget()->updateGeometry();
Application::processEvents(); Application::processEvents();
QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar(); QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar();
verticalScrollBar->setSliderPosition(verticalScrollBar->maximum()); verticalScrollBar->setSliderPosition(verticalScrollBar->maximum());

View file

@ -127,7 +127,7 @@ void Oscilloscope::render(int x, int y) {
} }
// fetch low pass factor (and convert to fix point) / downsample factor // fetch low pass factor (and convert to fix point) / downsample factor
int lowPassFixPt = -int(std::numeric_limits<short>::min()) * _lowPassCoeff; int lowPassFixPt = -(int)(std::numeric_limits<short>::min()) * _lowPassCoeff;
unsigned downsample = _downsampleRatio; unsigned downsample = _downsampleRatio;
// keep half of the buffer for writing and ensure an even vertex count // keep half of the buffer for writing and ensure an even vertex count
unsigned usedWidth = min(_width, MAX_SAMPLES_PER_CHANNEL / (downsample * 2)) & ~1u; unsigned usedWidth = min(_width, MAX_SAMPLES_PER_CHANNEL / (downsample * 2)) & ~1u;
@ -141,7 +141,7 @@ void Oscilloscope::render(int x, int y) {
short const* inPtr = _samples + _writePos[ch]; short const* inPtr = _samples + _writePos[ch];
short* outPtr = _vertices + MAX_COORDS_PER_CHANNEL * ch; short* outPtr = _vertices + MAX_COORDS_PER_CHANNEL * ch;
int sample = 0, x = usedWidth; int sample = 0, x = usedWidth;
for (int i = int(usedSamples); --i >= 0 ;) { for (int i = (int)usedSamples; --i >= 0 ;) {
if (inPtr == basePtr) { if (inPtr == basePtr) {
// handle boundary, reading the circular sample buffer // handle boundary, reading the circular sample buffer
inPtr = endPtr; inPtr = endPtr;

View file

@ -506,8 +506,10 @@ void VoxelSystem::initVoxelMemory() {
_perlinModulateProgram.setUniformValue("permutationNormalTexture", 0); _perlinModulateProgram.setUniformValue("permutationNormalTexture", 0);
_perlinModulateProgram.release(); _perlinModulateProgram.release();
_shadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() _shadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
+ "shaders/shadow_map.frag"); Application::resourcesPath() + "shaders/shadow_map.vert");
_shadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/shadow_map.frag");
_shadowMapProgram.link(); _shadowMapProgram.link();
_shadowMapProgram.bind(); _shadowMapProgram.bind();
@ -1471,10 +1473,6 @@ void VoxelSystem::applyScaleAndBindProgram(bool texture) {
if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) {
_shadowMapProgram.bind(); _shadowMapProgram.bind();
glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID()); glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID());
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
glEnable(GL_TEXTURE_2D);
glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]); glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]);
glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]); glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]);
@ -1496,10 +1494,7 @@ void VoxelSystem::removeScaleAndReleaseProgram(bool texture) {
if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) {
_shadowMapProgram.release(); _shadowMapProgram.release();
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_GEN_R);
glDisable(GL_TEXTURE_2D);
} else if (texture) { } else if (texture) {
_perlinModulateProgram.release(); _perlinModulateProgram.release();

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>ChatWindow</class> <class>ChatWindow</class>
<widget class="QWidget" name="ChatWindow"> <widget class="QDockWidget" name="ChatWindow">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
@ -13,180 +13,188 @@
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>400</width> <width>400</width>
<height>0</height> <height>238</height>
</size> </size>
</property> </property>
<property name="windowTitle">
<string>Chat</string>
</property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">font-family: Helvetica, Arial, sans-serif;</string> <string notr="true">font-family: Helvetica, Arial, sans-serif;</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <property name="features">
<property name="spacing"> <set>QDockWidget::NoDockWidgetFeatures</set>
<number>0</number> </property>
</property> <property name="allowedAreas">
<property name="leftMargin"> <set>Qt::NoDockWidgetArea</set>
<number>8</number> </property>
</property> <property name="windowTitle">
<property name="topMargin"> <string>Chat</string>
<number>8</number> </property>
</property> <widget class="QWidget" name="dockWidgetContents">
<property name="rightMargin"> <layout class="QVBoxLayout" name="verticalLayout">
<number>8</number> <property name="spacing">
</property> <number>0</number>
<property name="bottomMargin"> </property>
<number>8</number> <property name="leftMargin">
</property> <number>8</number>
<item> </property>
<widget class="QLabel" name="connectingToXMPPLabel"> <property name="topMargin">
<property name="sizePolicy"> <number>8</number>
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> </property>
<horstretch>0</horstretch> <property name="rightMargin">
<verstretch>0</verstretch> <number>8</number>
</sizepolicy> </property>
</property> <property name="bottomMargin">
<property name="text"> <number>8</number>
<string>Connecting to XMPP...</string> </property>
</property> <item>
<property name="alignment"> <widget class="QLabel" name="connectingToXMPPLabel">
<set>Qt::AlignCenter</set> <property name="sizePolicy">
</property> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
</widget> <horstretch>0</horstretch>
</item> <verstretch>0</verstretch>
<item> </sizepolicy>
<layout class="QHBoxLayout" name="horizontalLayout"> </property>
<item> <property name="text">
<widget class="QLabel" name="numOnlineLabel"> <string>Connecting to XMPP...</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="numOnlineLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">font-weight: bold; color: palette(shadow); margin-bottom: 4px;</string>
</property>
<property name="text">
<string> online now:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources/resources.qrc">
<normaloff>:/images/close.svg</normaloff>:/images/close.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="usersWidget" native="true"/>
</item>
<item>
<widget class="QScrollArea" name="messagesScrollArea">
<property name="styleSheet">
<string notr="true">margin-top: 12px;</string>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>382</width>
<height>16</height>
</rect>
</property>
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">font-weight: bold; color: palette(shadow); margin-bottom: 4px;</string> <string notr="true">margin-top: 0px;</string>
</property>
<property name="text">
<string> online now:</string>
</property> </property>
<layout class="QGridLayout" name="messagesGridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
</layout>
</widget> </widget>
</item> </widget>
<item> </item>
<widget class="QPushButton" name="closeButton"> <item>
<property name="sizePolicy"> <widget class="QPlainTextEdit" name="messagePlainTextEdit">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <property name="sizePolicy">
<horstretch>0</horstretch> <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<verstretch>0</verstretch> <horstretch>0</horstretch>
</sizepolicy> <verstretch>0</verstretch>
</property> </sizepolicy>
<property name="maximumSize"> </property>
<size> <property name="minimumSize">
<width>16</width> <size>
<height>16</height> <width>0</width>
</size> <height>60</height>
</property> </size>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources/resources.qrc">
<normaloff>:/images/close.svg</normaloff>:/images/close.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="usersWidget" native="true"/>
</item>
<item>
<widget class="QScrollArea" name="messagesScrollArea">
<property name="styleSheet">
<string notr="true">margin-top: 12px;</string>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>358</width>
<height>464</height>
</rect>
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">margin-top: 0px;</string> <string notr="true">border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="tabChangesFocus">
<bool>true</bool>
</property> </property>
<layout class="QFormLayout" name="messagesFormLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="horizontalSpacing">
<number>0</number>
</property>
<property name="verticalSpacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
</layout>
</widget> </widget>
</widget> </item>
</item> </layout>
<item> </widget>
<widget class="QPlainTextEdit" name="messagePlainTextEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>60</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="tabChangesFocus">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget> </widget>
<tabstops> <tabstops>
<tabstop>messagePlainTextEdit</tabstop> <tabstop>messagePlainTextEdit</tabstop>

View file

@ -1096,7 +1096,7 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin
QScriptValue minimum = infoValue.property(guide->_minimumHandle); QScriptValue minimum = infoValue.property(guide->_minimumHandle);
MetavoxelInfo info = { MetavoxelInfo info = {
glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber()), glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber()),
float(infoValue.property(guide->_sizeHandle).toNumber()), guide->_visitation->info.inputValues, (float)infoValue.property(guide->_sizeHandle).toNumber(), guide->_visitation->info.inputValues,
guide->_visitation->info.outputValues, infoValue.property(guide->_isLeafHandle).toBool() }; guide->_visitation->info.outputValues, infoValue.property(guide->_isLeafHandle).toBool() };
// extract and convert the values provided by the script // extract and convert the values provided by the script

View file

@ -144,14 +144,15 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::
break; break;
case QNetworkAccessManager::PostOperation: case QNetworkAccessManager::PostOperation:
case QNetworkAccessManager::PutOperation: case QNetworkAccessManager::PutOperation:
authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
if (dataMultiPart) { if (dataMultiPart) {
if (operation == QNetworkAccessManager::PostOperation) { if (operation == QNetworkAccessManager::PostOperation) {
networkReply = _networkAccessManager->post(authenticatedRequest, dataMultiPart); networkReply = _networkAccessManager->post(authenticatedRequest, dataMultiPart);
} else { } else {
networkReply = _networkAccessManager->put(authenticatedRequest, dataMultiPart); networkReply = _networkAccessManager->put(authenticatedRequest, dataMultiPart);
} }
dataMultiPart->setParent(networkReply);
} else { } else {
authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
if (operation == QNetworkAccessManager::PostOperation) { if (operation == QNetworkAccessManager::PostOperation) {
networkReply = _networkAccessManager->post(authenticatedRequest, dataByteArray); networkReply = _networkAccessManager->post(authenticatedRequest, dataByteArray);
} else { } else {
@ -199,6 +200,7 @@ void AccountManager::passSuccessToCallback() {
qDebug() << jsonResponse; qDebug() << jsonResponse;
} }
} }
delete requestReply;
} }
void AccountManager::passErrorToCallback(QNetworkReply::NetworkError errorCode) { void AccountManager::passErrorToCallback(QNetworkReply::NetworkError errorCode) {
@ -219,6 +221,7 @@ void AccountManager::passErrorToCallback(QNetworkReply::NetworkError errorCode)
qDebug() << "Error" << errorCode << "-" << requestReply->errorString(); qDebug() << "Error" << errorCode << "-" << requestReply->errorString();
} }
} }
delete requestReply;
} }
bool AccountManager::hasValidAccessToken() { bool AccountManager::hasValidAccessToken() {

View file

@ -13,11 +13,11 @@
#include <QFileDialog> #include <QFileDialog>
#include <QStandardPaths> #include <QStandardPaths>
#include <QHttpMultiPart> #include <QHttpMultiPart>
#include <QTemporaryDir>
#include <QVariant> #include <QVariant>
#include <QMessageBox> #include <QMessageBox>
#include "AccountManager.h" #include "AccountManager.h"
#include "FstReader.h" #include "FstReader.h"
@ -25,20 +25,30 @@ static const QString NAME_FIELD = "name";
static const QString FILENAME_FIELD = "filename"; static const QString FILENAME_FIELD = "filename";
static const QString TEXDIR_FIELD = "texdir"; static const QString TEXDIR_FIELD = "texdir";
static const QString LOD_FIELD = "lod"; static const QString LOD_FIELD = "lod";
static const QString HEAD_SPECIFIC_FIELD = "bs";
static const QString MODEL_URL = "/api/v1/models"; static const QString MODEL_URL = "/api/v1/models";
static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB
FstReader::FstReader() : // Class providing the QObject parent system to QTemporaryDir
class TemporaryDir : public QTemporaryDir, public QObject {
public:
virtual ~TemporaryDir() {
// ensuring the entire object gets deleted by the QObject parent.
}
};
FstReader::FstReader(bool isHead) :
_zipDir(new TemporaryDir()),
_lodCount(-1), _lodCount(-1),
_texturesCount(-1), _texturesCount(-1),
_totalSize(0), _totalSize(0),
_isHead(false), _isHead(isHead),
_readyToSend(false), _readyToSend(false),
_dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType)) _dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType))
{ {
_zipDir->setParent(_dataMultiPart);
} }
FstReader::~FstReader() { FstReader::~FstReader() {
@ -63,20 +73,20 @@ bool FstReader::zip() {
QString("ModelUploader::zip()"), QString("ModelUploader::zip()"),
QString("Could not open FST file."), QString("Could not open FST file."),
QMessageBox::Ok); QMessageBox::Ok);
return false; qDebug() << "[Warning] " << QString("Could not open FST file.");
}
// Compress and copy the fst
if (!compressFile(QFileInfo(fst).filePath(), _zipDir.path() + "/" + QFileInfo(fst).fileName())) {
return false;
}
_totalSize += QFileInfo(fst).size();
if (!addPart(_zipDir.path() + "/" + QFileInfo(fst).fileName(),
QString("fst"))) {
return false; return false;
} }
qDebug() << "Reading FST file : " << QFileInfo(fst).filePath(); qDebug() << "Reading FST file : " << QFileInfo(fst).filePath();
// Compress and copy the fst
if (!compressFile(QFileInfo(fst).filePath(), _zipDir->path() + "/" + QFileInfo(fst).fileName())) {
return false;
}
if (!addPart(_zipDir->path() + "/" + QFileInfo(fst).fileName(),
QString("fst"))) {
return false;
}
// Let's read through the FST file // Let's read through the FST file
QTextStream stream(&fst); QTextStream stream(&fst);
QList<QString> line; QList<QString> line;
@ -86,73 +96,63 @@ bool FstReader::zip() {
continue; continue;
} }
if (_totalSize > MAX_SIZE) {
QMessageBox::warning(NULL,
QString("ModelUploader::zip()"),
QString("Model too big, over %1 Bytes.").arg(MAX_SIZE),
QMessageBox::Ok);
return false;
}
// according to what is read, we modify the command // according to what is read, we modify the command
if (line[1] == HEAD_SPECIFIC_FIELD) { if (line[0] == NAME_FIELD) {
_isHead = true;
} else if (line[1] == NAME_FIELD) {
QHttpPart textPart; QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
" name=\"model_name\""); " name=\"model_name\"");
textPart.setBody(line[1].toUtf8()); textPart.setBody(line[1].toUtf8());
_dataMultiPart->append(textPart); _dataMultiPart->append(textPart);
} else if (line[1] == FILENAME_FIELD) { } else if (line[0] == FILENAME_FIELD) {
QFileInfo fbx(QFileInfo(fst).path() + "/" + line[1]); QFileInfo fbx(QFileInfo(fst).path() + "/" + line[1]);
if (!fbx.exists() || !fbx.isFile()) { // Check existence if (!fbx.exists() || !fbx.isFile()) { // Check existence
QMessageBox::warning(NULL, QMessageBox::warning(NULL,
QString("ModelUploader::zip()"), QString("ModelUploader::zip()"),
QString("FBX file %1 could not be found.").arg(fbx.fileName()), QString("FBX file %1 could not be found.").arg(fbx.fileName()),
QMessageBox::Ok); QMessageBox::Ok);
qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(fbx.fileName());
return false; return false;
} }
// Compress and copy // Compress and copy
if (!compressFile(fbx.filePath(), _zipDir.path() + "/" + line[1])) { if (!compressFile(fbx.filePath(), _zipDir->path() + "/" + line[1])) {
return false; return false;
} }
_totalSize += fbx.size(); if (!addPart(_zipDir->path() + "/" + line[1], "fbx")) {
if (!addPart(_zipDir.path() + "/" + line[1], "fbx")) {
return false; return false;
} }
} else if (line[1] == TEXDIR_FIELD) { // Check existence } else if (line[0] == TEXDIR_FIELD) { // Check existence
QFileInfo texdir(QFileInfo(fst).path() + "/" + line[1]); QFileInfo texdir(QFileInfo(fst).path() + "/" + line[1]);
if (!texdir.exists() || !texdir.isDir()) { if (!texdir.exists() || !texdir.isDir()) {
QMessageBox::warning(NULL, QMessageBox::warning(NULL,
QString("ModelUploader::zip()"), QString("ModelUploader::zip()"),
QString("Texture directory could not be found."), QString("Texture directory could not be found."),
QMessageBox::Ok); QMessageBox::Ok);
qDebug() << "[Warning] " << QString("Texture directory could not be found.");
return false; return false;
} }
if (!addTextures(texdir)) { // Recursive compress and copy if (!addTextures(texdir)) { // Recursive compress and copy
return false; return false;
} }
} else if (line[1] == LOD_FIELD) { } else if (line[0] == LOD_FIELD) {
QFileInfo lod(QFileInfo(fst).path() + "/" + line[1]); QFileInfo lod(QFileInfo(fst).path() + "/" + line[1]);
if (!lod.exists() || !lod.isFile()) { // Check existence if (!lod.exists() || !lod.isFile()) { // Check existence
QMessageBox::warning(NULL, QMessageBox::warning(NULL,
QString("ModelUploader::zip()"), QString("ModelUploader::zip()"),
QString("FBX file %1 could not be found.").arg(lod.fileName()), QString("FBX file %1 could not be found.").arg(lod.fileName()),
QMessageBox::Ok); QMessageBox::Ok);
qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(lod.fileName());
return false; return false;
} }
// Compress and copy // Compress and copy
if (!compressFile(lod.filePath(), _zipDir.path() + "/" + line[1])) { if (!compressFile(lod.filePath(), _zipDir->path() + "/" + line[1])) {
return false; return false;
} }
_totalSize += lod.size(); if (!addPart(_zipDir->path() + "/" + line[1], QString("lod%1").arg(++_lodCount))) {
if (!addPart(_zipDir.path() + "/" + line[1], QString("lod%1").arg(++_lodCount))) {
return false; return false;
} }
} }
} }
QHttpPart textPart; QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
" name=\"model_category\""); " name=\"model_category\"");
@ -173,6 +173,9 @@ bool FstReader::send() {
} }
AccountManager::getInstance().authenticatedRequest(MODEL_URL, QNetworkAccessManager::PostOperation, JSONCallbackParameters(), QByteArray(), _dataMultiPart); AccountManager::getInstance().authenticatedRequest(MODEL_URL, QNetworkAccessManager::PostOperation, JSONCallbackParameters(), QByteArray(), _dataMultiPart);
_zipDir = NULL;
_dataMultiPart = NULL;
qDebug() << "Model sent.";
return true; return true;
} }
@ -189,11 +192,10 @@ bool FstReader::addTextures(const QFileInfo& texdir) {
foreach (QFileInfo info, list) { foreach (QFileInfo info, list) {
if (info.isFile()) { if (info.isFile()) {
// Compress and copy // Compress and copy
if (!compressFile(info.filePath(), _zipDir.path() + "/" + info.fileName())) { if (!compressFile(info.filePath(), _zipDir->path() + "/" + info.fileName())) {
return false; return false;
} }
_totalSize += info.size(); if (!addPart(_zipDir->path() + "/" + info.fileName(),
if (!addPart(_zipDir.path() + "/" + info.fileName(),
QString("texture%1").arg(++_texturesCount))) { QString("texture%1").arg(++_texturesCount))) {
return false; return false;
} }
@ -214,12 +216,13 @@ bool FstReader::compressFile(const QString &inFileName, const QString &outFileNa
QFile outFile(outFileName); QFile outFile(outFileName);
if (!outFile.open(QIODevice::WriteOnly)) { if (!outFile.open(QIODevice::WriteOnly)) {
QDir(_zipDir.path()).mkpath(QFileInfo(outFileName).path()); QDir(_zipDir->path()).mkpath(QFileInfo(outFileName).path());
if (!outFile.open(QIODevice::WriteOnly)) { if (!outFile.open(QIODevice::WriteOnly)) {
QMessageBox::warning(NULL, QMessageBox::warning(NULL,
QString("ModelUploader::compressFile()"), QString("ModelUploader::compressFile()"),
QString("Could not compress %1").arg(inFileName), QString("Could not compress %1").arg(inFileName),
QMessageBox::Ok); QMessageBox::Ok);
qDebug() << "[Warning] " << QString("Could not compress %1").arg(inFileName);
return false; return false;
} }
} }
@ -237,6 +240,8 @@ bool FstReader::addPart(const QString &path, const QString& name) {
QString("ModelUploader::addPart()"), QString("ModelUploader::addPart()"),
QString("Could not open %1").arg(path), QString("Could not open %1").arg(path),
QMessageBox::Ok); QMessageBox::Ok);
qDebug() << "[Warning] " << QString("Could not open %1").arg(path);
delete file;
return false; return false;
} }
@ -249,6 +254,19 @@ bool FstReader::addPart(const QString &path, const QString& name) {
_dataMultiPart->append(part); _dataMultiPart->append(part);
file->setParent(_dataMultiPart); file->setParent(_dataMultiPart);
qDebug() << "File " << QFileInfo(*file).fileName() << " added to model.";
_totalSize += file->size();
if (_totalSize > MAX_SIZE) {
QMessageBox::warning(NULL,
QString("ModelUploader::zip()"),
QString("Model too big, over %1 Bytes.").arg(MAX_SIZE),
QMessageBox::Ok);
qDebug() << "[Warning] " << QString("Model too big, over %1 Bytes.").arg(MAX_SIZE);
return false;
}
qDebug() << "Current model size: " << _totalSize;
return true; return true;
} }

View file

@ -10,20 +10,19 @@
#ifndef __hifi__FstReader__ #ifndef __hifi__FstReader__
#define __hifi__FstReader__ #define __hifi__FstReader__
#include <QTemporaryDir> class TemporaryDir;
class QHttpMultiPart; class QHttpMultiPart;
class FstReader { class FstReader : public QObject {
public: public:
FstReader(); FstReader(bool isHead);
~FstReader(); ~FstReader();
bool zip(); bool zip();
bool send(); bool send();
private: private:
QTemporaryDir _zipDir; TemporaryDir* _zipDir;
int _lodCount; int _lodCount;
int _texturesCount; int _texturesCount;
int _totalSize; int _totalSize;

View file

@ -51,6 +51,7 @@ Node::Node(const QUuid& uuid, char type, const HifiSockAddr& publicSocket, const
_lastHeardMicrostamp(usecTimestampNow()), _lastHeardMicrostamp(usecTimestampNow()),
_publicSocket(publicSocket), _publicSocket(publicSocket),
_localSocket(localSocket), _localSocket(localSocket),
_symmetricSocket(),
_activeSocket(NULL), _activeSocket(NULL),
_connectionSecret(), _connectionSecret(),
_bytesReceivedMovingAverage(NULL), _bytesReceivedMovingAverage(NULL),
@ -84,6 +85,15 @@ void Node::setLocalSocket(const HifiSockAddr& localSocket) {
_localSocket = localSocket; _localSocket = localSocket;
} }
void Node::setSymmetricSocket(const HifiSockAddr& symmetricSocket) {
if (_activeSocket == &_symmetricSocket) {
// if the active socket was the symmetric socket then reset it to NULL
_activeSocket = NULL;
}
_symmetricSocket = symmetricSocket;
}
void Node::activateLocalSocket() { void Node::activateLocalSocket() {
qDebug() << "Activating local socket for node" << *this; qDebug() << "Activating local socket for node" << *this;
_activeSocket = &_localSocket; _activeSocket = &_localSocket;
@ -94,6 +104,11 @@ void Node::activatePublicSocket() {
_activeSocket = &_publicSocket; _activeSocket = &_publicSocket;
} }
void Node::activateSymmetricSocket() {
qDebug() << "Activating symmetric socket for node" << *this;
_activeSocket = &_symmetricSocket;
}
void Node::recordBytesReceived(int bytesReceived) { void Node::recordBytesReceived(int bytesReceived) {
if (!_bytesReceivedMovingAverage) { if (!_bytesReceivedMovingAverage) {
_bytesReceivedMovingAverage = new SimpleMovingAverage(100); _bytesReceivedMovingAverage = new SimpleMovingAverage(100);

View file

@ -70,11 +70,14 @@ public:
void setPublicSocket(const HifiSockAddr& publicSocket); void setPublicSocket(const HifiSockAddr& publicSocket);
const HifiSockAddr& getLocalSocket() const { return _localSocket; } const HifiSockAddr& getLocalSocket() const { return _localSocket; }
void setLocalSocket(const HifiSockAddr& localSocket); void setLocalSocket(const HifiSockAddr& localSocket);
const HifiSockAddr& getSymmetricSocket() const { return _symmetricSocket; }
void setSymmetricSocket(const HifiSockAddr& symmetricSocket);
const HifiSockAddr* getActiveSocket() const { return _activeSocket; } const HifiSockAddr* getActiveSocket() const { return _activeSocket; }
void activatePublicSocket(); void activatePublicSocket();
void activateLocalSocket(); void activateLocalSocket();
void activateSymmetricSocket();
const QUuid& getConnectionSecret() const { return _connectionSecret; } const QUuid& getConnectionSecret() const { return _connectionSecret; }
void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; } void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; }
@ -110,6 +113,7 @@ private:
quint64 _lastHeardMicrostamp; quint64 _lastHeardMicrostamp;
HifiSockAddr _publicSocket; HifiSockAddr _publicSocket;
HifiSockAddr _localSocket; HifiSockAddr _localSocket;
HifiSockAddr _symmetricSocket;
HifiSockAddr* _activeSocket; HifiSockAddr* _activeSocket;
QUuid _connectionSecret; QUuid _connectionSecret;
SimpleMovingAverage* _bytesReceivedMovingAverage; SimpleMovingAverage* _bytesReceivedMovingAverage;

View file

@ -294,6 +294,15 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
matchingNode->setLastHeardMicrostamp(usecTimestampNow()); matchingNode->setLastHeardMicrostamp(usecTimestampNow());
QByteArray replyPacket = constructPingReplyPacket(packet); QByteArray replyPacket = constructPingReplyPacket(packet);
writeDatagram(replyPacket, matchingNode, senderSockAddr); writeDatagram(replyPacket, matchingNode, senderSockAddr);
// If we don't have a symmetric socket for this node and this socket doesn't match
// what we have for public and local then set it as the symmetric.
// This allows a server on a reachable port to communicate with nodes on symmetric NATs
if (matchingNode->getSymmetricSocket().isNull()) {
if (senderSockAddr != matchingNode->getLocalSocket() && senderSockAddr != matchingNode->getPublicSocket()) {
matchingNode->setSymmetricSocket(senderSockAddr);
}
}
} }
break; break;
@ -357,10 +366,20 @@ int NodeList::findNodeAndUpdateWithDataFromPacket(const QByteArray& packet) {
return 0; return 0;
} }
SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID) { SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID, bool blockingLock) {
QMutexLocker locker(&_nodeHashMutex); const int WAIT_TIME = 10; // wait up to 10ms in the try lock case
return _nodeHash.value(nodeUUID); SharedNodePointer node;
} // if caller wants us to block and guarantee the correct answer, then honor that request
if (blockingLock) {
// this will block till we can get access
QMutexLocker locker(&_nodeHashMutex);
node = _nodeHash.value(nodeUUID);
} else if (_nodeHashMutex.tryLock(WAIT_TIME)) { // some callers are willing to get wrong answers but not block
node = _nodeHash.value(nodeUUID);
_nodeHashMutex.unlock();
}
return node;
}
SharedNodePointer NodeList::sendingNodeForPacket(const QByteArray& packet) { SharedNodePointer NodeList::sendingNodeForPacket(const QByteArray& packet) {
QUuid nodeUUID = uuidFromPacketHeader(packet); QUuid nodeUUID = uuidFromPacketHeader(packet);
@ -772,7 +791,7 @@ QByteArray NodeList::constructPingReplyPacket(const QByteArray& pingPacket) {
return replyPacket; return replyPacket;
} }
void NodeList::pingPublicAndLocalSocketsForInactiveNode(const SharedNodePointer& node) { void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) {
// send the ping packet to the local and public sockets for this node // send the ping packet to the local and public sockets for this node
QByteArray localPingPacket = constructPingPacket(PingType::Local); QByteArray localPingPacket = constructPingPacket(PingType::Local);
@ -780,6 +799,11 @@ void NodeList::pingPublicAndLocalSocketsForInactiveNode(const SharedNodePointer&
QByteArray publicPingPacket = constructPingPacket(PingType::Public); QByteArray publicPingPacket = constructPingPacket(PingType::Public);
writeDatagram(publicPingPacket, node, node->getPublicSocket()); writeDatagram(publicPingPacket, node, node->getPublicSocket());
if (!node->getSymmetricSocket().isNull()) {
QByteArray symmetricPingPacket = constructPingPacket(PingType::Symmetric);
writeDatagram(symmetricPingPacket, node, node->getSymmetricSocket());
}
} }
SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType,
@ -850,7 +874,7 @@ void NodeList::pingInactiveNodes() {
foreach (const SharedNodePointer& node, getNodeHash()) { foreach (const SharedNodePointer& node, getNodeHash()) {
if (!node->getActiveSocket()) { if (!node->getActiveSocket()) {
// we don't have an active link to this node, ping it to set that up // we don't have an active link to this node, ping it to set that up
pingPublicAndLocalSocketsForInactiveNode(node); pingPunchForInactiveNode(node);
} }
} }
} }
@ -869,6 +893,8 @@ void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, con
sendingNode->activateLocalSocket(); sendingNode->activateLocalSocket();
} else if (pingType == PingType::Public && !sendingNode->getActiveSocket()) { } else if (pingType == PingType::Public && !sendingNode->getActiveSocket()) {
sendingNode->activatePublicSocket(); sendingNode->activatePublicSocket();
} else if (pingType == PingType::Symmetric && !sendingNode->getActiveSocket()) {
sendingNode->activateSymmetricSocket();
} }
} }

View file

@ -58,6 +58,7 @@ namespace PingType {
const PingType_t Agnostic = 0; const PingType_t Agnostic = 0;
const PingType_t Local = 1; const PingType_t Local = 1;
const PingType_t Public = 2; const PingType_t Public = 2;
const PingType_t Symmetric = 3;
} }
class NodeList : public QObject { class NodeList : public QObject {
@ -101,9 +102,9 @@ public:
QByteArray constructPingPacket(PingType_t pingType = PingType::Agnostic); QByteArray constructPingPacket(PingType_t pingType = PingType::Agnostic);
QByteArray constructPingReplyPacket(const QByteArray& pingPacket); QByteArray constructPingReplyPacket(const QByteArray& pingPacket);
void pingPublicAndLocalSocketsForInactiveNode(const SharedNodePointer& node); void pingPunchForInactiveNode(const SharedNodePointer& node);
SharedNodePointer nodeWithUUID(const QUuid& nodeUUID); SharedNodePointer nodeWithUUID(const QUuid& nodeUUID, bool blockingLock = true);
SharedNodePointer sendingNodeForPacket(const QByteArray& packet); SharedNodePointer sendingNodeForPacket(const QByteArray& packet);
SharedNodePointer addOrUpdateNode(const QUuid& uuid, char nodeType, SharedNodePointer addOrUpdateNode(const QUuid& uuid, char nodeType,

View file

@ -20,18 +20,15 @@ ThreadedAssignment::ThreadedAssignment(const QByteArray& packet) :
} }
void ThreadedAssignment::deleteLater() {
// move the NodeList back to the QCoreApplication instance's thread
NodeList::getInstance()->moveToThread(QCoreApplication::instance()->thread());
QObject::deleteLater();
}
void ThreadedAssignment::setFinished(bool isFinished) { void ThreadedAssignment::setFinished(bool isFinished) {
_isFinished = isFinished; _isFinished = isFinished;
if (_isFinished) { if (_isFinished) {
aboutToFinish(); aboutToFinish();
emit finished(); emit finished();
// move the NodeList back to the QCoreApplication instance's thread
NodeList::getInstance()->moveToThread(QCoreApplication::instance()->thread());
} }
} }

View file

@ -9,6 +9,8 @@
#ifndef __hifi__ThreadedAssignment__ #ifndef __hifi__ThreadedAssignment__
#define __hifi__ThreadedAssignment__ #define __hifi__ThreadedAssignment__
#include <QtCore/QSharedPointer>
#include "Assignment.h" #include "Assignment.h"
class ThreadedAssignment : public Assignment { class ThreadedAssignment : public Assignment {
@ -22,7 +24,6 @@ public:
public slots: public slots:
/// threaded run of assignment /// threaded run of assignment
virtual void run() = 0; virtual void run() = 0;
virtual void deleteLater();
virtual void readPendingDatagrams() = 0; virtual void readPendingDatagrams() = 0;
virtual void sendStatsPacket(); virtual void sendStatsPacket();
@ -36,5 +37,6 @@ signals:
void finished(); void finished();
}; };
typedef QSharedPointer<ThreadedAssignment> SharedAssignmentPointer;
#endif /* defined(__hifi__ThreadedAssignment__) */ #endif /* defined(__hifi__ThreadedAssignment__) */

View file

@ -1594,7 +1594,7 @@ void QTimeSpan::setFromMonths(qreal months)
{ {
Q_ASSERT_X(hasValidReference(), "setFromMonths", "Can not set interval from time unit month if there is no reference date."); Q_ASSERT_X(hasValidReference(), "setFromMonths", "Can not set interval from time unit month if there is no reference date.");
int fullMonths = int(months); int fullMonths = (int)months;
qreal fractionalMonth = months - fullMonths; qreal fractionalMonth = months - fullMonths;
QDateTime endDate = d->reference; QDateTime endDate = d->reference;
@ -1631,7 +1631,7 @@ void QTimeSpan::setFromYears(qreal years)
{ {
Q_ASSERT_X(hasValidReference(), "setFromYears", "Can not set interval from time unit year if there is no reference date."); Q_ASSERT_X(hasValidReference(), "setFromYears", "Can not set interval from time unit year if there is no reference date.");
int fullYears = int(years); int fullYears = (int)years;
qreal fractionalYear = years - fullYears; qreal fractionalYear = years - fullYears;
QDateTime endDate = d->reference; QDateTime endDate = d->reference;

View file

@ -192,7 +192,7 @@ void ShapeColliderTests::sphereMissesCapsule() {
float delta = 1.3f * (totalRadius + halfHeightB) / (numberOfSteps - 1); float delta = 1.3f * (totalRadius + halfHeightB) / (numberOfSteps - 1);
for (int i = 0; i < numberOfSteps; ++i) { for (int i = 0; i < numberOfSteps; ++i) {
// translate sphereA into world-frame // translate sphereA into world-frame
glm::vec3 localPosition = localStartPosition + (float(i) * delta) * yAxis; glm::vec3 localPosition = localStartPosition + ((float)i * delta) * yAxis;
sphereA.setPosition(rotation * localPosition + translation); sphereA.setPosition(rotation * localPosition + translation);
// sphereA agains capsuleB // sphereA agains capsuleB