Merging upstream

This commit is contained in:
matsukaze 2014-02-03 10:25:32 -05:00
commit 259b285841
243 changed files with 6996 additions and 4376 deletions

View file

@ -71,8 +71,15 @@ We have successfully built on OS X 10.8, Ubuntu and a few other modern Linux
distributions. A Windows build is planned for the future, but not currently in
development.
On a fresh Ubuntu 13.10 install, these are all the packages you need to grab and build the hifi project:
<pre>sudo apt-get install build-essential cmake git libcurl4-openssl-dev libqt5scripttools5 libqt5svg5-dev libqt5webkit5-dev libqt5location5 qtlocation5-dev qtdeclarative5-dev qtscript5-dev qtsensors5-dev qtmultimedia5-dev qtquick1-5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-dev</pre>
On a fresh Ubuntu 13.10 install, get these requirements from Ubuntu repositories:
sudo apt-get install build-essential cmake git libcurl4-openssl-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-dev
Then [download lastest Qt packages](http://qt-project.org/downloads), untar/install to your prefered path
and set your `QT_CMAKE_PREFIX_PATH` environment variable as described above in the CMake section. It's
recommended to set the variable automatically on each shell instance to save this task in the future:
echo 'export QT_CMAKE_PREFIX_PATH=~/Qt5.2.0/5.2.0/gcc_64/lib/cmake' >> ~/.bashrc
Running Interface
-----

View file

@ -16,7 +16,6 @@
#include <EnvironmentData.h>
#include <NodeList.h>
#include <NodeTypes.h>
#include <OctalCode.h>
#include <PacketHeaders.h>
#include <JurisdictionListener.h>
@ -152,7 +151,7 @@ static void renderMovingBug() {
}
// send the "erase message" first...
PACKET_TYPE message = PACKET_TYPE_VOXEL_ERASE;
PacketType message = PacketTypeVoxelErase;
::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_BUG, (VoxelDetail*)&details);
// Move the bug...
@ -212,7 +211,7 @@ static void renderMovingBug() {
}
// send the "create message" ...
message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE;
message = PacketTypeVoxelSetDestructive;
::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_BUG, (VoxelDetail*)&details);
}
@ -247,7 +246,7 @@ static void sendVoxelBlinkMessage() {
detail.green = 0 * ::intensity;
detail.blue = 0 * ::intensity;
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE;
PacketType message = PacketTypeVoxelSetDestructive;
::voxelEditPacketSender->sendVoxelEditMessage(message, detail);
}
@ -264,7 +263,7 @@ unsigned char onColor[3] = { 0, 255, 255 };
const float STRING_OF_LIGHTS_SIZE = 0.125f / TREE_SCALE; // approximately 1/8th meter
static void sendBlinkingStringOfLights() {
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
PacketType message = PacketTypeVoxelSetDestructive; // we're a bully!
float lightScale = STRING_OF_LIGHTS_SIZE;
static VoxelDetail details[LIGHTS_PER_SEGMENT];
@ -370,7 +369,7 @@ const int PACKETS_PER_DANCE_FLOOR = DANCE_FLOOR_VOXELS_PER_PACKET / (DANCE_FLOOR
int danceFloorColors[DANCE_FLOOR_WIDTH][DANCE_FLOOR_LENGTH];
void sendDanceFloor() {
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
PacketType message = PacketTypeVoxelSetDestructive; // we're a bully!
float lightScale = DANCE_FLOOR_LIGHT_SIZE;
static VoxelDetail details[DANCE_FLOOR_VOXELS_PER_PACKET];
@ -486,7 +485,7 @@ bool billboardMessage[BILLBOARD_HEIGHT][BILLBOARD_WIDTH] = {
};
static void sendBillboard() {
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
PacketType message = PacketTypeVoxelSetDestructive; // we're a bully!
float lightScale = BILLBOARD_LIGHT_SIZE;
static VoxelDetail details[VOXELS_PER_PACKET];
@ -557,7 +556,7 @@ void doBuildStreet() {
return;
}
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
PacketType message = PacketTypeVoxelSetDestructive; // we're a bully!
static VoxelDetail details[BRICKS_PER_PACKET];
for (int z = 0; z < ROAD_LENGTH; z++) {
@ -592,8 +591,8 @@ double start = 0;
void* animateVoxels(void* args) {
uint64_t lastAnimateTime = 0;
uint64_t lastProcessTime = 0;
quint64 lastAnimateTime = 0;
quint64 lastProcessTime = 0;
int processesPerAnimate = 0;
bool firstTime = true;
@ -623,8 +622,8 @@ void* animateVoxels(void* args) {
// The while loop will be running at PROCESSING_FPS, but we only want to call these animation functions at
// ANIMATE_FPS. So we check out last animate time and only call these if we've elapsed that time.
uint64_t now = usecTimestampNow();
uint64_t animationElapsed = now - lastAnimateTime;
quint64 now = usecTimestampNow();
quint64 animationElapsed = now - lastAnimateTime;
int withinAnimationTarget = ANIMATE_VOXELS_INTERVAL_USECS - animationElapsed;
const int CLOSE_ENOUGH_TO_ANIMATE = 2000; // approximately 2 ms
@ -677,7 +676,7 @@ void* animateVoxels(void* args) {
processesPerAnimate++;
}
// dynamically sleep until we need to fire off the next set of voxels
uint64_t usecToSleep = PROCESSING_INTERVAL_USECS - (usecTimestampNow() - lastProcessTime);
quint64 usecToSleep = PROCESSING_INTERVAL_USECS - (usecTimestampNow() - lastProcessTime);
if (usecToSleep > PROCESSING_INTERVAL_USECS) {
usecToSleep = PROCESSING_INTERVAL_USECS;
}
@ -695,7 +694,7 @@ AnimationServer::AnimationServer(int &argc, char **argv) :
{
::start = usecTimestampNow();
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_ANIMATION_SERVER, ANIMATION_LISTEN_PORT);
NodeList* nodeList = NodeList::createInstance(NodeType::AnimationServer, ANIMATION_LISTEN_PORT);
setvbuf(stdout, NULL, _IOLBF, 0);
// Handle Local Domain testing with the --local command line
@ -807,7 +806,7 @@ AnimationServer::AnimationServer(int &argc, char **argv) :
pthread_create(&::animateVoxelThread, NULL, animateVoxels, NULL);
NodeList::getInstance()->addNodeTypeToInterestSet(NODE_TYPE_VOXEL_SERVER);
NodeList::getInstance()->addNodeTypeToInterestSet(NodeType::VoxelServer);
QTimer* domainServerTimer = new QTimer(this);
connect(domainServerTimer, SIGNAL(timeout()), nodeList, SLOT(sendDomainServerCheckIn()));
@ -823,25 +822,24 @@ AnimationServer::AnimationServer(int &argc, char **argv) :
void AnimationServer::readPendingDatagrams() {
NodeList* nodeList = NodeList::getInstance();
static int receivedBytes = 0;
static unsigned char packetData[MAX_PACKET_SIZE];
static QByteArray receivedPacket;
static HifiSockAddr nodeSockAddr;
// Nodes sending messages to us...
while (nodeList->getNodeSocket().hasPendingDatagrams()
&& (receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE,
nodeSockAddr.getAddressPointer(),
nodeSockAddr.getPortPointer())) &&
packetVersionMatch(packetData, nodeSockAddr)) {
if (packetData[0] == PACKET_TYPE_JURISDICTION) {
int headerBytes = numBytesForPacketHeader(packetData);
// PACKET_TYPE_JURISDICTION, first byte is the node type...
if (packetData[headerBytes] == NODE_TYPE_VOXEL_SERVER && ::jurisdictionListener) {
::jurisdictionListener->queueReceivedPacket(nodeSockAddr, packetData, receivedBytes);
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(),
nodeSockAddr.getAddressPointer(), nodeSockAddr.getPortPointer());
if (packetVersionMatch(receivedPacket)) {
if (packetTypeForPacket(receivedPacket) == PacketTypeJurisdiction) {
int headerBytes = numBytesForPacketHeader(receivedPacket);
// PacketType_JURISDICTION, first byte is the node type...
if (receivedPacket.data()[headerBytes] == NodeType::VoxelServer && ::jurisdictionListener) {
::jurisdictionListener->queueReceivedPacket(nodeSockAddr, receivedPacket);
}
}
NodeList::getInstance()->processNodeData(nodeSockAddr, receivedPacket);
}
NodeList::getInstance()->processNodeData(nodeSockAddr, packetData, receivedBytes);
}
}

View file

@ -22,37 +22,42 @@
#include "Agent.h"
Agent::Agent(const unsigned char* dataBuffer, int numBytes) :
ThreadedAssignment(dataBuffer, numBytes)
Agent::Agent(const QByteArray& packet) :
ThreadedAssignment(packet)
{
}
void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
if (dataByteArray[0] == PACKET_TYPE_JURISDICTION) {
int headerBytes = numBytesForPacketHeader((const unsigned char*) dataByteArray.constData());
// PACKET_TYPE_JURISDICTION, first byte is the node type...
PacketType datagramPacketType = packetTypeForPacket(dataByteArray);
if (datagramPacketType == PacketTypeJurisdiction) {
int headerBytes = numBytesForPacketHeader(dataByteArray);
// PacketType_JURISDICTION, first byte is the node type...
switch (dataByteArray[headerBytes]) {
case NODE_TYPE_VOXEL_SERVER:
case NodeType::VoxelServer:
_scriptEngine.getVoxelsScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr,
(unsigned char*) dataByteArray.data(),
dataByteArray.size());
dataByteArray);
break;
case NODE_TYPE_PARTICLE_SERVER:
case NodeType::ParticleServer:
_scriptEngine.getParticlesScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr,
(unsigned char*) dataByteArray.data(),
dataByteArray.size());
dataByteArray);
break;
}
} else if (datagramPacketType == PacketTypeParticleAddResponse) {
// this will keep creatorTokenIDs to IDs mapped correctly
Particle::handleAddParticleResponse(dataByteArray);
// also give our local particle tree a chance to remap any internal locally created particles
_particleTree.handleAddParticleResponse(dataByteArray);
} else {
NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
NodeList::getInstance()->processNodeData(senderSockAddr, dataByteArray);
}
}
void Agent::run() {
NodeList* nodeList = NodeList::getInstance();
nodeList->setOwnerType(NODE_TYPE_AGENT);
nodeList->setOwnerType(NodeType::Agent);
nodeList->addSetOfNodeTypesToNodeInterestSet(QSet<NODE_TYPE>() << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER);
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer);
// figure out the URL for the script for this agent assignment
QString scriptURLString("http://%1:8080/assignment/%2");
@ -88,11 +93,17 @@ void Agent::run() {
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
// tell our script engine about our local particle tree
_scriptEngine.getParticlesScriptingInterface()->setParticleTree(&_particleTree);
// setup an Avatar for the script to use
AvatarData scriptedAvatar;
// give this AvatarData object to the script engine
_scriptEngine.setAvatarData(&scriptedAvatar, "Avatar");
// register ourselves to the script engine
_scriptEngine.registerGlobalObject("Agent", this);
_scriptEngine.setScriptContents(scriptContents);
_scriptEngine.run();

View file

@ -15,13 +15,19 @@
#include <QtCore/QObject>
#include <QtCore/QUrl>
#include <ParticleTree.h>
#include <ScriptEngine.h>
#include <ThreadedAssignment.h>
class Agent : public ThreadedAssignment {
Q_OBJECT
Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar)
public:
Agent(const unsigned char* dataBuffer, int numBytes);
Agent(const QByteArray& packet);
void setIsAvatar(bool isAvatar) { _scriptEngine.setIsAvatar(isAvatar); }
bool isAvatar() const { return _scriptEngine.isAvatar(); }
public slots:
void run();
@ -32,6 +38,7 @@ signals:
void willSendVisualDataCallback();
private:
ScriptEngine _scriptEngine;
ParticleTree _particleTree;
};
#endif /* defined(__hifi__Agent__) */

View file

@ -52,7 +52,7 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
_requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, requestAssignmentPool);
// create a NodeList as an unassigned client
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_UNASSIGNED);
NodeList* nodeList = NodeList::createInstance(NodeType::Unassigned);
const char CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION[] = "-a";
const char CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION[] = "-p";
@ -90,7 +90,8 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
timer->start(ASSIGNMENT_REQUEST_INTERVAL_MSECS);
// connect our readPendingDatagrams method to the readyRead() signal of the socket
connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams,
Qt::QueuedConnection);
}
void AssignmentClient::sendAssignmentRequest() {
@ -102,34 +103,33 @@ void AssignmentClient::sendAssignmentRequest() {
void AssignmentClient::readPendingDatagrams() {
NodeList* nodeList = NodeList::getInstance();
static unsigned char packetData[1500];
static qint64 receivedBytes = 0;
static HifiSockAddr senderSockAddr;
QByteArray receivedPacket;
HifiSockAddr senderSockAddr;
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(),
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
if ((receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE,
senderSockAddr.getAddressPointer(),
senderSockAddr.getPortPointer()))
&& packetVersionMatch(packetData, senderSockAddr)) {
if (packetVersionMatch(receivedPacket)) {
if (_currentAssignment) {
// have the threaded current assignment handle this datagram
QMetaObject::invokeMethod(_currentAssignment, "processDatagram", Qt::QueuedConnection,
Q_ARG(QByteArray, QByteArray((char*) packetData, receivedBytes)),
Q_ARG(QByteArray, receivedPacket),
Q_ARG(HifiSockAddr, senderSockAddr));
} else if (packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT || packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) {
} else if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) {
if (_currentAssignment) {
qDebug() << "Dropping received assignment since we are currently running one.";
} else {
// construct the deployed assignment from the packet data
_currentAssignment = AssignmentFactory::unpackAssignment(packetData, receivedBytes);
_currentAssignment = AssignmentFactory::unpackAssignment(receivedPacket);
qDebug() << "Received an assignment -" << *_currentAssignment;
// switch our nodelist domain IP and port to whoever sent us the assignment
if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) {
if (_currentAssignment) {
qDebug() << "Received an assignment -" << *_currentAssignment;
// switch our nodelist domain IP and port to whoever sent us the assignment
nodeList->setDomainSockAddr(senderSockAddr);
nodeList->setOwnerUUID(_currentAssignment->getUUID());
@ -153,12 +153,12 @@ void AssignmentClient::readPendingDatagrams() {
// Starts an event loop, and emits workerThread->started()
workerThread->start();
} else {
qDebug("Received a bad destination socket for assignment.");
qDebug() << "Received an assignment that could not be unpacked. Re-requesting.";
}
}
} else {
// have the NodeList attempt to handle it
nodeList->processNodeData(senderSockAddr, packetData, receivedBytes);
nodeList->processNodeData(senderSockAddr, receivedPacket);
}
}
}
@ -175,6 +175,6 @@ void AssignmentClient::assignmentCompleted() {
NodeList* nodeList = NodeList::getInstance();
// reset our NodeList by switching back to unassigned and clearing the list
nodeList->setOwnerType(NODE_TYPE_UNASSIGNED);
nodeList->setOwnerType(NodeType::Unassigned);
nodeList->reset();
}

View file

@ -18,25 +18,28 @@
#include "avatars/AvatarMixer.h"
#include "metavoxels/MetavoxelServer.h"
ThreadedAssignment* AssignmentFactory::unpackAssignment(const unsigned char* dataBuffer, int numBytes) {
int headerBytes = numBytesForPacketHeader(dataBuffer);
ThreadedAssignment* AssignmentFactory::unpackAssignment(const QByteArray& packet) {
QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet));
quint8 packedType;
packetStream >> packedType;
Assignment::Type assignmentType = Assignment::AllTypes;
memcpy(&assignmentType, dataBuffer + headerBytes, sizeof(Assignment::Type));
Assignment::Type unpackedType = (Assignment::Type) packedType;
switch (assignmentType) {
switch (unpackedType) {
case Assignment::AudioMixerType:
return new AudioMixer(dataBuffer, numBytes);
return new AudioMixer(packet);
case Assignment::AvatarMixerType:
return new AvatarMixer(dataBuffer, numBytes);
return new AvatarMixer(packet);
case Assignment::AgentType:
return new Agent(dataBuffer, numBytes);
return new Agent(packet);
case Assignment::VoxelServerType:
return new VoxelServer(dataBuffer, numBytes);
return new VoxelServer(packet);
case Assignment::ParticleServerType:
return new ParticleServer(dataBuffer, numBytes);
return new ParticleServer(packet);
case Assignment::MetavoxelServerType:
return new MetavoxelServer(dataBuffer, numBytes);
return new MetavoxelServer(packet);
default:
return NULL;
}

View file

@ -13,7 +13,7 @@
class AssignmentFactory {
public:
static ThreadedAssignment* unpackAssignment(const unsigned char* dataBuffer, int numBytes);
static ThreadedAssignment* unpackAssignment(const QByteArray& packet);
};
#endif /* defined(__hifi__AssignmentFactory__) */

View file

@ -37,7 +37,6 @@
#include <Logging.h>
#include <NodeList.h>
#include <Node.h>
#include <NodeTypes.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <StdDev.h>
@ -61,8 +60,8 @@ void attachNewBufferToNode(Node *newNode) {
}
}
AudioMixer::AudioMixer(const unsigned char* dataBuffer, int numBytes) :
ThreadedAssignment(dataBuffer, numBytes)
AudioMixer::AudioMixer(const QByteArray& packet) :
ThreadedAssignment(packet)
{
}
@ -210,18 +209,19 @@ void AudioMixer::prepareMixForListeningNode(Node* node) {
void AudioMixer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
// pull any new audio data from nodes off of the network stack
if (dataByteArray[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO
|| dataByteArray[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO
|| dataByteArray[0] == PACKET_TYPE_INJECT_AUDIO) {
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()),
NUM_BYTES_RFC4122_UUID));
PacketType mixerPacketType = packetTypeForPacket(dataByteArray);
if (mixerPacketType == PacketTypeMicrophoneAudioNoEcho
|| mixerPacketType == PacketTypeMicrophoneAudioWithEcho
|| mixerPacketType == PacketTypeInjectAudio) {
QUuid nodeUUID;
deconstructPacketHeader(dataByteArray, nodeUUID);
NodeList* nodeList = NodeList::getInstance();
SharedNodePointer matchingNode = nodeList->nodeWithUUID(nodeUUID);
if (matchingNode) {
nodeList->updateNodeWithData(matchingNode.data(), senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
nodeList->updateNodeWithData(matchingNode.data(), senderSockAddr, dataByteArray);
if (!matchingNode->getActiveSocket()) {
// we don't have an active socket for this node, but they're talking to us
@ -231,17 +231,17 @@ void AudioMixer::processDatagram(const QByteArray& dataByteArray, const HifiSock
}
} else {
// let processNodeData handle it.
NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
NodeList::getInstance()->processNodeData(senderSockAddr, dataByteArray);
}
}
void AudioMixer::run() {
commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NODE_TYPE_AUDIO_MIXER);
commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NodeType::AudioMixer);
NodeList* nodeList = NodeList::getInstance();
nodeList->addNodeTypeToInterestSet(NODE_TYPE_AGENT);
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
nodeList->linkedDataCreateCallback = attachNewBufferToNode;
@ -250,14 +250,14 @@ void AudioMixer::run() {
gettimeofday(&startTime, NULL);
int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_MIXED_AUDIO);
int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(PacketTypeMixedAudio);
// note: Visual Studio 2010 doesn't support variable sized local arrays
#ifdef _WIN32
unsigned char clientPacket[MAX_PACKET_SIZE];
#else
unsigned char clientPacket[NETWORK_BUFFER_LENGTH_BYTES_STEREO + numBytesPacketHeader];
#endif
populateTypeAndVersion(clientPacket, PACKET_TYPE_MIXED_AUDIO);
populatePacketHeader(reinterpret_cast<char*>(clientPacket), PacketTypeMixedAudio);
while (!_isFinished) {
@ -274,7 +274,7 @@ void AudioMixer::run() {
}
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
if (node->getType() == NODE_TYPE_AGENT && node->getActiveSocket() && node->getLinkedData()
if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData()
&& ((AudioMixerClientData*) node->getLinkedData())->getAvatarAudioRingBuffer()) {
prepareMixForListeningNode(node.data());

View file

@ -20,7 +20,7 @@ class AvatarAudioRingBuffer;
class AudioMixer : public ThreadedAssignment {
Q_OBJECT
public:
AudioMixer(const unsigned char* dataBuffer, int numBytes);
AudioMixer(const QByteArray& packet);
public slots:
/// threaded run of assignment
void run();

View file

@ -31,9 +31,10 @@ AvatarAudioRingBuffer* AudioMixerClientData::getAvatarAudioRingBuffer() const {
return NULL;
}
int AudioMixerClientData::parseData(unsigned char* packetData, int numBytes) {
if (packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO
|| packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO) {
int AudioMixerClientData::parseData(const QByteArray& packet) {
PacketType packetType = packetTypeForPacket(packet);
if (packetType == PacketTypeMicrophoneAudioWithEcho
|| packetType == PacketTypeMicrophoneAudioNoEcho) {
// grab the AvatarAudioRingBuffer from the vector (or create it if it doesn't exist)
AvatarAudioRingBuffer* avatarRingBuffer = getAvatarAudioRingBuffer();
@ -45,14 +46,12 @@ int AudioMixerClientData::parseData(unsigned char* packetData, int numBytes) {
}
// ask the AvatarAudioRingBuffer instance to parse the data
avatarRingBuffer->parseData(packetData, numBytes);
avatarRingBuffer->parseData(packet);
} else {
// this is injected audio
// grab the stream identifier for this injected audio
QByteArray rfcUUID = QByteArray((char*) packetData + numBytesForPacketHeader(packetData) + NUM_BYTES_RFC4122_UUID,
NUM_BYTES_RFC4122_UUID);
QUuid streamIdentifier = QUuid::fromRfc4122(rfcUUID);
QUuid streamIdentifier = QUuid::fromRfc4122(packet.mid(numBytesForPacketHeader(packet), NUM_BYTES_RFC4122_UUID));
InjectedAudioRingBuffer* matchingInjectedRingBuffer = NULL;
@ -69,7 +68,7 @@ int AudioMixerClientData::parseData(unsigned char* packetData, int numBytes) {
_ringBuffers.push_back(matchingInjectedRingBuffer);
}
matchingInjectedRingBuffer->parseData(packetData, numBytes);
matchingInjectedRingBuffer->parseData(packet);
}
return 0;

View file

@ -23,7 +23,7 @@ public:
const std::vector<PositionalAudioRingBuffer*> getRingBuffers() const { return _ringBuffers; }
AvatarAudioRingBuffer* getAvatarAudioRingBuffer() const;
int parseData(unsigned char* packetData, int numBytes);
int parseData(const QByteArray& packet);
void checkBuffersBeforeFrameSend(int jitterBufferLengthSamples);
void pushBuffersAfterFrameSend();
private:

View file

@ -15,7 +15,7 @@ AvatarAudioRingBuffer::AvatarAudioRingBuffer() :
}
int AvatarAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
_shouldLoopbackForNode = (sourceBuffer[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO);
return PositionalAudioRingBuffer::parseData(sourceBuffer, numBytes);
int AvatarAudioRingBuffer::parseData(const QByteArray& packet) {
_shouldLoopbackForNode = (packetTypeForPacket(packet) == PacketTypeMicrophoneAudioWithEcho);
return PositionalAudioRingBuffer::parseData(packet);
}

View file

@ -17,7 +17,7 @@ class AvatarAudioRingBuffer : public PositionalAudioRingBuffer {
public:
AvatarAudioRingBuffer();
int parseData(unsigned char* sourceBuffer, int numBytes);
int parseData(const QByteArray& packet);
private:
// disallow copying of AvatarAudioRingBuffer objects
AvatarAudioRingBuffer(const AvatarAudioRingBuffer&);

View file

@ -27,24 +27,13 @@ const char AVATAR_MIXER_LOGGING_NAME[] = "avatar-mixer";
const unsigned int AVATAR_DATA_SEND_INTERVAL_USECS = (1 / 60.0) * 1000 * 1000;
AvatarMixer::AvatarMixer(const unsigned char* dataBuffer, int numBytes) :
ThreadedAssignment(dataBuffer, numBytes)
AvatarMixer::AvatarMixer(const QByteArray& packet) :
ThreadedAssignment(packet)
{
// make sure we hear about node kills so we can tell the other nodes
connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled);
}
unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) {
QByteArray rfcUUID = nodeToAdd->getUUID().toRfc4122();
memcpy(currentPosition, rfcUUID.constData(), rfcUUID.size());
currentPosition += rfcUUID.size();
AvatarData *nodeData = (AvatarData *)nodeToAdd->getLinkedData();
currentPosition += nodeData->getBroadcastData(currentPosition);
return currentPosition;
}
void attachAvatarDataToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) {
newNode->setLinkedData(new AvatarData());
@ -59,58 +48,50 @@ void attachAvatarDataToNode(Node* newNode) {
// determine which avatars are included in the packet stream
// 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful).
void broadcastAvatarData() {
static unsigned char broadcastPacket[MAX_PACKET_SIZE];
static unsigned char avatarDataBuffer[MAX_PACKET_SIZE];
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_BULK_AVATAR_DATA);
unsigned char* currentBufferPosition = broadcastPacket + numHeaderBytes;
int packetLength = currentBufferPosition - broadcastPacket;
static QByteArray mixedAvatarByteArray;
int numPacketHeaderBytes = populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData);
int packetsSent = 0;
NodeList* nodeList = NodeList::getInstance();
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT && node->getActiveSocket()) {
if (node->getLinkedData() && node->getType() == NodeType::Agent && node->getActiveSocket()) {
// reset packet pointers for this node
currentBufferPosition = broadcastPacket + numHeaderBytes;
packetLength = currentBufferPosition - broadcastPacket;
mixedAvatarByteArray.resize(numPacketHeaderBytes);
// this is an AGENT we have received head data from
// send back a packet with other active node data to this node
foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) {
if (otherNode->getLinkedData() && otherNode->getUUID() != node->getUUID()) {
unsigned char* avatarDataEndpoint = addNodeToBroadcastPacket((unsigned char*)&avatarDataBuffer[0],
otherNode.data());
int avatarDataLength = avatarDataEndpoint - (unsigned char*)&avatarDataBuffer;
QByteArray avatarByteArray;
avatarByteArray.append(otherNode->getUUID().toRfc4122());
if (avatarDataLength + packetLength <= MAX_PACKET_SIZE) {
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
packetLength += avatarDataLength;
currentBufferPosition += avatarDataLength;
} else {
AvatarData* nodeData = (AvatarData*) otherNode->getLinkedData();
avatarByteArray.append(nodeData->toByteArray());
if (avatarByteArray.size() + mixedAvatarByteArray.size() > MAX_PACKET_SIZE) {
packetsSent++;
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket,
currentBufferPosition - broadcastPacket,
nodeList->getNodeSocket().writeDatagram(mixedAvatarByteArray,
node->getActiveSocket()->getAddress(),
node->getActiveSocket()->getPort());
// reset the packet
currentBufferPosition = broadcastPacket + numHeaderBytes;
packetLength = currentBufferPosition - broadcastPacket;
// copy the avatar that didn't fit into the next packet
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
packetLength += avatarDataLength;
currentBufferPosition += avatarDataLength;
mixedAvatarByteArray.resize(numPacketHeaderBytes);
}
// copy the avatar into the mixedAvatarByteArray packet
mixedAvatarByteArray.append(avatarByteArray);
}
}
packetsSent++;
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, currentBufferPosition - broadcastPacket,
nodeList->getNodeSocket().writeDatagram(mixedAvatarByteArray,
node->getActiveSocket()->getAddress(),
node->getActiveSocket()->getPort());
}
@ -118,18 +99,15 @@ void broadcastAvatarData() {
}
void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
if (killedNode->getType() == NODE_TYPE_AGENT
if (killedNode->getType() == NodeType::Agent
&& killedNode->getLinkedData()) {
// this was an avatar we were sending to other people
// send a kill packet for it to our other nodes
unsigned char packetData[MAX_PACKET_SIZE];
int numHeaderBytes = populateTypeAndVersion(packetData, PACKET_TYPE_KILL_AVATAR);
QByteArray killPacket = byteArrayWithPopluatedHeader(PacketTypeKillAvatar);
killPacket += killedNode->getUUID().toRfc4122();
QByteArray rfcUUID = killedNode->getUUID().toRfc4122();
memcpy(packetData + numHeaderBytes, rfcUUID.constData(), rfcUUID.size());
NodeList::getInstance()->broadcastToNodes(packetData, numHeaderBytes + NUM_BYTES_RFC4122_UUID,
QSet<NODE_TYPE>() << NODE_TYPE_AGENT);
NodeList::getInstance()->broadcastToNodes(killPacket,
NodeSet() << NodeType::Agent);
}
}
@ -137,39 +115,38 @@ void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSoc
NodeList* nodeList = NodeList::getInstance();
switch (dataByteArray[0]) {
case PACKET_TYPE_HEAD_DATA: {
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()),
NUM_BYTES_RFC4122_UUID));
switch (packetTypeForPacket(dataByteArray)) {
case PacketTypeAvatarData: {
QUuid nodeUUID;
deconstructPacketHeader(dataByteArray, nodeUUID);
// add or update the node in our list
SharedNodePointer avatarNode = nodeList->nodeWithUUID(nodeUUID);
if (avatarNode) {
// parse positional data from an node
nodeList->updateNodeWithData(avatarNode.data(), senderSockAddr,
(unsigned char*) dataByteArray.data(), dataByteArray.size());
nodeList->updateNodeWithData(avatarNode.data(), senderSockAddr, dataByteArray);
}
break;
}
case PACKET_TYPE_KILL_AVATAR: {
case PacketTypeKillAvatar: {
nodeList->processKillNode(dataByteArray);
break;
}
default:
// hand this off to the NodeList
nodeList->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
nodeList->processNodeData(senderSockAddr, dataByteArray);
break;
}
}
void AvatarMixer::run() {
commonInit(AVATAR_MIXER_LOGGING_NAME, NODE_TYPE_AVATAR_MIXER);
commonInit(AVATAR_MIXER_LOGGING_NAME, NodeType::AvatarMixer);
NodeList* nodeList = NodeList::getInstance();
nodeList->addNodeTypeToInterestSet(NODE_TYPE_AGENT);
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
nodeList->linkedDataCreateCallback = attachAvatarDataToNode;

View file

@ -14,7 +14,7 @@
/// Handles assignments of type AvatarMixer - distribution of avatar data to various clients
class AvatarMixer : public ThreadedAssignment {
public:
AvatarMixer(const unsigned char* dataBuffer, int numBytes);
AvatarMixer(const QByteArray& packet);
public slots:
/// runs the avatar mixer

View file

@ -17,22 +17,25 @@
const int SEND_INTERVAL = 50;
MetavoxelServer::MetavoxelServer(const unsigned char* dataBuffer, int numBytes) :
ThreadedAssignment(dataBuffer, numBytes),
_data(new MetavoxelData()) {
MetavoxelServer::MetavoxelServer(const QByteArray& packet) :
ThreadedAssignment(packet) {
_sendTimer.setSingleShot(true);
connect(&_sendTimer, SIGNAL(timeout()), SLOT(sendDeltas()));
}
void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) {
edit.apply(_data);
}
void MetavoxelServer::removeSession(const QUuid& sessionId) {
delete _sessions.take(sessionId);
_sessions.take(sessionId)->deleteLater();
}
const char METAVOXEL_SERVER_LOGGING_NAME[] = "metavoxel-server";
void MetavoxelServer::run() {
commonInit(METAVOXEL_SERVER_LOGGING_NAME, NODE_TYPE_METAVOXEL_SERVER);
commonInit(METAVOXEL_SERVER_LOGGING_NAME, NodeType::MetavoxelServer);
_lastSend = QDateTime::currentMSecsSinceEpoch();
_sendTimer.start(SEND_INTERVAL);
@ -40,12 +43,12 @@ void MetavoxelServer::run() {
void MetavoxelServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
switch (dataByteArray.at(0)) {
case PACKET_TYPE_METAVOXEL_DATA:
case PacketTypeMetavoxelData:
processData(dataByteArray, senderSockAddr);
break;
default:
NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*)dataByteArray.data(), dataByteArray.size());
NodeList::getInstance()->processNodeData(senderSockAddr, dataByteArray);
break;
}
}
@ -75,16 +78,18 @@ void MetavoxelServer::processData(const QByteArray& data, const HifiSockAddr& se
// forward to session, creating if necessary
MetavoxelSession*& session = _sessions[sessionID];
if (!session) {
session = new MetavoxelSession(this, sessionID, QByteArray::fromRawData(data.constData(), headerPlusIDSize));
session = new MetavoxelSession(this, sessionID, QByteArray::fromRawData(data.constData(), headerPlusIDSize), sender);
}
session->receivedData(data, sender);
}
MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId, const QByteArray& datagramHeader) :
MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId,
const QByteArray& datagramHeader, const HifiSockAddr& sender) :
QObject(server),
_server(server),
_sessionId(sessionId),
_sequencer(datagramHeader) {
_sequencer(datagramHeader),
_sender(sender) {
const int TIMEOUT_INTERVAL = 30 * 1000;
_timeoutTimer.setInterval(TIMEOUT_INTERVAL);
@ -94,10 +99,13 @@ MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const QUuid& session
connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&)));
connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&)));
connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(clearSendRecordsBefore(int)));
connect(&_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)), SLOT(handleMessage(const QVariant&)));
// insert the baseline send record
SendRecord record = { 0, MetavoxelDataPointer(new MetavoxelData()) };
SendRecord record = { 0 };
_sendRecords.append(record);
qDebug() << "Opened session [sessionId=" << _sessionId << ", sender=" << _sender << "]";
}
void MetavoxelSession::receivedData(const QByteArray& data, const HifiSockAddr& sender) {
@ -114,7 +122,7 @@ void MetavoxelSession::receivedData(const QByteArray& data, const HifiSockAddr&
void MetavoxelSession::sendDelta() {
Bitstream& out = _sequencer.startPacket();
out << QVariant::fromValue(MetavoxelDeltaMessage());
writeDelta(_server->getData(), _sendRecords.first().data, out);
_server->getData().writeDelta(_sendRecords.first().data, out);
_sequencer.endPacket();
// record the send
@ -134,25 +142,29 @@ void MetavoxelSession::sendData(const QByteArray& data) {
void MetavoxelSession::readPacket(Bitstream& in) {
QVariant message;
in >> message;
handleMessage(message, in);
handleMessage(message);
}
void MetavoxelSession::clearSendRecordsBefore(int index) {
_sendRecords.erase(_sendRecords.begin(), _sendRecords.begin() + index + 1);
}
void MetavoxelSession::handleMessage(const QVariant& message, Bitstream& in) {
void MetavoxelSession::handleMessage(const QVariant& message) {
int userType = message.userType();
if (userType == ClientStateMessage::Type) {
if (userType == CloseSessionMessage::Type) {
qDebug() << "Session closed [sessionId=" << _sessionId << ", sender=" << _sender << "]";
_server->removeSession(_sessionId);
} else if (userType == ClientStateMessage::Type) {
ClientStateMessage state = message.value<ClientStateMessage>();
_position = state.position;
} else if (userType == MetavoxelDeltaMessage::Type) {
} else if (userType == MetavoxelEditMessage::Type) {
_server->applyEdit(message.value<MetavoxelEditMessage>());
} else if (userType == QMetaType::QVariantList) {
foreach (const QVariant& element, message.toList()) {
handleMessage(element, in);
handleMessage(element);
}
}
}

View file

@ -20,6 +20,7 @@
#include <DatagramSequencer.h>
#include <MetavoxelData.h>
class MetavoxelEditMessage;
class MetavoxelSession;
/// Maintains a shared metavoxel system, accepting change requests and broadcasting updates.
@ -28,9 +29,11 @@ class MetavoxelServer : public ThreadedAssignment {
public:
MetavoxelServer(const unsigned char* dataBuffer, int numBytes);
MetavoxelServer(const QByteArray& packet);
const MetavoxelDataPointer& getData() const { return _data; }
void applyEdit(const MetavoxelEditMessage& edit);
const MetavoxelData& getData() const { return _data; }
void removeSession(const QUuid& sessionId);
@ -51,7 +54,7 @@ private:
QHash<QUuid, MetavoxelSession*> _sessions;
MetavoxelDataPointer _data;
MetavoxelData _data;
};
/// Contains the state of a single client session.
@ -60,7 +63,8 @@ class MetavoxelSession : public QObject {
public:
MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId, const QByteArray& datagramHeader);
MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId,
const QByteArray& datagramHeader, const HifiSockAddr& sender);
void receivedData(const QByteArray& data, const HifiSockAddr& sender);
@ -76,14 +80,14 @@ private slots:
void clearSendRecordsBefore(int index);
private:
void handleMessage(const QVariant& message);
void handleMessage(const QVariant& message, Bitstream& in);
private:
class SendRecord {
public:
int packetNumber;
MetavoxelDataPointer data;
MetavoxelData data;
};
MetavoxelServer* _server;

View file

@ -9,10 +9,12 @@ macro(AUTO_MTC TARGET ROOT_DIR)
${INCLUDE_FILES} DEPENDS mtc ${INCLUDE_FILES})
find_package(Qt5Core REQUIRED)
find_package(Qt5Script REQUIRED)
find_package(Qt5Widgets REQUIRED)
add_library(${TARGET}_automtc STATIC ${TARGET}_automtc.cpp)
qt5_use_modules(${TARGET}_automtc Core)
qt5_use_modules(${TARGET}_automtc Core Script Widgets)
target_link_libraries(${TARGET} ${TARGET}_automtc)

View file

@ -6,9 +6,12 @@
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
#include <QtCore/QDataStream>
#include <QtCore/QStringList>
#include <QtCore/QUuid>
#include <PacketHeaders.h>
#include <HifiSockAddr.h>
#include <UUID.h>
#include "DataServer.h"
@ -21,7 +24,8 @@ const unsigned short REDIS_PORT = 6379;
DataServer::DataServer(int argc, char* argv[]) :
QCoreApplication(argc, argv),
_socket(),
_redis(NULL)
_redis(NULL),
_uuid(QUuid::createUuid())
{
_socket.bind(QHostAddress::Any, DATA_SERVER_LISTEN_PORT);
@ -51,66 +55,60 @@ DataServer::~DataServer() {
const int MAX_PACKET_SIZE = 1500;
void DataServer::readPendingDatagrams() {
qint64 receivedBytes = 0;
static unsigned char packetData[MAX_PACKET_SIZE];
QByteArray receivedPacket;
HifiSockAddr senderSockAddr;
while (_socket.hasPendingDatagrams() &&
(receivedBytes = _socket.readDatagram(reinterpret_cast<char*>(packetData), MAX_PACKET_SIZE,
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()))) {
if ((packetData[0] == PACKET_TYPE_DATA_SERVER_PUT || packetData[0] == PACKET_TYPE_DATA_SERVER_GET) &&
packetVersionMatch(packetData, senderSockAddr)) {
int readBytes = numBytesForPacketHeader(packetData);
while (_socket.hasPendingDatagrams()) {
receivedPacket.resize(_socket.pendingDatagramSize());
_socket.readDatagram(receivedPacket.data(), _socket.pendingDatagramSize(),
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
PacketType requestType = packetTypeForPacket(receivedPacket);
if ((requestType == PacketTypeDataServerPut || requestType == PacketTypeDataServerGet) &&
packetVersionMatch(receivedPacket)) {
QDataStream packetStream(receivedPacket);
int numReceivedHeaderBytes = numBytesForPacketHeader(receivedPacket);
packetStream.skipRawData(numReceivedHeaderBytes);
// pull the sequence number used for this packet
quint8 sequenceNumber = 0;
memcpy(&sequenceNumber, packetData + readBytes, sizeof(sequenceNumber));
readBytes += sizeof(sequenceNumber);
packetStream >> sequenceNumber;
// pull the UUID that we will need as part of the key
QString uuidString(reinterpret_cast<char*>(packetData + readBytes));
QUuid parsedUUID(uuidString);
QString userString;
packetStream >> userString;
QUuid parsedUUID(userString);
if (parsedUUID.isNull()) {
// we failed to parse a UUID, this means the user has sent us a username
QString username(reinterpret_cast<char*>(packetData + readBytes));
readBytes += username.size() + sizeof('\0');
// ask redis for the UUID for this user
redisReply* reply = (redisReply*) redisCommand(_redis, "GET user:%s", qPrintable(username));
redisReply* reply = (redisReply*) redisCommand(_redis, "GET user:%s", qPrintable(userString));
if (reply->type == REDIS_REPLY_STRING) {
parsedUUID = QUuid(QString(reply->str));
}
if (!parsedUUID.isNull()) {
qDebug() << "Found UUID" << parsedUUID << "for username" << username;
qDebug() << "Found UUID" << parsedUUID << "for username" << userString;
} else {
qDebug() << "Failed UUID lookup for username" << username;
qDebug() << "Failed UUID lookup for username" << userString;
}
freeReplyObject(reply);
reply = NULL;
} else {
readBytes += uuidString.size() + sizeof('\0');
}
if (!parsedUUID.isNull()) {
// pull the number of keys the user has sent
unsigned char numKeys = packetData[readBytes++];
if (packetData[0] == PACKET_TYPE_DATA_SERVER_PUT) {
if (requestType == PacketTypeDataServerPut) {
// pull the key that specifies the data the user is putting/getting
QString dataKey(reinterpret_cast<char*>(packetData + readBytes));
readBytes += dataKey.size() + sizeof('\0');
// pull the key and value that specifies the data the user is putting/getting
QString dataKey, dataValue;
// grab the string value the user wants us to put, null terminate it
QString dataValue(reinterpret_cast<char*>(packetData + readBytes));
readBytes += dataValue.size() + sizeof('\0');
packetStream >> dataKey >> dataValue;
qDebug("Sending command to redis: SET uuid:%s:%s %s",
qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)),
@ -122,78 +120,72 @@ void DataServer::readPendingDatagrams() {
if (reply->type == REDIS_REPLY_STATUS && strcmp("OK", reply->str) == 0) {
// if redis stored the value successfully reply back with a confirm
// which is the sent packet with the header replaced
packetData[0] = PACKET_TYPE_DATA_SERVER_CONFIRM;
_socket.writeDatagram(reinterpret_cast<char*>(packetData), receivedBytes,
senderSockAddr.getAddress(), senderSockAddr.getPort());
// which is a reply packet with the sequence number
QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerConfirm, _uuid);
replyPacket.append(sequenceNumber);
_socket.writeDatagram(replyPacket, senderSockAddr.getAddress(), senderSockAddr.getPort());
}
freeReplyObject(reply);
} else {
// setup a send packet with the returned data
// leverage the packetData sent by overwriting and appending
int numSendPacketBytes = receivedBytes;
QByteArray sendPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerSend, _uuid);
QDataStream sendPacketStream(&sendPacket, QIODevice::Append);
packetData[0] = PACKET_TYPE_DATA_SERVER_SEND;
sendPacketStream << sequenceNumber;
if (strcmp((char*) packetData + readBytes, "uuid") != 0) {
// pull the key list that specifies the data the user is putting/getting
QString keyListString;
packetStream >> keyListString;
if (keyListString != "uuid") {
// copy the parsed UUID
sendPacketStream << uuidStringWithoutCurlyBraces(parsedUUID);
const char MULTI_KEY_VALUE_SEPARATOR = '|';
// the user has sent one or more keys - make the associated requests
for (int j = 0; j < numKeys; j++) {
// pull the key that specifies the data the user is putting/getting, null terminate it
int numDataKeyBytes = 0;
// look for the key separator or the null terminator
while (packetData[readBytes + numDataKeyBytes] != MULTI_KEY_VALUE_SEPARATOR
&& packetData[readBytes + numDataKeyBytes] != '\0') {
numDataKeyBytes++;
}
QString dataKey(QByteArray(reinterpret_cast<char*>(packetData + readBytes), numDataKeyBytes));
readBytes += dataKey.size() + sizeof('\0');
// append the keyListString back to the sendPacket
sendPacketStream << keyListString;
QStringList keyList = keyListString.split(MULTI_KEY_VALUE_SEPARATOR);
QStringList valueList;
foreach (const QString& dataKey, keyList) {
qDebug("Sending command to redis: GET uuid:%s:%s",
qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)),
qPrintable(dataKey));
redisReply* reply = (redisReply*) redisCommand(_redis, "GET uuid:%s:%s",
qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)),
qPrintable(dataKey));
qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)),
qPrintable(dataKey));
if (reply->len) {
// copy the value that redis returned
memcpy(packetData + numSendPacketBytes, reply->str, reply->len);
numSendPacketBytes += reply->len;
valueList << QString(reply->str);
} else {
// didn't find a value - insert a space
packetData[numSendPacketBytes++] = ' ';
valueList << QChar(' ');
}
// add the multi-value separator
packetData[numSendPacketBytes++] = MULTI_KEY_VALUE_SEPARATOR;
freeReplyObject(reply);
}
// null terminate the packet we're sending back (erases the trailing separator)
packetData[(numSendPacketBytes - 1)] = '\0';
// append the value QStringList using the right separator
sendPacketStream << valueList.join(MULTI_KEY_VALUE_SEPARATOR);
} else {
// user is asking for a UUID matching username, copy the UUID we found
QString uuidString = uuidStringWithoutCurlyBraces(parsedUUID);
memcpy(packetData + numSendPacketBytes, qPrintable(uuidString), uuidString.size() + sizeof('\0'));
numSendPacketBytes += uuidString.size() + sizeof('\0');
// user was asking for their UUID
sendPacketStream << userString;
sendPacketStream << QString("uuid");
sendPacketStream << uuidStringWithoutCurlyBraces(parsedUUID);
}
// reply back with the send packet
_socket.writeDatagram(reinterpret_cast<char*>(packetData), numSendPacketBytes,
senderSockAddr.getAddress(), senderSockAddr.getPort());
_socket.writeDatagram(sendPacket, senderSockAddr.getAddress(), senderSockAddr.getPort());
}
}
}
}
}
}

View file

@ -10,6 +10,7 @@
#define __hifi__DataServer__
#include <QtCore/QCoreApplication>
#include <QtCore/QUuid>
#include <QtNetwork/QUdpSocket>
#include <hiredis.h>
@ -22,6 +23,7 @@ public:
private:
QUdpSocket _socket;
redisContext* _redis;
QUuid _uuid;
private slots:
void readPendingDatagrams();
};

File diff suppressed because it is too large Load diff

View file

@ -9,17 +9,16 @@
#ifndef __hifi__DomainServer__
#define __hifi__DomainServer__
#include <deque>
#include <QtCore/QCoreApplication>
#include <QtCore/QFile>
#include <QtCore/QMutex>
#include <QtCore/QHash>
#include <QtCore/QQueue>
#include <QtCore/QSharedPointer>
#include <Assignment.h>
#include <HTTPManager.h>
#include <NodeList.h>
const int MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS = 1000;
typedef QSharedPointer<Assignment> SharedAssignmentPointer;
class DomainServer : public QCoreApplication, public HTTPRequestHandler {
Q_OBJECT
@ -35,39 +34,27 @@ public slots:
void nodeKilled(SharedNodePointer node);
private:
bool readConfigFile(const char* path);
QString readServerAssignmentConfig(QJsonObject jsonObj, const char* nodeName);
void prepopulateStaticAssignmentFile();
Assignment* matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NODE_TYPE nodeType);
Assignment* deployableAssignmentForRequest(Assignment& requestAssignment);
void removeAssignmentFromQueue(Assignment* removableAssignment);
bool checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket,
const HifiSockAddr& nodeLocalSocket,
const QUuid& checkInUUI);
void addReleasedAssignmentBackToQueue(Assignment* releasedAssignment);
void parseCommandLineTypeConfigs(const QStringList& argumentList, QSet<Assignment::Type>& excludedTypes);
void readConfigFile(const QString& path, QSet<Assignment::Type>& excludedTypes);
QString readServerAssignmentConfig(const QJsonObject& jsonObject, const QString& nodeName);
void addStaticAssignmentToAssignmentHash(Assignment* newAssignment);
void createStaticAssignmentsForTypeGivenConfigString(Assignment::Type type, const QString& configString);
void populateDefaultStaticAssignmentsExcludingTypes(const QSet<Assignment::Type>& excludedTypes);
unsigned char* addNodeToBroadcastPacket(unsigned char* currentPosition, Node* nodeToAdd);
SharedAssignmentPointer matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType);
SharedAssignmentPointer deployableAssignmentForRequest(const Assignment& requestAssignment);
void removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment);
void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
HTTPManager _HTTPManager;
QMutex _assignmentQueueMutex;
std::deque<Assignment*> _assignmentQueue;
QFile _staticAssignmentFile;
uchar* _staticAssignmentFileData;
Assignment* _staticAssignments;
const char* _voxelServerConfig;
const char* _particleServerConfig;
const char* _metavoxelServerConfig;
QHash<QUuid, SharedAssignmentPointer> _staticAssignmentHash;
QQueue<SharedAssignmentPointer> _assignmentQueue;
bool _hasCompletedRestartHold;
private slots:
void readAvailableDatagrams();
void addStaticAssignmentsBackToQueueAfterRestart();
void cleanup();
};
#endif /* defined(__hifi__DomainServer__) */

