Merge branch 'master' of https://github.com/highfidelity/hifi into local_voxels

This commit is contained in:
Atlante45 2014-03-03 16:47:42 -08:00
commit f1ce034c35
140 changed files with 1230 additions and 2008 deletions

View file

@ -39,4 +39,4 @@ add_subdirectory(domain-server)
add_subdirectory(interface)
add_subdirectory(tests)
add_subdirectory(voxel-edit)
add_subdirectory(SvoViewer)
add_subdirectory(svo-viewer)

View file

@ -19,7 +19,6 @@
#include <OctalCode.h>
#include <PacketHeaders.h>
#include <JurisdictionListener.h>
#include <SceneUtils.h>
#include <SharedUtil.h>
#include <VoxelEditPacketSender.h>
#include <VoxelTree.h>

View file

@ -27,6 +27,9 @@ Agent::Agent(const QByteArray& packet) :
_voxelEditSender(),
_particleEditSender()
{
// be the parent of the script engine so it gets moved when we do
_scriptEngine.setParent(this);
_scriptEngine.getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender);
_scriptEngine.getParticlesScriptingInterface()->setPacketSender(&_particleEditSender);
}

View file

@ -36,7 +36,7 @@ AvatarMixer::AvatarMixer(const QByteArray& packet) :
}
void attachAvatarDataToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) {
if (!newNode->getLinkedData()) {
newNode->setLinkedData(new AvatarMixerClientData());
}
}

View file

@ -23,13 +23,13 @@ OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer
_packetData(),
_nodeMissingCount(0)
{
qDebug() << "client connected";
_myServer->clientConnected();
qDebug() << "client connected - starting sending thread";
OctreeServer::clientConnected();
}
OctreeSendThread::~OctreeSendThread() {
qDebug() << "client disconnected";
_myServer->clientDisconnected();
qDebug() << "client disconnected - ending sending thread";
OctreeServer::clientDisconnected();
}
@ -43,7 +43,6 @@ bool OctreeSendThread::process() {
}
quint64 start = usecTimestampNow();
bool gotLock = false;
// don't do any send processing until the initial load of the octree is complete...
if (_myServer->isInitialLoadComplete()) {
@ -51,25 +50,19 @@ bool OctreeSendThread::process() {
if (node) {
_nodeMissingCount = 0;
// make sure the node list doesn't kill our node while we're using it
if (node->getMutex().tryLock()) {
gotLock = true;
OctreeQueryNode* nodeData = NULL;
OctreeQueryNode* nodeData = NULL;
nodeData = (OctreeQueryNode*) node->getLinkedData();
nodeData = (OctreeQueryNode*) node->getLinkedData();
int packetsSent = 0;
int packetsSent = 0;
// Sometimes the node data has not yet been linked, in which case we can't really do anything
if (nodeData) {
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
}
packetsSent = packetDistributor(node, nodeData, viewFrustumChanged);
// Sometimes the node data has not yet been linked, in which case we can't really do anything
if (nodeData) {
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
}
node->getMutex().unlock(); // we're done with this node for now.
packetsSent = packetDistributor(node, nodeData, viewFrustumChanged);
}
} else {
_nodeMissingCount++;
@ -81,7 +74,7 @@ bool OctreeSendThread::process() {
}
// Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap
if (isStillRunning() && gotLock) {
if (isStillRunning()) {
// dynamically sleep until we need to fire off the next set of octree elements
int elapsed = (usecTimestampNow() - start);
int usecToSleep = OCTREE_SEND_INTERVAL_USECS - elapsed;
@ -90,9 +83,12 @@ bool OctreeSendThread::process() {
PerformanceWarning warn(false,"OctreeSendThread... usleep()",false,&_usleepTime,&_usleepCalls);
usleep(usecToSleep);
} else {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
std::cout << "Last send took too much time, not sleeping!\n";
if (true || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) {
qDebug() << "Last send took too much time (" << (elapsed / USECS_PER_MSEC)
<<" msecs), barely sleeping 1 usec!\n";
}
const int MIN_USEC_TO_SLEEP = 1;
usleep(MIN_USEC_TO_SLEEP);
}
}
@ -114,6 +110,13 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQuer
int packetsSent = 0;
// double check that the node has an active socket, otherwise, don't send...
quint64 lockWaitStart = usecTimestampNow();
QMutexLocker locker(&node->getMutex());
quint64 lockWaitEnd = usecTimestampNow();
float lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart);
OctreeServer::trackNodeWaitTime(lockWaitElapsedUsec);
const HifiSockAddr* nodeAddress = node->getActiveSocket();
if (!nodeAddress) {
return packetsSent; // without sending...
@ -440,9 +443,19 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue
isFullScene, &nodeData->stats, _myServer->getJurisdiction());
quint64 lockWaitStart = usecTimestampNow();
_myServer->getOctree()->lockForRead();
quint64 lockWaitEnd = usecTimestampNow();
float lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart);
OctreeServer::trackTreeWaitTime(lockWaitElapsedUsec);
nodeData->stats.encodeStarted();
quint64 encodeStart = usecTimestampNow();
bytesWritten = _myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params);
quint64 encodeEnd = usecTimestampNow();
int encodeElapsedMsec = (encodeEnd - encodeStart)/USECS_PER_MSEC;
OctreeServer::trackEncodeTime(encodeElapsedMsec);
// If after calling encodeTreeBitstream() there are no nodes left to send, then we know we've
// sent the entire scene. We want to know this below so we'll actually write this content into
@ -555,7 +568,8 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue
quint64 end = usecTimestampNow();
int elapsedmsec = (end - start)/1000;
int elapsedmsec = (end - start)/USECS_PER_MSEC;
OctreeServer::trackLoopTime(elapsedmsec);
quint64 endCompressCalls = OctreePacketData::getCompressContentCalls();
int elapsedCompressCalls = endCompressCalls - startCompressCalls;
@ -563,7 +577,6 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue
quint64 endCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000;
int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs;
if (elapsedmsec > 100) {
if (elapsedmsec > 1000) {
int elapsedsec = (end - start)/1000000;

View file

@ -20,10 +20,13 @@
OctreeServer* OctreeServer::_instance = NULL;
int OctreeServer::_clientCount = 0;
SimpleMovingAverage OctreeServer::_averageLoopTime(10000);
SimpleMovingAverage OctreeServer::_averageEncodeTime(10000);
SimpleMovingAverage OctreeServer::_averageTreeWaitTime(10000);
SimpleMovingAverage OctreeServer::_averageNodeWaitTime(10000);
void OctreeServer::attachQueryNodeToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) {
if (!newNode->getLinkedData()) {
OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode();
newQueryNodeData->resetOctreePacket(true); // don't bump sequence
newNode->setLinkedData(newQueryNodeData);
@ -51,6 +54,7 @@ OctreeServer::OctreeServer(const QByteArray& packet) :
_startedUSecs(usecTimestampNow())
{
_instance = this;
_averageLoopTime.updateAverage(0);
}
OctreeServer::~OctreeServer() {
@ -266,7 +270,27 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
.arg(locale.toString((uint)getPacketsTotalPerSecond()).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString(" Total Clients Connected: %1 clients\r\n\r\n")
.arg(locale.toString((uint)getCurrentClientCount()).rightJustified(COLUMN_WIDTH, ' '));
float averageLoopTime = getAverageLoopTime();
statsString += QString().sprintf(" Average packetLoop() time: %5.2f msecs\r\n", averageLoopTime);
qDebug() << "averageLoopTime=" << averageLoopTime;
float averageEncodeTime = getAverageEncodeTime();
statsString += QString().sprintf(" Average encode time: %5.2f msecs\r\n", averageEncodeTime);
qDebug() << "averageEncodeTime=" << averageEncodeTime;
float averageTreeWaitTime = getAverageTreeWaitTime();
statsString += QString().sprintf(" Average tree lock wait time: %7.2f usecs\r\n", averageTreeWaitTime);
qDebug() << "averageTreeWaitTime=" << averageTreeWaitTime;
float averageNodeWaitTime = getAverageNodeWaitTime();
statsString += QString().sprintf(" Average node lock wait time: %7.2f usecs\r\n", averageNodeWaitTime);
qDebug() << "averageNodeWaitTime=" << averageNodeWaitTime;
statsString += QString("\r\n");
statsString += QString(" Total Outbound Packets: %1 packets\r\n")
.arg(locale.toString((uint)totalOutboundPackets).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString(" Total Outbound Bytes: %1 bytes\r\n")
@ -584,6 +608,9 @@ void OctreeServer::run() {
NodeList* nodeList = NodeList::getInstance();
nodeList->setOwnerType(getMyNodeType());
connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer)));
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)),SLOT(nodeKilled(SharedNodePointer)));
// we need to ask the DS about agents so we can ping/reply with them
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
@ -689,7 +716,7 @@ void OctreeServer::run() {
strftime(localBuffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm);
// Convert now to tm struct for UTC
tm* gmtm = gmtime(&_started);
if (gmtm != NULL) {
if (gmtm) {
strftime(utcBuffer, MAX_TIME_LENGTH, " [%m/%d/%Y %X UTC]", gmtm);
}
qDebug() << "Now running... started at: " << localBuffer << utcBuffer;
@ -703,3 +730,19 @@ void OctreeServer::run() {
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
}
void OctreeServer::nodeAdded(SharedNodePointer node) {
// we might choose to use this notifier to track clients in a pending state
}
void OctreeServer::nodeKilled(SharedNodePointer node) {
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
if (nodeData) {
// Note: It should be safe to do this without locking the node, because if any other threads
// are using the SharedNodePointer, then they have a reference to the SharedNodePointer and the deleteLater()
// won't actually delete it until all threads have released their references to the pointer.
// But we can and should clear the linked data so that no one else tries to access it.
nodeData->deleteLater();
node->setLinkedData(NULL);
}
}

View file

@ -74,11 +74,24 @@ public:
static void attachQueryNodeToNode(Node* newNode);
static void trackLoopTime(float time) { _averageLoopTime.updateAverage(time); }
static float getAverageLoopTime() { return _averageLoopTime.getAverage(); }
static void trackEncodeTime(float time) { _averageEncodeTime.updateAverage(time); }
static float getAverageEncodeTime() { return _averageEncodeTime.getAverage(); }
static void trackTreeWaitTime(float time) { _averageTreeWaitTime.updateAverage(time); }
static float getAverageTreeWaitTime() { return _averageTreeWaitTime.getAverage(); }
static void trackNodeWaitTime(float time) { _averageNodeWaitTime.updateAverage(time); }
static float getAverageNodeWaitTime() { return _averageNodeWaitTime.getAverage(); }
bool handleHTTPRequest(HTTPConnection* connection, const QString& path);
public slots:
/// runs the voxel server assignment
void run();
void readPendingDatagrams();
void nodeAdded(SharedNodePointer node);
void nodeKilled(SharedNodePointer node);
protected:
void parsePayload();
@ -109,6 +122,10 @@ protected:
quint64 _startedUSecs;
static int _clientCount;
static SimpleMovingAverage _averageLoopTime;
static SimpleMovingAverage _averageEncodeTime;
static SimpleMovingAverage _averageTreeWaitTime;
static SimpleMovingAverage _averageNodeWaitTime;
};
#endif // __octree_server__OctreeServer__

View file

@ -3,7 +3,7 @@
// hifi
//
// Created by Stephen Birarda on 2/20/14.
// Modified by Philip on 2/26/14
// Modified by Philip on 3/3/14
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// This is an example script that demonstrates an NPC avatar.
@ -25,49 +25,55 @@ var CHANCE_OF_BIG_MOVE = 0.1;
var isMoving = false;
var isTurningHead = false;
var isPlayingAudio = false;
var X_MIN = 0.0;
var X_MAX = 5.0;
var Z_MIN = 0.0;
var Z_MAX = 5.0;
var STARTING_RANGE = 18.0;
var MAX_RANGE = 25.0;
var MOVE_RANGE_SMALL = 0.5;
var MOVE_RANGE_BIG = 5.0;
var TURN_RANGE = 45.0;
var MOVE_RANGE_BIG = Math.max(X_MAX - X_MIN, Z_MAX - Z_MIN) / 2.0;
var TURN_RANGE = 70.0;
var STOP_TOLERANCE = 0.05;
var MOVE_RATE = 0.05;
var TURN_RATE = 0.15;
var PITCH_RATE = 0.20;
var PITCH_RANGE = 30.0;
var firstPosition = { x: getRandomFloat(0, STARTING_RANGE), y: 0, z: getRandomFloat(0, STARTING_RANGE) };
var firstPosition = { x: getRandomFloat(X_MIN, X_MAX), y: 0, z: getRandomFloat(Z_MIN, Z_MAX) };
var targetPosition = { x: 0, y: 0, z: 0 };
var targetDirection = { x: 0, y: 0, z: 0, w: 0 };
var currentDirection = { x: 0, y: 0, z: 0, w: 0 };
var targetHeadPitch = 0.0;
var sounds = [];
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh2.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh3.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh4.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh5.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh6.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh7.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh8.raw"));
loadSounds();
function clamp(val, min, max){
return Math.max(min, Math.min(max, val))
}
// Play a random sound from a list of conversational audio clips
function audioDone() {
isPlayingAudio = false;
}
var AVERAGE_AUDIO_LENGTH = 8000;
function playRandomSound(position) {
var whichSound = Math.floor((Math.random() * sounds.length) % sounds.length);
var audioOptions = new AudioInjectionOptions();
audioOptions.volume = 0.25 + (Math.random() * 0.75);
audioOptions.position = position;
Audio.playSound(sounds[whichSound], audioOptions);
print("PlaySound " + whichSound);
if (!isPlayingAudio) {
var whichSound = Math.floor((Math.random() * sounds.length) % sounds.length);
var audioOptions = new AudioInjectionOptions();
audioOptions.volume = 0.25 + (Math.random() * 0.75);
audioOptions.position = position;
Audio.playSound(sounds[whichSound], audioOptions);
isPlayingAudio = true;
Script.setTimeout(audioDone, AVERAGE_AUDIO_LENGTH);
}
}
// change the avatar's position to the random one
Avatar.position = firstPosition;
Avatar.position = firstPosition;
// pick an integer between 1 and 20 for the face model for this bot
botNumber = getRandomInt(1, 100);
@ -93,7 +99,7 @@ if (botNumber <= 20) {
// there is no need to change the body model - we're using the default
Avatar.faceModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newFaceFilePrefix + ".fst";
Avatar.skeletonModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newBodyFilePrefix + ".fst";
Avatar.billboardURL = "https://dl.dropboxusercontent.com/u/1864924/bot-billboard.png";
Avatar.billboardURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/billboards/bot" + botNumber + ".png";
Agent.isAvatar = true;
@ -102,6 +108,10 @@ function updateBehavior() {
playRandomSound(Avatar.position);
}
if (isPlayingAudio) {
Avatar.handPosition = Vec3.sum(Avatar.position, Quat.getFront(Avatar.orientation));
}
if (!isTurningHead && (Math.random() < CHANCE_OF_HEAD_TURNING)) {
targetHeadPitch = getRandomFloat(-PITCH_RANGE, PITCH_RANGE);
isTurningHead = true;
@ -120,6 +130,9 @@ function updateBehavior() {
} else {
targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_SMALL)));
}
targetPosition.x = clamp(targetPosition.x, X_MIN, X_MAX);
targetPosition.z = clamp(targetPosition.z, Z_MIN, Z_MAX);
isMoving = true;
} else {
Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(Vec3.subtract(targetPosition, Avatar.position), MOVE_RATE));
@ -128,10 +141,56 @@ function updateBehavior() {
isMoving = false;
}
}
if (Vec3.length(Avatar.position) > MAX_RANGE) {
// Don't let our happy little person get out of the cage
isMoving = false;
Avatar.position = { x: 0, y: 0, z: 0 };
}
}
Script.willSendVisualDataCallback.connect(updateBehavior);
function loadSounds() {
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/AB1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Anchorman2.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/B1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/B1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Bale1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Bandcamp.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Big1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Big2.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Brian1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Buster1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/CES1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/CES2.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/CES3.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/CES4.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Carrie1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Carrie3.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Charlotte1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/EN1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/EN2.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/EN3.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Eugene1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Francesco1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Italian1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Japanese1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Leigh1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Lucille1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Lucille2.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/MeanGirls.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Murray2.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Nigel1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/PennyLane.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Pitt1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Ricardo.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/SN.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Sake1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Samantha1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Samantha2.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Spicoli1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Supernatural.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Swearengen1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/TheDude.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Tony.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Triumph1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Uma1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Walken1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Walken2.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Z1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Z2.raw"));
}

View file

