mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-08 01:22:25 +02:00
- Layout fix
- Model browsers - merge
This commit is contained in:
parent
b417851dde
commit
9077a05f9d
41 changed files with 2347 additions and 1533 deletions
|
@ -62,7 +62,12 @@ void attachNewBufferToNode(Node *newNode) {
|
|||
}
|
||||
|
||||
AudioMixer::AudioMixer(const QByteArray& packet) :
|
||||
ThreadedAssignment(packet)
|
||||
ThreadedAssignment(packet),
|
||||
_trailingSleepRatio(1.0f),
|
||||
_minSourceLoudnessInFrame(1.0f),
|
||||
_maxSourceLoudnessInFrame(0.0f),
|
||||
_loudnessCutoffRatio(0.0f),
|
||||
_minRequiredLoudness(0.0f)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -301,7 +306,7 @@ void AudioMixer::prepareMixForListeningNode(Node* node) {
|
|||
if ((*otherNode != *node
|
||||
|| otherNodeBuffer->shouldLoopbackForNode())
|
||||
&& otherNodeBuffer->willBeAddedToMix()
|
||||
&& otherNodeClientData->getNextOutputLoudness() > 0) {
|
||||
&& otherNodeBuffer->getAverageLoudness() > _minRequiredLoudness) {
|
||||
addBufferToMixForListeningNodeWithBuffer(otherNodeBuffer, nodeRingBuffer);
|
||||
}
|
||||
}
|
||||
|
@ -350,14 +355,65 @@ void AudioMixer::run() {
|
|||
|
||||
char* clientMixBuffer = new char[NETWORK_BUFFER_LENGTH_BYTES_STEREO
|
||||
+ numBytesForPacketHeaderGivenPacketType(PacketTypeMixedAudio)];
|
||||
|
||||
int usecToSleep = BUFFER_SEND_INTERVAL_USECS;
|
||||
|
||||
while (!_isFinished) {
|
||||
|
||||
_minSourceLoudnessInFrame = 1.0f;
|
||||
_maxSourceLoudnessInFrame = 0.0f;
|
||||
|
||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
||||
if (node->getLinkedData()) {
|
||||
((AudioMixerClientData*) node->getLinkedData())->checkBuffersBeforeFrameSend(JITTER_BUFFER_SAMPLES);
|
||||
((AudioMixerClientData*) node->getLinkedData())->checkBuffersBeforeFrameSend(JITTER_BUFFER_SAMPLES,
|
||||
_minSourceLoudnessInFrame,
|
||||
_maxSourceLoudnessInFrame);
|
||||
}
|
||||
}
|
||||
|
||||
const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10;
|
||||
const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.30;
|
||||
const float CUTOFF_EPSILON = 0.0001;
|
||||
|
||||
const int TRAILING_AVERAGE_FRAMES = 100;
|
||||
const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
|
||||
const float PREVIOUS_FRAMES_RATIO = 1 - CURRENT_FRAME_RATIO;
|
||||
|
||||
if (usecToSleep < 0) {
|
||||
usecToSleep = 0;
|
||||
}
|
||||
|
||||
_trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio)
|
||||
+ (usecToSleep * CURRENT_FRAME_RATIO / (float) BUFFER_SEND_INTERVAL_USECS);
|
||||
|
||||
float lastCutoffRatio = _loudnessCutoffRatio;
|
||||
bool hasRatioChanged = false;
|
||||
|
||||
if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) {
|
||||
// we're struggling - change our min required loudness to reduce some load
|
||||
_loudnessCutoffRatio += (1 - _loudnessCutoffRatio) / 2;
|
||||
|
||||
qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
|
||||
<< lastCutoffRatio << "and is now" << _loudnessCutoffRatio;
|
||||
hasRatioChanged = true;
|
||||
} else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _loudnessCutoffRatio != 0) {
|
||||
// we've recovered and can back off the required loudness
|
||||
_loudnessCutoffRatio -= _loudnessCutoffRatio / 2;
|
||||
|
||||
if (_loudnessCutoffRatio < CUTOFF_EPSILON) {
|
||||
_loudnessCutoffRatio = 0;
|
||||
}
|
||||
|
||||
qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
|
||||
<< lastCutoffRatio << "and is now" << _loudnessCutoffRatio;
|
||||
hasRatioChanged = true;
|
||||
}
|
||||
|
||||
if (hasRatioChanged) {
|
||||
// set out min required loudness from the new ratio
|
||||
_minRequiredLoudness = _loudnessCutoffRatio * (_maxSourceLoudnessInFrame - _minSourceLoudnessInFrame);
|
||||
qDebug() << "Minimum loudness required to be mixed is now" << _minRequiredLoudness;
|
||||
}
|
||||
|
||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
||||
if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData()
|
||||
|
@ -384,7 +440,7 @@ void AudioMixer::run() {
|
|||
break;
|
||||
}
|
||||
|
||||
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||
usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
|
|
|
@ -37,7 +37,14 @@ private:
|
|||
void prepareMixForListeningNode(Node* node);
|
||||
|
||||
// client samples capacity is larger than what will be sent to optimize mixing
|
||||
int16_t _clientSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + SAMPLE_PHASE_DELAY_AT_90];
|
||||
// we are MMX adding 4 samples at a time so we need client samples to have an extra 4
|
||||
int16_t _clientSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)];
|
||||
|
||||
float _trailingSleepRatio;
|
||||
float _minSourceLoudnessInFrame;
|
||||
float _maxSourceLoudnessInFrame;
|
||||
float _loudnessCutoffRatio;
|
||||
float _minRequiredLoudness;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AudioMixer__) */
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <PacketHeaders.h>
|
||||
#include <UUID.h>
|
||||
|
||||
|
@ -14,8 +16,7 @@
|
|||
#include "AudioMixerClientData.h"
|
||||
|
||||
AudioMixerClientData::AudioMixerClientData() :
|
||||
_ringBuffers(),
|
||||
_nextOutputLoudness(0)
|
||||
_ringBuffers()
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -82,16 +83,29 @@ int AudioMixerClientData::parseData(const QByteArray& packet) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSamples) {
|
||||
void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSamples,
|
||||
float& currentMinLoudness,
|
||||
float& currentMaxLoudness) {
|
||||
for (unsigned int i = 0; i < _ringBuffers.size(); i++) {
|
||||
if (_ringBuffers[i]->shouldBeAddedToMix(jitterBufferLengthSamples)) {
|
||||
// this is a ring buffer that is ready to go
|
||||
// set its flag so we know to push its buffer when all is said and done
|
||||
_ringBuffers[i]->setWillBeAddedToMix(true);
|
||||
|
||||
|
||||
// calculate the average loudness for the next NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL
|
||||
// that would be mixed in
|
||||
_nextOutputLoudness = _ringBuffers[i]->averageLoudnessForBoundarySamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
|
||||
_ringBuffers[i]->updateAverageLoudnessForBoundarySamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
|
||||
|
||||
float ringBufferLoudness = _ringBuffers[i]->getAverageLoudness();
|
||||
|
||||
if (ringBufferLoudness != 0 && ringBufferLoudness < currentMinLoudness) {
|
||||
currentMinLoudness = ringBufferLoudness;
|
||||
}
|
||||
|
||||
if (ringBufferLoudness > currentMaxLoudness) {
|
||||
currentMaxLoudness = ringBufferLoudness;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,14 +24,11 @@ public:
|
|||
const std::vector<PositionalAudioRingBuffer*> getRingBuffers() const { return _ringBuffers; }
|
||||
AvatarAudioRingBuffer* getAvatarAudioRingBuffer() const;
|
||||
|
||||
float getNextOutputLoudness() const { return _nextOutputLoudness; }
|
||||
|
||||
int parseData(const QByteArray& packet);
|
||||
void checkBuffersBeforeFrameSend(int jitterBufferLengthSamples);
|
||||
void checkBuffersBeforeFrameSend(int jitterBufferLengthSamples, float& currentMinLoudness, float& currentMaxLoudness);
|
||||
void pushBuffersAfterFrameSend();
|
||||
private:
|
||||
std::vector<PositionalAudioRingBuffer*> _ringBuffers;
|
||||
float _nextOutputLoudness;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AudioMixerClientData__) */
|
||||
|
|
|
@ -36,18 +36,34 @@ OctreeQueryNode::OctreeQueryNode() :
|
|||
_lodChanged(false),
|
||||
_lodInitialized(false),
|
||||
_sequenceNumber(0),
|
||||
_lastRootTimestamp(0)
|
||||
_lastRootTimestamp(0),
|
||||
_myPacketType(PacketTypeUnknown),
|
||||
_isShuttingDown(false)
|
||||
{
|
||||
}
|
||||
|
||||
OctreeQueryNode::~OctreeQueryNode() {
|
||||
_isShuttingDown = true;
|
||||
const bool extraDebugging = false;
|
||||
if (extraDebugging) {
|
||||
qDebug() << "OctreeQueryNode::~OctreeQueryNode()";
|
||||
}
|
||||
if (_octreeSendThread) {
|
||||
if (extraDebugging) {
|
||||
qDebug() << "OctreeQueryNode::~OctreeQueryNode()... calling _octreeSendThread->terminate()";
|
||||
}
|
||||
_octreeSendThread->terminate();
|
||||
if (extraDebugging) {
|
||||
qDebug() << "OctreeQueryNode::~OctreeQueryNode()... calling delete _octreeSendThread";
|
||||
}
|
||||
delete _octreeSendThread;
|
||||
}
|
||||
|
||||
delete[] _octreePacket;
|
||||
delete[] _lastOctreePacket;
|
||||
if (extraDebugging) {
|
||||
qDebug() << "OctreeQueryNode::~OctreeQueryNode()... DONE...";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -59,9 +75,13 @@ void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, con
|
|||
}
|
||||
|
||||
bool OctreeQueryNode::packetIsDuplicate() const {
|
||||
// if shutting down, return immediately
|
||||
if (_isShuttingDown) {
|
||||
return false;
|
||||
}
|
||||
// since our packets now include header information, like sequence number, and createTime, we can't just do a memcmp
|
||||
// of the entire packet, we need to compare only the packet content...
|
||||
int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(getMyPacketType());
|
||||
int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(_myPacketType);
|
||||
|
||||
if (_lastOctreePacketLength == getPacketLength()) {
|
||||
if (memcmp(_lastOctreePacket + (numBytesPacketHeader + OCTREE_PACKET_EXTRA_HEADERS_SIZE),
|
||||
|
@ -74,6 +94,11 @@ bool OctreeQueryNode::packetIsDuplicate() const {
|
|||
}
|
||||
|
||||
bool OctreeQueryNode::shouldSuppressDuplicatePacket() {
|
||||
// if shutting down, return immediately
|
||||
if (_isShuttingDown) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shouldSuppress = false; // assume we won't suppress
|
||||
|
||||
// only consider duplicate packets
|
||||
|
@ -107,7 +132,17 @@ bool OctreeQueryNode::shouldSuppressDuplicatePacket() {
|
|||
return shouldSuppress;
|
||||
}
|
||||
|
||||
void OctreeQueryNode::init() {
|
||||
_myPacketType = getMyPacketType();
|
||||
resetOctreePacket(true); // don't bump sequence
|
||||
}
|
||||
|
||||
void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) {
|
||||
// if shutting down, return immediately
|
||||
if (_isShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Whenever we call this, we will keep a copy of the last packet, so we can determine if the last packet has
|
||||
// changed since we last reset it. Since we know that no two packets can ever be identical without being the same
|
||||
// scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing
|
||||
|
@ -128,7 +163,7 @@ void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) {
|
|||
}
|
||||
|
||||
_octreePacketAvailableBytes = MAX_PACKET_SIZE;
|
||||
int numBytesPacketHeader = populatePacketHeader(reinterpret_cast<char*>(_octreePacket), getMyPacketType());
|
||||
int numBytesPacketHeader = populatePacketHeader(reinterpret_cast<char*>(_octreePacket), _myPacketType);
|
||||
_octreePacketAt = _octreePacket + numBytesPacketHeader;
|
||||
_octreePacketAvailableBytes -= numBytesPacketHeader;
|
||||
|
||||
|
@ -158,6 +193,11 @@ void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) {
|
|||
}
|
||||
|
||||
void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int bytes) {
|
||||
// if shutting down, return immediately
|
||||
if (_isShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
// compressed packets include lead bytes which contain compressed size, this allows packing of
|
||||
// multiple compressed portions together
|
||||
if (_currentPacketIsCompressed) {
|
||||
|
@ -174,6 +214,11 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int by
|
|||
}
|
||||
|
||||
bool OctreeQueryNode::updateCurrentViewFrustum() {
|
||||
// if shutting down, return immediately
|
||||
if (_isShuttingDown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool currentViewFrustumChanged = false;
|
||||
ViewFrustum newestViewFrustum;
|
||||
// get position and orientation details from the camera
|
||||
|
@ -233,6 +278,11 @@ void OctreeQueryNode::setViewSent(bool viewSent) {
|
|||
}
|
||||
|
||||
void OctreeQueryNode::updateLastKnownViewFrustum() {
|
||||
// if shutting down, return immediately
|
||||
if (_isShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum);
|
||||
|
||||
if (frustumChanges) {
|
||||
|
@ -247,6 +297,11 @@ void OctreeQueryNode::updateLastKnownViewFrustum() {
|
|||
|
||||
|
||||
bool OctreeQueryNode::moveShouldDump() const {
|
||||
// if shutting down, return immediately
|
||||
if (_isShuttingDown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
glm::vec3 oldPosition = _lastKnownViewFrustum.getPosition();
|
||||
glm::vec3 newPosition = _currentViewFrustum.getPosition();
|
||||
|
||||
|
@ -259,6 +314,11 @@ bool OctreeQueryNode::moveShouldDump() const {
|
|||
}
|
||||
|
||||
void OctreeQueryNode::dumpOutOfView() {
|
||||
// if shutting down, return immediately
|
||||
if (_isShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
int stillInView = 0;
|
||||
int outOfView = 0;
|
||||
OctreeElementBag tempBag;
|
||||
|
|
|
@ -28,6 +28,7 @@ public:
|
|||
OctreeQueryNode();
|
||||
virtual ~OctreeQueryNode();
|
||||
|
||||
void init(); // called after creation to set up some virtual items
|
||||
virtual PacketType getMyPacketType() const = 0;
|
||||
|
||||
void resetOctreePacket(bool lastWasSurpressed = false); // resets octree packet to after "V" header
|
||||
|
@ -91,6 +92,7 @@ public:
|
|||
unsigned int getlastOctreePacketLength() const { return _lastOctreePacketLength; }
|
||||
int getDuplicatePacketCount() const { return _duplicatePacketCount; }
|
||||
|
||||
bool isShuttingDown() const { return _isShuttingDown; }
|
||||
|
||||
private:
|
||||
OctreeQueryNode(const OctreeQueryNode &);
|
||||
|
@ -127,6 +129,9 @@ private:
|
|||
|
||||
OCTREE_PACKET_SEQUENCE _sequenceNumber;
|
||||
quint64 _lastRootTimestamp;
|
||||
|
||||
PacketType _myPacketType;
|
||||
bool _isShuttingDown;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__OctreeQueryNode__) */
|
||||
|
|
|
@ -65,7 +65,7 @@ bool OctreeSendThread::process() {
|
|||
nodeData = (OctreeQueryNode*) node->getLinkedData();
|
||||
|
||||
// Sometimes the node data has not yet been linked, in which case we can't really do anything
|
||||
if (nodeData) {
|
||||
if (nodeData && !nodeData->isShuttingDown()) {
|
||||
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
|
||||
packetDistributor(node, nodeData, viewFrustumChanged);
|
||||
}
|
||||
|
@ -99,7 +99,14 @@ quint64 OctreeSendThread::_totalBytes = 0;
|
|||
quint64 OctreeSendThread::_totalWastedBytes = 0;
|
||||
quint64 OctreeSendThread::_totalPackets = 0;
|
||||
|
||||
int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) {
|
||||
int OctreeSendThread::handlePacketSend(const SharedNodePointer& node,
|
||||
OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) {
|
||||
|
||||
// if we're shutting down, then exit early
|
||||
if (nodeData->isShuttingDown()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool debug = _myServer->wantsDebugSending();
|
||||
quint64 now = usecTimestampNow();
|
||||
|
||||
|
@ -136,7 +143,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQuer
|
|||
|
||||
|
||||
// If we've got a stats message ready to send, then see if we can piggyback them together
|
||||
if (nodeData->stats.isReadyToSend()) {
|
||||
if (nodeData->stats.isReadyToSend() && !nodeData->isShuttingDown()) {
|
||||
// Send the stats message to the client
|
||||
unsigned char* statsMessage = nodeData->stats.getStatsMessage();
|
||||
int statsMessageLength = nodeData->stats.getStatsMessageLength();
|
||||
|
@ -203,7 +210,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQuer
|
|||
nodeData->stats.markAsSent();
|
||||
} else {
|
||||
// If there's actually a packet waiting, then send it.
|
||||
if (nodeData->isPacketWaiting()) {
|
||||
if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) {
|
||||
// just send the voxel packet
|
||||
NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(),
|
||||
SharedNodePointer(node));
|
||||
|
@ -234,6 +241,12 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQuer
|
|||
|
||||
/// Version of voxel distributor that sends the deepest LOD level at once
|
||||
int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged) {
|
||||
|
||||
// if shutting down, exit early
|
||||
if (nodeData->isShuttingDown()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int truePacketsSent = 0;
|
||||
int trueBytesSent = 0;
|
||||
int packetsSentThisInterval = 0;
|
||||
|
@ -336,7 +349,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue
|
|||
|
||||
int extraPackingAttempts = 0;
|
||||
bool completedScene = false;
|
||||
while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval) {
|
||||
while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval && !nodeData->isShuttingDown()) {
|
||||
float lockWaitElapsedUsec = OctreeServer::SKIP_TIME;
|
||||
float encodeElapsedUsec = OctreeServer::SKIP_TIME;
|
||||
float compressAndWriteElapsedUsec = OctreeServer::SKIP_TIME;
|
||||
|
@ -503,7 +516,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue
|
|||
// Here's where we can/should allow the server to send other data...
|
||||
// send the environment packet
|
||||
// TODO: should we turn this into a while loop to better handle sending multiple special packets
|
||||
if (_myServer->hasSpecialPacketToSend(node)) {
|
||||
if (_myServer->hasSpecialPacketToSend(node) && !nodeData->isShuttingDown()) {
|
||||
trueBytesSent += _myServer->sendSpecialPacket(node);
|
||||
truePacketsSent++;
|
||||
packetsSentThisInterval++;
|
||||
|
|
|
@ -168,7 +168,7 @@ void OctreeServer::trackPacketSendingTime(float time) {
|
|||
void OctreeServer::attachQueryNodeToNode(Node* newNode) {
|
||||
if (!newNode->getLinkedData()) {
|
||||
OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode();
|
||||
newQueryNodeData->resetOctreePacket(true); // don't bump sequence
|
||||
newQueryNodeData->init();
|
||||
newNode->setLinkedData(newQueryNodeData);
|
||||
}
|
||||
}
|
||||
|
@ -784,16 +784,26 @@ void OctreeServer::readPendingDatagrams() {
|
|||
if (packetType == getMyQueryMessageType()) {
|
||||
bool debug = false;
|
||||
if (debug) {
|
||||
qDebug() << "Got PacketTypeVoxelQuery at" << usecTimestampNow();
|
||||
if (matchingNode) {
|
||||
qDebug() << "Got PacketTypeVoxelQuery at" << usecTimestampNow() << "node:" << *matchingNode;
|
||||
} else {
|
||||
qDebug() << "Got PacketTypeVoxelQuery at" << usecTimestampNow() << "node: ??????";
|
||||
}
|
||||
}
|
||||
|
||||
// If we got a PacketType_VOXEL_QUERY, then we're talking to an NodeType_t_AVATAR, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
if (matchingNode) {
|
||||
if (debug) {
|
||||
qDebug() << "calling updateNodeWithDataFromPacket()... node:" << *matchingNode;
|
||||
}
|
||||
nodeList->updateNodeWithDataFromPacket(matchingNode, receivedPacket);
|
||||
|
||||
OctreeQueryNode* nodeData = (OctreeQueryNode*) matchingNode->getLinkedData();
|
||||
if (nodeData && !nodeData->isOctreeSendThreadInitalized()) {
|
||||
if (debug) {
|
||||
qDebug() << "calling initializeOctreeSendThread()... node:" << *matchingNode;
|
||||
}
|
||||
nodeData->initializeOctreeSendThread(this, matchingNode->getUUID());
|
||||
}
|
||||
}
|
||||
|
@ -999,6 +1009,8 @@ void OctreeServer::nodeKilled(SharedNodePointer node) {
|
|||
node->setLinkedData(NULL); // set this first in case another thread comes through and tryes to acces this
|
||||
qDebug() << qPrintable(_safeServerName) << "server deleting Linked Data for node:" << *node;
|
||||
nodeData->deleteLater();
|
||||
} else {
|
||||
qDebug() << qPrintable(_safeServerName) << "server node missing linked data node:" << *node;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,6 @@ var windowDimensions = Controller.getViewportDimensions();
|
|||
|
||||
var NEW_VOXEL_SIZE = 1.0;
|
||||
var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0;
|
||||
var ORBIT_RATE_ALTITUDE = 200.0;
|
||||
var ORBIT_RATE_AZIMUTH = 90.0;
|
||||
var PIXELS_PER_EXTRUDE_VOXEL = 16;
|
||||
var WHEEL_PIXELS_PER_SCALE_CHANGE = 100;
|
||||
var MAX_VOXEL_SCALE = 1.0;
|
||||
|
@ -37,19 +35,7 @@ var previewLineWidth = 1.5;
|
|||
var oldMode = Camera.getMode();
|
||||
|
||||
var isAdding = false;
|
||||
var isExtruding = false;
|
||||
var isOrbiting = false;
|
||||
var isOrbitingFromTouch = false;
|
||||
var isPanning = false;
|
||||
var isPanningFromTouch = false;
|
||||
var touchPointsToOrbit = 2; // you can change these, but be mindful that on some track pads 2 touch points = right click+drag
|
||||
var touchPointsToPan = 3;
|
||||
var orbitAzimuth = 0.0;
|
||||
var orbitAltitude = 0.0;
|
||||
var orbitCenter = { x: 0, y: 0, z: 0 };
|
||||
var orbitPosition = { x: 0, y: 0, z: 0 };
|
||||
var torsoToEyeVector = { x: 0, y: 0, z: 0 };
|
||||
var orbitRadius = 0.0;
|
||||
var isExtruding = false;
|
||||
var extrudeDirection = { x: 0, y: 0, z: 0 };
|
||||
var extrudeScale = 0.0;
|
||||
var lastVoxelPosition = { x: 0, y: 0, z: 0 };
|
||||
|
@ -444,24 +430,11 @@ function getNewVoxelPosition() {
|
|||
return newPosition;
|
||||
}
|
||||
|
||||
function fixEulerAngles(eulers) {
|
||||
var rVal = { x: 0, y: 0, z: eulers.z };
|
||||
if (eulers.x >= 90.0) {
|
||||
rVal.x = 180.0 - eulers.x;
|
||||
rVal.y = eulers.y - 180.0;
|
||||
} else if (eulers.x <= -90.0) {
|
||||
rVal.x = 180.0 - eulers.x;
|
||||
rVal.y = eulers.y - 180.0;
|
||||
}
|
||||
return rVal;
|
||||
}
|
||||
|
||||
var trackLastMouseX = 0;
|
||||
var trackLastMouseY = 0;
|
||||
var trackAsDelete = false;
|
||||
var trackAsRecolor = false;
|
||||
var trackAsEyedropper = false;
|
||||
var trackAsOrbitOrPan = false;
|
||||
|
||||
var voxelToolSelected = true;
|
||||
var recolorToolSelected = false;
|
||||
|
@ -761,10 +734,6 @@ function trackKeyPressEvent(event) {
|
|||
trackAsEyedropper = true;
|
||||
moveTools();
|
||||
}
|
||||
if (event.text == "ALT") {
|
||||
trackAsOrbitOrPan = true;
|
||||
moveTools();
|
||||
}
|
||||
showPreviewGuides();
|
||||
}
|
||||
|
||||
|
@ -802,10 +771,6 @@ function trackKeyReleaseEvent(event) {
|
|||
trackAsEyedropper = false;
|
||||
moveTools();
|
||||
}
|
||||
if (event.text == "ALT") {
|
||||
trackAsOrbitOrPan = false;
|
||||
moveTools();
|
||||
}
|
||||
|
||||
// on F1 toggle the preview mode between cubes and lines
|
||||
if (event.text == "F1") {
|
||||
|
@ -816,74 +781,6 @@ function trackKeyReleaseEvent(event) {
|
|||
}
|
||||
}
|
||||
|
||||
function startOrbitMode(event) {
|
||||
mouseX = event.x;
|
||||
mouseY = event.y;
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var intersection = Voxels.findRayIntersection(pickRay);
|
||||
|
||||
// start orbit camera!
|
||||
var cameraPosition = Camera.getPosition();
|
||||
torsoToEyeVector = Vec3.subtract(cameraPosition, MyAvatar.position);
|
||||
torsoToEyeVector.x = 0.0;
|
||||
torsoToEyeVector.z = 0.0;
|
||||
oldMode = Camera.getMode();
|
||||
Camera.setMode("independent");
|
||||
Camera.keepLookingAt(intersection.intersection);
|
||||
// get position for initial azimuth, elevation
|
||||
orbitCenter = intersection.intersection;
|
||||
var orbitVector = Vec3.subtract(cameraPosition, orbitCenter);
|
||||
orbitRadius = Vec3.length(orbitVector);
|
||||
orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x);
|
||||
orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector));
|
||||
|
||||
//print("startOrbitMode...");
|
||||
}
|
||||
|
||||
function handleOrbitingMove(event) {
|
||||
var cameraOrientation = Camera.getOrientation();
|
||||
var origEulers = Quat.safeEulerAngles(cameraOrientation);
|
||||
var newEulers = fixEulerAngles(Quat.safeEulerAngles(cameraOrientation));
|
||||
var dx = event.x - mouseX;
|
||||
var dy = event.y - mouseY;
|
||||
orbitAzimuth += dx / ORBIT_RATE_AZIMUTH;
|
||||
orbitAltitude += dy / ORBIT_RATE_ALTITUDE;
|
||||
var orbitVector = { x:(Math.cos(orbitAltitude) * Math.cos(orbitAzimuth)) * orbitRadius,
|
||||
y:Math.sin(orbitAltitude) * orbitRadius,
|
||||
z:(Math.cos(orbitAltitude) * Math.sin(orbitAzimuth)) * orbitRadius };
|
||||
orbitPosition = Vec3.sum(orbitCenter, orbitVector);
|
||||
Camera.setPosition(orbitPosition);
|
||||
|
||||
mouseX = event.x;
|
||||
mouseY = event.y;
|
||||
//print("handleOrbitingMove...");
|
||||
}
|
||||
|
||||
function endOrbitMode(event) {
|
||||
var cameraOrientation = Camera.getOrientation();
|
||||
MyAvatar.position = Vec3.subtract(Camera.getPosition(), torsoToEyeVector);
|
||||
MyAvatar.headOrientation = cameraOrientation;
|
||||
Camera.stopLooking();
|
||||
Camera.setMode(oldMode);
|
||||
Camera.setOrientation(cameraOrientation);
|
||||
//print("endOrbitMode...");
|
||||
}
|
||||
|
||||
function startPanMode(event, intersection) {
|
||||
// start pan camera!
|
||||
print("handle PAN mode!!!");
|
||||
}
|
||||
|
||||
function handlePanMove(event) {
|
||||
print("PANNING mode!!! ");
|
||||
//print("isPanning="+isPanning + " inPanningFromTouch="+isPanningFromTouch + " trackAsOrbitOrPan="+trackAsOrbitOrPan);
|
||||
}
|
||||
|
||||
function endPanMode(event) {
|
||||
print("ending PAN mode!!!");
|
||||
}
|
||||
|
||||
|
||||
function mousePressEvent(event) {
|
||||
|
||||
// if our tools are off, then don't do anything
|
||||
|
@ -891,31 +788,6 @@ function mousePressEvent(event) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Normally, if we're panning or orbiting from touch, ignore these... because our touch takes precedence.
|
||||
// but In the case of a button="RIGHT" click, we may get some touch messages first, and we actually want to
|
||||
// cancel any touch mode, and then let the right-click through
|
||||
if (isOrbitingFromTouch || isPanningFromTouch) {
|
||||
|
||||
// if the user is holding the ALT key AND they are clicking the RIGHT button (or on multi-touch doing a two
|
||||
// finger touch, then we want to let the new panning behavior take over.
|
||||
// if it's any other case we still want to bail
|
||||
if (event.button == "RIGHT" && trackAsOrbitOrPan) {
|
||||
// cancel our current multitouch operation...
|
||||
if (isOrbitingFromTouch) {
|
||||
endOrbitMode(event);
|
||||
isOrbitingFromTouch = false;
|
||||
}
|
||||
if (isPanningFromTouch) {
|
||||
//print("mousePressEvent... calling endPanMode()");
|
||||
endPanMode(event);
|
||||
isPanningFromTouch = false;
|
||||
}
|
||||
// let things fall through
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// no clicking on overlays while in panning mode
|
||||
if (!trackAsOrbitOrPan) {
|
||||
var clickedOnSomething = false;
|
||||
|
@ -1008,17 +880,7 @@ function mousePressEvent(event) {
|
|||
calcThumbFromScale(intersection.voxel.s);
|
||||
}
|
||||
|
||||
// Note: touch and mouse events can cross paths, so we want to ignore any mouse events that would
|
||||
// start a pan or orbit if we're already doing a pan or orbit via touch...
|
||||
if ((event.isAlt || trackAsOrbitOrPan) && !(isOrbitingFromTouch || isPanningFromTouch)) {
|
||||
if (event.isLeftButton && !event.isRightButton) {
|
||||
startOrbitMode(event);
|
||||
isOrbiting = true;
|
||||
} else if (event.isRightButton && !event.isLeftButton) {
|
||||
startPanMode(event);
|
||||
isPanning = true;
|
||||
}
|
||||
} else if (trackAsDelete || event.isRightButton && !trackAsEyedropper) {
|
||||
if (trackAsDelete || event.isRightButton && !trackAsEyedropper) {
|
||||
// Delete voxel
|
||||
voxelDetails = calculateVoxelFromIntersection(intersection,"delete");
|
||||
Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s);
|
||||
|
@ -1209,23 +1071,7 @@ function mouseMoveEvent(event) {
|
|||
return;
|
||||
}
|
||||
|
||||
// if we're panning or orbiting from touch, ignore these... because our touch takes precedence.
|
||||
if (isOrbitingFromTouch || isPanningFromTouch) {
|
||||
return;
|
||||
}
|
||||
|
||||
// double check that we didn't accidentally miss a pan or orbit click request
|
||||
if (trackAsOrbitOrPan && !isPanning && !isOrbiting) {
|
||||
if (event.isLeftButton && !event.isRightButton) {
|
||||
startOrbitMode(event);
|
||||
isOrbiting = true;
|
||||
}
|
||||
if (!event.isLeftButton && event.isRightButton) {
|
||||
startPanMode(event);
|
||||
isPanning = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!trackAsOrbitOrPan && isMovingSlider) {
|
||||
thumbX = (event.x - thumbClickOffsetX) - sliderX;
|
||||
if (thumbX < minThumbX) {
|
||||
|
@ -1236,10 +1082,6 @@ function mouseMoveEvent(event) {
|
|||
}
|
||||
calcScaleFromThumb(thumbX);
|
||||
|
||||
} else if (isOrbiting) {
|
||||
handleOrbitingMove(event);
|
||||
} else if (isPanning) {
|
||||
handlePanMove(event);
|
||||
} else if (!trackAsOrbitOrPan && isAdding) {
|
||||
// Watch the drag direction to tell which way to 'extrude' this voxel
|
||||
if (!isExtruding) {
|
||||
|
@ -1293,16 +1135,6 @@ function mouseReleaseEvent(event) {
|
|||
if (isMovingSlider) {
|
||||
isMovingSlider = false;
|
||||
}
|
||||
|
||||
if (isOrbiting) {
|
||||
endOrbitMode(event);
|
||||
isOrbiting = false;
|
||||
}
|
||||
if (isPanning) {
|
||||
print("mouseReleaseEvent... calling endPanMode()");
|
||||
endPanMode(event);
|
||||
isPanning = false;
|
||||
}
|
||||
isAdding = false;
|
||||
isExtruding = false;
|
||||
}
|
||||
|
@ -1396,96 +1228,18 @@ function touchBeginEvent(event) {
|
|||
if (!editToolsOn) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're already in the middle of orbiting or panning, then ignore these multi-touch events...
|
||||
if (isOrbiting || isPanning) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.isAlt || trackAsOrbitOrPan) {
|
||||
if (event.touchPoints == touchPointsToOrbit) {
|
||||
// we need to double check that we didn't start an orbit, because the touch events will sometimes
|
||||
// come in as 2 then 3 touches...
|
||||
if (isPanningFromTouch) {
|
||||
print("touchBeginEvent... calling endPanMode()");
|
||||
endPanMode(event);
|
||||
isPanningFromTouch = false;
|
||||
}
|
||||
startOrbitMode(event);
|
||||
isOrbitingFromTouch = true;
|
||||
} else if (event.touchPoints == touchPointsToPan) {
|
||||
// we need to double check that we didn't start an orbit, because the touch events will sometimes
|
||||
// come in as 2 then 3 touches...
|
||||
if (isOrbitingFromTouch) {
|
||||
endOrbitMode(event);
|
||||
isOrbitingFromTouch = false;
|
||||
}
|
||||
startPanMode(event);
|
||||
isPanningFromTouch = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function touchUpdateEvent(event) {
|
||||
if (!editToolsOn) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're already in the middle of orbiting or panning, then ignore these multi-touch events...
|
||||
if (isOrbiting || isPanning) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isOrbitingFromTouch) {
|
||||
// we need to double check that we didn't start an orbit, because the touch events will sometimes
|
||||
// come in as 2 then 3 touches...
|
||||
if (event.touchPoints == touchPointsToPan) {
|
||||
//print("we now have touchPointsToPan touches... switch to pan...");
|
||||
endOrbitMode(event);
|
||||
isOrbitingFromTouch = false;
|
||||
startPanMode(event);
|
||||
isPanningFromTouch = true;
|
||||
} else {
|
||||
handleOrbitingMove(event);
|
||||
}
|
||||
}
|
||||
if (isPanningFromTouch) {
|
||||
//print("touchUpdateEvent... isPanningFromTouch... event.touchPoints=" + event.touchPoints);
|
||||
// we need to double check that we didn't start an orbit, because the touch events will sometimes
|
||||
// come in as 2 then 3 touches...
|
||||
if (event.touchPoints == touchPointsToOrbit) {
|
||||
//print("we now have touchPointsToOrbit touches... switch to orbit...");
|
||||
//print("touchUpdateEvent... calling endPanMode()");
|
||||
endPanMode(event);
|
||||
isPanningFromTouch = false;
|
||||
startOrbitMode(event);
|
||||
isOrbitingFromTouch = true;
|
||||
handleOrbitingMove(event);
|
||||
} else {
|
||||
handlePanMove(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function touchEndEvent(event) {
|
||||
if (!editToolsOn) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're already in the middle of orbiting or panning, then ignore these multi-touch events...
|
||||
if (isOrbiting || isPanning) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isOrbitingFromTouch) {
|
||||
endOrbitMode(event);
|
||||
isOrbitingFromTouch = false;
|
||||
}
|
||||
if (isPanningFromTouch) {
|
||||
print("touchEndEvent... calling endPanMode()");
|
||||
endPanMode(event);
|
||||
isPanningFromTouch = false;
|
||||
}
|
||||
}
|
||||
|
||||
var lastFingerAddVoxel = { x: -1, y: -1, z: -1}; // off of the build-able area
|
||||
|
|
240
examples/inspect.js
Normal file
240
examples/inspect.js
Normal file
|
@ -0,0 +1,240 @@
|
|||
//
|
||||
// inspect.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Clément Brisset on March 20, 2014
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Allows you to inspect non moving objects (Voxels or Avatars) using Atl, Control (Command on Mac) and Shift
|
||||
//
|
||||
// radial mode = hold ALT
|
||||
// orbit mode = hold ALT + CONTROL
|
||||
// pan mode = hold ALT + CONTROL + SHIFT
|
||||
// Once you are in a mode left click on the object to inspect and hold the click
|
||||
// Dragging the mouse will move your camera according to the mode you are in.
|
||||
//
|
||||
|
||||
var AZIMUTH_RATE = 90.0;
|
||||
var ALTITUDE_RATE = 200.0;
|
||||
var RADIUS_RATE = 20.0;
|
||||
var PAN_RATE = 50.0;
|
||||
|
||||
var alt = false;
|
||||
var shift = false;
|
||||
var control = false;
|
||||
|
||||
var isActive = false;
|
||||
|
||||
var noMode = 0;
|
||||
var orbitMode = 1;
|
||||
var radialMode = 2;
|
||||
var panningMode = 3;
|
||||
|
||||
var mode = noMode;
|
||||
|
||||
var mouseLastX = 0;
|
||||
var mouseLastY = 0;
|
||||
|
||||
|
||||
var center = { x: 0, y: 0, z: 0 };
|
||||
var position = { x: 0, y: 0, z: 0 };
|
||||
var vector = { x: 0, y: 0, z: 0 };
|
||||
var radius = 0.0;
|
||||
var azimuth = 0.0;
|
||||
var altitude = 0.0;
|
||||
|
||||
|
||||
function handleRadialMode(dx, dy) {
|
||||
azimuth += dx / AZIMUTH_RATE;
|
||||
radius += radius * dy / RADIUS_RATE;
|
||||
if (radius < 1) {
|
||||
radius = 1;
|
||||
}
|
||||
|
||||
vector = { x: (Math.cos(altitude) * Math.cos(azimuth)) * radius,
|
||||
y: Math.sin(altitude) * radius,
|
||||
z: (Math.cos(altitude) * Math.sin(azimuth)) * radius };
|
||||
position = Vec3.sum(center, vector);
|
||||
Camera.setPosition(position);
|
||||
}
|
||||
|
||||
function handleOrbitMode(dx, dy) {
|
||||
azimuth += dx / AZIMUTH_RATE;
|
||||
altitude += dy / ALTITUDE_RATE;
|
||||
|
||||
vector = { x:(Math.cos(altitude) * Math.cos(azimuth)) * radius,
|
||||
y:Math.sin(altitude) * radius,
|
||||
z:(Math.cos(altitude) * Math.sin(azimuth)) * radius };
|
||||
position = Vec3.sum(center, vector);
|
||||
Camera.setPosition(position);
|
||||
}
|
||||
|
||||
|
||||
function handlePanMode(dx, dy) {
|
||||
var up = Quat.getUp(Camera.getOrientation());
|
||||
var right = Quat.getRight(Camera.getOrientation());
|
||||
var distance = Vec3.length(vector);
|
||||
|
||||
var dv = Vec3.sum(Vec3.multiply(up, - distance * dy / PAN_RATE), Vec3.multiply(right, distance * dx / PAN_RATE));
|
||||
|
||||
center = Vec3.sum(center, dv);
|
||||
position = Vec3.sum(position, dv);
|
||||
|
||||
Camera.setPosition(position);
|
||||
Camera.keepLookingAt(center);
|
||||
}
|
||||
|
||||
function saveCameraState() {
|
||||
oldMode = Camera.getMode();
|
||||
var oldPosition = Camera.getPosition();
|
||||
Camera.setMode("independent");
|
||||
Camera.setPosition(oldPosition);
|
||||
}
|
||||
|
||||
function restoreCameraState() {
|
||||
Camera.stopLooking();
|
||||
Camera.setMode(oldMode);
|
||||
}
|
||||
|
||||
function handleModes() {
|
||||
var newMode = noMode;
|
||||
if (alt) {
|
||||
if (control) {
|
||||
if (shift) {
|
||||
newMode = panningMode;
|
||||
} else {
|
||||
newMode = orbitMode;
|
||||
}
|
||||
} else {
|
||||
newMode = radialMode;
|
||||
}
|
||||
}
|
||||
|
||||
// if leaving noMode
|
||||
if (mode == noMode && newMode != noMode) {
|
||||
saveCameraState();
|
||||
}
|
||||
// if entering noMode
|
||||
if (newMode == noMode && mode != noMode) {
|
||||
restoreCameraState();
|
||||
}
|
||||
|
||||
mode = newMode;
|
||||
}
|
||||
|
||||
function keyPressEvent(event) {
|
||||
var changed = false;
|
||||
|
||||
if (event.text == "ALT") {
|
||||
alt = true;
|
||||
changed = true;
|
||||
}
|
||||
if (event.text == "CONTROL") {
|
||||
control = true;
|
||||
changed = true;
|
||||
}
|
||||
if (event.text == "SHIFT") {
|
||||
shift = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
handleModes();
|
||||
}
|
||||
}
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
var changed = false;
|
||||
|
||||
if (event.text == "ALT") {
|
||||
alt = false;
|
||||
changed = true;
|
||||
}
|
||||
if (event.text == "CONTROL") {
|
||||
control = false;
|
||||
changed = true;
|
||||
}
|
||||
if (event.text == "SHIFT") {
|
||||
shift = false;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
handleModes();
|
||||
}
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
if (alt) {
|
||||
isActive = true;
|
||||
mouseLastX = event.x;
|
||||
mouseLastY = event.y;
|
||||
|
||||
// Compute trajectories related values
|
||||
var pickRay = Camera.computePickRay(mouseLastX, mouseLastY);
|
||||
var intersection = Voxels.findRayIntersection(pickRay);
|
||||
|
||||
position = Camera.getPosition();
|
||||
|
||||
avatarTarget = MyAvatar.getTargetAvatarPosition();
|
||||
voxelTarget = intersection.intersection;
|
||||
if (Vec3.length(Vec3.subtract(avatarTarget, position)) < Vec3.length(Vec3.subtract(voxelTarget, position))) {
|
||||
if (avatarTarget.x != 0 || avatarTarget.y != 0 || avatarTarget.z != 0) {
|
||||
center = avatarTarget;
|
||||
} else {
|
||||
center = voxelTarget;
|
||||
}
|
||||
} else {
|
||||
if (voxelTarget.x != 0 || voxelTarget.y != 0 || voxelTarget.z != 0) {
|
||||
center = voxelTarget;
|
||||
} else {
|
||||
center = avatarTarget;
|
||||
}
|
||||
}
|
||||
|
||||
vector = Vec3.subtract(position, center);
|
||||
radius = Vec3.length(vector);
|
||||
azimuth = Math.atan2(vector.z, vector.x);
|
||||
altitude = Math.asin(vector.y / Vec3.length(vector));
|
||||
|
||||
Camera.keepLookingAt(center);
|
||||
}
|
||||
}
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
if (isActive) {
|
||||
isActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
if (isActive && mode != noMode) {
|
||||
if (mode == radialMode) {
|
||||
handleRadialMode(event.x - mouseLastX, event.y - mouseLastY);
|
||||
}
|
||||
if (mode == orbitMode) {
|
||||
handleOrbitMode(event.x - mouseLastX, event.y - mouseLastY);
|
||||
}
|
||||
if (mode == panningMode) {
|
||||
handlePanMode(event.x - mouseLastX, event.y - mouseLastY);
|
||||
}
|
||||
|
||||
mouseLastX = event.x;
|
||||
mouseLastY = event.y;
|
||||
}
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
if (mode != noMode) {
|
||||
restoreCameraState();
|
||||
}
|
||||
}
|
||||
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
|
||||
Script.scriptEnding.connect(scriptEnding);
|
|
@ -4,22 +4,22 @@
|
|||
<context>
|
||||
<name>Application</name>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="1354"/>
|
||||
<location filename="src/Application.cpp" line="1364"/>
|
||||
<source>Export Voxels</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="1355"/>
|
||||
<location filename="src/Application.cpp" line="1365"/>
|
||||
<source>Sparse Voxel Octree Files (*.svo)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="3565"/>
|
||||
<location filename="src/Application.cpp" line="3577"/>
|
||||
<source>Open Script</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="3566"/>
|
||||
<location filename="src/Application.cpp" line="3578"/>
|
||||
<source>JavaScript Files (*.js)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -113,18 +113,18 @@
|
|||
<context>
|
||||
<name>Menu</name>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="439"/>
|
||||
<location filename="src/Menu.cpp" line="440"/>
|
||||
<source>Open .ini config file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="441"/>
|
||||
<location filename="src/Menu.cpp" line="453"/>
|
||||
<location filename="src/Menu.cpp" line="442"/>
|
||||
<location filename="src/Menu.cpp" line="454"/>
|
||||
<source>Text files (*.ini)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="451"/>
|
||||
<location filename="src/Menu.cpp" line="452"/>
|
||||
<source>Save .ini config file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -132,122 +132,116 @@
|
|||
<context>
|
||||
<name>PreferencesDialog</name>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="187"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="555"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="196"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="611"/>
|
||||
<source>Avatar</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="221"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="556"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="230"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="612"/>
|
||||
<source><html><head/><body><p>Avatar display name <span style=" color:#909090;">(optional)</span></p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="257"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="557"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="266"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="613"/>
|
||||
<source>Not showing a name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="285"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="558"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="294"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="614"/>
|
||||
<source>Head</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="337"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="559"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="395"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="616"/>
|
||||
<source>Body</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="399"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="560"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="506"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="618"/>
|
||||
<source>Advanced Tuning</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="426"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="561"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="534"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="619"/>
|
||||
<source>It's not recomended that you play with these settings unless you've looked into exactly what they do.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="462"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="562"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="570"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="620"/>
|
||||
<source><p>Avatar</p></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="509"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="563"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="702"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="622"/>
|
||||
<source>Lean scale (applies to Faceshift users)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="591"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="564"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="602"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="621"/>
|
||||
<source>Vertical field of view</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="676"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="565"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="784"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="623"/>
|
||||
<source>Avatar scale (default is 1.0)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="755"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="566"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="863"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="624"/>
|
||||
<source>Pupil dillation</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="828"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="567"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="939"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="625"/>
|
||||
<source>Audio Jitter Buffer Samples (0 for automatic)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="913"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="568"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="1027"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="626"/>
|
||||
<source>Faceshift eye detection</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="990"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="569"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="1104"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="627"/>
|
||||
<source><html><head/><body><p>Voxels</p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="1019"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="570"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="1136"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="628"/>
|
||||
<source>Maximum voxels</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="1095"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="571"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="1212"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="629"/>
|
||||
<source>Max voxels sent each second</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="1170"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="572"/>
|
||||
<source>PushButton</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="89"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="553"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="90"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="609"/>
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="105"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="554"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="125"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="610"/>
|
||||
<source>Save all changes</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>images/close.svg</file>
|
||||
</qresource>
|
||||
<qresource prefix="/">
|
||||
<file>images/close.svg</file>
|
||||
<file>styles/search.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -13,8 +13,9 @@ QLineEdit {
|
|||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: #ccc;
|
||||
padding: 7px;
|
||||
opacity: 1;
|
||||
padding: 8px;
|
||||
font-size: 16px;
|
||||
color: rgb(51, 51, 51);
|
||||
}
|
||||
|
||||
QLabel p {
|
||||
|
@ -27,14 +28,15 @@ QPushButton {
|
|||
border-radius: 9px;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
font-weight: 100;
|
||||
color: #ffffff;
|
||||
padding: 10px 15px;
|
||||
padding: 10px 0px;
|
||||
}
|
||||
|
||||
QSpinBox, QDoubleSpinBox {
|
||||
padding: 5px;
|
||||
border-width: 1;
|
||||
font-size: 16px;
|
||||
color: rgb(51, 51, 51);
|
||||
}
|
||||
|
||||
QDoubleSpinBox::up-arrow,
|
||||
|
@ -98,3 +100,14 @@ QSlider::handle:horizontal {
|
|||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
QPushButton#closeButton {
|
||||
border-color: #ccc;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-radius: 0;
|
||||
background-color: #fff;
|
||||
background-image: url(styles/close.svg);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
|
|
@ -692,6 +692,8 @@ bool Application::event(QEvent* event) {
|
|||
|
||||
void Application::keyPressEvent(QKeyEvent* event) {
|
||||
|
||||
_keysPressed.insert(event->key());
|
||||
|
||||
_controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
|
@ -914,6 +916,8 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
|
||||
void Application::keyReleaseEvent(QKeyEvent* event) {
|
||||
|
||||
_keysPressed.remove(event->key());
|
||||
|
||||
_controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
|
@ -921,60 +925,66 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
|
|||
return;
|
||||
}
|
||||
|
||||
switch (event->key()) {
|
||||
case Qt::Key_E:
|
||||
_myAvatar->setDriveKeys(UP, 0.f);
|
||||
break;
|
||||
|
||||
if (activeWindow() == _window) {
|
||||
switch (event->key()) {
|
||||
case Qt::Key_E:
|
||||
_myAvatar->setDriveKeys(UP, 0.f);
|
||||
break;
|
||||
case Qt::Key_C:
|
||||
_myAvatar->setDriveKeys(DOWN, 0.f);
|
||||
break;
|
||||
|
||||
case Qt::Key_C:
|
||||
_myAvatar->setDriveKeys(DOWN, 0.f);
|
||||
break;
|
||||
case Qt::Key_W:
|
||||
_myAvatar->setDriveKeys(FWD, 0.f);
|
||||
break;
|
||||
|
||||
case Qt::Key_W:
|
||||
_myAvatar->setDriveKeys(FWD, 0.f);
|
||||
break;
|
||||
case Qt::Key_S:
|
||||
_myAvatar->setDriveKeys(BACK, 0.f);
|
||||
break;
|
||||
|
||||
case Qt::Key_S:
|
||||
_myAvatar->setDriveKeys(BACK, 0.f);
|
||||
break;
|
||||
case Qt::Key_A:
|
||||
_myAvatar->setDriveKeys(ROT_LEFT, 0.f);
|
||||
break;
|
||||
|
||||
case Qt::Key_A:
|
||||
_myAvatar->setDriveKeys(ROT_LEFT, 0.f);
|
||||
break;
|
||||
case Qt::Key_D:
|
||||
_myAvatar->setDriveKeys(ROT_RIGHT, 0.f);
|
||||
break;
|
||||
|
||||
case Qt::Key_D:
|
||||
_myAvatar->setDriveKeys(ROT_RIGHT, 0.f);
|
||||
break;
|
||||
case Qt::Key_Up:
|
||||
_myAvatar->setDriveKeys(FWD, 0.f);
|
||||
_myAvatar->setDriveKeys(UP, 0.f);
|
||||
break;
|
||||
|
||||
case Qt::Key_Up:
|
||||
_myAvatar->setDriveKeys(FWD, 0.f);
|
||||
_myAvatar->setDriveKeys(UP, 0.f);
|
||||
break;
|
||||
case Qt::Key_Down:
|
||||
_myAvatar->setDriveKeys(BACK, 0.f);
|
||||
_myAvatar->setDriveKeys(DOWN, 0.f);
|
||||
break;
|
||||
|
||||
case Qt::Key_Down:
|
||||
_myAvatar->setDriveKeys(BACK, 0.f);
|
||||
_myAvatar->setDriveKeys(DOWN, 0.f);
|
||||
break;
|
||||
case Qt::Key_Left:
|
||||
_myAvatar->setDriveKeys(LEFT, 0.f);
|
||||
_myAvatar->setDriveKeys(ROT_LEFT, 0.f);
|
||||
break;
|
||||
|
||||
case Qt::Key_Left:
|
||||
_myAvatar->setDriveKeys(LEFT, 0.f);
|
||||
_myAvatar->setDriveKeys(ROT_LEFT, 0.f);
|
||||
break;
|
||||
case Qt::Key_Right:
|
||||
_myAvatar->setDriveKeys(RIGHT, 0.f);
|
||||
_myAvatar->setDriveKeys(ROT_RIGHT, 0.f);
|
||||
break;
|
||||
|
||||
case Qt::Key_Right:
|
||||
_myAvatar->setDriveKeys(RIGHT, 0.f);
|
||||
_myAvatar->setDriveKeys(ROT_RIGHT, 0.f);
|
||||
break;
|
||||
|
||||
default:
|
||||
event->ignore();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
event->ignore();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Application::focusOutEvent(QFocusEvent* event) {
|
||||
// synthesize events for keys currently pressed, since we may not get their release events
|
||||
foreach (int key, _keysPressed) {
|
||||
QKeyEvent event(QEvent::KeyRelease, key, Qt::NoModifier);
|
||||
keyReleaseEvent(&event);
|
||||
}
|
||||
_keysPressed.clear();
|
||||
}
|
||||
|
||||
void Application::mouseMoveEvent(QMouseEvent* event) {
|
||||
_controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts
|
||||
|
||||
|
@ -1680,7 +1690,7 @@ void Application::updateMyAvatarLookAtPosition() {
|
|||
} else {
|
||||
// look in direction of the mouse ray, but use distance from intersection, if any
|
||||
float distance = TREE_SCALE;
|
||||
if (_myAvatar->getLookAtTargetAvatar()) {
|
||||
if (_myAvatar->getLookAtTargetAvatar() && _myAvatar != _myAvatar->getLookAtTargetAvatar()) {
|
||||
distance = glm::distance(_mouseRayOrigin,
|
||||
static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead()->calculateAverageEyePosition());
|
||||
}
|
||||
|
@ -2140,7 +2150,8 @@ void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
|
|||
}
|
||||
|
||||
glm::vec3 Application::getSunDirection() {
|
||||
return glm::normalize(_environment.getClosestData(_myCamera.getPosition()).getSunLocation() - _myCamera.getPosition());
|
||||
return glm::normalize(_environment.getClosestData(_myCamera.getPosition()).getSunLocation(_myCamera.getPosition()) -
|
||||
_myCamera.getPosition());
|
||||
}
|
||||
|
||||
void Application::updateShadowMap() {
|
||||
|
@ -2302,7 +2313,8 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
float alpha = 1.0f;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) {
|
||||
const EnvironmentData& closestData = _environment.getClosestData(whichCamera.getPosition());
|
||||
float height = glm::distance(whichCamera.getPosition(), closestData.getAtmosphereCenter());
|
||||
float height = glm::distance(whichCamera.getPosition(),
|
||||
closestData.getAtmosphereCenter(whichCamera.getPosition()));
|
||||
if (height < closestData.getAtmosphereInnerRadius()) {
|
||||
alpha = 0.0f;
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QSettings>
|
||||
#include <QTouchEvent>
|
||||
#include <QList>
|
||||
#include <QSet>
|
||||
#include <QStringList>
|
||||
#include <QPointer>
|
||||
|
||||
|
@ -122,6 +123,8 @@ public:
|
|||
void keyPressEvent(QKeyEvent* event);
|
||||
void keyReleaseEvent(QKeyEvent* event);
|
||||
|
||||
void focusOutEvent(QFocusEvent* event);
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
|
@ -432,6 +435,8 @@ private:
|
|||
|
||||
bool _mousePressed; // true if mouse has been pressed (clear when finished)
|
||||
|
||||
QSet<int> _keysPressed;
|
||||
|
||||
GeometryCache _geometryCache;
|
||||
TextureCache _textureCache;
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ glm::vec3 Environment::getGravity (const glm::vec3& position) {
|
|||
|
||||
foreach (const ServerData& serverData, _data) {
|
||||
foreach (const EnvironmentData& environmentData, serverData) {
|
||||
glm::vec3 vector = environmentData.getAtmosphereCenter() - position;
|
||||
glm::vec3 vector = environmentData.getAtmosphereCenter(position) - position;
|
||||
float surfaceRadius = environmentData.getAtmosphereInnerRadius();
|
||||
if (glm::length(vector) <= surfaceRadius) {
|
||||
// At or inside a planet, gravity is as set for the planet
|
||||
|
@ -116,7 +116,7 @@ const EnvironmentData Environment::getClosestData(const glm::vec3& position) {
|
|||
float closestDistance = FLT_MAX;
|
||||
foreach (const ServerData& serverData, _data) {
|
||||
foreach (const EnvironmentData& environmentData, serverData) {
|
||||
float distance = glm::distance(position, environmentData.getAtmosphereCenter()) -
|
||||
float distance = glm::distance(position, environmentData.getAtmosphereCenter(position)) -
|
||||
environmentData.getAtmosphereOuterRadius();
|
||||
if (distance < closestDistance) {
|
||||
closest = environmentData;
|
||||
|
@ -132,6 +132,8 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3
|
|||
// collide with the "floor"
|
||||
bool found = findCapsulePlanePenetration(start, end, radius, glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), penetration);
|
||||
|
||||
glm::vec3 middle = (start + end) * 0.5f;
|
||||
|
||||
// get the lock for the duration of the call
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
||||
|
@ -141,7 +143,7 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3
|
|||
continue; // don't bother colliding with gravity-less environments
|
||||
}
|
||||
glm::vec3 environmentPenetration;
|
||||
if (findCapsuleSpherePenetration(start, end, radius, environmentData.getAtmosphereCenter(),
|
||||
if (findCapsuleSpherePenetration(start, end, radius, environmentData.getAtmosphereCenter(middle),
|
||||
environmentData.getAtmosphereInnerRadius(), environmentPenetration)) {
|
||||
penetration = addPenetrations(penetration, environmentPenetration);
|
||||
found = true;
|
||||
|
@ -203,10 +205,12 @@ ProgramObject* Environment::createSkyProgram(const char* from, int* locations) {
|
|||
}
|
||||
|
||||
void Environment::renderAtmosphere(Camera& camera, const EnvironmentData& data) {
|
||||
glm::vec3 center = data.getAtmosphereCenter(camera.getPosition());
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(data.getAtmosphereCenter().x, data.getAtmosphereCenter().y, data.getAtmosphereCenter().z);
|
||||
|
||||
glm::vec3 relativeCameraPos = camera.getPosition() - data.getAtmosphereCenter();
|
||||
glTranslatef(center.x, center.y, center.z);
|
||||
|
||||
glm::vec3 relativeCameraPos = camera.getPosition() - center;
|
||||
float height = glm::length(relativeCameraPos);
|
||||
|
||||
// use the appropriate shader depending on whether we're inside or outside
|
||||
|
|
|
@ -55,6 +55,10 @@ void GLCanvas::keyReleaseEvent(QKeyEvent* event) {
|
|||
Application::getInstance()->keyReleaseEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::focusOutEvent(QFocusEvent* event) {
|
||||
Application::getInstance()->focusOutEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::mouseMoveEvent(QMouseEvent* event) {
|
||||
Application::getInstance()->mouseMoveEvent(event);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ protected:
|
|||
virtual void keyPressEvent(QKeyEvent* event);
|
||||
virtual void keyReleaseEvent(QKeyEvent* event);
|
||||
|
||||
virtual void focusOutEvent(QFocusEvent* event);
|
||||
|
||||
virtual void mouseMoveEvent(QMouseEvent* event);
|
||||
virtual void mousePressEvent(QMouseEvent* event);
|
||||
virtual void mouseReleaseEvent(QMouseEvent* event);
|
||||
|
|
|
@ -24,11 +24,12 @@
|
|||
#include <QSlider>
|
||||
#include <QStandardPaths>
|
||||
#include <QUuid>
|
||||
#include <QWindow>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <XmppClient.h>
|
||||
#include <UUID.h>
|
||||
#include <FileDownloader.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Menu.h"
|
||||
|
@ -36,6 +37,7 @@
|
|||
#include "Util.h"
|
||||
#include "InfoView.h"
|
||||
#include "ui/MetavoxelEditor.h"
|
||||
#include "ModelBrowser.h"
|
||||
|
||||
|
||||
Menu* Menu::_instance = NULL;
|
||||
|
@ -138,9 +140,8 @@ Menu::Menu() :
|
|||
this,
|
||||
SLOT(goTo()));
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Upload/Browse");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploaderAvatarHead, 0, Application::getInstance(), SLOT(uploadFST()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploaderAvatarSkeleton, 0, Application::getInstance(), SLOT(uploadFST()));
|
||||
addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadFST, 0, Application::getInstance(), SLOT(uploadFST()));
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Settings");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings()));
|
||||
|
@ -693,7 +694,7 @@ void Menu::loginForCurrentDomain() {
|
|||
}
|
||||
|
||||
void Menu::editPreferences() {
|
||||
if (! _preferencesDialog) {
|
||||
if (!_preferencesDialog) {
|
||||
_preferencesDialog = new PreferencesDialog(Application::getInstance()->getGLWidget());
|
||||
_preferencesDialog->show();
|
||||
}
|
||||
|
|
|
@ -300,8 +300,7 @@ namespace MenuOption {
|
|||
const QString StopAllScripts = "Stop All Scripts";
|
||||
const QString TestPing = "Test Ping";
|
||||
const QString TransmitterDrive = "Transmitter Drive";
|
||||
const QString UploaderAvatarHead = "Upload Avatar Head";
|
||||
const QString UploaderAvatarSkeleton = "Upload Avatar Skeleton";
|
||||
const QString UploadFST = "Upload FST file";
|
||||
const QString Visage = "Visage";
|
||||
const QString Quit = "Quit";
|
||||
const QString Voxels = "Voxels";
|
||||
|
|
150
interface/src/ModelBrowser.cpp
Normal file
150
interface/src/ModelBrowser.cpp
Normal file
|
@ -0,0 +1,150 @@
|
|||
//
|
||||
// ModelBrowser.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Clement on 3/17/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QUrl>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QEventLoop>
|
||||
#include <QMessageBox>
|
||||
#include <QGridLayout>
|
||||
#include <QDialog>
|
||||
#include <QStringListModel>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
#include <Application.h>
|
||||
|
||||
#include "ModelBrowser.h"
|
||||
|
||||
static const QString PREFIX_PARAMETER_NAME = "prefix";
|
||||
static const QString MARKER_PARAMETER_NAME = "marker";
|
||||
static const QString IS_TRUNCATED_NAME = "IsTruncated";
|
||||
static const QString CONTAINER_NAME = "Contents";
|
||||
static const QString KEY_NAME = "Key";
|
||||
|
||||
ModelBrowser::ModelBrowser(ModelType modelType, QWidget* parent) : QWidget(parent), _type(modelType) {
|
||||
QUrl url(S3_URL);
|
||||
QUrlQuery query;
|
||||
|
||||
if (_type == Head) {
|
||||
query.addQueryItem(PREFIX_PARAMETER_NAME, HEAD_MODELS_LOCATION);
|
||||
} else if (_type == Skeleton) {
|
||||
query.addQueryItem(PREFIX_PARAMETER_NAME, SKELETON_MODELS_LOCATION);
|
||||
}
|
||||
url.setQuery(query);
|
||||
|
||||
_downloader = new FileDownloader(url);
|
||||
connect(_downloader, SIGNAL(done(QNetworkReply::NetworkError)), SLOT(downloadFinished()));
|
||||
}
|
||||
|
||||
ModelBrowser::~ModelBrowser() {
|
||||
delete _downloader;
|
||||
}
|
||||
|
||||
void ModelBrowser::downloadFinished() {
|
||||
parseXML(_downloader->getData());
|
||||
}
|
||||
|
||||
void ModelBrowser::browse() {
|
||||
QDialog dialog(this);
|
||||
dialog.setWindowTitle("Browse models");
|
||||
|
||||
QGridLayout* layout = new QGridLayout(&dialog);
|
||||
dialog.setLayout(layout);
|
||||
|
||||
QLineEdit* searchBar = new QLineEdit(&dialog);
|
||||
layout->addWidget(searchBar, 0, 0);
|
||||
|
||||
ListView* listView = new ListView(&dialog);
|
||||
listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
layout->addWidget(listView, 1, 0);
|
||||
listView->connect(searchBar, SIGNAL(textChanged(const QString&)), SLOT(keyboardSearch(const QString&)));
|
||||
dialog.connect(listView, SIGNAL(doubleClicked(const QModelIndex&)), SLOT(accept()));
|
||||
|
||||
QStringListModel* model = new QStringListModel(_models.keys(), listView);
|
||||
model->sort(0);
|
||||
listView->setModel(model);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
layout->addWidget(buttons, 2, 0);
|
||||
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||
dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||
|
||||
if (dialog.exec() == QDialog::Rejected) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString selectedKey = model->data(listView->currentIndex(), Qt::DisplayRole).toString();
|
||||
|
||||
emit selected(_models[selectedKey]);
|
||||
}
|
||||
|
||||
bool ModelBrowser::parseXML(QByteArray xmlFile) {
|
||||
QXmlStreamReader xml(xmlFile);
|
||||
QRegExp rx(".*fst");
|
||||
bool truncated = false;
|
||||
QString lastKey;
|
||||
|
||||
// Read xml until the end or an error is detected
|
||||
while(!xml.atEnd() && !xml.hasError()) {
|
||||
if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == IS_TRUNCATED_NAME) {
|
||||
while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == IS_TRUNCATED_NAME)) {
|
||||
// Let's check if there is more
|
||||
xml.readNext();
|
||||
if (xml.text().toString() == "True") {
|
||||
truncated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == CONTAINER_NAME) {
|
||||
while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == CONTAINER_NAME)) {
|
||||
// If a file is find, process it
|
||||
if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == KEY_NAME) {
|
||||
xml.readNext();
|
||||
lastKey = xml.text().toString();
|
||||
if (rx.exactMatch(xml.text().toString())) {
|
||||
// Add the found file to the list
|
||||
_models.insert(QFileInfo(xml.text().toString()).baseName(),
|
||||
S3_URL + "/" + xml.text().toString());
|
||||
}
|
||||
}
|
||||
xml.readNext();
|
||||
}
|
||||
}
|
||||
xml.readNext();
|
||||
}
|
||||
|
||||
// Error handling
|
||||
if(xml.hasError()) {
|
||||
_models.clear();
|
||||
QMessageBox::critical(this,
|
||||
"ModelBrowser::ModelBrowser()",
|
||||
xml.errorString(),
|
||||
QMessageBox::Ok);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we didn't all the files, download the next ones
|
||||
if (truncated) {
|
||||
QUrl url(S3_URL);
|
||||
QUrlQuery query;
|
||||
|
||||
if (_type == Head) {
|
||||
query.addQueryItem(PREFIX_PARAMETER_NAME, HEAD_MODELS_LOCATION);
|
||||
} else if (_type == Skeleton) {
|
||||
query.addQueryItem(PREFIX_PARAMETER_NAME, SKELETON_MODELS_LOCATION);
|
||||
}
|
||||
query.addQueryItem(MARKER_PARAMETER_NAME, lastKey);
|
||||
url.setQuery(query);
|
||||
|
||||
delete _downloader;
|
||||
_downloader = new FileDownloader(url);
|
||||
connect(_downloader, SIGNAL(done(QNetworkReply::NetworkError)), SLOT(downloadFinished()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
62
interface/src/ModelBrowser.h
Normal file
62
interface/src/ModelBrowser.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// ModelBrowser.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Clement on 3/17/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__ModelBrowser__
|
||||
#define __hifi__ModelBrowser__
|
||||
|
||||
#include <FileDownloader.h>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QListView>
|
||||
|
||||
static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
|
||||
static const QString HEAD_MODELS_LOCATION = "models/heads/";
|
||||
static const QString SKELETON_MODELS_LOCATION = "models/skeletons/";
|
||||
|
||||
enum ModelType {
|
||||
Head,
|
||||
Skeleton
|
||||
};
|
||||
|
||||
class ModelBrowser : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModelBrowser(ModelType modelType, QWidget* parent = NULL);
|
||||
~ModelBrowser();
|
||||
|
||||
signals:
|
||||
void selected(QString filename);
|
||||
|
||||
public slots:
|
||||
void browse();
|
||||
|
||||
private slots:
|
||||
void downloadFinished();
|
||||
|
||||
private:
|
||||
ModelType _type;
|
||||
FileDownloader* _downloader;
|
||||
QHash<QString, QString> _models;
|
||||
|
||||
bool parseXML(QByteArray xmlFile);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ListView : public QListView {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ListView(QWidget* parent) : QListView(parent) {}
|
||||
public slots:
|
||||
void keyboardSearch(const QString& text) {
|
||||
QAbstractItemView::keyboardSearch(text);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ModelBrowser__) */
|
|
@ -582,16 +582,15 @@ void MyAvatar::updateLookAtTargetAvatar() {
|
|||
|
||||
foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) {
|
||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
||||
if (avatar == static_cast<Avatar*>(this)) {
|
||||
continue;
|
||||
}
|
||||
float distance;
|
||||
if (avatar->findRayIntersection(mouseOrigin, mouseDirection, distance)) {
|
||||
_lookAtTargetAvatar = avatarPointer;
|
||||
_targetAvatarPosition = avatarPointer->getPosition();
|
||||
return;
|
||||
}
|
||||
}
|
||||
_lookAtTargetAvatar.clear();
|
||||
_targetAvatarPosition = glm::vec3(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ public:
|
|||
|
||||
void orbit(const glm::vec3& position, int deltaX, int deltaY);
|
||||
|
||||
Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }
|
||||
AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
|
||||
void updateLookAtTargetAvatar();
|
||||
void clearLookAtTargetAvatar();
|
||||
|
@ -116,6 +117,7 @@ private:
|
|||
glm::vec3 _moveTarget;
|
||||
int _moveTargetStepCounter;
|
||||
QWeakPointer<AvatarData> _lookAtTargetAvatar;
|
||||
glm::vec3 _targetAvatarPosition;
|
||||
bool _shouldRender;
|
||||
|
||||
bool _billboardValid;
|
||||
|
|
|
@ -313,7 +313,7 @@ void ImageReader::run() {
|
|||
int imageArea = image.width() * image.height();
|
||||
if (opaquePixels == imageArea) {
|
||||
qDebug() << "Image with alpha channel is completely opaque:" << url;
|
||||
image.convertToFormat(QImage::Format_RGB888);
|
||||
image = image.convertToFormat(QImage::Format_RGB888);
|
||||
}
|
||||
QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image),
|
||||
Q_ARG(bool, translucentPixels >= imageArea / 2));
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
//
|
||||
//
|
||||
|
||||
#include <QDesktopWidget>
|
||||
#include <QFile>
|
||||
#include <QPushButton>
|
||||
#include <QSizeGrip>
|
||||
|
@ -13,13 +14,29 @@
|
|||
#include "Application.h"
|
||||
#include "FramelessDialog.h"
|
||||
|
||||
FramelessDialog::FramelessDialog(QWidget *parent, Qt::WindowFlags flags) : QDialog(parent, flags | Qt::FramelessWindowHint) {
|
||||
setWindowOpacity(0.9);
|
||||
const int RESIZE_HANDLE_WIDTH = 7;
|
||||
|
||||
FramelessDialog::FramelessDialog(QWidget *parent, Qt::WindowFlags flags) :
|
||||
QDialog(parent, flags | Qt::FramelessWindowHint),
|
||||
_isResizing(false) {
|
||||
|
||||
setWindowOpacity(0.95);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
isResizing = false;
|
||||
|
||||
installEventFilter(this);
|
||||
}
|
||||
|
||||
void FramelessDialog::setStyleSheet(const QString& fileName) {
|
||||
void FramelessDialog::showEvent(QShowEvent* event) {
|
||||
QDesktopWidget desktop;
|
||||
|
||||
// move to upper left
|
||||
move(desktop.availableGeometry().x(), desktop.availableGeometry().y());
|
||||
|
||||
// keep full height
|
||||
resize(size().width(), desktop.availableGeometry().height());
|
||||
}
|
||||
|
||||
void FramelessDialog::setStyleSheetFile(const QString& fileName) {
|
||||
QFile globalStyleSheet(Application::resourcesPath() + "styles/global.qss");
|
||||
QFile styleSheet(Application::resourcesPath() + fileName);
|
||||
if (styleSheet.open(QIODevice::ReadOnly) && globalStyleSheet.open(QIODevice::ReadOnly) ) {
|
||||
|
@ -27,27 +44,24 @@ void FramelessDialog::setStyleSheet(const QString& fileName) {
|
|||
QDialog::setStyleSheet(globalStyleSheet.readAll() + styleSheet.readAll());
|
||||
}
|
||||
}
|
||||
|
||||
void FramelessDialog::mousePressEvent(QMouseEvent* mouseEvent) {
|
||||
if (abs(mouseEvent->pos().x() - size().width()) < 2 && mouseEvent->button() == Qt::LeftButton) {
|
||||
isResizing = true;
|
||||
|
||||
if (abs(mouseEvent->pos().x() - size().width()) < RESIZE_HANDLE_WIDTH && mouseEvent->button() == Qt::LeftButton) {
|
||||
_isResizing = true;
|
||||
QApplication::setOverrideCursor(Qt::SizeHorCursor);
|
||||
}
|
||||
// propagate the event
|
||||
QDialog::mousePressEvent(mouseEvent);
|
||||
}
|
||||
|
||||
void FramelessDialog::mouseReleaseEvent(QMouseEvent* mouseEvent) {
|
||||
QApplication::restoreOverrideCursor();
|
||||
isResizing = false;
|
||||
// propagate the event
|
||||
QDialog::mouseReleaseEvent(mouseEvent);
|
||||
_isResizing = false;
|
||||
}
|
||||
|
||||
void FramelessDialog::mouseMoveEvent(QMouseEvent* mouseEvent) {
|
||||
if (isResizing) {
|
||||
if (_isResizing) {
|
||||
resize(mouseEvent->pos().x(), size().height());
|
||||
}
|
||||
QDialog::mouseMoveEvent(mouseEvent);
|
||||
}
|
||||
|
||||
FramelessDialog::~FramelessDialog() {
|
||||
|
|
|
@ -21,15 +21,16 @@ class FramelessDialog : public QDialog {
|
|||
public:
|
||||
FramelessDialog(QWidget* parent = 0, Qt::WindowFlags flags = 0);
|
||||
virtual ~FramelessDialog();
|
||||
void setStyleSheet(const QString& fileName);
|
||||
void setStyleSheetFile(const QString& fileName);
|
||||
|
||||
protected:
|
||||
void mouseMoveEvent(QMouseEvent* mouseEvent);
|
||||
void mousePressEvent(QMouseEvent* mouseEvent);
|
||||
void mouseReleaseEvent(QMouseEvent* mouseEvent);
|
||||
virtual void mouseMoveEvent(QMouseEvent* mouseEvent);
|
||||
virtual void mousePressEvent(QMouseEvent* mouseEvent);
|
||||
virtual void mouseReleaseEvent(QMouseEvent* mouseEvent);
|
||||
virtual void showEvent(QShowEvent* event);
|
||||
|
||||
private:
|
||||
bool isResizing;
|
||||
bool _isResizing;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -6,14 +6,27 @@
|
|||
//
|
||||
//
|
||||
|
||||
#include "PreferencesDialog.h"
|
||||
#include "Application.h"
|
||||
#include "Menu.h"
|
||||
#include "ModelBrowser.h"
|
||||
#include "PreferencesDialog.h"
|
||||
|
||||
const int SCROLL_PANEL_BOTTOM_MARGIN = 30;
|
||||
const int OK_BUTTON_RIGHT_MARGIN = 30;
|
||||
const int BUTTONS_TOP_MARGIN = 24;
|
||||
|
||||
PreferencesDialog::PreferencesDialog(QWidget* parent, Qt::WindowFlags flags) : FramelessDialog(parent, flags) {
|
||||
|
||||
ui.setupUi(this);
|
||||
setStyleSheet("styles/preferences.qss");
|
||||
setStyleSheetFile("styles/preferences.qss");
|
||||
loadPreferences();
|
||||
connect(ui.closeButton, &QPushButton::clicked, this, &QDialog::close);
|
||||
connect(ui.buttonBrowseHead, &QPushButton::clicked, this, &PreferencesDialog::openHeadModelBrowser);
|
||||
connect(ui.buttonBrowseBody, &QPushButton::clicked, this, &PreferencesDialog::openBodyModelBrowser);
|
||||
}
|
||||
|
||||
PreferencesDialog::~PreferencesDialog() {
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void PreferencesDialog::accept() {
|
||||
|
@ -21,6 +34,39 @@ void PreferencesDialog::accept() {
|
|||
close();
|
||||
}
|
||||
|
||||
void PreferencesDialog::openHeadModelBrowser() {
|
||||
ModelBrowser modelBrowser(Head);
|
||||
modelBrowser.browse();
|
||||
connect(&modelBrowser, &ModelBrowser::selected, ui.faceURLEdit, &QLineEdit::setText);
|
||||
}
|
||||
|
||||
void PreferencesDialog::openBodyModelBrowser() {
|
||||
ModelBrowser modelBrowser(Skeleton);
|
||||
modelBrowser.browse();
|
||||
connect(&modelBrowser, &ModelBrowser::selected, ui.skeletonURLEdit, &QLineEdit::setText);
|
||||
}
|
||||
|
||||
void PreferencesDialog::resizeEvent(QResizeEvent *resizeEvent) {
|
||||
|
||||
// keep buttons panel at the bottom
|
||||
ui.buttonsPanel->setGeometry(0, size().height() - ui.buttonsPanel->height(), size().width(), ui.buttonsPanel->height());
|
||||
|
||||
// set width and height of srcollarea to match bottom panel and width
|
||||
ui.scrollArea->setGeometry(ui.scrollArea->geometry().x(), ui.scrollArea->geometry().y(),
|
||||
size().width(),
|
||||
size().height() - ui.buttonsPanel->height() -
|
||||
SCROLL_PANEL_BOTTOM_MARGIN - ui.scrollArea->geometry().y());
|
||||
|
||||
// move Save button to left position
|
||||
ui.defaultButton->move(size().width() - OK_BUTTON_RIGHT_MARGIN - ui.defaultButton->size().width(), BUTTONS_TOP_MARGIN);
|
||||
|
||||
// move Save button to left position
|
||||
ui.cancelButton->move(ui.defaultButton->pos().x() - ui.cancelButton->size().width(), BUTTONS_TOP_MARGIN);
|
||||
|
||||
// move close button
|
||||
ui.closeButton->move(size().width() - OK_BUTTON_RIGHT_MARGIN - ui.closeButton->size().width(), ui.closeButton->pos().y());
|
||||
}
|
||||
|
||||
void PreferencesDialog::loadPreferences() {
|
||||
|
||||
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
|
||||
|
|
|
@ -19,16 +19,22 @@ class PreferencesDialog : public FramelessDialog {
|
|||
|
||||
public:
|
||||
PreferencesDialog(QWidget* parent = 0, Qt::WindowFlags flags = 0);
|
||||
~PreferencesDialog();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* resizeEvent);
|
||||
|
||||
private:
|
||||
Ui_PreferencesDialog ui;
|
||||
void loadPreferences();
|
||||
void savePreferences();
|
||||
|
||||
void openHeadModelBrowser();
|
||||
void openBodyModelBrowser();
|
||||
|
||||
Ui_PreferencesDialog ui;
|
||||
QString _faceURLString;
|
||||
QString _skeletonURLString;
|
||||
QString _displayNameString;
|
||||
|
||||
|
||||
private slots:
|
||||
void accept();
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -19,7 +19,8 @@ AudioRingBuffer::AudioRingBuffer(int numFrameSamples) :
|
|||
NodeData(),
|
||||
_sampleCapacity(numFrameSamples * RING_BUFFER_LENGTH_FRAMES),
|
||||
_isStarved(true),
|
||||
_hasStarted(false)
|
||||
_hasStarted(false),
|
||||
_averageLoudness(0)
|
||||
{
|
||||
if (numFrameSamples) {
|
||||
_buffer = new int16_t[_sampleCapacity];
|
||||
|
@ -55,18 +56,22 @@ int AudioRingBuffer::parseData(const QByteArray& packet) {
|
|||
return writeData(packet.data() + numBytesPacketHeader, packet.size() - numBytesPacketHeader);
|
||||
}
|
||||
|
||||
float AudioRingBuffer::averageLoudnessForBoundarySamples(int numSamples) {
|
||||
void AudioRingBuffer::updateAverageLoudnessForBoundarySamples(int numSamples) {
|
||||
// ForBoundarySamples means that we expect the number of samples not to roll of the end of the ring buffer
|
||||
float averageLoudness = 0;
|
||||
float nextLoudness = 0;
|
||||
|
||||
for (int i = 0; i < numSamples; ++i) {
|
||||
averageLoudness += fabsf(_nextOutput[i]);
|
||||
nextLoudness += fabsf(_nextOutput[i]);
|
||||
}
|
||||
|
||||
averageLoudness /= numSamples;
|
||||
averageLoudness /= MAX_SAMPLE_VALUE;
|
||||
nextLoudness /= numSamples;
|
||||
nextLoudness /= MAX_SAMPLE_VALUE;
|
||||
|
||||
const int TRAILING_AVERAGE_FRAMES = 100;
|
||||
const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
|
||||
const float PREVIOUS_FRAMES_RATIO = 1 - CURRENT_FRAME_RATIO;
|
||||
|
||||
return averageLoudness;
|
||||
_averageLoudness = (_averageLoudness * PREVIOUS_FRAMES_RATIO) + (CURRENT_FRAME_RATIO * nextLoudness);
|
||||
}
|
||||
|
||||
qint64 AudioRingBuffer::readSamples(int16_t* destination, qint64 maxSamples) {
|
||||
|
|
|
@ -50,7 +50,8 @@ public:
|
|||
const int16_t* getNextOutput() { return _nextOutput; }
|
||||
const int16_t* getBuffer() { return _buffer; }
|
||||
|
||||
float averageLoudnessForBoundarySamples(int numSamples);
|
||||
void updateAverageLoudnessForBoundarySamples(int numSamples);
|
||||
float getAverageLoudness() const { return _averageLoudness; }
|
||||
|
||||
qint64 readSamples(int16_t* destination, qint64 maxSamples);
|
||||
qint64 writeSamples(const int16_t* source, qint64 maxSamples);
|
||||
|
@ -85,6 +86,8 @@ protected:
|
|||
int16_t* _buffer;
|
||||
bool _isStarved;
|
||||
bool _hasStarted;
|
||||
|
||||
float _averageLoudness;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__AudioRingBuffer__) */
|
||||
|
|
|
@ -50,6 +50,7 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) {
|
|||
int16_t numSilentSamples;
|
||||
|
||||
memcpy(&numSilentSamples, packet.data() + readBytes, sizeof(int16_t));
|
||||
|
||||
readBytes += sizeof(int16_t);
|
||||
|
||||
addSilentFrame(numSilentSamples);
|
||||
|
|
|
@ -88,6 +88,17 @@ QByteArray AvatarData::toByteArray() {
|
|||
// Body scale
|
||||
destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _targetScale);
|
||||
|
||||
// Head rotation (NOTE: This needs to become a quaternion to save two bytes)
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getTweakedYaw());
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getTweakedPitch());
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getTweakedRoll());
|
||||
|
||||
// Head lean X,Z (head lateral and fwd/back motion relative to torso)
|
||||
memcpy(destinationBuffer, &_headData->_leanSideways, sizeof(_headData->_leanSideways));
|
||||
destinationBuffer += sizeof(_headData->_leanSideways);
|
||||
memcpy(destinationBuffer, &_headData->_leanForward, sizeof(_headData->_leanForward));
|
||||
destinationBuffer += sizeof(_headData->_leanForward);
|
||||
|
||||
// Lookat Position
|
||||
memcpy(destinationBuffer, &_headData->_lookAtPosition, sizeof(_headData->_lookAtPosition));
|
||||
destinationBuffer += sizeof(_headData->_lookAtPosition);
|
||||
|
@ -191,6 +202,21 @@ int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) {
|
|||
// Body scale
|
||||
sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, _targetScale);
|
||||
|
||||
// Head rotation (NOTE: This needs to become a quaternion to save two bytes)
|
||||
float headYaw, headPitch, headRoll;
|
||||
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headYaw);
|
||||
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headPitch);
|
||||
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headRoll);
|
||||
_headData->setYaw(headYaw);
|
||||
_headData->setPitch(headPitch);
|
||||
_headData->setRoll(headRoll);
|
||||
|
||||
// Head position relative to pelvis
|
||||
memcpy(&_headData->_leanSideways, sourceBuffer, sizeof(_headData->_leanSideways));
|
||||
sourceBuffer += sizeof(float);
|
||||
memcpy(&_headData->_leanForward, sourceBuffer, sizeof(_headData->_leanForward));
|
||||
sourceBuffer += sizeof(_headData->_leanForward);
|
||||
|
||||
// Lookat Position
|
||||
memcpy(&_headData->_lookAtPosition, sourceBuffer, sizeof(_headData->_lookAtPosition));
|
||||
sourceBuffer += sizeof(_headData->_lookAtPosition);
|
||||
|
|
65
libraries/shared/src/FileDownloader.cpp
Normal file
65
libraries/shared/src/FileDownloader.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// FileDownloader.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Clement Brisset on 3/14/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
//
|
||||
|
||||
#include <QUrl>
|
||||
#include <QNetworkRequest>
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
|
||||
#include "FileDownloader.h"
|
||||
|
||||
FileDownloader::FileDownloader(const QUrl dataURL, QObject* parent) :
|
||||
QObject(parent),
|
||||
_done(false)
|
||||
{
|
||||
connect(&_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(processReply(QNetworkReply*)));
|
||||
|
||||
QNetworkRequest request(dataURL);
|
||||
_networkAccessManager.get(request);
|
||||
}
|
||||
|
||||
void FileDownloader::processReply(QNetworkReply *reply) {
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
_downloadedData = reply->readAll();
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
_done = true;
|
||||
emit done(reply->error());
|
||||
}
|
||||
|
||||
void FileDownloader::waitForFile(int timeout) {
|
||||
QTimer timer;
|
||||
QEventLoop loop;
|
||||
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
|
||||
connect(this, SIGNAL(done(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
|
||||
|
||||
if (!_done) {
|
||||
if (timeout > 0) {
|
||||
timer.start(timeout);
|
||||
}
|
||||
loop.exec();
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray FileDownloader::download(const QUrl dataURL, int timeout) {
|
||||
QTimer timer;
|
||||
QEventLoop loop;
|
||||
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit));
|
||||
|
||||
FileDownloader downloader(dataURL);
|
||||
connect(&downloader, SIGNAL(done(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
|
||||
|
||||
if (timeout > 0) {
|
||||
timer.start(timeout);
|
||||
}
|
||||
loop.exec();
|
||||
|
||||
return downloader.getData();
|
||||
}
|
44
libraries/shared/src/FileDownloader.h
Normal file
44
libraries/shared/src/FileDownloader.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// FileDownloader.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Clement Brisset on 3/14/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __hifi__FileDownloader__
|
||||
#define __hifi__FileDownloader__
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class FileDownloader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FileDownloader(const QUrl dataURL, QObject* parent = NULL);
|
||||
|
||||
void waitForFile(int timeout = 0);
|
||||
|
||||
QByteArray getData() const { return _downloadedData; }
|
||||
bool done() { return _done; }
|
||||
|
||||
static QByteArray download(const QUrl dataURL, int timeout = 0);
|
||||
|
||||
signals:
|
||||
void done(QNetworkReply::NetworkError error);
|
||||
|
||||
private slots:
|
||||
void processReply(QNetworkReply* reply);
|
||||
|
||||
private:
|
||||
QNetworkAccessManager _networkAccessManager;
|
||||
QByteArray _downloadedData;
|
||||
|
||||
bool _done;
|
||||
};
|
||||
|
||||
|
||||
#endif /* defined(__hifi__FileDownloader__) */
|
|
@ -24,6 +24,7 @@ static const QString NAME_FIELD = "name";
|
|||
static const QString FILENAME_FIELD = "filename";
|
||||
static const QString TEXDIR_FIELD = "texdir";
|
||||
static const QString LOD_FIELD = "lod";
|
||||
static const QString HEAD_SPECIFIC_FIELD = "bs";
|
||||
|
||||
static const QString MODEL_URL = "/api/v1/models";
|
||||
|
||||
|
@ -32,6 +33,7 @@ static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB
|
|||
FstReader::FstReader() :
|
||||
_lodCount(-1),
|
||||
_texturesCount(-1),
|
||||
_isHead(false),
|
||||
_readyToSend(false),
|
||||
_dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType))
|
||||
{
|
||||
|
@ -81,13 +83,15 @@ bool FstReader::zip() {
|
|||
}
|
||||
|
||||
// according to what is read, we modify the command
|
||||
if (line.first() == NAME_FIELD) {
|
||||
if (line[1] == HEAD_SPECIFIC_FIELD) {
|
||||
_isHead = true;
|
||||
} else if (line[1] == NAME_FIELD) {
|
||||
QHttpPart textPart;
|
||||
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
|
||||
" name=\"model_name\"");
|
||||
textPart.setBody(line[1].toUtf8());
|
||||
_dataMultiPart->append(textPart);
|
||||
} else if (line.first() == FILENAME_FIELD) {
|
||||
} else if (line[1] == FILENAME_FIELD) {
|
||||
QFileInfo fbx(QFileInfo(fst).path() + "/" + line[1]);
|
||||
if (!fbx.exists() || !fbx.isFile()) { // Check existence
|
||||
qDebug() << "[ERROR] FBX file " << fbx.absoluteFilePath() << " doesn't exist.";
|
||||
|
@ -101,7 +105,7 @@ bool FstReader::zip() {
|
|||
if (!addPart(_zipDir.path() + "/" + line[1], "fbx")) {
|
||||
return false;
|
||||
}
|
||||
} else if (line.first() == TEXDIR_FIELD) { // Check existence
|
||||
} else if (line[1] == TEXDIR_FIELD) { // Check existence
|
||||
QFileInfo texdir(QFileInfo(fst).path() + "/" + line[1]);
|
||||
if (!texdir.exists() || !texdir.isDir()) {
|
||||
qDebug() << "[ERROR] Texture directory " << texdir.absolutePath() << " doesn't exist.";
|
||||
|
@ -110,7 +114,7 @@ bool FstReader::zip() {
|
|||
if (!addTextures(texdir)) { // Recursive compress and copy
|
||||
return false;
|
||||
}
|
||||
} else if (line.first() == LOD_FIELD) {
|
||||
} else if (line[1] == LOD_FIELD) {
|
||||
QFileInfo lod(QFileInfo(fst).path() + "/" + line[1]);
|
||||
if (!lod.exists() || !lod.isFile()) { // Check existence
|
||||
qDebug() << "[ERROR] FBX file " << lod.absoluteFilePath() << " doesn't exist.";
|
||||
|
@ -127,6 +131,17 @@ bool FstReader::zip() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
QHttpPart textPart;
|
||||
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
|
||||
" name=\"model_category\"");
|
||||
if (_isHead) {
|
||||
textPart.setBody("head");
|
||||
} else {
|
||||
textPart.setBody("skeleton");
|
||||
}
|
||||
_dataMultiPart->append(textPart);
|
||||
|
||||
_readyToSend = true;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ private:
|
|||
int _lodCount;
|
||||
int _texturesCount;
|
||||
int _totalSize;
|
||||
bool _isHead;
|
||||
bool _readyToSend;
|
||||
|
||||
QHttpMultiPart* _dataMultiPart;
|
||||
|
|
|
@ -45,7 +45,9 @@ int packArithmeticallyCodedValue(int value, char* destination) {
|
|||
PacketVersion versionForPacketType(PacketType type) {
|
||||
switch (type) {
|
||||
case PacketTypeAvatarData:
|
||||
return 2;
|
||||
return 3;
|
||||
case PacketTypeEnvironmentData:
|
||||
return 1;
|
||||
case PacketTypeParticleData:
|
||||
return 1;
|
||||
case PacketTypeDomainList:
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
// GameEngine.cpp
|
||||
EnvironmentData::EnvironmentData(int id) :
|
||||
_id(id),
|
||||
_flat(true),
|
||||
_gravity(0.0f),
|
||||
_atmosphereCenter(0, -1000, 0),
|
||||
_atmosphereInnerRadius(1000),
|
||||
|
@ -25,12 +26,23 @@ EnvironmentData::EnvironmentData(int id) :
|
|||
_sunBrightness(20.0f) {
|
||||
}
|
||||
|
||||
glm::vec3 EnvironmentData::getAtmosphereCenter(const glm::vec3& cameraPosition) const {
|
||||
return _atmosphereCenter + (_flat ? glm::vec3(cameraPosition.x, 0.0f, cameraPosition.z) : glm::vec3());
|
||||
}
|
||||
|
||||
glm::vec3 EnvironmentData::getSunLocation(const glm::vec3& cameraPosition) const {
|
||||
return _sunLocation + (_flat ? glm::vec3(cameraPosition.x, 0.0f, cameraPosition.z) : glm::vec3());
|
||||
}
|
||||
|
||||
int EnvironmentData::getBroadcastData(unsigned char* destinationBuffer) const {
|
||||
unsigned char* bufferStart = destinationBuffer;
|
||||
|
||||
memcpy(destinationBuffer, &_id, sizeof(_id));
|
||||
destinationBuffer += sizeof(_id);
|
||||
|
||||
memcpy(destinationBuffer, &_flat, sizeof(_flat));
|
||||
destinationBuffer += sizeof(_flat);
|
||||
|
||||
memcpy(destinationBuffer, &_gravity, sizeof(_gravity));
|
||||
destinationBuffer += sizeof(_gravity);
|
||||
|
||||
|
@ -67,6 +79,9 @@ int EnvironmentData::parseData(const unsigned char* sourceBuffer, int numBytes)
|
|||
memcpy(&_id, sourceBuffer, sizeof(_id));
|
||||
sourceBuffer += sizeof(_id);
|
||||
|
||||
memcpy(&_flat, sourceBuffer, sizeof(_flat));
|
||||
sourceBuffer += sizeof(_flat);
|
||||
|
||||
memcpy(&_gravity, sourceBuffer, sizeof(_gravity));
|
||||
sourceBuffer += sizeof(_gravity);
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@ public:
|
|||
void setID(int id) { _id = id; }
|
||||
int getID() const { return _id; }
|
||||
|
||||
void setFlat(bool flat) { _flat = flat; }
|
||||
bool isFlat() const { return _flat; }
|
||||
|
||||
void setGravity(float gravity) { _gravity = gravity; }
|
||||
float getGravity() const { return _gravity; }
|
||||
|
||||
|
@ -42,6 +45,9 @@ public:
|
|||
const glm::vec3& getSunLocation() const { return _sunLocation; }
|
||||
float getSunBrightness() const { return _sunBrightness; }
|
||||
|
||||
glm::vec3 getAtmosphereCenter(const glm::vec3& cameraPosition) const;
|
||||
glm::vec3 getSunLocation(const glm::vec3& cameraPosition) const;
|
||||
|
||||
int getBroadcastData(unsigned char* destinationBuffer) const;
|
||||
int parseData(const unsigned char* sourceBuffer, int numBytes);
|
||||
|
||||
|
@ -49,6 +55,8 @@ private:
|
|||
|
||||
int _id;
|
||||
|
||||
bool _flat;
|
||||
|
||||
float _gravity;
|
||||
|
||||
glm::vec3 _atmosphereCenter;
|
||||
|
|
Loading…
Reference in a new issue