View file

@ -62,4 +62,4 @@ function maybePlaySound() {
}
// Connect a call back that happens every frame
Agent.willSendVisualDataCallback.connect(maybePlaySound);
Script.willSendVisualDataCallback.connect(maybePlaySound);

View file

@ -132,7 +132,7 @@ function draw() {
print(scriptB);
numberParticlesAdded++;
} else {
Agent.stop();
Script.stop();
}
print("Particles Stats: " + Particles.getLifetimeInSeconds() + " seconds," +
@ -150,5 +150,5 @@ function draw() {
// register the call back so it fires before each data send
print("here...\n");
Particles.setPacketsPerSecond(40000);
Agent.willSendVisualDataCallback.connect(draw);
Script.willSendVisualDataCallback.connect(draw);
print("and here...\n");

View file

@ -0,0 +1,202 @@
//
// controllerExample.js
// hifi
//
// Created by Brad Hefta-Gaub on 1/28/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// This is an example script that demonstrates use of the Controller class
//
//
// initialize our triggers
var triggerPulled = new Array();
var numberOfTriggers = Controller.getNumberOfTriggers();
for (t = 0; t < numberOfTriggers; t++) {
triggerPulled[t] = false;
}
function checkController() {
var numberOfTriggers = Controller.getNumberOfTriggers();
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
// this is expected for hydras
if (numberOfTriggers == 2 && controllersPerTrigger == 2) {
for (var t = 0; t < numberOfTriggers; t++) {
var triggerValue = Controller.getTriggerValue(t);
if (triggerPulled[t]) {
// must release to at least 0.1
if (triggerValue < 0.1) {
triggerPulled[t] = false; // unpulled
}
} else {
// must pull to at least 0.9
if (triggerValue > 0.9) {
triggerPulled[t] = true; // pulled
triggerToggled = true;
}
}
if (triggerToggled) {
print("a trigger was toggled");
}
}
}
}
function keyPressEvent(event) {
print("keyPressEvent event.key=" + event.key);
print("keyPressEvent event.text=" + event.text);
print("keyPressEvent event.isShifted=" + event.isShifted);
print("keyPressEvent event.isControl=" + event.isControl);
print("keyPressEvent event.isMeta=" + event.isMeta);
print("keyPressEvent event.isAlt=" + event.isAlt);
print("keyPressEvent event.isKeypad=" + event.isKeypad);
if (event.key == "A".charCodeAt(0)) {
print("the A key was pressed");
}
if (event.key == " ".charCodeAt(0)) {
print("the <space> key was pressed");
}
}
function mouseMoveEvent(event) {
print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y);
}
function touchBeginEvent(event) {
print("touchBeginEvent event.x,y=" + event.x + ", " + event.y);
}
function touchUpdateEvent(event) {
print("touchUpdateEvent event.x,y=" + event.x + ", " + event.y);
}
function touchEndEvent(event) {
print("touchEndEvent event.x,y=" + event.x + ", " + event.y);
}
// register the call back so it fires before each data send
Script.willSendVisualDataCallback.connect(checkController);
// Map keyPress and mouse move events to our callbacks
Controller.keyPressEvent.connect(keyPressEvent);
var KeyEvent_A = {
key: "A".charCodeAt(0),
text: "A",
isShifted: false,
isMeta: false
};
var KeyEvent_a = {
text: "a",
isShifted: false,
isMeta: false
};
var KeyEvent_a2 = {
key: "a".charCodeAt(0),
isShifted: false,
isMeta: false
};
var KeyEvent_a3 = {
text: "a"
};
var KeyEvent_A2 = {
text: "A"
};
var KeyEvent_9 = {
text: "9"
};
var KeyEvent_Num = {
text: "#"
};
var KeyEvent_At = {
text: "@"
};
var KeyEvent_MetaAt = {
text: "@",
isMeta: true
};
var KeyEvent_Up = {
text: "up"
};
var KeyEvent_Down = {
text: "down"
};
var KeyEvent_Left = {
text: "left"
};
var KeyEvent_Right = {
text: "right"
};
// prevent the A key from going through to the application
print("KeyEvent_A");
Controller.captureKeyEvents(KeyEvent_A);
print("KeyEvent_A2");
Controller.captureKeyEvents(KeyEvent_A2);
print("KeyEvent_a");
Controller.captureKeyEvents(KeyEvent_a);
print("KeyEvent_a2");
Controller.captureKeyEvents(KeyEvent_a2);
print("KeyEvent_a3");
Controller.captureKeyEvents(KeyEvent_a3);
print("KeyEvent_9");
Controller.captureKeyEvents(KeyEvent_9);
print("KeyEvent_Num");
Controller.captureKeyEvents(KeyEvent_Num);
print("KeyEvent_At");
Controller.captureKeyEvents(KeyEvent_At);
print("KeyEvent_MetaAt");
Controller.captureKeyEvents(KeyEvent_MetaAt);
print("KeyEvent_Up");
Controller.captureKeyEvents(KeyEvent_Up);
print("KeyEvent_Down");
Controller.captureKeyEvents(KeyEvent_Down);
print("KeyEvent_Left");
Controller.captureKeyEvents(KeyEvent_Left);
print("KeyEvent_Right");
Controller.captureKeyEvents(KeyEvent_Right);
Controller.mouseMoveEvent.connect(mouseMoveEvent);
// Map touch events to our callbacks
Controller.touchBeginEvent.connect(touchBeginEvent);
Controller.touchUpdateEvent.connect(touchUpdateEvent);
Controller.touchEndEvent.connect(touchEndEvent);
// disable the standard application for touch events
Controller.captureTouchEvents();
function scriptEnding() {
// re-enabled the standard application for touch events
Controller.releaseTouchEvents();
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -20,7 +20,7 @@ function scriptEnding() {
}
// register the call back so it fires before each data send
Agent.willSendVisualDataCallback.connect(displayCount);
Script.willSendVisualDataCallback.connect(displayCount);
// register our scriptEnding callback
Agent.scriptEnding.connect(scriptEnding);
Script.scriptEnding.connect(scriptEnding);

View file

@ -69,4 +69,4 @@ function checkSticks() {
}
// Connect a call back that happens every frame
Agent.willSendVisualDataCallback.connect(checkSticks);
Script.willSendVisualDataCallback.connect(checkSticks);

View file

@ -9,6 +9,9 @@
//
var count = 0;
var moveUntil = 2000;
var stopAfter = moveUntil + 100;
var expectedLifetime = (moveUntil/60) + 1; // 1 second after done moving...
var originalProperties = {
position: { x: 10,
@ -28,7 +31,9 @@ var originalProperties = {
color: { red: 0,
green: 255,
blue: 0 }
blue: 0 },
lifetime: expectedLifetime
};
@ -38,19 +43,18 @@ var positionDelta = { x: 0.05, y: 0, z: 0 };
var particleID = Particles.addParticle(originalProperties);
function moveParticle() {
if (count >= 100) {
//Agent.stop();
if (count >= moveUntil) {
// delete it...
if (count == 100) {
if (count == moveUntil) {
print("calling Particles.deleteParticle()");
Particles.deleteParticle(particleID);
}
// stop it...
if (count >= 200) {
print("calling Agent.stop()");
Agent.stop();
if (count >= stopAfter) {
print("calling Script.stop()");
Script.stop();
}
count++;
@ -77,19 +81,9 @@ function moveParticle() {
print("newProperties.position = " + newProperties.position.x + "," + newProperties.position.y+ "," + newProperties.position.z);
Particles.editParticle(particleID, newProperties);
// also check to see if we can "find" particles...
var searchAt = { x: 9, y: 0, z: 0};
var searchRadius = 2;
var foundParticle = Particles.findClosestParticle(searchAt, searchRadius);
if (foundParticle.isKnownID) {
print("found particle:" + foundParticle.id);
} else {
print("could not find particle in or around x=9 to x=11:");
}
}
// register the call back so it fires before each data send
Agent.willSendVisualDataCallback.connect(moveParticle);
Script.willSendVisualDataCallback.connect(moveParticle);

View file

@ -1,5 +1,5 @@
//
// editParticleExample.js
// findParticleExample.js
// hifi
//
// Created by Brad Hefta-Gaub on 1/24/14.
@ -65,8 +65,8 @@ function findParticles() {
// run for a while, then clean up
// stop it...
if (iteration >= 100) {
print("calling Agent.stop()");
Agent.stop();
print("calling Script.stop()");
Script.stop();
}
print("--------------------------");
@ -122,7 +122,7 @@ function findParticles() {
// register the call back so it fires before each data send
Agent.willSendVisualDataCallback.connect(findParticles);
Script.willSendVisualDataCallback.connect(findParticles);
// register our scriptEnding callback
Agent.scriptEnding.connect(scriptEnding);
Script.scriptEnding.connect(scriptEnding);

View file

@ -60,8 +60,8 @@ function makeFountain() {
totalParticles++;
}
if (totalParticles > 100) {
Agent.stop();
Script.stop();
}
}
// register the call back so it fires before each data send
Agent.willSendVisualDataCallback.connect(makeFountain);
Script.willSendVisualDataCallback.connect(makeFountain);

133
examples/gameoflife.js Normal file
View file

@ -0,0 +1,133 @@
// Add your JavaScript for assignment below this line
// The following is an example of Conway's Game of Life (http://en.wikipedia.org/wiki/Conway's_Game_of_Life)
var NUMBER_OF_CELLS_EACH_DIMENSION = 64;
var NUMBER_OF_CELLS = NUMBER_OF_CELLS_EACH_DIMENSION * NUMBER_OF_CELLS_EACH_DIMENSION;
var currentCells = [];
var nextCells = [];
var METER_LENGTH = 1;
var cellScale = (NUMBER_OF_CELLS_EACH_DIMENSION * METER_LENGTH) / NUMBER_OF_CELLS_EACH_DIMENSION;
// randomly populate the cell start values
for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) {
// create the array to hold this row
currentCells[i] = [];
// create the array to hold this row in the nextCells array
nextCells[i] = [];
for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) {
currentCells[i][j] = Math.floor(Math.random() * 2);
// put the same value in the nextCells array for first board draw
nextCells[i][j] = currentCells[i][j];
}
}
function isNeighbourAlive(i, j) {
if (i < 0 || i >= NUMBER_OF_CELLS_EACH_DIMENSION
|| i < 0 || j >= NUMBER_OF_CELLS_EACH_DIMENSION) {
return 0;
} else {
return currentCells[i][j];
}
}
function updateCells() {
var i = 0;
var j = 0;
for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) {
for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) {
// figure out the number of live neighbours for the i-j cell
var liveNeighbours =
isNeighbourAlive(i + 1, j - 1) + isNeighbourAlive(i + 1, j) + isNeighbourAlive(i + 1, j + 1) +
isNeighbourAlive(i, j - 1) + isNeighbourAlive(i, j + 1) +
isNeighbourAlive(i - 1, j - 1) + isNeighbourAlive(i - 1, j) + isNeighbourAlive(i - 1, j + 1);
if (currentCells[i][j]) {
// live cell
if (liveNeighbours < 2) {
// rule #1 - under-population - this cell will die
// mark it zero to mark the change
nextCells[i][j] = 0;
} else if (liveNeighbours < 4) {
// rule #2 - this cell lives
// mark it -1 to mark no change
nextCells[i][j] = -1;
} else {
// rule #3 - overcrowding - this cell dies
// mark it zero to mark the change
nextCells[i][j] = 0;
}
} else {
// dead cell
if (liveNeighbours == 3) {
// rule #4 - reproduction - this cell revives
// mark it one to mark the change
nextCells[i][j] = 1;
} else {
// this cell stays dead
// mark it -1 for no change
nextCells[i][j] = -1;
}
}
if (Math.random() < 0.001) {
// Random mutation to keep things interesting in there.
nextCells[i][j] = 1;
}
}
}
for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) {
for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) {
if (nextCells[i][j] != -1) {
// there has been a change to this cell, change the value in the currentCells array
currentCells[i][j] = nextCells[i][j];
}
}
}
}
function sendNextCells() {
for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) {
for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) {
if (nextCells[i][j] != -1) {
// there has been a change to the state of this cell, send it
// find the x and y position for this voxel, z = 0
var x = j * cellScale;
var y = i * cellScale;
// queue a packet to add a voxel for the new cell
var color = (nextCells[i][j] == 1) ? 255 : 1;
Voxels.setVoxel(x, y, 0, cellScale, color, color, color);
}
}
}
}
var sentFirstBoard = false;
function step() {
print("step()...");
if (sentFirstBoard) {
// we've already sent the first full board, perform a step in time
updateCells();
} else {
// this will be our first board send
sentFirstBoard = true;
}
sendNextCells();
}
print("here");
Script.willSendVisualDataCallback.connect(step);
Voxels.setPacketsPerSecond(200);
print("now here");

View file

@ -0,0 +1,26 @@
//
// globalCollisionsExample.js
// hifi
//
// Created by Brad Hefta-Gaub on 1/29/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// This is an example script that demonstrates use of the Controller class
//
//
function particleCollisionWithVoxel(particle, voxel) {
print("particleCollisionWithVoxel()..");
print(" particle.getID()=" + particle.id);
print(" voxel color...=" + voxel.red + ", " + voxel.green + ", " + voxel.blue);
}
function particleCollisionWithParticle(particleA, particleB) {
print("particleCollisionWithParticle()..");
print(" particleA.getID()=" + particleA.id);
print(" particleB.getID()=" + particleB.id);
}
Particles.particleCollisionWithVoxel.connect(particleCollisionWithVoxel);
Particles.particleCollisionWithParticle.connect(particleCollisionWithParticle);

View file

@ -72,13 +72,13 @@ function checkController() {
" function collisionWithVoxel(voxel) { " +
" print('collisionWithVoxel(voxel)... '); " +
" print('myID=' + Particle.getID() + '\\n'); " +
" var voxelColor = voxel.getColor();" +
" print('voxelColor=' + voxelColor.red + ', ' + voxelColor.green + ', ' + voxelColor.blue + '\\n'); " +
" var voxelColor = { red: voxel.red, green: voxel.green, blue: voxel.blue };" +
" var voxelAt = { x: voxel.x, y: voxel.y, z: voxel.z };" +
" var voxelScale = voxel.s;" +
" print('voxelColor=' + voxel.red + ', ' + voxel.green + ', ' + voxel.blue + '\\n'); " +
" var myColor = Particle.getColor();" +
" print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); " +
" Particle.setColor(voxelColor); " +
" var voxelAt = voxel.getPosition();" +
" var voxelScale = voxel.getScale();" +
" Voxels.eraseVoxel(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale); " +
" print('Voxels.eraseVoxel(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " +
" } " +
@ -99,4 +99,4 @@ function checkController() {
// register the call back so it fires before each data send
Agent.willSendVisualDataCallback.connect(checkController);
Script.willSendVisualDataCallback.connect(checkController);

74
examples/lookWithMouse.js Normal file
View file

@ -0,0 +1,74 @@
//
// lookWithMouse.js
// hifi
//
// Created by Brad Hefta-Gaub on 1/28/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// This is an example script that demonstrates use of the Controller class
//
//
var isMouseDown = false;
var lastX = 0;
var lastY = 0;
var yawFromMouse = 0;
var pitchFromMouse = 0;
function mousePressEvent(event) {
print("mousePressEvent event.x,y=" + event.x + ", " + event.y);
isMouseDown = true;
lastX = event.x;
lastY = event.y;
}
function mouseReleaseEvent(event) {
print("mouseReleaseEvent event.x,y=" + event.x + ", " + event.y);
isMouseDown = false;
}
function mouseMoveEvent(event) {
print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y);
if (isMouseDown) {
print("isMouseDown... attempting to change pitch...");
var MOUSE_YAW_SCALE = -0.25;
var MOUSE_PITCH_SCALE = -12.5;
var FIXED_MOUSE_TIMESTEP = 0.016;
yawFromMouse += ((event.x - lastX) * MOUSE_YAW_SCALE * FIXED_MOUSE_TIMESTEP);
pitchFromMouse += ((event.y - lastY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP);
lastX = event.x;
lastY = event.y;
}
}
function update() {
// rotate body yaw for yaw received from mouse
MyAvatar.orientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMouse, z: 0 } ));
yawFromMouse = 0;
// apply pitch from mouse
MyAvatar.headPitch = MyAvatar.headPitch + pitchFromMouse;
pitchFromMouse = 0;
}
// Map the mouse events to our functions
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
// disable the standard application for mouse events
Controller.captureMouseEvents();
function scriptEnding() {
// re-enabled the standard application for mouse events
Controller.releaseMouseEvents();
}
MyAvatar.bodyYaw = 0;
MyAvatar.bodyPitch = 0;
MyAvatar.bodyRoll = 0;
// would be nice to change to update
Script.willSendVisualDataCallback.connect(update);
Script.scriptEnding.connect(scriptEnding);