@ -945,19 +945,32 @@ function setupMenus() {
// hook up menus
Menu.menuItemEvent.connect(menuItemEvent);
// delete the standard application menu item
Menu.addSeparator("Edit", "Voxels");
// add our menuitems
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Voxels", isSeparator: true, beforeItem: "Physics" });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Cut", shortcutKey: "CTRL+X", afterItem: "Voxels" });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Copy", shortcutKey: "CTRL+C", afterItem: "Cut" });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste", shortcutKey: "CTRL+V", afterItem: "Copy" });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Nudge", shortcutKey: "CTRL+N", afterItem: "Paste" });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", shortcutKeyEvent: { text: "backspace" }, afterItem: "Nudge" });
Menu.addSeparator("File", "Voxels");
Menu.addMenuItem({ menuName: "File", menuItemName: "Voxels", isSeparator: true, beforeItem: "Settings" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Voxels", shortcutKey: "CTRL+E", afterItem: "Voxels" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Voxels", shortcutKey: "CTRL+I", afterItem: "Export Voxels" });
}
function cleanupMenus() {
// delete our menuitems
Menu.removeSeparator("Edit", "Voxels");
Menu.removeMenuItem("Edit", "Cut");
Menu.removeMenuItem("Edit", "Copy");
Menu.removeMenuItem("Edit", "Paste");
Menu.removeMenuItem("Edit", "Nudge");
Menu.removeMenuItem("Edit", "Delete");
Menu.removeSeparator("File", "Voxels");
Menu.removeMenuItem("File", "Export Voxels");
Menu.removeMenuItem("File", "Import Voxels");
}
function menuItemEvent(menuItem) {
// handle clipboard items
@ -1277,12 +1290,55 @@ function touchEndEvent(event) {
}
}
var lastFingerAddVoxel = { x: -1, y: -1, z: -1}; // off of the build-able area
var lastFingerDeleteVoxel = { x: -1, y: -1, z: -1}; // off of the build-able area
function checkControllers() {
var controllersPerPalm = 2; // palm and finger
for (var palm = 0; palm < 2; palm++) {
var palmController = palm * controllersPerPalm;
var fingerTipController = palmController + 1;
var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController);
var BUTTON_COUNT = 6;
var BUTTON_BASE = palm * BUTTON_COUNT;
var BUTTON_1 = BUTTON_BASE + 1;
var BUTTON_2 = BUTTON_BASE + 2;
var FINGERTIP_VOXEL_SIZE = 0.05;
if (Controller.isButtonPressed(BUTTON_1)) {
if (Vec3.length(Vec3.subtract(fingerTipPosition,lastFingerAddVoxel)) > (FINGERTIP_VOXEL_SIZE / 2)) {
if (whichColor == -1) {
newColor = { red: colors[0].red, green: colors[0].green, blue: colors[0].blue };
} else {
newColor = { red: colors[whichColor].red, green: colors[whichColor].green, blue: colors[whichColor].blue };
}
Voxels.eraseVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE);
Voxels.setVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE,
newColor.red, newColor.green, newColor.blue);
lastFingerAddVoxel = fingerTipPosition;
}
} else if (Controller.isButtonPressed(BUTTON_2)) {
if (Vec3.length(Vec3.subtract(fingerTipPosition,lastFingerDeleteVoxel)) > (FINGERTIP_VOXEL_SIZE / 2)) {
Voxels.eraseVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE);
lastFingerDeleteVoxel = fingerTipPosition;
}
}
}
}
function update() {
var newWindowDimensions = Controller.getViewportDimensions();
if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) {
windowDimensions = newWindowDimensions;
moveTools();
}
if (editToolsOn) {
checkControllers();
}
}
function wheelEvent(event) {
@ -1339,6 +1395,7 @@ function scriptEnding() {
Overlays.deleteOverlay(thumb);
Controller.releaseKeyEvents({ text: "+" });
Controller.releaseKeyEvents({ text: "-" });
cleanupMenus();
}
Script.scriptEnding.connect(scriptEnding);

167
examples/voxelDrumming.js Normal file
View file

@ -0,0 +1,167 @@
//
// voxelDrumming.js
// hifi
//
// Created by Brad Hefta-Gaub on 2/14/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// This is an example script that demonstrates use of the Overlays, Controller, and Audio classes
//
// It adds Hydra controller "fingertip on voxels" drumming
//
Menu.addMenuItem({
menuName: "Developer > Hand Options",
menuItemName: "Voxel Drumming",
isCheckable: true,
isChecked: false
});
var collisionCenter = new Array();
collisionCenter[0] = { x: 0, y: 0, z: 0};
collisionCenter[1] = { x: 0, y: 0, z: 0};
var collisionAge = new Array();
collisionAge[0] = 0;
collisionAge[1] = 0;
var collisionDuration = new Array();
collisionDuration[0] = 0;
collisionDuration[1] = 0;
var isColliding = new Array();
isColliding[0] = false;
isColliding[1] = false;
var highlightVoxel = Overlays.addOverlay("cube",
{
position: { x: 0, y: 0, z: 0},
size: 0,
color: { red: 0, green: 0, blue: 0 },
visible: false,
lineWidth: 3,
solid: false
});
var collisionBubble = new Array();
collisionBubble[0] = Overlays.addOverlay("sphere",
{
position: { x: 0, y: 0, z: 0},
size: 0,
color: { red: 0, green: 0, blue: 0 },
alpha: 0.5,
visible: false
});
collisionBubble[1] = Overlays.addOverlay("sphere",
{
position: { x: 0, y: 0, z: 0},
size: 0,
color: { red: 0, green: 0, blue: 0 },
alpha: 0.5,
visible: false
});
var audioOptions = new AudioInjectionOptions();
audioOptions.position = { x: MyAvatar.position.x, y: MyAvatar.position.y + 1, z: MyAvatar.position.z };
audioOptions.volume = 1;
function clamp(valueToClamp, minValue, maxValue) {
return Math.max(minValue, Math.min(maxValue, valueToClamp));
}
function produceCollisionSound(palm, voxelDetail) {
// Collision between finger and a voxel plays sound
var palmVelocity = Controller.getSpatialControlVelocity(palm * 2);
var speed = Vec3.length(palmVelocity);
var fingerTipPosition = Controller.getSpatialControlPosition(palm * 2 + 1);
var deltaTime = 1/60; //close enough
var LOWEST_FREQUENCY = 100.0;
var HERTZ_PER_RGB = 3.0;
var DECAY_PER_SAMPLE = 0.0005;
var DURATION_MAX = 2.0;
var MIN_VOLUME = 0.1;
var volume = MIN_VOLUME + clamp(speed, 0.0, (1.0 - MIN_VOLUME));
var duration = volume;
collisionCenter[palm] = fingerTipPosition;
collisionAge[palm] = deltaTime;
collisionDuration[palm] = duration;
var voxelBrightness = voxelDetail.red + voxelDetail.green + voxelDetail.blue;
var frequency = LOWEST_FREQUENCY + (voxelBrightness * HERTZ_PER_RGB);
audioOptions.position = fingerTipPosition;
Audio.startDrumSound(volume, frequency, DURATION_MAX, DECAY_PER_SAMPLE, audioOptions);
}
function update() {
var deltaTime = 1/60; //close enough
// Voxel Drumming with fingertips if enabled
if (Menu.isOptionChecked("Voxel Drumming")) {
for (var palm = 0; palm < 2; palm++) {
var fingerTipPosition = Controller.getSpatialControlPosition(palm * 2 + 1);
var voxel = Voxels.getVoxelEnclosingPoint(fingerTipPosition);
if (voxel.s > 0) {
if (!isColliding[palm]) {
// Collision has just started
isColliding[palm] = true;
produceCollisionSound(palm, voxel);
// Set highlight voxel
Overlays.editOverlay(highlightVoxel,
{
position: { x: voxel.x, y: voxel.y, z: voxel.z},
size: voxel.s + 0.002,
color: { red: voxel.red + 128, green: voxel.green + 128, blue: voxel.blue + 128 },
visible: true
});
}
} else {
if (isColliding[palm]) {
// Collision has just ended
isColliding[palm] = false;
Overlays.editOverlay(highlightVoxel, { visible: false });
}
}
if (collisionAge[palm] > 0) {
collisionAge[palm] += deltaTime;
}
// If hand/voxel collision has happened, render a little expanding sphere
if (collisionAge[palm] > 0) {
var opacity = clamp(1 - (collisionAge[palm] / collisionDuration[palm]), 0, 1);
var size = collisionAge[palm] * 0.25;
Overlays.editOverlay(collisionBubble[palm],
{
position: { x: collisionCenter[palm].x, y: collisionCenter[palm].y, z: collisionCenter[palm].z},
size: size,
color: { red: 255, green: 0, blue: 0 },
alpha: 0.5 * opacity,
visible: true
});
if (collisionAge[palm] > collisionDuration[palm]) {
collisionAge[palm] = 0;
Overlays.editOverlay(collisionBubble[palm], { visible: false });
}
}
} // palm loop
} // menu item check
}
Script.willSendVisualDataCallback.connect(update);
function scriptEnding() {
Overlays.deleteOverlay(highlightVoxel);
Overlays.deleteOverlay(collisionBubble[0]);
Overlays.deleteOverlay(collisionBubble[1]);
Menu.removeMenuItem("Developer > Hand Options","Voxel Drumming");
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -140,8 +140,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_touchAvgY(0.0f),
_isTouchPressed(false),
_mousePressed(false),
_isHoverVoxel(false),
_isHighlightVoxel(false),
_chatEntryOn(false),
_audio(&_audioScope, STARTUP_JITTER_SAMPLES),
_enableProcessVoxelsThread(true),
@ -364,6 +362,8 @@ Application::~Application() {
_myAvatar = NULL;
delete _glWidget;
AccountManager::getInstance().destroy();
}
void Application::restoreSizeAndPosition() {
@ -746,9 +746,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_S:
if (isShifted && !isMeta) {
_voxels.collectStatsForTreesAndVBOs();
} else if (isShifted && isMeta) {
if (isShifted && isMeta) {
Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings);
} else if (!isShifted && isMeta) {
takeSnapshot();
@ -1035,11 +1033,6 @@ void Application::mousePressEvent(QMouseEvent* event) {
return;
}
if (!_isHoverVoxel || _myAvatar->getLookAtTargetAvatar()) {
// disable for now
// _pieMenu.mousePressEvent(_mouseX, _mouseY);
}
} else if (event->button() == Qt::RightButton) {
// right click items here
}
@ -1159,7 +1152,7 @@ void Application::dropEvent(QDropEvent *event) {
}
SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(snapshotPath);
if (snapshotData != NULL) {
if (snapshotData) {
if (!snapshotData->getDomain().isEmpty()) {
Menu::getInstance()->goToDomain(snapshotData->getDomain());
}
@ -1294,7 +1287,7 @@ void Application::removeVoxel(glm::vec3 position,
_voxelEditSender.sendVoxelEditMessage(PacketTypeVoxelErase, voxel);
// delete it locally to see the effect immediately (and in case no voxel server is present)
_voxels.deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s);
_voxels.getTree()->deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s);
}
@ -1316,8 +1309,7 @@ void Application::makeVoxel(glm::vec3 position,
_voxelEditSender.sendVoxelEditMessage(message, voxel);
// create the voxel locally so it appears immediately
_voxels.createVoxel(voxel.x, voxel.y, voxel.z, voxel.s,
_voxels.getTree()->createVoxel(voxel.x, voxel.y, voxel.z, voxel.s,
voxel.red, voxel.green, voxel.blue,
isDestructive);
}
@ -1376,10 +1368,11 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) {
tr("Sparse Voxel Octree Files (*.svo)"));
QByteArray fileNameAscii = fileNameString.toLocal8Bit();
const char* fileName = fileNameAscii.data();
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
VoxelTreeElement* selectedNode = _voxels.getTree()->getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
if (selectedNode) {
VoxelTree exportTree;
_voxels.copySubTreeIntoNewTree(selectedNode, &exportTree, true);
getVoxelTree()->copySubTreeIntoNewTree(selectedNode, &exportTree, true);
exportTree.writeToSVOFile(fileName);
}
@ -1421,9 +1414,10 @@ void Application::copyVoxels(const VoxelDetail& sourceVoxel) {
}
// then copy onto it if there is something to copy
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
VoxelTreeElement* selectedNode = _voxels.getTree()->getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
if (selectedNode) {
_voxels.copySubTreeIntoNewTree(selectedNode, &_sharedVoxelSystem, true);
getVoxelTree()->copySubTreeIntoNewTree(selectedNode, _sharedVoxelSystem.getTree(), true);
_sharedVoxelSystem.forceRedrawEntireTree();
}
}
@ -1445,7 +1439,7 @@ void Application::pasteVoxelsToOctalCode(const unsigned char* octalCodeDestinati
void Application::pasteVoxels(const VoxelDetail& sourceVoxel) {
unsigned char* calculatedOctCode = NULL;
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
VoxelTreeElement* selectedNode = _voxels.getTree()->getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
// voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a
@ -1465,7 +1459,7 @@ void Application::pasteVoxels(const VoxelDetail& sourceVoxel) {
}
void Application::nudgeVoxelsByVector(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec) {
VoxelTreeElement* nodeToNudge = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
VoxelTreeElement* nodeToNudge = _voxels.getTree()->getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
if (nodeToNudge) {
_voxels.getTree()->nudgeSubTree(nodeToNudge, nudgeVec, _voxelEditSender);
}
@ -1623,20 +1617,6 @@ bool Application::isLookingAtMyAvatar(Avatar* avatar) {
return false;
}
void Application::renderHighlightVoxel(VoxelDetail voxel) {
glDisable(GL_LIGHTING);
glPushMatrix();
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
const float EDGE_EXPAND = 1.02f;
glColor3ub(voxel.red + 128, voxel.green + 128, voxel.blue + 128);
glTranslatef(voxel.x + voxel.s * 0.5f,
voxel.y + voxel.s * 0.5f,
voxel.z + voxel.s * 0.5f);
glLineWidth(2.0f);
glutWireCube(voxel.s * EDGE_EXPAND);
glPopMatrix();
}
void Application::updateMouseRay() {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
@ -1707,9 +1687,6 @@ void Application::updateMyAvatarLookAtPosition() {
if (_myAvatar->getLookAtTargetAvatar()) {
distance = glm::distance(_mouseRayOrigin,
static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead()->calculateAverageEyePosition());
} else if (_isHoverVoxel) {
distance = glm::distance(_mouseRayOrigin, getMouseVoxelWorldCoordinates(_hoverVoxel));
}
const float FIXED_MIN_EYE_DISTANCE = 0.3f;
float minEyeDistance = FIXED_MIN_EYE_DISTANCE + (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON ? 0.0f :
@ -1740,17 +1717,6 @@ void Application::updateMyAvatarLookAtPosition() {
_myAvatar->getHead()->setLookAtPosition(lookAtSpot);
}
void Application::updateHoverVoxels(float deltaTime, float& distance, BoxFace& face) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels()");
if (!_mousePressed) {
PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels() _voxels.findRayIntersection()");
_isHoverVoxel = _voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _hoverVoxel, distance, face);
}
}
void Application::updateHandAndTouch(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateHandAndTouch()");
@ -1899,11 +1865,6 @@ void Application::update(float deltaTime) {
_myAvatar->updateLookAtTargetAvatar();
updateMyAvatarLookAtPosition();
// Find the voxel we are hovering over, and respond if clicked
float distance;
BoxFace face;
updateHoverVoxels(deltaTime, distance, face); // clicking on voxels and making sounds
updateHandAndTouch(deltaTime); // Update state for touch sensors
updateLeap(deltaTime); // Leap finger-sensing device
updateSixense(deltaTime); // Razer Hydra controllers
@ -1988,7 +1949,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
// only send to the NodeTypes that are serverType
if (node->getActiveSocket() != NULL && node->getType() == serverType) {
if (node->getActiveSocket() && node->getType() == serverType) {
totalServers++;
// get the server bounds for this server
@ -2048,7 +2009,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
// only send to the NodeTypes that are serverType
if (node->getActiveSocket() != NULL && node->getType() == serverType) {
if (node->getActiveSocket() && node->getType() == serverType) {
// get the server bounds for this server
@ -2410,11 +2371,6 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
// restore default, white specular
glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR);
// Render the highlighted voxel
if (_isHighlightVoxel) {
renderHighlightVoxel(_highlightVoxel);
}
}
bool forceRenderMyHead = (whichCamera.getInterpolatedMode() == CAMERA_MODE_MIRROR);
@ -2476,7 +2432,7 @@ void Application::computeOffAxisFrustum(float& left, float& right, float& bottom
_viewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
}
const float WHITE_TEXT[] = { 0.93, 0.93, 0.93 };
const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
void Application::displayOverlay() {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displayOverlay()");
@ -2525,15 +2481,6 @@ void Application::displayOverlay() {
}
}
// testing rendering coverage map
if (Menu::getInstance()->isOptionChecked(MenuOption::CoverageMapV2)) {
renderCoverageMapV2();
}
if (Menu::getInstance()->isOptionChecked(MenuOption::CoverageMap)) {
renderCoverageMap();
}
// Show chat entry field
if (_chatEntryOn) {
_chatEntry.render(_glWidget->width(), _glWidget->height());
@ -2975,115 +2922,6 @@ glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) {
return screenPoint;
}
// render the coverage map on screen
void Application::renderCoverageMapV2() {
glDisable(GL_LIGHTING);
glLineWidth(2.0);
glBegin(GL_LINES);
glColor3f(0,1,1);
renderCoverageMapsV2Recursively(&_voxels.myCoverageMapV2);
glEnd();
glEnable(GL_LIGHTING);
}
void Application::renderCoverageMapsV2Recursively(CoverageMapV2* map) {
// render ourselves...
if (map->isCovered()) {
BoundingBox box = map->getBoundingBox();
glm::vec2 firstPoint = getScaledScreenPoint(box.getVertex(0));
glm::vec2 lastPoint(firstPoint);
for (int i = 1; i < box.getVertexCount(); i++) {
glm::vec2 thisPoint = getScaledScreenPoint(box.getVertex(i));
glVertex2f(lastPoint.x, lastPoint.y);
glVertex2f(thisPoint.x, thisPoint.y);
lastPoint = thisPoint;
}
glVertex2f(lastPoint.x, lastPoint.y);
glVertex2f(firstPoint.x, firstPoint.y);
} else {
// iterate our children and call render on them.
for (int i = 0; i < CoverageMapV2::NUMBER_OF_CHILDREN; i++) {
CoverageMapV2* childMap = map->getChild(i);
if (childMap) {
renderCoverageMapsV2Recursively(childMap);
}
}
}
}
// render the coverage map on screen
void Application::renderCoverageMap() {
glDisable(GL_LIGHTING);
glLineWidth(2.0);
glBegin(GL_LINES);
glColor3f(0,0,1);
renderCoverageMapsRecursively(&_voxels.myCoverageMap);
glEnd();
glEnable(GL_LIGHTING);
}
void Application::renderCoverageMapsRecursively(CoverageMap* map) {
for (int i = 0; i < map->getPolygonCount(); i++) {
OctreeProjectedPolygon* polygon = map->getPolygon(i);
if (polygon->getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_BOTTOM)) {
glColor3f(.5,0,0); // dark red
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT)) {
glColor3f(.5,.5,0); // dark yellow
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT)) {
glColor3f(.5,.5,.5); // gray
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT | PROJECTION_BOTTOM)) {
glColor3f(.5,0,.5); // dark magenta
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_BOTTOM)) {
glColor3f(.75,0,0); // red
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_TOP)) {
glColor3f(1,0,1); // magenta
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT | PROJECTION_TOP)) {
glColor3f(0,0,1); // Blue
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT | PROJECTION_TOP)) {
glColor3f(0,1,0); // green
} else if (polygon->getProjectionType() == (PROJECTION_NEAR)) {
glColor3f(1,1,0); // yellow
} else if (polygon->getProjectionType() == (PROJECTION_FAR | PROJECTION_RIGHT | PROJECTION_BOTTOM)) {
glColor3f(0,.5,.5); // dark cyan
} else {
glColor3f(1,0,0);
}
glm::vec2 firstPoint = getScaledScreenPoint(polygon->getVertex(0));
glm::vec2 lastPoint(firstPoint);
for (int i = 1; i < polygon->getVertexCount(); i++) {
glm::vec2 thisPoint = getScaledScreenPoint(polygon->getVertex(i));
glVertex2f(lastPoint.x, lastPoint.y);
glVertex2f(thisPoint.x, thisPoint.y);
lastPoint = thisPoint;
}
glVertex2f(lastPoint.x, lastPoint.y);
glVertex2f(firstPoint.x, firstPoint.y);
}
// iterate our children and call render on them.
for (int i = 0; i < CoverageMapV2::NUMBER_OF_CHILDREN; i++) {
CoverageMap* childMap = map->getChild(i);
if (childMap) {
renderCoverageMapsRecursively(childMap);
}
}
}
void Application::renderRearViewMirror(const QRect& region, bool billboard) {
bool eyeRelativeCamera = false;
if (billboard) {
@ -3333,7 +3171,7 @@ void Application::deleteVoxelAt(const VoxelDetail& voxel) {
_voxelEditSender.sendVoxelEditMessage(PacketTypeVoxelErase, voxel);
// delete it locally to see the effect immediately (and in case no voxel server is present)
_voxels.deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s);
_voxels.getTree()->deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s);
}
}

View file

@ -34,12 +34,15 @@
#include "BandwidthMeter.h"
#include "BuckyBalls.h"
#include "Camera.h"
#include "ControllerScriptingInterface.h"
#include "DatagramProcessor.h"
#include "Environment.h"
#include "FileLogger.h"
#include "GLCanvas.h"
#include "Menu.h"
#include "MetavoxelSystem.h"
#include "PacketHeaders.h"
#include "ParticleTreeRenderer.h"
#include "PieMenu.h"
#include "Stars.h"
#include "ViewFrustum.h"
@ -68,9 +71,6 @@
#include "ui/LodToolsDialog.h"
#include "ui/LogDialog.h"
#include "ui/UpdateDialog.h"
#include "FileLogger.h"
#include "ParticleTreeRenderer.h"
#include "ControllerScriptingInterface.h"
#include "ui/Overlays.h"
@ -154,6 +154,7 @@ public:
Camera* getCamera() { return &_myCamera; }
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
VoxelSystem* getVoxels() { return &_voxels; }
VoxelTree* getVoxelTree() { return _voxels.getTree(); }
ParticleTreeRenderer* getParticles() { return &_particles; }
MetavoxelSystem* getMetavoxels() { return &_metavoxels; }
VoxelSystem* getSharedVoxelSystem() { return &_sharedVoxelSystem; }
@ -214,10 +215,6 @@ public:
NodeToJurisdictionMap& getParticleServerJurisdictions() { return _particleServerJurisdictions; }
void pasteVoxelsToOctalCode(const unsigned char* octalCodeDestination);
/// set a voxel which is to be rendered with a highlight
void setHighlightVoxel(const VoxelDetail& highlightVoxel) { _highlightVoxel = highlightVoxel; }
void setIsHighlightVoxel(bool isHighlightVoxel) { _isHighlightVoxel = isHighlightVoxel; }
void skipVersion(QString latestVersion);
signals:
@ -261,12 +258,6 @@ private slots:
void setEnable3DTVMode(bool enable3DTVMode);
void cameraMenuChanged();
void renderCoverageMap();
void renderCoverageMapsRecursively(CoverageMap* map);
void renderCoverageMapV2();
void renderCoverageMapsV2Recursively(CoverageMapV2* map);
glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint);
void closeMirrorView();
@ -297,7 +288,6 @@ private:
void updateFaceshift();
void updateVisage();
void updateMyAvatarLookAtPosition();
void updateHoverVoxels(float deltaTime, float& distance, BoxFace& face);
void updateHandAndTouch(float deltaTime);
void updateLeap(float deltaTime);
void updateSixense(float deltaTime);
@ -313,7 +303,6 @@ private:
bool isLookingAtMyAvatar(Avatar* avatar);
void renderLookatIndicator(glm::vec3 pointOfInterest);
void renderHighlightVoxel(VoxelDetail voxel);
void updateMyAvatar(float deltaTime);
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions);
@ -434,12 +423,6 @@ private:
bool _mousePressed; // true if mouse has been pressed (clear when finished)
VoxelDetail _hoverVoxel; // Stuff about the voxel I am hovering or clicking
bool _isHoverVoxel;
VoxelDetail _highlightVoxel;
bool _isHighlightVoxel;
ChatEntry _chatEntry; // chat entry field
bool _chatEntryOn; // Whether to show the chat entry

