mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 10:13:12 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into 19755
This commit is contained in:
commit
1959768513
42 changed files with 2070 additions and 241 deletions
|
@ -86,7 +86,7 @@ bool ModelServer::hasSpecialPacketToSend(const SharedNodePointer& node) {
|
||||||
return shouldSendDeletedModels;
|
return shouldSendDeletedModels;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ModelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) {
|
int ModelServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) {
|
||||||
unsigned char outputBuffer[MAX_PACKET_SIZE];
|
unsigned char outputBuffer[MAX_PACKET_SIZE];
|
||||||
size_t packetLength = 0;
|
size_t packetLength = 0;
|
||||||
|
|
||||||
|
@ -99,6 +99,7 @@ int ModelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodeP
|
||||||
bool hasMoreToSend = true;
|
bool hasMoreToSend = true;
|
||||||
|
|
||||||
// TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 models?
|
// TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 models?
|
||||||
|
packetsSent = 0;
|
||||||
while (hasMoreToSend) {
|
while (hasMoreToSend) {
|
||||||
hasMoreToSend = tree->encodeModelsDeletedSince(queryNode->getSequenceNumber(), deletedModelsSentAt,
|
hasMoreToSend = tree->encodeModelsDeletedSince(queryNode->getSequenceNumber(), deletedModelsSentAt,
|
||||||
outputBuffer, MAX_PACKET_SIZE, packetLength);
|
outputBuffer, MAX_PACKET_SIZE, packetLength);
|
||||||
|
@ -107,6 +108,7 @@ int ModelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodeP
|
||||||
|
|
||||||
NodeList::getInstance()->writeDatagram((char*) outputBuffer, packetLength, SharedNodePointer(node));
|
NodeList::getInstance()->writeDatagram((char*) outputBuffer, packetLength, SharedNodePointer(node));
|
||||||
queryNode->packetSent(outputBuffer, packetLength);
|
queryNode->packetSent(outputBuffer, packetLength);
|
||||||
|
packetsSent++;
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeData->setLastDeletedModelsSentAt(deletePacketSentAt);
|
nodeData->setLastDeletedModelsSentAt(deletePacketSentAt);
|
||||||
|
|
|
@ -37,7 +37,7 @@ public:
|
||||||
// subclass may implement these method
|
// subclass may implement these method
|
||||||
virtual void beforeRun();
|
virtual void beforeRun();
|
||||||
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node);
|
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node);
|
||||||
virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node);
|
virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent);
|
||||||
|
|
||||||
virtual void modelCreated(const ModelItem& newModel, const SharedNodePointer& senderNode);
|
virtual void modelCreated(const ModelItem& newModel, const SharedNodePointer& senderNode);
|
||||||
|
|
||||||
|
|
|
@ -384,7 +384,7 @@ bool OctreeQueryNode::hasNextNackedPacket() const {
|
||||||
const QByteArray* OctreeQueryNode::getNextNackedPacket() {
|
const QByteArray* OctreeQueryNode::getNextNackedPacket() {
|
||||||
if (!_nackedSequenceNumbers.isEmpty()) {
|
if (!_nackedSequenceNumbers.isEmpty()) {
|
||||||
// could return null if packet is not in the history
|
// could return null if packet is not in the history
|
||||||
return _sentPacketHistory.getPacket(_nackedSequenceNumbers.takeFirst());
|
return _sentPacketHistory.getPacket(_nackedSequenceNumbers.dequeue());
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,6 @@ bool OctreeSendThread::process() {
|
||||||
if (nodeData && !nodeData->isShuttingDown()) {
|
if (nodeData && !nodeData->isShuttingDown()) {
|
||||||
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
|
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
|
||||||
packetDistributor(nodeData, viewFrustumChanged);
|
packetDistributor(nodeData, viewFrustumChanged);
|
||||||
resendNackedPackets(nodeData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -281,26 +280,6 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
||||||
return packetsSent;
|
return packetsSent;
|
||||||
}
|
}
|
||||||
|
|
||||||
int OctreeSendThread::resendNackedPackets(OctreeQueryNode* nodeData) {
|
|
||||||
|
|
||||||
const int MAX_PACKETS_RESEND = 10;
|
|
||||||
int packetsSent = 0;
|
|
||||||
|
|
||||||
const QByteArray* packet;
|
|
||||||
while (nodeData->hasNextNackedPacket() && packetsSent < MAX_PACKETS_RESEND) {
|
|
||||||
packet = nodeData->getNextNackedPacket();
|
|
||||||
if (packet) {
|
|
||||||
NodeList::getInstance()->writeDatagram(*packet, _node);
|
|
||||||
packetsSent++;
|
|
||||||
|
|
||||||
_totalBytes += packet->size();
|
|
||||||
_totalPackets++;
|
|
||||||
_totalWastedBytes += MAX_PACKET_SIZE - packet->size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return packetsSent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Version of voxel distributor that sends the deepest LOD level at once
|
/// Version of voxel distributor that sends the deepest LOD level at once
|
||||||
int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrustumChanged) {
|
int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrustumChanged) {
|
||||||
|
|
||||||
|
@ -311,6 +290,10 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calculate max number of packets that can be sent during this interval
|
||||||
|
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND));
|
||||||
|
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
|
||||||
|
|
||||||
int truePacketsSent = 0;
|
int truePacketsSent = 0;
|
||||||
int trueBytesSent = 0;
|
int trueBytesSent = 0;
|
||||||
int packetsSentThisInterval = 0;
|
int packetsSentThisInterval = 0;
|
||||||
|
@ -408,9 +391,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
||||||
//quint64 startCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000;
|
//quint64 startCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000;
|
||||||
//quint64 startCompressCalls = OctreePacketData::getCompressContentCalls();
|
//quint64 startCompressCalls = OctreePacketData::getCompressContentCalls();
|
||||||
|
|
||||||
int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND));
|
|
||||||
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
|
|
||||||
|
|
||||||
int extraPackingAttempts = 0;
|
int extraPackingAttempts = 0;
|
||||||
bool completedScene = false;
|
bool completedScene = false;
|
||||||
while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval && !nodeData->isShuttingDown()) {
|
while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval && !nodeData->isShuttingDown()) {
|
||||||
|
@ -581,12 +561,26 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
||||||
// send the environment packet
|
// send the environment packet
|
||||||
// TODO: should we turn this into a while loop to better handle sending multiple special packets
|
// TODO: should we turn this into a while loop to better handle sending multiple special packets
|
||||||
if (_myServer->hasSpecialPacketToSend(_node) && !nodeData->isShuttingDown()) {
|
if (_myServer->hasSpecialPacketToSend(_node) && !nodeData->isShuttingDown()) {
|
||||||
trueBytesSent += _myServer->sendSpecialPacket(nodeData, _node);
|
int specialPacketsSent;
|
||||||
|
trueBytesSent += _myServer->sendSpecialPacket(_node, nodeData, specialPacketsSent);
|
||||||
nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed
|
nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed
|
||||||
truePacketsSent++;
|
truePacketsSent += specialPacketsSent;
|
||||||
packetsSentThisInterval++;
|
packetsSentThisInterval += specialPacketsSent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-send packets that were nacked by the client
|
||||||
|
while (nodeData->hasNextNackedPacket() && packetsSentThisInterval < maxPacketsPerInterval) {
|
||||||
|
const QByteArray* packet = nodeData->getNextNackedPacket();
|
||||||
|
if (packet) {
|
||||||
|
NodeList::getInstance()->writeDatagram(*packet, _node);
|
||||||
|
truePacketsSent++;
|
||||||
|
packetsSentThisInterval++;
|
||||||
|
|
||||||
|
_totalBytes += packet->size();
|
||||||
|
_totalPackets++;
|
||||||
|
_totalWastedBytes += MAX_PACKET_SIZE - packet->size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
quint64 end = usecTimestampNow();
|
quint64 end = usecTimestampNow();
|
||||||
int elapsedmsec = (end - start)/USECS_PER_MSEC;
|
int elapsedmsec = (end - start)/USECS_PER_MSEC;
|
||||||
|
|
|
@ -55,9 +55,6 @@ private:
|
||||||
|
|
||||||
int _nodeMissingCount;
|
int _nodeMissingCount;
|
||||||
bool _isShuttingDown;
|
bool _isShuttingDown;
|
||||||
|
|
||||||
int resendNackedPackets(OctreeQueryNode* nodeData);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_OctreeSendThread_h
|
#endif // hifi_OctreeSendThread_h
|
||||||
|
|
|
@ -72,7 +72,7 @@ public:
|
||||||
// subclass may implement these method
|
// subclass may implement these method
|
||||||
virtual void beforeRun() { };
|
virtual void beforeRun() { };
|
||||||
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node) { return false; }
|
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node) { return false; }
|
||||||
virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) { return 0; }
|
virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { return 0; }
|
||||||
|
|
||||||
static void attachQueryNodeToNode(Node* newNode);
|
static void attachQueryNodeToNode(Node* newNode);
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ bool ParticleServer::hasSpecialPacketToSend(const SharedNodePointer& node) {
|
||||||
return shouldSendDeletedParticles;
|
return shouldSendDeletedParticles;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ParticleServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) {
|
int ParticleServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) {
|
||||||
unsigned char outputBuffer[MAX_PACKET_SIZE];
|
unsigned char outputBuffer[MAX_PACKET_SIZE];
|
||||||
size_t packetLength = 0;
|
size_t packetLength = 0;
|
||||||
|
|
||||||
|
@ -99,6 +99,7 @@ int ParticleServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNo
|
||||||
bool hasMoreToSend = true;
|
bool hasMoreToSend = true;
|
||||||
|
|
||||||
// TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 particles?
|
// TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 particles?
|
||||||
|
packetsSent = 0;
|
||||||
while (hasMoreToSend) {
|
while (hasMoreToSend) {
|
||||||
hasMoreToSend = tree->encodeParticlesDeletedSince(queryNode->getSequenceNumber(), deletedParticlesSentAt,
|
hasMoreToSend = tree->encodeParticlesDeletedSince(queryNode->getSequenceNumber(), deletedParticlesSentAt,
|
||||||
outputBuffer, MAX_PACKET_SIZE, packetLength);
|
outputBuffer, MAX_PACKET_SIZE, packetLength);
|
||||||
|
@ -107,6 +108,7 @@ int ParticleServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNo
|
||||||
|
|
||||||
NodeList::getInstance()->writeDatagram((char*) outputBuffer, packetLength, SharedNodePointer(node));
|
NodeList::getInstance()->writeDatagram((char*) outputBuffer, packetLength, SharedNodePointer(node));
|
||||||
queryNode->packetSent(outputBuffer, packetLength);
|
queryNode->packetSent(outputBuffer, packetLength);
|
||||||
|
packetsSent++;
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeData->setLastDeletedParticlesSentAt(deletePacketSentAt);
|
nodeData->setLastDeletedParticlesSentAt(deletePacketSentAt);
|
||||||
|
|
|
@ -37,7 +37,7 @@ public:
|
||||||
// subclass may implement these method
|
// subclass may implement these method
|
||||||
virtual void beforeRun();
|
virtual void beforeRun();
|
||||||
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node);
|
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node);
|
||||||
virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node);
|
virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent);
|
||||||
|
|
||||||
virtual void particleCreated(const Particle& newParticle, const SharedNodePointer& senderNode);
|
virtual void particleCreated(const Particle& newParticle, const SharedNodePointer& senderNode);
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ bool VoxelServer::hasSpecialPacketToSend(const SharedNodePointer& node) {
|
||||||
return shouldSendEnvironments;
|
return shouldSendEnvironments;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VoxelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) {
|
int VoxelServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) {
|
||||||
|
|
||||||
unsigned char* copyAt = _tempOutputBuffer;
|
unsigned char* copyAt = _tempOutputBuffer;
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@ int VoxelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodeP
|
||||||
|
|
||||||
NodeList::getInstance()->writeDatagram((char*) _tempOutputBuffer, envPacketLength, SharedNodePointer(node));
|
NodeList::getInstance()->writeDatagram((char*) _tempOutputBuffer, envPacketLength, SharedNodePointer(node));
|
||||||
queryNode->packetSent(_tempOutputBuffer, envPacketLength);
|
queryNode->packetSent(_tempOutputBuffer, envPacketLength);
|
||||||
|
packetsSent = 1;
|
||||||
|
|
||||||
return envPacketLength;
|
return envPacketLength;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ public:
|
||||||
// subclass may implement these method
|
// subclass may implement these method
|
||||||
virtual void beforeRun();
|
virtual void beforeRun();
|
||||||
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node);
|
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node);
|
||||||
virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node);
|
virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _sendEnvironments;
|
bool _sendEnvironments;
|
||||||
|
|
832
examples/growTrees.js
Normal file
832
examples/growTrees.js
Normal file
|
@ -0,0 +1,832 @@
|
||||||
|
//
|
||||||
|
// growPlants.js
|
||||||
|
// examples
|
||||||
|
//
|
||||||
|
// Created by Benjamin Arnold on May 29, 2014
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This sample script allows the user to grow different types of plants on the voxels
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z fighting
|
||||||
|
var previewLineWidth = 2.0;
|
||||||
|
|
||||||
|
var voxelSize = 1;
|
||||||
|
var windowDimensions = Controller.getViewportDimensions();
|
||||||
|
var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/";
|
||||||
|
|
||||||
|
var MAX_VOXEL_SCALE_POWER = 5;
|
||||||
|
var MIN_VOXEL_SCALE_POWER = -8;
|
||||||
|
var MAX_VOXEL_SCALE = Math.pow(2.0, MAX_VOXEL_SCALE_POWER);
|
||||||
|
var MIN_VOXEL_SCALE = Math.pow(2.0, MIN_VOXEL_SCALE_POWER);
|
||||||
|
|
||||||
|
|
||||||
|
var linePreviewTop = Overlays.addOverlay("line3d", {
|
||||||
|
position: { x: 0, y: 0, z: 0},
|
||||||
|
end: { x: 0, y: 0, z: 0},
|
||||||
|
color: { red: 0, green: 255, blue: 0},
|
||||||
|
alpha: 1,
|
||||||
|
visible: false,
|
||||||
|
lineWidth: previewLineWidth
|
||||||
|
});
|
||||||
|
|
||||||
|
var linePreviewBottom = Overlays.addOverlay("line3d", {
|
||||||
|
position: { x: 0, y: 0, z: 0},
|
||||||
|
end: { x: 0, y: 0, z: 0},
|
||||||
|
color: { red: 0, green: 255, blue: 0},
|
||||||
|
alpha: 1,
|
||||||
|
visible: false,
|
||||||
|
lineWidth: previewLineWidth
|
||||||
|
});
|
||||||
|
|
||||||
|
var linePreviewLeft = Overlays.addOverlay("line3d", {
|
||||||
|
position: { x: 0, y: 0, z: 0},
|
||||||
|
end: { x: 0, y: 0, z: 0},
|
||||||
|
color: { red: 0, green: 255, blue: 0},
|
||||||
|
alpha: 1,
|
||||||
|
visible: false,
|
||||||
|
lineWidth: previewLineWidth
|
||||||
|
});
|
||||||
|
|
||||||
|
var linePreviewRight = Overlays.addOverlay("line3d", {
|
||||||
|
position: { x: 0, y: 0, z: 0},
|
||||||
|
end: { x: 0, y: 0, z: 0},
|
||||||
|
color: { red: 0, green: 255, blue: 0},
|
||||||
|
alpha: 1,
|
||||||
|
visible: false,
|
||||||
|
lineWidth: previewLineWidth
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var UIColor = { red: 0, green: 160, blue: 0};
|
||||||
|
var activeUIColor = { red: 0, green: 255, blue: 0};
|
||||||
|
|
||||||
|
var toolHeight = 50;
|
||||||
|
var toolWidth = 50;
|
||||||
|
|
||||||
|
var editToolsOn = true;
|
||||||
|
|
||||||
|
var voxelToolSelected = false;
|
||||||
|
|
||||||
|
var scaleSelectorWidth = 144;
|
||||||
|
var scaleSelectorHeight = 37;
|
||||||
|
|
||||||
|
var scaleSelectorX = windowDimensions.x / 5.0;
|
||||||
|
var scaleSelectorY = windowDimensions.y - scaleSelectorHeight;
|
||||||
|
|
||||||
|
var voxelTool = Overlays.addOverlay("image", {
|
||||||
|
x: scaleSelectorX + scaleSelectorWidth + 1, y: windowDimensions.y - toolHeight, width: toolWidth, height: toolHeight,
|
||||||
|
subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
|
||||||
|
imageURL: toolIconUrl + "voxel-tool.svg",
|
||||||
|
visible: editToolsOn,
|
||||||
|
color: UIColor,
|
||||||
|
alpha: 0.9
|
||||||
|
});
|
||||||
|
|
||||||
|
var copyScale = true;
|
||||||
|
function ScaleSelector() {
|
||||||
|
this.x = scaleSelectorX;
|
||||||
|
this.y = scaleSelectorY;
|
||||||
|
this.width = scaleSelectorWidth;
|
||||||
|
this.height = scaleSelectorHeight;
|
||||||
|
|
||||||
|
this.displayPower = false;
|
||||||
|
this.scale = 1.0;
|
||||||
|
this.power = 0;
|
||||||
|
|
||||||
|
this.FIRST_PART = this.width * 40.0 / 100.0;
|
||||||
|
this.SECOND_PART = this.width * 37.0 / 100.0;
|
||||||
|
|
||||||
|
this.buttonsOverlay = Overlays.addOverlay("image", {
|
||||||
|
x: this.x, y: this.y,
|
||||||
|
width: this.width, height: this.height,
|
||||||
|
//subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
|
||||||
|
imageURL: toolIconUrl + "voxel-size-selector.svg",
|
||||||
|
alpha: 0.9,
|
||||||
|
visible: editToolsOn,
|
||||||
|
color: activeUIColor
|
||||||
|
});
|
||||||
|
this.textOverlay = Overlays.addOverlay("text", {
|
||||||
|
x: this.x + this.FIRST_PART, y: this.y,
|
||||||
|
width: this.SECOND_PART, height: this.height,
|
||||||
|
topMargin: 13,
|
||||||
|
text: this.scale.toString(),
|
||||||
|
alpha: 0.0,
|
||||||
|
visible: editToolsOn,
|
||||||
|
color: activeUIColor
|
||||||
|
});
|
||||||
|
this.powerOverlay = Overlays.addOverlay("text", {
|
||||||
|
x: this.x + this.FIRST_PART, y: this.y,
|
||||||
|
width: this.SECOND_PART, height: this.height,
|
||||||
|
leftMargin: 28,
|
||||||
|
text: this.power.toString(),
|
||||||
|
alpha: 0.0,
|
||||||
|
visible: false,
|
||||||
|
color: activeUIColor
|
||||||
|
});
|
||||||
|
this.setScale = function(scale) {
|
||||||
|
if (scale > MAX_VOXEL_SCALE) {
|
||||||
|
scale = MAX_VOXEL_SCALE;
|
||||||
|
}
|
||||||
|
if (scale < MIN_VOXEL_SCALE) {
|
||||||
|
scale = MIN_VOXEL_SCALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scale = scale;
|
||||||
|
this.power = Math.floor(Math.log(scale) / Math.log(2));
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.show = function(doShow) {
|
||||||
|
Overlays.editOverlay(this.buttonsOverlay, {visible: doShow});
|
||||||
|
Overlays.editOverlay(this.textOverlay, {visible: doShow});
|
||||||
|
Overlays.editOverlay(this.powerOverlay, {visible: doShow && this.displayPower});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.move = function() {
|
||||||
|
this.x = swatchesX + swatchesWidth;
|
||||||
|
this.y = swatchesY;
|
||||||
|
|
||||||
|
Overlays.editOverlay(this.buttonsOverlay, {
|
||||||
|
x: this.x, y: this.y,
|
||||||
|
});
|
||||||
|
Overlays.editOverlay(this.textOverlay, {
|
||||||
|
x: this.x + this.FIRST_PART, y: this.y,
|
||||||
|
});
|
||||||
|
Overlays.editOverlay(this.powerOverlay, {
|
||||||
|
x: this.x + this.FIRST_PART, y: this.y,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.switchDisplay = function() {
|
||||||
|
this.displayPower = !this.displayPower;
|
||||||
|
|
||||||
|
if (this.displayPower) {
|
||||||
|
Overlays.editOverlay(this.textOverlay, {
|
||||||
|
leftMargin: 18,
|
||||||
|
text: "2"
|
||||||
|
});
|
||||||
|
Overlays.editOverlay(this.powerOverlay, {
|
||||||
|
text: this.power.toString(),
|
||||||
|
visible: editToolsOn
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Overlays.editOverlay(this.textOverlay, {
|
||||||
|
leftMargin: 13,
|
||||||
|
text: this.scale.toString()
|
||||||
|
});
|
||||||
|
Overlays.editOverlay(this.powerOverlay, {
|
||||||
|
visible: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.update = function() {
|
||||||
|
if (this.displayPower) {
|
||||||
|
Overlays.editOverlay(this.powerOverlay, {text: this.power.toString()});
|
||||||
|
} else {
|
||||||
|
Overlays.editOverlay(this.textOverlay, {text: this.scale.toString()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.incrementScale = function() {
|
||||||
|
copyScale = false;
|
||||||
|
if (this.power < MAX_VOXEL_SCALE_POWER) {
|
||||||
|
++this.power;
|
||||||
|
this.scale *= 2.0;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.decrementScale = function() {
|
||||||
|
copyScale = false;
|
||||||
|
if (MIN_VOXEL_SCALE_POWER < this.power) {
|
||||||
|
--this.power;
|
||||||
|
this.scale /= 2.0;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clicked = function(x, y) {
|
||||||
|
if (this.x < x && x < this.x + this.width &&
|
||||||
|
this.y < y && y < this.y + this.height) {
|
||||||
|
|
||||||
|
if (x < this.x + this.FIRST_PART) {
|
||||||
|
this.decrementScale();
|
||||||
|
} else if (x < this.x + this.FIRST_PART + this.SECOND_PART) {
|
||||||
|
this.switchDisplay();
|
||||||
|
} else {
|
||||||
|
this.incrementScale();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cleanup = function() {
|
||||||
|
Overlays.deleteOverlay(this.buttonsOverlay);
|
||||||
|
Overlays.deleteOverlay(this.textOverlay);
|
||||||
|
Overlays.deleteOverlay(this.powerOverlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
var scaleSelector = new ScaleSelector();
|
||||||
|
|
||||||
|
|
||||||
|
function calculateVoxelFromIntersection(intersection, operation) {
|
||||||
|
|
||||||
|
var resultVoxel;
|
||||||
|
|
||||||
|
var x;
|
||||||
|
var y;
|
||||||
|
var z;
|
||||||
|
|
||||||
|
// if our "target voxel size" is larger than the voxel we intersected with, then we need to find the closest
|
||||||
|
// ancestor voxel of our target size that contains our intersected voxel.
|
||||||
|
if (voxelSize > intersection.voxel.s) {
|
||||||
|
x = Math.floor(intersection.voxel.x / voxelSize) * voxelSize;
|
||||||
|
y = Math.floor(intersection.voxel.y / voxelSize) * voxelSize;
|
||||||
|
z = Math.floor(intersection.voxel.z / voxelSize) * voxelSize;
|
||||||
|
} else {
|
||||||
|
// otherwise, calculate the enclosed voxel of size voxelSize that the intersection point falls inside of.
|
||||||
|
// if you have a voxelSize that's smaller than the voxel you're intersecting, this calculation will result
|
||||||
|
// in the subvoxel that the intersection point falls in, if the target voxelSize matches the intersecting
|
||||||
|
// voxel this still works and results in returning the intersecting voxel which is what we want
|
||||||
|
var adjustToCenter = Vec3.multiply(Voxels.getFaceVector(intersection.face), (voxelSize * -0.5));
|
||||||
|
|
||||||
|
var centerOfIntersectingVoxel = Vec3.sum(intersection.intersection, adjustToCenter);
|
||||||
|
x = Math.floor(centerOfIntersectingVoxel.x / voxelSize) * voxelSize;
|
||||||
|
y = Math.floor(centerOfIntersectingVoxel.y / voxelSize) * voxelSize;
|
||||||
|
z = Math.floor(centerOfIntersectingVoxel.z / voxelSize) * voxelSize;
|
||||||
|
}
|
||||||
|
resultVoxel = { x: x, y: y, z: z, s: voxelSize };
|
||||||
|
var highlightAt = { x: x, y: y, z: z, s: voxelSize };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// we only do the "add to the face we're pointing at" adjustment, if the operation is an add
|
||||||
|
// operation, and the target voxel size is equal to or smaller than the intersecting voxel.
|
||||||
|
var wantAddAdjust = (operation == "add" && (voxelSize <= intersection.voxel.s));
|
||||||
|
|
||||||
|
// now we also want to calculate the "edge square" for the face for this voxel
|
||||||
|
if (intersection.face == "MIN_X_FACE") {
|
||||||
|
|
||||||
|
highlightAt.x = x - zFightingSizeAdjust;
|
||||||
|
if (wantAddAdjust) {
|
||||||
|
resultVoxel.x -= voxelSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust };
|
||||||
|
resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust };
|
||||||
|
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust };
|
||||||
|
resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust };
|
||||||
|
|
||||||
|
} else if (intersection.face == "MAX_X_FACE") {
|
||||||
|
|
||||||
|
highlightAt.x = x + voxelSize + zFightingSizeAdjust;
|
||||||
|
if (wantAddAdjust) {
|
||||||
|
resultVoxel.x += resultVoxel.s;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust };
|
||||||
|
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust };
|
||||||
|
resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust };
|
||||||
|
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust };
|
||||||
|
|
||||||
|
} else if (intersection.face == "MIN_Y_FACE") {
|
||||||
|
|
||||||
|
highlightAt.y = y - zFightingSizeAdjust;
|
||||||
|
if (wantAddAdjust) {
|
||||||
|
resultVoxel.y -= voxelSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust };
|
||||||
|
resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust };
|
||||||
|
resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust };
|
||||||
|
resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust };
|
||||||
|
|
||||||
|
} else if (intersection.face == "MAX_Y_FACE") {
|
||||||
|
|
||||||
|
highlightAt.y = y + voxelSize + zFightingSizeAdjust;
|
||||||
|
if (wantAddAdjust) {
|
||||||
|
resultVoxel.y += voxelSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust };
|
||||||
|
resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust};
|
||||||
|
resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust};
|
||||||
|
resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust};
|
||||||
|
|
||||||
|
} else if (intersection.face == "MIN_Z_FACE") {
|
||||||
|
|
||||||
|
highlightAt.z = z - zFightingSizeAdjust;
|
||||||
|
if (wantAddAdjust) {
|
||||||
|
resultVoxel.z -= voxelSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z };
|
||||||
|
resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z};
|
||||||
|
resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z };
|
||||||
|
resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z};
|
||||||
|
|
||||||
|
} else if (intersection.face == "MAX_Z_FACE") {
|
||||||
|
|
||||||
|
highlightAt.z = z + voxelSize + zFightingSizeAdjust;
|
||||||
|
if (wantAddAdjust) {
|
||||||
|
resultVoxel.z += voxelSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultVoxel.bottomLeft = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z };
|
||||||
|
resultVoxel.bottomRight = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z};
|
||||||
|
resultVoxel.topLeft = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z };
|
||||||
|
resultVoxel.topRight = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z};
|
||||||
|
|
||||||
|
}
|
||||||
|
return resultVoxel;
|
||||||
|
}
|
||||||
|
|
||||||
|
var trackLastMouseX = 0;
|
||||||
|
var trackLastMouseY = 0;
|
||||||
|
|
||||||
|
function showPreviewLines() {
|
||||||
|
|
||||||
|
var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY);
|
||||||
|
|
||||||
|
var intersection = Voxels.findRayIntersection(pickRay);
|
||||||
|
|
||||||
|
if (intersection.intersects) {
|
||||||
|
var resultVoxel = calculateVoxelFromIntersection(intersection, "");
|
||||||
|
Overlays.editOverlay(linePreviewTop, { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true });
|
||||||
|
Overlays.editOverlay(linePreviewBottom, { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true });
|
||||||
|
Overlays.editOverlay(linePreviewLeft, { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true });
|
||||||
|
Overlays.editOverlay(linePreviewRight, { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true });
|
||||||
|
} else {
|
||||||
|
Overlays.editOverlay(linePreviewTop, { visible: false });
|
||||||
|
Overlays.editOverlay(linePreviewBottom, { visible: false });
|
||||||
|
Overlays.editOverlay(linePreviewLeft, { visible: false });
|
||||||
|
Overlays.editOverlay(linePreviewRight, { visible: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseMoveEvent(event) {
|
||||||
|
trackLastMouseX = event.x;
|
||||||
|
trackLastMouseY = event.y;
|
||||||
|
if (!voxelToolSelected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showPreviewLines();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Array of possible trees, right now there is only one
|
||||||
|
var treeTypes = [];
|
||||||
|
|
||||||
|
treeTypes.push({
|
||||||
|
name: "Tall Green",
|
||||||
|
// Voxel Colors
|
||||||
|
wood: { r: 133, g: 81, b: 53 },
|
||||||
|
leaves: { r: 22, g: 83, b: 31 },
|
||||||
|
|
||||||
|
// How tall the tree is
|
||||||
|
height: { min: 20, max: 60 },
|
||||||
|
middleHeight: 0.3,
|
||||||
|
|
||||||
|
// Chance of making a branch
|
||||||
|
branchChance: { min: 0.01, max: 0.1 },
|
||||||
|
branchLength: { min: 30, max: 60 },
|
||||||
|
branchThickness: { min: 2, max: 7},
|
||||||
|
|
||||||
|
// The width of the core, affects width and shape
|
||||||
|
coreWidth: { min: 1, max: 4 },
|
||||||
|
|
||||||
|
//TODO: Make this quadratic splines instead of linear
|
||||||
|
bottomThickness: { min: 2, max: 8 },
|
||||||
|
middleThickness: { min: 1, max: 4 },
|
||||||
|
topThickness: { min: 3, max: 6 },
|
||||||
|
|
||||||
|
//Modifies leaves at top
|
||||||
|
leafCapSizeOffset: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// Applies noise to color
|
||||||
|
var colorNoiseRange = 0.2;
|
||||||
|
|
||||||
|
|
||||||
|
// Useful constants
|
||||||
|
var LEFT = 0;
|
||||||
|
var BACK = 1;
|
||||||
|
var RIGHT = 2;
|
||||||
|
var FRONT = 3;
|
||||||
|
var UP = 4;
|
||||||
|
|
||||||
|
// Interpolates between min and max of treevar based on b
|
||||||
|
function interpolate(treeVar, b) {
|
||||||
|
return (treeVar.min + (treeVar.max - treeVar.min) * b);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeBranch(x, y, z, step, length, dir, thickness, wood, leaves) {
|
||||||
|
var moveDir;
|
||||||
|
|
||||||
|
var currentThickness;
|
||||||
|
//thickness attenuates to thickness - 3
|
||||||
|
var finalThickness = thickness - 3;
|
||||||
|
if (finalThickness < 1) {
|
||||||
|
finalThickness = 1;
|
||||||
|
}
|
||||||
|
//Iterative branch generation
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
//If we are at the end, place a ball of leaves
|
||||||
|
if (step == 0) {
|
||||||
|
makeSphere(x, y, z, 2 + finalThickness, leaves);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//thickness attenuation
|
||||||
|
currentThickness = Math.round((finalThickness + (thickness - finalThickness) * (step/length))) - 1;
|
||||||
|
|
||||||
|
|
||||||
|
// If the branch is thick, grow a vertical slice
|
||||||
|
if (currentThickness > 0) {
|
||||||
|
for (var i = -currentThickness; i <= currentThickness; i++) {
|
||||||
|
var len = currentThickness - Math.abs(i);
|
||||||
|
switch (dir) {
|
||||||
|
case 0: //left
|
||||||
|
case 2: //right
|
||||||
|
growInDirection(x, y + i * voxelSize, z, len, len, BACK, wood, false, true);
|
||||||
|
growInDirection(x, y + i * voxelSize, z, len, len, FRONT, wood, false, false)
|
||||||
|
break;
|
||||||
|
case 1: //back
|
||||||
|
case 3: //front
|
||||||
|
growInDirection(x, y + i * voxelSize, z, len, len, LEFT, wood, false, true);
|
||||||
|
growInDirection(x, y + i * voxelSize, z, len, len, RIGHT, wood, false, false)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Otherwise place a single voxel
|
||||||
|
var colorNoise = (colorNoiseRange * Math.random() - colorNoiseRange * 0.5) + 1.0;
|
||||||
|
Voxels.setVoxel(x, y, z, voxelSize, wood.r * colorNoise, wood.g * colorNoise, wood.b * colorNoise);
|
||||||
|
}
|
||||||
|
|
||||||
|
// determines random change in direction for branch
|
||||||
|
var r = Math.floor(Math.random() * 9);
|
||||||
|
|
||||||
|
|
||||||
|
if (r >= 6){
|
||||||
|
moveDir = dir; //in same direction
|
||||||
|
} else if (r >= 4) {
|
||||||
|
moveDir = UP; //up
|
||||||
|
}
|
||||||
|
else if (dir == LEFT){
|
||||||
|
if (r >= 2){
|
||||||
|
moveDir = FRONT;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
moveDir = BACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dir == BACK){
|
||||||
|
if (r >= 2){
|
||||||
|
moveDir = LEFT;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
moveDir = RIGHT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dir == RIGHT){
|
||||||
|
if (r >= 2){
|
||||||
|
moveDir = BACK;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
moveDir = FRONT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dir == FRONT){
|
||||||
|
if (r >= 2){
|
||||||
|
moveDir = RIGHT;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
moveDir = LEFT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Move the branch by moveDir
|
||||||
|
switch (moveDir) {
|
||||||
|
case 0: //left
|
||||||
|
x = x - voxelSize;
|
||||||
|
break;
|
||||||
|
case 1: //back
|
||||||
|
z = z - voxelSize;
|
||||||
|
break;
|
||||||
|
case 2: //right
|
||||||
|
x = x + voxelSize;
|
||||||
|
break;
|
||||||
|
case 3: //front
|
||||||
|
z = z + voxelSize;
|
||||||
|
break;
|
||||||
|
case 4: //up
|
||||||
|
y = y + voxelSize;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
step--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Places a sphere of voxels
|
||||||
|
function makeSphere(x, y, z, radius, color) {
|
||||||
|
if (radius <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var width = radius * 2 + 1;
|
||||||
|
var distance;
|
||||||
|
|
||||||
|
for (var i = -radius; i <= radius; i++){
|
||||||
|
for (var j = -radius; j <= radius; j++){
|
||||||
|
for (var k = -radius; k <= radius; k++){
|
||||||
|
distance = Math.sqrt(i * i + j * j + k * k);
|
||||||
|
if (distance <= radius){
|
||||||
|
var colorNoise = (colorNoiseRange * Math.random() - colorNoiseRange * 0.5) + 1.0;
|
||||||
|
Voxels.setVoxel(x + i * voxelSize, y + j * voxelSize, z + k * voxelSize, voxelSize, color.r * colorNoise, color.g * colorNoise, color.b * colorNoise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function growInDirection(x, y, z, step, length, dir, color, isSideBranching, addVoxel) {
|
||||||
|
|
||||||
|
|
||||||
|
if (addVoxel == true) {
|
||||||
|
var colorNoise = (colorNoiseRange * Math.random() - colorNoiseRange * 0.5) + 1.0;
|
||||||
|
Voxels.setVoxel(x, y, z, voxelSize, color.r * colorNoise, color.g * colorNoise, color.b * colorNoise);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a main vein, it will branch outward perpendicular to its motion
|
||||||
|
if (isSideBranching == true){
|
||||||
|
var step2;
|
||||||
|
if (step >= length - 1){
|
||||||
|
step2 = length;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
step2 = step + 1;
|
||||||
|
}
|
||||||
|
growInDirection(x, y, z, step, length, BACK, color, false, false);
|
||||||
|
growInDirection(x, y, z, step, length, FRONT, color, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step < 1) return;
|
||||||
|
|
||||||
|
// Recursively move in the direction
|
||||||
|
if (dir == LEFT) { //left
|
||||||
|
growInDirection(x - voxelSize, y, z, step - 1, length, dir, color, isSideBranching, true);
|
||||||
|
}
|
||||||
|
else if (dir == BACK) { //back
|
||||||
|
growInDirection(x, y, z - voxelSize, step - 1, length, dir, color, isSideBranching, true);
|
||||||
|
}
|
||||||
|
else if (dir == RIGHT) { //right
|
||||||
|
growInDirection(x + voxelSize, y, z, step - 1, length, dir, color, isSideBranching, true);
|
||||||
|
}
|
||||||
|
else if (dir == FRONT) {//front
|
||||||
|
growInDirection(x, y, z + voxelSize, step - 1, length, dir, color, isSideBranching, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grows the thickness of the tree
|
||||||
|
function growHorizontalSlice(x, y, z, thickness, color, side) {
|
||||||
|
// The side variable determines which directions we should grow in
|
||||||
|
// it is an optimization that prevents us from visiting voxels multiple
|
||||||
|
// times for trees with a coreWidth > 1
|
||||||
|
|
||||||
|
// side:
|
||||||
|
// 8 == all directions
|
||||||
|
// 0 1 2
|
||||||
|
// 3 -1 4
|
||||||
|
// 5 6 7
|
||||||
|
|
||||||
|
Voxels.setVoxel(x, y, z, voxelSize, color.r, color.g, color.b);
|
||||||
|
|
||||||
|
//We are done if there is no thickness
|
||||||
|
if (thickness == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
switch (side) {
|
||||||
|
case 0:
|
||||||
|
growInDirection(x, y, z, thickness, thickness, LEFT, color, true, false);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
growInDirection(x, y, z, thickness, thickness, BACK, color, false, false);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
growInDirection(x, y, z, thickness, thickness, RIGHT, color, true, false);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
growInDirection(x, y, z, thickness, thickness, LEFT, color, false, false);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
growInDirection(x, y, z, thickness, thickness, BACK, color, false, false);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
growInDirection(x, y, z, thickness, thickness, RIGHT, color, true, false);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
growInDirection(x, y, z, thickness, thickness, FRONT, color, false, false);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
growInDirection(x, y, z, thickness, thickness, RIGHT, color, true, false);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
if (thickness > 1){
|
||||||
|
growInDirection(x, y, z, thickness, thickness, LEFT, color, true, false);
|
||||||
|
growInDirection(x, y, z, thickness, thickness, RIGHT, color, true, false)
|
||||||
|
} else if (thickness == 1){
|
||||||
|
Voxels.setVoxel(x - voxelSize, y, z, voxelSize, color.r, color.g, color.b);
|
||||||
|
Voxels.setVoxel(x + voxelSize, y, z, voxelSize, color.r, color.g, color.b);
|
||||||
|
Voxels.setVoxel(x, y, z - voxelSize, voxelSize, color.r, color.g, color.b);
|
||||||
|
Voxels.setVoxel(x, y, z + voxelSize, voxelSize, color.r, color.g, color.b);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function computeSide(x, z, coreWidth) {
|
||||||
|
// side:
|
||||||
|
// 8 == all directions
|
||||||
|
// 0 1 2
|
||||||
|
// 3 -1 4
|
||||||
|
// 5 6 7
|
||||||
|
|
||||||
|
// if the core is only a single block, we can grow out in all directions
|
||||||
|
if (coreWidth == 1){
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back face
|
||||||
|
if (z == 0) {
|
||||||
|
if (x == 0) {
|
||||||
|
return 0;
|
||||||
|
} else if (x == coreWidth - 1) {
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Front face
|
||||||
|
if (z == (coreWidth - 1)) {
|
||||||
|
if (x == 0) {
|
||||||
|
return 5;
|
||||||
|
} else if (x == (coreWidth - 1)) {
|
||||||
|
return 7;
|
||||||
|
} else {
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left face
|
||||||
|
if (x == 0) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right face
|
||||||
|
if (x == (coreWidth - 1)) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Interior
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function growTree(x, y, z, tree) {
|
||||||
|
|
||||||
|
// The size of the tree, from 0-1
|
||||||
|
var treeSize = Math.random();
|
||||||
|
|
||||||
|
// Get tree properties by interpolating with the treeSize
|
||||||
|
var height = interpolate(tree.height, treeSize);
|
||||||
|
var baseHeight = Math.ceil(tree.middleHeight * height);
|
||||||
|
var bottomThickness = interpolate(tree.bottomThickness, treeSize);
|
||||||
|
var middleThickness = interpolate(tree.middleThickness, treeSize);
|
||||||
|
var topThickness = interpolate(tree.topThickness, treeSize);
|
||||||
|
var coreWidth = Math.ceil(interpolate(tree.coreWidth, treeSize));
|
||||||
|
|
||||||
|
var thickness;
|
||||||
|
var side;
|
||||||
|
|
||||||
|
//Loop upwards through each slice of the tree
|
||||||
|
for (var i = 0; i < height; i++){
|
||||||
|
|
||||||
|
//Branch properties are based on current height as well as the overall tree size
|
||||||
|
var branchChance = interpolate(tree.branchChance, i / height);
|
||||||
|
var branchLength = Math.ceil(interpolate(tree.branchLength, (i / height) * treeSize));
|
||||||
|
var branchThickness = Math.round(interpolate(tree.branchThickness, (i / height) * treeSize));
|
||||||
|
|
||||||
|
// Get the "thickness" of the tree by doing linear interpolation between the middle thickness
|
||||||
|
// and the top and bottom thickness.
|
||||||
|
if (i <= baseHeight && baseHeight != 0){
|
||||||
|
thickness = (i / (baseHeight) * (middleThickness - bottomThickness) + bottomThickness);
|
||||||
|
} else {
|
||||||
|
var denom = ((height - baseHeight)) * (topThickness - middleThickness) + middleThickness;
|
||||||
|
if (denom != 0) {
|
||||||
|
thickness = (i - baseHeight) / denom;
|
||||||
|
} else {
|
||||||
|
thickness = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The core of the tree is a vertical rectangular prism through the middle of the tree
|
||||||
|
|
||||||
|
//Loop through the "core", which helps shape the trunk
|
||||||
|
var startX = x - Math.floor(coreWidth / 2) * voxelSize;
|
||||||
|
var startZ = z - Math.floor(coreWidth / 2) * voxelSize;
|
||||||
|
for (var j = 0; j < coreWidth; j++) {
|
||||||
|
for (var k = 0; k < coreWidth; k++) {
|
||||||
|
//determine which side of the tree we are on
|
||||||
|
side = computeSide(j, k, coreWidth);
|
||||||
|
//grow a horizontal slice of the tree
|
||||||
|
growHorizontalSlice(startX + j * voxelSize, y + i * voxelSize, startZ + k * voxelSize, Math.floor(thickness), tree.wood, side);
|
||||||
|
|
||||||
|
// Branches
|
||||||
|
if (side != -1) {
|
||||||
|
var r = Math.random();
|
||||||
|
if (r <= branchChance){
|
||||||
|
var dir = Math.floor((Math.random() * 4));
|
||||||
|
makeBranch(startX + j * voxelSize, y + i * voxelSize, startZ + k * voxelSize, branchLength, branchLength, dir, branchThickness, tree.wood, tree.leaves);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
makeSphere(x, y + height * voxelSize, z, topThickness + coreWidth + tree.leafCapSizeOffset, tree.leaves);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mousePressEvent(event) {
|
||||||
|
var mouseX = event.x;
|
||||||
|
var mouseY = event.y;
|
||||||
|
|
||||||
|
var clickedOnSomething = false;
|
||||||
|
// Check if we clicked an overlay
|
||||||
|
var clickedOverlay = Overlays.getOverlayAtPoint({x: mouseX, y: mouseY});
|
||||||
|
|
||||||
|
if (clickedOverlay == voxelTool) {
|
||||||
|
voxelToolSelected = !voxelToolSelected;
|
||||||
|
|
||||||
|
if (voxelToolSelected == true) {
|
||||||
|
Overlays.editOverlay(voxelTool, {
|
||||||
|
color: activeUIColor
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Overlays.editOverlay(voxelTool, {
|
||||||
|
color: UIColor
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clickedOnSomething = true;
|
||||||
|
} else if (scaleSelector.clicked(event.x, event.y)) {
|
||||||
|
clickedOnSomething = true;
|
||||||
|
voxelSize = scaleSelector.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if we clicked on the UI or the voxel tool is disabled
|
||||||
|
if (clickedOnSomething || !voxelToolSelected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the picking ray for the click
|
||||||
|
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||||
|
var intersection = Voxels.findRayIntersection(pickRay);
|
||||||
|
var resultVoxel = calculateVoxelFromIntersection(intersection, "add");
|
||||||
|
|
||||||
|
// Currently not in use, could randomly select a tree
|
||||||
|
var treeIndex = Math.floor(Math.random() * treeTypes.length);
|
||||||
|
|
||||||
|
// Grow the first tree type
|
||||||
|
growTree(resultVoxel.x, resultVoxel.y, resultVoxel.z, treeTypes[0]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function scriptEnding() {
|
||||||
|
Overlays.deleteOverlay(linePreviewTop);
|
||||||
|
Overlays.deleteOverlay(linePreviewBottom);
|
||||||
|
Overlays.deleteOverlay(linePreviewLeft);
|
||||||
|
Overlays.deleteOverlay(linePreviewRight);
|
||||||
|
scaleSelector.cleanup();
|
||||||
|
Overlays.deleteOverlay(voxelTool);
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller.mousePressEvent.connect(mousePressEvent);
|
||||||
|
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
|
|
||||||
|
Voxels.setPacketsPerSecond(10000);
|
|
@ -159,7 +159,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
_mousePressed(false),
|
_mousePressed(false),
|
||||||
_audio(STARTUP_JITTER_SAMPLES),
|
_audio(STARTUP_JITTER_SAMPLES),
|
||||||
_enableProcessVoxelsThread(true),
|
_enableProcessVoxelsThread(true),
|
||||||
_voxelProcessor(),
|
_octreeProcessor(),
|
||||||
_voxelHideShowThread(&_voxels),
|
_voxelHideShowThread(&_voxels),
|
||||||
_packetsPerSecond(0),
|
_packetsPerSecond(0),
|
||||||
_bytesPerSecond(0),
|
_bytesPerSecond(0),
|
||||||
|
@ -419,7 +419,7 @@ Application::~Application() {
|
||||||
_audio.thread()->quit();
|
_audio.thread()->quit();
|
||||||
_audio.thread()->wait();
|
_audio.thread()->wait();
|
||||||
|
|
||||||
_voxelProcessor.terminate();
|
_octreeProcessor.terminate();
|
||||||
_voxelHideShowThread.terminate();
|
_voxelHideShowThread.terminate();
|
||||||
_voxelEditSender.terminate();
|
_voxelEditSender.terminate();
|
||||||
_particleEditSender.terminate();
|
_particleEditSender.terminate();
|
||||||
|
@ -518,7 +518,7 @@ void Application::initializeGL() {
|
||||||
qDebug( "init() complete.");
|
qDebug( "init() complete.");
|
||||||
|
|
||||||
// create thread for parsing of voxel data independent of the main network and rendering threads
|
// create thread for parsing of voxel data independent of the main network and rendering threads
|
||||||
_voxelProcessor.initialize(_enableProcessVoxelsThread);
|
_octreeProcessor.initialize(_enableProcessVoxelsThread);
|
||||||
_voxelEditSender.initialize(_enableProcessVoxelsThread);
|
_voxelEditSender.initialize(_enableProcessVoxelsThread);
|
||||||
_voxelHideShowThread.initialize(_enableProcessVoxelsThread);
|
_voxelHideShowThread.initialize(_enableProcessVoxelsThread);
|
||||||
_particleEditSender.initialize(_enableProcessVoxelsThread);
|
_particleEditSender.initialize(_enableProcessVoxelsThread);
|
||||||
|
@ -1090,6 +1090,13 @@ void Application::focusOutEvent(QFocusEvent* event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::mouseMoveEvent(QMouseEvent* event) {
|
void Application::mouseMoveEvent(QMouseEvent* event) {
|
||||||
|
|
||||||
|
bool showMouse = true;
|
||||||
|
// If this mouse move event is emitted by a controller, dont show the mouse cursor
|
||||||
|
if (event->type() == CONTROLLER_MOVE_EVENT) {
|
||||||
|
showMouse = false;
|
||||||
|
}
|
||||||
|
|
||||||
_controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts
|
_controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts
|
||||||
|
|
||||||
// if one of our scripts have asked to capture this event, then stop processing it
|
// if one of our scripts have asked to capture this event, then stop processing it
|
||||||
|
@ -1097,10 +1104,9 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_lastMouseMove = usecTimestampNow();
|
_lastMouseMove = usecTimestampNow();
|
||||||
|
|
||||||
if (_mouseHidden && !OculusManager::isConnected()) {
|
if (_mouseHidden && showMouse && !OculusManager::isConnected()) {
|
||||||
getGLWidget()->setCursor(Qt::ArrowCursor);
|
getGLWidget()->setCursor(Qt::ArrowCursor);
|
||||||
_mouseHidden = false;
|
_mouseHidden = false;
|
||||||
_seenMouseMove = true;
|
_seenMouseMove = true;
|
||||||
|
@ -1885,7 +1891,7 @@ void Application::updateThreads(float deltaTime) {
|
||||||
|
|
||||||
// parse voxel packets
|
// parse voxel packets
|
||||||
if (!_enableProcessVoxelsThread) {
|
if (!_enableProcessVoxelsThread) {
|
||||||
_voxelProcessor.threadRoutine();
|
_octreeProcessor.threadRoutine();
|
||||||
_voxelHideShowThread.threadRoutine();
|
_voxelHideShowThread.threadRoutine();
|
||||||
_voxelEditSender.threadRoutine();
|
_voxelEditSender.threadRoutine();
|
||||||
_particleEditSender.threadRoutine();
|
_particleEditSender.threadRoutine();
|
||||||
|
@ -2096,11 +2102,11 @@ void Application::updateMyAvatar(float deltaTime) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sent a nack packet containing missing sequence numbers of received packets
|
// sent nack packets containing missing sequence numbers of received packets from nodes
|
||||||
{
|
{
|
||||||
quint64 now = usecTimestampNow();
|
quint64 now = usecTimestampNow();
|
||||||
quint64 sinceLastNack = now - _lastNackTime;
|
quint64 sinceLastNack = now - _lastNackTime;
|
||||||
const quint64 TOO_LONG_SINCE_LAST_NACK = 250 * MSECS_PER_SECOND;
|
const quint64 TOO_LONG_SINCE_LAST_NACK = 1 * USECS_PER_SECOND;
|
||||||
if (sinceLastNack > TOO_LONG_SINCE_LAST_NACK) {
|
if (sinceLastNack > TOO_LONG_SINCE_LAST_NACK) {
|
||||||
_lastNackTime = now;
|
_lastNackTime = now;
|
||||||
sendNack();
|
sendNack();
|
||||||
|
@ -2110,6 +2116,10 @@ void Application::updateMyAvatar(float deltaTime) {
|
||||||
|
|
||||||
void Application::sendNack() {
|
void Application::sendNack() {
|
||||||
|
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
char packet[MAX_PACKET_SIZE];
|
char packet[MAX_PACKET_SIZE];
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
|
@ -2122,6 +2132,12 @@ void Application::sendNack() {
|
||||||
|| node->getType() == NodeType::ModelServer)
|
|| node->getType() == NodeType::ModelServer)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
// if there are octree packets from this node that are waiting to be processed,
|
||||||
|
// don't send a NACK since the missing packets may be among those waiting packets.
|
||||||
|
if (_octreeProcessor.hasPacketsToProcessFrom(node)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
QUuid nodeUUID = node->getUUID();
|
QUuid nodeUUID = node->getUUID();
|
||||||
|
|
||||||
_octreeSceneStatsLock.lockForRead();
|
_octreeSceneStatsLock.lockForRead();
|
||||||
|
@ -3239,6 +3255,11 @@ void Application::nodeAdded(SharedNodePointer node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::nodeKilled(SharedNodePointer node) {
|
void Application::nodeKilled(SharedNodePointer node) {
|
||||||
|
|
||||||
|
// this is here because connecting NodeList::nodeKilled to OctreePacketProcessor::nodeKilled doesn't work:
|
||||||
|
// OctreePacketProcessor::nodeKilled is not called when NodeList::nodeKilled is emitted for some reason.
|
||||||
|
_octreeProcessor.nodeKilled(node);
|
||||||
|
|
||||||
if (node->getType() == NodeType::VoxelServer) {
|
if (node->getType() == NodeType::VoxelServer) {
|
||||||
QUuid nodeUUID = node->getUUID();
|
QUuid nodeUUID = node->getUUID();
|
||||||
// see if this is the first we've heard of this node...
|
// see if this is the first we've heard of this node...
|
||||||
|
@ -3484,8 +3505,10 @@ void Application::saveScripts() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScriptFromEditor) {
|
ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScriptFromEditor) {
|
||||||
if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptName) && !_scriptEnginesHash[scriptName]->isFinished()){
|
QUrl scriptUrl(scriptName);
|
||||||
return _scriptEnginesHash[scriptName];
|
const QString& scriptURLString = scriptUrl.toString();
|
||||||
|
if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptURLString) && !_scriptEnginesHash[scriptURLString]->isFinished()){
|
||||||
|
return _scriptEnginesHash[scriptURLString];
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptEngine* scriptEngine;
|
ScriptEngine* scriptEngine;
|
||||||
|
@ -3493,9 +3516,8 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript
|
||||||
scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface);
|
scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface);
|
||||||
} else {
|
} else {
|
||||||
// start the script on a new thread...
|
// start the script on a new thread...
|
||||||
QUrl scriptUrl(scriptName);
|
|
||||||
scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface);
|
scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface);
|
||||||
_scriptEnginesHash.insert(scriptName, scriptEngine);
|
_scriptEnginesHash.insert(scriptURLString, scriptEngine);
|
||||||
|
|
||||||
if (!scriptEngine->hasScript()) {
|
if (!scriptEngine->hasScript()) {
|
||||||
qDebug() << "Application::loadScript(), script failed to load...";
|
qDebug() << "Application::loadScript(), script failed to load...";
|
||||||
|
|
|
@ -88,7 +88,7 @@
|
||||||
#include "voxels/VoxelFade.h"
|
#include "voxels/VoxelFade.h"
|
||||||
#include "voxels/VoxelHideShowThread.h"
|
#include "voxels/VoxelHideShowThread.h"
|
||||||
#include "voxels/VoxelImporter.h"
|
#include "voxels/VoxelImporter.h"
|
||||||
#include "voxels/VoxelPacketProcessor.h"
|
#include "voxels/OctreePacketProcessor.h"
|
||||||
#include "voxels/VoxelSystem.h"
|
#include "voxels/VoxelSystem.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ static const float MIRROR_FIELD_OF_VIEW = 30.0f;
|
||||||
class Application : public QApplication {
|
class Application : public QApplication {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
friend class VoxelPacketProcessor;
|
friend class OctreePacketProcessor;
|
||||||
friend class VoxelEditPacketSender;
|
friend class VoxelEditPacketSender;
|
||||||
friend class DatagramProcessor;
|
friend class DatagramProcessor;
|
||||||
|
|
||||||
|
@ -192,7 +192,7 @@ public:
|
||||||
ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; }
|
ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; }
|
||||||
VoxelSystem* getVoxels() { return &_voxels; }
|
VoxelSystem* getVoxels() { return &_voxels; }
|
||||||
VoxelTree* getVoxelTree() { return _voxels.getTree(); }
|
VoxelTree* getVoxelTree() { return _voxels.getTree(); }
|
||||||
const VoxelPacketProcessor& getVoxelPacketProcessor() const { return _voxelProcessor; }
|
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
|
||||||
ParticleTreeRenderer* getParticles() { return &_particles; }
|
ParticleTreeRenderer* getParticles() { return &_particles; }
|
||||||
MetavoxelSystem* getMetavoxels() { return &_metavoxels; }
|
MetavoxelSystem* getMetavoxels() { return &_metavoxels; }
|
||||||
ModelTreeRenderer* getModels() { return &_models; }
|
ModelTreeRenderer* getModels() { return &_models; }
|
||||||
|
@ -533,7 +533,7 @@ private:
|
||||||
Audio _audio;
|
Audio _audio;
|
||||||
|
|
||||||
bool _enableProcessVoxelsThread;
|
bool _enableProcessVoxelsThread;
|
||||||
VoxelPacketProcessor _voxelProcessor;
|
OctreePacketProcessor _octreeProcessor;
|
||||||
VoxelHideShowThread _voxelHideShowThread;
|
VoxelHideShowThread _voxelHideShowThread;
|
||||||
VoxelEditPacketSender _voxelEditSender;
|
VoxelEditPacketSender _voxelEditSender;
|
||||||
ParticleEditPacketSender _particleEditSender;
|
ParticleEditPacketSender _particleEditSender;
|
||||||
|
|
|
@ -306,7 +306,7 @@ void linearResampling(int16_t* sourceSamples, int16_t* destinationSamples,
|
||||||
} else {
|
} else {
|
||||||
// this is a 48 to 24 resampling but both source and destination are two channels
|
// this is a 48 to 24 resampling but both source and destination are two channels
|
||||||
// squish two samples into one in each channel
|
// squish two samples into one in each channel
|
||||||
for (int i = 0; i < numSourceSamples; i += 4) {
|
for (unsigned int i = 0; i < numSourceSamples; i += 4) {
|
||||||
destinationSamples[i / 2] = (sourceSamples[i] / 2) + (sourceSamples[i + 2] / 2);
|
destinationSamples[i / 2] = (sourceSamples[i] / 2) + (sourceSamples[i + 2] / 2);
|
||||||
destinationSamples[(i / 2) + 1] = (sourceSamples[i + 1] / 2) + (sourceSamples[i + 3] / 2);
|
destinationSamples[(i / 2) + 1] = (sourceSamples[i + 1] / 2) + (sourceSamples[i + 3] / 2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ void DatagramProcessor::processDatagrams() {
|
||||||
case PacketTypeOctreeStats:
|
case PacketTypeOctreeStats:
|
||||||
case PacketTypeEnvironmentData: {
|
case PacketTypeEnvironmentData: {
|
||||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
"Application::networkReceive()... _voxelProcessor.queueReceivedPacket()");
|
"Application::networkReceive()... _octreeProcessor.queueReceivedPacket()");
|
||||||
|
|
||||||
bool wantExtraDebugging = application->getLogger()->extraDebugging();
|
bool wantExtraDebugging = application->getLogger()->extraDebugging();
|
||||||
if (wantExtraDebugging && packetTypeForPacket(incomingPacket) == PacketTypeVoxelData) {
|
if (wantExtraDebugging && packetTypeForPacket(incomingPacket) == PacketTypeVoxelData) {
|
||||||
|
@ -92,7 +92,7 @@ void DatagramProcessor::processDatagrams() {
|
||||||
|
|
||||||
if (matchedNode) {
|
if (matchedNode) {
|
||||||
// add this packet to our list of voxel packets and process them on the voxel processing
|
// add this packet to our list of voxel packets and process them on the voxel processing
|
||||||
application->_voxelProcessor.queueReceivedPacket(matchedNode, incomingPacket);
|
application->_octreeProcessor.queueReceivedPacket(matchedNode, incomingPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -103,6 +103,7 @@ Menu::Menu() :
|
||||||
_fastFPSAverage(ONE_SECOND_OF_FRAMES),
|
_fastFPSAverage(ONE_SECOND_OF_FRAMES),
|
||||||
_loginAction(NULL),
|
_loginAction(NULL),
|
||||||
_preferencesDialog(NULL),
|
_preferencesDialog(NULL),
|
||||||
|
_loginDialog(NULL),
|
||||||
_snapshotsLocation()
|
_snapshotsLocation()
|
||||||
{
|
{
|
||||||
Application *appInstance = Application::getInstance();
|
Application *appInstance = Application::getInstance();
|
||||||
|
@ -379,6 +380,11 @@ Menu::Menu() :
|
||||||
addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::AllowOculusCameraModeChange, 0, false);
|
addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::AllowOculusCameraModeChange, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::DisplayOculusOverlays, 0, true);
|
addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::DisplayOculusOverlays, 0, true);
|
||||||
|
|
||||||
|
QMenu* sixenseOptionsMenu = developerMenu->addMenu("Sixense Options");
|
||||||
|
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true);
|
||||||
|
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLeftHanded, 0, false);
|
||||||
|
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseInvertInputButtons, 0, false);
|
||||||
|
|
||||||
QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options");
|
QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options");
|
||||||
|
|
||||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu,
|
addCheckableActionToQMenuAndActionHash(handOptionsMenu,
|
||||||
|
@ -394,6 +400,8 @@ Menu::Menu() :
|
||||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, true);
|
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, true);
|
||||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false);
|
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false);
|
||||||
|
|
||||||
|
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisableNackPackets, 0, false);
|
||||||
|
|
||||||
addDisabledActionAndSeparator(developerMenu, "Testing");
|
addDisabledActionAndSeparator(developerMenu, "Testing");
|
||||||
|
|
||||||
QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools");
|
QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools");
|
||||||
|
@ -911,9 +919,11 @@ void sendFakeEnterEvent() {
|
||||||
const float DIALOG_RATIO_OF_WINDOW = 0.30f;
|
const float DIALOG_RATIO_OF_WINDOW = 0.30f;
|
||||||
|
|
||||||
void Menu::loginForCurrentDomain() {
|
void Menu::loginForCurrentDomain() {
|
||||||
LoginDialog* loginDialog = new LoginDialog(Application::getInstance()->getWindow());
|
if (!_loginDialog) {
|
||||||
loginDialog->show();
|
_loginDialog = new LoginDialog(Application::getInstance()->getWindow());
|
||||||
loginDialog->resizeAndPosition(false);
|
_loginDialog->show();
|
||||||
|
_loginDialog->resizeAndPosition(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::editPreferences() {
|
void Menu::editPreferences() {
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "ui/PreferencesDialog.h"
|
#include "ui/PreferencesDialog.h"
|
||||||
#include "ui/ChatWindow.h"
|
#include "ui/ChatWindow.h"
|
||||||
#include "ui/JSConsole.h"
|
#include "ui/JSConsole.h"
|
||||||
|
#include "ui/LoginDialog.h"
|
||||||
#include "ui/ScriptEditorWindow.h"
|
#include "ui/ScriptEditorWindow.h"
|
||||||
|
|
||||||
const float ADJUST_LOD_DOWN_FPS = 40.0;
|
const float ADJUST_LOD_DOWN_FPS = 40.0;
|
||||||
|
@ -270,6 +271,7 @@ private:
|
||||||
QPointer<PreferencesDialog> _preferencesDialog;
|
QPointer<PreferencesDialog> _preferencesDialog;
|
||||||
QPointer<AttachmentsDialog> _attachmentsDialog;
|
QPointer<AttachmentsDialog> _attachmentsDialog;
|
||||||
QPointer<AnimationsDialog> _animationsDialog;
|
QPointer<AnimationsDialog> _animationsDialog;
|
||||||
|
QPointer<LoginDialog> _loginDialog;
|
||||||
QAction* _chatAction;
|
QAction* _chatAction;
|
||||||
QString _snapshotsLocation;
|
QString _snapshotsLocation;
|
||||||
};
|
};
|
||||||
|
@ -321,6 +323,7 @@ namespace MenuOption {
|
||||||
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
||||||
const QString DecreaseVoxelSize = "Decrease Voxel Size";
|
const QString DecreaseVoxelSize = "Decrease Voxel Size";
|
||||||
const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD";
|
const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD";
|
||||||
|
const QString DisableNackPackets = "Disable NACK Packets";
|
||||||
const QString DisplayFrustum = "Display Frustum";
|
const QString DisplayFrustum = "Display Frustum";
|
||||||
const QString DisplayHands = "Display Hands";
|
const QString DisplayHands = "Display Hands";
|
||||||
const QString DisplayHandTargets = "Display Hand Targets";
|
const QString DisplayHandTargets = "Display Hand Targets";
|
||||||
|
@ -396,6 +399,9 @@ namespace MenuOption {
|
||||||
const QString SettingsExport = "Export Settings";
|
const QString SettingsExport = "Export Settings";
|
||||||
const QString SettingsImport = "Import Settings";
|
const QString SettingsImport = "Import Settings";
|
||||||
const QString SimpleShadows = "Simple";
|
const QString SimpleShadows = "Simple";
|
||||||
|
const QString SixenseInvertInputButtons = "Invert Sixense Mouse Input Buttons";
|
||||||
|
const QString SixenseLeftHanded = "Left Handed Sixense Mouse Input";
|
||||||
|
const QString SixenseMouseInput = "Enable Sixense Mouse Input";
|
||||||
const QString ShowBordersVoxelNodes = "Show Voxel Nodes";
|
const QString ShowBordersVoxelNodes = "Show Voxel Nodes";
|
||||||
const QString ShowBordersModelNodes = "Show Model Nodes";
|
const QString ShowBordersModelNodes = "Show Model Nodes";
|
||||||
const QString ShowBordersParticleNodes = "Show Particle Nodes";
|
const QString ShowBordersParticleNodes = "Show Particle Nodes";
|
||||||
|
|
|
@ -30,7 +30,7 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) {
|
||||||
}
|
}
|
||||||
setTranslation(neckPosition);
|
setTranslation(neckPosition);
|
||||||
glm::quat neckParentRotation;
|
glm::quat neckParentRotation;
|
||||||
if (!owningAvatar->getSkeletonModel().getNeckParentRotation(neckParentRotation)) {
|
if (!owningAvatar->getSkeletonModel().getNeckParentRotationFromDefaultOrientation(neckParentRotation)) {
|
||||||
neckParentRotation = owningAvatar->getOrientation();
|
neckParentRotation = owningAvatar->getOrientation();
|
||||||
}
|
}
|
||||||
setRotation(neckParentRotation);
|
setRotation(neckParentRotation);
|
||||||
|
|
|
@ -144,9 +144,9 @@ void SkeletonModel::getBodyShapes(QVector<const Shape*>& shapes) const {
|
||||||
void SkeletonModel::renderIKConstraints() {
|
void SkeletonModel::renderIKConstraints() {
|
||||||
renderJointConstraints(getRightHandJointIndex());
|
renderJointConstraints(getRightHandJointIndex());
|
||||||
renderJointConstraints(getLeftHandJointIndex());
|
renderJointConstraints(getLeftHandJointIndex());
|
||||||
if (isActive() && _owningAvatar->isMyAvatar()) {
|
//if (isActive() && _owningAvatar->isMyAvatar()) {
|
||||||
renderRagDoll();
|
// renderRagDoll();
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IndexValue {
|
class IndexValue {
|
||||||
|
@ -195,35 +195,20 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// rotate palm to align with its normal (normal points out of hand's palm)
|
// rotate palm to align with its normal (normal points out of hand's palm)
|
||||||
glm::quat palmRotation;
|
|
||||||
glm::quat r0, r1;
|
|
||||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK) &&
|
|
||||||
Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
|
|
||||||
JointState parentState = _jointStates[parentJointIndex];
|
|
||||||
palmRotation = parentState.getRotationFromBindToModelFrame();
|
|
||||||
r0 = palmRotation;
|
|
||||||
} else {
|
|
||||||
JointState state = _jointStates[jointIndex];
|
|
||||||
palmRotation = state.getRotationFromBindToModelFrame();
|
|
||||||
}
|
|
||||||
glm::quat inverseRotation = glm::inverse(_rotation);
|
glm::quat inverseRotation = glm::inverse(_rotation);
|
||||||
glm::vec3 palmNormal = inverseRotation * palm.getNormal();
|
|
||||||
palmRotation = rotationBetween(palmRotation * geometry.palmDirection, palmNormal) * palmRotation;
|
|
||||||
r1 = palmRotation;
|
|
||||||
|
|
||||||
// rotate palm to align with finger direction
|
|
||||||
glm::vec3 direction = inverseRotation * palm.getFingerDirection();
|
|
||||||
palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction) * palmRotation;
|
|
||||||
|
|
||||||
// set hand position, rotation
|
|
||||||
glm::vec3 palmPosition = inverseRotation * (palm.getPosition() - _translation);
|
glm::vec3 palmPosition = inverseRotation * (palm.getPosition() - _translation);
|
||||||
|
glm::vec3 palmNormal = inverseRotation * palm.getNormal();
|
||||||
|
glm::vec3 fingerDirection = inverseRotation * palm.getFingerDirection();
|
||||||
|
|
||||||
|
glm::quat palmRotation = rotationBetween(geometry.palmDirection, palmNormal);
|
||||||
|
palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), fingerDirection) * palmRotation;
|
||||||
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK)) {
|
||||||
setHandPosition(jointIndex, palmPosition, palmRotation);
|
setHandPosition(jointIndex, palmPosition, palmRotation);
|
||||||
|
|
||||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
|
} else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
|
||||||
glm::vec3 forearmVector = palmRotation * glm::vec3(sign, 0.0f, 0.0f);
|
float forearmLength = geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale);
|
||||||
setJointPosition(parentJointIndex, palmPosition + forearmVector *
|
glm::vec3 forearm = palmRotation * glm::vec3(sign * forearmLength, 0.0f, 0.0f);
|
||||||
geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale),
|
setJointPosition(parentJointIndex, palmPosition + forearm,
|
||||||
glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
||||||
JointState& parentState = _jointStates[parentJointIndex];
|
JointState& parentState = _jointStates[parentJointIndex];
|
||||||
parentState.setRotationFromBindFrame(palmRotation, PALM_PRIORITY);
|
parentState.setRotationFromBindFrame(palmRotation, PALM_PRIORITY);
|
||||||
|
@ -449,7 +434,7 @@ bool SkeletonModel::getNeckPosition(glm::vec3& neckPosition) const {
|
||||||
return isActive() && getJointPositionInWorldFrame(_geometry->getFBXGeometry().neckJointIndex, neckPosition);
|
return isActive() && getJointPositionInWorldFrame(_geometry->getFBXGeometry().neckJointIndex, neckPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SkeletonModel::getNeckParentRotation(glm::quat& neckParentRotation) const {
|
bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const {
|
||||||
if (!isActive()) {
|
if (!isActive()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -457,7 +442,13 @@ bool SkeletonModel::getNeckParentRotation(glm::quat& neckParentRotation) const {
|
||||||
if (geometry.neckJointIndex == -1) {
|
if (geometry.neckJointIndex == -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return getJointRotationInWorldFrame(geometry.joints.at(geometry.neckJointIndex).parentIndex, neckParentRotation);
|
int parentIndex = geometry.joints.at(geometry.neckJointIndex).parentIndex;
|
||||||
|
glm::quat worldFrameRotation;
|
||||||
|
if (getJointRotationInWorldFrame(parentIndex, worldFrameRotation)) {
|
||||||
|
neckParentRotation = worldFrameRotation * _jointStates[parentIndex].getFBXJoint().inverseDefaultRotation;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
|
bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
|
||||||
|
|
|
@ -86,9 +86,9 @@ public:
|
||||||
/// \return whether or not the neck was found
|
/// \return whether or not the neck was found
|
||||||
bool getNeckPosition(glm::vec3& neckPosition) const;
|
bool getNeckPosition(glm::vec3& neckPosition) const;
|
||||||
|
|
||||||
/// Returns the rotation of the neck joint's parent.
|
/// Returns the rotation of the neck joint's parent from default orientation
|
||||||
/// \return whether or not the neck was found
|
/// \return whether or not the neck was found
|
||||||
bool getNeckParentRotation(glm::quat& neckRotation) const;
|
bool getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const;
|
||||||
|
|
||||||
/// Retrieve the positions of up to two eye meshes.
|
/// Retrieve the positions of up to two eye meshes.
|
||||||
/// \return whether or not both eye meshes were found
|
/// \return whether or not both eye meshes were found
|
||||||
|
|
|
@ -39,6 +39,10 @@ SixenseManager::SixenseManager() {
|
||||||
|
|
||||||
sixenseInit();
|
sixenseInit();
|
||||||
#endif
|
#endif
|
||||||
|
_triggerPressed = false;
|
||||||
|
_bumperPressed = false;
|
||||||
|
_oldX = -1;
|
||||||
|
_oldY = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
SixenseManager::~SixenseManager() {
|
SixenseManager::~SixenseManager() {
|
||||||
|
@ -107,6 +111,15 @@ void SixenseManager::update(float deltaTime) {
|
||||||
palm->setTrigger(data->trigger);
|
palm->setTrigger(data->trigger);
|
||||||
palm->setJoystick(data->joystick_x, data->joystick_y);
|
palm->setJoystick(data->joystick_x, data->joystick_y);
|
||||||
|
|
||||||
|
|
||||||
|
// Emulate the mouse so we can use scripts
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) {
|
||||||
|
// Check if we are on the correct palm
|
||||||
|
if ((Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded) && numActiveControllers == 1) || numActiveControllers == 2) {
|
||||||
|
emulateMouse(palm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
|
// NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
|
||||||
glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
|
glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
|
||||||
position *= METERS_PER_MILLIMETER;
|
position *= METERS_PER_MILLIMETER;
|
||||||
|
@ -313,5 +326,79 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Injecting mouse movements and clicks
|
||||||
|
void SixenseManager::emulateMouse(PalmData *palm) {
|
||||||
|
MyAvatar* avatar = Application::getInstance()->getAvatar();
|
||||||
|
QPoint pos;
|
||||||
|
// Get directon relative to avatar orientation
|
||||||
|
glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection();
|
||||||
|
|
||||||
|
// Get the angles, scaled between 0-1
|
||||||
|
float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f;
|
||||||
|
float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f);
|
||||||
|
|
||||||
|
float cursorRange = Application::getInstance()->getGLWidget()->width();
|
||||||
|
|
||||||
|
pos.setX(cursorRange * xAngle);
|
||||||
|
pos.setY(cursorRange * yAngle);
|
||||||
|
|
||||||
|
//If position has changed, emit a mouse move to the application
|
||||||
|
if (pos.x() != _oldX || pos.y() != _oldY) {
|
||||||
|
QMouseEvent mouseEvent(static_cast<QEvent::Type>(CONTROLLER_MOVE_EVENT), pos, Qt::NoButton, Qt::NoButton, 0);
|
||||||
|
|
||||||
|
Application::getInstance()->mouseMoveEvent(&mouseEvent);
|
||||||
|
}
|
||||||
|
_oldX = pos.x();
|
||||||
|
_oldY = pos.y();
|
||||||
|
|
||||||
|
Qt::MouseButton bumperButton;
|
||||||
|
Qt::MouseButton triggerButton;
|
||||||
|
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseInvertInputButtons)) {
|
||||||
|
bumperButton = Qt::LeftButton;
|
||||||
|
triggerButton = Qt::RightButton;
|
||||||
|
} else {
|
||||||
|
bumperButton = Qt::RightButton;
|
||||||
|
triggerButton = Qt::LeftButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check for bumper press ( Right Click )
|
||||||
|
if (palm->getControllerButtons() & BUTTON_FWD) {
|
||||||
|
if (!_bumperPressed) {
|
||||||
|
_bumperPressed = true;
|
||||||
|
|
||||||
|
QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, bumperButton, bumperButton, 0);
|
||||||
|
|
||||||
|
Application::getInstance()->mousePressEvent(&mouseEvent);
|
||||||
|
}
|
||||||
|
} else if (_bumperPressed) {
|
||||||
|
QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0);
|
||||||
|
|
||||||
|
Application::getInstance()->mouseReleaseEvent(&mouseEvent);
|
||||||
|
|
||||||
|
_bumperPressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check for trigger press ( Left Click )
|
||||||
|
if (palm->getTrigger() == 1.0f) {
|
||||||
|
if (!_triggerPressed) {
|
||||||
|
_triggerPressed = true;
|
||||||
|
|
||||||
|
QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, triggerButton, triggerButton, 0);
|
||||||
|
|
||||||
|
Application::getInstance()->mousePressEvent(&mouseEvent);
|
||||||
|
}
|
||||||
|
} else if (_triggerPressed) {
|
||||||
|
QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0);
|
||||||
|
|
||||||
|
Application::getInstance()->mouseReleaseEvent(&mouseEvent);
|
||||||
|
|
||||||
|
_triggerPressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // HAVE_SIXENSE
|
#endif // HAVE_SIXENSE
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,9 @@ const unsigned int BUTTON_3 = 1U << 3;
|
||||||
const unsigned int BUTTON_4 = 1U << 4;
|
const unsigned int BUTTON_4 = 1U << 4;
|
||||||
const unsigned int BUTTON_FWD = 1U << 7;
|
const unsigned int BUTTON_FWD = 1U << 7;
|
||||||
|
|
||||||
|
// Event type that represents moving the controller
|
||||||
|
const unsigned int CONTROLLER_MOVE_EVENT = 1500U;
|
||||||
|
|
||||||
/// Handles interaction with the Sixense SDK (e.g., Razer Hydra).
|
/// Handles interaction with the Sixense SDK (e.g., Razer Hydra).
|
||||||
class SixenseManager : public QObject {
|
class SixenseManager : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -44,6 +47,7 @@ public slots:
|
||||||
private:
|
private:
|
||||||
#ifdef HAVE_SIXENSE
|
#ifdef HAVE_SIXENSE
|
||||||
void updateCalibration(const sixenseControllerData* controllers);
|
void updateCalibration(const sixenseControllerData* controllers);
|
||||||
|
void emulateMouse(PalmData *palm);
|
||||||
|
|
||||||
int _calibrationState;
|
int _calibrationState;
|
||||||
|
|
||||||
|
@ -65,6 +69,12 @@ private:
|
||||||
#endif
|
#endif
|
||||||
quint64 _lastMovement;
|
quint64 _lastMovement;
|
||||||
glm::vec3 _amountMoved;
|
glm::vec3 _amountMoved;
|
||||||
|
|
||||||
|
// for mouse emulation
|
||||||
|
bool _triggerPressed;
|
||||||
|
bool _bumperPressed;
|
||||||
|
int _oldX;
|
||||||
|
int _oldY;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_SixenseManager_h
|
#endif // hifi_SixenseManager_h
|
||||||
|
|
|
@ -57,10 +57,10 @@ void ModelTreeRenderer::render(RenderMode renderMode) {
|
||||||
|
|
||||||
const FBXGeometry* ModelTreeRenderer::getGeometryForModel(const ModelItem& modelItem) {
|
const FBXGeometry* ModelTreeRenderer::getGeometryForModel(const ModelItem& modelItem) {
|
||||||
const FBXGeometry* result = NULL;
|
const FBXGeometry* result = NULL;
|
||||||
|
|
||||||
Model* model = getModel(modelItem);
|
Model* model = getModel(modelItem);
|
||||||
if (model) {
|
if (model) {
|
||||||
result = &model->getGeometry()->getFBXGeometry();
|
result = &model->getGeometry()->getFBXGeometry();
|
||||||
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,14 @@ Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) {
|
||||||
if (_knownModelsItemModels.find(modelItem.getID()) != _knownModelsItemModels.end()) {
|
if (_knownModelsItemModels.find(modelItem.getID()) != _knownModelsItemModels.end()) {
|
||||||
model = _knownModelsItemModels[modelItem.getID()];
|
model = _knownModelsItemModels[modelItem.getID()];
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
// Make sure we only create new models on the thread that owns the ModelTreeRenderer
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(Model*, model), Q_ARG(const ModelItem&, modelItem));
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
model = new Model();
|
model = new Model();
|
||||||
model->init();
|
model->init();
|
||||||
model->setURL(QUrl(modelItem.getModelURL()));
|
model->setURL(QUrl(modelItem.getModelURL()));
|
||||||
|
@ -81,6 +89,13 @@ Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) {
|
||||||
if (_unknownModelsItemModels.find(modelItem.getCreatorTokenID()) != _unknownModelsItemModels.end()) {
|
if (_unknownModelsItemModels.find(modelItem.getCreatorTokenID()) != _unknownModelsItemModels.end()) {
|
||||||
model = _unknownModelsItemModels[modelItem.getCreatorTokenID()];
|
model = _unknownModelsItemModels[modelItem.getCreatorTokenID()];
|
||||||
} else {
|
} else {
|
||||||
|
// Make sure we only create new models on the thread that owns the ModelTreeRenderer
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(Model*, model), Q_ARG(const ModelItem&, modelItem));
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
model = new Model();
|
model = new Model();
|
||||||
model->init();
|
model->init();
|
||||||
model->setURL(QUrl(modelItem.getModelURL()));
|
model->setURL(QUrl(modelItem.getModelURL()));
|
||||||
|
@ -187,10 +202,12 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
||||||
|
|
||||||
if (drawAsModel) {
|
if (drawAsModel) {
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
|
{
|
||||||
const float alpha = 1.0f;
|
const float alpha = 1.0f;
|
||||||
|
|
||||||
Model* model = getModel(modelItem);
|
Model* model = getModel(modelItem);
|
||||||
|
|
||||||
|
if (model) {
|
||||||
model->setScaleToFit(true, radius * 2.0f);
|
model->setScaleToFit(true, radius * 2.0f);
|
||||||
model->setSnapModelToCenter(true);
|
model->setSnapModelToCenter(true);
|
||||||
|
|
||||||
|
@ -223,9 +240,28 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
||||||
|
|
||||||
if (modelItem.getGlowLevel() > 0.0f) {
|
if (modelItem.getGlowLevel() > 0.0f) {
|
||||||
Glower glower(modelItem.getGlowLevel());
|
Glower glower(modelItem.getGlowLevel());
|
||||||
|
|
||||||
|
if (model->isActive()) {
|
||||||
model->render(alpha, modelRenderMode);
|
model->render(alpha, modelRenderMode);
|
||||||
} else {
|
} else {
|
||||||
|
// if we couldn't get a model, then just draw a sphere
|
||||||
|
glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(position.x, position.y, position.z);
|
||||||
|
glutSolidSphere(radius, 15, 15);
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (model->isActive()) {
|
||||||
model->render(alpha, modelRenderMode);
|
model->render(alpha, modelRenderMode);
|
||||||
|
} else {
|
||||||
|
// if we couldn't get a model, then just draw a sphere
|
||||||
|
glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(position.x, position.y, position.z);
|
||||||
|
glutSolidSphere(radius, 15, 15);
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isShadowMode && displayModelBounds) {
|
if (!isShadowMode && displayModelBounds) {
|
||||||
|
@ -269,10 +305,19 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// if we couldn't get a model, then just draw a sphere
|
||||||
|
glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(position.x, position.y, position.z);
|
||||||
|
glutSolidSphere(radius, 15, 15);
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
}
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
} else {
|
} else {
|
||||||
glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
|
//glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
|
||||||
|
glColor3f(1.0f, 0.0f, 0.0f);
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glTranslatef(position.x, position.y, position.z);
|
glTranslatef(position.x, position.y, position.z);
|
||||||
glutSolidSphere(radius, 15, 15);
|
glutSolidSphere(radius, 15, 15);
|
||||||
|
|
|
@ -1275,8 +1275,8 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl
|
||||||
if (useRotation) {
|
if (useRotation) {
|
||||||
JointState& state = _jointStates[jointIndex];
|
JointState& state = _jointStates[jointIndex];
|
||||||
|
|
||||||
state.setRotation(rotation, true, priority);
|
state.setRotationFromBindFrame(rotation, priority);
|
||||||
endRotation = state.getRotation();
|
endRotation = state.getRotationFromBindToModelFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
// then, we go from the joint upwards, rotating the end as close as possible to the target
|
// then, we go from the joint upwards, rotating the end as close as possible to the target
|
||||||
|
|
|
@ -35,6 +35,82 @@ ApplicationOverlay::~ApplicationOverlay() {
|
||||||
|
|
||||||
const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
|
const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
|
||||||
|
|
||||||
|
void renderControllerPointer() {
|
||||||
|
Application* application = Application::getInstance();
|
||||||
|
QGLWidget* glWidget = application->getGLWidget();
|
||||||
|
MyAvatar* myAvatar = application->getAvatar();
|
||||||
|
|
||||||
|
const HandData* handData = Application::getInstance()->getAvatar()->getHandData();
|
||||||
|
int numberOfPalms = handData->getNumPalms();
|
||||||
|
|
||||||
|
|
||||||
|
int palmIndex;
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded)) {
|
||||||
|
palmIndex = 2;
|
||||||
|
} else {
|
||||||
|
palmIndex = 3;
|
||||||
|
}
|
||||||
|
const PalmData* palmData = NULL;
|
||||||
|
|
||||||
|
if (palmIndex >= handData->getPalms().size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handData->getPalms()[palmIndex].isActive()) {
|
||||||
|
palmData = &handData->getPalms()[palmIndex];
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get directon relative to avatar orientation
|
||||||
|
glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection();
|
||||||
|
|
||||||
|
// Get the angles, scaled between 0-1
|
||||||
|
float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f;
|
||||||
|
float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f);
|
||||||
|
|
||||||
|
float cursorRange = glWidget->width();
|
||||||
|
|
||||||
|
int mouseX = cursorRange * xAngle;
|
||||||
|
int mouseY = cursorRange * yAngle;
|
||||||
|
|
||||||
|
if (mouseX < 0) {
|
||||||
|
mouseX = 0;
|
||||||
|
} else if (mouseX > glWidget->width()) {
|
||||||
|
mouseX = glWidget->width();
|
||||||
|
}
|
||||||
|
if (mouseY < 0) {
|
||||||
|
mouseY = 0;
|
||||||
|
} else if (mouseY > glWidget->width()) {
|
||||||
|
mouseY = glWidget->width();
|
||||||
|
}
|
||||||
|
|
||||||
|
const float pointerWidth = 40;
|
||||||
|
const float pointerHeight = 40;
|
||||||
|
const float crossPad = 16;
|
||||||
|
|
||||||
|
mouseX -= pointerWidth / 2.0f;
|
||||||
|
mouseY += pointerHeight / 2.0f;
|
||||||
|
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
|
||||||
|
glColor3f(0, 0, 1);
|
||||||
|
|
||||||
|
//Horizontal crosshair
|
||||||
|
glVertex2i(mouseX, mouseY - crossPad);
|
||||||
|
glVertex2i(mouseX + pointerWidth, mouseY - crossPad);
|
||||||
|
glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad);
|
||||||
|
glVertex2i(mouseX, mouseY - pointerHeight + crossPad);
|
||||||
|
|
||||||
|
//Vertical crosshair
|
||||||
|
glVertex2i(mouseX + crossPad, mouseY);
|
||||||
|
glVertex2i(mouseX + pointerWidth - crossPad, mouseY);
|
||||||
|
glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight);
|
||||||
|
glVertex2i(mouseX + crossPad, mouseY - pointerHeight);
|
||||||
|
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
|
||||||
// Renders the overlays either to a texture or to the screen
|
// Renders the overlays either to a texture or to the screen
|
||||||
void ApplicationOverlay::renderOverlay(bool renderToTexture) {
|
void ApplicationOverlay::renderOverlay(bool renderToTexture) {
|
||||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()");
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()");
|
||||||
|
@ -45,7 +121,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
|
||||||
QGLWidget* glWidget = application->getGLWidget();
|
QGLWidget* glWidget = application->getGLWidget();
|
||||||
MyAvatar* myAvatar = application->getAvatar();
|
MyAvatar* myAvatar = application->getAvatar();
|
||||||
Audio* audio = application->getAudio();
|
Audio* audio = application->getAudio();
|
||||||
const VoxelPacketProcessor& voxelPacketProcessor = application->getVoxelPacketProcessor();
|
const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor();
|
||||||
BandwidthMeter* bandwidthMeter = application->getBandwidthMeter();
|
BandwidthMeter* bandwidthMeter = application->getBandwidthMeter();
|
||||||
NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay();
|
NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay();
|
||||||
|
|
||||||
|
@ -200,7 +276,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
|
||||||
// let's set horizontal offset to give stats some margin to mirror
|
// let's set horizontal offset to give stats some margin to mirror
|
||||||
int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
|
int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
|
||||||
int voxelPacketsToProcess = voxelPacketProcessor.packetsToProcessCount();
|
int voxelPacketsToProcess = octreePacketProcessor.packetsToProcessCount();
|
||||||
// Onscreen text about position, servers, etc
|
// Onscreen text about position, servers, etc
|
||||||
Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, application->getFps(), application->getPacketsPerSecond(), application->getBytesPerSecond(), voxelPacketsToProcess);
|
Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, application->getFps(), application->getPacketsPerSecond(), application->getBytesPerSecond(), voxelPacketsToProcess);
|
||||||
// Bandwidth meter
|
// Bandwidth meter
|
||||||
|
@ -254,8 +330,10 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
|
||||||
glVertex2i(mouseX + crossPad, mouseY - pointerHeight);
|
glVertex2i(mouseX + crossPad, mouseY - pointerHeight);
|
||||||
|
|
||||||
glEnd();
|
glEnd();
|
||||||
|
} else if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) {
|
||||||
|
//only render controller pointer if we aren't already rendering a mouse pointer
|
||||||
|
renderControllerPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
|
|
@ -103,8 +103,9 @@ void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authoriz
|
||||||
codedAuthorizationURL.setQuery(authQuery);
|
codedAuthorizationURL.setQuery(authQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connect(_activeWebView.data(), &QWebView::urlChanged, this, &OAuthWebViewHandler::handleURLChanged);
|
||||||
|
|
||||||
_activeWebView->load(codedAuthorizationURL);
|
_activeWebView->load(codedAuthorizationURL);
|
||||||
_activeWebView->show();
|
|
||||||
|
|
||||||
connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors,
|
connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors,
|
||||||
this, &OAuthWebViewHandler::handleSSLErrors);
|
this, &OAuthWebViewHandler::handleSSLErrors);
|
||||||
|
@ -137,3 +138,17 @@ void OAuthWebViewHandler::handleLoadFinished(bool success) {
|
||||||
void OAuthWebViewHandler::handleWebViewDestroyed(QObject* destroyedObject) {
|
void OAuthWebViewHandler::handleWebViewDestroyed(QObject* destroyedObject) {
|
||||||
_webViewRedisplayTimer.restart();
|
_webViewRedisplayTimer.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OAuthWebViewHandler::handleURLChanged(const QUrl& url) {
|
||||||
|
// check if this is the authorization screen - if it is then we need to show the OAuthWebViewHandler
|
||||||
|
const QString ACCESS_TOKEN_URL_REGEX_STRING = "redirect_uri=[\\w:\\/\\.]+&access_token=";
|
||||||
|
QRegExp accessTokenRegex(ACCESS_TOKEN_URL_REGEX_STRING);
|
||||||
|
|
||||||
|
if (accessTokenRegex.indexIn(url.toString()) != -1) {
|
||||||
|
_activeWebView->show();
|
||||||
|
} else if (url.toString() == DEFAULT_NODE_AUTH_URL.toString() + "/login") {
|
||||||
|
// this is a login request - we're going to close the webview and signal the AccountManager that we need a login
|
||||||
|
_activeWebView->close();
|
||||||
|
AccountManager::getInstance().checkAndSignalForAccessToken();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ private slots:
|
||||||
void handleSSLErrors(QNetworkReply* networkReply, const QList<QSslError>& errorList);
|
void handleSSLErrors(QNetworkReply* networkReply, const QList<QSslError>& errorList);
|
||||||
void handleLoadFinished(bool success);
|
void handleLoadFinished(bool success);
|
||||||
void handleWebViewDestroyed(QObject* destroyedObject);
|
void handleWebViewDestroyed(QObject* destroyedObject);
|
||||||
|
void handleURLChanged(const QUrl& url);
|
||||||
private:
|
private:
|
||||||
QPointer<QWebView> _activeWebView;
|
QPointer<QWebView> _activeWebView;
|
||||||
QElapsedTimer _webViewRedisplayTimer;
|
QElapsedTimer _webViewRedisplayTimer;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// VoxelPacketProcessor.cpp
|
// OctreePacketProcessor.cpp
|
||||||
// interface/src/voxels
|
// interface/src/voxels
|
||||||
//
|
//
|
||||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||||
|
@ -13,18 +13,18 @@
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
#include "VoxelPacketProcessor.h"
|
#include "OctreePacketProcessor.h"
|
||||||
|
|
||||||
void VoxelPacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) {
|
void OctreePacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) {
|
||||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
"VoxelPacketProcessor::processPacket()");
|
"OctreePacketProcessor::processPacket()");
|
||||||
|
|
||||||
QByteArray mutablePacket = packet;
|
QByteArray mutablePacket = packet;
|
||||||
|
|
||||||
const int WAY_BEHIND = 300;
|
const int WAY_BEHIND = 300;
|
||||||
|
|
||||||
if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) {
|
if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) {
|
||||||
qDebug("VoxelPacketProcessor::processPacket() packets to process=%d", packetsToProcessCount());
|
qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount());
|
||||||
}
|
}
|
||||||
ssize_t messageLength = mutablePacket.size();
|
ssize_t messageLength = mutablePacket.size();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// VoxelPacketProcessor.h
|
// OctreePacketProcessor.h
|
||||||
// interface/src/voxels
|
// interface/src/voxels
|
||||||
//
|
//
|
||||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||||
|
@ -9,16 +9,16 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef hifi_VoxelPacketProcessor_h
|
#ifndef hifi_OctreePacketProcessor_h
|
||||||
#define hifi_VoxelPacketProcessor_h
|
#define hifi_OctreePacketProcessor_h
|
||||||
|
|
||||||
#include <ReceivedPacketProcessor.h>
|
#include <ReceivedPacketProcessor.h>
|
||||||
|
|
||||||
/// Handles processing of incoming voxel packets for the interface application. As with other ReceivedPacketProcessor classes
|
/// Handles processing of incoming voxel packets for the interface application. As with other ReceivedPacketProcessor classes
|
||||||
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
|
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
|
||||||
class VoxelPacketProcessor : public ReceivedPacketProcessor {
|
class OctreePacketProcessor : public ReceivedPacketProcessor {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
protected:
|
protected:
|
||||||
virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
|
virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
|
||||||
};
|
};
|
||||||
#endif // hifi_VoxelPacketProcessor_h
|
#endif // hifi_OctreePacketProcessor_h
|
|
@ -14,6 +14,7 @@
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
|
#include <QScriptValueIterator>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
|
|
||||||
|
@ -30,6 +31,7 @@ REGISTER_SIMPLE_TYPE_STREAMER(uint)
|
||||||
REGISTER_SIMPLE_TYPE_STREAMER(float)
|
REGISTER_SIMPLE_TYPE_STREAMER(float)
|
||||||
REGISTER_SIMPLE_TYPE_STREAMER(QByteArray)
|
REGISTER_SIMPLE_TYPE_STREAMER(QByteArray)
|
||||||
REGISTER_SIMPLE_TYPE_STREAMER(QColor)
|
REGISTER_SIMPLE_TYPE_STREAMER(QColor)
|
||||||
|
REGISTER_SIMPLE_TYPE_STREAMER(QScriptValue)
|
||||||
REGISTER_SIMPLE_TYPE_STREAMER(QString)
|
REGISTER_SIMPLE_TYPE_STREAMER(QString)
|
||||||
REGISTER_SIMPLE_TYPE_STREAMER(QUrl)
|
REGISTER_SIMPLE_TYPE_STREAMER(QUrl)
|
||||||
REGISTER_SIMPLE_TYPE_STREAMER(QVariantList)
|
REGISTER_SIMPLE_TYPE_STREAMER(QVariantList)
|
||||||
|
@ -285,6 +287,12 @@ void Bitstream::writeDelta(const QVariant& value, const QVariant& reference) {
|
||||||
streamer->writeRawDelta(*this, value, reference);
|
streamer->writeRawDelta(*this, value, reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Bitstream::writeRawDelta(const QVariant& value, const QVariant& reference) {
|
||||||
|
const TypeStreamer* streamer = getTypeStreamers().value(value.userType());
|
||||||
|
_typeStreamerStreamer << streamer;
|
||||||
|
streamer->writeRawDelta(*this, value, reference);
|
||||||
|
}
|
||||||
|
|
||||||
void Bitstream::readRawDelta(QVariant& value, const QVariant& reference) {
|
void Bitstream::readRawDelta(QVariant& value, const QVariant& reference) {
|
||||||
TypeReader typeReader;
|
TypeReader typeReader;
|
||||||
_typeStreamerStreamer >> typeReader;
|
_typeStreamerStreamer >> typeReader;
|
||||||
|
@ -309,6 +317,272 @@ void Bitstream::readRawDelta(QObject*& value, const QObject* reference) {
|
||||||
value = objectReader.readDelta(*this, reference);
|
value = objectReader.readDelta(*this, reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Bitstream::writeRawDelta(const QScriptValue& value, const QScriptValue& reference) {
|
||||||
|
if (reference.isUndefined() || reference.isNull()) {
|
||||||
|
*this << value;
|
||||||
|
|
||||||
|
} else if (reference.isBool()) {
|
||||||
|
if (value.isBool()) {
|
||||||
|
*this << false;
|
||||||
|
*this << value.toBool();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*this << true;
|
||||||
|
*this << value;
|
||||||
|
}
|
||||||
|
} else if (reference.isNumber()) {
|
||||||
|
if (value.isNumber()) {
|
||||||
|
*this << false;
|
||||||
|
*this << value.toNumber();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*this << true;
|
||||||
|
*this << value;
|
||||||
|
}
|
||||||
|
} else if (reference.isString()) {
|
||||||
|
if (value.isString()) {
|
||||||
|
*this << false;
|
||||||
|
*this << value.toString();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*this << true;
|
||||||
|
*this << value;
|
||||||
|
}
|
||||||
|
} else if (reference.isVariant()) {
|
||||||
|
if (value.isVariant()) {
|
||||||
|
*this << false;
|
||||||
|
writeRawDelta(value.toVariant(), reference.toVariant());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*this << true;
|
||||||
|
*this << value;
|
||||||
|
}
|
||||||
|
} else if (reference.isQObject()) {
|
||||||
|
if (value.isQObject()) {
|
||||||
|
*this << false;
|
||||||
|
writeRawDelta(value.toQObject(), reference.toQObject());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*this << true;
|
||||||
|
*this << value;
|
||||||
|
}
|
||||||
|
} else if (reference.isQMetaObject()) {
|
||||||
|
if (value.isQMetaObject()) {
|
||||||
|
*this << false;
|
||||||
|
*this << value.toQMetaObject();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*this << true;
|
||||||
|
*this << value;
|
||||||
|
}
|
||||||
|
} else if (reference.isDate()) {
|
||||||
|
if (value.isDate()) {
|
||||||
|
*this << false;
|
||||||
|
*this << value.toDateTime();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*this << true;
|
||||||
|
*this << value;
|
||||||
|
}
|
||||||
|
} else if (reference.isRegExp()) {
|
||||||
|
if (value.isRegExp()) {
|
||||||
|
*this << false;
|
||||||
|
*this << value.toRegExp();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*this << true;
|
||||||
|
*this << value;
|
||||||
|
}
|
||||||
|
} else if (reference.isArray()) {
|
||||||
|
if (value.isArray()) {
|
||||||
|
*this << false;
|
||||||
|
int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32();
|
||||||
|
*this << length;
|
||||||
|
int referenceLength = reference.property(ScriptCache::getInstance()->getLengthString()).toInt32();
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
if (i < referenceLength) {
|
||||||
|
writeDelta(value.property(i), reference.property(i));
|
||||||
|
} else {
|
||||||
|
*this << value.property(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*this << true;
|
||||||
|
*this << value;
|
||||||
|
}
|
||||||
|
} else if (reference.isObject()) {
|
||||||
|
if (value.isObject() && !(value.isArray() || value.isRegExp() || value.isDate() ||
|
||||||
|
value.isQMetaObject() || value.isQObject() || value.isVariant())) {
|
||||||
|
*this << false;
|
||||||
|
for (QScriptValueIterator it(value); it.hasNext(); ) {
|
||||||
|
it.next();
|
||||||
|
QScriptValue referenceValue = reference.property(it.scriptName());
|
||||||
|
if (it.value() != referenceValue) {
|
||||||
|
*this << it.scriptName();
|
||||||
|
writeRawDelta(it.value(), referenceValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (QScriptValueIterator it(reference); it.hasNext(); ) {
|
||||||
|
it.next();
|
||||||
|
if (!value.property(it.scriptName()).isValid()) {
|
||||||
|
*this << it.scriptName();
|
||||||
|
writeRawDelta(QScriptValue(), it.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*this << QScriptString();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*this << true;
|
||||||
|
*this << value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*this << value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) {
|
||||||
|
if (reference.isUndefined() || reference.isNull()) {
|
||||||
|
*this >> value;
|
||||||
|
|
||||||
|
} else if (reference.isBool()) {
|
||||||
|
bool typeChanged;
|
||||||
|
*this >> typeChanged;
|
||||||
|
if (typeChanged) {
|
||||||
|
*this >> value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
bool boolValue;
|
||||||
|
*this >> boolValue;
|
||||||
|
value = QScriptValue(boolValue);
|
||||||
|
}
|
||||||
|
} else if (reference.isNumber()) {
|
||||||
|
bool typeChanged;
|
||||||
|
*this >> typeChanged;
|
||||||
|
if (typeChanged) {
|
||||||
|
*this >> value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
qsreal numberValue;
|
||||||
|
*this >> numberValue;
|
||||||
|
value = QScriptValue(numberValue);
|
||||||
|
}
|
||||||
|
} else if (reference.isString()) {
|
||||||
|
bool typeChanged;
|
||||||
|
*this >> typeChanged;
|
||||||
|
if (typeChanged) {
|
||||||
|
*this >> value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
QString stringValue;
|
||||||
|
*this >> stringValue;
|
||||||
|
value = QScriptValue(stringValue);
|
||||||
|
}
|
||||||
|
} else if (reference.isVariant()) {
|
||||||
|
bool typeChanged;
|
||||||
|
*this >> typeChanged;
|
||||||
|
if (typeChanged) {
|
||||||
|
*this >> value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
QVariant variant;
|
||||||
|
readRawDelta(variant, reference.toVariant());
|
||||||
|
value = ScriptCache::getInstance()->getEngine()->newVariant(variant);
|
||||||
|
}
|
||||||
|
} else if (reference.isQObject()) {
|
||||||
|
bool typeChanged;
|
||||||
|
*this >> typeChanged;
|
||||||
|
if (typeChanged) {
|
||||||
|
*this >> value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
QObject* object;
|
||||||
|
readRawDelta(object, reference.toQObject());
|
||||||
|
value = ScriptCache::getInstance()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership);
|
||||||
|
}
|
||||||
|
} else if (reference.isQMetaObject()) {
|
||||||
|
bool typeChanged;
|
||||||
|
*this >> typeChanged;
|
||||||
|
if (typeChanged) {
|
||||||
|
*this >> value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const QMetaObject* metaObject;
|
||||||
|
*this >> metaObject;
|
||||||
|
value = ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject);
|
||||||
|
}
|
||||||
|
} else if (reference.isDate()) {
|
||||||
|
bool typeChanged;
|
||||||
|
*this >> typeChanged;
|
||||||
|
if (typeChanged) {
|
||||||
|
*this >> value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
QDateTime dateTime;
|
||||||
|
*this >> dateTime;
|
||||||
|
value = ScriptCache::getInstance()->getEngine()->newDate(dateTime);
|
||||||
|
}
|
||||||
|
} else if (reference.isRegExp()) {
|
||||||
|
bool typeChanged;
|
||||||
|
*this >> typeChanged;
|
||||||
|
if (typeChanged) {
|
||||||
|
*this >> value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
QRegExp regExp;
|
||||||
|
*this >> regExp;
|
||||||
|
value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp);
|
||||||
|
}
|
||||||
|
} else if (reference.isArray()) {
|
||||||
|
bool typeChanged;
|
||||||
|
*this >> typeChanged;
|
||||||
|
if (typeChanged) {
|
||||||
|
*this >> value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
int length;
|
||||||
|
*this >> length;
|
||||||
|
value = ScriptCache::getInstance()->getEngine()->newArray(length);
|
||||||
|
int referenceLength = reference.property(ScriptCache::getInstance()->getLengthString()).toInt32();
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
QScriptValue element;
|
||||||
|
if (i < referenceLength) {
|
||||||
|
readDelta(element, reference.property(i));
|
||||||
|
} else {
|
||||||
|
*this >> element;
|
||||||
|
}
|
||||||
|
value.setProperty(i, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (reference.isObject()) {
|
||||||
|
bool typeChanged;
|
||||||
|
*this >> typeChanged;
|
||||||
|
if (typeChanged) {
|
||||||
|
*this >> value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// start by shallow-copying the reference
|
||||||
|
value = ScriptCache::getInstance()->getEngine()->newObject();
|
||||||
|
for (QScriptValueIterator it(reference); it.hasNext(); ) {
|
||||||
|
it.next();
|
||||||
|
value.setProperty(it.scriptName(), it.value());
|
||||||
|
}
|
||||||
|
// then apply the requested changes
|
||||||
|
forever {
|
||||||
|
QScriptString name;
|
||||||
|
*this >> name;
|
||||||
|
if (!name.isValid()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QScriptValue scriptValue;
|
||||||
|
readRawDelta(scriptValue, reference.property(name));
|
||||||
|
value.setProperty(name, scriptValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*this >> value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Bitstream& Bitstream::operator<<(bool value) {
|
Bitstream& Bitstream::operator<<(bool value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
_byte |= (1 << _position);
|
_byte |= (1 << _position);
|
||||||
|
@ -350,6 +624,14 @@ Bitstream& Bitstream::operator>>(uint& value) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bitstream& Bitstream::operator<<(qint64 value) {
|
||||||
|
return write(&value, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitstream& Bitstream::operator>>(qint64& value) {
|
||||||
|
return read(&value, 64);
|
||||||
|
}
|
||||||
|
|
||||||
Bitstream& Bitstream::operator<<(float value) {
|
Bitstream& Bitstream::operator<<(float value) {
|
||||||
return write(&value, 32);
|
return write(&value, 32);
|
||||||
}
|
}
|
||||||
|
@ -358,6 +640,14 @@ Bitstream& Bitstream::operator>>(float& value) {
|
||||||
return read(&value, 32);
|
return read(&value, 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bitstream& Bitstream::operator<<(double value) {
|
||||||
|
return write(&value, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitstream& Bitstream::operator>>(double& value) {
|
||||||
|
return read(&value, 64);
|
||||||
|
}
|
||||||
|
|
||||||
Bitstream& Bitstream::operator<<(const glm::vec3& value) {
|
Bitstream& Bitstream::operator<<(const glm::vec3& value) {
|
||||||
return *this << value.x << value.y << value.z;
|
return *this << value.x << value.y << value.z;
|
||||||
}
|
}
|
||||||
|
@ -420,6 +710,40 @@ Bitstream& Bitstream::operator>>(QUrl& url) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bitstream& Bitstream::operator<<(const QDateTime& dateTime) {
|
||||||
|
return *this << dateTime.toMSecsSinceEpoch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitstream& Bitstream::operator>>(QDateTime& dateTime) {
|
||||||
|
qint64 msecsSinceEpoch;
|
||||||
|
*this >> msecsSinceEpoch;
|
||||||
|
dateTime = QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitstream& Bitstream::operator<<(const QRegExp& regExp) {
|
||||||
|
*this << regExp.pattern();
|
||||||
|
Qt::CaseSensitivity caseSensitivity = regExp.caseSensitivity();
|
||||||
|
write(&caseSensitivity, 1);
|
||||||
|
QRegExp::PatternSyntax syntax = regExp.patternSyntax();
|
||||||
|
write(&syntax, 3);
|
||||||
|
return *this << regExp.isMinimal();
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitstream& Bitstream::operator>>(QRegExp& regExp) {
|
||||||
|
QString pattern;
|
||||||
|
*this >> pattern;
|
||||||
|
Qt::CaseSensitivity caseSensitivity = (Qt::CaseSensitivity)0;
|
||||||
|
read(&caseSensitivity, 1);
|
||||||
|
QRegExp::PatternSyntax syntax = (QRegExp::PatternSyntax)0;
|
||||||
|
read(&syntax, 3);
|
||||||
|
regExp = QRegExp(pattern, caseSensitivity, syntax);
|
||||||
|
bool minimal;
|
||||||
|
*this >> minimal;
|
||||||
|
regExp.setMinimal(minimal);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
Bitstream& Bitstream::operator<<(const QVariant& value) {
|
Bitstream& Bitstream::operator<<(const QVariant& value) {
|
||||||
if (!value.isValid()) {
|
if (!value.isValid()) {
|
||||||
_typeStreamerStreamer << NULL;
|
_typeStreamerStreamer << NULL;
|
||||||
|
@ -543,6 +867,185 @@ Bitstream& Bitstream::operator>>(QScriptString& string) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ScriptValueType {
|
||||||
|
INVALID_SCRIPT_VALUE,
|
||||||
|
UNDEFINED_SCRIPT_VALUE,
|
||||||
|
NULL_SCRIPT_VALUE,
|
||||||
|
BOOL_SCRIPT_VALUE,
|
||||||
|
NUMBER_SCRIPT_VALUE,
|
||||||
|
STRING_SCRIPT_VALUE,
|
||||||
|
VARIANT_SCRIPT_VALUE,
|
||||||
|
QOBJECT_SCRIPT_VALUE,
|
||||||
|
QMETAOBJECT_SCRIPT_VALUE,
|
||||||
|
DATE_SCRIPT_VALUE,
|
||||||
|
REGEXP_SCRIPT_VALUE,
|
||||||
|
ARRAY_SCRIPT_VALUE,
|
||||||
|
OBJECT_SCRIPT_VALUE
|
||||||
|
};
|
||||||
|
|
||||||
|
const int SCRIPT_VALUE_BITS = 4;
|
||||||
|
|
||||||
|
static void writeScriptValueType(Bitstream& out, ScriptValueType type) {
|
||||||
|
out.write(&type, SCRIPT_VALUE_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ScriptValueType readScriptValueType(Bitstream& in) {
|
||||||
|
ScriptValueType type = (ScriptValueType)0;
|
||||||
|
in.read(&type, SCRIPT_VALUE_BITS);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitstream& Bitstream::operator<<(const QScriptValue& value) {
|
||||||
|
if (value.isUndefined()) {
|
||||||
|
writeScriptValueType(*this, UNDEFINED_SCRIPT_VALUE);
|
||||||
|
|
||||||
|
} else if (value.isNull()) {
|
||||||
|
writeScriptValueType(*this, NULL_SCRIPT_VALUE);
|
||||||
|
|
||||||
|
} else if (value.isBool()) {
|
||||||
|
writeScriptValueType(*this, BOOL_SCRIPT_VALUE);
|
||||||
|
*this << value.toBool();
|
||||||
|
|
||||||
|
} else if (value.isNumber()) {
|
||||||
|
writeScriptValueType(*this, NUMBER_SCRIPT_VALUE);
|
||||||
|
*this << value.toNumber();
|
||||||
|
|
||||||
|
} else if (value.isString()) {
|
||||||
|
writeScriptValueType(*this, STRING_SCRIPT_VALUE);
|
||||||
|
*this << value.toString();
|
||||||
|
|
||||||
|
} else if (value.isVariant()) {
|
||||||
|
writeScriptValueType(*this, VARIANT_SCRIPT_VALUE);
|
||||||
|
*this << value.toVariant();
|
||||||
|
|
||||||
|
} else if (value.isQObject()) {
|
||||||
|
writeScriptValueType(*this, QOBJECT_SCRIPT_VALUE);
|
||||||
|
*this << value.toQObject();
|
||||||
|
|
||||||
|
} else if (value.isQMetaObject()) {
|
||||||
|
writeScriptValueType(*this, QMETAOBJECT_SCRIPT_VALUE);
|
||||||
|
*this << value.toQMetaObject();
|
||||||
|
|
||||||
|
} else if (value.isDate()) {
|
||||||
|
writeScriptValueType(*this, DATE_SCRIPT_VALUE);
|
||||||
|
*this << value.toDateTime();
|
||||||
|
|
||||||
|
} else if (value.isRegExp()) {
|
||||||
|
writeScriptValueType(*this, REGEXP_SCRIPT_VALUE);
|
||||||
|
*this << value.toRegExp();
|
||||||
|
|
||||||
|
} else if (value.isArray()) {
|
||||||
|
writeScriptValueType(*this, ARRAY_SCRIPT_VALUE);
|
||||||
|
int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32();
|
||||||
|
*this << length;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
*this << value.property(i);
|
||||||
|
}
|
||||||
|
} else if (value.isObject()) {
|
||||||
|
writeScriptValueType(*this, OBJECT_SCRIPT_VALUE);
|
||||||
|
for (QScriptValueIterator it(value); it.hasNext(); ) {
|
||||||
|
it.next();
|
||||||
|
*this << it.scriptName();
|
||||||
|
*this << it.value();
|
||||||
|
}
|
||||||
|
*this << QScriptString();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
writeScriptValueType(*this, INVALID_SCRIPT_VALUE);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitstream& Bitstream::operator>>(QScriptValue& value) {
|
||||||
|
switch (readScriptValueType(*this)) {
|
||||||
|
case UNDEFINED_SCRIPT_VALUE:
|
||||||
|
value = QScriptValue(QScriptValue::UndefinedValue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NULL_SCRIPT_VALUE:
|
||||||
|
value = QScriptValue(QScriptValue::NullValue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BOOL_SCRIPT_VALUE: {
|
||||||
|
bool boolValue;
|
||||||
|
*this >> boolValue;
|
||||||
|
value = QScriptValue(boolValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NUMBER_SCRIPT_VALUE: {
|
||||||
|
qsreal numberValue;
|
||||||
|
*this >> numberValue;
|
||||||
|
value = QScriptValue(numberValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case STRING_SCRIPT_VALUE: {
|
||||||
|
QString stringValue;
|
||||||
|
*this >> stringValue;
|
||||||
|
value = QScriptValue(stringValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VARIANT_SCRIPT_VALUE: {
|
||||||
|
QVariant variantValue;
|
||||||
|
*this >> variantValue;
|
||||||
|
value = ScriptCache::getInstance()->getEngine()->newVariant(variantValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QOBJECT_SCRIPT_VALUE: {
|
||||||
|
QObject* object;
|
||||||
|
*this >> object;
|
||||||
|
ScriptCache::getInstance()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QMETAOBJECT_SCRIPT_VALUE: {
|
||||||
|
const QMetaObject* metaObject;
|
||||||
|
*this >> metaObject;
|
||||||
|
ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DATE_SCRIPT_VALUE: {
|
||||||
|
QDateTime dateTime;
|
||||||
|
*this >> dateTime;
|
||||||
|
value = ScriptCache::getInstance()->getEngine()->newDate(dateTime);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case REGEXP_SCRIPT_VALUE: {
|
||||||
|
QRegExp regExp;
|
||||||
|
*this >> regExp;
|
||||||
|
value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ARRAY_SCRIPT_VALUE: {
|
||||||
|
int length;
|
||||||
|
*this >> length;
|
||||||
|
value = ScriptCache::getInstance()->getEngine()->newArray(length);
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
QScriptValue element;
|
||||||
|
*this >> element;
|
||||||
|
value.setProperty(i, element);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OBJECT_SCRIPT_VALUE: {
|
||||||
|
value = ScriptCache::getInstance()->getEngine()->newObject();
|
||||||
|
forever {
|
||||||
|
QScriptString name;
|
||||||
|
*this >> name;
|
||||||
|
if (!name.isValid()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QScriptValue scriptValue;
|
||||||
|
*this >> scriptValue;
|
||||||
|
value.setProperty(name, scriptValue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
value = QScriptValue();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
Bitstream& Bitstream::operator<<(const SharedObjectPointer& object) {
|
Bitstream& Bitstream::operator<<(const SharedObjectPointer& object) {
|
||||||
_sharedObjectStreamer << object;
|
_sharedObjectStreamer << object;
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -561,7 +1064,7 @@ Bitstream& Bitstream::operator<(const QMetaObject* metaObject) {
|
||||||
if (_metadataType == NO_METADATA) {
|
if (_metadataType == NO_METADATA) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
const QVector<PropertyWriter>& propertyWriters = getPropertyWriters().value(metaObject);
|
const PropertyWriterVector& propertyWriters = getPropertyWriters().value(metaObject);
|
||||||
*this << propertyWriters.size();
|
*this << propertyWriters.size();
|
||||||
QCryptographicHash hash(QCryptographicHash::Md5);
|
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||||
foreach (const PropertyWriter& propertyWriter, propertyWriters) {
|
foreach (const PropertyWriter& propertyWriter, propertyWriters) {
|
||||||
|
@ -600,7 +1103,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) {
|
||||||
}
|
}
|
||||||
int storedPropertyCount;
|
int storedPropertyCount;
|
||||||
*this >> storedPropertyCount;
|
*this >> storedPropertyCount;
|
||||||
QVector<PropertyReader> properties(storedPropertyCount);
|
PropertyReaderVector properties(storedPropertyCount);
|
||||||
for (int i = 0; i < storedPropertyCount; i++) {
|
for (int i = 0; i < storedPropertyCount; i++) {
|
||||||
TypeReader typeReader;
|
TypeReader typeReader;
|
||||||
*this >> typeReader;
|
*this >> typeReader;
|
||||||
|
@ -619,7 +1122,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) {
|
||||||
QCryptographicHash hash(QCryptographicHash::Md5);
|
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||||
bool matches = true;
|
bool matches = true;
|
||||||
if (metaObject) {
|
if (metaObject) {
|
||||||
const QVector<PropertyWriter>& propertyWriters = getPropertyWriters().value(metaObject);
|
const PropertyWriterVector& propertyWriters = getPropertyWriters().value(metaObject);
|
||||||
if (propertyWriters.size() == properties.size()) {
|
if (propertyWriters.size() == properties.size()) {
|
||||||
for (int i = 0; i < propertyWriters.size(); i++) {
|
for (int i = 0; i < propertyWriters.size(); i++) {
|
||||||
const PropertyWriter& propertyWriter = propertyWriters.at(i);
|
const PropertyWriter& propertyWriter = propertyWriters.at(i);
|
||||||
|
@ -899,14 +1402,17 @@ Bitstream& Bitstream::operator>(AttributePointer& attribute) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString INVALID_STRING("%INVALID%");
|
||||||
|
|
||||||
Bitstream& Bitstream::operator<(const QScriptString& string) {
|
Bitstream& Bitstream::operator<(const QScriptString& string) {
|
||||||
return *this << string.toString();
|
return *this << (string.isValid() ? string.toString() : INVALID_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitstream& Bitstream::operator>(QScriptString& string) {
|
Bitstream& Bitstream::operator>(QScriptString& string) {
|
||||||
QString rawString;
|
QString rawString;
|
||||||
*this >> rawString;
|
*this >> rawString;
|
||||||
string = ScriptCache::getInstance()->getEngine()->toStringHandle(rawString);
|
string = (rawString == INVALID_STRING) ? QScriptString() :
|
||||||
|
ScriptCache::getInstance()->getEngine()->toStringHandle(rawString);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -990,17 +1496,17 @@ QHash<int, const TypeStreamer*>& Bitstream::getTypeStreamers() {
|
||||||
return typeStreamers;
|
return typeStreamers;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*>& Bitstream::getEnumStreamers() {
|
const QHash<ScopeNamePair, const TypeStreamer*>& Bitstream::getEnumStreamers() {
|
||||||
static QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> enumStreamers = createEnumStreamers();
|
static QHash<ScopeNamePair, const TypeStreamer*> enumStreamers = createEnumStreamers();
|
||||||
return enumStreamers;
|
return enumStreamers;
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> Bitstream::createEnumStreamers() {
|
QHash<ScopeNamePair, const TypeStreamer*> Bitstream::createEnumStreamers() {
|
||||||
QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> enumStreamers;
|
QHash<ScopeNamePair, const TypeStreamer*> enumStreamers;
|
||||||
foreach (const QMetaObject* metaObject, getMetaObjects()) {
|
foreach (const QMetaObject* metaObject, getMetaObjects()) {
|
||||||
for (int i = 0; i < metaObject->enumeratorCount(); i++) {
|
for (int i = 0; i < metaObject->enumeratorCount(); i++) {
|
||||||
QMetaEnum metaEnum = metaObject->enumerator(i);
|
QMetaEnum metaEnum = metaObject->enumerator(i);
|
||||||
const TypeStreamer*& streamer = enumStreamers[QPair<QByteArray, QByteArray>(metaEnum.scope(), metaEnum.name())];
|
const TypeStreamer*& streamer = enumStreamers[ScopeNamePair(metaEnum.scope(), metaEnum.name())];
|
||||||
if (!streamer) {
|
if (!streamer) {
|
||||||
streamer = new EnumTypeStreamer(metaEnum);
|
streamer = new EnumTypeStreamer(metaEnum);
|
||||||
}
|
}
|
||||||
|
@ -1022,15 +1528,15 @@ QHash<QByteArray, const TypeStreamer*> Bitstream::createEnumStreamersByName() {
|
||||||
return enumStreamersByName;
|
return enumStreamersByName;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QHash<const QMetaObject*, QVector<PropertyReader> >& Bitstream::getPropertyReaders() {
|
const QHash<const QMetaObject*, PropertyReaderVector>& Bitstream::getPropertyReaders() {
|
||||||
static QHash<const QMetaObject*, QVector<PropertyReader> > propertyReaders = createPropertyReaders();
|
static QHash<const QMetaObject*, PropertyReaderVector> propertyReaders = createPropertyReaders();
|
||||||
return propertyReaders;
|
return propertyReaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<const QMetaObject*, QVector<PropertyReader> > Bitstream::createPropertyReaders() {
|
QHash<const QMetaObject*, PropertyReaderVector> Bitstream::createPropertyReaders() {
|
||||||
QHash<const QMetaObject*, QVector<PropertyReader> > propertyReaders;
|
QHash<const QMetaObject*, PropertyReaderVector> propertyReaders;
|
||||||
foreach (const QMetaObject* metaObject, getMetaObjects()) {
|
foreach (const QMetaObject* metaObject, getMetaObjects()) {
|
||||||
QVector<PropertyReader>& readers = propertyReaders[metaObject];
|
PropertyReaderVector& readers = propertyReaders[metaObject];
|
||||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||||
QMetaProperty property = metaObject->property(i);
|
QMetaProperty property = metaObject->property(i);
|
||||||
if (!property.isStored()) {
|
if (!property.isStored()) {
|
||||||
|
@ -1039,7 +1545,7 @@ QHash<const QMetaObject*, QVector<PropertyReader> > Bitstream::createPropertyRea
|
||||||
const TypeStreamer* streamer;
|
const TypeStreamer* streamer;
|
||||||
if (property.isEnumType()) {
|
if (property.isEnumType()) {
|
||||||
QMetaEnum metaEnum = property.enumerator();
|
QMetaEnum metaEnum = property.enumerator();
|
||||||
streamer = getEnumStreamers().value(QPair<QByteArray, QByteArray>(
|
streamer = getEnumStreamers().value(ScopeNamePair(
|
||||||
QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())),
|
QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())),
|
||||||
QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name()))));
|
QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name()))));
|
||||||
} else {
|
} else {
|
||||||
|
@ -1053,15 +1559,15 @@ QHash<const QMetaObject*, QVector<PropertyReader> > Bitstream::createPropertyRea
|
||||||
return propertyReaders;
|
return propertyReaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QHash<const QMetaObject*, QVector<PropertyWriter> >& Bitstream::getPropertyWriters() {
|
const QHash<const QMetaObject*, PropertyWriterVector>& Bitstream::getPropertyWriters() {
|
||||||
static QHash<const QMetaObject*, QVector<PropertyWriter> > propertyWriters = createPropertyWriters();
|
static QHash<const QMetaObject*, PropertyWriterVector> propertyWriters = createPropertyWriters();
|
||||||
return propertyWriters;
|
return propertyWriters;
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<const QMetaObject*, QVector<PropertyWriter> > Bitstream::createPropertyWriters() {
|
QHash<const QMetaObject*, PropertyWriterVector> Bitstream::createPropertyWriters() {
|
||||||
QHash<const QMetaObject*, QVector<PropertyWriter> > propertyWriters;
|
QHash<const QMetaObject*, PropertyWriterVector> propertyWriters;
|
||||||
foreach (const QMetaObject* metaObject, getMetaObjects()) {
|
foreach (const QMetaObject* metaObject, getMetaObjects()) {
|
||||||
QVector<PropertyWriter>& writers = propertyWriters[metaObject];
|
PropertyWriterVector& writers = propertyWriters[metaObject];
|
||||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||||
QMetaProperty property = metaObject->property(i);
|
QMetaProperty property = metaObject->property(i);
|
||||||
if (!property.isStored()) {
|
if (!property.isStored()) {
|
||||||
|
@ -1070,7 +1576,7 @@ QHash<const QMetaObject*, QVector<PropertyWriter> > Bitstream::createPropertyWri
|
||||||
const TypeStreamer* streamer;
|
const TypeStreamer* streamer;
|
||||||
if (property.isEnumType()) {
|
if (property.isEnumType()) {
|
||||||
QMetaEnum metaEnum = property.enumerator();
|
QMetaEnum metaEnum = property.enumerator();
|
||||||
streamer = getEnumStreamers().value(QPair<QByteArray, QByteArray>(
|
streamer = getEnumStreamers().value(ScopeNamePair(
|
||||||
QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())),
|
QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())),
|
||||||
QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name()))));
|
QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name()))));
|
||||||
} else {
|
} else {
|
||||||
|
@ -1324,7 +1830,7 @@ void FieldReader::readDelta(Bitstream& in, const TypeStreamer* streamer, QVarian
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject,
|
ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject,
|
||||||
const QVector<PropertyReader>& properties) :
|
const PropertyReaderVector& properties) :
|
||||||
_className(className),
|
_className(className),
|
||||||
_metaObject(metaObject),
|
_metaObject(metaObject),
|
||||||
_properties(properties) {
|
_properties(properties) {
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
class QColor;
|
class QColor;
|
||||||
class QDataStream;
|
class QDataStream;
|
||||||
|
class QScriptValue;
|
||||||
class QUrl;
|
class QUrl;
|
||||||
|
|
||||||
class Attribute;
|
class Attribute;
|
||||||
|
@ -44,6 +45,10 @@ class TypeStreamer;
|
||||||
|
|
||||||
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
|
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
|
||||||
|
|
||||||
|
typedef QPair<QByteArray, QByteArray> ScopeNamePair;
|
||||||
|
typedef QVector<PropertyReader> PropertyReaderVector;
|
||||||
|
typedef QVector<PropertyWriter> PropertyWriterVector;
|
||||||
|
|
||||||
/// Streams integer identifiers that conform to the following pattern: each ID encountered in the stream is either one that
|
/// Streams integer identifiers that conform to the following pattern: each ID encountered in the stream is either one that
|
||||||
/// has been sent (received) before, or is one more than the highest previously encountered ID (starting at zero). This allows
|
/// has been sent (received) before, or is one more than the highest previously encountered ID (starting at zero). This allows
|
||||||
/// us to use the minimum number of bits to encode the IDs.
|
/// us to use the minimum number of bits to encode the IDs.
|
||||||
|
@ -287,11 +292,15 @@ public:
|
||||||
template<class T> void writeDelta(const T& value, const T& reference);
|
template<class T> void writeDelta(const T& value, const T& reference);
|
||||||
template<class T> void readDelta(T& value, const T& reference);
|
template<class T> void readDelta(T& value, const T& reference);
|
||||||
|
|
||||||
|
void writeRawDelta(const QVariant& value, const QVariant& reference);
|
||||||
void readRawDelta(QVariant& value, const QVariant& reference);
|
void readRawDelta(QVariant& value, const QVariant& reference);
|
||||||
|
|
||||||
void writeRawDelta(const QObject* value, const QObject* reference);
|
void writeRawDelta(const QObject* value, const QObject* reference);
|
||||||
void readRawDelta(QObject*& value, const QObject* reference);
|
void readRawDelta(QObject*& value, const QObject* reference);
|
||||||
|
|
||||||
|
void writeRawDelta(const QScriptValue& value, const QScriptValue& reference);
|
||||||
|
void readRawDelta(QScriptValue& value, const QScriptValue& reference);
|
||||||
|
|
||||||
template<class T> void writeRawDelta(const T& value, const T& reference);
|
template<class T> void writeRawDelta(const T& value, const T& reference);
|
||||||
template<class T> void readRawDelta(T& value, const T& reference);
|
template<class T> void readRawDelta(T& value, const T& reference);
|
||||||
|
|
||||||
|
@ -316,9 +325,15 @@ public:
|
||||||
Bitstream& operator<<(uint value);
|
Bitstream& operator<<(uint value);
|
||||||
Bitstream& operator>>(uint& value);
|
Bitstream& operator>>(uint& value);
|
||||||
|
|
||||||
|
Bitstream& operator<<(qint64 value);
|
||||||
|
Bitstream& operator>>(qint64& value);
|
||||||
|
|
||||||
Bitstream& operator<<(float value);
|
Bitstream& operator<<(float value);
|
||||||
Bitstream& operator>>(float& value);
|
Bitstream& operator>>(float& value);
|
||||||
|
|
||||||
|
Bitstream& operator<<(double value);
|
||||||
|
Bitstream& operator>>(double& value);
|
||||||
|
|
||||||
Bitstream& operator<<(const glm::vec3& value);
|
Bitstream& operator<<(const glm::vec3& value);
|
||||||
Bitstream& operator>>(glm::vec3& value);
|
Bitstream& operator>>(glm::vec3& value);
|
||||||
|
|
||||||
|
@ -337,6 +352,12 @@ public:
|
||||||
Bitstream& operator<<(const QUrl& url);
|
Bitstream& operator<<(const QUrl& url);
|
||||||
Bitstream& operator>>(QUrl& url);
|
Bitstream& operator>>(QUrl& url);
|
||||||
|
|
||||||
|
Bitstream& operator<<(const QDateTime& dateTime);
|
||||||
|
Bitstream& operator>>(QDateTime& dateTime);
|
||||||
|
|
||||||
|
Bitstream& operator<<(const QRegExp& regExp);
|
||||||
|
Bitstream& operator>>(QRegExp& regExp);
|
||||||
|
|
||||||
Bitstream& operator<<(const QVariant& value);
|
Bitstream& operator<<(const QVariant& value);
|
||||||
Bitstream& operator>>(QVariant& value);
|
Bitstream& operator>>(QVariant& value);
|
||||||
|
|
||||||
|
@ -372,6 +393,9 @@ public:
|
||||||
Bitstream& operator<<(const QScriptString& string);
|
Bitstream& operator<<(const QScriptString& string);
|
||||||
Bitstream& operator>>(QScriptString& string);
|
Bitstream& operator>>(QScriptString& string);
|
||||||
|
|
||||||
|
Bitstream& operator<<(const QScriptValue& value);
|
||||||
|
Bitstream& operator>>(QScriptValue& value);
|
||||||
|
|
||||||
Bitstream& operator<<(const SharedObjectPointer& object);
|
Bitstream& operator<<(const SharedObjectPointer& object);
|
||||||
Bitstream& operator>>(SharedObjectPointer& object);
|
Bitstream& operator>>(SharedObjectPointer& object);
|
||||||
|
|
||||||
|
@ -422,14 +446,18 @@ private:
|
||||||
static QHash<QByteArray, const QMetaObject*>& getMetaObjects();
|
static QHash<QByteArray, const QMetaObject*>& getMetaObjects();
|
||||||
static QMultiHash<const QMetaObject*, const QMetaObject*>& getMetaObjectSubClasses();
|
static QMultiHash<const QMetaObject*, const QMetaObject*>& getMetaObjectSubClasses();
|
||||||
static QHash<int, const TypeStreamer*>& getTypeStreamers();
|
static QHash<int, const TypeStreamer*>& getTypeStreamers();
|
||||||
static const QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*>& getEnumStreamers();
|
|
||||||
static QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> createEnumStreamers();
|
static const QHash<ScopeNamePair, const TypeStreamer*>& getEnumStreamers();
|
||||||
|
static QHash<ScopeNamePair, const TypeStreamer*> createEnumStreamers();
|
||||||
|
|
||||||
static const QHash<QByteArray, const TypeStreamer*>& getEnumStreamersByName();
|
static const QHash<QByteArray, const TypeStreamer*>& getEnumStreamersByName();
|
||||||
static QHash<QByteArray, const TypeStreamer*> createEnumStreamersByName();
|
static QHash<QByteArray, const TypeStreamer*> createEnumStreamersByName();
|
||||||
static const QHash<const QMetaObject*, QVector<PropertyReader> >& getPropertyReaders();
|
|
||||||
static QHash<const QMetaObject*, QVector<PropertyReader> > createPropertyReaders();
|
static const QHash<const QMetaObject*, PropertyReaderVector>& getPropertyReaders();
|
||||||
static const QHash<const QMetaObject*, QVector<PropertyWriter> >& getPropertyWriters();
|
static QHash<const QMetaObject*, PropertyReaderVector> createPropertyReaders();
|
||||||
static QHash<const QMetaObject*, QVector<PropertyWriter> > createPropertyWriters();
|
|
||||||
|
static const QHash<const QMetaObject*, PropertyWriterVector>& getPropertyWriters();
|
||||||
|
static QHash<const QMetaObject*, PropertyWriterVector> createPropertyWriters();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class T> inline void Bitstream::writeDelta(const T& value, const T& reference) {
|
template<class T> inline void Bitstream::writeDelta(const T& value, const T& reference) {
|
||||||
|
|
|
@ -13,11 +13,95 @@
|
||||||
|
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QScriptEngine>
|
#include <QScriptEngine>
|
||||||
|
#include <QScriptValueIterator>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
|
|
||||||
#include "AttributeRegistry.h"
|
#include "AttributeRegistry.h"
|
||||||
#include "ScriptCache.h"
|
#include "ScriptCache.h"
|
||||||
|
|
||||||
|
static int scriptValueMetaTypeId = qRegisterMetaType<QScriptValue>();
|
||||||
|
static bool scriptValueComparators = QMetaType::registerComparators<QScriptValue>();
|
||||||
|
|
||||||
|
bool operator==(const QScriptValue& first, const QScriptValue& second) {
|
||||||
|
if (first.isUndefined()) {
|
||||||
|
return second.isUndefined();
|
||||||
|
|
||||||
|
} else if (first.isNull()) {
|
||||||
|
return second.isNull();
|
||||||
|
|
||||||
|
} else if (first.isBool()) {
|
||||||
|
return second.isBool() && first.toBool() == second.toBool();
|
||||||
|
|
||||||
|
} else if (first.isNumber()) {
|
||||||
|
return second.isNumber() && first.toNumber() == second.toNumber();
|
||||||
|
|
||||||
|
} else if (first.isString()) {
|
||||||
|
return second.isString() && first.toString() == second.toString();
|
||||||
|
|
||||||
|
} else if (first.isVariant()) {
|
||||||
|
return second.isVariant() && first.toVariant() == second.toVariant();
|
||||||
|
|
||||||
|
} else if (first.isQObject()) {
|
||||||
|
return second.isQObject() && first.toQObject() == second.toQObject();
|
||||||
|
|
||||||
|
} else if (first.isQMetaObject()) {
|
||||||
|
return second.isQMetaObject() && first.toQMetaObject() == second.toQMetaObject();
|
||||||
|
|
||||||
|
} else if (first.isDate()) {
|
||||||
|
return second.isDate() && first.toDateTime() == second.toDateTime();
|
||||||
|
|
||||||
|
} else if (first.isRegExp()) {
|
||||||
|
return second.isRegExp() && first.toRegExp() == second.toRegExp();
|
||||||
|
|
||||||
|
} else if (first.isArray()) {
|
||||||
|
if (!second.isArray()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int length = first.property(ScriptCache::getInstance()->getLengthString()).toInt32();
|
||||||
|
if (second.property(ScriptCache::getInstance()->getLengthString()).toInt32() != length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
if (first.property(i) != second.property(i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else if (first.isObject()) {
|
||||||
|
if (!second.isObject()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int propertyCount = 0;
|
||||||
|
for (QScriptValueIterator it(first); it.hasNext(); ) {
|
||||||
|
it.next();
|
||||||
|
if (second.property(it.scriptName()) != it.value()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
propertyCount++;
|
||||||
|
}
|
||||||
|
// make sure the second has exactly as many properties as the first
|
||||||
|
for (QScriptValueIterator it(second); it.hasNext(); ) {
|
||||||
|
it.next();
|
||||||
|
if (--propertyCount < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return !second.isValid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const QScriptValue& first, const QScriptValue& second) {
|
||||||
|
return !(first == second);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const QScriptValue& first, const QScriptValue& second) {
|
||||||
|
return first.lessThan(second);
|
||||||
|
}
|
||||||
|
|
||||||
ScriptCache* ScriptCache::getInstance() {
|
ScriptCache* ScriptCache::getInstance() {
|
||||||
static ScriptCache cache;
|
static ScriptCache cache;
|
||||||
return &cache;
|
return &cache;
|
||||||
|
|
|
@ -65,6 +65,12 @@ private:
|
||||||
QScriptString _generatorString;
|
QScriptString _generatorString;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QScriptValue)
|
||||||
|
|
||||||
|
bool operator==(const QScriptValue& first, const QScriptValue& second);
|
||||||
|
bool operator!=(const QScriptValue& first, const QScriptValue& second);
|
||||||
|
bool operator<(const QScriptValue& first, const QScriptValue& second);
|
||||||
|
|
||||||
/// A program loaded from the network.
|
/// A program loaded from the network.
|
||||||
class NetworkProgram : public Resource {
|
class NetworkProgram : public Resource {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
|
@ -186,6 +186,11 @@ bool ModelTreeElement::findDetailedRayIntersection(const glm::vec3& origin, cons
|
||||||
if (fbxGeometry && fbxGeometry->meshExtents.isValid()) {
|
if (fbxGeometry && fbxGeometry->meshExtents.isValid()) {
|
||||||
Extents extents = fbxGeometry->meshExtents;
|
Extents extents = fbxGeometry->meshExtents;
|
||||||
|
|
||||||
|
// NOTE: If the model has a bad mesh, then extents will be 0,0,0 & 0,0,0
|
||||||
|
if (extents.minimum == extents.maximum && extents.minimum == glm::vec3(0,0,0)) {
|
||||||
|
extents.maximum = glm::vec3(1.0f,1.0f,1.0f); // in this case we will simulate the unit cube
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: these extents are model space, so we need to scale and center them accordingly
|
// NOTE: these extents are model space, so we need to scale and center them accordingly
|
||||||
// size is our "target size in world space"
|
// size is our "target size in world space"
|
||||||
// we need to set our model scale so that the extents of the mesh, fit in a cube that size...
|
// we need to set our model scale so that the extents of the mesh, fit in a cube that size...
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
|
|
||||||
#include "NetworkPacket.h"
|
#include "NetworkPacket.h"
|
||||||
|
|
||||||
void NetworkPacket::copyContents(const SharedNodePointer& destinationNode, const QByteArray& packet) {
|
void NetworkPacket::copyContents(const SharedNodePointer& node, const QByteArray& packet) {
|
||||||
if (packet.size() && packet.size() <= MAX_PACKET_SIZE) {
|
if (packet.size() && packet.size() <= MAX_PACKET_SIZE) {
|
||||||
_destinationNode = destinationNode;
|
_node = node;
|
||||||
_byteArray = packet;
|
_byteArray = packet;
|
||||||
} else {
|
} else {
|
||||||
qDebug(">>> NetworkPacket::copyContents() unexpected length = %d", packet.size());
|
qDebug(">>> NetworkPacket::copyContents() unexpected length = %d", packet.size());
|
||||||
|
@ -27,28 +27,28 @@ void NetworkPacket::copyContents(const SharedNodePointer& destinationNode, const
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkPacket::NetworkPacket(const NetworkPacket& packet) {
|
NetworkPacket::NetworkPacket(const NetworkPacket& packet) {
|
||||||
copyContents(packet.getDestinationNode(), packet.getByteArray());
|
copyContents(packet.getNode(), packet.getByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkPacket::NetworkPacket(const SharedNodePointer& destinationNode, const QByteArray& packet) {
|
NetworkPacket::NetworkPacket(const SharedNodePointer& node, const QByteArray& packet) {
|
||||||
copyContents(destinationNode, packet);
|
copyContents(node, packet);
|
||||||
};
|
};
|
||||||
|
|
||||||
// copy assignment
|
// copy assignment
|
||||||
NetworkPacket& NetworkPacket::operator=(NetworkPacket const& other) {
|
NetworkPacket& NetworkPacket::operator=(NetworkPacket const& other) {
|
||||||
copyContents(other.getDestinationNode(), other.getByteArray());
|
copyContents(other.getNode(), other.getByteArray());
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_MOVE_SEMANTICS
|
#ifdef HAS_MOVE_SEMANTICS
|
||||||
// move, same as copy, but other packet won't be used further
|
// move, same as copy, but other packet won't be used further
|
||||||
NetworkPacket::NetworkPacket(NetworkPacket && packet) {
|
NetworkPacket::NetworkPacket(NetworkPacket && packet) {
|
||||||
copyContents(packet.getDestinationNode(), packet.getByteArray());
|
copyContents(packet.getNode(), packet.getByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
// move assignment
|
// move assignment
|
||||||
NetworkPacket& NetworkPacket::operator=(NetworkPacket&& other) {
|
NetworkPacket& NetworkPacket::operator=(NetworkPacket&& other) {
|
||||||
copyContents(other.getDestinationNode(), other.getByteArray());
|
copyContents(other.getNode(), other.getByteArray());
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
/// Storage of not-yet processed inbound, or not yet sent outbound generic UDP network packet
|
/// Storage of not-yet processed inbound, or not yet sent outbound generic UDP network packet
|
||||||
class NetworkPacket {
|
class NetworkPacket {
|
||||||
public:
|
public:
|
||||||
|
NetworkPacket() { }
|
||||||
NetworkPacket(const NetworkPacket& packet); // copy constructor
|
NetworkPacket(const NetworkPacket& packet); // copy constructor
|
||||||
NetworkPacket& operator= (const NetworkPacket& other); // copy assignment
|
NetworkPacket& operator= (const NetworkPacket& other); // copy assignment
|
||||||
|
|
||||||
|
@ -34,15 +35,15 @@ public:
|
||||||
NetworkPacket& operator= (NetworkPacket&& other); // move assignment
|
NetworkPacket& operator= (NetworkPacket&& other); // move assignment
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NetworkPacket(const SharedNodePointer& destinationNode, const QByteArray& byteArray);
|
NetworkPacket(const SharedNodePointer& node, const QByteArray& byteArray);
|
||||||
|
|
||||||
const SharedNodePointer& getDestinationNode() const { return _destinationNode; }
|
const SharedNodePointer& getNode() const { return _node; }
|
||||||
const QByteArray& getByteArray() const { return _byteArray; }
|
const QByteArray& getByteArray() const { return _byteArray; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void copyContents(const SharedNodePointer& destinationNode, const QByteArray& byteArray);
|
void copyContents(const SharedNodePointer& node, const QByteArray& byteArray);
|
||||||
|
|
||||||
SharedNodePointer _destinationNode;
|
SharedNodePointer _node;
|
||||||
QByteArray _byteArray;
|
QByteArray _byteArray;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -281,7 +281,7 @@ void NodeList::processSTUNResponse(const QByteArray& packet) {
|
||||||
int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN;
|
int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN;
|
||||||
|
|
||||||
uint8_t addressFamily = 0;
|
uint8_t addressFamily = 0;
|
||||||
memcpy(&addressFamily, packet.data(), sizeof(addressFamily));
|
memcpy(&addressFamily, packet.data() + byteIndex, sizeof(addressFamily));
|
||||||
|
|
||||||
byteIndex += sizeof(addressFamily);
|
byteIndex += sizeof(addressFamily);
|
||||||
|
|
||||||
|
|
|
@ -271,7 +271,7 @@ bool PacketSender::nonThreadedProcess() {
|
||||||
unlock();
|
unlock();
|
||||||
|
|
||||||
// send the packet through the NodeList...
|
// send the packet through the NodeList...
|
||||||
NodeList::getInstance()->writeDatagram(temporary.getByteArray(), temporary.getDestinationNode());
|
NodeList::getInstance()->writeDatagram(temporary.getByteArray(), temporary.getNode());
|
||||||
packetsSentThisCall++;
|
packetsSentThisCall++;
|
||||||
_packetsOverCheckInterval++;
|
_packetsOverCheckInterval++;
|
||||||
_totalPacketsSent++;
|
_totalPacketsSent++;
|
||||||
|
|
|
@ -17,13 +17,14 @@ void ReceivedPacketProcessor::terminating() {
|
||||||
_hasPackets.wakeAll();
|
_hasPackets.wakeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& destinationNode, const QByteArray& packet) {
|
void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) {
|
||||||
// Make sure our Node and NodeList knows we've heard from this node.
|
// Make sure our Node and NodeList knows we've heard from this node.
|
||||||
destinationNode->setLastHeardMicrostamp(usecTimestampNow());
|
sendingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||||
|
|
||||||
NetworkPacket networkPacket(destinationNode, packet);
|
NetworkPacket networkPacket(sendingNode, packet);
|
||||||
lock();
|
lock();
|
||||||
_packets.push_back(networkPacket);
|
_packets.push_back(networkPacket);
|
||||||
|
_nodePacketCounts[sendingNode->getUUID()]++;
|
||||||
unlock();
|
unlock();
|
||||||
|
|
||||||
// Make sure to wake our actual processing thread because we now have packets for it to process.
|
// Make sure to wake our actual processing thread because we now have packets for it to process.
|
||||||
|
@ -42,8 +43,15 @@ bool ReceivedPacketProcessor::process() {
|
||||||
NetworkPacket& packet = _packets.front(); // get the oldest packet
|
NetworkPacket& packet = _packets.front(); // get the oldest packet
|
||||||
NetworkPacket temporary = packet; // make a copy of the packet in case the vector is resized on us
|
NetworkPacket temporary = packet; // make a copy of the packet in case the vector is resized on us
|
||||||
_packets.erase(_packets.begin()); // remove the oldest packet
|
_packets.erase(_packets.begin()); // remove the oldest packet
|
||||||
|
_nodePacketCounts[temporary.getNode()->getUUID()]--;
|
||||||
unlock(); // let others add to the packets
|
unlock(); // let others add to the packets
|
||||||
processPacket(temporary.getDestinationNode(), temporary.getByteArray()); // process our temporary copy
|
processPacket(temporary.getNode(), temporary.getByteArray()); // process our temporary copy
|
||||||
}
|
}
|
||||||
return isStillRunning(); // keep running till they terminate us
|
return isStillRunning(); // keep running till they terminate us
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReceivedPacketProcessor::nodeKilled(SharedNodePointer node) {
|
||||||
|
lock();
|
||||||
|
_nodePacketCounts.remove(node->getUUID());
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
|
@ -28,20 +28,26 @@ public:
|
||||||
/// \param packetData pointer to received data
|
/// \param packetData pointer to received data
|
||||||
/// \param ssize_t packetLength size of received data
|
/// \param ssize_t packetLength size of received data
|
||||||
/// \thread network receive thread
|
/// \thread network receive thread
|
||||||
void queueReceivedPacket(const SharedNodePointer& destinationNode, const QByteArray& packet);
|
void queueReceivedPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
|
||||||
|
|
||||||
/// Are there received packets waiting to be processed
|
/// Are there received packets waiting to be processed
|
||||||
bool hasPacketsToProcess() const { return _packets.size() > 0; }
|
bool hasPacketsToProcess() const { return _packets.size() > 0; }
|
||||||
|
|
||||||
|
/// Are there received packets waiting to be processed from a certain node
|
||||||
|
bool hasPacketsToProcessFrom(const SharedNodePointer& sendingNode) const {
|
||||||
|
return _nodePacketCounts[sendingNode->getUUID()] > 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// How many received packets waiting are to be processed
|
/// How many received packets waiting are to be processed
|
||||||
int packetsToProcessCount() const { return _packets.size(); }
|
int packetsToProcessCount() const { return _packets.size(); }
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void nodeKilled(SharedNodePointer node);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Callback for processing of recieved packets. Implement this to process the incoming packets.
|
/// Callback for processing of recieved packets. Implement this to process the incoming packets.
|
||||||
/// \param sockaddr& senderAddress the address of the sender
|
/// \param SharedNodePointer& sendingNode the node that sent this packet
|
||||||
/// \param packetData pointer to received data
|
/// \param QByteArray& the packet to be processed
|
||||||
/// \param ssize_t packetLength size of received data
|
|
||||||
/// \thread "this" individual processing thread
|
|
||||||
virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) = 0;
|
virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) = 0;
|
||||||
|
|
||||||
/// Implements generic processing behavior for this thread.
|
/// Implements generic processing behavior for this thread.
|
||||||
|
@ -51,7 +57,9 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::vector<NetworkPacket> _packets;
|
QVector<NetworkPacket> _packets;
|
||||||
|
QHash<QUuid, int> _nodePacketCounts;
|
||||||
|
|
||||||
QWaitCondition _hasPackets;
|
QWaitCondition _hasPackets;
|
||||||
QMutex _waitingOnPacketsMutex;
|
QMutex _waitingOnPacketsMutex;
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <QScriptValueIterator>
|
||||||
|
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
#include <MetavoxelMessages.h>
|
#include <MetavoxelMessages.h>
|
||||||
|
@ -41,6 +43,8 @@ static int streamedBytesReceived = 0;
|
||||||
static int sharedObjectsCreated = 0;
|
static int sharedObjectsCreated = 0;
|
||||||
static int sharedObjectsDestroyed = 0;
|
static int sharedObjectsDestroyed = 0;
|
||||||
static int objectMutationsPerformed = 0;
|
static int objectMutationsPerformed = 0;
|
||||||
|
static int scriptObjectsCreated = 0;
|
||||||
|
static int scriptMutationsPerformed = 0;
|
||||||
|
|
||||||
static QByteArray createRandomBytes(int minimumSize, int maximumSize) {
|
static QByteArray createRandomBytes(int minimumSize, int maximumSize) {
|
||||||
QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0);
|
QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0);
|
||||||
|
@ -79,6 +83,48 @@ static TestSharedObjectA::TestFlags getRandomTestFlags() {
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QScriptValue createRandomScriptValue(bool complex = false) {
|
||||||
|
scriptObjectsCreated++;
|
||||||
|
switch (randIntInRange(0, complex ? 5 : 3)) {
|
||||||
|
case 0:
|
||||||
|
return QScriptValue(QScriptValue::NullValue);
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return QScriptValue(randomBoolean());
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
return QScriptValue(randFloat());
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
return QScriptValue(QString(createRandomBytes()));
|
||||||
|
|
||||||
|
case 4: {
|
||||||
|
int length = randIntInRange(2, 6);
|
||||||
|
QScriptValue value = ScriptCache::getInstance()->getEngine()->newArray(length);
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
value.setProperty(i, createRandomScriptValue());
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
QScriptValue value = ScriptCache::getInstance()->getEngine()->newObject();
|
||||||
|
if (randomBoolean()) {
|
||||||
|
value.setProperty("foo", createRandomScriptValue());
|
||||||
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
value.setProperty("bar", createRandomScriptValue());
|
||||||
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
value.setProperty("baz", createRandomScriptValue());
|
||||||
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
value.setProperty("bong", createRandomScriptValue());
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static TestMessageC createRandomMessageC() {
|
static TestMessageC createRandomMessageC() {
|
||||||
TestMessageC message;
|
TestMessageC message;
|
||||||
message.foo = randomBoolean();
|
message.foo = randomBoolean();
|
||||||
|
@ -86,6 +132,7 @@ static TestMessageC createRandomMessageC() {
|
||||||
message.baz = randFloat();
|
message.baz = randFloat();
|
||||||
message.bong.foo = createRandomBytes();
|
message.bong.foo = createRandomBytes();
|
||||||
message.bong.baz = getRandomTestEnum();
|
message.bong.baz = getRandomTestEnum();
|
||||||
|
message.bizzle = createRandomScriptValue(true);
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,6 +248,7 @@ bool MetavoxelTests::run() {
|
||||||
datagramsReceived << "with" << bytesReceived << "bytes";
|
datagramsReceived << "with" << bytesReceived << "bytes";
|
||||||
qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed;
|
qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed;
|
||||||
qDebug() << "Performed" << objectMutationsPerformed << "object mutations";
|
qDebug() << "Performed" << objectMutationsPerformed << "object mutations";
|
||||||
|
qDebug() << "Created" << scriptObjectsCreated << "script objects, mutated" << scriptMutationsPerformed;
|
||||||
qDebug();
|
qDebug();
|
||||||
|
|
||||||
qDebug() << "Running serialization tests...";
|
qDebug() << "Running serialization tests...";
|
||||||
|
@ -284,7 +332,7 @@ static QVariant createRandomMessage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static SharedObjectPointer mutate(const SharedObjectPointer& state) {
|
static SharedObjectPointer mutate(const SharedObjectPointer& state) {
|
||||||
switch(randIntInRange(0, 3)) {
|
switch (randIntInRange(0, 4)) {
|
||||||
case 0: {
|
case 0: {
|
||||||
SharedObjectPointer newState = state->clone(true);
|
SharedObjectPointer newState = state->clone(true);
|
||||||
static_cast<TestSharedObjectA*>(newState.data())->setFoo(randFloat());
|
static_cast<TestSharedObjectA*>(newState.data())->setFoo(randFloat());
|
||||||
|
@ -303,6 +351,38 @@ static SharedObjectPointer mutate(const SharedObjectPointer& state) {
|
||||||
objectMutationsPerformed++;
|
objectMutationsPerformed++;
|
||||||
return newState;
|
return newState;
|
||||||
}
|
}
|
||||||
|
case 3: {
|
||||||
|
SharedObjectPointer newState = state->clone(true);
|
||||||
|
QScriptValue oldValue = static_cast<TestSharedObjectA*>(newState.data())->getBizzle();
|
||||||
|
QScriptValue newValue = ScriptCache::getInstance()->getEngine()->newObject();
|
||||||
|
for (QScriptValueIterator it(oldValue); it.hasNext(); ) {
|
||||||
|
it.next();
|
||||||
|
newValue.setProperty(it.scriptName(), it.value());
|
||||||
|
}
|
||||||
|
switch (randIntInRange(0, 2)) {
|
||||||
|
case 0: {
|
||||||
|
QScriptValue oldArray = oldValue.property("foo");
|
||||||
|
int oldLength = oldArray.property(ScriptCache::getInstance()->getLengthString()).toInt32();
|
||||||
|
QScriptValue newArray = ScriptCache::getInstance()->getEngine()->newArray(oldLength);
|
||||||
|
for (int i = 0; i < oldLength; i++) {
|
||||||
|
newArray.setProperty(i, oldArray.property(i));
|
||||||
|
}
|
||||||
|
newArray.setProperty(randIntInRange(0, oldLength - 1), createRandomScriptValue(true));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
newValue.setProperty("bar", QScriptValue(randFloat()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
newValue.setProperty("baz", createRandomScriptValue(true));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
static_cast<TestSharedObjectA*>(newState.data())->setBizzle(newValue);
|
||||||
|
scriptMutationsPerformed++;
|
||||||
|
objectMutationsPerformed++;
|
||||||
|
return newState;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -504,6 +584,9 @@ TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) :
|
||||||
_baz(baz),
|
_baz(baz),
|
||||||
_bong(bong) {
|
_bong(bong) {
|
||||||
sharedObjectsCreated++;
|
sharedObjectsCreated++;
|
||||||
|
|
||||||
|
_bizzle = ScriptCache::getInstance()->getEngine()->newObject();
|
||||||
|
_bizzle.setProperty("foo", ScriptCache::getInstance()->getEngine()->newArray(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
TestSharedObjectA::~TestSharedObjectA() {
|
TestSharedObjectA::~TestSharedObjectA() {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <QVariantList>
|
#include <QVariantList>
|
||||||
|
|
||||||
#include <DatagramSequencer.h>
|
#include <DatagramSequencer.h>
|
||||||
|
#include <ScriptCache.h>
|
||||||
|
|
||||||
class SequencedTestMessage;
|
class SequencedTestMessage;
|
||||||
|
|
||||||
|
@ -96,6 +97,7 @@ class TestSharedObjectA : public SharedObject {
|
||||||
Q_PROPERTY(float foo READ getFoo WRITE setFoo NOTIFY fooChanged)
|
Q_PROPERTY(float foo READ getFoo WRITE setFoo NOTIFY fooChanged)
|
||||||
Q_PROPERTY(TestEnum baz READ getBaz WRITE setBaz)
|
Q_PROPERTY(TestEnum baz READ getBaz WRITE setBaz)
|
||||||
Q_PROPERTY(TestFlags bong READ getBong WRITE setBong)
|
Q_PROPERTY(TestFlags bong READ getBong WRITE setBong)
|
||||||
|
Q_PROPERTY(QScriptValue bizzle READ getBizzle WRITE setBizzle)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -116,6 +118,9 @@ public:
|
||||||
void setBong(TestFlags bong) { _bong = bong; }
|
void setBong(TestFlags bong) { _bong = bong; }
|
||||||
TestFlags getBong() const { return _bong; }
|
TestFlags getBong() const { return _bong; }
|
||||||
|
|
||||||
|
void setBizzle(const QScriptValue& bizzle) { _bizzle = bizzle; }
|
||||||
|
const QScriptValue& getBizzle() const { return _bizzle; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
void fooChanged(float foo);
|
void fooChanged(float foo);
|
||||||
|
@ -125,6 +130,7 @@ private:
|
||||||
float _foo;
|
float _foo;
|
||||||
TestEnum _baz;
|
TestEnum _baz;
|
||||||
TestFlags _bong;
|
TestFlags _bong;
|
||||||
|
QScriptValue _bizzle;
|
||||||
};
|
};
|
||||||
|
|
||||||
DECLARE_ENUM_METATYPE(TestSharedObjectA, TestEnum)
|
DECLARE_ENUM_METATYPE(TestSharedObjectA, TestEnum)
|
||||||
|
@ -204,6 +210,7 @@ class TestMessageC : STREAM public TestMessageA {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
STREAM TestMessageB bong;
|
STREAM TestMessageB bong;
|
||||||
|
STREAM QScriptValue bizzle;
|
||||||
};
|
};
|
||||||
|
|
||||||
DECLARE_STREAMABLE_METATYPE(TestMessageC)
|
DECLARE_STREAMABLE_METATYPE(TestMessageC)
|
||||||
|
|
Loading…
Reference in a new issue