View file

@ -41,4 +41,4 @@ function moveVoxel() {
Voxels.setPacketsPerSecond(300);
// Connect a call back that happens every frame
Agent.willSendVisualDataCallback.connect(moveVoxel);
Script.willSendVisualDataCallback.connect(moveVoxel);

View file

@ -65,13 +65,13 @@ function checkController() {
" function collisionWithVoxel(voxel) { " +
" print('collisionWithVoxel(voxel)... '); " +
" print('myID=' + Particle.getID() + '\\n'); " +
" var voxelColor = voxel.getColor();" +
" var voxelColor = { red: voxel.red, green: voxel.green, blue: voxel.blue };" +
" var voxelAt = { x: voxel.x, y: voxel.y, z: voxel.z };" +
" var voxelScale = voxel.s;" +
" print('voxelColor=' + voxelColor.red + ', ' + voxelColor.green + ', ' + voxelColor.blue + '\\n'); " +
" var myColor = Particle.getColor();" +
" print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); " +
" Particle.setColor(voxelColor); " +
" var voxelAt = voxel.getPosition();" +
" var voxelScale = voxel.getScale();" +
" Voxels.setVoxel(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale, 255, 255, 0); " +
" print('Voxels.setVoxel(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " +
" } " +
@ -93,4 +93,4 @@ function checkController() {
// register the call back so it fires before each data send
Agent.willSendVisualDataCallback.connect(checkController);
Script.willSendVisualDataCallback.connect(checkController);

View file

@ -95,7 +95,7 @@ function moveBird() {
var nowTimeInSeconds = new Date().getTime() / 1000;
if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) {
print("our bird is dying, stop our script");
Agent.stop();
Script.stop();
return;
}
@ -171,4 +171,4 @@ function moveBird() {
}
}
// register the call back so it fires before each data send
Agent.willSendVisualDataCallback.connect(moveBird);
Script.willSendVisualDataCallback.connect(moveBird);

View file

@ -0,0 +1,51 @@
//
// particleModelExample.js
// hifi
//
// Created by Brad Hefta-Gaub on 1/28/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// This is an example script that demonstrates creating and editing a particle
//
var count = 0;
var stopAfter = 100;
var modelProperties = {
position: { x: 1, y: 1, z: 1 },
velocity: { x: 0.5, y: 0, z: 0.5 },
gravity: { x: 0, y: 0, z: 0 },
damping: 0,
radius : 0.25,
modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
lifetime: 20
};
var ballProperties = {
position: { x: 1, y: 0.5, z: 1 },
velocity: { x: 0.5, y: 0, z: 0.5 },
gravity: { x: 0, y: 0, z: 0 },
damping: 0,
radius : 0.25,
color: { red: 255, green: 0, blue: 0 },
lifetime: 20
};
var modelParticleID = Particles.addParticle(modelProperties);
var ballParticleID = Particles.addParticle(ballProperties);
function endAfterAWhile() {
// stop it...
if (count >= stopAfter) {
print("calling Script.stop()");
Script.stop();
}
print("count =" + count);
count++;
}
// register the call back so it fires before each data send
Script.willSendVisualDataCallback.connect(endAfterAWhile);

View file

@ -17,4 +17,4 @@ function maybePlaySound() {
}
// Connect a call back that happens every frame
Agent.willSendVisualDataCallback.connect(maybePlaySound);
Script.willSendVisualDataCallback.connect(maybePlaySound);

View file

@ -0,0 +1,50 @@
//
// rideAlongWithAParticleExample.js
// hifi
//
// Created by Brad Hefta-Gaub on 1/24/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// This is an example script that demonstrates "finding" particles
//
var iteration = 0;
var lengthOfRide = 2000; // in iterations
var particleA = Particles.addParticle(
{
position: { x: 10, y: 0, z: 10 },
velocity: { x: 5, y: 0, z: 5 },
gravity: { x: 0, y: 0, z: 0 },
radius : 0.1,
color: { red: 0, green: 255, blue: 0 },
damping: 0,
lifetime: (lengthOfRide * 60) + 1
});
function rideWithParticle() {
if (iteration <= lengthOfRide) {
// Check to see if we've been notified of the actual identity of the particles we created
if (!particleA.isKnownID) {
particleA = Particles.identifyParticle(particleA);
}
var propertiesA = Particles.getParticleProperties(particleA);
var newPosition = propertiesA.position;
MyAvatar.position = { x: propertiesA.position.x,
y: propertiesA.position.y + 2,
z: propertiesA.position.z };
} else {
Script.stop();
}
iteration++;
print("iteration="+iteration);
}
// register the call back so it fires before each data send
Script.willSendVisualDataCallback.connect(rideWithParticle);

View file

@ -226,4 +226,4 @@ function checkController() {
// register the call back so it fires before each data send
Agent.willSendVisualDataCallback.connect(checkController);
Script.willSendVisualDataCallback.connect(checkController);

View file

@ -130,4 +130,4 @@ function moveBird() {
Voxels.setPacketsPerSecond(10000);
// Connect a call back that happens every frame
Agent.willSendVisualDataCallback.connect(moveBird);
Script.willSendVisualDataCallback.connect(moveBird);

View file

@ -0,0 +1,19 @@
{
"importFormats" : [
{
"extension": "png",
"description": "Square PNG",
"icon": "raster.svg"
},
{
"extension": "svo",
"description": "Sparse Voxel Octree Files",
"icon": "voxel.svg"
},
{
"extension": "schematic",
"description": "Schematic Files",
"icon": "voxel.svg"
}
]
}

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
<g>
<path fill="#666666" d="M18.223,11.845c0,0.835-0.683,1.519-1.519,1.519h-5.163c0,0.807,0.607,1.49,0.607,1.822
s-0.275,0.607-0.607,0.607H6.682c-0.332,0-0.607-0.275-0.607-0.607c0-0.351,0.607-0.997,0.607-1.822H1.519
C0.683,13.363,0,12.68,0,11.845V1.519C0,0.684,0.683,0,1.519,0h15.186c0.835,0,1.519,0.684,1.519,1.519V11.845z M17.008,1.519
c0-0.161-0.143-0.304-0.304-0.304H1.519c-0.161,0-0.304,0.143-0.304,0.304v7.896c0,0.161,0.143,0.304,0.304,0.304h15.186
c0.161,0,0.304-0.143,0.304-0.304V1.519z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 884 B

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
<g>
<g>
<rect fill="#666666" width="17" height="1"/>
</g>
<g>
<rect y="2" fill="#666666" width="17" height="11"/>
</g>
<rect x="1" y="4" fill="#989898" width="1" height="1"/>
<rect x="1" y="6" fill="#989898" width="1" height="1"/>
<rect x="4" y="4" fill="#989898" width="1" height="1"/>
<rect x="4" y="6" fill="#989898" width="1" height="1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 732 B

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
<g>
<g>
<path fill="#666666" d="M17.615,4.556v11.541c0,0.503-0.408,0.911-0.911,0.911H7.593c-0.503,0-0.911-0.408-0.911-0.911v-2.733
H1.519c-0.503,0-0.911-0.408-0.911-0.911V6.074c0-0.503,0.295-1.206,0.646-1.557l3.872-3.872C5.477,0.294,6.179,0,6.682,0h3.948
c0.503,0,0.911,0.408,0.911,0.911v3.113c0.37-0.219,0.845-0.38,1.215-0.38h3.948C17.207,3.645,17.615,4.053,17.615,4.556z
M10.326,5.163V1.215H6.682v3.948c0,0.503-0.408,0.911-0.911,0.911H1.822v6.074h4.859v-2.43c0-0.503,0.295-1.206,0.646-1.557
L10.326,5.163z M2.629,4.859h2.838V2.021L2.629,4.859z M16.4,4.859h-3.645v3.948c0,0.503-0.408,0.911-0.911,0.911H7.896v6.074
H16.4V4.859z M8.703,8.504h2.838V5.666L8.703,8.504z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
<g>
<path fill="#666666" d="M12.148,13.667c0,0.503-0.408,0.911-0.911,0.911H0.911C0.408,14.578,0,14.17,0,13.667V0.911
C0,0.408,0.408,0,0.911,0h6.074c0.503,0,1.206,0.294,1.557,0.646l2.961,2.961c0.351,0.351,0.646,1.054,0.646,1.557V13.667z
M10.934,13.363V6.074H6.985c-0.503,0-0.911-0.408-0.911-0.911V1.215H1.215v12.148H10.934z M9.719,8.2
c0,0.171-0.133,0.304-0.304,0.304H2.733C2.562,8.504,2.43,8.371,2.43,8.2V7.593c0-0.171,0.133-0.304,0.304-0.304h6.682
c0.171,0,0.304,0.133,0.304,0.304V8.2z M9.719,10.63c0,0.171-0.133,0.304-0.304,0.304H2.733c-0.171,0-0.304-0.133-0.304-0.304
v-0.607c0-0.171,0.133-0.304,0.304-0.304h6.682c0.171,0,0.304,0.133,0.304,0.304V10.63z M10.857,4.859
c-0.057-0.161-0.142-0.323-0.208-0.389L7.678,1.5C7.612,1.433,7.45,1.348,7.289,1.291v3.568H10.857z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
<g>
<path fill="#666666" d="M14.854,10.569c0,1.098-0.901,2-2,2H2c-1.098,0-2-0.901-2-2V2c0-1.098,0.901-2,2-2h2.856
c1.098,0,2,0.901,2,2v0.286h5.999c1.098,0,2,0.901,2,2V10.569z M13.711,4.285c0-0.473-0.384-0.857-0.857-0.857H6.57
c-0.473,0-0.857-0.384-0.857-0.857V2c0-0.473-0.384-0.857-0.857-0.857H2C1.526,1.143,1.143,1.526,1.143,2v8.569
c0,0.473,0.384,0.857,0.857,0.857h10.854c0.473,0,0.857-0.384,0.857-0.857V4.285z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 800 B

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
<g>
<g>
<path fill="#666666" d="M16.191,7.893c-0.054,0.063-0.139,0.106-0.223,0.116c-0.011,0-0.021,0-0.032,0
c-0.085,0-0.159-0.021-0.223-0.074L8.375,1.816L1.037,7.935C0.963,7.988,0.878,8.02,0.782,8.009
c-0.085-0.01-0.17-0.053-0.223-0.116l-0.657-0.785c-0.117-0.138-0.096-0.36,0.042-0.477l7.625-6.352
c0.445-0.372,1.166-0.372,1.612,0l2.587,2.163V0.374c0-0.191,0.148-0.339,0.34-0.339h2.036c0.19,0,0.339,0.148,0.339,0.339v4.327
l2.322,1.93c0.139,0.117,0.159,0.339,0.043,0.477L16.191,7.893z M14.483,12.93c0,0.371-0.308,0.679-0.679,0.679H9.732V9.536H7.018
v4.072H2.945c-0.371,0-0.679-0.308-0.679-0.679V7.84c0-0.021,0.011-0.042,0.011-0.063l6.098-5.027l6.098,5.027
c0.011,0.021,0.011,0.042,0.011,0.063V12.93z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
<g>
<path fill="#666666" d="M15.049,10.75c0,0.688-0.562,1.25-1.25,1.25h-12.5c-0.688,0-1.25-0.562-1.25-1.25v-9.5
C0.049,0.562,0.611,0,1.299,0h12.5c0.688,0,1.25,0.562,1.25,1.25V10.75z M1.299,1c-0.133,0-0.25,0.117-0.25,0.25v9.5
c0,0.133,0.117,0.25,0.25,0.25h12.5c0.133,0,0.25-0.117,0.25-0.25v-9.5c0-0.133-0.117-0.25-0.25-0.25H1.299z M3.549,5
c-0.828,0-1.5-0.672-1.5-1.5S2.72,2,3.549,2s1.5,0.672,1.5,1.5S4.377,5,3.549,5z M13.049,10h-11V8.5l2.5-2.5l1.25,1.25l4-4
l3.25,3.25V10z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 862 B

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
<g>
<g>
<path fill="#666666" d="M13.402,4.612c0-0.098-0.052-0.188-0.137-0.237c-0.085-0.049-0.189-0.049-0.274,0L7.704,7.428
C7.423,7.59,7.25,7.89,7.25,8.215v6.105c0,0.098,0.052,0.188,0.137,0.237c0.085,0.049,0.189,0.049,0.274,0l5.085-2.936
c0.407-0.235,0.657-0.668,0.657-1.138V4.612z"/>
<path fill="#666666" d="M0.41,4.375c-0.085-0.049-0.189-0.049-0.274,0C0.052,4.424,0,4.514,0,4.612c0,0,0,0,0,0v5.872
c0,0.469,0.25,0.903,0.657,1.138l5.085,2.936c0,0,0,0,0,0c0.085,0.049,0.189,0.049,0.274,0c0.085-0.049,0.136-0.139,0.136-0.237
V8.215c0-0.325-0.173-0.625-0.455-0.787L0.41,4.375z"/>
</g>
<path fill="#666666" d="M12.443,3.586c0.085-0.049,0.137-0.139,0.137-0.237c0-0.098-0.052-0.188-0.137-0.237c0,0,0,0,0,0
L7.358,0.176c-0.407-0.235-0.907-0.235-1.313,0L0.959,3.112c0,0,0,0,0,0C0.874,3.161,0.822,3.251,0.822,3.349
c0,0.098,0.052,0.188,0.137,0.237l5.287,3.053c0.281,0.162,0.628,0.162,0.909,0L12.443,3.586z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -25,5 +25,5 @@ void main(void) {
// modulate texture by base color and add specular contribution
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) +
pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular;
vec4(pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular.rgb, 0.0);
}

View file

@ -37,5 +37,5 @@ void main(void) {
// modulate texture by base color and add specular contribution
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) +
pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular;
vec4(pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular.rgb, 0.0);
}

Binary file not shown.

View file

@ -0,0 +1,88 @@
/*
* import_dialog.qss
* hifi
*
* Created by Stojce on 1/5/2014.
* Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
*/
* {
font-family: Helvetica, Arial, sans-serif;
}
QLabel {
font-size: 16px;
color: #333333;
}
QLabel#infoLabel {
padding: 7px 0 0 7px;
color: #666666;
}
QPushButton {
border-width: 0;
border-radius: 9px;
font-size: 18px;
padding: 17px 0px 15px;
width: 107px;
margin-top: 20px;
margin-bottom: 18px;
}
QPushButton#importButton {
color: #FFFFFF;
font-weight: bold;
margin-right: 6px;
}
QPushButton#importButton:enabled {
background: #333333;
}
QPushButton#importButton:!enabled {
background: rgba(50, 50, 50, 0.5);
}
QPushButton#cancelButton {
color: #333333;
background-color: #FFFFFF;
width: 74px;
margin-right: 11px;
}
QSidebar, QTreeView {
border: 1px solid #C5C5C5;
font-size: 14px;
margin: 0px;
selection-background-color: #BDE4E3;
selection-color: #333333;
}
QTreeView {
border-left: none;
}
QSplitter::handle, QDialog {
background-color: white;
}
QTreeView QHeaderView {
background: white;
}
QTreeView QHeaderView:section {
border-left: none;
border-top: none;
border-bottom: 1px solid #C5C5C5;
border-right: 1px solid #C5C5C5;
background: white;
color: #666666;
padding: 10px 20px;
}
QSidebar::item,
QTreeView::item {
padding: 5px 0 4px;
}