View file

@ -272,7 +272,6 @@ Menu::Menu() :
SLOT(setFilter(bool)));
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::VoxelDrumming, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::PlaySlaps, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandsCollideWithSelf, 0, false);
@ -282,11 +281,6 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true);
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer);
addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, this, SLOT(runTests()));
addActionToQMenuAndActionHash(timingMenu,
MenuOption::TreeStats,
Qt::SHIFT | Qt::Key_S,
appInstance->getVoxels(),
SLOT(collectStatsForTreesAndVBOs()));
QMenu* frustumMenu = developerMenu->addMenu("View Frustum Debugging Tools");
addCheckableActionToQMenuAndActionHash(frustumMenu, MenuOption::DisplayFrustum, Qt::SHIFT | Qt::Key_F);
@ -302,62 +296,6 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings, Qt::CTRL | Qt::SHIFT | Qt::Key_P);
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::SuppressShortTimings, Qt::CTRL | Qt::SHIFT | Qt::Key_S);
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::AutomaticallyAuditTree);
addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::ShowAllLocalVoxels,
Qt::CTRL | Qt::Key_A,
appInstance->getVoxels(),
SLOT(showAllLocalVoxels()));
addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::KillLocalVoxels,
Qt::CTRL | Qt::Key_K,
appInstance, SLOT(doKillLocalVoxels()));
addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::RandomizeVoxelColors,
Qt::CTRL | Qt::Key_R,
appInstance->getVoxels(),
SLOT(randomizeVoxelColors()));
addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::FalseColorRandomly,
0,
appInstance->getVoxels(),
SLOT(falseColorizeRandom()));
addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::FalseColorEveryOtherVoxel,
0,
appInstance->getVoxels(),
SLOT(falseColorizeRandomEveryOther()));
addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::FalseColorByDistance,
0,
appInstance->getVoxels(),
SLOT(falseColorizeDistanceFromView()));
addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::FalseColorOutOfView,
0,
appInstance->getVoxels(),
SLOT(falseColorizeInView()));
addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::FalseColorBySource,
0,
appInstance->getVoxels(),
SLOT(falseColorizeBySource()));
addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::ShowTrueColors,
Qt::CTRL | Qt::Key_T,
appInstance->getVoxels(),
SLOT(trueColorize()));
addCheckableActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::CullSharedFaces,
Qt::CTRL | Qt::SHIFT | Qt::Key_C,
@ -372,22 +310,6 @@ Menu::Menu() :
appInstance->getVoxels(),
SLOT(showCulledSharedFaces()));
addDisabledActionAndSeparator(renderDebugMenu, "Coverage Maps");
addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::FalseColorOccluded,
0,
appInstance->getVoxels(),
SLOT(falseColorizeOccluded()));
addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::FalseColorOccludedV2,
0,
appInstance->getVoxels(),
SLOT(falseColorizeOccludedV2()));
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::CoverageMap, Qt::SHIFT | Qt::CTRL | Qt::Key_O);
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::CoverageMapV2, Qt::SHIFT | Qt::CTRL | Qt::Key_P);
QMenu* audioDebugMenu = developerMenu->addMenu("Audio Debugging Tools");
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction,
0,
@ -587,9 +509,24 @@ void Menu::handleViewFrustumOffsetKeyModifier(int key) {
}
}
void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName) {
destinationMenu->addSeparator();
(destinationMenu->addAction(actionName))->setEnabled(false);
void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName, int menuItemLocation) {
QAction* actionBefore = NULL;
if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) {
actionBefore = destinationMenu->actions()[menuItemLocation];
}
if (actionBefore) {
QAction* separator = new QAction("",destinationMenu);
destinationMenu->insertAction(actionBefore, separator);
separator->setSeparator(true);
QAction* separatorText = new QAction(actionName,destinationMenu);
separatorText->setEnabled(false);
destinationMenu->insertAction(actionBefore, separatorText);
} else {
destinationMenu->addSeparator();
(destinationMenu->addAction(actionName))->setEnabled(false);
}
}
QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
@ -650,11 +587,18 @@ void Menu::removeAction(QMenu* menu, const QString& actionName) {
}
void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) {
return _actionHash.value(menuOption)->setChecked(isChecked);
QAction* menu = _actionHash.value(menuOption);
if (menu) {
menu->setChecked(isChecked);
}
}
bool Menu::isOptionChecked(const QString& menuOption) {
return _actionHash.value(menuOption)->isChecked();
QAction* menu = _actionHash.value(menuOption);
if (menu) {
return menu->isChecked();
}
return false;
}
void Menu::triggerOption(const QString& menuOption) {
@ -1317,6 +1261,18 @@ int Menu::findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem) {
return UNSPECIFIED_POSITION; // not found
}
int Menu::positionBeforeSeparatorIfNeeded(QMenu* menu, int requestedPosition) {
QList<QAction*> menuActions = menu->actions();
if (requestedPosition > 1 && requestedPosition < menuActions.size()) {
QAction* beforeRequested = menuActions[requestedPosition - 1];
if (beforeRequested->isSeparator()) {
requestedPosition--;
}
}
return requestedPosition;
}
QMenu* Menu::addMenu(const QString& menuName) {
QStringList menuTree = menuName.split(">");
QMenu* addTo = NULL;
@ -1362,6 +1318,21 @@ void Menu::addSeparator(const QString& menuName, const QString& separatorName) {
}
}
void Menu::removeSeparator(const QString& menuName, const QString& separatorName) {
QMenu* menu = getMenu(menuName);
if (menu) {
int textAt = findPositionOfMenuItem(menu, separatorName);
QList<QAction*> menuActions = menu->actions();
QAction* separatorText = menuActions[textAt];
QAction* separatorLine = menuActions[textAt - 1];
if (separatorLine->isSeparator()) {
menu->removeAction(separatorText);
menu->removeAction(separatorLine);
}
}
QMenuBar::repaint();
}
void Menu::addMenuItem(const MenuItemProperties& properties) {
QMenu* menuObj = getMenu(properties.menuName);
if (menuObj) {
@ -1374,6 +1345,8 @@ void Menu::addMenuItem(const MenuItemProperties& properties) {
int requestedPosition = properties.position;
if (requestedPosition == UNSPECIFIED_POSITION && !properties.beforeItem.isEmpty()) {
requestedPosition = findPositionOfMenuItem(menuObj, properties.beforeItem);
// double check that the requested location wasn't a separator label
requestedPosition = positionBeforeSeparatorIfNeeded(menuObj, requestedPosition);
}
if (requestedPosition == UNSPECIFIED_POSITION && !properties.afterItem.isEmpty()) {
int afterPosition = findPositionOfMenuItem(menuObj, properties.afterItem);
@ -1382,9 +1355,11 @@ void Menu::addMenuItem(const MenuItemProperties& properties) {
}
}
QAction* menuItemAction;
if (properties.isCheckable) {
menuItemAction = addCheckableActionToQMenuAndActionHash(menuObj, properties.menuItemName,
QAction* menuItemAction = NULL;
if (properties.isSeparator) {
addDisabledActionAndSeparator(menuObj, properties.menuItemName, requestedPosition);
} else if (properties.isCheckable) {
menuItemAction = addCheckableActionToQMenuAndActionHash(menuObj, properties.menuItemName,
properties.shortcutKeySequence, properties.isChecked,
MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()), requestedPosition);
} else {
@ -1392,7 +1367,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) {
MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()),
QAction::NoRole, requestedPosition);
}
if (shortcut) {
if (shortcut && menuItemAction) {
connect(shortcut, SIGNAL(activated()), menuItemAction, SLOT(trigger()));
}
QMenuBar::repaint();

View file

@ -124,6 +124,7 @@ public slots:
QMenu* addMenu(const QString& menuName);
void removeMenu(const QString& menuName);
void addSeparator(const QString& menuName, const QString& separatorName);
void removeSeparator(const QString& menuName, const QString& separatorName);
void addMenuItem(const MenuItemProperties& properties);
void removeMenuItem(const QString& menuName, const QString& menuitem);
@ -152,7 +153,8 @@ private:
void scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set);
/// helper method to have separators with labels that are also compatible with OS X
void addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName);
void addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName,
int menuItemLocation = UNSPECIFIED_POSITION);
QAction* addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu,
const QString& actionName,
@ -172,6 +174,7 @@ private:
QAction* getMenuAction(const QString& menuName);
int findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem);
int positionBeforeSeparatorIfNeeded(QMenu* menu, int requestedPosition);
QMenu* getMenu(const QString& menuName);
@ -203,7 +206,6 @@ namespace MenuOption {
const QString Avatars = "Avatars";
const QString Atmosphere = "Atmosphere";
const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD";
const QString AutomaticallyAuditTree = "Automatically Audit Tree Stats";
const QString Bandwidth = "Bandwidth Display";
const QString BandwidthDetails = "Bandwidth Details";
const QString BuckyBalls = "Bucky Balls";
@ -213,8 +215,6 @@ namespace MenuOption {
const QString CollideWithParticles = "Collide With Particles";
const QString CollideWithVoxels = "Collide With Voxels";
const QString CollideWithEnvironment = "Collide With World Boundaries";
const QString CoverageMap = "Render Coverage Map";
const QString CoverageMapV2 = "Render Coverage Map V2";
const QString CullSharedFaces = "Cull Shared Voxel Faces";
const QString DecreaseAvatarSize = "Decrease Avatar Size";
const QString DecreaseVoxelSize = "Decrease Voxel Size";
@ -231,13 +231,6 @@ namespace MenuOption {
const QString HeadMouse = "Head Mouse";
const QString HandsCollideWithSelf = "Collide With Self";
const QString FaceshiftTCP = "Faceshift (TCP)";
const QString FalseColorByDistance = "FALSE Color By Distance";
const QString FalseColorBySource = "FALSE Color By Source";
const QString FalseColorEveryOtherVoxel = "FALSE Color Every Other Randomly";
const QString FalseColorOccluded = "FALSE Color Occluded Voxels";
const QString FalseColorOccludedV2 = "FALSE Color Occluded V2 Voxels";
const QString FalseColorOutOfView = "FALSE Color Voxel Out of View";
const QString FalseColorRandomly = "FALSE Color Voxels Randomly";
const QString FirstPerson = "First Person";
const QString FrameTimer = "Show Timer";
const QString FrustumRenderMode = "Render Mode";
@ -249,7 +242,6 @@ namespace MenuOption {
const QString GoTo = "Go To...";
const QString IncreaseAvatarSize = "Increase Avatar Size";
const QString IncreaseVoxelSize = "Increase Voxel Size";
const QString KillLocalVoxels = "Kill Local Voxels";
const QString GoHome = "Go Home";
const QString Gravity = "Use Gravity";
const QString LodTools = "LOD Tools";
@ -274,7 +266,6 @@ namespace MenuOption {
const QString PipelineWarnings = "Show Render Pipeline Warnings";
const QString PlaySlaps = "Play Slaps";
const QString Preferences = "Preferences...";
const QString RandomizeVoxelColors = "Randomize Voxel TRUE Colors";
const QString ReloadAllScripts = "Reload All Scripts";
const QString RenderSkeletonCollisionProxies = "Skeleton Collision Proxies";
const QString RenderHeadCollisionProxies = "Head Collision Proxies";
@ -283,19 +274,15 @@ namespace MenuOption {
const QString SettingsImport = "Import Settings";
const QString Shadows = "Shadows";
const QString SettingsExport = "Export Settings";
const QString ShowAllLocalVoxels = "Show All Local Voxels";
const QString ShowCulledSharedFaces = "Show Culled Shared Voxel Faces";
const QString ShowTrueColors = "Show TRUE Colors";
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
const QString Stars = "Stars";
const QString Stats = "Stats";
const QString StopAllScripts = "Stop All Scripts";
const QString TestPing = "Test Ping";
const QString TreeStats = "Calculate Tree Stats";
const QString TransmitterDrive = "Transmitter Drive";
const QString Quit = "Quit";
const QString Voxels = "Voxels";
const QString VoxelDrumming = "Voxel Drumming";
const QString VoxelMode = "Cycle Voxel Mode";
const QString VoxelStats = "Voxel Stats";
const QString VoxelTextures = "Voxel Textures";

View file

@ -38,6 +38,12 @@ void MenuScriptingInterface::addSeparator(const QString& menuName, const QString
Q_ARG(const QString&, separatorName));
}
void MenuScriptingInterface::removeSeparator(const QString& menuName, const QString& separatorName) {
QMetaObject::invokeMethod(Menu::getInstance(), "removeSeparator",
Q_ARG(const QString&, menuName),
Q_ARG(const QString&, separatorName));
}
void MenuScriptingInterface::addMenuItem(const MenuItemProperties& properties) {
QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties));
}

View file

@ -32,6 +32,7 @@ public slots:
void removeMenu(const QString& menuName);
void addSeparator(const QString& menuName, const QString& separatorName);
void removeSeparator(const QString& menuName, const QString& separatorName);
void addMenuItem(const MenuItemProperties& properties);
void addMenuItem(const QString& menuName, const QString& menuitem, const QString& shortcutKey);

View file

@ -33,10 +33,7 @@ void ParticleTreeRenderer::init() {
void ParticleTreeRenderer::update() {
if (_tree) {
ParticleTree* tree = static_cast<ParticleTree*>(_tree);
if (tree->tryLockForWrite()) {
tree->update();
tree->unlock();
}
tree->update();
}
}

View file

@ -146,11 +146,11 @@ void ImportTask::run() {
// Then we call the righ method for the job
if (_filename.endsWith(".png", Qt::CaseInsensitive)) {
voxelSystem->readFromSquareARGB32Pixels(_filename.toLocal8Bit().data());
voxelSystem->getTree()->readFromSquareARGB32Pixels(_filename.toLocal8Bit().data());
} else if (_filename.endsWith(".svo", Qt::CaseInsensitive)) {
voxelSystem->readFromSVOFile(_filename.toLocal8Bit().data());
voxelSystem->getTree()->readFromSVOFile(_filename.toLocal8Bit().data());
} else if (_filename.endsWith(".schematic", Qt::CaseInsensitive)) {
voxelSystem->readFromSchematicFile(_filename.toLocal8Bit().data());
voxelSystem->getTree()->readFromSchematicFile(_filename.toLocal8Bit().data());
} else {
// We should never get here.
qDebug() << "[ERROR] Invalid file extension." << endl;

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,6 @@
#include <SharedUtil.h>
#include <CoverageMapV2.h>
#include <NodeData.h>
#include <ViewFrustum.h>
#include <VoxelTree.h>
@ -68,11 +67,6 @@ public:
ViewFrustum* getLastCulledViewFrustum() { return &_lastCulledViewFrustum; }
void writeToSVOFile(const char* filename, VoxelTreeElement* element) const;
bool readFromSVOFile(const char* filename);
bool readFromSquareARGB32Pixels(const char* filename);
bool readFromSchematicFile(const char* filename);
void setMaxVoxels(int maxVoxels);
long int getMaxVoxels() const { return _maxVoxels; }
unsigned long getVoxelMemoryUsageRAM() const { return _memoryUsageRAM; }
@ -82,66 +76,25 @@ public:
void killLocalVoxels();
virtual void removeOutOfView();
virtual void hideOutOfView(bool forceFullFrustum = false);
void inspectForOcclusions();
bool hasViewChanged();
bool isViewChanging();
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
VoxelDetail& detail, float& distance, BoxFace& face);
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration);
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration);
void deleteVoxelAt(float x, float y, float z, float s);
VoxelTreeElement* getVoxelAt(float x, float y, float z, float s) const;
void createVoxel(float x, float y, float z, float s,
unsigned char red, unsigned char green, unsigned char blue, bool destructive = false);
void createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color, bool destructive = false);
void copySubTreeIntoNewTree(VoxelTreeElement* startNode, VoxelSystem* destinationTree, bool rebaseToRoot);
void copySubTreeIntoNewTree(VoxelTreeElement* startNode, VoxelTree* destinationTree, bool rebaseToRoot);
void copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelTreeElement* destinationNode);
void recurseTreeWithOperation(RecurseOctreeOperation operation, void* extraData=NULL);
CoverageMapV2 myCoverageMapV2;
CoverageMap myCoverageMap;
virtual void elementDeleted(OctreeElement* element);
virtual void elementUpdated(OctreeElement* element);
VoxelTreeElement* getVoxelEnclosing(const glm::vec3& point);
signals:
void importSize(float x, float y, float z);
void importProgress(int progress);
public slots:
void nodeAdded(SharedNodePointer node);
void nodeKilled(SharedNodePointer node);
void collectStatsForTreesAndVBOs();
// Methods that recurse tree
void showAllLocalVoxels();
void randomizeVoxelColors();
void falseColorizeRandom();
void trueColorize();
void falseColorizeInView();
void falseColorizeDistanceFromView();
void falseColorizeRandomEveryOther();
void falseColorizeOccluded();
void falseColorizeOccludedV2();
void falseColorizeBySource();
void forceRedrawEntireTree();
void clearAllNodesBufferIndex();
void cullSharedFaces();
void showCulledSharedFaces();
void cancelImport();
void setDisableFastVoxelPipeline(bool disableFastVoxelPipeline);
void setUseVoxelShader(bool useVoxelShader);
void setVoxelsAsPoints(bool voxelsAsPoints);
@ -154,18 +107,24 @@ protected:
void setupNewVoxelsForDrawing();
static const bool DONT_BAIL_EARLY; // by default we will bail early, if you want to force not bailing, then use this
void setupNewVoxelsForDrawingSingleNode(bool allowBailEarly = true);
/// called on the hide/show thread to hide any out of view voxels and show any newly in view voxels.
void checkForCulling();
/// single pass to remove old VBO data and fill it with correct current view, used when switching LOD or needing to force
/// a full redraw of everything in view
void recreateVoxelGeometryInView();
glm::vec3 computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const;
virtual void updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex,
float voxelScale, const nodeColor& color);
virtual void copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd);
virtual void updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd);
virtual void applyScaleAndBindProgram(bool texture);
virtual void removeScaleAndReleaseProgram(bool texture);
virtual void applyScaleAndBindProgram(bool texture); /// used in render() to apply shadows and textures
virtual void removeScaleAndReleaseProgram(bool texture); /// stop the shaders for shadows and textures
private:
// disallow copying of VoxelSystem objects
@ -178,19 +137,6 @@ private:
// Operation functions for tree recursion methods
static int _nodeCount;
static bool randomColorOperation(OctreeElement* element, void* extraData);
static bool falseColorizeRandomOperation(OctreeElement* element, void* extraData);
static bool trueColorizeOperation(OctreeElement* element, void* extraData);
static bool falseColorizeInViewOperation(OctreeElement* element, void* extraData);
static bool falseColorizeDistanceFromViewOperation(OctreeElement* element, void* extraData);
static bool getDistanceFromViewRangeOperation(OctreeElement* element, void* extraData);
static bool removeOutOfViewOperation(OctreeElement* element, void* extraData);
static bool falseColorizeRandomEveryOtherOperation(OctreeElement* element, void* extraData);
static bool collectStatsForTreesAndVBOsOperation(OctreeElement* element, void* extraData);
static bool falseColorizeOccludedOperation(OctreeElement* element, void* extraData);
static bool falseColorizeSubTreeOperation(OctreeElement* element, void* extraData);
static bool falseColorizeOccludedV2Operation(OctreeElement* element, void* extraData);
static bool falseColorizeBySourceOperation(OctreeElement* element, void* extraData);
static bool killSourceVoxelsOperation(OctreeElement* element, void* extraData);
static bool forceRedrawEntireTreeOperation(OctreeElement* element, void* extraData);
static bool clearAllNodesBufferIndexOperation(OctreeElement* element, void* extraData);
@ -199,7 +145,6 @@ private:
static bool hideOutOfViewOperation(OctreeElement* element, void* extraData);
static bool hideAllSubTreeOperation(OctreeElement* element, void* extraData);
static bool showAllSubTreeOperation(OctreeElement* element, void* extraData);
static bool showAllLocalVoxelsOperation(OctreeElement* element, void* extraData);
static bool getVoxelEnclosingOperation(OctreeElement* element, void* extraData);
static bool recreateVoxelGeometryInViewOperation(OctreeElement* element, void* extraData);