View file

@ -17,6 +17,7 @@ class AbstractLoggerInterface : public QObject {
Q_OBJECT
public:
AbstractLoggerInterface(QObject* parent = NULL) : QObject(parent) {};
inline bool extraDebugging() { return _extraDebugging; };
inline void setExtraDebugging(bool debugging) { _extraDebugging = debugging; };

File diff suppressed because it is too large Load diff

View file

@ -142,7 +142,7 @@ public:
glm::vec3 getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVoxel);
QGLWidget* getGLWidget() { return _glWidget; }
MyAvatar* getAvatar() { return &_myAvatar; }
MyAvatar* getAvatar() { return _myAvatar; }
Audio* getAudio() { return &_audio; }
Camera* getCamera() { return &_myCamera; }
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
@ -175,8 +175,7 @@ public:
Profile* getProfile() { return &_profile; }
void resetProfile(const QString& username);
void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
const QSet<NODE_TYPE>& destinationNodeTypes);
void controlledBroadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes);
void setupWorldLight();
@ -265,7 +264,7 @@ private:
void updateProjectionMatrix(Camera& camera, bool updateViewFrustum = true);
static bool sendVoxelsOperation(OctreeElement* node, void* extraData);
static void sendPingPackets();
void sendPingPackets();
void initDisplay();
void init();
@ -299,14 +298,18 @@ private:
void renderHighlightVoxel(VoxelDetail voxel);
void updateAvatar(float deltaTime);
void queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, NodeToJurisdictionMap& jurisdictions);
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions);
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
glm::vec3 getSunDirection();
void updateShadowMap();
void displayOverlay();
void displayStatsBackground(unsigned int rgba, int x, int y, int width, int height);
void displayStats();
void checkStatsClick();
void toggleStatsExpanded();
void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false);
void renderViewFrustum(ViewFrustum& viewFrustum);
void checkBandwidthMeterClick();
@ -327,6 +330,7 @@ private:
QMainWindow* _window;
QGLWidget* _glWidget;
bool _statsExpanded;
BandwidthMeter _bandwidthMeter;
QThread* _nodeThread;
@ -351,7 +355,7 @@ private:
VoxelSystem _voxels;
VoxelTree _clipboard; // if I copy/paste
VoxelImporter _voxelImporter;
VoxelImporter* _voxelImporter;
VoxelSystem _sharedVoxelSystem;
ViewFrustum _sharedVoxelSystemViewFrustum;
@ -370,10 +374,10 @@ private:
VoxelQuery _voxelQuery; // NodeData derived class for querying voxels from voxel server
AvatarManager _avatarManager;
MyAvatar _myAvatar; // The rendered avatar of oneself
Profile _profile; // The data-server linked profile for this user
MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
Profile _profile; // The data-server linked profile for this user
Transmitter _myTransmitter; // Gets UDP data from transmitter app used to animate the avatar
Transmitter _myTransmitter; // Gets UDP data from transmitter app used to animate the avatar
Faceshift _faceshift;
@ -399,7 +403,7 @@ private:
int _mouseY;
int _mouseDragStartedX;
int _mouseDragStartedY;
uint64_t _lastMouseMove;
quint64 _lastMouseMove;
bool _mouseHidden;
bool _seenMouseMove;
@ -477,9 +481,8 @@ private:
PieMenu _pieMenu;
int parseOctreeStats(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderAddress);
void trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength,
const HifiSockAddr& senderSockAddr, bool wasStatsPacket);
int parseOctreeStats(const QByteArray& packet, const HifiSockAddr& senderAddress);
void trackIncomingVoxelPacket(const QByteArray& packet, const HifiSockAddr& senderSockAddr, bool wasStatsPacket);
NodeToJurisdictionMap _voxelServerJurisdictions;
NodeToJurisdictionMap _particleServerJurisdictions;
@ -500,6 +503,7 @@ private:
void checkVersion();
void displayUpdateDialog();
bool shouldSkipVersion(QString latestVersion);
void takeSnapshot();
};
#endif /* defined(__interface__Application__) */

View file

@ -22,7 +22,6 @@
#include <AngleUtil.h>
#include <NodeList.h>
#include <NodeTypes.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <StdDev.h>
@ -40,8 +39,9 @@ static const float AUDIO_CALLBACK_MSECS = (float) NETWORK_BUFFER_LENGTH_SAMPLES_
// Mute icon configration
static const int ICON_SIZE = 24;
static const int ICON_LEFT = 20;
static const int BOTTOM_PADDING = 110;
static const int ICON_LEFT = 0;
static const int ICON_TOP = 115;
static const int ICON_TOP_MIRROR = 220;
Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* parent) :
AbstractAudioInterface(parent),
@ -285,8 +285,8 @@ void Audio::start() {
void Audio::handleAudioInput() {
static char monoAudioDataPacket[MAX_PACKET_SIZE];
static int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO);
static int leadingBytes = numBytesPacketHeader + sizeof(glm::vec3) + sizeof(glm::quat) + NUM_BYTES_RFC4122_UUID;
static int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(PacketTypeMicrophoneAudioNoEcho);
static int leadingBytes = numBytesPacketHeader + sizeof(glm::vec3) + sizeof(glm::quat);
static int16_t* monoAudioSamples = (int16_t*) (monoAudioDataPacket + leadingBytes);
@ -365,7 +365,7 @@ void Audio::handleAudioInput() {
NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
NodeList* nodeList = NodeList::getInstance();
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER);
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
if (audioMixer && nodeList->getNodeActiveSocketOrPing(audioMixer.data())) {
MyAvatar* interfaceAvatar = Application::getInstance()->getAvatar();
@ -375,16 +375,10 @@ void Audio::handleAudioInput() {
// we need the amount of bytes in the buffer + 1 for type
// + 12 for 3 floats for position + float for bearing + 1 attenuation byte
PACKET_TYPE packetType = Menu::getInstance()->isOptionChecked(MenuOption::EchoServerAudio)
? PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO : PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO;
PacketType packetType = Menu::getInstance()->isOptionChecked(MenuOption::EchoServerAudio)
? PacketTypeMicrophoneAudioWithEcho : PacketTypeMicrophoneAudioNoEcho;
char* currentPacketPtr = monoAudioDataPacket + populateTypeAndVersion((unsigned char*) monoAudioDataPacket,
packetType);
// pack Source Data
QByteArray rfcUUID = NodeList::getInstance()->getOwnerUUID().toRfc4122();
memcpy(currentPacketPtr, rfcUUID.constData(), rfcUUID.size());
currentPacketPtr += rfcUUID.size();
char* currentPacketPtr = monoAudioDataPacket + populatePacketHeader(monoAudioDataPacket, packetType);
// memcpy the three float positions
memcpy(currentPacketPtr, &headPosition, sizeof(headPosition));
@ -433,7 +427,7 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
}
}
_ringBuffer.parseData((unsigned char*) audioByteArray.data(), audioByteArray.size());
_ringBuffer.parseData(audioByteArray);
static float networkOutputToOutputRatio = (_desiredOutputFormat.sampleRate() / (float) _outputFormat.sampleRate())
* (_desiredOutputFormat.channelCount() / (float) _outputFormat.channelCount());
@ -545,16 +539,16 @@ void Audio::toggleMute() {
}
void Audio::render(int screenWidth, int screenHeight) {
if (_audioInput) {
if (_audioInput && _audioOutput) {
glLineWidth(2.0);
glBegin(GL_LINES);
glColor3f(1,1,1);
glColor3f(.93f, .93f, .93f);
int startX = 20.0;
int currentX = startX;
int topY = screenHeight - 40;
int bottomY = screenHeight - 20;
float frameWidth = 20.0;
int topY = screenHeight - 45;
int bottomY = screenHeight - 25;
float frameWidth = 23.0;
float halfY = topY + ((bottomY - topY) / 2.0);
// draw the lines for the base of the ring buffer
@ -583,9 +577,9 @@ void Audio::render(int screenWidth, int screenHeight) {
float msLeftForAudioOutput = (secondsLeftForAudioOutput + secondsLeftForRingBuffer) * 1000;
if (_numFramesDisplayStarve == 0) {
glColor3f(0, 1, 0);
glColor3f(0, .8f, .4f);
} else {
glColor3f(0.5 + (_numFramesDisplayStarve / 20.0f), 0, 0);
glColor3f(0.5 + (_numFramesDisplayStarve / 20.0f), .2f, .4f);
_numFramesDisplayStarve--;
}
@ -596,44 +590,44 @@ void Audio::render(int screenWidth, int screenHeight) {
}
glBegin(GL_QUADS);
glVertex2f(startX, topY + 2);
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth, topY + 2);
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth, bottomY - 2);
glVertex2f(startX, bottomY - 2);
glVertex2f(startX + 1, topY + 2);
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth + 1, topY + 2);
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth + 1, bottomY - 2);
glVertex2f(startX + 1, bottomY - 2);
glEnd();
// Show a yellow bar with the averaged msecs latency you are hearing (from time of packet receipt)
glColor3f(1,1,0);
glColor3f(1, .8f, 0);
glBegin(GL_QUADS);
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth - 2, topY - 2);
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth + 2, topY - 2);
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth + 2, bottomY + 2);
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth - 2, bottomY + 2);
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth - 1, topY - 2);
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth + 3, topY - 2);
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth + 3, bottomY + 2);
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth - 1, bottomY + 2);
glEnd();
char out[40];
sprintf(out, "%3.0f\n", _averagedLatency);
drawtext(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth - 10, topY - 9, 0.10f, 0, 1, 0, out, 1,1,0);
drawtext(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth - 10, topY - 9, 0.10f, 0, 1, 2, out, 1, .8f, 0);
// Show a red bar with the 'start' point of one frame plus the jitter buffer
glColor3f(1, 0, 0);
glColor3f(1, .2f, .4f);
int jitterBufferPels = (1.f + (float)getJitterBufferSamples()
/ (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL) * frameWidth;
sprintf(out, "%.0f\n", getJitterBufferSamples() / SAMPLE_RATE * 1000.f);
drawtext(startX + jitterBufferPels - 5, topY - 9, 0.10f, 0, 1, 0, out, 1, 0, 0);
drawtext(startX + jitterBufferPels - 5, topY - 9, 0.10f, 0, 1, 2, out, 1, .2f, .4f);
sprintf(out, "j %.1f\n", _measuredJitter);
if (Menu::getInstance()->getAudioJitterBufferSamples() == 0) {
drawtext(startX + jitterBufferPels - 5, bottomY + 12, 0.10f, 0, 1, 0, out, 1, 0, 0);
drawtext(startX + jitterBufferPels - 5, bottomY + 12, 0.10f, 0, 1, 2, out, 1, .2f, .4f);
} else {
drawtext(startX, bottomY + 12, 0.10f, 0, 1, 0, out, 1, 0, 0);
drawtext(startX, bottomY + 12, 0.10f, 0, 1, 2, out, 1, .2f, .4f);
}
glBegin(GL_QUADS);
glVertex2f(startX + jitterBufferPels - 2, topY - 2);
glVertex2f(startX + jitterBufferPels + 2, topY - 2);
glVertex2f(startX + jitterBufferPels + 2, bottomY + 2);
glVertex2f(startX + jitterBufferPels - 2, bottomY + 2);
glVertex2f(startX + jitterBufferPels - 1, topY - 2);
glVertex2f(startX + jitterBufferPels + 3, topY - 2);
glVertex2f(startX + jitterBufferPels + 3, bottomY + 2);
glVertex2f(startX + jitterBufferPels - 1, bottomY + 2);
glEnd();
}
@ -739,11 +733,13 @@ void Audio::handleAudioByteArray(const QByteArray& audioByteArray) {
void Audio::renderToolIcon(int screenHeight) {
_iconBounds = QRect(ICON_LEFT, screenHeight - BOTTOM_PADDING, ICON_SIZE, ICON_SIZE);
int iconTop = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) ? ICON_TOP_MIRROR : ICON_TOP;
_iconBounds = QRect(ICON_LEFT, iconTop, ICON_SIZE, ICON_SIZE);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _micTextureId);
glColor3f(1, 1, 1);
glColor3f(.93f, .93f, .93f);
glBegin(GL_QUADS);
glTexCoord2f(1, 1);

View file

@ -14,21 +14,21 @@
namespace { // .cpp-local
int const AREA_WIDTH = -280; // Width of the area used. Aligned to the right when negative.
int const AREA_HEIGHT = 40; // Height of the area used. Aligned to the bottom when negative.
int const BORDER_DISTANCE_HORIZ = -20; // Distance to edge of screen (use negative value when width is negative).
int const BORDER_DISTANCE_VERT = 40; // Distance to edge of screen (use negative value when height is negative).
int const AREA_HEIGHT = -40; // Height of the area used. Aligned to the bottom when negative.
int const BORDER_DISTANCE_HORIZ = -10; // Distance to edge of screen (use negative value when width is negative).
int const BORDER_DISTANCE_VERT = -15; // Distance to edge of screen (use negative value when height is negative).
int SPACING_VERT_BARS = 2; // Vertical distance between input and output bar
int SPACING_RIGHT_CAPTION_IN_OUT = 4; // IN/OUT <--> |######## : |
int SPACING_LEFT_CAPTION_UNIT = 4; // |######## : | <--> UNIT
int PADDING_HORIZ_VALUE = 2; // |<-->X.XX<:-># |
unsigned const COLOR_TEXT = 0xe0e0e0e0; // ^ ^ ^ ^ ^ ^
unsigned const COLOR_TEXT = 0xedededff; // ^ ^ ^ ^ ^ ^
unsigned const COLOR_FRAME = 0xe0e0e0b0; // | | |
unsigned const COLOR_INDICATOR = 0xc0c0c0b0; // |
char const* CAPTION_IN = "IN";
char const* CAPTION_OUT = "OUT";
char const* CAPTION_IN = "In";
char const* CAPTION_OUT = "Out";
char const* CAPTION_UNIT = "Mbps";
double const UNIT_SCALE = 8000.0 / (1024.0 * 1024.0); // Bytes/ms -> Mbps
@ -38,13 +38,13 @@ namespace { // .cpp-local
}
BandwidthMeter::ChannelInfo BandwidthMeter::_CHANNELS[] = {
{ "Audio" , "Kbps", 8000.0 / 1024.0, 0x40ff40d0 },
{ "Audio" , "Kbps", 8000.0 / 1024.0, 0x33cc99ff },
{ "Avatars" , "Kbps", 8000.0 / 1024.0, 0xffef40c0 },
{ "Voxels" , "Kbps", 8000.0 / 1024.0, 0xd0d0d0a0 }
};
BandwidthMeter::BandwidthMeter() :
_textRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT),
_textRenderer(INCONSOLATA_FONT_FAMILY, -1, QFont::Bold, false),
_scaleMaxIndex(INITIAL_SCALE_MAXIMUM_INDEX) {
_channels = static_cast<ChannelInfo*>( malloc(sizeof(_CHANNELS)) );

View file

@ -10,6 +10,14 @@
#include "Application.h"
#include "ControllerScriptingInterface.h"
ControllerScriptingInterface::ControllerScriptingInterface() :
_mouseCaptured(false),
_touchCaptured(false),
_wheelCaptured(false)
{
}
const PalmData* ControllerScriptingInterface::getPrimaryPalm() const {
int leftPalmIndex, rightPalmIndex;
@ -179,5 +187,35 @@ glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex
return glm::vec3(0); // bad index
}
bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const {
return isKeyCaptured(KeyEvent(*event));
}
bool ControllerScriptingInterface::isKeyCaptured(const KeyEvent& event) const {
// if we've captured some combination of this key it will be in the map
if (_capturedKeys.contains(event.key, event)) {
return true;
}
return false;
}
void ControllerScriptingInterface::captureKeyEvents(const KeyEvent& event) {
// if it's valid
if (event.isValid) {
// and not already captured
if (!isKeyCaptured(event)) {
// then add this KeyEvent record to the captured combos for this key
_capturedKeys.insert(event.key, event);
}
}
}
void ControllerScriptingInterface::releaseKeyEvents(const KeyEvent& event) {
if (event.isValid) {
// and not already captured
if (isKeyCaptured(event)) {
_capturedKeys.remove(event.key, event);
}
}
}

View file

@ -1,3 +1,4 @@
//
// ControllerScriptingInterface.h
// hifi
@ -18,6 +19,27 @@ class PalmData;
class ControllerScriptingInterface : public AbstractControllerScriptingInterface {
Q_OBJECT
public:
ControllerScriptingInterface();
void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
void emitMouseMoveEvent(QMouseEvent* event) { emit mouseMoveEvent(MouseEvent(*event)); }
void emitMousePressEvent(QMouseEvent* event) { emit mousePressEvent(MouseEvent(*event)); }
void emitMouseReleaseEvent(QMouseEvent* event) { emit mouseReleaseEvent(MouseEvent(*event)); }
void emitTouchBeginEvent(QTouchEvent* event) { emit touchBeginEvent(*event); }
void emitTouchEndEvent(QTouchEvent* event) { emit touchEndEvent(*event); }
void emitTouchUpdateEvent(QTouchEvent* event) { emit touchUpdateEvent(*event); }
void emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); }
bool isKeyCaptured(QKeyEvent* event) const;
bool isKeyCaptured(const KeyEvent& event) const;
bool isMouseCaptured() const { return _mouseCaptured; }
bool isTouchCaptured() const { return _touchCaptured; }
bool isWheelCaptured() const { return _wheelCaptured; }
public slots:
virtual bool isPrimaryButtonPressed() const;
virtual glm::vec2 getPrimaryJoystickPosition() const;
@ -36,11 +58,28 @@ public slots:
virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const;
virtual glm::vec3 getSpatialControlNormal(int controlIndex) const;
virtual void captureKeyEvents(const KeyEvent& event);
virtual void releaseKeyEvents(const KeyEvent& event);
virtual void captureMouseEvents() { _mouseCaptured = true; }
virtual void releaseMouseEvents() { _mouseCaptured = false; }
virtual void captureTouchEvents() { _touchCaptured = true; }
virtual void releaseTouchEvents() { _touchCaptured = false; }
virtual void captureWheelEvents() { _wheelCaptured = true; }
virtual void releaseWheelEvents() { _wheelCaptured = false; }
private:
const PalmData* getPrimaryPalm() const;
const PalmData* getPalm(int palmIndex) const;
int getNumberOfActivePalms() const;
const PalmData* getActivePalm(int palmIndex) const;
bool _mouseCaptured;
bool _touchCaptured;
bool _wheelCaptured;
QMultiMap<int,KeyEvent> _capturedKeys;
};
const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip

View file

@ -26,52 +26,53 @@ void DatagramProcessor::processDatagrams() {
"DatagramProcessor::processDatagrams()");
HifiSockAddr senderSockAddr;
ssize_t bytesReceived;
static unsigned char incomingPacket[MAX_PACKET_SIZE];
static QByteArray incomingPacket;
Application* application = Application::getInstance();
NodeList* nodeList = NodeList::getInstance();
while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams() &&
(bytesReceived = NodeList::getInstance()->getNodeSocket().readDatagram((char*) incomingPacket,
MAX_PACKET_SIZE,
senderSockAddr.getAddressPointer(),
senderSockAddr.getPortPointer()))) {
while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams()) {
incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
nodeList->getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size(),
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
_packetCount++;
_byteCount += bytesReceived;
_byteCount += incomingPacket.size();
if (packetVersionMatch(incomingPacket, senderSockAddr)) {
if (packetVersionMatch(incomingPacket)) {
// only process this packet if we have a match on the packet version
switch (incomingPacket[0]) {
case PACKET_TYPE_TRANSMITTER_DATA_V2:
switch (packetTypeForPacket(incomingPacket)) {
case PacketTypeTransmitterData:
// V2 = IOS transmitter app
application->_myTransmitter.processIncomingData(incomingPacket, bytesReceived);
application->_myTransmitter.processIncomingData(reinterpret_cast<unsigned char*>(incomingPacket.data()),
incomingPacket.size());
break;
case PACKET_TYPE_MIXED_AUDIO:
case PacketTypeMixedAudio:
QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection,
Q_ARG(QByteArray, QByteArray((char*) incomingPacket, bytesReceived)));
Q_ARG(QByteArray, incomingPacket));
break;
case PACKET_TYPE_PARTICLE_ADD_RESPONSE:
case PacketTypeParticleAddResponse:
// this will keep creatorTokenIDs to IDs mapped correctly
Particle::handleAddParticleResponse(incomingPacket, bytesReceived);
Particle::handleAddParticleResponse(incomingPacket);
application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket);
break;
case PACKET_TYPE_PARTICLE_DATA:
case PACKET_TYPE_PARTICLE_ERASE:
case PACKET_TYPE_VOXEL_DATA:
case PACKET_TYPE_VOXEL_ERASE:
case PACKET_TYPE_OCTREE_STATS:
case PACKET_TYPE_ENVIRONMENT_DATA: {
case PacketTypeParticleData:
case PacketTypeParticleErase:
case PacketTypeVoxelData:
case PacketTypeVoxelErase:
case PacketTypeOctreeStats:
case PacketTypeEnvironmentData: {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::networkReceive()... _voxelProcessor.queueReceivedPacket()");
bool wantExtraDebugging = application->getLogger()->extraDebugging();
if (wantExtraDebugging && incomingPacket[0] == PACKET_TYPE_VOXEL_DATA) {
if (wantExtraDebugging && packetTypeForPacket(incomingPacket) == PacketTypeVoxelData) {
int numBytesPacketHeader = numBytesForPacketHeader(incomingPacket);
unsigned char* dataAt = incomingPacket + numBytesPacketHeader;
unsigned char* dataAt = reinterpret_cast<unsigned char*>(incomingPacket.data()) + numBytesPacketHeader;
dataAt += sizeof(VOXEL_PACKET_FLAGS);
VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(VOXEL_PACKET_SEQUENCE);
@ -80,52 +81,49 @@ void DatagramProcessor::processDatagrams() {
VOXEL_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
int flightTime = arrivedAt - sentAt;
printf("got PACKET_TYPE_VOXEL_DATA, sequence:%d flightTime:%d\n", sequence, flightTime);
printf("got PacketType_VOXEL_DATA, sequence:%d flightTime:%d\n", sequence, flightTime);
}
// add this packet to our list of voxel packets and process them on the voxel processing
application->_voxelProcessor.queueReceivedPacket(senderSockAddr, incomingPacket, bytesReceived);
application->_voxelProcessor.queueReceivedPacket(senderSockAddr, incomingPacket);
break;
}
case PACKET_TYPE_METAVOXEL_DATA:
application->_metavoxels.processData(QByteArray((const char*) incomingPacket, bytesReceived),
senderSockAddr);
case PacketTypeMetavoxelData:
application->_metavoxels.processData(incomingPacket, senderSockAddr);
break;
case PACKET_TYPE_BULK_AVATAR_DATA:
case PACKET_TYPE_KILL_AVATAR: {
case PacketTypeBulkAvatarData:
case PacketTypeKillAvatar: {
// update having heard from the avatar-mixer and record the bytes received
SharedNodePointer avatarMixer = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
if (avatarMixer) {
avatarMixer->setLastHeardMicrostamp(usecTimestampNow());
avatarMixer->recordBytesReceived(bytesReceived);
avatarMixer->recordBytesReceived(incomingPacket.size());
QByteArray datagram(reinterpret_cast<char*>(incomingPacket), bytesReceived);
if (incomingPacket[0] == PACKET_TYPE_BULK_AVATAR_DATA) {
if (packetTypeForPacket(incomingPacket) == PacketTypeBulkAvatarData) {
QMetaObject::invokeMethod(&application->getAvatarManager(), "processAvatarMixerDatagram",
Q_ARG(const QByteArray&, datagram),
Q_ARG(const QByteArray&, incomingPacket),
Q_ARG(const QWeakPointer<Node>&, avatarMixer));
} else {
// this is an avatar kill, pass it to the application AvatarManager
QMetaObject::invokeMethod(&application->getAvatarManager(), "processKillAvatar",
Q_ARG(const QByteArray&, datagram));
Q_ARG(const QByteArray&, incomingPacket));
}
}
application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived);
application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size());
break;
}
case PACKET_TYPE_DATA_SERVER_GET:
case PACKET_TYPE_DATA_SERVER_PUT:
case PACKET_TYPE_DATA_SERVER_SEND:
case PACKET_TYPE_DATA_SERVER_CONFIRM:
DataServerClient::processMessageFromDataServer(incomingPacket, bytesReceived);
}
case PacketTypeDataServerGet:
case PacketTypeDataServerPut:
case PacketTypeDataServerSend:
case PacketTypeDataServerConfirm:
DataServerClient::processMessageFromDataServer(incomingPacket);
break;
default:
NodeList::getInstance()->processNodeData(senderSockAddr, incomingPacket, bytesReceived);
NodeList::getInstance()->processNodeData(senderSockAddr, incomingPacket);
break;
}
}
}
}
}

View file