View file

@ -135,10 +135,10 @@ void Avatar::simulate(float deltaTime) {
getHand()->simulate(deltaTime, false);
_skeletonModel.setLODDistance(getLODDistance());
_skeletonModel.simulate(deltaTime, _shouldRenderBillboard);
glm::vec3 headPosition;
if (!_skeletonModel.getHeadPosition(headPosition)) {
headPosition = _position;
glm::vec3 headPosition = _position;
if (!_shouldRenderBillboard) {
_skeletonModel.simulate(deltaTime);
_skeletonModel.getHeadPosition(headPosition);
}
Head* head = getHead();
head->setPosition(headPosition);
@ -309,6 +309,9 @@ void Avatar::renderBody() {
renderBillboard();
return;
}
if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
return; // wait until both models are loaded
}
_skeletonModel.render(1.0f);
getHead()->render(1.0f);
getHand()->render(false);
@ -564,13 +567,13 @@ bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float parti
void Avatar::setFaceModelURL(const QUrl& faceModelURL) {
AvatarData::setFaceModelURL(faceModelURL);
const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_head.fst");
getHead()->getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL, !isMyAvatar());
getHead()->getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL, true, !isMyAvatar());
}
void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
AvatarData::setSkeletonModelURL(skeletonModelURL);
const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_body.fst");
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, !isMyAvatar());
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
}
void Avatar::setDisplayName(const QString& displayName) {

View file

@ -20,7 +20,6 @@
using namespace std;
const float FINGERTIP_COLLISION_RADIUS = 0.01f;
const float FINGERTIP_VOXEL_SIZE = 0.05f;
const float PALM_COLLISION_RADIUS = 0.03f;
@ -29,7 +28,6 @@ Hand::Hand(Avatar* owningAvatar) :
_owningAvatar(owningAvatar),
_renderAlpha(1.0),
_ballColor(0.0, 0.0, 0.4),
_collisionCenter(0,0,0),
_collisionAge(0),
_collisionDuration(0)
@ -37,13 +35,6 @@ Hand::Hand(Avatar* owningAvatar) :
}
void Hand::init() {
// Different colors for my hand and others' hands
if (_owningAvatar && _owningAvatar->isMyAvatar()) {
_ballColor = glm::vec3(0.0, 0.4, 0.0);
}
else {
_ballColor = glm::vec3(0.0, 0.0, 0.4);
}
}
void Hand::reset() {
@ -61,61 +52,6 @@ void Hand::simulate(float deltaTime, bool isMine) {
// Iterate hand controllers, take actions as needed
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
if (palm.isActive()) {
FingerData& finger = palm.getFingers()[0]; // Sixense has only one finger
glm::vec3 fingerTipPosition = finger.getTipPosition();
if (palm.getControllerButtons() & BUTTON_1) {
if (glm::length(fingerTipPosition - _lastFingerAddVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
// TODO: we need to move this code to JS so it can access the editVoxels.js color palette
QColor paintColor(128,128,128);
Application::getInstance()->makeVoxel(fingerTipPosition,
FINGERTIP_VOXEL_SIZE,
paintColor.red(),
paintColor.green(),
paintColor.blue(),
true);
_lastFingerAddVoxel = fingerTipPosition;
}
} else if (palm.getControllerButtons() & BUTTON_2) {
if (glm::length(fingerTipPosition - _lastFingerDeleteVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
Application::getInstance()->removeVoxel(fingerTipPosition, FINGERTIP_VOXEL_SIZE);
_lastFingerDeleteVoxel = fingerTipPosition;
}
}
// Voxel Drumming with fingertips if enabled
if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelDrumming)) {
VoxelTreeElement* fingerNode = Application::getInstance()->getVoxels()->getVoxelEnclosing(
glm::vec3(fingerTipPosition / (float)TREE_SCALE));
if (fingerNode) {
if (!palm.getIsCollidingWithVoxel()) {
// Collision has just started
palm.setIsCollidingWithVoxel(true);
handleVoxelCollision(&palm, fingerTipPosition, fingerNode, deltaTime);
// Set highlight voxel
VoxelDetail voxel;
glm::vec3 pos = fingerNode->getCorner();
voxel.x = pos.x;
voxel.y = pos.y;
voxel.z = pos.z;
voxel.s = fingerNode->getScale();
voxel.red = fingerNode->getColor()[0];
voxel.green = fingerNode->getColor()[1];
voxel.blue = fingerNode->getColor()[2];
Application::getInstance()->setHighlightVoxel(voxel);
Application::getInstance()->setIsHighlightVoxel(true);
}
} else {
if (palm.getIsCollidingWithVoxel()) {
// Collision has just ended
palm.setIsCollidingWithVoxel(false);
Application::getInstance()->setIsHighlightVoxel(false);
}
}
}
}
palm.setLastControllerButtons(palm.getControllerButtons());
}
}
@ -349,7 +285,6 @@ void Hand::renderLeapHands(bool isMine) {
const float alpha = 1.0f;
//const glm::vec3 handColor = _ballColor;
const glm::vec3 handColor(1.0, 0.84, 0.66); // use the skin color
glEnable(GL_DEPTH_TEST);

View file

@ -51,7 +51,6 @@ public:
void reset();
void simulate(float deltaTime, bool isMine);
void render(bool isMine);
void setBallColor (glm::vec3 ballColor ) { _ballColor = ballColor; }
// getters
const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [ball].position;}
@ -69,7 +68,6 @@ private:
Avatar* _owningAvatar;
float _renderAlpha;
glm::vec3 _ballColor;
std::vector<HandBall> _leapFingerTipBalls;
std::vector<HandBall> _leapFingerRootBalls;

View file

@ -58,7 +58,7 @@ void Head::reset() {
void Head::simulate(float deltaTime, bool isMine, bool delayLoad) {
void Head::simulate(float deltaTime, bool isMine, bool billboard) {
// Update audio trailing average for rendering facial animations
Faceshift* faceshift = Application::getInstance()->getFaceshift();
@ -75,7 +75,7 @@ void Head::simulate(float deltaTime, bool isMine, bool delayLoad) {
}
}
if (!_isFaceshiftConnected) {
if (!(_isFaceshiftConnected || billboard)) {
// Update eye saccades
const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f;
const float AVERAGE_SACCADE_INTERVAL = 4.0f;
@ -161,11 +161,10 @@ void Head::simulate(float deltaTime, bool isMine, bool delayLoad) {
if (!isMine) {
_faceModel.setLODDistance(static_cast<Avatar*>(_owningAvatar)->getLODDistance());
}
_faceModel.simulate(deltaTime, delayLoad);
// the blend face may have custom eye meshes
if (!_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition)) {
_leftEyePosition = _rightEyePosition = getPosition();
_leftEyePosition = _rightEyePosition = getPosition();
if (!billboard) {
_faceModel.simulate(deltaTime);
_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition);
}
_eyePosition = calculateAverageEyePosition();
}

View file

@ -36,7 +36,7 @@ public:
void init();
void reset();
void simulate(float deltaTime, bool isMine, bool delayLoad = false);
void simulate(float deltaTime, bool isMine, bool billboard = false);
void render(float alpha);
void setScale(float scale);
void setPosition(glm::vec3 position) { _position = position; }
@ -74,9 +74,9 @@ public:
void tweakYaw(float yaw) { _tweakedYaw = yaw; }
void tweakRoll(float roll) { _tweakedRoll = roll; }
float getTweakedPitch() const;
float getTweakedYaw() const;
float getTweakedRoll() const;
virtual float getTweakedPitch() const;
virtual float getTweakedYaw() const;
virtual float getTweakedRoll() const;
void applyCollision(CollisionInfo& collisionInfo);

View file

@ -677,6 +677,10 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
}
void MyAvatar::renderBody(bool forceRenderHead) {
if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
return; // wait until both models are loaded
}
// Render the body's voxels and head
_skeletonModel.render(1.0f);
@ -806,7 +810,7 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
glm::vec3 penetration;
float pelvisFloatingHeight = getPelvisFloatingHeight();
if (Application::getInstance()->getVoxels()->findCapsulePenetration(
if (Application::getInstance()->getVoxelTree()->findCapsulePenetration(
_position - glm::vec3(0.0f, pelvisFloatingHeight - radius, 0.0f),
_position + glm::vec3(0.0f, getSkeletonHeight() - pelvisFloatingHeight + radius, 0.0f), radius, penetration)) {
_lastCollisionPosition = _position;

View file

@ -28,6 +28,8 @@
using namespace std;
static int fbxGeometryMetaTypeId = qRegisterMetaType<FBXGeometry>();
template<class T> QVariant readBinaryArray(QDataStream& in) {
quint32 arrayLength;
quint32 encoding;

View file

@ -9,6 +9,7 @@
#ifndef __interface__FBXReader__
#define __interface__FBXReader__
#include <QMetaType>
#include <QUrl>
#include <QVarLengthArray>
#include <QVariant>
@ -167,6 +168,8 @@ public:
QVector<FBXAttachment> attachments;
};
Q_DECLARE_METATYPE(FBXGeometry)
/// Reads an FST mapping from the supplied data.
QVariantHash readMapping(const QByteArray& data);

View file

@ -8,6 +8,8 @@
#include <cmath>
#include <QNetworkReply>
#include <QRunnable>
#include <QThreadPool>
#include "Application.h"
#include "GeometryCache.h"
@ -307,6 +309,21 @@ NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGe
_fallback(fallback) {
}
bool NetworkGeometry::isLoadedWithTextures() const {
if (!isLoaded()) {
return false;
}
foreach (const NetworkMesh& mesh, _meshes) {
foreach (const NetworkMeshPart& part, mesh.parts) {
if ((part.diffuseTexture && !part.diffuseTexture->isLoaded()) ||
(part.normalTexture && !part.normalTexture->isLoaded())) {
return false;
}
}
}
return true;
}
QSharedPointer<NetworkGeometry> NetworkGeometry::getLODOrFallback(float distance, float& hysteresis, bool delayLoad) const {
if (_lodParent.data() != this) {
return _lodParent.data()->getLODOrFallback(distance, hysteresis, delayLoad);
@ -428,6 +445,46 @@ void NetworkGeometry::clearLoadPriority(const QPointer<QObject>& owner) {
}
}
/// Reads geometry in a worker thread.
class GeometryReader : public QRunnable {
public:
GeometryReader(const QWeakPointer<Resource>& geometry, const QUrl& url,
const QByteArray& data, const QVariantHash& mapping);
virtual void run();
private:
QWeakPointer<Resource> _geometry;
QUrl _url;
QByteArray _data;
QVariantHash _mapping;
};
GeometryReader::GeometryReader(const QWeakPointer<Resource>& geometry, const QUrl& url,
const QByteArray& data, const QVariantHash& mapping) :
_geometry(geometry),
_url(url),
_data(data),
_mapping(mapping) {
}
void GeometryReader::run() {
QSharedPointer<Resource> geometry = _geometry.toStrongRef();
if (geometry.isNull()) {
return;
}
try {
QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&,
_url.path().toLower().endsWith(".svo") ? readSVO(_data) : readFBX(_data, _mapping)));
} catch (const QString& error) {
qDebug() << "Error reading " << _url << ": " << error;
QMetaObject::invokeMethod(geometry.data(), "finishedLoading", Q_ARG(bool, false));
}
}
void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
QUrl url = reply->url();
QByteArray data = reply->readAll();
@ -438,7 +495,7 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
QString filename = _mapping.value("filename").toString();
if (filename.isNull()) {
qDebug() << "Mapping file " << url << " has no filename.";
_failedToLoad = true;
finishedLoading(false);
} else {
QString texdir = _mapping.value("texdir").toString();
@ -452,6 +509,7 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
for (QVariantHash::const_iterator it = lods.begin(); it != lods.end(); it++) {
QSharedPointer<NetworkGeometry> geometry(new NetworkGeometry(url.resolved(it.key()),
QSharedPointer<NetworkGeometry>(), true, _mapping, _textureBase));
geometry->setSelf(geometry.staticCast<Resource>());
geometry->setLODParent(_lodParent);
_lods.insert(it.value().toFloat(), geometry);
}
@ -466,14 +524,12 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
return;
}
try {
_geometry = url.path().toLower().endsWith(".svo") ? readSVO(data) : readFBX(data, _mapping);
} catch (const QString& error) {
qDebug() << "Error reading " << url << ": " << error;
_failedToLoad = true;
return;
}
// send the reader off to the thread pool
QThreadPool::globalInstance()->start(new GeometryReader(_self, url, data, _mapping));
}
void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
_geometry = geometry;
foreach (const FBXMesh& mesh, _geometry.meshes) {
NetworkMesh networkMesh = { QOpenGLBuffer(QOpenGLBuffer::IndexBuffer), QOpenGLBuffer(QOpenGLBuffer::VertexBuffer) };
@ -567,6 +623,8 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
_meshes.append(networkMesh);
}
finishedLoading(true);
}
bool NetworkMeshPart::isTranslucent() const {

View file

@ -69,8 +69,8 @@ public:
NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback, bool delayLoad,
const QVariantHash& mapping = QVariantHash(), const QUrl& textureBase = QUrl());
/// Checks whether the geometry is fulled loaded.
bool isLoaded() const { return !_geometry.joints.isEmpty(); }
/// Checks whether the geometry and its textures are loaded.
bool isLoadedWithTextures() const;
/// Returns a pointer to the geometry appropriate for the specified distance.
/// \param hysteresis a hysteresis parameter that prevents rapid model switching
@ -90,6 +90,8 @@ protected:
virtual void downloadFinished(QNetworkReply* reply);
Q_INVOKABLE void setGeometry(const FBXGeometry& geometry);
private:
friend class GeometryCache;

View file

@ -105,13 +105,13 @@ void GlowEffect::end() {
}
static void maybeBind(QOpenGLFramebufferObject* fbo) {
if (fbo != NULL) {
if (fbo) {
fbo->bind();
}
}
static void maybeRelease(QOpenGLFramebufferObject* fbo) {
if (fbo != NULL) {
if (fbo) {
fbo->release();
}
}

View file

@ -57,21 +57,6 @@ QVector<Model::JointState> Model::createJointStates(const FBXGeometry& geometry)
return jointStates;
}
bool Model::isLoadedWithTextures() const {
if (!isActive()) {
return false;
}
foreach (const NetworkMesh& mesh, _geometry->getMeshes()) {
foreach (const NetworkMeshPart& part, mesh.parts) {
if ((part.diffuseTexture && !part.diffuseTexture->isLoaded()) ||
(part.normalTexture && !part.normalTexture->isLoaded())) {
return false;
}
}
}
return true;
}
void Model::init() {
if (!_program.isLinked()) {
switchToResourcesParentIfRequired();
@ -117,32 +102,7 @@ void Model::reset() {
void Model::simulate(float deltaTime, bool delayLoad) {
// update our LOD
QVector<JointState> newJointStates;
if (_geometry) {
QSharedPointer<NetworkGeometry> geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad);
if (_geometry != geometry) {
if (!_jointStates.isEmpty()) {
// copy the existing joint states
const FBXGeometry& oldGeometry = _geometry->getFBXGeometry();
const FBXGeometry& newGeometry = geometry->getFBXGeometry();
newJointStates = createJointStates(newGeometry);
for (QHash<QString, int>::const_iterator it = oldGeometry.jointIndices.constBegin();
it != oldGeometry.jointIndices.constEnd(); it++) {
int newIndex = newGeometry.jointIndices.value(it.key());
if (newIndex != 0) {
newJointStates[newIndex - 1] = _jointStates.at(it.value() - 1);
}
}
}
deleteGeometry();
_dilatedTextures.clear();
_geometry = geometry;
}
if (!delayLoad) {
_geometry->setLoadPriority(this, -_lodDistance);
_geometry->ensureLoading();
}
}
QVector<JointState> newJointStates = updateGeometry(delayLoad);
if (!isActive()) {
return;
}
@ -447,20 +407,18 @@ float Model::getRightArmLength() const {
return getLimbLength(getRightHandJointIndex());
}
void Model::setURL(const QUrl& url, const QUrl& fallback, bool delayLoad) {
void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bool delayLoad) {
// don't recreate the geometry if it's the same URL
if (_url == url) {
return;
}
_url = url;
// delete our local geometry and custom textures
deleteGeometry();
_dilatedTextures.clear();
_lodHysteresis = NetworkGeometry::NO_HYSTERESIS;
// we retain a reference to the base geometry so that its reference count doesn't fall to zero
_baseGeometry = _geometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback, delayLoad);
// if so instructed, keep the current geometry until the new one is loaded
_nextBaseGeometry = _nextGeometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback, delayLoad);
if (!retainCurrent || !isActive() || _nextGeometry->isLoaded()) {
applyNextGeometry();
}
}
glm::vec4 Model::computeAverageColor() const {
@ -823,6 +781,61 @@ void Model::applyCollision(CollisionInfo& collision) {
}
}
QVector<Model::JointState> Model::updateGeometry(bool delayLoad) {
QVector<JointState> newJointStates;
if (_nextGeometry) {
_nextGeometry = _nextGeometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad);
if (!delayLoad) {
_nextGeometry->setLoadPriority(this, -_lodDistance);
_nextGeometry->ensureLoading();
}
if (_nextGeometry->isLoaded()) {
applyNextGeometry();
return newJointStates;
}
}
if (!_geometry) {
return newJointStates;
}
QSharedPointer<NetworkGeometry> geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad);
if (_geometry != geometry) {
if (!_jointStates.isEmpty()) {
// copy the existing joint states
const FBXGeometry& oldGeometry = _geometry->getFBXGeometry();
const FBXGeometry& newGeometry = geometry->getFBXGeometry();
newJointStates = createJointStates(newGeometry);
for (QHash<QString, int>::const_iterator it = oldGeometry.jointIndices.constBegin();
it != oldGeometry.jointIndices.constEnd(); it++) {
int newIndex = newGeometry.jointIndices.value(it.key());
if (newIndex != 0) {
newJointStates[newIndex - 1] = _jointStates.at(it.value() - 1);
}
}
}
deleteGeometry();
_dilatedTextures.clear();
_geometry = geometry;
}
if (!delayLoad) {
_geometry->setLoadPriority(this, -_lodDistance);
_geometry->ensureLoading();
}
return newJointStates;
}
void Model::applyNextGeometry() {
// delete our local geometry and custom textures
deleteGeometry();
_dilatedTextures.clear();
_lodHysteresis = NetworkGeometry::NO_HYSTERESIS;
// we retain a reference to the base geometry so that its reference count doesn't fall to zero
_baseGeometry = _nextBaseGeometry;
_geometry = _nextGeometry;
_nextBaseGeometry.reset();
_nextGeometry.reset();
}
void Model::deleteGeometry() {
foreach (Model* attachment, _attachments) {
delete attachment;
@ -983,18 +996,18 @@ void Model::renderMeshes(float alpha, bool translucent) {
Texture* diffuseMap = networkPart.diffuseTexture.data();
if (mesh.isEye) {
if (diffuseMap != NULL) {
if (diffuseMap) {
diffuseMap = (_dilatedTextures[i][j] =
static_cast<DilatableNetworkTexture*>(diffuseMap)->getDilatedTexture(_pupilDilation)).data();
}
}
glBindTexture(GL_TEXTURE_2D, diffuseMap == NULL ?
glBindTexture(GL_TEXTURE_2D, !diffuseMap ?
Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID());
if (!mesh.tangents.isEmpty()) {
glActiveTexture(GL_TEXTURE1);
Texture* normalMap = networkPart.normalTexture.data();
glBindTexture(GL_TEXTURE_2D, normalMap == NULL ?
glBindTexture(GL_TEXTURE_2D, !normalMap ?
Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID());
glActiveTexture(GL_TEXTURE0);
}

View file

@ -46,14 +46,22 @@ public:
bool isActive() const { return _geometry && _geometry->isLoaded(); }
bool isLoadedWithTextures() const;
bool isRenderable() const { return !_meshStates.isEmpty(); }
bool isLoadedWithTextures() const { return _geometry && _geometry->isLoadedWithTextures(); }
void init();
void reset();
void simulate(float deltaTime, bool delayLoad = false);
bool render(float alpha);
Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false);
/// Sets the URL of the model to render.
/// \param fallback the URL of a fallback model to render if the requested model fails to load
/// \param retainCurrent if true, keep rendering the current model until the new one is loaded
/// \param delayLoad if true, don't load the model immediately; wait until actually requested
Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl(),
bool retainCurrent = false, bool delayLoad = false);
const QUrl& getURL() const { return _url; }
/// Sets the distance parameter used for LOD computations.
@ -229,10 +237,14 @@ protected:
private:
QVector<JointState> updateGeometry(bool delayLoad);
void applyNextGeometry();
void deleteGeometry();
void renderMeshes(float alpha, bool translucent);
QSharedPointer<NetworkGeometry> _baseGeometry; ///< reference required to prevent collection of base
QSharedPointer<NetworkGeometry> _nextBaseGeometry;
QSharedPointer<NetworkGeometry> _nextGeometry;
float _lodDistance;
float _lodHysteresis;

View file

@ -11,6 +11,8 @@
#include <QGLWidget>
#include <QNetworkReply>
#include <QOpenGLFramebufferObject>
#include <QRunnable>
#include <QThreadPool>
#include <glm/gtc/random.hpp>
@ -38,14 +40,14 @@ TextureCache::~TextureCache() {
foreach (GLuint id, _fileTextureIDs) {
glDeleteTextures(1, &id);
}
if (_primaryFramebufferObject != NULL) {
if (_primaryFramebufferObject) {
delete _primaryFramebufferObject;
glDeleteTextures(1, &_primaryDepthTextureID);
}
if (_secondaryFramebufferObject != NULL) {
if (_secondaryFramebufferObject) {
delete _secondaryFramebufferObject;
}
if (_tertiaryFramebufferObject != NULL) {
if (_tertiaryFramebufferObject) {
delete _tertiaryFramebufferObject;
}
}
@ -129,13 +131,14 @@ QSharedPointer<NetworkTexture> TextureCache::getTexture(const QUrl& url, bool no
QSharedPointer<NetworkTexture> texture = _dilatableNetworkTextures.value(url);
if (texture.isNull()) {
texture = QSharedPointer<NetworkTexture>(new DilatableNetworkTexture(url));
texture->setSelf(texture);
_dilatableNetworkTextures.insert(url, texture);
}
return texture;
}
QOpenGLFramebufferObject* TextureCache::getPrimaryFramebufferObject() {
if (_primaryFramebufferObject == NULL) {
if (!_primaryFramebufferObject) {
_primaryFramebufferObject = createFramebufferObject();
glGenTextures(1, &_primaryDepthTextureID);
@ -161,21 +164,21 @@ GLuint TextureCache::getPrimaryDepthTextureID() {
}
QOpenGLFramebufferObject* TextureCache::getSecondaryFramebufferObject() {
if (_secondaryFramebufferObject == NULL) {
if (!_secondaryFramebufferObject) {
_secondaryFramebufferObject = createFramebufferObject();
}
return _secondaryFramebufferObject;
}
QOpenGLFramebufferObject* TextureCache::getTertiaryFramebufferObject() {
if (_tertiaryFramebufferObject == NULL) {
if (!_tertiaryFramebufferObject) {
_tertiaryFramebufferObject = createFramebufferObject();
}
return _tertiaryFramebufferObject;
}
QOpenGLFramebufferObject* TextureCache::getShadowFramebufferObject() {
if (_shadowFramebufferObject == NULL) {
if (!_shadowFramebufferObject) {
const int SHADOW_MAP_SIZE = 2048;
_shadowFramebufferObject = new QOpenGLFramebufferObject(SHADOW_MAP_SIZE, SHADOW_MAP_SIZE,
QOpenGLFramebufferObject::NoAttachment, GL_TEXTURE_2D, GL_RGB);
@ -209,16 +212,16 @@ GLuint TextureCache::getShadowDepthTextureID() {
bool TextureCache::eventFilter(QObject* watched, QEvent* event) {
if (event->type() == QEvent::Resize) {
QSize size = static_cast<QResizeEvent*>(event)->size();
if (_primaryFramebufferObject != NULL && _primaryFramebufferObject->size() != size) {
if (_primaryFramebufferObject && _primaryFramebufferObject->size() != size) {
delete _primaryFramebufferObject;
_primaryFramebufferObject = NULL;
glDeleteTextures(1, &_primaryDepthTextureID);
}
if (_secondaryFramebufferObject != NULL && _secondaryFramebufferObject->size() != size) {
if (_secondaryFramebufferObject && _secondaryFramebufferObject->size() != size) {
delete _secondaryFramebufferObject;
_secondaryFramebufferObject = NULL;
}
if (_tertiaryFramebufferObject != NULL && _tertiaryFramebufferObject->size() != size) {
if (_tertiaryFramebufferObject && _tertiaryFramebufferObject->size() != size) {
delete _tertiaryFramebufferObject;
_tertiaryFramebufferObject = NULL;
}
@ -254,8 +257,7 @@ Texture::~Texture() {
NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) :
Resource(url),
_averageColor(1.0f, 1.0f, 1.0f, 1.0f),
_translucent(false),
_loaded(false) {
_translucent(false) {
if (!url.isValid()) {
_loaded = true;
@ -268,10 +270,30 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) :
glBindTexture(GL_TEXTURE_2D, 0);
}
void NetworkTexture::downloadFinished(QNetworkReply* reply) {
_loaded = true;
class ImageReader : public QRunnable {
public:
ImageReader(const QWeakPointer<Resource>& texture, const QByteArray& data);
QImage image = QImage::fromData(reply->readAll());
virtual void run();
private:
QWeakPointer<Resource> _texture;
QByteArray _data;
};
ImageReader::ImageReader(const QWeakPointer<Resource>& texture, const QByteArray& data) :
_texture(texture),
_data(data) {
}
void ImageReader::run() {
QSharedPointer<Resource> texture = _texture.toStrongRef();
if (texture.isNull()) {
return;
}
QImage image = QImage::fromData(_data);
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
@ -295,9 +317,21 @@ void NetworkTexture::downloadFinished(QNetworkReply* reply) {
}
}
int imageArea = image.width() * image.height();
_averageColor = accumulated / (imageArea * EIGHT_BIT_MAXIMUM);
_translucent = (translucentPixels >= imageArea / 2);
QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image),
Q_ARG(const glm::vec4&, accumulated / (imageArea * EIGHT_BIT_MAXIMUM)),
Q_ARG(bool, translucentPixels >= imageArea / 2));
}
void NetworkTexture::downloadFinished(QNetworkReply* reply) {
// send the reader off to the thread pool
QThreadPool::globalInstance()->start(new ImageReader(_self, reply->readAll()));
}
void NetworkTexture::setImage(const QImage& image, const glm::vec4& averageColor, bool translucent) {
_averageColor = averageColor;
_translucent = translucent;
finishedLoading(true);
imageLoaded(image);
glBindTexture(GL_TEXTURE_2D, getID());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1,

View file

@ -117,8 +117,6 @@ public:
NetworkTexture(const QUrl& url, bool normalMap);
bool isLoaded() const { return _loaded; }
/// Returns the average color over the entire texture.
const glm::vec4& getAverageColor() const { return _averageColor; }
@ -131,11 +129,12 @@ protected:
virtual void downloadFinished(QNetworkReply* reply);
virtual void imageLoaded(const QImage& image);
Q_INVOKABLE void setImage(const QImage& image, const glm::vec4& averageColor, bool translucent);
private:
glm::vec4 _averageColor;
bool _translucent;
bool _loaded;
};
/// Caches derived, dilated textures.

View file

@ -18,8 +18,6 @@
#include "AudioInjector.h"
int abstractAudioPointerMeta = qRegisterMetaType<AbstractAudioInterface*>("AbstractAudioInterface*");
AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions) :
_sound(sound),
_options(injectorOptions)
@ -53,7 +51,7 @@ void AudioInjector::injectAudio() {
packetStream << QUuid::createUuid();
// pack the flag for loopback
uchar loopbackFlag = (uchar) (_options.getLoopbackAudioInterface() == NULL);
uchar loopbackFlag = (uchar) (!_options.getLoopbackAudioInterface());
packetStream << loopbackFlag;
// pack the position for injected audio

View file

@ -18,9 +18,6 @@
#include "AudioInjectorOptions.h"
#include "Sound.h"
class AbstractAudioInterface;
class AudioScriptingInterface;
class AudioInjector : public QObject {
Q_OBJECT
public:

View file

@ -25,4 +25,26 @@ void AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions
connect(injectorThread, SIGNAL(finished()), injectorThread, SLOT(deleteLater()));
injectorThread->start();
}
}
void AudioScriptingInterface::startDrumSound(float volume, float frequency, float duration, float decay,
const AudioInjectorOptions* injectorOptions) {
Sound* sound = new Sound(volume, frequency, duration, decay);
AudioInjector* injector = new AudioInjector(sound, *injectorOptions);
sound->setParent(injector);
QThread* injectorThread = new QThread();
injector->moveToThread(injectorThread);
// start injecting when the injector thread starts
connect(injectorThread, SIGNAL(started()), injector, SLOT(injectAudio()));
// connect the right slots and signals so that the AudioInjector is killed once the injection is complete
connect(injector, SIGNAL(finished()), injector, SLOT(deleteLater()));
connect(injector, SIGNAL(finished()), injectorThread, SLOT(quit()));
connect(injectorThread, SIGNAL(finished()), injectorThread, SLOT(deleteLater()));
injectorThread->start();
}

View file

@ -18,6 +18,8 @@ class AudioScriptingInterface : public QObject {
Q_OBJECT
public slots:
static void playSound(Sound* sound, const AudioInjectorOptions* injectorOptions = NULL);
static void startDrumSound(float volume, float frequency, float duration, float decay,
const AudioInjectorOptions* injectorOptions = NULL);
};
#endif /* defined(__hifi__AudioScriptingInterface__) */

View file

@ -9,6 +9,8 @@
#include <stdint.h>
#include <glm/glm.hpp>
#include <QDataStream>
#include <QtCore/QDebug>
#include <QtNetwork/QNetworkAccessManager>
@ -16,8 +18,51 @@
#include <QtNetwork/QNetworkReply>
#include <qendian.h>
#include <SharedUtil.h>
#include "AudioRingBuffer.h"
#include "Sound.h"
// procedural audio version of Sound
Sound::Sound(float volume, float frequency, float duration, float decay, QObject* parent) :
QObject(parent)
{
static char monoAudioData[MAX_PACKET_SIZE];
static int16_t* monoAudioSamples = (int16_t*)(monoAudioData);
float t;
const float AUDIO_CALLBACK_MSECS = (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float)SAMPLE_RATE * 1000.0;
const float MAX_VOLUME = 32000.f;
const float MAX_DURATION = 2.f;
const float MIN_AUDIBLE_VOLUME = 0.001f;
const float NOISE_MAGNITUDE = 0.02f;
const int MAX_SAMPLE_VALUE = std::numeric_limits<int16_t>::max();
const int MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::min();
int numSamples = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; // we add sounds in chunks of this many samples
int chunkStartingSample = 0;
float waveFrequency = (frequency / SAMPLE_RATE) * PI_TIMES_TWO;
while (volume > 0.f) {
for (int i = 0; i < numSamples; i++) {
t = (float)chunkStartingSample + (float)i;
float sample = sinf(t * waveFrequency);
sample += ((randFloat() - 0.5f) * NOISE_MAGNITUDE);
sample *= volume * MAX_VOLUME;
monoAudioSamples[i] = glm::clamp((int)sample, MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE);
volume *= (1.f - decay);
}
// add the monoAudioSamples to our actual output Byte Array
_byteArray.append(monoAudioData, numSamples * sizeof(int16_t));
chunkStartingSample += numSamples;
duration = glm::clamp(duration - (AUDIO_CALLBACK_MSECS / 1000.f), 0.f, MAX_DURATION);
//qDebug() << "decaying... _duration=" << _duration;
if (duration == 0.f || (volume < MIN_AUDIBLE_VOLUME)) {
volume = 0.f;
}
}
}
Sound::Sound(const QUrl& sampleURL, QObject* parent) :
QObject(parent)
{

View file

@ -17,7 +17,8 @@ class QNetworkReply;
class Sound : public QObject {
Q_OBJECT
public:
Sound(const QUrl& sampleURL, QObject* parent = 0);
Sound(const QUrl& sampleURL, QObject* parent = NULL);
Sound(float volume, float frequency, float duration, float decay, QObject* parent = NULL);
const QByteArray& getByteArray() { return _byteArray; }

View file

@ -91,9 +91,9 @@ QByteArray AvatarData::toByteArray() {
destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _targetScale);
// Head rotation (NOTE: This needs to become a quaternion to save two bytes)
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->_yaw);
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->_pitch);
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->_roll);
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getTweakedYaw());
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getTweakedPitch());
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getTweakedRoll());
// Head lean X,Z (head lateral and fwd/back motion relative to torso)

View file

@ -27,6 +27,7 @@ class AvatarData;
class HeadData {
public:
HeadData(AvatarData* owningAvatar);
virtual ~HeadData() { };
float getLeanSideways() const { return _leanSideways; }
void setLeanSideways(float leanSideways) { _leanSideways = leanSideways; }
@ -43,6 +44,10 @@ public:
float getRoll() const { return _roll; }
void setRoll(float roll) { _roll = glm::clamp(roll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); }
virtual float getTweakedYaw() const { return _yaw; }
virtual float getTweakedPitch() const { return _pitch; }
virtual float getTweakedRoll() const { return _roll; }
glm::quat getOrientation() const;
void setOrientation(const glm::quat& orientation);

View file

@ -69,7 +69,7 @@ int Bitstream::registerMetaObject(const char* className, const QMetaObject* meta
getMetaObjects().insert(className, metaObject);
// register it as a subclass of itself and all of its superclasses
for (const QMetaObject* superClass = metaObject; superClass != NULL; superClass = superClass->superClass()) {
for (const QMetaObject* superClass = metaObject; superClass; superClass = superClass->superClass()) {
getMetaObjectSubClasses().insert(superClass, metaObject);
}
return 0;

View file

@ -784,7 +784,7 @@ void ScriptedMetavoxelGuide::setURL(const ParameterizedURL& url) {
bool MetavoxelVisitation::allInputNodesLeaves() const {
foreach (MetavoxelNode* node, inputNodes) {
if (node != NULL && !node->isLeaf()) {
if (node && !node->isLeaf()) {
return false;
}
}
@ -792,7 +792,7 @@ bool MetavoxelVisitation::allInputNodesLeaves() const {
}
AttributeValue MetavoxelVisitation::getInheritedOutputValue(int index) const {
for (const MetavoxelVisitation* visitation = previous; visitation != NULL; visitation = visitation->previous) {
for (const MetavoxelVisitation* visitation = previous; visitation; visitation = visitation->previous) {
MetavoxelNode* node = visitation->outputNodes.at(index);
if (node) {
return node->getAttributeValue(visitor.getOutputs().at(index));

View file

@ -62,7 +62,7 @@ DelegatingItemEditorFactory::DelegatingItemEditorFactory() :
QWidget* DelegatingItemEditorFactory::createEditor(int userType, QWidget* parent) const {
QWidget* editor = QItemEditorFactory::createEditor(userType, parent);
return (editor == NULL) ? _parentFactory->createEditor(userType, parent) : editor;
return (!editor) ? _parentFactory->createEditor(userType, parent) : editor;
}
QByteArray DelegatingItemEditorFactory::valuePropertyName(int userType) const {

View file

@ -61,6 +61,7 @@ NetworkProgram::NetworkProgram(ScriptCache* cache, const QUrl& url) :
void NetworkProgram::downloadFinished(QNetworkReply* reply) {
_program = QScriptProgram(QTextStream(reply).readAll(), reply->url().toString());
finishedLoading(true);
emit loaded();
}

View file

@ -72,8 +72,6 @@ public:
ScriptCache* getCache() const { return _cache; }
bool isLoaded() const { return !_program.isNull(); }
const QScriptProgram& getProgram() const { return _program; }
signals:

View file

@ -163,7 +163,7 @@ void SharedObjectEditor::updateType() {
oldObject->disconnect(this);
}
const QMetaObject* metaObject = _type->itemData(_type->currentIndex()).value<const QMetaObject*>();
if (metaObject == NULL) {
if (!metaObject) {
_object.reset();
return;
}

View file

@ -62,7 +62,7 @@ bool JurisdictionSender::process() {
_nodesRequestingJurisdictions.pop();
SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(nodeUUID);
if (node && node->getActiveSocket() != NULL) {
if (node && node->getActiveSocket()) {
_packetSender.queuePacketForSending(node, QByteArray(reinterpret_cast<char *>(bufferOut), sizeOut));
nodeCount++;
}

View file

@ -41,7 +41,8 @@ float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeSc
Octree::Octree(bool shouldReaverage) :
_isDirty(true),
_shouldReaverage(shouldReaverage),
_stopImport(false) {
_stopImport(false),
_lock(QReadWriteLock::Recursive) {
_rootNode = NULL;
_isViewing = false;
}
@ -150,7 +151,7 @@ void Octree::recurseNodeWithOperationDistanceSorted(OctreeElement* node, Recurse
OctreeElement* Octree::nodeForOctalCode(OctreeElement* ancestorNode,
const unsigned char* needleCode, OctreeElement** parentOfFoundNode) const {
// special case for NULL octcode
if (needleCode == NULL) {
if (!needleCode) {
return _rootNode;
}
@ -330,7 +331,9 @@ void Octree::readBitstreamToTree(const unsigned char * bitstream, unsigned long
void Octree::deleteOctreeElementAt(float x, float y, float z, float s) {
unsigned char* octalCode = pointToOctalCode(x,y,z,s);
lockForWrite();
deleteOctalCodeFromTree(octalCode);
unlock();
delete[] octalCode; // cleanup memory
}
@ -497,7 +500,7 @@ void Octree::processRemoveOctreeElementsBitstream(const unsigned char* bitstream
// Note: this is an expensive call. Don't call it unless you really need to reaverage the entire tree (from startNode)
void Octree::reaverageOctreeElements(OctreeElement* startNode) {
if (startNode == NULL) {
if (!startNode) {
startNode = getRoot();
}
// if our tree is a reaveraging tree, then we do this, otherwise we don't do anything
@ -549,7 +552,10 @@ OctreeElement* Octree::getOctreeElementAt(float x, float y, float z, float s) co
OctreeElement* Octree::getOrCreateChildElementAt(float x, float y, float z, float s) {
return getRoot()->getOrCreateChildElementAt(x, y, z, s);
lockForWrite();
OctreeElement* result = getRoot()->getOrCreateChildElementAt(x, y, z, s);
unlock();
return result;
}
@ -586,9 +592,16 @@ bool findRayIntersectionOp(OctreeElement* node, void* extraData) {
}
bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElement*& node, float& distance, BoxFace& face) {
OctreeElement*& node, float& distance, BoxFace& face, bool tryLock) {
RayArgs args = { origin / (float)(TREE_SCALE), direction, node, distance, face };
recurseTreeWithOperation(findRayIntersectionOp, &args);
if (!tryLock) {
lockForRead();
}
if (tryLock && tryLockForRead()) {
recurseTreeWithOperation(findRayIntersectionOp, &args);
unlock();
}
return args.found;
}
@ -624,7 +637,7 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) {
}
bool Octree::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration,
void** penetratedObject) {
void** penetratedObject, bool tryLock) {
SphereArgs args = {
center / (float)(TREE_SCALE),
@ -633,9 +646,16 @@ bool Octree::findSpherePenetration(const glm::vec3& center, float radius, glm::v
false,
NULL };
penetration = glm::vec3(0.0f, 0.0f, 0.0f);
recurseTreeWithOperation(findSpherePenetrationOp, &args);
if (penetratedObject) {
*penetratedObject = args.penetratedObject;
if (!tryLock) {
lockForRead();
}
if (tryLock && tryLockForRead()) {
recurseTreeWithOperation(findSpherePenetrationOp, &args);
if (penetratedObject) {
*penetratedObject = args.penetratedObject;
}
unlock();
}
return args.found;
}
@ -670,17 +690,67 @@ bool findCapsulePenetrationOp(OctreeElement* node, void* extraData) {
return false;
}
bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) {
bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius,
glm::vec3& penetration, bool tryLock) {
CapsuleArgs args = {
start / (float)(TREE_SCALE),
end / (float)(TREE_SCALE),
radius / (float)(TREE_SCALE),
penetration };
penetration,
false };
penetration = glm::vec3(0.0f, 0.0f, 0.0f);
recurseTreeWithOperation(findCapsulePenetrationOp, &args);
if (!tryLock) {
lockForRead();
}
if (tryLock && tryLockForRead()) {
recurseTreeWithOperation(findCapsulePenetrationOp, &args);
unlock();
}
return args.found;
}
class GetElementEnclosingArgs {
public:
OctreeElement* element;
glm::vec3 point;
};
// Find the smallest colored voxel enclosing a point (if there is one)
bool getElementEnclosingOperation(OctreeElement* element, void* extraData) {
GetElementEnclosingArgs* args = static_cast<GetElementEnclosingArgs*>(extraData);
AABox elementBox = element->getAABox();
if (elementBox.contains(args->point)) {
if (element->hasContent() && element->isLeaf()) {
// we've reached a solid leaf containing the point, return the node.
args->element = element;
return false;
}
} else {
// The point is not inside this voxel, so stop recursing.
return false;
}
return true; // keep looking
}
OctreeElement* Octree::getElementEnclosingPoint(const glm::vec3& point, bool tryLock) {
GetElementEnclosingArgs args;
args.point = point;
args.element = NULL;
if (!tryLock) {
lockForRead();
}
if (tryLock && tryLockForRead()) {
recurseTreeWithOperation(getElementEnclosingOperation, (void*)&args);
unlock();
}
return args.element;
}
int Octree::encodeTreeBitstream(OctreeElement* node,
OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params) {

View file

@ -12,7 +12,6 @@
#include <set>
#include <SimpleMovingAverage.h>
//#include "CoverageMap.h"
class CoverageMap;
class ReadBitstreamToTreeParams;
class Octree;
@ -223,12 +222,15 @@ public:
void setDirtyBit() { _isDirty = true; }
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElement*& node, float& distance, BoxFace& face);
OctreeElement*& node, float& distance, BoxFace& face, bool tryLock = true);
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration,
void** penetratedObject = NULL);
void** penetratedObject = NULL, bool tryLock = true);
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration);
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius,
glm::vec3& penetration, bool tryLock = true);
OctreeElement* getElementEnclosingPoint(const glm::vec3& point, bool tryLock = true);
// Note: this assumes the fileFormat is the HIO individual voxels code files
void loadOctreeFile(const char* fileName, bool wantColorRandomizer);
@ -236,16 +238,13 @@ public:
// these will read/write files that match the wireformat, excluding the 'V' leading
void writeToSVOFile(const char* filename, OctreeElement* node = NULL);
bool readFromSVOFile(const char* filename);
// reads voxels from square image with alpha as a Y-axis
bool readFromSquareARGB32Pixels(const char *filename);
bool readFromSchematicFile(const char* filename);
// Octree does not currently handle its own locking, caller must use these to lock/unlock
void lockForRead() { lock.lockForRead(); }
bool tryLockForRead() { return lock.tryLockForRead(); }
void lockForWrite() { lock.lockForWrite(); }
bool tryLockForWrite() { return lock.tryLockForWrite(); }
void unlock() { lock.unlock(); }
void lockForRead() { _lock.lockForRead(); }
bool tryLockForRead() { return _lock.tryLockForRead(); }
void lockForWrite() { _lock.lockForWrite(); }
bool tryLockForWrite() { return _lock.tryLockForWrite(); }
void unlock() { _lock.unlock(); }
unsigned long getOctreeElementsCount();
@ -330,7 +329,7 @@ protected:
/// flushes out any Octal Codes that had to be queued
void emptyDeleteQueue();
QReadWriteLock lock;
QReadWriteLock _lock;
/// This tree is receiving inbound viewer datagrams.
bool _isViewing;

View file

@ -177,7 +177,7 @@ void OctreeEditPacketSender::queuePacketToNodes(unsigned char* buffer, ssize_t l
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
// only send to the NodeTypes that are getMyNodeType()
if (node->getActiveSocket() != NULL && node->getType() == getMyNodeType()) {
if (node->getActiveSocket() && node->getType() == getMyNodeType()) {
QUuid nodeUUID = node->getUUID();
bool isMyJurisdiction = true;
// we need to get the jurisdiction for this
@ -226,7 +226,7 @@ void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, unsigned ch
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
// only send to the NodeTypes that are getMyNodeType()
if (node->getActiveSocket() != NULL && node->getType() == getMyNodeType()) {
if (node->getActiveSocket() && node->getType() == getMyNodeType()) {
QUuid nodeUUID = node->getUUID();
bool isMyJurisdiction = true;

View file

@ -739,7 +739,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) {
_externalChildrenMemoryUsage += NUMBER_OF_CHILDREN * sizeof(OctreeElement*);
} else if (previousChildCount == 2 && newChildCount == 1) {
assert(child == NULL); // we are removing a child, so this must be true!
assert(!child); // we are removing a child, so this must be true!
OctreeElement* previousFirstChild = _children.external[firstIndex];
OctreeElement* previousSecondChild = _children.external[secondIndex];
delete[] _children.external;

View file

@ -75,7 +75,7 @@ void OctreeHeadlessViewer::queryOctree() {
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
// only send to the NodeTypes that are serverType
if (node->getActiveSocket() != NULL && node->getType() == serverType) {
if (node->getActiveSocket() && node->getType() == serverType) {
totalServers++;
// get the server bounds for this server
@ -135,7 +135,7 @@ void OctreeHeadlessViewer::queryOctree() {
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
// only send to the NodeTypes that are serverType
if (node->getActiveSocket() != NULL && node->getType() == serverType) {
if (node->getActiveSocket() && node->getType() == serverType) {
// get the server bounds for this server

View file

@ -68,9 +68,7 @@ bool OctreePersistThread::process() {
usleep(USECS_TO_SLEEP);
// do our updates then check to save...
_tree->lockForWrite();
_tree->update();
_tree->unlock();
quint64 now = usecTimestampNow();
quint64 sinceLastSave = now - _lastCheck;

View file

@ -14,16 +14,32 @@
#include <PerfStat.h>
#include "OctreeRenderer.h"
OctreeRenderer::OctreeRenderer() {
_tree = NULL;
_viewFrustum = NULL;
OctreeRenderer::OctreeRenderer() :
_tree(NULL),
_managedTree(false),
_viewFrustum(NULL)
{
}
void OctreeRenderer::init() {
_tree = createTree();
if (!_tree) {
_tree = createTree();
_managedTree = true;
}
}
OctreeRenderer::~OctreeRenderer() {
if (_tree && _managedTree) {
delete _tree;
}
}
void OctreeRenderer::setTree(Octree* newTree) {
if (_tree && _managedTree) {
delete _tree;
_managedTree = false;
}
_tree = newTree;
}
void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) {

View file

@ -44,6 +44,8 @@ public:
virtual PacketType getMyQueryMessageType() const = 0;
virtual PacketType getExpectedPacketType() const = 0;
virtual void renderElement(OctreeElement* element, RenderArgs* args) = 0;
virtual void setTree(Octree* newTree);
/// process incoming data
virtual void processDatagram(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
@ -63,6 +65,7 @@ public:
void clear();
protected:
Octree* _tree;
bool _managedTree;
ViewFrustum* _viewFrustum;
};

View file

@ -16,7 +16,6 @@
#include <QtCore/QDebug>
//#include "CoverageMap.h"
#include "GeometryUtil.h"
#include "SharedUtil.h"
#include "ViewFrustum.h"

View file

@ -385,7 +385,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
} else {
// look up the existing particle
const Particle* existingParticle = tree->findParticleByID(editID, true);
const Particle* existingParticle = tree->findParticleByID(editID);
// copy existing properties before over-writing with new properties
if (existingParticle) {

View file

@ -99,8 +99,8 @@ void ParticleTree::storeParticle(const Particle& particle, const SharedNodePoint
if (!args.found) {
glm::vec3 position = particle.getPosition();
float size = std::max(MINIMUM_PARTICLE_ELEMENT_SIZE, particle.getRadius());
ParticleTreeElement* element = (ParticleTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size);
ParticleTreeElement* element = (ParticleTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size);
element->storeParticle(particle);
}
// what else do we need to do here to get reaveraging to work
@ -149,8 +149,8 @@ void ParticleTree::addParticle(const ParticleID& particleID, const ParticlePrope
Particle particle(particleID, properties);
glm::vec3 position = particle.getPosition();
float size = std::max(MINIMUM_PARTICLE_ELEMENT_SIZE, particle.getRadius());
ParticleTreeElement* element = (ParticleTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size);
element->storeParticle(particle);
_isDirty = true;
@ -370,17 +370,12 @@ bool ParticleTree::findByIDOperation(OctreeElement* element, void* extraData) {
}
const Particle* ParticleTree::findParticleByID(uint32_t id, bool alreadyLocked) {
const Particle* ParticleTree::findParticleByID(uint32_t id) {
FindByIDArgs args = { id, false, NULL };
if (!alreadyLocked) {
//qDebug() << "ParticleTree::findParticleByID().... about to call lockForRead()....";
lockForRead();
//qDebug() << "ParticleTree::findParticleByID().... after call lockForRead()....";
}
lockForRead();
recurseTreeWithOperation(findByIDOperation, &args);
if (!alreadyLocked) {
unlock();
}
unlock();
return args.foundParticle;
}
@ -474,7 +469,9 @@ void ParticleTree::update() {
AABox treeBounds = getRoot()->getAABox();
if (!shouldDie && treeBounds.contains(args._movingParticles[i].getPosition())) {
lockForWrite();
storeParticle(args._movingParticles[i]);
unlock();
} else {
uint32_t particleID = args._movingParticles[i].getID();
quint64 deletedAt = usecTimestampNow();
@ -485,7 +482,9 @@ void ParticleTree::update() {
}
// prune the tree...
lockForWrite();
recurseTreeWithOperation(pruneOperation, NULL);
unlock();
}

View file

@ -44,7 +44,7 @@ public:
void addParticle(const ParticleID& particleID, const ParticleProperties& properties);
void deleteParticle(const ParticleID& particleID);
const Particle* findClosestParticle(glm::vec3 position, float targetRadius);
const Particle* findParticleByID(uint32_t id, bool alreadyLocked = false);
const Particle* findParticleByID(uint32_t id);
/// finds all particles that touch a sphere
/// \param center the center of the sphere

View file

@ -33,9 +33,7 @@ ParticleID ParticlesScriptingInterface::addParticle(const ParticleProperties& pr
// If we have a local particle tree set, then also update it.
if (_particleTree) {
_particleTree->lockForWrite();
_particleTree->addParticle(id, properties);
_particleTree->unlock();
}
return id;
@ -66,7 +64,7 @@ ParticleProperties ParticlesScriptingInterface::getParticleProperties(ParticleID
}
if (_particleTree) {
_particleTree->lockForRead();
const Particle* particle = _particleTree->findParticleByID(identity.id, true);
const Particle* particle = _particleTree->findParticleByID(identity.id);
if (particle) {
results.copyFromParticle(*particle);
} else {

View file

@ -30,12 +30,10 @@ public:
private slots:
/// inbound slots for external collision systems
void forwardParticleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel) {
qDebug() << "forwardParticleCollisionWithVoxel()";
emit particleCollisionWithVoxel(particleID, voxel);
}
void forwardParticleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB) {
qDebug() << "forwardParticleCollisionWithParticle()";
emit particleCollisionWithParticle(idA, idB);
}

View file

@ -71,6 +71,7 @@ void menuItemPropertiesFromScriptValue(const QScriptValue& object, MenuItemPrope
properties.menuItemName = object.property("menuItemName").toVariant().toString();
properties.isCheckable = object.property("isCheckable").toVariant().toBool();
properties.isChecked = object.property("isChecked").toVariant().toBool();
properties.isSeparator = object.property("isSeparator").toVariant().toBool();
// handle the shortcut key options in order...
QScriptValue shortcutKeyValue = object.property("shortcutKey");

View file

@ -39,6 +39,7 @@ public:
// other properties
bool isCheckable;
bool isChecked;
bool isSeparator;
};
Q_DECLARE_METATYPE(MenuItemProperties)
QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuItemProperties& props);

View file

@ -35,7 +35,7 @@ const QString ACCOUNTS_GROUP = "accounts";
AccountManager::AccountManager() :
_authURL(),
_networkAccessManager(),
_networkAccessManager(new QNetworkAccessManager(this)),
_pendingCallbackMap(),
_accountInfo()
{
@ -130,16 +130,16 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::
switch (operation) {
case QNetworkAccessManager::GetOperation:
networkReply = _networkAccessManager.get(authenticatedRequest);
networkReply = _networkAccessManager->get(authenticatedRequest);
break;
case QNetworkAccessManager::PostOperation:
case QNetworkAccessManager::PutOperation:
authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
if (operation == QNetworkAccessManager::PostOperation) {
networkReply = _networkAccessManager.post(authenticatedRequest, dataByteArray);
networkReply = _networkAccessManager->post(authenticatedRequest, dataByteArray);
} else {
networkReply = _networkAccessManager.put(authenticatedRequest, dataByteArray);
networkReply = _networkAccessManager->put(authenticatedRequest, dataByteArray);
}
break;
@ -242,7 +242,7 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
request.setUrl(grantURL);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QNetworkReply* requestReply = _networkAccessManager.post(request, postData);
QNetworkReply* requestReply = _networkAccessManager->post(request, postData);
connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestFinished);
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError)));
}
@ -292,4 +292,4 @@ void AccountManager::requestFinished() {
void AccountManager::requestError(QNetworkReply::NetworkError error) {
// TODO: error handling
qDebug() << "AccountManager requestError - " << error;
}
}

View file

@ -23,7 +23,7 @@ public:
jsonCallbackReceiver(NULL), jsonCallbackMethod(),
errorCallbackReceiver(NULL), errorCallbackMethod() {};
bool isEmpty() const { return jsonCallbackReceiver == NULL && errorCallbackReceiver == NULL; }
bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; }
QObject* jsonCallbackReceiver;
QString jsonCallbackMethod;
@ -53,6 +53,8 @@ public:
QString getUsername() const { return _accountInfo.getUsername(); }
void destroy() { delete _networkAccessManager; }
public slots:
void requestFinished();
void requestError(QNetworkReply::NetworkError error);
@ -76,7 +78,7 @@ private:
const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray);
QUrl _authURL;
QNetworkAccessManager _networkAccessManager;
QNetworkAccessManager* _networkAccessManager;
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
DataServerAccountInfo _accountInfo;

View file

@ -72,6 +72,5 @@ void GenericThread::threadRoutine() {
if (_isThreaded && _thread) {
_thread->quit();
}
emit finished();
}

View file

@ -98,7 +98,7 @@ void Node::activatePublicSocket() {
}
void Node::recordBytesReceived(int bytesReceived) {
if (_bytesReceivedMovingAverage == NULL) {
if (!_bytesReceivedMovingAverage) {
_bytesReceivedMovingAverage = new SimpleMovingAverage(100);
}

View file

@ -264,6 +264,11 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
break;
}
default:
// the node decided not to do anything with this packet
// if it comes from a known source we should keep that node alive
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
break;
}
}
@ -810,7 +815,7 @@ void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, con
SharedNodePointer NodeList::soloNodeOfType(char nodeType) {
if (memchr(SOLO_NODE_TYPES, nodeType, sizeof(SOLO_NODE_TYPES)) != NULL) {
if (memchr(SOLO_NODE_TYPES, nodeType, sizeof(SOLO_NODE_TYPES))) {
foreach (const SharedNodePointer& node, getNodeHash()) {
if (node->getType() == nodeType) {
return node;

View file

@ -73,7 +73,7 @@ unsigned char* childOctalCode(const unsigned char* parentOctalCode, char childNu
// find the length (in number of three bit code sequences)
// in the parent
int parentCodeSections = parentOctalCode != NULL
int parentCodeSections = parentOctalCode
? numberOfThreeBitSectionsInCode(parentOctalCode)
: 0;
@ -87,7 +87,7 @@ unsigned char* childOctalCode(const unsigned char* parentOctalCode, char childNu
unsigned char* newCode = new unsigned char[childCodeBytes];
// copy the parent code to the child
if (parentOctalCode != NULL) {
if (parentOctalCode) {
memcpy(newCode, parentOctalCode, parentCodeBytes);
}

View file

@ -10,7 +10,15 @@
#include "RegisteredMetaTypes.h"
static int vec4MetaTypeId = qRegisterMetaType<glm::vec4>();
static int vec3MetaTypeId = qRegisterMetaType<glm::vec3>();
static int vec2MetaTypeId = qRegisterMetaType<glm::vec2>();
static int quatMetaTypeId = qRegisterMetaType<glm::quat>();
static int xColorMetaTypeId = qRegisterMetaType<xColor>();
static int pickRayMetaTypeId = qRegisterMetaType<PickRay>();
void registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue);
qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue);
qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue);
qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue);
@ -18,6 +26,22 @@ void registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue);
}
QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4) {
QScriptValue obj = engine->newObject();
obj.setProperty("x", vec4.x);
obj.setProperty("y", vec4.y);
obj.setProperty("z", vec4.z);
obj.setProperty("w", vec4.w);
return obj;
}
void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4) {
vec4.x = object.property("x").toVariant().toFloat();
vec4.y = object.property("y").toVariant().toFloat();
vec4.z = object.property("z").toVariant().toFloat();
vec4.w = object.property("w").toVariant().toFloat();
}
QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3) {
QScriptValue obj = engine->newObject();
obj.setProperty("x", vec3.x);

View file

@ -17,6 +17,7 @@
#include "SharedUtil.h"
Q_DECLARE_METATYPE(glm::vec4)
Q_DECLARE_METATYPE(glm::vec3)
Q_DECLARE_METATYPE(glm::vec2)
Q_DECLARE_METATYPE(glm::quat)
@ -24,6 +25,9 @@ Q_DECLARE_METATYPE(xColor)
void registerMetaTypes(QScriptEngine* engine);
QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4);
void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4);
QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3);
void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3);

View file

@ -27,6 +27,7 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
if (resource.isNull()) {
resource = createResource(url, fallback.isValid() ?
getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra);
resource->setSelf(resource);
_resources.insert(url, resource);
}
return resource;
@ -77,8 +78,9 @@ Resource::Resource(const QUrl& url, bool delayLoad) :
_request(url),
_startedLoading(false),
_failedToLoad(false),
_attempts(0),
_reply(NULL) {
_loaded(false),
_reply(NULL),
_attempts(0) {
if (!url.isValid()) {
_startedLoading = _failedToLoad = true;
@ -106,10 +108,15 @@ void Resource::ensureLoading() {
}
void Resource::setLoadPriority(const QPointer<QObject>& owner, float priority) {
_loadPriorities.insert(owner, priority);
if (!(_failedToLoad || _loaded)) {
_loadPriorities.insert(owner, priority);
}
}
void Resource::setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities) {
if (_failedToLoad || _loaded) {
return;
}
for (QHash<QPointer<QObject>, float>::const_iterator it = priorities.constBegin();
it != priorities.constEnd(); it++) {
_loadPriorities.insert(it.key(), it.value());
@ -117,7 +124,9 @@ void Resource::setLoadPriorities(const QHash<QPointer<QObject>, float>& prioriti
}
void Resource::clearLoadPriority(const QPointer<QObject>& owner) {
_loadPriorities.remove(owner);
if (!(_failedToLoad || _loaded)) {
_loadPriorities.remove(owner);
}
}
float Resource::getLoadPriority() {
@ -138,6 +147,15 @@ void Resource::attemptRequest() {
ResourceCache::attemptRequest(this);
}
void Resource::finishedLoading(bool success) {
if (success) {
_loaded = true;
} else {
_failedToLoad = true;
}
_loadPriorities.clear();
}
void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
if (!_reply->isFinished()) {
return;
@ -182,7 +200,7 @@ void Resource::handleReplyError() {
// fall through to final failure
}
default:
_failedToLoad = true;
finishedLoading(false);
break;
}
}

View file

@ -88,6 +88,11 @@ public:
/// Returns the highest load priority across all owners.
float getLoadPriority();
/// Checks whether the resource has loaded.
bool isLoaded() const { return _loaded; }
void setSelf(const QWeakPointer<Resource>& self) { _self = self; }
protected slots:
void attemptRequest();
@ -96,10 +101,15 @@ protected:
virtual void downloadFinished(QNetworkReply* reply) = 0;
/// Should be called by subclasses when all the loading that will be done has been done.
Q_INVOKABLE void finishedLoading(bool success);
QNetworkRequest _request;
bool _startedLoading;
bool _failedToLoad;
bool _loaded;
QHash<QPointer<QObject>, float> _loadPriorities;
QWeakPointer<Resource> _self;
private slots:

View file

@ -1,16 +0,0 @@
//
// VoxelPacketData.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 11/19/2013.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include <PerfStat.h>
#include "VoxelPacketData.h"
// currently just an alias for OctreePacketData
VoxelPacketData::VoxelPacketData(bool enableCompression, int maxFinalizedSize) :
OctreePacketData(enableCompression, maxFinalizedSize) {
};

View file

@ -1,35 +0,0 @@
//
// VoxelPacketData.h
// hifi
//
// Created by Brad Hefta-Gaub on 11/19/2013
//
// TO DO:
//
// * add stats tracking for number of unique colors and consecutive identical colors.
// (as research for color dictionaries and RLE)
//
// * further testing of compression to determine optimal configuration for performance and compression
//
// * improve semantics for "reshuffle" - current approach will work for now and with compression
// but wouldn't work with RLE because the colors in the levels would get reordered and RLE would need
// to be recalculated
//
#ifndef __hifi__VoxelPacketData__
#define __hifi__VoxelPacketData__
#include <SharedUtil.h>
#include <OctreePacketData.h>
#include "VoxelConstants.h"
#include "VoxelTreeElement.h"
/// Handles packing of the data portion of PacketType_VOXEL_DATA messages.
class VoxelPacketData : public OctreePacketData {
public:
VoxelPacketData(bool enableCompression = false, int maxFinalizedSize = MAX_OCTREE_PACKET_DATA_SIZE);
};
#endif /* defined(__hifi__VoxelPacketData__) */

View file

@ -47,28 +47,12 @@ void VoxelTree::createVoxel(float x, float y, float z, float s,
unsigned char red, unsigned char green, unsigned char blue, bool destructive) {
unsigned char* voxelData = pointToVoxel(x,y,z,s,red,green,blue);
//int length = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(voxelData)) + BYTES_PER_COLOR;
//printf("createVoxel()...");
//outputBufferBits(voxelData,length);
this->readCodeColorBufferToTree(voxelData, destructive);
lockForWrite();
readCodeColorBufferToTree(voxelData, destructive);
unlock();
delete[] voxelData;
}
void VoxelTree::createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color, bool destructive) {
glm::vec3 distance = point2 - point1;
glm::vec3 items = distance / unitSize;
int maxItems = std::max(items.x, std::max(items.y, items.z));
glm::vec3 increment = distance * (1.0f/ maxItems);
glm::vec3 pointAt = point1;
for (int i = 0; i <= maxItems; i++ ) {
pointAt += increment;
createVoxel(pointAt.x, pointAt.y, pointAt.z, unitSize, color[0], color[1], color[2], destructive);
}
}
class NodeChunkArgs {
public:
VoxelTree* thisVoxelTree;
@ -577,4 +561,3 @@ int VoxelTree::processEditPacketData(PacketType packetType, const unsigned char*
return 0;
}
}

View file

@ -9,17 +9,9 @@
#ifndef __hifi__VoxelTree__
#define __hifi__VoxelTree__
#include <set>
#include <SimpleMovingAverage.h>
#include <OctreeElementBag.h>
#include <Octree.h>
#include <CoverageMap.h>
#include <JurisdictionMap.h>
#include <ViewFrustum.h>
#include "VoxelTreeElement.h"
#include "VoxelPacketData.h"
#include "VoxelSceneStats.h"
#include "VoxelEditPacketSender.h"
class ReadCodeColorBufferToTreeArgs;
@ -38,11 +30,8 @@ public:
void createVoxel(float x, float y, float z, float s,
unsigned char red, unsigned char green, unsigned char blue, bool destructive = false);
void createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color, bool destructive = false);
void nudgeSubTree(VoxelTreeElement* elementToNudge, const glm::vec3& nudgeAmount, VoxelEditPacketSender& voxelEditSender);
/// reads voxels from square image with alpha as a Y-axis
bool readFromSquareARGB32Pixels(const char *filename);
@ -55,16 +44,6 @@ public:
virtual bool handlesEditPacketType(PacketType packetType) const;
virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
const unsigned char* editData, int maxLength, const SharedNodePointer& node);
void processSetVoxelsBitstream(const unsigned char* bitstream, int bufferSizeBytes);
/**
signals:
void importSize(float x, float y, float z);
void importProgress(int progress);
public slots:
void cancelImport();
**/
private:
// helper functions for nudgeSubTree

View file

@ -39,8 +39,7 @@ void VoxelTreeElement::init(unsigned char* octalCode) {
setVoxelSystem(NULL);
setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
_falseColored = false; // assume true color
_currentColor[0] = _currentColor[1] = _currentColor[2] = _currentColor[3] = 0;
_trueColor[0] = _trueColor[1] = _trueColor[2] = _trueColor[3] = 0;
_color[0] = _color[1] = _color[2] = _color[3] = 0;
_density = 0.0f;
OctreeElement::init(octalCode);
_voxelMemoryUsage += sizeof(VoxelTreeElement);
@ -99,7 +98,7 @@ VoxelSystem* VoxelTreeElement::getVoxelSystem() const {
}
void VoxelTreeElement::setVoxelSystem(VoxelSystem* voxelSystem) {
if (voxelSystem == NULL) {
if (!voxelSystem) {
_voxelSystemIndex = INDEX_FOR_NULL;
} else {
uint8_t index;
@ -115,39 +114,9 @@ void VoxelTreeElement::setVoxelSystem(VoxelSystem* voxelSystem) {
}
}
void VoxelTreeElement::setFalseColor(colorPart red, colorPart green, colorPart blue) {
if (_falseColored != true || _currentColor[0] != red || _currentColor[1] != green || _currentColor[2] != blue) {
_falseColored=true;
_currentColor[0] = red;
_currentColor[1] = green;
_currentColor[2] = blue;
_currentColor[3] = 1; // XXXBHG - False colors are always considered set
_isDirty = true;
markWithChangedTime();
}
}
void VoxelTreeElement::setFalseColored(bool isFalseColored) {
if (_falseColored != isFalseColored) {
// if we were false colored, and are no longer false colored, then swap back
if (_falseColored && !isFalseColored) {
memcpy(&_currentColor,&_trueColor,sizeof(nodeColor));
}
_falseColored = isFalseColored;
_isDirty = true;
_density = 1.0f; // If color set, assume leaf, re-averaging will update density if needed.
markWithChangedTime();
}
};
void VoxelTreeElement::setColor(const nodeColor& color) {
if (_trueColor[0] != color[0] || _trueColor[1] != color[1] || _trueColor[2] != color[2]) {
memcpy(&_trueColor,&color,sizeof(nodeColor));
if (!_falseColored) {
memcpy(&_currentColor,&color,sizeof(nodeColor));
}
if (_color[0] != color[0] || _color[1] != color[1] || _color[2] != color[2]) {
memcpy(&_color,&color,sizeof(nodeColor));
_isDirty = true;
if (color[3]) {
_density = 1.0f; // If color set, assume leaf, re-averaging will update density if needed.
@ -168,7 +137,7 @@ void VoxelTreeElement::calculateAverageFromChildren() {
VoxelTreeElement* childAt = getChildAtIndex(i);
if (childAt && childAt->isColored()) {
for (int j = 0; j < 3; j++) {
colorArray[j] += childAt->getTrueColor()[j]; // color averaging should always be based on true colors
colorArray[j] += childAt->getColor()[j]; // color averaging should always be based on true colors
}
colorArray[3]++;
}
@ -260,9 +229,9 @@ bool VoxelTreeElement::findSpherePenetration(const glm::vec3& center, float radi
voxelDetails->y = _box.getCorner().y;
voxelDetails->z = _box.getCorner().z;
voxelDetails->s = _box.getScale();
voxelDetails->red = getTrueColor()[RED_INDEX];
voxelDetails->green = getTrueColor()[GREEN_INDEX];
voxelDetails->blue = getTrueColor()[BLUE_INDEX];
voxelDetails->red = getColor()[RED_INDEX];
voxelDetails->green = getColor()[GREEN_INDEX];
voxelDetails->blue = getColor()[BLUE_INDEX];
*penetratedObject = (void*)voxelDetails;
}

View file

@ -57,14 +57,10 @@ public:
virtual bool isRendered() const { return isKnownBufferIndex(); }
bool isColored() const { return _trueColor[3] == 1; }
bool isColored() const { return _color[3] == 1; }
void setFalseColor(colorPart red, colorPart green, colorPart blue);
void setFalseColored(bool isFalseColored);
bool getFalseColored() { return _falseColored; }
void setColor(const nodeColor& color);
const nodeColor& getTrueColor() const { return _trueColor; }
const nodeColor& getColor() const { return _currentColor; }
const nodeColor& getColor() const { return _color; }
void setDensity(float density) { _density = density; }
float getDensity() const { return _density; }
@ -92,8 +88,7 @@ protected:
float _density; /// Client and server, If leaf: density = 1, if internal node: 0-1 density of voxels inside, 4 bytes
nodeColor _trueColor; /// Client and server, true color of this voxel, 4 bytes
nodeColor _currentColor; /// Client only, false color of this voxel, 4 bytes
nodeColor _color; /// Client and server, true color of this voxel, 4 bytes
private:
unsigned char _exteriorOcclusions; ///< Exterior shared partition boundaries that are completely occupied

View file

@ -47,9 +47,7 @@ void VoxelsScriptingInterface::setVoxelNonDestructive(float x, float y, float z,
// handle the local tree also...
if (_tree) {
_tree->lockForWrite();
_tree->createVoxel(addVoxelDetail.x, addVoxelDetail.y, addVoxelDetail.z, addVoxelDetail.s, red, green, blue, false);
_tree->unlock();
}
}
@ -64,9 +62,7 @@ void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale,
// handle the local tree also...
if (_tree) {
_tree->lockForWrite();
_tree->createVoxel(addVoxelDetail.x, addVoxelDetail.y, addVoxelDetail.z, addVoxelDetail.s, red, green, blue, true);
_tree->unlock();
}
}
@ -80,9 +76,7 @@ void VoxelsScriptingInterface::eraseVoxel(float x, float y, float z, float scale
// handle the local tree also...
if (_tree) {
_tree->lockForWrite();
_tree->deleteVoxelAt(deleteVoxelDetail.x, deleteVoxelDetail.y, deleteVoxelDetail.z, deleteVoxelDetail.s);
_tree->unlock();
}
}
@ -90,21 +84,18 @@ void VoxelsScriptingInterface::eraseVoxel(float x, float y, float z, float scale
RayToVoxelIntersectionResult VoxelsScriptingInterface::findRayIntersection(const PickRay& ray) {
RayToVoxelIntersectionResult result;
if (_tree) {
if (_tree->tryLockForRead()) {
OctreeElement* element;
result.intersects = _tree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face);
if (result.intersects) {
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
result.voxel.x = voxel->getCorner().x;
result.voxel.y = voxel->getCorner().y;
result.voxel.z = voxel->getCorner().z;
result.voxel.s = voxel->getScale();
result.voxel.red = voxel->getColor()[0];
result.voxel.green = voxel->getColor()[1];
result.voxel.blue = voxel->getColor()[2];
result.intersection = ray.origin + (ray.direction * result.distance);
}
_tree->unlock();
OctreeElement* element;
result.intersects = _tree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face);
if (result.intersects) {
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
result.voxel.x = voxel->getCorner().x;
result.voxel.y = voxel->getCorner().y;
result.voxel.z = voxel->getCorner().z;
result.voxel.s = voxel->getScale();
result.voxel.red = voxel->getColor()[0];
result.voxel.green = voxel->getColor()[1];
result.voxel.blue = voxel->getColor()[2];
result.intersection = ray.origin + (ray.direction * result.distance);
}
}
return result;
@ -127,3 +118,22 @@ glm::vec3 VoxelsScriptingInterface::getFaceVector(const QString& face) {
return glm::vec3(0, 0, 0); //error case
}
VoxelDetail VoxelsScriptingInterface::getVoxelEnclosingPoint(const glm::vec3& point) {
VoxelDetail result = { 0.0f, 0.0f, 0.0f, 0.0f, 0, 0, 0 };
if (_tree) {
OctreeElement* element = _tree->getElementEnclosingPoint(point / (float)TREE_SCALE);
if (element) {
VoxelTreeElement* voxel = static_cast<VoxelTreeElement*>(element);
result.x = voxel->getCorner().x;
result.y = voxel->getCorner().y;
result.z = voxel->getCorner().z;
result.s = voxel->getScale();
result.red = voxel->getColor()[0];
result.green = voxel->getColor()[1];
result.blue = voxel->getColor()[2];
}
}
return result;
}

View file

@ -70,6 +70,11 @@ public slots:
/// returns a voxel space axis aligned vector for the face, useful in doing voxel math
glm::vec3 getFaceVector(const QString& face);
/// checks the local voxel tree for the smallest voxel enclosing the point
/// \param point the x,y,z coordinates of the point (in meter units)
/// \return VoxelDetail - if no voxel encloses the point then VoxelDetail items will be 0
VoxelDetail getVoxelEnclosingPoint(const glm::vec3& point);
private:
void queueVoxelAdd(PacketType addPacketType, VoxelDetail& addVoxelDetails);

View file

@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.8)
set(ROOT_DIR ..)
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
set(TARGET_NAME SvoViewer)
set(TARGET_NAME svo-viewer)
project(${TARGET_NAME})
# setup for find modules
@ -42,9 +42,9 @@ endif (WIN32)
include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
# create the ${TARGET_NAME}Config.h file based on GL_HEADERS above
configure_file(${TARGET_NAME}Config.h.in ${PROJECT_BINARY_DIR}/includes/${TARGET_NAME}Config.h)
configure_file(${TARGET_NAME}Version.h.in ${PROJECT_BINARY_DIR}/includes/${TARGET_NAME}Version.h)
# create the ${TARGET_NAME}-config.h file based on GL_HEADERS above
configure_file(${TARGET_NAME}-config.h.in ${PROJECT_BINARY_DIR}/includes/${TARGET_NAME}-config.h)
configure_file(${TARGET_NAME}-version.h.in ${PROJECT_BINARY_DIR}/includes/${TARGET_NAME}-version.h)
# grab the implementation and header files from src dirs
file(GLOB APPLICATION_SRCS src/*.c src/*.cpp src/*.h)
@ -81,7 +81,7 @@ if (APPLE)
# configure CMake to use a custom Info.plist
SET_TARGET_PROPERTIES( ${this_target} PROPERTIES MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in )
set(MACOSX_BUNDLE_BUNDLE_NAME SvoViewer)
set(MACOSX_BUNDLE_BUNDLE_NAME svo-viewer)
set(MACOSX_BUNDLE_GUI_IDENTIFIER io.highfidelity.${TARGET_NAME})
# set how the icon shows up in the Info.plist file

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