@ -148,32 +148,28 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3
return found;
}
int Environment::parseData(const HifiSockAddr& senderAddress, unsigned char* sourceBuffer, int numBytes) {
int Environment::parseData(const HifiSockAddr& senderAddress, const QByteArray& packet) {
// push past the packet header
unsigned char* start = sourceBuffer;
int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer);
sourceBuffer += numBytesPacketHeader;
numBytes -= numBytesPacketHeader;
int bytesRead = numBytesForPacketHeader(packet);
// get the lock for the duration of the call
QMutexLocker locker(&_mutex);
EnvironmentData newData;
while (numBytes > 0) {
int dataLength = newData.parseData(sourceBuffer, numBytes);
while (bytesRead < packet.size()) {
int dataLength = newData.parseData(reinterpret_cast<const unsigned char*>(packet.data()) + bytesRead,
packet.size() - bytesRead);
// update the mapping by address/ID
_data[senderAddress][newData.getID()] = newData;
sourceBuffer += dataLength;
numBytes -= dataLength;
bytesRead += dataLength;
}
// remove the default mapping, if any
_data.remove(HifiSockAddr());
return sourceBuffer - start;
return bytesRead;
}
ProgramObject* Environment::createSkyProgram(const char* from, int* locations) {

View file

@ -34,7 +34,7 @@ public:
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration);
int parseData(const HifiSockAddr& senderSockAddr, unsigned char* sourceBuffer, int numBytes);
int parseData(const HifiSockAddr& senderSockAddr, const QByteArray& packet);
private:

View file

@ -14,17 +14,20 @@
#include <QDir>
#include <QDesktopServices>
FileLogger::FileLogger() : _logData(NULL) {
setExtraDebugging(false);
_fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
QDir logDir(_fileName);
if (!logDir.exists(_fileName)) {
logDir.mkdir(_fileName);
}
const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt";
const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss";
const QString LOGS_DIRECTORY = "Logs";
FileLogger::FileLogger(QObject* parent) :
AbstractLoggerInterface(parent),
_logData(NULL)
{
setExtraDebugging(false);
_fileName = FileUtils::standardPath(LOGS_DIRECTORY);
QHostAddress clientAddress = QHostAddress(getHostOrderLocalAddress());
QDateTime now = QDateTime::currentDateTime();
_fileName.append(QString("/hifi-log_%1_%2.txt").arg(clientAddress.toString(), now.toString("yyyy-MM-dd_hh.mm.ss")));
_fileName.append(QString(FILENAME_FORMAT).arg(clientAddress.toString(), now.toString(DATETIME_FORMAT)));
}
void FileLogger::addMessage(QString message) {
@ -40,5 +43,5 @@ void FileLogger::addMessage(QString message) {
}
void FileLogger::locateLog() {
FileUtils::LocateFile(_fileName);
FileUtils::locateFile(_fileName);
}

View file

@ -16,7 +16,7 @@ class FileLogger : public AbstractLoggerInterface {
Q_OBJECT
public:
FileLogger();
FileLogger(QObject* parent = NULL);
virtual void addMessage(QString);
virtual QStringList getLogData() { return _logData; };

View file

@ -6,212 +6,148 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include "ImportDialog.h"
#include "Application.h"
#include <QStandardPaths>
#include <QGridLayout>
#include <QMouseEvent>
#include <QSplitter>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
const QString WINDOW_NAME = QObject::tr("Import Voxels");
const QString IMPORT_BUTTON_NAME = QObject::tr("Import");
const QString IMPORT_TO_CLIPBOARD_CHECKBOX_STRING = QObject::tr("Import into clipboard");
const QString PREVIEW_CHECKBOX_STRING = QObject::tr("Load preview");
const QString IMPORT_FILE_TYPES = QObject::tr("Sparse Voxel Octree Files, "
"Square PNG, "
"Schematic Files "
"(*.svo *.png *.schematic)");
const QString WINDOW_NAME = QObject::tr("Import Voxels");
const QString IMPORT_BUTTON_NAME = QObject::tr("Import");
const QString IMPORT_INFO = QObject::tr("<b>Import</b> %1 as voxels");
const QString CANCEL_BUTTON_NAME = QObject::tr("Cancel");
const QString INFO_LABEL_TEXT = QObject::tr("<div style='line-height:20px;'>"
"This will load the selected file into Hifi and allow you<br/>"
"to place it with %1-V; you must be in select or<br/>"
"add mode (S or V keys will toggle mode) to place.</div>");
const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
const int SHORT_FILE_EXTENSION = 4;
const int SECOND_INDEX_LETTER = 1;
QIcon HiFiIconProvider::icon(QFileIconProvider::IconType type) const {
const glm::vec3 UP_VECT = glm::vec3(0, 1, 0);
const float ANGULAR_RATE = 0.02f;
const float VERTICAL_ANGLE = (float)M_PI_4 / 2.0f;
const float RETURN_RATE = 0.02f;
const float NEAR_CLIP = 0.5f;
const float FAR_CLIP = 10.0f;
const float FIELD_OF_VIEW = 60.0f;
switchToResourcesParentIfRequired();
class GLWidget : public QGLWidget {
public:
GLWidget(QWidget* parent = NULL);
void setDraw(bool draw) {_draw = draw;}
void setTargetCenter(glm::vec3 targetCenter) { _targetCenter = targetCenter; }
// types
// Computer, Desktop, Trashcan, Network, Drive, Folder, File
QString typeString;
protected:
virtual void initializeGL();
virtual void resizeGL(int width, int height);
virtual void paintGL();
switch (type) {
case QFileIconProvider::Computer:
typeString = "computer";
break;
void mousePressEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
case QFileIconProvider::Desktop:
typeString = "desktop";
break;
private:
VoxelSystem* _voxelSystem;
case QFileIconProvider::Trashcan:
case QFileIconProvider::Network:
case QFileIconProvider::Drive:
case QFileIconProvider::Folder:
typeString = "folder";
break;
bool _draw;
double _a; // horizontal angle of the camera to the center of the object
double _h; // vertical angle of the camera to the center of the object
glm::vec3 _targetCenter;
bool _pressed;
int _mouseX;
int _mouseY;
};
GLWidget::GLWidget(QWidget *parent)
: QGLWidget(parent, Application::getInstance()->getGLWidget()),
_draw(false),
_a(0.0f),
_h(VERTICAL_ANGLE),
_targetCenter(0.5f, 0.5f, 0.5f),
_pressed(false),
_mouseX(0),
_mouseY(0) {
_voxelSystem = Application::getInstance()->getSharedVoxelSystem();
}
void GLWidget::initializeGL() {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glShadeModel (GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
}
void GLWidget::resizeGL(int width, int height) {
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(FIELD_OF_VIEW,
(float) width / height,
NEAR_CLIP,
FAR_CLIP);
}
void GLWidget::paintGL() {
glEnable(GL_LINE_SMOOTH);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (!_pressed) {
_a += ANGULAR_RATE;
_h = (1.0f - RETURN_RATE) * _h + RETURN_RATE * VERTICAL_ANGLE;
default:
typeString = "file";
break;
}
gluLookAt(_targetCenter.x + (glm::length(_targetCenter) + NEAR_CLIP) * cos(_a),
_targetCenter.y + (glm::length(_targetCenter) + NEAR_CLIP) * sin(_h),
_targetCenter.z + (glm::length(_targetCenter) + NEAR_CLIP) * sin(_a),
_targetCenter.x, _targetCenter.y, _targetCenter.z,
UP_VECT.x, UP_VECT.y, UP_VECT.z);
return QIcon("resources/icons/" + typeString + ".svg");
}
QIcon HiFiIconProvider::icon(const QFileInfo &info) const {
switchToResourcesParentIfRequired();
const QString ext = info.suffix().toLower();
if (_draw) {
glBegin(GL_LINES);
glColor3d(1, 1 ,1);
glVertex3d(0, 0, 0);
glVertex3d(1, 0, 0);
glVertex3d(0, 0, 0);
glVertex3d(0, 1, 0);
glVertex3d(0, 0, 0);
glVertex3d(0, 0, 1);
glColor3d(0.4f, 0.4f ,0.4f);
glVertex3d(2 * _targetCenter.x, 2 * _targetCenter.y, 2 * _targetCenter.z);
glVertex3d(0 , 2 * _targetCenter.y, 2 * _targetCenter.z);
glVertex3d(2 * _targetCenter.x, 2 * _targetCenter.y, 2 * _targetCenter.z);
glVertex3d(2 * _targetCenter.x, 0 , 2 * _targetCenter.z);
glVertex3d(2 * _targetCenter.x, 2 * _targetCenter.y, 2 * _targetCenter.z);
glVertex3d(2 * _targetCenter.x, 2 * _targetCenter.y, 0 );
glEnd();
glScalef(1.0f / TREE_SCALE, 1.0f / TREE_SCALE, 1.0f / TREE_SCALE);
_voxelSystem->render(false);
if (info.isDir()) {
if (info.absoluteFilePath() == QDir::homePath()) {
return QIcon("resources/icons/home.svg");
} else if (info.absoluteFilePath() == DESKTOP_LOCATION) {
return QIcon("resources/icons/desktop.svg");
} else if (info.absoluteFilePath() == QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)) {
return QIcon("resources/icons/documents.svg");
}
return QIcon("resources/icons/folder.svg");
}
QFileInfo iconFile("resources/icons/" + iconsMap[ext]);
if (iconFile.exists() && iconFile.isFile()) {
return QIcon(iconFile.filePath());
}
return QIcon("resources/icons/file.svg");
}
void GLWidget::mousePressEvent(QMouseEvent* event) {
_pressed = true;
_mouseX = event->globalX();
_mouseY = event->globalY();
QString HiFiIconProvider::type(const QFileInfo &info) const {
if (info.isFile()) {
if (info.suffix().size() > SHORT_FILE_EXTENSION) {
// Capitalize extension
return info.suffix().left(SECOND_INDEX_LETTER).toUpper() + info.suffix().mid(SECOND_INDEX_LETTER);
}
return info.suffix().toUpper();
}
return QFileIconProvider::type(info);
}
void GLWidget::mouseMoveEvent(QMouseEvent* event) {
_a += (M_PI * (event->globalX() - _mouseX)) / height();
_h += (M_PI * (event->globalY() - _mouseY)) / height();
_h = glm::clamp(_h, -M_PI_4, M_PI_4);
ImportDialog::ImportDialog(QWidget* parent) :
QFileDialog(parent, WINDOW_NAME, DESKTOP_LOCATION, NULL),
_importButton(IMPORT_BUTTON_NAME, this),
_cancelButton(CANCEL_BUTTON_NAME, this),
fileAccepted(false) {
_mouseX = event->globalX();
_mouseY = event->globalY();
}
void GLWidget::mouseReleaseEvent(QMouseEvent* event) {
_pressed = false;
}
ImportDialog::ImportDialog(QWidget *parent)
: QFileDialog(parent, WINDOW_NAME, DESKTOP_LOCATION, IMPORT_FILE_TYPES),
_importButton (IMPORT_BUTTON_NAME, this),
_clipboardImportBox(IMPORT_TO_CLIPBOARD_CHECKBOX_STRING, this),
_previewBox (PREVIEW_CHECKBOX_STRING, this),
_previewBar (this),
_glPreview (new GLWidget(this)) {
setOption(QFileDialog::DontUseNativeDialog, true);
setFileMode(QFileDialog::ExistingFile);
setViewMode(QFileDialog::Detail);
QGridLayout* gridLayout = (QGridLayout*) layout();
gridLayout->addWidget(&_importButton , 2, 2);
gridLayout->addWidget(&_clipboardImportBox, 2, 3);
gridLayout->addWidget(&_previewBox , 3, 3);
gridLayout->addWidget(&_previewBar , 0, 3);
gridLayout->addWidget(_glPreview , 1, 3);
gridLayout->setColumnStretch(3, 1);
#ifdef Q_OS_MAC
QString cmdString = ("Command");
#else
QString cmdString = ("Control");
#endif
QLabel* infoLabel = new QLabel(QString(INFO_LABEL_TEXT).arg(cmdString));
infoLabel->setObjectName("infoLabel");
_previewBar.setVisible(false);
_previewBar.setRange(0, 100);
_previewBar.setValue(0);
QGridLayout* gridLayout = (QGridLayout*) layout();
gridLayout->addWidget(infoLabel, 2, 0, 2, 1);
gridLayout->addWidget(&_cancelButton, 2, 1, 2, 1);
gridLayout->addWidget(&_importButton, 2, 2, 2, 1);
setImportTypes();
setLayout();
connect(&_importButton, SIGNAL(pressed()), SLOT(import()));
connect(&_previewBox, SIGNAL(toggled(bool)), SIGNAL(previewToggled(bool)));
connect(&_previewBox, SIGNAL(toggled(bool)), SLOT(preview(bool)));
connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString)));
connect(&_glTimer, SIGNAL(timeout()), SLOT(timer()));
connect(&_cancelButton, SIGNAL(pressed()), SLOT(close()));
connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString)));
}
ImportDialog::~ImportDialog() {
delete _glPreview;
}
void ImportDialog::init() {
VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
connect(voxelSystem, SIGNAL(importSize(float,float,float)), SLOT(setGLCamera(float,float,float)));
connect(voxelSystem, SIGNAL(importProgress(int)), &_previewBar, SLOT(setValue(int)));
deleteLater();
}
void ImportDialog::import() {
_importButton.setDisabled(true);
_clipboardImportBox.setDisabled(true);
_previewBox.setDisabled(true);
_previewBar.setValue(0);
_previewBar.setVisible(true);
fileAccepted = true;
emit accepted();
}
void ImportDialog::accept() {
QFileDialog::accept();
// do nothing if import is not enable
if (!_importButton.isEnabled()) {
return;
}
if (!fileAccepted) {
fileAccepted = true;
emit accepted();
} else {
QFileDialog::accept();
}
}
void ImportDialog::reject() {
@ -219,44 +155,137 @@ void ImportDialog::reject() {
}
int ImportDialog::exec() {
// deselect selected file
selectFile(" ");
return QFileDialog::exec();
}
void ImportDialog::setGLCamera(float x, float y, float z) {
_glPreview->setTargetCenter(glm::vec3(x, y, z) / 2.0f);
}
void ImportDialog::reset() {
_previewBox.setChecked(false);
_previewBar.setVisible(false);
_previewBar.setValue(0);
_importButton.setEnabled(true);
_clipboardImportBox.setEnabled(true);
_previewBox.setEnabled(true);
_glTimer.stop();
_glPreview->setDraw(false);
_glPreview->updateGL();
}
void ImportDialog::preview(bool wantPreview) {
_previewBar.setValue(0);
_previewBar.setVisible(wantPreview);
_glPreview->setDraw(wantPreview);
if (wantPreview) {
_glTimer.start();
} else {
_glTimer.stop();
_glPreview->updateGL();
}
_importButton.setEnabled(false);
}
void ImportDialog::saveCurrentFile(QString filename) {
_currentFile = filename;
if (!filename.isEmpty() && QFileInfo(filename).isFile()) {
_currentFile = filename;
_importButton.setEnabled(true);
} else {
_currentFile.clear();
_importButton.setEnabled(false);
}
}
void ImportDialog::timer() {
_glPreview->updateGL();
_glTimer.start(16);
void ImportDialog::setLayout() {
// set ObjectName used in qss for styling
_importButton.setObjectName("importButton");
_cancelButton.setObjectName("cancelButton");
// set fixed size
_importButton.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
_cancelButton.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
// hide unused embedded widgets in QFileDialog
QWidget* widget = findChild<QWidget*>("lookInCombo");
widget->hide();
widget = findChild<QWidget*>("backButton");
widget->hide();
widget = findChild<QWidget*>("forwardButton");
widget->hide();
widget = findChild<QWidget*>("toParentButton");
widget->hide();
widget = findChild<QWidget*>("newFolderButton");
widget->hide();
widget = findChild<QWidget*>("listModeButton");
widget->hide();
widget = findChild<QWidget*>("detailModeButton");
widget->hide();
widget = findChild<QWidget*>("fileNameEdit");
widget->hide();
widget = findChild<QWidget*>("fileTypeCombo");
widget->hide();
widget = findChild<QWidget*>("fileTypeLabel");
widget->hide();
widget = findChild<QWidget*>("fileNameLabel");
widget->hide();
widget = findChild<QWidget*>("buttonBox");
widget->hide();
QSplitter* splitter = findChild<QSplitter*>("splitter");
splitter->setHandleWidth(0);
// remove blue outline on Mac
widget = findChild<QWidget*>("sidebar");
widget->setAttribute(Qt::WA_MacShowFocusRect, false);
widget = findChild<QWidget*>("treeView");
widget->setAttribute(Qt::WA_MacShowFocusRect, false);
switchToResourcesParentIfRequired();
QFile styleSheet("resources/styles/import_dialog.qss");
if (styleSheet.open(QIODevice::ReadOnly)) {
setStyleSheet(styleSheet.readAll());
}
}
void ImportDialog::setImportTypes() {
switchToResourcesParentIfRequired();
QFile config("resources/config/config.json");
config.open(QFile::ReadOnly | QFile::Text);
QJsonDocument document = QJsonDocument::fromJson(config.readAll());
if (!document.isNull() && !document.isEmpty()) {
QString importFormatsInfo;
QString importFormatsFilterList;
QHash<QString, QString> iconsMap;
QJsonObject configObject = document.object();
if (!configObject.isEmpty()) {
QJsonArray fileFormats = configObject["importFormats"].toArray();
int formatsCounter = 0;
foreach (const QJsonValue& fileFormat, fileFormats) {
QJsonObject fileFormatObject = fileFormat.toObject();
QString ext(fileFormatObject["extension"].toString());
QString icon(fileFormatObject.value("icon").toString());
if (formatsCounter > 0) {
importFormatsInfo.append(",");
}
// set ' or' on last import type text
if (formatsCounter == fileFormats.count() - 1) {
importFormatsInfo.append(" or");
}
importFormatsFilterList.append(QString("*.%1 ").arg(ext));
importFormatsInfo.append(" .").append(ext);
iconsMap[ext] = icon;
formatsCounter++;
}
}
// set custom file icons
setIconProvider(new HiFiIconProvider(iconsMap));
setNameFilter(importFormatsFilterList);
#ifdef Q_OS_MAC
QString cmdString = ("Command");
#else
QString cmdString = ("Control");
#endif
setLabelText(QFileDialog::LookIn, QString(IMPORT_INFO).arg(importFormatsInfo));
}
}

View file

@ -9,54 +9,54 @@
#ifndef __hifi__ImportDialog__
#define __hifi__ImportDialog__
#include <VoxelSystem.h>
#include <QFileDialog>
#include <QPushButton>
#include <QCheckBox>
#include <QProgressBar>
#include <QGLWidget>
#include <QTimer>
#include <QLabel>
#include <QFileIconProvider>
#include <QHash>
class GLWidget;
#include <SharedUtil.h>
class HiFiIconProvider : public QFileIconProvider {
public:
HiFiIconProvider(const QHash<QString, QString> map) { iconsMap = map; };
virtual QIcon icon(IconType type) const;
virtual QIcon icon(const QFileInfo &info) const;
virtual QString type(const QFileInfo &info) const;
QHash<QString, QString> iconsMap;
};
class ImportDialog : public QFileDialog {
Q_OBJECT
public:
ImportDialog(QWidget* parent = NULL);
~ImportDialog();
void init();
void reset();
bool getWantPreview() const { return _previewBox.isChecked(); }
QString getCurrentFile() const { return _currentFile; }
bool getImportIntoClipboard() const { return _clipboardImportBox.isChecked(); }
signals:
void previewToggled(bool);
void accepted();
public slots:
int exec();
void setGLCamera(float x, float y, float z);
void import();
void accept();
void reject();
private slots:
void preview(bool preview);
void saveCurrentFile(QString);
void timer();
private:
QString _currentFile;
QPushButton _importButton;
QCheckBox _clipboardImportBox;
QCheckBox _previewBox;
QProgressBar _previewBar;
GLWidget* _glPreview;
QTimer _glTimer;
QString _currentFile;
QPushButton _importButton;
QPushButton _cancelButton;
void setLayout();
void setImportTypes();
bool fileAccepted;
};
#endif /* defined(__hifi__ImportDialog__) */

View file

@ -116,10 +116,10 @@ Menu::Menu() :
this,
SLOT(goToLocation()));
addActionToQMenuAndActionHash(fileMenu,
MenuOption::GoToUser,
MenuOption::GoTo,
Qt::Key_At,
this,
SLOT(goToUser()));
SLOT(goTo()));
addDisabledActionAndSeparator(fileMenu, "Settings");
@ -917,6 +917,60 @@ void Menu::goToDomain() {
sendFakeEnterEvent();
}
void Menu::goTo() {
QInputDialog gotoDialog(Application::getInstance()->getWindow());
gotoDialog.setWindowTitle("Go to");
gotoDialog.setLabelText("Destination:");
QString destination = Application::getInstance()->getProfile()->getUsername();
gotoDialog.setTextValue(destination);
gotoDialog.setWindowFlags(Qt::Sheet);
gotoDialog.resize(gotoDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, gotoDialog.size().height());
int dialogReturn = gotoDialog.exec();
if (dialogReturn == QDialog::Accepted && !gotoDialog.textValue().isEmpty()) {
destination = gotoDialog.textValue();
QStringList coordinateItems = destination.split(QRegExp("_|,"), QString::SkipEmptyParts);
const int NUMBER_OF_COORDINATE_ITEMS = 3;
const int X_ITEM = 0;
const int Y_ITEM = 1;
const int Z_ITEM = 2;
if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) {
double x = replaceLastOccurrence('-', '.', coordinateItems[X_ITEM].trimmed()).toDouble();
double y = replaceLastOccurrence('-', '.', coordinateItems[Y_ITEM].trimmed()).toDouble();
double z = replaceLastOccurrence('-', '.', coordinateItems[Z_ITEM].trimmed()).toDouble();
glm::vec3 newAvatarPos(x, y, z);
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
glm::vec3 avatarPos = myAvatar->getPosition();
if (newAvatarPos != avatarPos) {
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
MyAvatar::sendKillAvatar();
qDebug("Going To Location: %f, %f, %f...", x, y, z);
myAvatar->setPosition(newAvatarPos);
}
} else {
// there's a username entered by the user, make a request to the data-server
DataServerClient::getValuesForKeysAndUserString(
QStringList()
<< DataServerKey::Domain
<< DataServerKey::Position
<< DataServerKey::Orientation,
destination, Application::getInstance()->getProfile());
}
}
sendFakeEnterEvent();
}
void Menu::goToLocation() {
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
glm::vec3 avatarPos = myAvatar->getPosition();
@ -961,26 +1015,6 @@ void Menu::goToLocation() {
sendFakeEnterEvent();
}
void Menu::goToUser() {
QInputDialog userDialog(Application::getInstance()->getWindow());
userDialog.setWindowTitle("Go to User");
userDialog.setLabelText("Destination user:");
QString username = Application::getInstance()->getProfile()->getUsername();
userDialog.setTextValue(username);
userDialog.setWindowFlags(Qt::Sheet);
userDialog.resize(userDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, userDialog.size().height());
int dialogReturn = userDialog.exec();
if (dialogReturn == QDialog::Accepted && !userDialog.textValue().isEmpty()) {
// there's a username entered by the user, make a request to the data-server
DataServerClient::getValuesForKeysAndUserString(
QStringList() << DataServerKey::Domain << DataServerKey::Position << DataServerKey::Orientation,
userDialog.textValue(), Application::getInstance()->getProfile());
}
sendFakeEnterEvent();
}
void Menu::pasteToVoxel() {
QInputDialog pasteToOctalCodeDialog(Application::getInstance()->getWindow());
pasteToOctalCodeDialog.setWindowTitle("Paste to Voxel");
@ -1144,3 +1178,14 @@ void Menu::updateFrustumRenderModeAction() {
}
}
QString Menu::replaceLastOccurrence(QChar search, QChar replace, QString string) {
int lastIndex;
lastIndex = string.lastIndexOf(search);
if (lastIndex > 0) {
lastIndex = string.lastIndexOf(search);
string.replace(lastIndex, 1, replace);
}
return string;
}

View file

@ -92,7 +92,7 @@ public slots:
void saveSettings(QSettings* settings = NULL);
void importSettings();
void exportSettings();
void goToUser();
void goTo();
void pasteToVoxel();
private slots:
@ -152,6 +152,7 @@ private:
QAction* _useVoxelShader;
int _maxVoxelPacketsPerSecond;
QMenu* _activeScriptsMenu;
QString replaceLastOccurrence(QChar search, QChar replace, QString string);
};
namespace MenuOption {
@ -210,7 +211,7 @@ namespace MenuOption {
const QString GlowMode = "Cycle Glow Mode";
const QString GoToDomain = "Go To Domain...";
const QString GoToLocation = "Go To Location...";
const QString GoToUser = "Go To User...";
const QString GoTo = "Go To...";
const QString ImportVoxels = "Import Voxels";
const QString ImportVoxelsClipboard = "Import Voxels to Clipboard";
const QString IncreaseAvatarSize = "Increase Avatar Size";

View file

@ -6,11 +6,11 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <QMutexLocker>
#include <QtDebug>
#include <SharedUtil.h>
#include <MetavoxelMessages.h>
#include <MetavoxelUtil.h>
#include "Application.h"
@ -24,6 +24,12 @@ MetavoxelSystem::MetavoxelSystem() :
_buffer(QOpenGLBuffer::VertexBuffer) {
}
MetavoxelSystem::~MetavoxelSystem() {
for (QHash<QUuid, MetavoxelClient*>::const_iterator it = _clients.begin(); it != _clients.end(); it++) {
delete it.value();
}
}
void MetavoxelSystem::init() {
if (!_program.isLinked()) {
switchToResourcesParentIfRequired();
@ -42,19 +48,23 @@ void MetavoxelSystem::init() {
_buffer.create();
}
void MetavoxelSystem::applyEdit(const MetavoxelEditMessage& edit) {
foreach (MetavoxelClient* client, _clients) {
client->applyEdit(edit);
}
}
void MetavoxelSystem::processData(const QByteArray& data, const HifiSockAddr& sender) {
QMetaObject::invokeMethod(this, "receivedData", Q_ARG(const QByteArray&, data), Q_ARG(const HifiSockAddr&, sender));
}
void MetavoxelSystem::simulate(float deltaTime) {
// simulate the clients
_points.clear();
foreach (MetavoxelClient* client, _clients) {
client->simulate(deltaTime);
client->simulate(deltaTime, _pointVisitor);
}
_points.clear();
_data.guide(_pointVisitor);
_buffer.bind();
int bytes = _points.size() * sizeof(Point);
if (_buffer.size() < bytes) {
@ -107,21 +117,20 @@ void MetavoxelSystem::render() {
}
void MetavoxelSystem::nodeAdded(SharedNodePointer node) {
if (node->getType() == NODE_TYPE_METAVOXEL_SERVER) {
QMetaObject::invokeMethod(this, "addClient", Q_ARG(const QUuid&, node->getUUID()),
Q_ARG(const HifiSockAddr&, node->getLocalSocket()));
if (node->getType() == NodeType::MetavoxelServer) {
QMetaObject::invokeMethod(this, "addClient", Q_ARG(const SharedNodePointer&, node));
}
}
void MetavoxelSystem::nodeKilled(SharedNodePointer node) {
if (node->getType() == NODE_TYPE_METAVOXEL_SERVER) {
if (node->getType() == NodeType::MetavoxelServer) {
QMetaObject::invokeMethod(this, "removeClient", Q_ARG(const QUuid&, node->getUUID()));
}
}
void MetavoxelSystem::addClient(const QUuid& uuid, const HifiSockAddr& address) {
MetavoxelClient* client = new MetavoxelClient(address);
_clients.insert(uuid, client);
void MetavoxelSystem::addClient(const SharedNodePointer& node) {
MetavoxelClient* client = new MetavoxelClient(node);
_clients.insert(node->getUUID(), client);
_clientsBySessionID.insert(client->getSessionID(), client);
}
@ -139,7 +148,7 @@ void MetavoxelSystem::receivedData(const QByteArray& data, const HifiSockAddr& s
}
MetavoxelClient* client = _clientsBySessionID.value(sessionID);
if (client) {
client->receivedData(data, sender);
client->receivedData(data);
}
}
@ -167,17 +176,15 @@ bool MetavoxelSystem::PointVisitor::visit(MetavoxelInfo& info) {
}
static QByteArray createDatagramHeader(const QUuid& sessionID) {
QByteArray header(MAX_PACKET_HEADER_BYTES, 0);
populateTypeAndVersion(reinterpret_cast<unsigned char*>(header.data()), PACKET_TYPE_METAVOXEL_DATA);
QByteArray header = byteArrayWithPopluatedHeader(PacketTypeMetavoxelData);
header += sessionID.toRfc4122();
return header;
}
MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) :
_address(address),
MetavoxelClient::MetavoxelClient(const SharedNodePointer& node) :
_node(node),
_sessionID(QUuid::createUuid()),
_sequencer(createDatagramHeader(_sessionID)),
_data(new MetavoxelData()) {
_sequencer(createDatagramHeader(_sessionID)) {
connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&)));
connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&)));
@ -188,23 +195,41 @@ MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) :
_receiveRecords.append(record);
}
void MetavoxelClient::simulate(float deltaTime) {
MetavoxelClient::~MetavoxelClient() {
// close the session
Bitstream& out = _sequencer.startPacket();
out << QVariant::fromValue(CloseSessionMessage());
_sequencer.endPacket();
}
void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit) {
// apply immediately to local tree
edit.apply(_data);
// start sending it out
_sequencer.sendHighPriorityMessage(QVariant::fromValue(edit));
}
void MetavoxelClient::simulate(float deltaTime, MetavoxelVisitor& visitor) {
Bitstream& out = _sequencer.startPacket();
ClientStateMessage state = { Application::getInstance()->getCamera()->getPosition() };
out << QVariant::fromValue(state);
_sequencer.endPacket();
_data.guide(visitor);
}
void MetavoxelClient::receivedData(const QByteArray& data, const HifiSockAddr& sender) {
// save the most recent sender
_address = sender;
void MetavoxelClient::receivedData(const QByteArray& data) {
// process through sequencer
_sequencer.receivedDatagram(data);
}
void MetavoxelClient::sendData(const QByteArray& data) {
NodeList::getInstance()->getNodeSocket().writeDatagram(data, _address.getAddress(), _address.getPort());
QMutexLocker locker(&_node->getMutex());
const HifiSockAddr* address = _node->getActiveSocket();
if (address) {
NodeList::getInstance()->getNodeSocket().writeDatagram(data, address->getAddress(), address->getPort());
}
}
void MetavoxelClient::readPacket(Bitstream& in) {
@ -215,6 +240,13 @@ void MetavoxelClient::readPacket(Bitstream& in) {
// record the receipt
ReceiveRecord record = { _sequencer.getIncomingPacketNumber(), _data };
_receiveRecords.append(record);
// reapply local edits
foreach (const DatagramSequencer::HighPriorityMessage& message, _sequencer.getHighPriorityMessages()) {
if (message.data.userType() == MetavoxelEditMessage::Type) {
message.data.value<MetavoxelEditMessage>().apply(_data);
}
}
}
void MetavoxelClient::clearReceiveRecordsBefore(int index) {
@ -224,7 +256,7 @@ void MetavoxelClient::clearReceiveRecordsBefore(int index) {
void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) {
int userType = message.userType();
if (userType == MetavoxelDeltaMessage::Type) {
readDelta(_data, _receiveRecords.first().data, in);
_data.readDelta(_receiveRecords.first().data, in);
} else if (userType == QMetaType::QVariantList) {
foreach (const QVariant& element, message.toList()) {

View file

@ -19,6 +19,7 @@
#include <DatagramSequencer.h>
#include <MetavoxelData.h>
#include <MetavoxelMessages.h>
#include "renderer/ProgramObject.h"
@ -31,10 +32,11 @@ class MetavoxelSystem : public QObject {
public:
MetavoxelSystem();
~MetavoxelSystem();
void init();
MetavoxelData& getData() { return _data; }
void applyEdit(const MetavoxelEditMessage& edit);
void processData(const QByteArray& data, const HifiSockAddr& sender);
@ -48,7 +50,7 @@ public slots:
private:
Q_INVOKABLE void addClient(const QUuid& uuid, const HifiSockAddr& address);
Q_INVOKABLE void addClient(const SharedNodePointer& node);
Q_INVOKABLE void removeClient(const QUuid& uuid);
Q_INVOKABLE void receivedData(const QByteArray& data, const HifiSockAddr& sender);
@ -71,7 +73,6 @@ private:
static ProgramObject _program;
static int _pointScaleLocation;
MetavoxelData _data;
QVector<Point> _points;
PointVisitor _pointVisitor;
QOpenGLBuffer _buffer;
@ -86,13 +87,16 @@ class MetavoxelClient : public QObject {
public:
MetavoxelClient(const HifiSockAddr& address);
MetavoxelClient(const SharedNodePointer& node);
virtual ~MetavoxelClient();
const QUuid& getSessionID() const { return _sessionID; }
void simulate(float deltaTime);
void applyEdit(const MetavoxelEditMessage& edit);
void receivedData(const QByteArray& data, const HifiSockAddr& sender);
void simulate(float deltaTime, MetavoxelVisitor& visitor);
void receivedData(const QByteArray& data);
private slots:
@ -109,15 +113,15 @@ private:
class ReceiveRecord {
public:
int packetNumber;
MetavoxelDataPointer data;
MetavoxelData data;
};
HifiSockAddr _address;
SharedNodePointer _node;
QUuid _sessionID;
DatagramSequencer _sequencer;
MetavoxelDataPointer _data;
MetavoxelData _data;
QList<ReceiveRecord> _receiveRecords;
};

View file

@ -7,6 +7,8 @@
//
//
#include <glm/gtx/quaternion.hpp>
#include "InterfaceConfig.h"
#include "ParticleTreeRenderer.h"
@ -16,17 +18,47 @@ ParticleTreeRenderer::ParticleTreeRenderer() :
}
ParticleTreeRenderer::~ParticleTreeRenderer() {
// delete the models in _particleModels
foreach(Model* model, _particleModels) {
delete model;
}
_particleModels.clear();
}
void ParticleTreeRenderer::init() {
OctreeRenderer::init();
}
void ParticleTreeRenderer::update() {
if (_tree) {
ParticleTree* tree = (ParticleTree*)_tree;
_tree->lockForWrite();
tree->update();
_tree->unlock();
ParticleTree* tree = static_cast<ParticleTree*>(_tree);
if (tree->tryLockForWrite()) {
tree->update();
tree->unlock();
}
}
}
void ParticleTreeRenderer::render() {
OctreeRenderer::render();
}
Model* ParticleTreeRenderer::getModel(const QString& url) {
Model* model = NULL;
// if we don't already have this model then create it and initialize it
if (_particleModels.find(url) == _particleModels.end()) {
model = new Model();
model->init();
model->setURL(QUrl(url));
_particleModels[url] = model;
} else {
model = _particleModels[url];
}
return model;
}
void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) {
// actually render it here...
// we need to iterate the actual particles of the element
@ -36,27 +68,48 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg
uint16_t numberOfParticles = particles.size();
bool drawAsSphere = true;
for (uint16_t i = 0; i < numberOfParticles; i++) {
const Particle& particle = particles[i];
// render particle aspoints
glm::vec3 position = particle.getPosition() * (float)TREE_SCALE;
glColor3ub(particle.getColor()[RED_INDEX],particle.getColor()[GREEN_INDEX],particle.getColor()[BLUE_INDEX]);
float sphereRadius = particle.getRadius() * (float)TREE_SCALE;
float radius = particle.getRadius() * (float)TREE_SCALE;
bool drawAsModel = particle.hasModel();
args->_renderedItems++;
if (drawAsSphere) {
if (drawAsModel) {
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutSolidSphere(sphereRadius, 15, 15);
const float alpha = 1.0f;
Model* model = getModel(particle.getModelURL());
glm::vec3 translationAdjustment = particle.getModelTranslation();
// set the position
glm::vec3 translation(position.x, position.y, position.z);
model->setTranslation(translation + translationAdjustment);
// set the rotation
glm::quat rotation = particle.getModelRotation();
model->setRotation(rotation);
// scale
// TODO: need to figure out correct scale adjust, this was arbitrarily set to make a couple models work
const float MODEL_SCALE = 0.00575f;
glm::vec3 scale(1.0f,1.0f,1.0f);
model->setScale(scale * MODEL_SCALE * radius * particle.getModelScale());
model->simulate(0.0f);
model->render(alpha); // TODO: should we allow particles to have alpha on their models?
glPopMatrix();
} else {
glPointSize(sphereRadius);
glBegin(GL_POINTS);
glVertex3f(position.x, position.y, position.z);
glEnd();
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutSolidSphere(radius, 15, 15);
glPopMatrix();
}
}
}

View file

@ -20,6 +20,7 @@
#include <OctreeRenderer.h>
#include <ParticleTree.h>
#include <ViewFrustum.h>
#include "renderer/Model.h"
// Generic client side Octree renderer class.
class ParticleTreeRenderer : public OctreeRenderer {
@ -28,9 +29,9 @@ public:
virtual ~ParticleTreeRenderer();
virtual Octree* createTree() { return new ParticleTree(true); }
virtual NODE_TYPE getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
virtual PACKET_TYPE getMyQueryMessageType() const { return PACKET_TYPE_PARTICLE_QUERY; }
virtual PACKET_TYPE getExpectedPacketType() const { return PACKET_TYPE_PARTICLE_DATA; }
virtual NodeType_t getMyNodeType() const { return NodeType::ParticleServer; }
virtual PacketType getMyQueryMessageType() const { return PacketTypeParticleQuery; }
virtual PacketType getExpectedPacketType() const { return PacketTypeParticleData; }
virtual void renderElement(OctreeElement* element, RenderArgs* args);
void update();
@ -39,7 +40,13 @@ public:
void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode);
virtual void init();
virtual void render();
protected:
Model* getModel(const QString& url);
QMap<QString, Model*> _particleModels;
};
#endif /* defined(__hifi__ParticleTreeRenderer__) */

View file

@ -105,34 +105,6 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) {
return glm::angleAxis(angle, axis);
}
// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's
// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde,
// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
glm::vec3 safeEulerAngles(const glm::quat& q) {
float sy = 2.0f * (q.y * q.w - q.x * q.z);
if (sy < 1.0f - EPSILON) {
if (sy > -1.0f + EPSILON) {
return glm::degrees(glm::vec3(
atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)),
asinf(sy),
atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z))));
} else {
// not a unique solution; x + z = atan2(-m21, m11)
return glm::degrees(glm::vec3(
0.0f,
PIf * -0.5f,
atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z))));
}
} else {
// not a unique solution; x - z = atan2(-m21, m11)
return glm::degrees(glm::vec3(
0.0f,
PIf * 0.5f,
-atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z))));
}
}
// Safe version of glm::mix; based on the code in Nick Bobick's article,
// http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde,
// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
@ -373,9 +345,18 @@ const glm::vec3 randVector() {
}
static TextRenderer* textRenderer(int mono) {
static TextRenderer* monoRenderer = new TextRenderer(MONO_FONT_FAMILY);
static TextRenderer* proportionalRenderer = new TextRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT);
return mono ? monoRenderer : proportionalRenderer;
static TextRenderer* monoRenderer = new TextRenderer(MONO_FONT_FAMILY);
static TextRenderer* proportionalRenderer = new TextRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT);
static TextRenderer* inconsolataRenderer = new TextRenderer(INCONSOLATA_FONT_FAMILY, -1, QFont::Bold, false);
switch (mono) {
case 1:
return monoRenderer;
case 2:
return inconsolataRenderer;
case 0:
default:
return proportionalRenderer;
}
}
int widthText(float scale, int mono, char const* string) {

View file

@ -25,6 +25,9 @@
// the standard mono font family
#define MONO_FONT_FAMILY "Courier"
// the Inconsolata font family
#define INCONSOLATA_FONT_FAMILY "Inconsolata"
void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * fwd, glm::vec3 * left, glm::vec3 * up);
float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos);
@ -51,8 +54,6 @@ float angleBetween(const glm::vec3& v1, const glm::vec3& v2);
glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2);
glm::vec3 safeEulerAngles(const glm::quat& q);
glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);
glm::vec3 extractTranslation(const glm::mat4& matrix);

View file

@ -21,15 +21,15 @@ VoxelHideShowThread::VoxelHideShowThread(VoxelSystem* theSystem) :
}
bool VoxelHideShowThread::process() {
const uint64_t MSECS_TO_USECS = 1000;
const uint64_t SECS_TO_USECS = 1000 * MSECS_TO_USECS;
const uint64_t FRAME_RATE = 60;
const uint64_t USECS_PER_FRAME = SECS_TO_USECS / FRAME_RATE; // every 60fps
const quint64 MSECS_TO_USECS = 1000;
const quint64 SECS_TO_USECS = 1000 * MSECS_TO_USECS;
const quint64 FRAME_RATE = 60;
const quint64 USECS_PER_FRAME = SECS_TO_USECS / FRAME_RATE; // every 60fps
uint64_t start = usecTimestampNow();
quint64 start = usecTimestampNow();
_theSystem->checkForCulling();
uint64_t end = usecTimestampNow();
uint64_t elapsed = end - start;
quint64 end = usecTimestampNow();
quint64 elapsed = end - start;
bool showExtraDebugging = Application::getInstance()->getLogger()->extraDebugging();
if (showExtraDebugging && elapsed > USECS_PER_FRAME) {
@ -38,7 +38,7 @@ bool VoxelHideShowThread::process() {
if (isStillRunning()) {
if (elapsed < USECS_PER_FRAME) {
uint64_t sleepFor = USECS_PER_FRAME - elapsed;
quint64 sleepFor = USECS_PER_FRAME - elapsed;
usleep(sleepFor);
}
}

View file

@ -21,20 +21,30 @@ private:
QString _filename;
};
VoxelImporter::VoxelImporter(QWidget* parent)
: QObject(parent),
_voxelTree(true),
_importDialog(parent),
_currentTask(NULL),
_nextTask(NULL) {
const QString SETTINGS_GROUP_NAME = "VoxelImport";
const QString IMPORT_DIALOG_SETTINGS_KEY = "ImportDialogSettings";
connect(&_importDialog, SIGNAL(previewToggled(bool)), SLOT(preImport()));
connect(&_importDialog, SIGNAL(currentChanged(QString)), SLOT(preImport()));
connect(&_importDialog, SIGNAL(accepted()), SLOT(import()));
VoxelImporter::VoxelImporter(QWidget* parent) :
QObject(parent),
_voxelTree(true),
_importDialog(parent),
_currentTask(NULL),
_nextTask(NULL)
{
connect(&_importDialog, &QFileDialog::currentChanged, this, &VoxelImporter::preImport);
connect(&_importDialog, &QFileDialog::accepted, this, &VoxelImporter::import);
}
void VoxelImporter::init() {
_importDialog.init();
void VoxelImporter::saveSettings(QSettings* settings) {
settings->beginGroup(SETTINGS_GROUP_NAME);
settings->setValue(IMPORT_DIALOG_SETTINGS_KEY, _importDialog.saveState());
settings->endGroup();
}
void VoxelImporter::init(QSettings* settings) {
settings->beginGroup(SETTINGS_GROUP_NAME);
_importDialog.restoreState(settings->value(IMPORT_DIALOG_SETTINGS_KEY).toByteArray());
settings->endGroup();
}
VoxelImporter::~VoxelImporter() {
@ -74,15 +84,13 @@ int VoxelImporter::exec() {
reset();
} else {
_importDialog.reset();
if (_importDialog.getImportIntoClipboard()) {
VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
voxelSystem->copySubTreeIntoNewTree(voxelSystem->getTree()->getRoot(),
Application::getInstance()->getClipboard(),
true);
voxelSystem->changeTree(Application::getInstance()->getClipboard());
}
VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
voxelSystem->copySubTreeIntoNewTree(voxelSystem->getTree()->getRoot(),
Application::getInstance()->getClipboard(),
true);
voxelSystem->changeTree(Application::getInstance()->getClipboard());
}
return ret;
@ -94,24 +102,22 @@ int VoxelImporter::preImport() {
if (!QFileInfo(filename).isFile()) {
return 0;
}
if (_importDialog.getWantPreview()) {
_filename = filename;
if (_nextTask) {
delete _nextTask;
}
_nextTask = new ImportTask(_filename);
connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask()));
if (_currentTask != NULL) {
_voxelTree.cancelImport();
} else {
launchTask();
}
_filename = filename;
if (_nextTask) {
delete _nextTask;
}
_nextTask = new ImportTask(_filename);
connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask()));
if (_currentTask != NULL) {
_voxelTree.cancelImport();
} else {
launchTask();
}
return 1;
}

View file

@ -23,8 +23,9 @@ public:
VoxelImporter(QWidget* parent = NULL);
~VoxelImporter();
void init();
void init(QSettings* settings);
void reset();
void saveSettings(QSettings* settings);
VoxelTree* getVoxelTree() { return &_voxelTree; }

View file

@ -14,16 +14,18 @@
#include "Menu.h"
#include "VoxelPacketProcessor.h"
void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength) {
void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"VoxelPacketProcessor::processPacket()");
QByteArray mutablePacket = packet;
const int WAY_BEHIND = 300;
if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) {
qDebug("VoxelPacketProcessor::processPacket() packets to process=%d", packetsToProcessCount());
}
ssize_t messageLength = packetLength;
ssize_t messageLength = mutablePacket.size();
Application* app = Application::getInstance();
bool wasStatsPacket = false;
@ -34,18 +36,19 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns
app->_voxels.killLocalVoxels();
app->_wantToKillLocalVoxels = false;
}
PacketType voxelPacketType = packetTypeForPacket(mutablePacket);
// note: PACKET_TYPE_OCTREE_STATS can have PACKET_TYPE_VOXEL_DATA
// immediately following them inside the same packet. So, we process the PACKET_TYPE_OCTREE_STATS first
// note: PacketType_OCTREE_STATS can have PacketType_VOXEL_DATA
// immediately following them inside the same packet. So, we process the PacketType_OCTREE_STATS first
// then process any remaining bytes as if it was another packet
if (packetData[0] == PACKET_TYPE_OCTREE_STATS) {
if (voxelPacketType == PacketTypeOctreeStats) {
int statsMessageLength = app->parseOctreeStats(packetData, messageLength, senderSockAddr);
int statsMessageLength = app->parseOctreeStats(mutablePacket, senderSockAddr);
wasStatsPacket = true;
if (messageLength > statsMessageLength) {
packetData += statsMessageLength;
messageLength -= statsMessageLength;
if (!packetVersionMatch(packetData, senderSockAddr)) {
mutablePacket = mutablePacket.mid(statsMessageLength);
if (!packetVersionMatch(packet)) {
return; // bail since piggyback data doesn't match our versioning
}
} else {
@ -53,31 +56,31 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns
return; // bail since no piggyback data
}
} // fall through to piggyback message
voxelPacketType = packetTypeForPacket(mutablePacket);
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
app->trackIncomingVoxelPacket(packetData, messageLength, senderSockAddr, wasStatsPacket);
app->trackIncomingVoxelPacket(mutablePacket, senderSockAddr, wasStatsPacket);
SharedNodePointer serverNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
if (serverNode && serverNode->getActiveSocket() && *serverNode->getActiveSocket() == senderSockAddr) {
switch(packetData[0]) {
case PACKET_TYPE_PARTICLE_ERASE: {
app->_particles.processEraseMessage(QByteArray((char*) packetData, messageLength),
senderSockAddr, serverNode.data());
switch(voxelPacketType) {
case PacketTypeParticleErase: {
app->_particles.processEraseMessage(mutablePacket, senderSockAddr, serverNode.data());
} break;
case PACKET_TYPE_PARTICLE_DATA: {
app->_particles.processDatagram(QByteArray((char*) packetData, messageLength),
senderSockAddr, serverNode.data());
case PacketTypeParticleData: {
app->_particles.processDatagram(mutablePacket, senderSockAddr, serverNode.data());
} break;
case PACKET_TYPE_ENVIRONMENT_DATA: {
app->_environment.parseData(senderSockAddr, packetData, messageLength);
case PacketTypeEnvironmentData: {
app->_environment.parseData(senderSockAddr, mutablePacket);
} break;
default : {
app->_voxels.setDataSourceUUID(serverNode->getUUID());
app->_voxels.parseData(packetData, messageLength);
app->_voxels.parseData(mutablePacket);
app->_voxels.setDataSourceUUID(QUuid());
} break;
}

View file

@ -17,6 +17,6 @@
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
class VoxelPacketProcessor : public ReceivedPacketProcessor {
protected:
virtual void processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength);
virtual void processPacket(const HifiSockAddr& senderSockAddr, const QByteArray& packet);
};
#endif // __shared__VoxelPacketProcessor__

View file

@ -16,7 +16,6 @@
#include <PerfStat.h>
#include <SharedUtil.h>
#include <NodeList.h>
#include <NodeTypes.h>
#include "Application.h"
#include "CoverageMap.h"
@ -581,17 +580,17 @@ bool VoxelSystem::readFromSchematicFile(const char* filename) {
return result;
}
int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
int VoxelSystem::parseData(const QByteArray& packet) {
bool showTimingDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showTimingDetails, "VoxelSystem::parseData()",showTimingDetails);
unsigned char command = *sourceBuffer;
int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer);
PacketType command = packetTypeForPacket(packet);
int numBytesPacketHeader = numBytesForPacketHeader(packet);
switch(command) {
case PACKET_TYPE_VOXEL_DATA: {
PerformanceWarning warn(showTimingDetails, "VoxelSystem::parseData() PACKET_TYPE_VOXEL_DATA part...",showTimingDetails);
unsigned char* dataAt = sourceBuffer + numBytesPacketHeader;
case PacketTypeVoxelData: {
PerformanceWarning warn(showTimingDetails, "VoxelSystem::parseData() PacketType_VOXEL_DATA part...",showTimingDetails);
const unsigned char* dataAt = reinterpret_cast<const unsigned char*>(packet.data()) + numBytesPacketHeader;
VOXEL_PACKET_FLAGS flags = (*(VOXEL_PACKET_FLAGS*)(dataAt));
dataAt += sizeof(VOXEL_PACKET_FLAGS);
@ -608,7 +607,7 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
int flightTime = arrivedAt - sentAt;
VOXEL_PACKET_INTERNAL_SECTION_SIZE sectionLength = 0;
int dataBytes = numBytes - VOXEL_PACKET_HEADER_SIZE;
int dataBytes = packet.size() - VOXEL_PACKET_HEADER_SIZE;
int subsection = 1;
while (dataBytes > 0) {
@ -636,7 +635,8 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
" color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d"
" subsection:%d sectionLength:%d uncompressed:%d",
debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed),
sequence, flightTime, numBytes, dataBytes, subsection, sectionLength, packetData.getUncompressedSize());
sequence, flightTime, packet.size(), dataBytes, subsection, sectionLength,
packetData.getUncompressedSize());
}
_tree->readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args);
unlockTree();
@ -647,7 +647,8 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
}
subsection++;
}
break;
default:
break;
}
if (!_useFastVoxelPipeline || _writeRenderFullVBO) {
setupNewVoxelsForDrawing();
@ -655,9 +656,9 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY);
}
Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::VOXELS).updateValue(numBytes);
Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::VOXELS).updateValue(packet.size());
return numBytes;
return packet.size();
}
void VoxelSystem::setupNewVoxelsForDrawing() {
@ -668,8 +669,8 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
return; // bail early if we're not initialized
}
uint64_t start = usecTimestampNow();
uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
quint64 start = usecTimestampNow();
quint64 sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging
if (!iAmDebugging && sinceLastTime <= std::max((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) {
@ -729,7 +730,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
_bufferWriteLock.unlock();
}
uint64_t end = usecTimestampNow();
quint64 end = usecTimestampNow();
int elapsedmsec = (end - start) / 1000;
_setupNewVoxelsForDrawingLastFinished = end;
_setupNewVoxelsForDrawingLastElapsed = elapsedmsec;
@ -746,8 +747,8 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"setupNewVoxelsForDrawingSingleNode() xxxxx");
uint64_t start = usecTimestampNow();
uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
quint64 start = usecTimestampNow();
quint64 sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging
if (allowBailEarly && !iAmDebugging &&
@ -777,7 +778,7 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) {
_bufferWriteLock.unlock();
}
uint64_t end = usecTimestampNow();
quint64 end = usecTimestampNow();
int elapsedmsec = (end - start) / 1000;
_setupNewVoxelsForDrawingLastFinished = end;
_setupNewVoxelsForDrawingLastElapsed = elapsedmsec;
@ -786,14 +787,14 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) {
void VoxelSystem::checkForCulling() {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "checkForCulling()");
uint64_t start = usecTimestampNow();
quint64 start = usecTimestampNow();
// track how long its been since we were last moving. If we have recently moved then only use delta frustums, if
// it's been a long time since we last moved, then go ahead and do a full frustum cull.
if (isViewChanging()) {
_lastViewIsChanging = start;
}
uint64_t sinceLastMoving = (start - _lastViewIsChanging) / 1000;
quint64 sinceLastMoving = (start - _lastViewIsChanging) / 1000;
bool enoughTime = (sinceLastMoving >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS));
// These has changed events will occur before we stop. So we need to remember this for when we finally have stopped
@ -815,7 +816,7 @@ void VoxelSystem::checkForCulling() {
hideOutOfView(forceFullFrustum);
if (forceFullFrustum) {
uint64_t endViewCulling = usecTimestampNow();
quint64 endViewCulling = usecTimestampNow();
_lastViewCullingElapsed = (endViewCulling - start) / 1000;
}
@ -824,7 +825,7 @@ void VoxelSystem::checkForCulling() {
// VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary.
cleanupRemovedVoxels();
uint64_t sinceLastAudit = (start - _lastAudit) / 1000;
quint64 sinceLastAudit = (start - _lastAudit) / 1000;
if (Menu::getInstance()->isOptionChecked(MenuOption::AutomaticallyAuditTree)) {
if (sinceLastAudit >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)) {
@ -1895,7 +1896,7 @@ void VoxelSystem::falseColorizeBySource() {
// create a bunch of colors we'll use during colorization
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
if (node->getType() == NodeType::VoxelServer) {
uint16_t nodeID = VoxelTreeElement::getSourceNodeUUIDKey(node->getUUID());
int groupColor = voxelServerCount % NUMBER_OF_COLOR_GROUPS;
args.colors[nodeID] = groupColors[groupColor];
@ -2995,7 +2996,7 @@ void VoxelSystem::falseColorizeOccludedV2() {
}
void VoxelSystem::nodeAdded(SharedNodePointer node) {
if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
if (node->getType() == NodeType::VoxelServer) {
qDebug("VoxelSystem... voxel server %s added...", node->getUUID().toString().toLocal8Bit().constData());
_voxelServerCount++;
}
@ -3016,7 +3017,7 @@ bool VoxelSystem::killSourceVoxelsOperation(OctreeElement* element, void* extraD
}
void VoxelSystem::nodeKilled(SharedNodePointer node) {
if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
if (node->getType() == NodeType::VoxelServer) {
_voxelServerCount--;
QUuid nodeUUID = node->getUUID();
qDebug("VoxelSystem... voxel server %s removed...", nodeUUID.toString().toLocal8Bit().constData());

View file

@ -51,7 +51,7 @@ public:
void setDataSourceUUID(const QUuid& dataSourceUUID) { _dataSourceUUID = dataSourceUUID; }
const QUuid& getDataSourceUUID() const { return _dataSourceUUID; }
int parseData(unsigned char* sourceBuffer, int numBytes);
int parseData(const QByteArray& packet);
virtual void init();
void simulate(float deltaTime) { }
@ -235,10 +235,10 @@ private:
bool _readRenderFullVBO;
int _setupNewVoxelsForDrawingLastElapsed;
uint64_t _setupNewVoxelsForDrawingLastFinished;
uint64_t _lastViewCulling;
uint64_t _lastViewIsChanging;
uint64_t _lastAudit;
quint64 _setupNewVoxelsForDrawingLastFinished;
quint64 _lastViewCulling;
quint64 _lastViewIsChanging;
quint64 _lastAudit;
int _lastViewCullingElapsed;
bool _hasRecentlyChanged;

View file

@ -12,7 +12,6 @@
#include <glm/gtx/vector_angle.hpp>
#include <NodeList.h>
#include <NodeTypes.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
@ -336,11 +335,11 @@ bool Avatar::findSphereCollision(const glm::vec3& sphereCenter, float sphereRadi
return false;
}
int Avatar::parseData(unsigned char* sourceBuffer, int numBytes) {
int Avatar::parseData(const QByteArray& packet) {
// change in position implies movement
glm::vec3 oldPosition = _position;
int bytesRead = AvatarData::parseData(sourceBuffer, numBytes);
int bytesRead = AvatarData::parseData(packet);
const float MOVE_DISTANCE_THRESHOLD = 0.001f;
_moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD;

View file

@ -114,7 +114,7 @@ public:
virtual bool isMyAvatar() { return false; }
int parseData(unsigned char* sourceBuffer, int numBytes);
int parseData(const QByteArray& packet);
static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);

View file

@ -16,15 +16,31 @@
#include "AvatarManager.h"
// We add _myAvatar into the hash with all the other AvatarData, and we use the default NULL QUid as the key.
const QUuid MY_AVATAR_KEY; // NULL key
AvatarManager::AvatarManager(QObject* parent) :
_lookAtTargetAvatar(),
_lookAtOtherPosition(),
_lookAtIndicatorScale(1.0f),
_avatarHash(),
_avatarFades()
{
_avatarFades() {
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer");
_myAvatar = QSharedPointer<MyAvatar>(new MyAvatar());
}
void AvatarManager::clear() {
_lookAtTargetAvatar.clear();
_avatarFades.clear();
_avatarHash.clear();
_myAvatar.clear();
}
void AvatarManager::init() {
_myAvatar->init();
_myAvatar->setPosition(START_LOCATION);
_myAvatar->setDisplayingLookatVectors(false);
_avatarHash.insert(MY_AVATAR_KEY, _myAvatar);
}
void AvatarManager::updateLookAtTargetAvatar(glm::vec3 &eyePosition) {
@ -34,22 +50,26 @@ void AvatarManager::updateLookAtTargetAvatar(glm::vec3 &eyePosition) {
Application* applicationInstance = Application::getInstance();
if (!applicationInstance->isMousePressed()) {
foreach (const AvatarSharedPointer& avatar, _avatarHash) {
float distance;
if (avatar->findRayIntersection(applicationInstance->getMouseRayOrigin(),
applicationInstance->getMouseRayDirection(), distance)) {
// rescale to compensate for head embiggening
eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) *
(avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot();
_lookAtIndicatorScale = avatar->getHead().getScale();
_lookAtOtherPosition = avatar->getHead().getPosition();
_lookAtTargetAvatar = avatar;
// found the look at target avatar, return
return;
glm::vec3 mouseOrigin = applicationInstance->getMouseRayOrigin();
glm::vec3 mouseDirection = applicationInstance->getMouseRayDirection();
foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) {
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
if (avatar != static_cast<Avatar*>(_myAvatar.data())) {
float distance;
if (avatar->findRayIntersection(mouseOrigin, mouseDirection, distance)) {
// rescale to compensate for head embiggening
eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) *
(avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot();
_lookAtIndicatorScale = avatar->getHead().getScale();
_lookAtOtherPosition = avatar->getHead().getPosition();
_lookAtTargetAvatar = avatarPointer;
// found the look at target avatar, return
return;
}
}
}
@ -61,20 +81,28 @@ void AvatarManager::updateAvatars(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
Application* applicationInstance = Application::getInstance();
glm::vec3 mouseOrigin = applicationInstance->getMouseRayOrigin();
glm::vec3 mouseDirection = applicationInstance->getMouseRayDirection();
// simulate avatars
AvatarHash::iterator avatar = _avatarHash.begin();
if (avatar != _avatarHash.end()) {
if (avatar->data()->getOwningAvatarMixer()) {
AvatarHash::iterator avatarIterator = _avatarHash.begin();
while (avatarIterator != _avatarHash.end()) {
Avatar* avatar = static_cast<Avatar*>(avatarIterator.value().data());
if (avatar == static_cast<Avatar*>(_myAvatar.data())) {
// for now skip updates to _myAvatar because it is done explicitly in Application
// TODO: update _myAvatar in this context
++avatarIterator;
continue;
}
if (avatar->getOwningAvatarMixer()) {
// this avatar's mixer is still around, go ahead and simulate it
avatar->data()->simulate(deltaTime, NULL);
Application* applicationInstance = Application::getInstance();
avatar->data()->setMouseRay(applicationInstance->getMouseRayOrigin(),
applicationInstance->getMouseRayDirection());
avatar->simulate(deltaTime, NULL);
avatar->setMouseRay(mouseOrigin, mouseDirection);
++avatarIterator;
} else {
// the mixer that owned this avatar is gone, give it to the vector of fades and kill it
avatar = removeAvatarAtHashIterator(avatar);
avatarIterator = erase(avatarIterator);
}
}
@ -88,41 +116,44 @@ void AvatarManager::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
}
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::renderAvatars()");
bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors);
if (!selfAvatarOnly) {
// Render avatars of other nodes
foreach (const AvatarSharedPointer& avatar, _avatarHash) {
foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) {
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
if (!avatar->isInitialized()) {
avatar->init();
}
avatar->render(false);
avatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors));
if (avatar == static_cast<Avatar*>(_myAvatar.data())) {
avatar->render(forceRenderHead);
} else {
avatar->render(false);
}
avatar->setDisplayingLookatVectors(renderLookAtVectors);
}
renderAvatarFades();
} else {
// just render myAvatar
_myAvatar->render(forceRenderHead);
_myAvatar->setDisplayingLookatVectors(renderLookAtVectors);
}
// Render my own Avatar
Avatar* myAvatar = Application::getInstance()->getAvatar();
myAvatar->render(forceRenderHead);
myAvatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors));
}
void AvatarManager::simulateAvatarFades(float deltaTime) {
QVector<AvatarSharedPointer>::iterator fadingAvatar = _avatarFades.begin();
QVector<AvatarSharedPointer>::iterator fadingIterator = _avatarFades.begin();
while (fadingAvatar != _avatarFades.end()) {
const float SHRINK_RATE = 0.9f;
fadingAvatar->data()->setTargetScale(fadingAvatar->data()->getScale() * SHRINK_RATE);
const float MIN_FADE_SCALE = 0.001f;
if (fadingAvatar->data()->getTargetScale() < MIN_FADE_SCALE) {
fadingAvatar = _avatarFades.erase(fadingAvatar);
const float SHRINK_RATE = 0.9f;
const float MIN_FADE_SCALE = 0.001f;
while (fadingIterator != _avatarFades.end()) {
Avatar* avatar = static_cast<Avatar*>(fadingIterator->data());
avatar->setTargetScale(avatar->getScale() * SHRINK_RATE);
if (avatar->getTargetScale() < MIN_FADE_SCALE) {
fadingIterator = _avatarFades.erase(fadingIterator);
} else {
fadingAvatar->data()->simulate(deltaTime, NULL);
avatar->simulate(deltaTime, NULL);
++fadingIterator;
}
}
}
@ -132,29 +163,36 @@ void AvatarManager::renderAvatarFades() {
Glower glower;
foreach(const AvatarSharedPointer& fadingAvatar, _avatarFades) {
fadingAvatar->render(false);
Avatar* avatar = static_cast<Avatar*>(fadingAvatar.data());
avatar->render(false);
}
}
void AvatarManager::processDataServerResponse(const QString& userString, const QStringList& keyList,
const QStringList &valueList) {
QUuid avatarKey = QUuid(userString);
if (avatarKey == MY_AVATAR_KEY) {
// ignore updates to our own mesh
return;
}
for (int i = 0; i < keyList.size(); i++) {
if (valueList[i] != " ") {
if (keyList[i] == DataServerKey::FaceMeshURL || keyList[i] == DataServerKey::SkeletonURL) {
// mesh URL for a UUID, find avatar in our list
AvatarSharedPointer matchingAvatar = _avatarHash.value(QUuid(userString));
AvatarSharedPointer matchingAvatar = _avatarHash.value(avatarKey);
if (matchingAvatar) {
Avatar* avatar = static_cast<Avatar*>(matchingAvatar.data());
if (keyList[i] == DataServerKey::FaceMeshURL) {
qDebug() << "Changing mesh to" << valueList[i] << "for avatar with UUID"
<< uuidStringWithoutCurlyBraces(QUuid(userString));
<< uuidStringWithoutCurlyBraces(avatarKey);
QMetaObject::invokeMethod(&matchingAvatar->getHead().getFaceModel(),
QMetaObject::invokeMethod(&(avatar->getHead().getFaceModel()),
"setURL", Q_ARG(QUrl, QUrl(valueList[i])));
} else if (keyList[i] == DataServerKey::SkeletonURL) {
qDebug() << "Changing skeleton to" << valueList[i] << "for avatar with UUID"
<< uuidStringWithoutCurlyBraces(QString(userString));
<< uuidStringWithoutCurlyBraces(avatarKey.toString());
QMetaObject::invokeMethod(&matchingAvatar->getSkeletonModel(),
QMetaObject::invokeMethod(&(avatar->getSkeletonModel()),
"setURL", Q_ARG(QUrl, QUrl(valueList[i])));
}
}
@ -164,15 +202,12 @@ void AvatarManager::processDataServerResponse(const QString& userString, const Q
}
void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer) {
unsigned char packetData[MAX_PACKET_SIZE];
memcpy(packetData, datagram.data(), datagram.size());
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
int bytesRead = numBytesForPacketHeader(datagram);
int bytesRead = numBytesPacketHeader;
unsigned char avatarData[MAX_PACKET_SIZE];
int numBytesDummyPacketHeader = populateTypeAndVersion(avatarData, PACKET_TYPE_HEAD_DATA);
QByteArray dummyAvatarByteArray = byteArrayWithPopluatedHeader(PacketTypeAvatarData);
int numDummyHeaderBytes = dummyAvatarByteArray.size();
int numDummyHeaderBytesWithoutUUID = numDummyHeaderBytes - NUM_BYTES_RFC4122_UUID;
// enumerate over all of the avatars in this packet
// only add them if mixerWeakPointer points to something (meaning that mixer is still around)
@ -183,10 +218,11 @@ void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const
if (!matchingAvatar) {
// construct a new Avatar for this node
matchingAvatar = AvatarSharedPointer(new Avatar());
matchingAvatar->setOwningAvatarMixer(mixerWeakPointer);
Avatar* avatar = new Avatar();
avatar->setOwningAvatarMixer(mixerWeakPointer);
// insert the new avatar into our hash
matchingAvatar = AvatarSharedPointer(avatar);
_avatarHash.insert(nodeUUID, matchingAvatar);
// new UUID requires mesh and skeleton request to data-server
@ -197,37 +233,44 @@ void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const
}
// copy the rest of the packet to the avatarData holder so we can read the next Avatar from there
memcpy(avatarData + numBytesDummyPacketHeader, packetData + bytesRead, datagram.size() - bytesRead);
dummyAvatarByteArray.resize(numDummyHeaderBytesWithoutUUID);
// make this Avatar's UUID the UUID in the packet and tack the remaining data onto the end
dummyAvatarByteArray.append(datagram.mid(bytesRead));
// have the matching (or new) avatar parse the data from the packet
bytesRead += matchingAvatar->parseData(avatarData, datagram.size() - bytesRead);
bytesRead += matchingAvatar->parseData(dummyAvatarByteArray) - numDummyHeaderBytesWithoutUUID;
}
}
void AvatarManager::processKillAvatar(const QByteArray& datagram) {
// read the node id
QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(numBytesForPacketHeader(reinterpret_cast<const unsigned char*>
(datagram.data())),
NUM_BYTES_RFC4122_UUID));
QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(numBytesForPacketHeader(datagram), NUM_BYTES_RFC4122_UUID));
// remove the avatar with that UUID from our hash, if it exists
AvatarHash::iterator matchedAvatar = _avatarHash.find(nodeUUID);
if (matchedAvatar != _avatarHash.end()) {
removeAvatarAtHashIterator(matchedAvatar);
erase(matchedAvatar);
}
}
AvatarHash::iterator AvatarManager::removeAvatarAtHashIterator(const AvatarHash::iterator& iterator) {
qDebug() << "Removing Avatar with UUID" << iterator.key() << "from AvatarManager hash.";
_avatarFades.push_back(iterator.value());
return _avatarHash.erase(iterator);
AvatarHash::iterator AvatarManager::erase(const AvatarHash::iterator& iterator) {
if (iterator.key() != MY_AVATAR_KEY) {
qDebug() << "Removing Avatar with UUID" << iterator.key() << "from AvatarManager hash.";
_avatarFades.push_back(iterator.value());
return AvatarHashMap::erase(iterator);
} else {
// never remove _myAvatar from the list
AvatarHash::iterator returnIterator = iterator;
return ++returnIterator;
}
}
void AvatarManager::clearHash() {
// clear the AvatarManager hash - typically happens on the removal of the avatar-mixer
void AvatarManager::clearMixedAvatars() {
// clear any avatars that came from an avatar-mixer
AvatarHash::iterator removeAvatar = _avatarHash.begin();
while (removeAvatar != _avatarHash.end()) {
removeAvatar = removeAvatarAtHashIterator(removeAvatar);
removeAvatar = erase(removeAvatar);
}
}
_lookAtTargetAvatar.clear();
}

View file

@ -13,30 +13,33 @@
#include <QtCore/QObject>
#include <QtCore/QSharedPointer>
#include <AvatarHashMap.h>
#include <DataServerClient.h>
#include "Avatar.h"
typedef QSharedPointer<Avatar> AvatarSharedPointer;
typedef QHash<QUuid, AvatarSharedPointer> AvatarHash;
class MyAvatar;
class AvatarManager : public QObject, public DataServerCallbackObject {
class AvatarManager : public QObject, public DataServerCallbackObject, public AvatarHashMap {
Q_OBJECT
public:
AvatarManager(QObject* parent = 0);
void clear();
void init();
MyAvatar* getMyAvatar() { return _myAvatar.data(); }
const AvatarHash& getAvatarHash() { return _avatarHash; }
int size() const { return _avatarHash.size(); }
Avatar* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
void updateLookAtTargetAvatar(glm::vec3& eyePosition);
void updateAvatars(float deltaTime);
void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false);
void clearHash();
void clearMixedAvatars();
public slots:
void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList);
@ -44,17 +47,20 @@ public slots:
void processKillAvatar(const QByteArray& datagram);
private:
AvatarManager(const AvatarManager& other);
void simulateAvatarFades(float deltaTime);
void renderAvatarFades();
AvatarHash::iterator removeAvatarAtHashIterator(const AvatarHash::iterator& iterator);
// virtual override
AvatarHash::iterator erase(const AvatarHash::iterator& iterator);
QWeakPointer<Avatar> _lookAtTargetAvatar;
QWeakPointer<AvatarData> _lookAtTargetAvatar;
glm::vec3 _lookAtOtherPosition;
float _lookAtIndicatorScale;
AvatarHash _avatarHash;
QVector<AvatarSharedPointer> _avatarFades;
QSharedPointer<MyAvatar> _myAvatar;
};
#endif /* defined(__hifi__AvatarManager__) */

View file

@ -183,7 +183,12 @@ void Hand::updateCollisions() {
glm::vec3 totalPenetration;
// check other avatars
foreach (const AvatarSharedPointer& avatar, Application::getInstance()->getAvatarManager().getAvatarHash()) {
foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) {
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
if (avatar == _owningAvatar) {
// don't collid with our own hands
continue;
}
if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
// Check for palm collisions
glm::vec3 myPalmPosition = palm.getPosition();
@ -205,9 +210,9 @@ void Hand::updateCollisions() {
const float PALM_COLLIDE_DURATION_MAX = 0.75f;
const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f;
Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME,
PALM_COLLIDE_FREQUENCY,
PALM_COLLIDE_DURATION_MAX,
PALM_COLLIDE_DECAY_PER_SAMPLE);
PALM_COLLIDE_FREQUENCY,
PALM_COLLIDE_DURATION_MAX,
PALM_COLLIDE_DECAY_PER_SAMPLE);
// If the other person's palm is in motion, move mine downward to show I was hit
const float MIN_VELOCITY_FOR_SLAP = 0.05f;
if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) {

View file

@ -37,8 +37,6 @@ Head::Head(Avatar* owningAvatar) :
_leftEyeBlinkVelocity(0.0f),
_rightEyeBlinkVelocity(0.0f),
_timeWithoutTalking(0.0f),
_cameraPitch(_pitch),
_mousePitch(0.f),
_cameraYaw(_yaw),
_isCameraMoving(false),
_faceModel(this)
@ -52,7 +50,6 @@ void Head::init() {
void Head::reset() {
_yaw = _pitch = _roll = 0.0f;
_mousePitch = 0.0f;
_leanForward = _leanSideways = 0.0f;
_faceModel.reset();
}
@ -186,13 +183,6 @@ void Head::setScale (float scale) {
_scale = scale;
}
void Head::setMousePitch(float mousePitch) {
const float MAX_PITCH = 90.0f;
_mousePitch = glm::clamp(mousePitch, -MAX_PITCH, MAX_PITCH);
}
glm::quat Head::getOrientation() const {
return glm::quat(glm::radians(_bodyRotation)) * glm::quat(glm::radians(glm::vec3(_pitch, _yaw, _roll)));
}
@ -200,7 +190,7 @@ glm::quat Head::getOrientation() const {
glm::quat Head::getCameraOrientation () const {
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
return owningAvatar->getWorldAlignedOrientation()
* glm::quat(glm::radians(glm::vec3(_cameraPitch + _mousePitch, _cameraYaw, 0.0f)));
* glm::quat(glm::radians(glm::vec3(_pitch, _cameraYaw, 0.0f)));
}
glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const {

View file

@ -46,9 +46,6 @@ public:
void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; }
void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; }
float getMousePitch() const { return _mousePitch; }
void setMousePitch(float mousePitch);
glm::quat getOrientation() const;
glm::quat getCameraOrientation () const;
const glm::vec3& getAngularVelocity() const { return _angularVelocity; }
@ -99,8 +96,6 @@ private:
float _leftEyeBlinkVelocity;
float _rightEyeBlinkVelocity;
float _timeWithoutTalking;
float _cameraPitch; // Used to position the camera differently from the head
float _mousePitch;
float _cameraYaw;
bool _isCameraMoving;
FaceModel _faceModel;

View file

@ -12,7 +12,6 @@
#include <glm/gtx/vector_angle.hpp>
#include <NodeList.h>
#include <NodeTypes.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
@ -238,7 +237,7 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
// Adjust body yaw by yaw from controller
setOrientation(glm::angleAxis(-euler.y, glm::vec3(0, 1, 0)) * getOrientation());
// Adjust head pitch from controller
getHead().setMousePitch(getHead().getMousePitch() - euler.x);
getHead().setPitch(getHead().getPitch() - euler.x);
_position += _velocity * deltaTime;
@ -284,8 +283,6 @@ void MyAvatar::updateFromGyros(bool turnWithHead) {
}
}
} else {
_head.setPitch(_head.getMousePitch());
// restore rotation, lean to neutral positions
const float RESTORE_RATE = 0.05f;
_head.setYaw(glm::mix(_head.getYaw(), 0.0f, RESTORE_RATE));
@ -434,7 +431,7 @@ void MyAvatar::saveData(QSettings* settings) {
settings->setValue("bodyPitch", _bodyPitch);
settings->setValue("bodyRoll", _bodyRoll);
settings->setValue("mousePitch", _head.getMousePitch());
settings->setValue("headPitch", _head.getPitch());
settings->setValue("position_x", _position.x);
settings->setValue("position_y", _position.y);
@ -456,7 +453,7 @@ void MyAvatar::loadData(QSettings* settings) {
_bodyPitch = loadSetting(settings, "bodyPitch", 0.0f);
_bodyRoll = loadSetting(settings, "bodyRoll", 0.0f);
_head.setMousePitch(loadSetting(settings, "mousePitch", 0.0f));
_head.setPitch(loadSetting(settings, "headPitch", 0.0f));
_position.x = loadSetting(settings, "position_x", 0.0f);
_position.y = loadSetting(settings, "position_y", 0.0f);
@ -473,18 +470,8 @@ void MyAvatar::loadData(QSettings* settings) {
}
void MyAvatar::sendKillAvatar() {
unsigned char packet[MAX_PACKET_SIZE];
unsigned char* packetPosition = packet;
packetPosition += populateTypeAndVersion(packetPosition, PACKET_TYPE_KILL_AVATAR);
NodeList* nodeList = NodeList::getInstance();
QByteArray rfcUUID = nodeList->getOwnerUUID().toRfc4122();
memcpy(packetPosition, rfcUUID.constData(), rfcUUID.size());
packetPosition += rfcUUID.size();
nodeList->broadcastToNodes(packet, packetPosition - packet, QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
QByteArray killPacket = byteArrayWithPopluatedHeader(PacketTypeKillAvatar);
NodeList::getInstance()->broadcastToNodes(killPacket, NodeSet() << NodeType::AvatarMixer);
}
void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) {
@ -497,9 +484,10 @@ void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) {
setOrientation(orientation);
// then vertically
float oldMousePitch = _head.getMousePitch();
_head.setMousePitch(oldMousePitch + deltaY * -ANGULAR_SCALE);
rotation = glm::angleAxis(_head.getMousePitch() - oldMousePitch, orientation * IDENTITY_RIGHT);
float oldPitch = _head.getPitch();
_head.setPitch(oldPitch + deltaY * -ANGULAR_SCALE);
rotation = glm::angleAxis(_head.getPitch() - oldPitch, orientation * IDENTITY_RIGHT);
setPosition(position + rotation * (getPosition() - position));
}
@ -549,7 +537,7 @@ void MyAvatar::updateThrust(float deltaTime, Transmitter * transmitter) {
_thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up;
_bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_MAG * deltaTime;
_bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_MAG * deltaTime;
_head.setMousePitch(_head.getMousePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_MAG * deltaTime);
_head.setPitch(_head.getPitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_MAG * deltaTime);
// If thrust keys are being held down, slowly increase thrust to allow reaching great speeds
if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) {
@ -791,14 +779,15 @@ void MyAvatar::updateChatCircle(float deltaTime) {
// find all circle-enabled members and sort by distance
QVector<SortedAvatar> sortedAvatars;
foreach (const AvatarSharedPointer& avatar, Application::getInstance()->getAvatarManager().getAvatarHash()) {
SortedAvatar sortedAvatar;
sortedAvatar.avatar = avatar.data();
if (!sortedAvatar.avatar->isChatCirclingEnabled()) {
foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) {
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
if ( ! avatar->isChatCirclingEnabled() ||
avatar == static_cast<Avatar*>(this)) {
continue;
}
SortedAvatar sortedAvatar;
sortedAvatar.avatar = avatar;
sortedAvatar.distance = glm::distance(_position, sortedAvatar.avatar->getPosition());
sortedAvatars.append(sortedAvatar);
}

View file

@ -53,7 +53,10 @@ void Profile::setUUID(const QUuid& uuid) {
// when the UUID is changed we need set it appropriately on the NodeList instance
NodeList::getInstance()->setOwnerUUID(uuid);
}
// ask for a window title update so the new UUID is presented
Application::getInstance()->updateWindowTitle();
}
}
void Profile::setFaceModelURL(const QUrl& faceModelURL) {
@ -87,7 +90,7 @@ void Profile::updatePosition(const glm::vec3 position) {
if (_lastPosition != position) {
static timeval lastPositionSend = {};
const uint64_t DATA_SERVER_POSITION_UPDATE_INTERVAL_USECS = 5 * 1000 * 1000;
const quint64 DATA_SERVER_POSITION_UPDATE_INTERVAL_USECS = 5 * 1000 * 1000;
const float DATA_SERVER_POSITION_CHANGE_THRESHOLD_METERS = 1;
if (usecTimestampNow() - usecTimestamp(&lastPositionSend) >= DATA_SERVER_POSITION_UPDATE_INTERVAL_USECS &&
@ -115,10 +118,10 @@ void Profile::updateOrientation(const glm::quat& orientation) {
if (_lastOrientation == eulerAngles) {
return;
}
const uint64_t DATA_SERVER_ORIENTATION_UPDATE_INTERVAL_USECS = 5 * 1000 * 1000;
const quint64 DATA_SERVER_ORIENTATION_UPDATE_INTERVAL_USECS = 5 * 1000 * 1000;
const float DATA_SERVER_ORIENTATION_CHANGE_THRESHOLD_DEGREES = 5.0f;
uint64_t now = usecTimestampNow();
quint64 now = usecTimestampNow();
if (now - _lastOrientationSend >= DATA_SERVER_ORIENTATION_UPDATE_INTERVAL_USECS &&
glm::distance(_lastOrientation, eulerAngles) >= DATA_SERVER_ORIENTATION_CHANGE_THRESHOLD_DEGREES) {
DataServerClient::putValueForKeyAndUserString(DataServerKey::Orientation, QString(createByteArray(eulerAngles)),

View file

@ -57,7 +57,7 @@ private:
QString _lastDomain;
glm::vec3 _lastPosition;
glm::vec3 _lastOrientation;
uint64_t _lastOrientationSend;
quint64 _lastOrientationSend;
QUrl _faceModelURL;
QUrl _skeletonModelURL;
};

View file

@ -56,7 +56,7 @@ Faceshift::Faceshift() :
}
bool Faceshift::isActive() const {
const uint64_t ACTIVE_TIMEOUT_USECS = 1000000;
const quint64 ACTIVE_TIMEOUT_USECS = 1000000;
return (usecTimestampNow() - _lastTrackingStateReceived) < ACTIVE_TIMEOUT_USECS;
}

View file

@ -90,7 +90,7 @@ private:
bool _tcpEnabled;
int _tcpRetryCount;
bool _tracking;
uint64_t _lastTrackingStateReceived;
quint64 _lastTrackingStateReceived;
glm::quat _headRotation;
glm::vec3 _headAngularVelocity;

View file

@ -25,7 +25,7 @@ public slots:
private:
uint64_t _lastMovement;
quint64 _lastMovement;
};
#endif /* defined(__interface__SixenseManager__) */

View file

@ -60,7 +60,7 @@ void Transmitter::resetLevels() {
void Transmitter::processIncomingData(unsigned char* packetData, int numBytes) {
// Packet's first byte is 'T'
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(packetData));
const int ROTATION_MARKER_SIZE = 1; // 'R' = Rotation (clockwise about x,y,z)
const int ACCELERATION_MARKER_SIZE = 1; // 'A' = Acceleration (x,y,z)

View file

@ -170,6 +170,7 @@ public:
const QByteArray& getDatum() const { return _datum; }
void pushBackToken(int token) { _pushedBackToken = token; }
void ungetChar(char ch) { _device->ungetChar(ch); }
private:
@ -221,7 +222,7 @@ int Tokenizer::nextToken() {
_datum.append(ch);
while (_device->getChar(&ch)) {
if (QChar(ch).isSpace() || ch == ';' || ch == ':' || ch == '{' || ch == '}' || ch == ',' || ch == '\"') {
_device->ungetChar(ch); // read until we encounter a special character, then replace it
ungetChar(ch); // read until we encounter a special character, then replace it
break;
}
_datum.append(ch);
@ -257,9 +258,17 @@ FBXNode parseTextFBXNode(Tokenizer& tokenizer) {
expectingDatum = true;
} else if (token == Tokenizer::DATUM_TOKEN && expectingDatum) {
node.properties.append(tokenizer.getDatum());
expectingDatum = false;
QByteArray datum = tokenizer.getDatum();
if ((token = tokenizer.nextToken()) == ':') {
tokenizer.ungetChar(':');
tokenizer.pushBackToken(Tokenizer::DATUM_TOKEN);
return node;
} else {
tokenizer.pushBackToken(token);
node.properties.append(datum);
expectingDatum = false;
}
} else {
tokenizer.pushBackToken(token);
return node;
@ -377,6 +386,9 @@ glm::mat4 createMat4(const QVector<double>& doubleVector) {
}
QVector<int> getIntVector(const QVariantList& properties, int index) {
if (index >= properties.size()) {
return QVector<int>();
}
QVector<int> vector = properties.at(index).value<QVector<int> >();
if (!vector.isEmpty()) {
return vector;
@ -388,6 +400,9 @@ QVector<int> getIntVector(const QVariantList& properties, int index) {
}
QVector<double> getDoubleVector(const QVariantList& properties, int index) {
if (index >= properties.size()) {
return QVector<double>();
}
QVector<double> vector = properties.at(index).value<QVector<double> >();
if (!vector.isEmpty()) {
return vector;
@ -723,6 +738,22 @@ ExtractedMesh extractMesh(const FBXNode& object) {
return data.extracted;
}
FBXBlendshape extractBlendshape(const FBXNode& object) {
FBXBlendshape blendshape;
foreach (const FBXNode& data, object.children) {
if (data.name == "Indexes") {
blendshape.indices = getIntVector(data.properties, 0);
} else if (data.name == "Vertices") {
blendshape.vertices = createVec3Vector(getDoubleVector(data.properties, 0));
} else if (data.name == "Normals") {
blendshape.normals = createVec3Vector(getDoubleVector(data.properties, 0));
}
}
return blendshape;
}
void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) {
glm::vec3 normal = glm::normalize(mesh.normals.at(firstIndex));
glm::vec3 bitangent = glm::cross(normal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex));
@ -745,6 +776,49 @@ QVector<int> getIndices(const QVector<QString> ids, QVector<QString> modelIDs) {
return indices;
}
typedef QPair<int, float> WeightedIndex;
void addBlendshapes(const ExtractedBlendshape& extracted, const QList<WeightedIndex>& indices, ExtractedMesh& extractedMesh) {
foreach (const WeightedIndex& index, indices) {
extractedMesh.mesh.blendshapes.resize(max(extractedMesh.mesh.blendshapes.size(), index.first + 1));
extractedMesh.blendshapeIndexMaps.resize(extractedMesh.mesh.blendshapes.size());
FBXBlendshape& blendshape = extractedMesh.mesh.blendshapes[index.first];
QHash<int, int>& blendshapeIndexMap = extractedMesh.blendshapeIndexMaps[index.first];
for (int i = 0; i < extracted.blendshape.indices.size(); i++) {
int oldIndex = extracted.blendshape.indices.at(i);
for (QMultiHash<int, int>::const_iterator it = extractedMesh.newIndices.constFind(oldIndex);
it != extractedMesh.newIndices.constEnd() && it.key() == oldIndex; it++) {
QHash<int, int>::iterator blendshapeIndex = blendshapeIndexMap.find(it.value());
if (blendshapeIndex == blendshapeIndexMap.end()) {
blendshapeIndexMap.insert(it.value(), blendshape.indices.size());
blendshape.indices.append(it.value());
blendshape.vertices.append(extracted.blendshape.vertices.at(i) * index.second);
blendshape.normals.append(extracted.blendshape.normals.at(i) * index.second);
} else {
blendshape.vertices[*blendshapeIndex] += extracted.blendshape.vertices.at(i) * index.second;
blendshape.normals[*blendshapeIndex] += extracted.blendshape.normals.at(i) * index.second;
}
}
}
}
}
QString getTopModelID(const QMultiHash<QString, QString>& parentMap,
const QHash<QString, FBXModel>& models, const QString& modelID) {
QString topID = modelID;
forever {
foreach (const QString& parentID, parentMap.values(topID)) {
if (models.contains(parentID)) {
topID = parentID;
goto outerContinue;
}
}
return topID;
outerContinue: ;
}
}
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {
QHash<QString, ExtractedMesh> meshes;
QVector<ExtractedBlendshape> blendshapes;
@ -784,7 +858,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
QVector<QString> jointRightFingertipIDs(jointRightFingertipNames.size());
QVariantHash blendshapeMappings = mapping.value("bs").toHash();
typedef QPair<int, float> WeightedIndex;
QMultiHash<QByteArray, WeightedIndex> blendshapeIndices;
for (int i = 0;; i++) {
QByteArray blendshapeName = FACESHIFT_BLENDSHAPES[i];
@ -812,22 +885,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
meshes.insert(getID(object.properties), extractMesh(object));
} else { // object.properties.at(2) == "Shape"
ExtractedBlendshape extracted = { getID(object.properties) };
foreach (const FBXNode& data, object.children) {
if (data.name == "Indexes") {
extracted.blendshape.indices = getIntVector(data.properties, 0);
} else if (data.name == "Vertices") {
extracted.blendshape.vertices = createVec3Vector(
getDoubleVector(data.properties, 0));
} else if (data.name == "Normals") {
extracted.blendshape.normals = createVec3Vector(
getDoubleVector(data.properties, 0));
}
}
ExtractedBlendshape extracted = { getID(object.properties), extractBlendshape(object) };
blendshapes.append(extracted);
}
} else if (object.name == "Model") {
@ -885,6 +943,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
bool rotationMaxX = false, rotationMaxY = false, rotationMaxZ = false;
glm::vec3 rotationMin, rotationMax;
FBXModel model = { name, -1 };
ExtractedMesh* mesh = NULL;
QVector<ExtractedBlendshape> blendshapes;
foreach (const FBXNode& subobject, object.children) {
bool properties = false;
QByteArray propertyName;
@ -954,9 +1014,23 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
}
} else if (subobject.name == "Vertices") {
// it's a mesh as well as a model
meshes.insert(getID(object.properties), extractMesh(object));
mesh = &meshes[getID(object.properties)];
*mesh = extractMesh(object);
} else if (subobject.name == "Shape") {
ExtractedBlendshape blendshape = { subobject.properties.at(0).toString(),
extractBlendshape(subobject) };
blendshapes.append(blendshape);
}
}
// add the blendshapes included in the model, if any
if (mesh) {
foreach (const ExtractedBlendshape& extracted, blendshapes) {
addBlendshapes(extracted, blendshapeIndices.values(extracted.id.toLatin1()), *mesh);
}
}
// see FBX documentation, http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html
model.translation = translation;
model.preTransform = glm::translate(rotationOffset) * glm::translate(rotationPivot);
@ -1069,29 +1143,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
QString blendshapeChannelID = parentMap.value(extracted.id);
QString blendshapeID = parentMap.value(blendshapeChannelID);
QString meshID = parentMap.value(blendshapeID);
ExtractedMesh& extractedMesh = meshes[meshID];
foreach (const WeightedIndex& index, blendshapeChannelIndices.values(blendshapeChannelID)) {
extractedMesh.mesh.blendshapes.resize(max(extractedMesh.mesh.blendshapes.size(), index.first + 1));
extractedMesh.blendshapeIndexMaps.resize(extractedMesh.mesh.blendshapes.size());
FBXBlendshape& blendshape = extractedMesh.mesh.blendshapes[index.first];
QHash<int, int>& blendshapeIndexMap = extractedMesh.blendshapeIndexMaps[index.first];
for (int i = 0; i < extracted.blendshape.indices.size(); i++) {
int oldIndex = extracted.blendshape.indices.at(i);
for (QMultiHash<int, int>::const_iterator it = extractedMesh.newIndices.constFind(oldIndex);
it != extractedMesh.newIndices.constEnd() && it.key() == oldIndex; it++) {
QHash<int, int>::iterator blendshapeIndex = blendshapeIndexMap.find(it.value());
if (blendshapeIndex == blendshapeIndexMap.end()) {
blendshapeIndexMap.insert(it.value(), blendshape.indices.size());
blendshape.indices.append(it.value());
blendshape.vertices.append(extracted.blendshape.vertices.at(i) * index.second);
blendshape.normals.append(extracted.blendshape.normals.at(i) * index.second);
} else {
blendshape.vertices[*blendshapeIndex] += extracted.blendshape.vertices.at(i) * index.second;
blendshape.normals[*blendshapeIndex] += extracted.blendshape.normals.at(i) * index.second;
}
}
}
}
addBlendshapes(extracted, blendshapeChannelIndices.values(blendshapeChannelID), meshes[meshID]);
}
// get offset transform from mapping
@ -1106,23 +1158,30 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
QVector<QString> modelIDs;
QSet<QString> remainingModels;
for (QHash<QString, FBXModel>::const_iterator model = models.constBegin(); model != models.constEnd(); model++) {
// models with clusters must be parented to the cluster top
foreach (const QString& deformerID, childMap.values(model.key())) {
foreach (const QString& clusterID, childMap.values(deformerID)) {
if (!clusters.contains(clusterID)) {
continue;
}
QString topID = getTopModelID(parentMap, models, childMap.value(clusterID));
childMap.remove(parentMap.take(model.key()), model.key());
parentMap.insert(model.key(), topID);
goto outerBreak;
}
}
outerBreak:
// make sure the parent is in the child map
QString parent = parentMap.value(model.key());
if (!childMap.contains(parent, model.key())) {
childMap.insert(parent, model.key());
}
remainingModels.insert(model.key());
}
while (!remainingModels.isEmpty()) {
QString top = *remainingModels.constBegin();
forever {
foreach (const QString& name, parentMap.values(top)) {
if (models.contains(name)) {
top = name;
goto outerContinue;
}
}
top = parentMap.value(top);
break;
outerContinue: ;
}
appendModelIDs(top, childMap, models, remainingModels, modelIDs);
QString topID = getTopModelID(parentMap, models, *remainingModels.constBegin());
appendModelIDs(parentMap.value(topID), childMap, models, remainingModels, modelIDs);
}
// convert the models to joints

View file

@ -12,6 +12,7 @@
#include <QNetworkReply>
#include <QOpenGLBuffer>
#include <QTimer>
#include "Application.h"
#include "GeometryCache.h"
@ -507,3 +508,17 @@ void NetworkGeometry::maybeReadModelWithMapping() {
_meshes.append(networkMesh);
}
}
bool NetworkMeshPart::isTranslucent() const {
return diffuseTexture && diffuseTexture->isTranslucent();
}
int NetworkMesh::getTranslucentPartCount() const {
int count = 0;
foreach (const NetworkMeshPart& part, parts) {
if (part.isTranslucent()) {
count++;
}
}
return count;
}

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