diff --git a/CMakeLists.txt b/CMakeLists.txt index b271664c35..347341efa0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,10 +45,11 @@ if (WIN32) # Caveats: http://stackoverflow.com/questions/2288728/drawbacks-of-using-largeaddressaware-for-32-bit-windows-executables # TODO: Remove when building 64-bit. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE") -elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - #SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic") - #SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter -ggdb") +else () + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter") + if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb") + endif () endif(WIN32) if (NOT MSVC12) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index a0da273462..594805c7c2 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -114,7 +114,23 @@ void AssignmentClient::stopAssignmentClient() { qDebug() << "Exiting."; _requestTimer.stop(); _statsTimerACM.stop(); - QCoreApplication::quit(); + if (_currentAssignment) { + _currentAssignment->aboutToQuit(); + QThread* currentAssignmentThread = _currentAssignment->thread(); + currentAssignmentThread->quit(); + currentAssignmentThread->wait(); + } +} + + +void AssignmentClient::aboutToQuit() { + stopAssignmentClient(); + // clear the log handler so that Qt doesn't call the destructor on LogHandler + qInstallMessageHandler(0); + // clear out pointer to the assignment so the destructor gets called. if we don't do this here, + // it will get destroyed along with all the other "static" stuff. various static member variables + // will be destroyed first and things go wrong. + _currentAssignment.clear(); } @@ -197,6 +213,7 @@ void AssignmentClient::readPendingDatagrams() { // start the deployed assignment AssignmentThread* workerThread = new AssignmentThread(_currentAssignment, this); + workerThread->setObjectName("worker"); connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run); connect(_currentAssignment.data(), &ThreadedAssignment::finished, workerThread, &QThread::quit); diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index 08673ab04c..1ffb862dd3 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -34,6 +34,9 @@ private slots: void sendStatsPacketToACM(); void stopAssignmentClient(); +public slots: + void aboutToQuit(); + private: void setUpStatsToMonitor(int ppid); Assignment _requestAssignment; diff --git a/assignment-client/src/AssignmentClientApp.cpp b/assignment-client/src/AssignmentClientApp.cpp index 17f2eac70d..2de349ca4e 100644 --- a/assignment-client/src/AssignmentClientApp.cpp +++ b/assignment-client/src/AssignmentClientApp.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include @@ -180,14 +181,19 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : } } + QThread::currentThread()->setObjectName("main thread"); + + DependencyManager::registerInheritance(); if (numForks || minForks || maxForks) { AssignmentClientMonitor monitor(numForks, minForks, maxForks, requestAssignmentType, assignmentPool, walletUUID, assignmentServerHostname, assignmentServerPort); + connect(this, &QCoreApplication::aboutToQuit, &monitor, &AssignmentClientMonitor::aboutToQuit); exec(); } else { AssignmentClient client(ppid, requestAssignmentType, assignmentPool, walletUUID, assignmentServerHostname, assignmentServerPort); + connect(this, &QCoreApplication::aboutToQuit, &client, &AssignmentClient::aboutToQuit); exec(); } } diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index f058502cd3..1002bc61aa 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -70,6 +70,17 @@ AssignmentClientMonitor::~AssignmentClientMonitor() { stopChildProcesses(); } +void AssignmentClientMonitor::waitOnChildren(int msecs) { + QMutableListIterator i(_childProcesses); + while (i.hasNext()) { + QProcess* childProcess = i.next(); + bool finished = childProcess->waitForFinished(msecs); + if (finished) { + i.remove(); + } + } +} + void AssignmentClientMonitor::stopChildProcesses() { auto nodeList = DependencyManager::get(); @@ -79,11 +90,22 @@ void AssignmentClientMonitor::stopChildProcesses() { QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode); nodeList->writeUnverifiedDatagram(diePacket, *node->getActiveSocket()); }); + + // try to give all the children time to shutdown + waitOnChildren(15000); +} + +void AssignmentClientMonitor::aboutToQuit() { + stopChildProcesses(); + // clear the log handler so that Qt doesn't call the destructor on LogHandler + qInstallMessageHandler(0); } void AssignmentClientMonitor::spawnChildClient() { QProcess *assignmentClient = new QProcess(this); + _childProcesses.append(assignmentClient); + // unparse the parts of the command-line that the child cares about QStringList _childArguments; if (_assignmentPool != "") { @@ -120,8 +142,6 @@ void AssignmentClientMonitor::spawnChildClient() { qDebug() << "Spawned a child client with PID" << assignmentClient->pid(); } - - void AssignmentClientMonitor::checkSpares() { auto nodeList = DependencyManager::get(); QUuid aSpareId = ""; @@ -157,6 +177,8 @@ void AssignmentClientMonitor::checkSpares() { nodeList->writeUnverifiedDatagram(diePacket, childNode); } } + + waitOnChildren(0); } diff --git a/assignment-client/src/AssignmentClientMonitor.h b/assignment-client/src/AssignmentClientMonitor.h index 996220b1b4..e0129bd9b9 100644 --- a/assignment-client/src/AssignmentClientMonitor.h +++ b/assignment-client/src/AssignmentClientMonitor.h @@ -32,12 +32,16 @@ public: QString assignmentPool, QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort); ~AssignmentClientMonitor(); - + + void waitOnChildren(int msecs); void stopChildProcesses(); private slots: void readPendingDatagrams(); void checkSpares(); +public slots: + void aboutToQuit(); + private: void spawnChildClient(); QTimer _checkSparesTimer; // every few seconds see if it need fewer or more spare children @@ -52,6 +56,7 @@ private: QString _assignmentServerHostname; quint16 _assignmentServerPort; + QList _childProcesses; }; #endif // hifi_AssignmentClientMonitor_h diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index c6cfad2518..4c7c5fdf32 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -49,6 +49,9 @@ AvatarMixer::AvatarMixer(const QByteArray& packet) : } AvatarMixer::~AvatarMixer() { + if (_broadcastTimer) { + _broadcastTimer->deleteLater(); + } _broadcastThread.quit(); _broadcastThread.wait(); } @@ -457,9 +460,9 @@ void AvatarMixer::run() { nodeList->linkedDataCreateCallback = attachAvatarDataToNode; // setup the timer that will be fired on the broadcast thread - QTimer* broadcastTimer = new QTimer(); - broadcastTimer->setInterval(AVATAR_DATA_SEND_INTERVAL_MSECS); - broadcastTimer->moveToThread(&_broadcastThread); + _broadcastTimer = new QTimer(); + _broadcastTimer->setInterval(AVATAR_DATA_SEND_INTERVAL_MSECS); + _broadcastTimer->moveToThread(&_broadcastThread); // connect appropriate signals and slots connect(broadcastTimer, &QTimer::timeout, this, &AvatarMixer::broadcastAvatarData, Qt::DirectConnection); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 714dba2471..7505c4324e 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -50,6 +50,8 @@ private: int _sumIdentityPackets; float _maxKbpsPerNode = 0.0f; + + QTimer* _broadcastTimer = nullptr; }; #endif // hifi_AvatarMixer_h diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 85d0a7414c..bb5042f4b4 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -27,6 +27,11 @@ EntityServer::EntityServer(const QByteArray& packet) } EntityServer::~EntityServer() { + if (_pruneDeletedEntitiesTimer) { + _pruneDeletedEntitiesTimer->stop(); + _pruneDeletedEntitiesTimer->deleteLater(); + } + EntityTree* tree = (EntityTree*)_tree; tree->removeNewlyCreatedHook(this); } @@ -48,10 +53,10 @@ Octree* EntityServer::createTree() { } void EntityServer::beforeRun() { - QTimer* pruneDeletedEntitiesTimer = new QTimer(this); - connect(pruneDeletedEntitiesTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedEntities())); + _pruneDeletedEntitiesTimer = new QTimer(); + connect(_pruneDeletedEntitiesTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedEntities())); const int PRUNE_DELETED_MODELS_INTERVAL_MSECS = 1 * 1000; // once every second - pruneDeletedEntitiesTimer->start(PRUNE_DELETED_MODELS_INTERVAL_MSECS); + _pruneDeletedEntitiesTimer->start(PRUNE_DELETED_MODELS_INTERVAL_MSECS); } void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) { diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index d8c2e39f3b..9edec7b704 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -51,6 +51,7 @@ protected: private: EntitySimulation* _entitySimulation; + QTimer* _pruneDeletedEntitiesTimer = nullptr; }; #endif // hifi_EntityServer_h diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.h b/assignment-client/src/octree/OctreeInboundPacketProcessor.h index 8f07f9d566..156e09b493 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.h +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.h @@ -74,7 +74,7 @@ public: NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; } - void shuttingDown() { _shuttingDown = true;} + virtual void terminating() { _shuttingDown = true; ReceivedPacketProcessor::terminating(); } protected: diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 6c5eb38fad..9abace0c5b 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -267,16 +267,19 @@ OctreeServer::~OctreeServer() { } if (_jurisdictionSender) { + _jurisdictionSender->terminating(); _jurisdictionSender->terminate(); _jurisdictionSender->deleteLater(); } if (_octreeInboundPacketProcessor) { + _octreeInboundPacketProcessor->terminating(); _octreeInboundPacketProcessor->terminate(); _octreeInboundPacketProcessor->deleteLater(); } if (_persistThread) { + _persistThread->terminating(); _persistThread->terminate(); _persistThread->deleteLater(); } @@ -1096,8 +1099,6 @@ void OctreeServer::readConfiguration() { } void OctreeServer::run() { - qInstallMessageHandler(LogHandler::verboseMessageHandler); - _safeServerName = getMyServerName(); // Before we do anything else, create our tree... @@ -1220,8 +1221,15 @@ void OctreeServer::forceNodeShutdown(SharedNodePointer node) { void OctreeServer::aboutToFinish() { qDebug() << qPrintable(_safeServerName) << "server STARTING about to finish..."; qDebug() << qPrintable(_safeServerName) << "inform Octree Inbound Packet Processor that we are shutting down..."; - _octreeInboundPacketProcessor->shuttingDown(); - + + if (_octreeInboundPacketProcessor) { + _octreeInboundPacketProcessor->terminating(); + } + + if (_jurisdictionSender) { + _jurisdictionSender->terminating(); + } + DependencyManager::get()->eachNode([this](const SharedNodePointer& node) { qDebug() << qPrintable(_safeServerName) << "server about to finish while node still connected node:" << *node; forceNodeShutdown(node); @@ -1229,6 +1237,7 @@ void OctreeServer::aboutToFinish() { if (_persistThread) { _persistThread->aboutToFinish(); + _persistThread->terminating(); } qDebug() << qPrintable(_safeServerName) << "server ENDING about to finish..."; diff --git a/examples/avatarSelector.js b/examples/avatarSelector.js new file mode 100644 index 0000000000..dc2916a1a8 --- /dev/null +++ b/examples/avatarSelector.js @@ -0,0 +1,416 @@ +// +// avatarSelector.js +// examples +// +// Created by David Rowe on 21 Apr 2015. +// Copyright 2015 High Fidelity, Inc. +// +// Based on lobby.js created by Stephen Birarda on 17 Oct 2014. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + +var panelWall = false; +var orbShell = false; +var descriptionText = false; +var showText = false; + +// used for formating the description text, in meters +var textWidth = 4; +var textHeight = .5; +var numberOfLines = 2; +var textMargin = 0.0625; +var lineHeight = (textHeight - (2 * textMargin)) / numberOfLines; + +var avatarStickPosition = {}; + +var orbNaturalExtentsMin = { x: -1.230354, y: -1.22077, z: -1.210487 }; +var orbNaturalExtentsMax = { x: 1.230353, y: 1.229819, z: 1.210487 }; +var panelsNaturalExtentsMin = { x: -1.223182, y: -0.348487, z: 0.0451369 }; +var panelsNaturalExtentsMax = { x: 1.223039, y: 0.602978, z: 1.224298 }; + +var orbNaturalDimensions = Vec3.subtract(orbNaturalExtentsMax, orbNaturalExtentsMin); +var panelsNaturalDimensions = Vec3.subtract(panelsNaturalExtentsMax, panelsNaturalExtentsMin); + +var SCALING_FACTOR = 10; +var orbDimensions = Vec3.multiply(orbNaturalDimensions, SCALING_FACTOR); +var panelsDimensions = Vec3.multiply(panelsNaturalDimensions, SCALING_FACTOR); + +var orbNaturalCenter = Vec3.sum(orbNaturalExtentsMin, Vec3.multiply(orbNaturalDimensions, 0.5)); +var panelsNaturalCenter = Vec3.sum(panelsNaturalExtentsMin, Vec3.multiply(panelsNaturalDimensions, 0.5)); +var orbCenter = Vec3.multiply(orbNaturalCenter, SCALING_FACTOR); +var panelsCenter = Vec3.multiply(panelsNaturalCenter, SCALING_FACTOR); +var panelsCenterShift = Vec3.subtract(panelsCenter, orbCenter); + +var ORB_SHIFT = { x: 0, y: -1.4, z: -0.8 }; + +var LOBBY_PANEL_WALL_URL = HIFI_PUBLIC_BUCKET + "models/sets/Lobby/PanelWallForInterface.fbx"; +var LOBBY_BLANK_PANEL_TEXTURE_URL = HIFI_PUBLIC_BUCKET + "models/sets/Lobby/Texture.jpg"; +var LOBBY_SHELL_URL = HIFI_PUBLIC_BUCKET + "models/sets/Lobby/LobbyShellForInterface.fbx"; + +var droneSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/drone.stereo.raw") +var currentDrone = null; + +var latinSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/latin.stereo.raw") +var latinInjector = null; +var elevatorSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/elevator.stereo.raw") +var elevatorInjector = null; +var currentMuzakInjector = null; +var currentSound = null; + +function textOverlayPosition() { + var TEXT_DISTANCE_OUT = 6; + var TEXT_DISTANCE_DOWN = -2; + return Vec3.sum(Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), TEXT_DISTANCE_OUT)), + Vec3.multiply(Quat.getUp(Camera.orientation), TEXT_DISTANCE_DOWN)); +} + +var panelPlaceOrder = [ + 7, 8, 9, 10, 11, 12, 13, + 0, 1, 2, 3, 4, 5, 6, + 14, 15, 16, 17, 18, 19, 20 +]; + +// Avatar index is 0-based +function avatarIndexToPanelIndex(avatarIndex) { + return panelPlaceOrder.indexOf(avatarIndex) + 1; +} + +// Panel index is 1-based +function panelIndexToPlaceIndex(panelIndex) { + return panelPlaceOrder[panelIndex - 1]; +} + +var MAX_NUM_PANELS = 21; +var DRONE_VOLUME = 0.3; + +function drawLobby() { + if (!panelWall) { + print("Adding overlays for the avatar selector panel wall and orb shell."); + + var cameraEuler = Quat.safeEulerAngles(Camera.orientation); + var towardsMe = Quat.angleAxis(cameraEuler.y + 180, { x: 0, y: 1, z: 0 }); + + var orbPosition = Vec3.sum(Camera.position, Vec3.multiplyQbyV(towardsMe, ORB_SHIFT)); + + var panelWallProps = { + url: LOBBY_PANEL_WALL_URL, + position: Vec3.sum(orbPosition, Vec3.multiplyQbyV(towardsMe, panelsCenterShift)), + rotation: towardsMe, + dimensions: panelsDimensions + }; + + var orbShellProps = { + url: LOBBY_SHELL_URL, + position: orbPosition, + rotation: towardsMe, + dimensions: orbDimensions, + ignoreRayIntersection: true + }; + + var windowDimensions = Controller.getViewportDimensions(); + + var descriptionTextProps = { + position: textOverlayPosition(), + dimensions: { x: textWidth, y: textHeight }, + backgroundColor: { red: 0, green: 0, blue: 0 }, + color: { red: 255, green: 255, blue: 255 }, + topMargin: textMargin, + leftMargin: textMargin, + bottomMargin: textMargin, + rightMargin: textMargin, + text: "", + lineHeight: lineHeight, + alpha: 0.9, + backgroundAlpha: 0.9, + ignoreRayIntersection: true, + visible: false, + isFacingAvatar: true + }; + + avatarStickPosition = MyAvatar.position; + + panelWall = Overlays.addOverlay("model", panelWallProps); + orbShell = Overlays.addOverlay("model", orbShellProps); + descriptionText = Overlays.addOverlay("text3d", descriptionTextProps); + + if (droneSound.downloaded) { + // start the drone sound + if (!currentDrone) { + currentDrone = Audio.playSound(droneSound, { stereo: true, loop: true, localOnly: true, volume: DRONE_VOLUME }); + } else { + currentDrone.restart(); + } + } + + // start one of our muzak sounds + playRandomMuzak(); + } +} + +var avatars = {}; + +function changeLobbyTextures() { + var req = new XMLHttpRequest(); + req.open("GET", "https://metaverse.highfidelity.com/api/v1/marketplace?category=head+%26+body&limit=21", false); + req.send(); // Data returned is randomized. + + avatars = JSON.parse(req.responseText).data.items; + + var NUM_PANELS = avatars.length; + + var textureProp = { + textures: {} + }; + + for (var j = 0; j < NUM_PANELS; j++) { + var panelIndex = avatarIndexToPanelIndex(j); + textureProp["textures"]["file" + panelIndex] = avatars[j].preview_url; + }; + + Overlays.editOverlay(panelWall, textureProp); +} + +var MUZAK_VOLUME = 0.1; + +function playCurrentSound(secondOffset) { + if (currentSound == latinSound) { + if (!latinInjector) { + latinInjector = Audio.playSound(latinSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME }); + } else { + latinInjector.restart(); + } + + currentMuzakInjector = latinInjector; + } else if (currentSound == elevatorSound) { + if (!elevatorInjector) { + elevatorInjector = Audio.playSound(elevatorSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME }); + } else { + elevatorInjector.restart(); + } + + currentMuzakInjector = elevatorInjector; + } +} + +function playNextMuzak() { + if (panelWall) { + if (currentSound == latinSound) { + if (elevatorSound.downloaded) { + currentSound = elevatorSound; + } + } else if (currentSound == elevatorSound) { + if (latinSound.downloaded) { + currentSound = latinSound; + } + } + + playCurrentSound(0); + } +} + +function playRandomMuzak() { + currentSound = null; + + if (latinSound.downloaded && elevatorSound.downloaded) { + currentSound = Math.random() < 0.5 ? latinSound : elevatorSound; + } else if (latinSound.downloaded) { + currentSound = latinSound; + } else if (elevatorSound.downloaded) { + currentSound = elevatorSound; + } + + if (currentSound) { + // pick a random number of seconds from 0-10 to offset the muzak + var secondOffset = Math.random() * 10; + + playCurrentSound(secondOffset); + } else { + currentMuzakInjector = null; + } +} + +function cleanupLobby() { + toggleEnvironmentRendering(true); + + // for each of the 21 placeholder textures, set them back to default so the cached model doesn't have changed textures + var panelTexturesReset = {}; + panelTexturesReset["textures"] = {}; + + for (var j = 0; j < MAX_NUM_PANELS; j++) { + panelTexturesReset["textures"]["file" + (j + 1)] = LOBBY_BLANK_PANEL_TEXTURE_URL; + }; + + Overlays.editOverlay(panelWall, panelTexturesReset); + + Overlays.deleteOverlay(panelWall); + Overlays.deleteOverlay(orbShell); + Overlays.deleteOverlay(descriptionText); + + panelWall = false; + orbShell = false; + + if (currentDrone) { + currentDrone.stop(); + currentDrone = null + } + + if (currentMuzakInjector) { + currentMuzakInjector.stop(); + currentMuzakInjector = null; + } + + avatars = {}; + +} + +function actionStartEvent(event) { + if (panelWall) { + // we've got an action event and our panel wall is up + // check if we hit a panel and if we should jump there + var result = Overlays.findRayIntersection(event.actionRay); + if (result.intersects && result.overlayID == panelWall) { + + var panelName = result.extraInfo; + + var panelStringIndex = panelName.indexOf("Panel"); + if (panelStringIndex != -1) { + var panelIndex = parseInt(panelName.slice(5)); + var avatarIndex = panelIndexToPlaceIndex(panelIndex); + if (avatarIndex < avatars.length) { + var actionPlace = avatars[avatarIndex]; + + print("Changing avatar to " + actionPlace.name + + " after click on panel " + panelIndex + " with avatar index " + avatarIndex); + + MyAvatar.useFullAvatarURL(actionPlace.content_url); + + maybeCleanupLobby(); + } + } + } + } +} + +var control = false; + +function keyPressEvent(event) { + if (event.text === "CONTROL") { + control = true; + } + + if (control && event.text === "a") { + if (!panelWall) { + toggleEnvironmentRendering(false); + drawLobby(); + changeLobbyTextures(); + } else { + cleanupLobby(); + } + } +} + +function keyReleaseEvent(event) { + if (event.text === "CONTROL") { + control = false; + } +} + +var CLEANUP_EPSILON_DISTANCE = 0.05; + +function maybeCleanupLobby() { + if (panelWall && Vec3.length(Vec3.subtract(avatarStickPosition, MyAvatar.position)) > CLEANUP_EPSILON_DISTANCE) { + cleanupLobby(); + } +} + +function toggleEnvironmentRendering(shouldRender) { + Scene.shouldRenderAvatars = shouldRender; + Scene.shouldRenderEntities = shouldRender; +} + +function handleLookAt(pickRay) { + if (panelWall && descriptionText) { + // we've got an action event and our panel wall is up + // check if we hit a panel and if we should jump there + var result = Overlays.findRayIntersection(pickRay); + if (result.intersects && result.overlayID == panelWall) { + var panelName = result.extraInfo; + var panelStringIndex = panelName.indexOf("Panel"); + if (panelStringIndex != -1) { + var panelIndex = parseInt(panelName.slice(5)); + var avatarIndex = panelIndexToPlaceIndex(panelIndex); + if (avatarIndex < avatars.length) { + var actionPlace = avatars[avatarIndex]; + + if (actionPlace.description == "") { + Overlays.editOverlay(descriptionText, { text: actionPlace.name, visible: showText }); + } else { + // handle line wrapping + var allWords = actionPlace.description.split(" "); + var currentGoodLine = ""; + var currentTestLine = ""; + var formatedDescription = ""; + var wordsFormated = 0; + var currentTestWord = 0; + var wordsOnLine = 0; + while (wordsFormated < allWords.length) { + // first add the "next word" to the line and test it. + currentTestLine = currentGoodLine; + if (wordsOnLine > 0) { + currentTestLine += " " + allWords[currentTestWord]; + } else { + currentTestLine = allWords[currentTestWord]; + } + var lineLength = Overlays.textSize(descriptionText, currentTestLine).width; + if (lineLength < textWidth || wordsOnLine == 0) { + wordsFormated++; + currentTestWord++; + wordsOnLine++; + currentGoodLine = currentTestLine; + } else { + formatedDescription += currentGoodLine + "\n"; + wordsOnLine = 0; + currentGoodLine = ""; + currentTestLine = ""; + } + } + formatedDescription += currentGoodLine; + Overlays.editOverlay(descriptionText, { text: formatedDescription, visible: showText }); + } + } else { + Overlays.editOverlay(descriptionText, { text: "", visible: false }); + } + } + } + } +} + +function update(deltaTime) { + maybeCleanupLobby(); + if (panelWall) { + Overlays.editOverlay(descriptionText, { position: textOverlayPosition() }); + + // if the reticle is up then we may need to play the next muzak + if (currentMuzakInjector && !currentMuzakInjector.isPlaying) { + playNextMuzak(); + } + } +} + +function mouseMoveEvent(event) { + if (panelWall) { + var pickRay = Camera.computePickRay(event.x, event.y); + handleLookAt(pickRay); + } +} + +Controller.actionStartEvent.connect(actionStartEvent); +Controller.keyPressEvent.connect(keyPressEvent); +Controller.keyReleaseEvent.connect(keyReleaseEvent); +Script.update.connect(update); +Script.scriptEnding.connect(maybeCleanupLobby); +Controller.mouseMoveEvent.connect(mouseMoveEvent); diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index 05ffb0bd3f..a5c086fc44 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -18,3 +18,4 @@ Script.load("lobby.js"); Script.load("notifications.js"); Script.load("look.js"); Script.load("users.js"); +Script.load("grab.js"); diff --git a/examples/edit.js b/examples/edit.js index 59dcbaad19..93c06c38f2 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -592,7 +592,11 @@ var idleMouseTimerId = null; var IDLE_MOUSE_TIMEOUT = 200; var DEFAULT_ENTITY_DRAG_DROP_DISTANCE = 2.0; -function mouseMoveEvent(event) { +var lastMouseMoveEvent = null; +function mouseMoveEventBuffered(event) { + lastMouseMoveEvent = event; +} +function mouseMove(event) { mouseHasMovedSincePress = true; if (placingEntityID) { @@ -661,6 +665,10 @@ function highlightEntityUnderCursor(position, accurateRay) { function mouseReleaseEvent(event) { + if (lastMouseMoveEvent) { + mouseMove(lastMouseMoveEvent); + lastMouseMoveEvent = null; + } if (propertyMenu.mouseReleaseEvent(event) || toolBar.mouseReleaseEvent(event)) { return true; } @@ -772,7 +780,7 @@ function mouseClickEvent(event) { } Controller.mousePressEvent.connect(mousePressEvent); -Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.mouseMoveEvent.connect(mouseMoveEventBuffered); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); @@ -882,6 +890,10 @@ Script.update.connect(function (deltaTime) { lastOrientation = Camera.orientation; lastPosition = Camera.position; } + if (lastMouseMoveEvent) { + mouseMove(lastMouseMoveEvent); + lastMouseMoveEvent = null; + } }); function insideBox(center, dimensions, point) { @@ -1201,6 +1213,9 @@ PropertiesTool = function(opts) { data.properties.rotation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z); } Entities.editEntity(selectionManager.selections[0], data.properties); + if (data.properties.name != undefined) { + entityListTool.sendUpdate(); + } } pushCommandForSelections(); selectionManager._update(); diff --git a/examples/grab.js b/examples/grab.js new file mode 100644 index 0000000000..cd7d59701e --- /dev/null +++ b/examples/grab.js @@ -0,0 +1,166 @@ +var isGrabbing = false; +var grabbedEntity = null; +var prevMouse = {}; +var deltaMouse = { + z: 0 +} +var entityProps; +var targetPosition; +var moveUpDown = false; + +var currentPosition, currentVelocity; + +var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav"); +var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav"); + +var DROP_DISTANCE = 5.0; +var DROP_COLOR = { + red: 200, + green: 200, + blue: 200 +}; +var DROP_WIDTH = 2; + + +var dropLine = Overlays.addOverlay("line3d", { + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: DROP_COLOR, + alpha: 1, + visible: false, + lineWidth: DROP_WIDTH +}); + + +function mousePressEvent(event) { + if (!event.isLeftButton) { + return; + } + var pickRay = Camera.computePickRay(event.x, event.y); + var intersection = Entities.findRayIntersection(pickRay); + if (intersection.intersects && intersection.properties.collisionsWillMove) { + grabbedEntity = intersection.entityID; + var props = Entities.getEntityProperties(grabbedEntity) + isGrabbing = true; + targetPosition = props.position; + currentPosition = props.position; + currentVelocity = props.velocity; + updateDropLine(targetPosition); + Audio.playSound(grabSound, { + position: props.position, + volume: 0.4 + }); + } +} + +function updateDropLine(position) { + Overlays.editOverlay(dropLine, { + visible: true, + start: position, + end: Vec3.sum(position, { + x: 0, + y: -DROP_DISTANCE, + z: 0 + }) + }) +} + + +function mouseReleaseEvent() { + if (isGrabbing) { + isGrabbing = false; + Overlays.editOverlay(dropLine, { + visible: false + }); + targetPosition = null; + Audio.playSound(grabSound, { + position: entityProps.position, + volume: 0.25 + }); + + } +} + +function mouseMoveEvent(event) { + if (isGrabbing) { + deltaMouse.x = event.x - prevMouse.x; + if (!moveUpDown) { + deltaMouse.z = event.y - prevMouse.y; + deltaMouse.y = 0; + } else { + deltaMouse.y = (event.y - prevMouse.y) * -1; + deltaMouse.z = 0; + } + // Update the target position by the amount the mouse moved + var camYaw = Quat.safeEulerAngles(Camera.getOrientation()).y; + var dPosition = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, camYaw, 0), deltaMouse); + // Adjust target position for the object by the mouse move + var avatarEntityDistance = Vec3.distance(Camera.getPosition(), currentPosition); + // Scale distance we want to move by the distance from the camera to the grabbed object + // TODO: Correct SCREEN_TO_METERS to be correct for the actual FOV, resolution + var SCREEN_TO_METERS = 0.001; + targetPosition = Vec3.sum(targetPosition, Vec3.multiply(dPosition, avatarEntityDistance * SCREEN_TO_METERS)); + } + prevMouse.x = event.x; + prevMouse.y = event.y; + +} + + +function keyReleaseEvent(event) { + if (event.text === "SHIFT") { + moveUpDown = false; + } +} + +function keyPressEvent(event) { + if (event.text === "SHIFT") { + moveUpDown = true; + } +} + +function update(deltaTime) { + if (isGrabbing) { + + entityProps = Entities.getEntityProperties(grabbedEntity); + currentPosition = entityProps.position; + currentVelocity = entityProps.velocity; + + var dPosition = Vec3.subtract(targetPosition, currentPosition); + var CLOSE_ENOUGH = 0.001; + if (Vec3.length(dPosition) > CLOSE_ENOUGH) { + // compute current velocity in the direction we want to move + var velocityTowardTarget = Vec3.dot(currentVelocity, Vec3.normalize(dPosition)); + // compute the speed we would like to be going toward the target position + var SPRING_RATE = 0.35; + var DAMPING_RATE = 0.55; + var desiredVelocity = Vec3.multiply(dPosition, (1.0 / deltaTime) * SPRING_RATE); + // compute how much we want to add to the existing velocity + var addedVelocity = Vec3.subtract(desiredVelocity, velocityTowardTarget); + var newVelocity = Vec3.sum(currentVelocity, addedVelocity); + // Add Damping + newVelocity = Vec3.subtract(newVelocity, Vec3.multiply(newVelocity, DAMPING_RATE)); + // Update entity + Entities.editEntity(grabbedEntity, { + velocity: newVelocity + }) + } + updateDropLine(currentPosition); + } +} + +Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); +Controller.keyPressEvent.connect(keyPressEvent); +Controller.keyReleaseEvent.connect(keyReleaseEvent); +Script.update.connect(update); + diff --git a/examples/html/entityList.html b/examples/html/entityList.html index bcc1c117ea..5795cc2acb 100644 --- a/examples/html/entityList.html +++ b/examples/html/entityList.html @@ -13,7 +13,7 @@ var DESC_STRING = ' ▴'; function loaded() { - entityList = new List('entity-list', { valueNames: ['type', 'url']}); + entityList = new List('entity-list', { valueNames: ['name', 'type', 'url']}); entityList.clear(); elEntityTable = document.getElementById("entity-table"); elEntityTableBody = document.getElementById("entity-table-body"); @@ -22,6 +22,9 @@ elTeleport = document.getElementById("teleport"); elNoEntitiesMessage = document.getElementById("no-entities"); + document.getElementById("entity-name").onclick = function() { + setSortColumn('name'); + }; document.getElementById("entity-type").onclick = function() { setSortColumn('type'); }; @@ -56,31 +59,34 @@ })); } - function addEntity(id, type, url) { + function addEntity(id, name, type, url) { if (entities[id] === undefined) { var urlParts = url.split('/'); var filename = urlParts[urlParts.length - 1]; - entityList.add([{ id: id, type: type, url: filename }], function(items) { + entityList.add([{ id: id, name: name, type: type, url: filename }], function(items) { var el = items[0].elm; var id = items[0]._values.id; entities[id] = { id: id, - name: id, + name: name, el: el, + item: items[0], }; el.setAttribute('id', 'entity_' + id); el.setAttribute('title', url); el.dataset.entityId = id; el.onclick = onRowClicked; el.ondblclick = onRowDoubleClicked; - el.innerHTML }); if (refreshEntityListTimer) { clearTimeout(refreshEntityListTimer); } refreshEntityListTimer = setTimeout(refreshEntityListObject, 50); + } else { + var item = entities[id].item; + item.values({ name: name, url: url }); } } @@ -90,6 +96,7 @@ } var elSortOrder = { + name: document.querySelector('#entity-name .sort-order'), type: document.querySelector('#entity-type .sort-order'), url: document.querySelector('#entity-url .sort-order'), } @@ -164,7 +171,7 @@ elNoEntitiesMessage.style.display = "none"; for (var i = 0; i < newEntities.length; i++) { var id = newEntities[i].id; - addEntity(id, newEntities[i].type, newEntities[i].url); + addEntity(id, newEntities[i].name, newEntities[i].type, newEntities[i].url); } updateSelectedEntities(data.selectedIDs); } @@ -190,6 +197,7 @@ Type  ▾ + Name  ▾ URL @@ -197,6 +205,7 @@ Type Type + Name
URL
diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index c01b2a6d10..3e775ec698 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -98,6 +98,7 @@ var allSections = []; var elID = document.getElementById("property-id"); var elType = document.getElementById("property-type"); + var elName = document.getElementById("property-name"); var elLocked = document.getElementById("property-locked"); var elVisible = document.getElementById("property-visible"); var elPositionX = document.getElementById("property-pos-x"); @@ -146,11 +147,10 @@ var elScriptURL = document.getElementById("property-script-url"); var elUserData = document.getElementById("property-user-data"); - var elBoxSections = document.querySelectorAll(".box-section"); - allSections.push(elBoxSections); - var elBoxColorRed = document.getElementById("property-box-red"); - var elBoxColorGreen = document.getElementById("property-box-green"); - var elBoxColorBlue = document.getElementById("property-box-blue"); + var elColorSection = document.getElementById("color-section"); + var elColorRed = document.getElementById("property-color-red"); + var elColorGreen = document.getElementById("property-color-green"); + var elColorBlue = document.getElementById("property-color-blue"); var elLightSections = document.querySelectorAll(".light-section"); allSections.push(elLightSections); @@ -262,6 +262,8 @@ enableChildren(document.getElementById("properties-list"), 'input'); } + elName.value = properties.name; + elVisible.checked = properties.visible; elPositionX.value = properties.position.x.toFixed(2); @@ -310,16 +312,17 @@ allSections[i][j].style.display = 'none'; } } - - if (properties.type == "Box") { - for (var i = 0; i < elBoxSections.length; i++) { - elBoxSections[i].style.display = 'block'; - } - elBoxColorRed.value = properties.color.red; - elBoxColorGreen.value = properties.color.green; - elBoxColorBlue.value = properties.color.blue; - } else if (properties.type == "Model") { + if (properties.type == "Box" || properties.type == "Sphere") { + elColorSection.style.display = 'block'; + elColorRed.value = properties.color.red; + elColorGreen.value = properties.color.green; + elColorBlue.value = properties.color.blue; + } else { + elColorSection.style.display = 'none'; + } + + if (properties.type == "Model") { for (var i = 0; i < elModelSections.length; i++) { elModelSections[i].style.display = 'block'; } @@ -395,6 +398,7 @@ } elLocked.addEventListener('change', createEmitCheckedPropertyUpdateFunction('locked')); + elName.addEventListener('change', createEmitTextPropertyUpdateFunction('name')); elVisible.addEventListener('change', createEmitCheckedPropertyUpdateFunction('visible')); var positionChangeFunction = createEmitVec3PropertyUpdateFunction( @@ -454,11 +458,11 @@ elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script')); elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData')); - var boxColorChangeFunction = createEmitColorPropertyUpdateFunction( - 'color', elBoxColorRed, elBoxColorGreen, elBoxColorBlue); - elBoxColorRed.addEventListener('change', boxColorChangeFunction); - elBoxColorGreen.addEventListener('change', boxColorChangeFunction); - elBoxColorBlue.addEventListener('change', boxColorChangeFunction); + var colorChangeFunction = createEmitColorPropertyUpdateFunction( + 'color', elColorRed, elColorGreen, elColorBlue); + elColorRed.addEventListener('change', colorChangeFunction); + elColorGreen.addEventListener('change', colorChangeFunction); + elColorBlue.addEventListener('change', colorChangeFunction); elLightSpotLight.addEventListener('change', createEmitCheckedPropertyUpdateFunction('isSpotlight')); @@ -590,6 +594,12 @@ +
+ Name +
+ +
+
Locked @@ -743,12 +753,12 @@
-
+
Color
-
R
-
G
-
B
+
R
+
G
+
B
diff --git a/examples/html/style.css b/examples/html/style.css index 6a258d8f02..8be9b92a51 100644 --- a/examples/html/style.css +++ b/examples/html/style.css @@ -301,3 +301,7 @@ input[type="number"]::-webkit-inner-spin-button:hover, input[type="number"]::-webkit-inner-spin-button:active{ opacity: .8; } + +input#property-name { + width: 100%; +} diff --git a/examples/libraries/entityList.js b/examples/libraries/entityList.js index d0b8ddac7f..e10aed4051 100644 --- a/examples/libraries/entityList.js +++ b/examples/libraries/entityList.js @@ -31,7 +31,7 @@ EntityListTool = function(opts) { webView.eventBridge.emitScriptEvent(JSON.stringify(data)); }); - function sendUpdate() { + that.sendUpdate = function() { var entities = []; var ids = Entities.findEntities(MyAvatar.position, 100); for (var i = 0; i < ids.length; i++) { @@ -39,6 +39,7 @@ EntityListTool = function(opts) { var properties = Entities.getEntityProperties(id); entities.push({ id: id.id, + name: properties.name, type: properties.type, url: properties.type == "Model" ? properties.modelURL : "", }); @@ -76,7 +77,7 @@ EntityListTool = function(opts) { Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); } } else if (data.type == "refresh") { - sendUpdate(); + that.sendUpdate(); } else if (data.type == "teleport") { if (selectionManager.hasSelection()) { MyAvatar.position = selectionManager.worldPosition; diff --git a/examples/lotsoBlocks.js b/examples/lotsoBlocks.js new file mode 100644 index 0000000000..63ed774d2d --- /dev/null +++ b/examples/lotsoBlocks.js @@ -0,0 +1,63 @@ +var NUM_BLOCKS = 200; +var size; +var SPAWN_RANGE = 10; +var boxes = []; +var basePosition, avatarRot; +var isAssignmentScript = false; +if(isAssignmentScript){ + basePosition = {x: 8000, y: 8000, z: 8000}; +} +else { + avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); + basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(SPAWN_RANGE * 3, Quat.getFront(avatarRot))); +} +basePosition.y -= SPAWN_RANGE; + +var ground = Entities.addEntity({ + type: "Model", + modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/woodFloor.fbx", + dimensions: { + x: 100, + y: 2, + z: 100 + }, + position: basePosition, + shapeType: 'box' +}); + + +basePosition.y += SPAWN_RANGE + 2; +for (var i = 0; i < NUM_BLOCKS; i++) { + size = randFloat(-.2, 0.7); + boxes.push(Entities.addEntity({ + type: 'Box', + dimensions: { + x: size, + y: size, + z: size + }, + position: { + x: basePosition.x + randFloat(-SPAWN_RANGE, SPAWN_RANGE), + y: basePosition.y - randFloat(-SPAWN_RANGE, SPAWN_RANGE), + z: basePosition.z + randFloat(-SPAWN_RANGE, SPAWN_RANGE) + }, + color: {red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255}, + collisionsWillMove: true, + gravity: {x: 0, y: 0, z: 0} + })); +} + + + +function cleanup() { + Entities.deleteEntity(ground); + boxes.forEach(function(box){ + Entities.deleteEntity(box); + }); +} + +Script.scriptEnding.connect(cleanup); + +function randFloat(low, high) { + return low + Math.random() * ( high - low ); +} \ No newline at end of file diff --git a/interface/resources/qml/controls/Dialog.qml b/interface/resources/qml/controls/Dialog.qml index 46add1dc07..f32b4e2e66 100644 --- a/interface/resources/qml/controls/Dialog.qml +++ b/interface/resources/qml/controls/Dialog.qml @@ -66,7 +66,7 @@ DialogBase { // our close function performs the same way as the OffscreenUI class: // don't do anything but manipulate the enabled flag and let the other - // mechanisms decide if the window should be destoryed after the close + // mechanisms decide if the window should be destroyed after the close // animation completes function close() { if (destroyOnCloseButton) { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3f37067c75..d0d01f4534 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1153,7 +1153,7 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_A: if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::Atmosphere); - } else { + } else if (!isMeta) { _myAvatar->setDriveKeys(ROT_LEFT, 1.0f); } break; diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index c607d9fa0e..110fa33c64 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -66,16 +66,16 @@ static const int DDE_TO_FACESHIFT_MAPPING[] = { // The DDE coefficients, overall, range from -0.2 to 1.5 or so. However, individual coefficients typically vary much // less than this. static const float DDE_COEFFICIENT_SCALES[] = { - 4.0f, // EyeBlink_L - 4.0f, // EyeBlink_R + 1.0f, // EyeBlink_L + 1.0f, // EyeBlink_R 1.0f, // EyeSquint_L 1.0f, // EyeSquint_R 1.0f, // EyeDown_L 1.0f, // EyeDown_R 1.0f, // EyeIn_L 1.0f, // EyeIn_R - 4.0f, // EyeOpen_L - 4.0f, // EyeOpen_R + 1.0f, // EyeOpen_L + 1.0f, // EyeOpen_R 1.0f, // EyeOut_L 1.0f, // EyeOut_R 1.0f, // EyeUp_L @@ -137,6 +137,16 @@ struct Packet { const float STARTING_DDE_MESSAGE_TIME = 0.033f; +const int FPS_TIMER_DELAY = 2000; // ms +const int FPS_TIMER_DURATION = 2000; // ms + +#ifdef WIN32 +// warning C4351: new behavior: elements of array 'DdeFaceTracker::_lastEyeBlinks' will be default initialized +// warning C4351: new behavior: elements of array 'DdeFaceTracker::_filteredEyeBlinks' will be default initialized +// warning C4351: new behavior: elements of array 'DdeFaceTracker::_lastEyeCoefficients' will be default initialized +#pragma warning(disable:4351) +#endif + DdeFaceTracker::DdeFaceTracker() : DdeFaceTracker(QHostAddress::Any, DDE_SERVER_PORT, DDE_CONTROL_PORT) { @@ -167,24 +177,35 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, qui _averageMessageTime(STARTING_DDE_MESSAGE_TIME), _lastHeadTranslation(glm::vec3(0.0f)), _filteredHeadTranslation(glm::vec3(0.0f)), - _lastLeftEyeBlink(0.0f), - _filteredLeftEyeBlink(0.0f), - _lastRightEyeBlink(0.0f), - _filteredRightEyeBlink(0.0f) + _lastBrowUp(0.0f), + _filteredBrowUp(0.0f), + _lastEyeBlinks(), + _filteredEyeBlinks(), + _lastEyeCoefficients(), + _isCalculatingFPS(false), + _frameCount(0) { _coefficients.resize(NUM_FACESHIFT_BLENDSHAPES); _blendshapeCoefficients.resize(NUM_FACESHIFT_BLENDSHAPES); - + + _eyeStates[0] = EYE_OPEN; + _eyeStates[1] = EYE_OPEN; + connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams())); connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketErrorOccurred(QAbstractSocket::SocketError))); - connect(&_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SLOT(socketStateChanged(QAbstractSocket::SocketState))); + connect(&_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + SLOT(socketStateChanged(QAbstractSocket::SocketState))); } DdeFaceTracker::~DdeFaceTracker() { setEnabled(false); } +#ifdef WIN32 +#pragma warning(default:4351) +#endif + void DdeFaceTracker::setEnabled(bool enabled) { #ifdef HAVE_DDE // isOpen() does not work as one might expect on QUdpSocket; don't test isOpen() before closing socket. @@ -351,49 +372,25 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { } // Translate DDE coefficients to Faceshift compatible coefficients - for (int i = 0; i < NUM_EXPRESSIONS; i += 1) { + for (int i = 0; i < NUM_EXPRESSIONS; i++) { _coefficients[DDE_TO_FACESHIFT_MAPPING[i]] = packet.expressions[i]; } - // Use EyeBlink values to control both EyeBlink and EyeOpen - static const float RELAXED_EYE_VALUE = 0.1f; - float leftEye = _coefficients[_leftBlinkIndex]; - float rightEye = _coefficients[_rightBlinkIndex]; - if (isFiltering) { - const float BLINK_VELOCITY_FILTER_STRENGTH = 0.3f; - - float velocity = fabs(leftEye - _lastLeftEyeBlink) / _averageMessageTime; - float velocityFilter = glm::clamp(velocity * BLINK_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); - _filteredLeftEyeBlink = velocityFilter * leftEye + (1.0f - velocityFilter) * _filteredLeftEyeBlink; - _lastLeftEyeBlink = leftEye; - leftEye = _filteredLeftEyeBlink; - - velocity = fabs(rightEye - _lastRightEyeBlink) / _averageMessageTime; - velocityFilter = glm::clamp(velocity * BLINK_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); - _filteredRightEyeBlink = velocityFilter * rightEye + (1.0f - velocityFilter) * _filteredRightEyeBlink; - _lastRightEyeBlink = rightEye; - rightEye = _filteredRightEyeBlink; - } - if (leftEye > RELAXED_EYE_VALUE) { - _coefficients[_leftBlinkIndex] = leftEye - RELAXED_EYE_VALUE; - _coefficients[_leftEyeOpenIndex] = 0.0f; - } else { - _coefficients[_leftBlinkIndex] = 0.0f; - _coefficients[_leftEyeOpenIndex] = RELAXED_EYE_VALUE - leftEye; - } - if (rightEye > RELAXED_EYE_VALUE) { - _coefficients[_rightBlinkIndex] = rightEye - RELAXED_EYE_VALUE; - _coefficients[_rightEyeOpenIndex] = 0.0f; - } else { - _coefficients[_rightBlinkIndex] = 0.0f; - _coefficients[_rightEyeOpenIndex] = RELAXED_EYE_VALUE - rightEye; - } - // Use BrowsU_C to control both brows' up and down - _coefficients[_browDownLeftIndex] = -_coefficients[_browUpCenterIndex]; - _coefficients[_browDownRightIndex] = -_coefficients[_browUpCenterIndex]; - _coefficients[_browUpLeftIndex] = _coefficients[_browUpCenterIndex]; - _coefficients[_browUpRightIndex] = _coefficients[_browUpCenterIndex]; + float browUp = _coefficients[_browUpCenterIndex]; + if (isFiltering) { + const float BROW_VELOCITY_FILTER_STRENGTH = 0.75f; + float velocity = fabs(browUp - _lastBrowUp) / _averageMessageTime; + float velocityFilter = glm::clamp(velocity * BROW_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); + _filteredBrowUp = velocityFilter * browUp + (1.0f - velocityFilter) * _filteredBrowUp; + _lastBrowUp = browUp; + browUp = _filteredBrowUp; + _coefficients[_browUpCenterIndex] = browUp; + } + _coefficients[_browUpLeftIndex] = browUp; + _coefficients[_browUpRightIndex] = browUp; + _coefficients[_browDownLeftIndex] = -browUp; + _coefficients[_browDownRightIndex] = -browUp; // Offset jaw open coefficient static const float JAW_OPEN_THRESHOLD = 0.16f; @@ -404,9 +401,91 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { _coefficients[_mouthSmileLeftIndex] = _coefficients[_mouthSmileLeftIndex] - SMILE_THRESHOLD; _coefficients[_mouthSmileRightIndex] = _coefficients[_mouthSmileRightIndex] - SMILE_THRESHOLD; + // Velocity filter EyeBlink values + const float DDE_EYEBLINK_SCALE = 3.0f; + float eyeBlinks[] = { DDE_EYEBLINK_SCALE * _coefficients[_leftBlinkIndex], DDE_EYEBLINK_SCALE * _coefficients[_rightBlinkIndex] }; + if (isFiltering) { + const float BLINK_VELOCITY_FILTER_STRENGTH = 0.3f; + for (int i = 0; i < 2; i++) { + float velocity = fabs(eyeBlinks[i] - _lastEyeBlinks[i]) / _averageMessageTime; + float velocityFilter = glm::clamp(velocity * BLINK_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); + _filteredEyeBlinks[i] = velocityFilter * eyeBlinks[i] + (1.0f - velocityFilter) * _filteredEyeBlinks[i]; + _lastEyeBlinks[i] = eyeBlinks[i]; + } + } + + // Finesse EyeBlink values + float eyeCoefficients[2]; + for (int i = 0; i < 2; i++) { + // Scale EyeBlink values so that they can be used to control both EyeBlink and EyeOpen + // -ve values control EyeOpen; +ve values control EyeBlink + static const float EYE_CONTROL_THRESHOLD = 0.5f; // Resting eye value + eyeCoefficients[i] = (_filteredEyeBlinks[i] - EYE_CONTROL_THRESHOLD) / (1.0f - EYE_CONTROL_THRESHOLD); + + // Change to closing or opening states + const float EYE_CONTROL_HYSTERISIS = 0.25f; + const float EYE_CLOSING_THRESHOLD = 0.95f; + const float EYE_OPENING_THRESHOLD = EYE_CONTROL_THRESHOLD - EYE_CONTROL_HYSTERISIS; + if ((_eyeStates[i] == EYE_OPEN || _eyeStates[i] == EYE_OPENING) && eyeCoefficients[i] > EYE_CLOSING_THRESHOLD) { + _eyeStates[i] = EYE_CLOSING; + } else if ((_eyeStates[i] == EYE_CLOSED || _eyeStates[i] == EYE_CLOSING) + && eyeCoefficients[i] < EYE_OPENING_THRESHOLD) { + _eyeStates[i] = EYE_OPENING; + } + + const float EYELID_MOVEMENT_RATE = 10.0f; // units/second + const float EYE_OPEN_SCALE = 0.2f; + if (_eyeStates[i] == EYE_CLOSING) { + // Close eyelid until it's fully closed + float closingValue = _lastEyeCoefficients[i] + EYELID_MOVEMENT_RATE * _averageMessageTime; + if (closingValue >= 1.0) { + _eyeStates[i] = EYE_CLOSED; + eyeCoefficients[i] = 1.0; + } else { + eyeCoefficients[i] = closingValue; + } + } else if (_eyeStates[i] == EYE_OPENING) { + // Open eyelid until it meets the current adjusted value + float openingValue = _lastEyeCoefficients[i] - EYELID_MOVEMENT_RATE * _averageMessageTime; + if (openingValue < eyeCoefficients[i] * EYE_OPEN_SCALE) { + _eyeStates[i] = EYE_OPEN; + eyeCoefficients[i] = eyeCoefficients[i] * EYE_OPEN_SCALE; + } else { + eyeCoefficients[i] = openingValue; + } + } else if (_eyeStates[i] == EYE_OPEN) { + // Reduce eyelid movement + eyeCoefficients[i] = eyeCoefficients[i] * EYE_OPEN_SCALE; + } else if (_eyeStates[i] == EYE_CLOSED) { + // Keep eyelid fully closed + eyeCoefficients[i] = 1.0; + } + } + if (_eyeStates[0] == EYE_OPEN && _eyeStates[1] == EYE_OPEN) { + // Couple eyelids + eyeCoefficients[0] = eyeCoefficients[1] = (eyeCoefficients[0] + eyeCoefficients[0]) / 2.0f; + } + _lastEyeCoefficients[0] = eyeCoefficients[0]; + _lastEyeCoefficients[1] = eyeCoefficients[1]; + + // Use EyeBlink values to control both EyeBlink and EyeOpen + if (eyeCoefficients[0] > 0) { + _coefficients[_leftBlinkIndex] = eyeCoefficients[0]; + _coefficients[_leftEyeOpenIndex] = 0.0f; + } else { + _coefficients[_leftBlinkIndex] = 0.0f; + _coefficients[_leftEyeOpenIndex] = -eyeCoefficients[0]; + } + if (eyeCoefficients[1] > 0) { + _coefficients[_rightBlinkIndex] = eyeCoefficients[1]; + _coefficients[_rightEyeOpenIndex] = 0.0f; + } else { + _coefficients[_rightBlinkIndex] = 0.0f; + _coefficients[_rightEyeOpenIndex] = -eyeCoefficients[1]; + } // Scale all coefficients - for (int i = 0; i < NUM_EXPRESSIONS; i += 1) { + for (int i = 0; i < NUM_EXPRESSIONS; i++) { _blendshapeCoefficients[i] = glm::clamp(DDE_COEFFICIENT_SCALES[i] * _coefficients[i], 0.0f, 1.0f); } diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h index d9df5723cf..9fb0e943f2 100644 --- a/interface/src/devices/DdeFaceTracker.h +++ b/interface/src/devices/DdeFaceTracker.h @@ -103,12 +103,26 @@ private: quint64 _lastMessageReceived; float _averageMessageTime; + glm::vec3 _lastHeadTranslation; glm::vec3 _filteredHeadTranslation; - float _lastLeftEyeBlink; - float _filteredLeftEyeBlink; - float _lastRightEyeBlink; - float _filteredRightEyeBlink; + + float _lastBrowUp; + float _filteredBrowUp; + + enum EyeState { + EYE_OPEN, + EYE_CLOSING, + EYE_CLOSED, + EYE_OPENING + }; + EyeState _eyeStates[2]; + float _lastEyeBlinks[2]; + float _filteredEyeBlinks[2]; + float _lastEyeCoefficients[2]; + + bool _isCalculatingFPS; + int _frameCount; }; -#endif // hifi_DdeFaceTracker_h \ No newline at end of file +#endif // hifi_DdeFaceTracker_h diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp index f7828806ab..2f40b6ce6b 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp @@ -35,6 +35,14 @@ void RenderableBoxEntityItem::render(RenderArgs* args) { glm::vec4 cubeColor(getColor()[RED_INDEX] / MAX_COLOR, getColor()[GREEN_INDEX] / MAX_COLOR, getColor()[BLUE_INDEX] / MAX_COLOR, getLocalRenderAlpha()); + + bool highlightSimulationOwnership = false; + if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) { + auto nodeList = DependencyManager::get(); + const QUuid& myNodeID = nodeList->getSessionUUID(); + highlightSimulationOwnership = (getSimulatorID() == myNodeID); + } + glPushMatrix(); glTranslatef(position.x, position.y, position.z); glm::vec3 axis = glm::axis(rotation); @@ -43,8 +51,11 @@ void RenderableBoxEntityItem::render(RenderArgs* args) { glm::vec3 positionToCenter = center - position; glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); glScalef(dimensions.x, dimensions.y, dimensions.z); - DependencyManager::get()->renderSolidCube(1.0f, cubeColor); + if (highlightSimulationOwnership) { + DependencyManager::get()->renderWireCube(1.0f, cubeColor); + } else { + DependencyManager::get()->renderSolidCube(1.0f, cubeColor); + } glPopMatrix(); glPopMatrix(); - }; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index b9bf1d39a8..cedb5b9924 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -109,6 +109,33 @@ void RenderableModelEntityItem::remapTextures() { } +void RenderableModelEntityItem::renderBoundingBox(RenderArgs* args) { + glm::vec3 position = getPosition(); + glm::vec3 center = getCenter(); + glm::vec3 dimensions = getDimensions(); + glm::quat rotation = getRotation(); + // float size = glm::length(dimensions) / 2.0f; + + const float MAX_COLOR = 255.0f; + glm::vec4 cubeColor(getColor()[RED_INDEX] / MAX_COLOR, + getColor()[GREEN_INDEX] / MAX_COLOR, + getColor()[BLUE_INDEX] / MAX_COLOR, + getLocalRenderAlpha()); + + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glPushMatrix(); + glm::vec3 positionToCenter = center - position; + glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); + glScalef(dimensions.x, dimensions.y, dimensions.z); + DependencyManager::get()->renderWireCube(1.0f, cubeColor); + glPopMatrix(); + glPopMatrix(); +} + + void RenderableModelEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RMEIrender"); assert(getType() == EntityTypes::Model); @@ -117,7 +144,6 @@ void RenderableModelEntityItem::render(RenderArgs* args) { glm::vec3 position = getPosition(); glm::vec3 dimensions = getDimensions(); - float size = glm::length(dimensions); bool highlightSimulationOwnership = false; if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) { @@ -126,6 +152,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { highlightSimulationOwnership = (getSimulatorID() == myNodeID); } + bool didDraw = false; if (drawAsModel && !highlightSimulationOwnership) { remapTextures(); glPushMatrix(); @@ -179,34 +206,20 @@ void RenderableModelEntityItem::render(RenderArgs* args) { if (args && (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE)) { if (movingOrAnimating) { _model->renderInScene(alpha, args); + didDraw = true; } } else { _model->renderInScene(alpha, args); + didDraw = true; } - } else { - // if we couldn't get a model, then just draw a cube - glm::vec4 color(getColor()[RED_INDEX]/255, getColor()[GREEN_INDEX]/255, getColor()[BLUE_INDEX]/255, 1.0f); - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - DependencyManager::get()->renderWireCube(size, color); - glPopMatrix(); } - } else { - // if we couldn't get a model, then just draw a cube - glm::vec4 color(getColor()[RED_INDEX]/255, getColor()[GREEN_INDEX]/255, getColor()[BLUE_INDEX]/255, 1.0f); - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - DependencyManager::get()->renderWireCube(size, color); - glPopMatrix(); } } glPopMatrix(); - } else { - glm::vec4 color(getColor()[RED_INDEX]/255, getColor()[GREEN_INDEX]/255, getColor()[BLUE_INDEX]/255, 1.0f); - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - DependencyManager::get()->renderWireCube(size, color); - glPopMatrix(); + } + + if (!didDraw) { + renderBoundingBox(args); } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 43f18af0db..ec0e30985c 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -42,6 +42,7 @@ public: virtual void somethingChangedNotification() { _needsInitialSimulation = true; } + void renderBoundingBox(RenderArgs* args); virtual void render(RenderArgs* args); virtual bool supportsDetailedRayIntersection() const { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 1d358f175a..7c0598fbfa 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -63,6 +63,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) : _simulatorID(ENTITY_ITEM_DEFAULT_SIMULATOR_ID), _simulatorIDChangedTime(0), _marketplaceID(ENTITY_ITEM_DEFAULT_MARKETPLACE_ID), + _name(ENTITY_ITEM_DEFAULT_NAME), _physicsInfo(NULL), _dirtyFlags(0), _element(NULL) @@ -105,6 +106,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_LOCKED; requestedProperties += PROP_USER_DATA; requestedProperties += PROP_MARKETPLACE_ID; + requestedProperties += PROP_NAME; requestedProperties += PROP_SIMULATOR_ID; return requestedProperties; @@ -231,6 +233,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_USER_DATA, appendValue, getUserData()); APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_ID, appendValue, getSimulatorID()); APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, appendValue, getMarketplaceID()); + APPEND_ENTITY_PROPERTY(PROP_NAME, appendValue, getName()); appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -528,7 +531,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY_SETTER(PROP_DIMENSIONS, glm::vec3, updateDimensionsInDomainUnits); } } - + READ_ENTITY_PROPERTY_QUAT_SETTER(PROP_ROTATION, updateRotation); READ_ENTITY_PROPERTY_SETTER(PROP_DENSITY, float, updateDensity); if (useMeters) { @@ -566,6 +569,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY_STRING(PROP_MARKETPLACE_ID, setMarketplaceID); } + READ_ENTITY_PROPERTY_STRING(PROP_NAME, setName); + bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData); //////////////////////////////////// @@ -897,6 +902,7 @@ EntityItemProperties EntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData); COPY_ENTITY_PROPERTY_TO_PROPERTIES(simulatorID, getSimulatorID); COPY_ENTITY_PROPERTY_TO_PROPERTIES(marketplaceID, getMarketplaceID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName); properties._defaultSettings = false; @@ -928,6 +934,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData); SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulatorID, setSimulatorID); SET_ENTITY_PROPERTY_FROM_PROPERTIES(marketplaceID, setMarketplaceID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName); if (somethingChanged) { somethingChangedNotification(); // notify derived classes that something has changed diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index e71f88d723..ef1612ab06 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -229,6 +229,9 @@ public: float getAngularDamping() const { return _angularDamping; } void setAngularDamping(float value) { _angularDamping = value; } + QString getName() const { return _name; } + void setName(const QString& value) { _name = value; } + bool getVisible() const { return _visible; } void setVisible(bool value) { _visible = value; } bool isVisible() const { return _visible; } @@ -348,6 +351,7 @@ protected: QUuid _simulatorID; // id of Node which is currently responsible for simulating this Entity quint64 _simulatorIDChangedTime; // when was _simulatorID last updated? QString _marketplaceID; + QString _name; // NOTE: Damping is applied like this: v *= pow(1 - damping, dt) // diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 734f7897bb..0a416aeaf5 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -86,6 +86,7 @@ EntityItemProperties::EntityItemProperties() : CONSTRUCT_PROPERTY(stageAltitude, ZoneEntityItem::DEFAULT_STAGE_ALTITUDE), CONSTRUCT_PROPERTY(stageDay, ZoneEntityItem::DEFAULT_STAGE_DAY), CONSTRUCT_PROPERTY(stageHour, ZoneEntityItem::DEFAULT_STAGE_HOUR), + CONSTRUCT_PROPERTY(name, ENTITY_ITEM_DEFAULT_NAME), _id(UNKNOWN_ENTITY_ID), _idSet(false), @@ -281,6 +282,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_LOCAL_GRAVITY, localGravity); CHECK_PROPERTY_CHANGE(PROP_PARTICLE_RADIUS, particleRadius); CHECK_PROPERTY_CHANGE(PROP_MARKETPLACE_ID, marketplaceID); + CHECK_PROPERTY_CHANGE(PROP_NAME, name); CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_COLOR, keyLightColor); CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_INTENSITY, keyLightIntensity); CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_AMBIENT_INTENSITY, keyLightAmbientIntensity); @@ -295,20 +297,23 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { return changedProperties; } -QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) const { +QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool skipDefaults) const { QScriptValue properties = engine->newObject(); + EntityItemProperties defaultEntityProperties; if (_idSet) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(id, _id.toString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(isKnownID, (_id != UNKNOWN_ENTITY_ID)); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(isKnownID, (_id != UNKNOWN_ENTITY_ID)); } else { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(isKnownID, false); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(isKnownID, false); } COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(type, EntityTypes::getEntityTypeName(_type)); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(position); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(dimensions); - COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(naturalDimensions); // gettable, but not settable + if (!skipDefaults) { + COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(naturalDimensions); // gettable, but not settable + } COPY_PROPERTY_TO_QSCRIPTVALUE_QUAT(rotation); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(velocity); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(gravity); @@ -316,8 +321,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons COPY_PROPERTY_TO_QSCRIPTVALUE(damping); COPY_PROPERTY_TO_QSCRIPTVALUE(density); COPY_PROPERTY_TO_QSCRIPTVALUE(lifetime); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(age, getAge()); // gettable, but not settable - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable + if (!skipDefaults) { + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(age, getAge()); // gettable, but not settable + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable + } COPY_PROPERTY_TO_QSCRIPTVALUE(script); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(registrationPoint); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(angularVelocity); @@ -356,6 +363,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons COPY_PROPERTY_TO_QSCRIPTVALUE(localGravity); COPY_PROPERTY_TO_QSCRIPTVALUE(particleRadius); COPY_PROPERTY_TO_QSCRIPTVALUE(marketplaceID); + COPY_PROPERTY_TO_QSCRIPTVALUE(name); COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR(keyLightColor); COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightIntensity); @@ -369,31 +377,37 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons COPY_PROPERTY_TO_QSCRIPTVALUE(stageHour); // Sitting properties support - QScriptValue sittingPoints = engine->newObject(); - for (int i = 0; i < _sittingPoints.size(); ++i) { - QScriptValue sittingPoint = engine->newObject(); - sittingPoint.setProperty("name", _sittingPoints.at(i).name); - sittingPoint.setProperty("position", vec3toScriptValue(engine, _sittingPoints.at(i).position)); - sittingPoint.setProperty("rotation", quatToScriptValue(engine, _sittingPoints.at(i).rotation)); - sittingPoints.setProperty(i, sittingPoint); + if (!skipDefaults) { + QScriptValue sittingPoints = engine->newObject(); + for (int i = 0; i < _sittingPoints.size(); ++i) { + QScriptValue sittingPoint = engine->newObject(); + sittingPoint.setProperty("name", _sittingPoints.at(i).name); + sittingPoint.setProperty("position", vec3toScriptValue(engine, _sittingPoints.at(i).position)); + sittingPoint.setProperty("rotation", quatToScriptValue(engine, _sittingPoints.at(i).rotation)); + sittingPoints.setProperty(i, sittingPoint); + } + sittingPoints.setProperty("length", _sittingPoints.size()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(sittingPoints, sittingPoints); // gettable, but not settable } - sittingPoints.setProperty("length", _sittingPoints.size()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(sittingPoints, sittingPoints); // gettable, but not settable - AABox aaBox = getAABox(); - QScriptValue boundingBox = engine->newObject(); - QScriptValue bottomRightNear = vec3toScriptValue(engine, aaBox.getCorner()); - QScriptValue topFarLeft = vec3toScriptValue(engine, aaBox.calcTopFarLeft()); - QScriptValue center = vec3toScriptValue(engine, aaBox.calcCenter()); - QScriptValue boundingBoxDimensions = vec3toScriptValue(engine, aaBox.getDimensions()); - boundingBox.setProperty("brn", bottomRightNear); - boundingBox.setProperty("tfl", topFarLeft); - boundingBox.setProperty("center", center); - boundingBox.setProperty("dimensions", boundingBoxDimensions); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(boundingBox, boundingBox); // gettable, but not settable + if (!skipDefaults) { + AABox aaBox = getAABox(); + QScriptValue boundingBox = engine->newObject(); + QScriptValue bottomRightNear = vec3toScriptValue(engine, aaBox.getCorner()); + QScriptValue topFarLeft = vec3toScriptValue(engine, aaBox.calcTopFarLeft()); + QScriptValue center = vec3toScriptValue(engine, aaBox.calcCenter()); + QScriptValue boundingBoxDimensions = vec3toScriptValue(engine, aaBox.getDimensions()); + boundingBox.setProperty("brn", bottomRightNear); + boundingBox.setProperty("tfl", topFarLeft); + boundingBox.setProperty("center", center); + boundingBox.setProperty("dimensions", boundingBoxDimensions); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(boundingBox, boundingBox); // gettable, but not settable + } QString textureNamesList = _textureNames.join(",\n"); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(originalTextures, textureNamesList); // gettable, but not settable + if (!skipDefaults) { + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(originalTextures, textureNamesList); // gettable, but not settable + } return properties; } @@ -451,6 +465,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(localGravity, setLocalGravity); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(particleRadius, setParticleRadius); COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(marketplaceID, setMarketplaceID); + COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(name, setName); COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(keyLightColor, setKeyLightColor); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(keyLightIntensity, setKeyLightIntensity); @@ -467,7 +482,11 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { } QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties) { - return properties.copyToScriptValue(engine); + return properties.copyToScriptValue(engine, false); +} + +QScriptValue EntityItemNonDefaultPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties) { + return properties.copyToScriptValue(engine, true); } void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties) { @@ -666,6 +685,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem } APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, appendValue, properties.getMarketplaceID()); + APPEND_ENTITY_PROPERTY(PROP_NAME, appendValue, properties.getName()); } if (propertyCount > 0) { int endOfEntityItemData = packetData->getUncompressedByteOffset(); @@ -914,6 +934,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int } READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MARKETPLACE_ID, setMarketplaceID); + READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_NAME, setName); return valid; } @@ -963,6 +984,7 @@ void EntityItemProperties::markAllChanged() { _registrationPointChanged = true; _angularVelocityChanged = true; _angularDampingChanged = true; + _nameChanged = true; _visibleChanged = true; _colorChanged = true; _modelURLChanged = true; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 9dfa8802ba..8daf0169e8 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -98,6 +98,7 @@ enum EntityPropertyList { PROP_MARKETPLACE_ID, PROP_ACCELERATION, PROP_SIMULATOR_ID, + PROP_NAME, //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties ABOVE this line @@ -160,7 +161,7 @@ public: EntityTypes::EntityType getType() const { return _type; } void setType(EntityTypes::EntityType type) { _type = type; } - virtual QScriptValue copyToScriptValue(QScriptEngine* engine) const; + virtual QScriptValue copyToScriptValue(QScriptEngine* engine, bool skipDefaults) const; virtual void copyFromScriptValue(const QScriptValue& object); // editing related features supported by all entities @@ -240,6 +241,7 @@ public: DEFINE_PROPERTY(PROP_STAGE_ALTITUDE, StageAltitude, stageAltitude, float); DEFINE_PROPERTY(PROP_STAGE_DAY, StageDay, stageDay, quint16); DEFINE_PROPERTY(PROP_STAGE_HOUR, StageHour, stageHour, float); + DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString); public: @@ -308,6 +310,7 @@ private: }; Q_DECLARE_METATYPE(EntityItemProperties); QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); +QScriptValue EntityItemNonDefaultPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties); @@ -331,6 +334,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, Dimensions, dimensions, "in meters"); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Velocity, velocity, "in meters"); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Name, name, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Visible, visible, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Rotation, rotation, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Density, density, ""); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index 001f963ecb..bdc1fb37e6 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -55,4 +55,6 @@ const float ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING = 0.39347f; // approx timescale const bool ENTITY_ITEM_DEFAULT_IGNORE_FOR_COLLISIONS = false; const bool ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE = false; +const QString ENTITY_ITEM_DEFAULT_NAME = QString(""); + #endif // hifi_EntityItemPropertiesDefaults_h diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 33714f4ea2..3fc6cfa85f 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -198,26 +198,41 @@ #define COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(P) \ - QScriptValue P = vec3toScriptValue(engine, _##P); \ - properties.setProperty(#P, P); + if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ + QScriptValue P = vec3toScriptValue(engine, _##P); \ + properties.setProperty(#P, P); \ + } #define COPY_PROPERTY_TO_QSCRIPTVALUE_QUAT(P) \ - QScriptValue P = quatToScriptValue(engine, _##P); \ - properties.setProperty(#P, P); + if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ + QScriptValue P = quatToScriptValue(engine, _##P); \ + properties.setProperty(#P, P); \ + } #define COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR(P) \ - QScriptValue P = xColorToScriptValue(engine, _##P); \ - properties.setProperty(#P, P); + if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ + QScriptValue P = xColorToScriptValue(engine, _##P); \ + properties.setProperty(#P, P); \ + } #define COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(P,G) \ - QScriptValue P = xColorToScriptValue(engine, G); \ - properties.setProperty(#P, P); + if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ + QScriptValue P = xColorToScriptValue(engine, G); \ + properties.setProperty(#P, P); \ + } -#define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(P, G) \ +#define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(P, G) \ properties.setProperty(#P, G); +#define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(P, G) \ + if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ + properties.setProperty(#P, G); \ + } + #define COPY_PROPERTY_TO_QSCRIPTVALUE(P) \ - properties.setProperty(#P, _##P); + if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ + properties.setProperty(#P, _##P); \ + } #define COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(P, S) \ QScriptValue P = object.property(#P); \ diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 436339b3fe..f8f94f8d17 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1137,10 +1137,10 @@ bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData) return true; } -bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElement* element) { +bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElement* element, bool skipDefaultValues) { entityDescription["Entities"] = QVariantList(); QScriptEngine scriptEngine; - RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine); + RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine, skipDefaultValues); recurseTreeWithOperator(&theOperator); return true; } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 3f01dec408..3880d67eda 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -163,7 +163,7 @@ public: bool wantEditLogging() const { return _wantEditLogging; } void setWantEditLogging(bool value) { _wantEditLogging = value; } - bool writeToMap(QVariantMap& entityDescription, OctreeElement* element); + bool writeToMap(QVariantMap& entityDescription, OctreeElement* element, bool skipDefaultValues); bool readFromMap(QVariantMap& entityDescription); signals: diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.cpp b/libraries/entities/src/RecurseOctreeToMapOperator.cpp index afe28e17e0..daa01c203e 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.cpp +++ b/libraries/entities/src/RecurseOctreeToMapOperator.cpp @@ -12,11 +12,15 @@ #include "RecurseOctreeToMapOperator.h" -RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map, OctreeElement *top, QScriptEngine *engine) : +RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map, + OctreeElement *top, + QScriptEngine *engine, + bool skipDefaultValues) : RecurseOctreeOperator(), _map(map), _top(top), - _engine(engine) + _engine(engine), + _skipDefaultValues(skipDefaultValues) { // if some element "top" was given, only save information for that element and it's children. if (_top) { @@ -36,6 +40,8 @@ bool RecurseOctreeToMapOperator::preRecursion(OctreeElement* element) { bool RecurseOctreeToMapOperator::postRecursion(OctreeElement* element) { + EntityItemProperties defaultProperties; + EntityTreeElement* entityTreeElement = static_cast(element); const QList& entities = entityTreeElement->getEntities(); @@ -43,7 +49,12 @@ bool RecurseOctreeToMapOperator::postRecursion(OctreeElement* element) { foreach (EntityItem* entityItem, entities) { EntityItemProperties properties = entityItem->getProperties(); - QScriptValue qScriptValues = EntityItemPropertiesToScriptValue(_engine, properties); + QScriptValue qScriptValues; + if (_skipDefaultValues) { + qScriptValues = EntityItemNonDefaultPropertiesToScriptValue(_engine, properties); + } else { + qScriptValues = EntityItemPropertiesToScriptValue(_engine, properties); + } entitiesQList << qScriptValues.toVariant(); } _map["Entities"] = entitiesQList; diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.h b/libraries/entities/src/RecurseOctreeToMapOperator.h index 6bd44f3cbf..bfa5024b09 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.h +++ b/libraries/entities/src/RecurseOctreeToMapOperator.h @@ -13,7 +13,7 @@ class RecurseOctreeToMapOperator : public RecurseOctreeOperator { public: - RecurseOctreeToMapOperator(QVariantMap& map, OctreeElement *top, QScriptEngine *engine); + RecurseOctreeToMapOperator(QVariantMap& map, OctreeElement *top, QScriptEngine *engine, bool skipDefaultValues); bool preRecursion(OctreeElement* element); bool postRecursion(OctreeElement* element); private: @@ -21,4 +21,5 @@ public: OctreeElement *_top; QScriptEngine *_engine; bool _withinTop; + bool _skipDefaultValues; }; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 871f3d0581..648c47c975 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -203,6 +203,16 @@ public: glm::quat rotation; // relative orientation }; +inline bool operator==(const SittingPoint& lhs, const SittingPoint& rhs) +{ + return (lhs.name == rhs.name) && (lhs.position == rhs.position) && (lhs.rotation == rhs.rotation); +} + +inline bool operator!=(const SittingPoint& lhs, const SittingPoint& rhs) +{ + return (lhs.name != rhs.name) || (lhs.position != rhs.position) || (lhs.rotation != rhs.rotation); +} + /// A set of meshes extracted from an FBX document. class FBXGeometry { public: diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp index 2529070b18..dd8e9c68b6 100755 --- a/libraries/gpu/src/gpu/GLBackendOutput.cpp +++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp @@ -74,6 +74,19 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe } } } +#if (GPU_FEATURE_PROFILE == GPU_LEGACY) + // for reasons that i don;t understand yet, it seems that on mac gl, a fbo must have a color buffer... + else { + GLuint renderBuffer = 0; + glGenRenderbuffers(1, &renderBuffer); + glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, framebuffer.getWidth(), framebuffer.getHeight()); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBuffer); + CHECK_GL_ERROR(); + } +#endif + if (framebuffer.hasDepthStencil()) { auto surface = framebuffer.getDepthStencilBuffer(); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 8d05fa8fab..6ebe30c930 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -239,8 +239,6 @@ protected: HifiSockAddr _localSockAddr; HifiSockAddr _publicSockAddr; HifiSockAddr _stunSockAddr; - - QTimer* _silentNodeTimer; // XXX can BandwidthRecorder be used for this? int _numCollectedPackets; diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 8fedc6b979..a9ccec34bb 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -74,7 +74,7 @@ PacketVersion versionForPacketType(PacketType type) { return 1; case PacketTypeEntityAddOrEdit: case PacketTypeEntityData: - return VERSION_ENTITIES_ZONE_ENTITIES_HAVE_DYNAMIC_SHAPE; + return VERSION_ENTITIES_HAVE_NAMES; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 01302f5568..7b12b1a089 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -139,5 +139,6 @@ const PacketVersion VERSION_ENTITIES_HAVE_ACCELERATION = 15; const PacketVersion VERSION_ENTITIES_HAVE_UUIDS = 16; const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_EXIST = 17; const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_DYNAMIC_SHAPE = 18; +const PacketVersion VERSION_ENTITIES_HAVE_NAMES = 19; #endif // hifi_PacketHeaders_h diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index d5fc006882..bcc9f9a1f5 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -47,6 +47,8 @@ public: /// How many received packets waiting are to be processed int packetsToProcessCount() const { return _packets.size(); } + virtual void terminating(); + public slots: void nodeKilled(SharedNodePointer node); @@ -71,8 +73,6 @@ protected: /// Override to do work after the packets processing loop. Default does nothing. virtual void postProcess() { } - virtual void terminating(); - protected: QVector _packets; diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 79b4e7f437..43bcce4530 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -30,6 +30,17 @@ void ThreadedAssignment::setFinished(bool isFinished) { _isFinished = isFinished; if (_isFinished) { + if (_domainServerTimer) { + _domainServerTimer->stop(); + delete _domainServerTimer; + _domainServerTimer = nullptr; + } + if (_statsTimer) { + _statsTimer->stop(); + delete _statsTimer; + _statsTimer = nullptr; + } + aboutToFinish(); auto nodeList = DependencyManager::get(); @@ -63,15 +74,15 @@ void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeTy // this is a temp fix for Qt 5.3 - rebinding the node socket gives us readyRead for the socket on this thread nodeList->rebindNodeSocket(); - QTimer* domainServerTimer = new QTimer(this); - connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); - domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS); + _domainServerTimer = new QTimer(); + connect(_domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); + _domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS); if (shouldSendStats) { // send a stats packet every 1 second - QTimer* statsTimer = new QTimer(this); - connect(statsTimer, &QTimer::timeout, this, &ThreadedAssignment::sendStatsPacket); - statsTimer->start(1000); + _statsTimer = new QTimer(); + connect(_statsTimer, &QTimer::timeout, this, &ThreadedAssignment::sendStatsPacket); + _statsTimer->start(1000); } } diff --git a/libraries/networking/src/ThreadedAssignment.h b/libraries/networking/src/ThreadedAssignment.h index 454baa85f2..590c2f56ca 100644 --- a/libraries/networking/src/ThreadedAssignment.h +++ b/libraries/networking/src/ThreadedAssignment.h @@ -28,8 +28,15 @@ public: public slots: /// threaded run of assignment virtual void run() = 0; + Q_INVOKABLE virtual void stop() { setFinished(true); } virtual void readPendingDatagrams() = 0; virtual void sendStatsPacket(); + +public slots: + virtual void aboutToQuit() { + QMetaObject::invokeMethod(this, "stop"); + } + signals: void finished(); @@ -38,6 +45,8 @@ protected: void commonInit(const QString& targetName, NodeType_t nodeType, bool shouldSendStats = true); bool _isFinished; QThread* _datagramProcessingThread; + QTimer* _domainServerTimer = nullptr; + QTimer* _statsTimer = nullptr; private slots: void checkInWithDomainServerOrExit(); diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 059c0e5bbc..d53d29e444 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -2087,7 +2087,7 @@ void Octree::writeToJSONFile(const char* fileName, OctreeElement* element) { QFile persistFile(fileName); QVariantMap entityDescription; - qCDebug(octree, "Saving to file %s...", fileName); + qCDebug(octree, "Saving JSON SVO to file %s...", fileName); OctreeElement* top; if (element) { @@ -2096,7 +2096,7 @@ void Octree::writeToJSONFile(const char* fileName, OctreeElement* element) { top = _rootElement; } - bool entityDescriptionSuccess = writeToMap(entityDescription, top); + bool entityDescriptionSuccess = writeToMap(entityDescription, top, true); if (entityDescriptionSuccess && persistFile.open(QIODevice::WriteOnly)) { persistFile.write(QJsonDocument::fromVariant(entityDescription).toJson()); } else { @@ -2108,7 +2108,7 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { std::ofstream file(fileName, std::ios::out|std::ios::binary); if(file.is_open()) { - qCDebug(octree, "Saving to file %s...", fileName); + qCDebug(octree, "Saving binary SVO to file %s...", fileName); PacketType expectedType = expectedDataPacketType(); PacketVersion expectedVersion = versionForPacketType(expectedType); diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 21c3efc01d..d7fc58699f 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -331,7 +331,7 @@ public: void writeToFile(const char* filename, OctreeElement* element = NULL, QString persistAsFileType = "svo"); void writeToJSONFile(const char* filename, OctreeElement* element = NULL); void writeToSVOFile(const char* filename, OctreeElement* element = NULL); - virtual bool writeToMap(QVariantMap& entityDescription, OctreeElement* element) = 0; + virtual bool writeToMap(QVariantMap& entityDescription, OctreeElement* element, bool skipDefaultValues) = 0; // Octree importers bool readFromFile(const char* filename); diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index eafc9065a2..a80d071851 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -228,6 +228,7 @@ void OctreePersistThread::aboutToFinish() { qCDebug(octree) << "Persist thread about to finish..."; persist(); qCDebug(octree) << "Persist thread done with about to finish..."; + _stopThread = true; } void OctreePersistThread::persist() { diff --git a/libraries/render-utils/src/model.slf b/libraries/render-utils/src/model.slf index adf80d01bf..7648cac429 100755 --- a/libraries/render-utils/src/model.slf +++ b/libraries/render-utils/src/model.slf @@ -19,7 +19,7 @@ uniform sampler2D diffuseMap; // the interpolated normal -varying vec4 normal; +varying vec4 interpolatedNormal; varying vec3 color; @@ -31,7 +31,7 @@ void main(void) { Material mat = getMaterial(); packDeferredFragment( - normalize(normal.xyz), + normalize(interpolatedNormal.xyz), evalOpaqueFinalAlpha(getMaterialOpacity(mat), diffuse.a), getMaterialDiffuse(mat) * diffuse.rgb * color, getMaterialSpecular(mat), diff --git a/libraries/render-utils/src/model.slv b/libraries/render-utils/src/model.slv index f4511da944..97b5eb640b 100755 --- a/libraries/render-utils/src/model.slv +++ b/libraries/render-utils/src/model.slv @@ -20,7 +20,7 @@ const int MAX_TEXCOORDS = 2; uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; // the interpolated normal -varying vec4 normal; +varying vec4 interpolatedNormal; varying vec3 color; @@ -36,7 +36,7 @@ void main(void) { TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> - <$transformModelToEyeDir(cam, obj, gl_Normal, normal.xyz)$> + <$transformModelToEyeDir(cam, obj, gl_Normal, interpolatedNormal.xyz)$> - normal = vec4(normalize(normal.xyz), 0.0); + interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); } diff --git a/libraries/render-utils/src/model_lightmap.slf b/libraries/render-utils/src/model_lightmap.slf index b7660da16d..00d74ed997 100755 --- a/libraries/render-utils/src/model_lightmap.slf +++ b/libraries/render-utils/src/model_lightmap.slf @@ -24,7 +24,7 @@ uniform sampler2D emissiveMap; uniform vec2 emissiveParams; // the interpolated normal -varying vec4 normal; +varying vec4 interpolatedNormal; varying vec3 color; @@ -39,7 +39,7 @@ void main(void) { Material mat = getMaterial(); packDeferredFragmentLightmap( - normalize(normal.xyz), + normalize(interpolatedNormal.xyz), evalOpaqueFinalAlpha(getMaterialOpacity(mat), diffuse.a), getMaterialDiffuse(mat) * diffuse.rgb * color, getMaterialSpecular(mat), diff --git a/libraries/render-utils/src/model_lightmap.slv b/libraries/render-utils/src/model_lightmap.slv index 56b34a5aa8..54d9bd21e1 100755 --- a/libraries/render-utils/src/model_lightmap.slv +++ b/libraries/render-utils/src/model_lightmap.slv @@ -23,7 +23,7 @@ uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; attribute vec2 texcoord1; // the interpolated normal -varying vec4 normal; +varying vec4 interpolatedNormal; // the interpolated texcoord1 varying vec2 interpolatedTexcoord1; @@ -44,8 +44,8 @@ void main(void) { TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> - <$transformModelToEyeDir(cam, obj, gl_Normal, normal.xyz)$> + <$transformModelToEyeDir(cam, obj, gl_Normal, interpolatedNormal.xyz)$> - normal = vec4(normalize(normal.xyz), 0.0); + interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); } diff --git a/libraries/render-utils/src/model_lightmap_specular_map.slf b/libraries/render-utils/src/model_lightmap_specular_map.slf index e4bb682601..daabfe2d07 100755 --- a/libraries/render-utils/src/model_lightmap_specular_map.slf +++ b/libraries/render-utils/src/model_lightmap_specular_map.slf @@ -27,7 +27,7 @@ uniform vec2 emissiveParams; uniform sampler2D specularMap; // the interpolated normal -varying vec4 normal; +varying vec4 interpolatedNormal; varying vec2 interpolatedTexcoord1; @@ -42,7 +42,7 @@ void main(void) { Material mat = getMaterial(); packDeferredFragmentLightmap( - normalize(normal.xyz), + normalize(interpolatedNormal.xyz), evalOpaqueFinalAlpha(getMaterialOpacity(mat), diffuse.a), getMaterialDiffuse(mat) * diffuse.rgb * color, specular, // no use of getMaterialSpecular(mat) diff --git a/libraries/render-utils/src/model_normal_map.slv b/libraries/render-utils/src/model_normal_map.slv index e702b446b8..2f493053dd 100755 --- a/libraries/render-utils/src/model_normal_map.slv +++ b/libraries/render-utils/src/model_normal_map.slv @@ -1,5 +1,5 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> +<@include gpu/Config.slh@> +<$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // // model.vert @@ -12,9 +12,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include gpu/Transform.slh@> - -<$declareStandardTransform()$> +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> const int MAX_TEXCOORDS = 2; @@ -29,7 +29,7 @@ varying vec4 interpolatedNormal; // the interpolated tangent varying vec4 interpolatedTangent; -varying vec3 color; +varying vec3 color; void main(void) { // transform and store the normal and tangent for interpolation @@ -42,13 +42,13 @@ void main(void) { // and the texture coordinates gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.0, 1.0); - // standard transform - TransformCamera cam = getTransformCamera(); - TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> - <$transformModelToEyeDir(cam, obj, gl_Normal, interpolatedNormal.xyz)$> - <$transformModelToEyeDir(cam, obj, tangent, interpolatedTangent.xyz)$> - - interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> + <$transformModelToEyeDir(cam, obj, gl_Normal, interpolatedNormal.xyz)$> + <$transformModelToEyeDir(cam, obj, tangent, interpolatedTangent.xyz)$> + + interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); interpolatedTangent = vec4(normalize(interpolatedTangent.xyz), 0.0); } diff --git a/libraries/render-utils/src/model_specular_map.slf b/libraries/render-utils/src/model_specular_map.slf index a0203d74af..46f565de6c 100755 --- a/libraries/render-utils/src/model_specular_map.slf +++ b/libraries/render-utils/src/model_specular_map.slf @@ -23,7 +23,7 @@ uniform sampler2D diffuseMap; uniform sampler2D specularMap; // the interpolated normal -varying vec4 normal; +varying vec4 interpolatedNormal; varying vec3 color; @@ -35,7 +35,7 @@ void main(void) { Material mat = getMaterial(); packDeferredFragment( - normalize(normal.xyz), + normalize(interpolatedNormal.xyz), evalOpaqueFinalAlpha(getMaterialOpacity(mat), diffuse.a), getMaterialDiffuse(mat) * diffuse.rgb * color, specular, //getMaterialSpecular(mat), diff --git a/libraries/render-utils/src/model_translucent.slf b/libraries/render-utils/src/model_translucent.slf index 9b8eb97f70..9b34951f88 100755 --- a/libraries/render-utils/src/model_translucent.slf +++ b/libraries/render-utils/src/model_translucent.slf @@ -19,7 +19,7 @@ // the diffuse texture uniform sampler2D diffuseMap; -varying vec4 normal; +varying vec4 interpolatedNormal; varying vec3 color; @@ -31,7 +31,7 @@ void main(void) { Material mat = getMaterial(); packDeferredFragmentTranslucent( - normalize(normal.xyz), + normalize(interpolatedNormal.xyz), getMaterialOpacity(mat) * diffuse.a, getMaterialDiffuse(mat) * diffuse.rgb * color, getMaterialSpecular(mat), diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index 3e70674e33..4e1a09fb92 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -15,7 +15,7 @@ <@include DeferredBufferWrite.slh@> // the interpolated normal -varying vec4 normal; +varying vec4 interpolatedNormal; // the glow intensity //uniform float glowIntensity; @@ -28,7 +28,7 @@ void main(void) { */ packDeferredFragment( - normalize(normal.xyz), + normalize(interpolatedNormal.xyz), glowIntensity, gl_Color.rgb, gl_FrontMaterial.specular.rgb, diff --git a/libraries/render-utils/src/simple.slv b/libraries/render-utils/src/simple.slv index 61338b440d..9ad47a3e66 100644 --- a/libraries/render-utils/src/simple.slv +++ b/libraries/render-utils/src/simple.slv @@ -13,11 +13,11 @@ // // the interpolated normal -varying vec4 normal; +varying vec4 interpolatedNormal; void main(void) { // transform and store the normal for interpolation - normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); + interpolatedNormal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); // pass along the diffuse color gl_FrontColor = gl_Color; diff --git a/libraries/render-utils/src/skin_model.slv b/libraries/render-utils/src/skin_model.slv index f475cbd1f5..c94fc0d151 100755 --- a/libraries/render-utils/src/skin_model.slv +++ b/libraries/render-utils/src/skin_model.slv @@ -1,5 +1,5 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> +<@include gpu/Config.slh@> +<$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // // skin_model.vert @@ -12,8 +12,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include gpu/Transform.slh@> -<$declareStandardTransform()$> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> const int MAX_TEXCOORDS = 2; const int MAX_CLUSTERS = 128; @@ -26,18 +26,18 @@ attribute vec4 clusterIndices; attribute vec4 clusterWeights; // the interpolated normal -varying vec4 normal; +varying vec4 interpolatedNormal; varying vec3 color; void main(void) { vec4 position = vec4(0.0, 0.0, 0.0, 0.0); - normal = vec4(0.0, 0.0, 0.0, 0.0); + interpolatedNormal = vec4(0.0, 0.0, 0.0, 0.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[int(clusterIndices[i])]; float clusterWeight = clusterWeights[i]; position += clusterMatrix * gl_Vertex * clusterWeight; - normal += clusterMatrix * vec4(gl_Normal, 0.0) * clusterWeight; + interpolatedNormal += clusterMatrix * vec4(gl_Normal, 0.0) * clusterWeight; } // pass along the diffuse color @@ -46,11 +46,11 @@ void main(void) { // and the texture coordinates gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.0, 1.0); - // standard transform - TransformCamera cam = getTransformCamera(); - TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, position, gl_Position)$> - <$transformModelToEyeDir(cam, obj, normal.xyz, normal.xyz)$> - - normal = vec4(normalize(normal.xyz), 0.0); + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, position, gl_Position)$> + <$transformModelToEyeDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$> + + interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); } diff --git a/libraries/render-utils/src/skin_model_normal_map.slv b/libraries/render-utils/src/skin_model_normal_map.slv index bbc8f81d12..ed552a7aca 100755 --- a/libraries/render-utils/src/skin_model_normal_map.slv +++ b/libraries/render-utils/src/skin_model_normal_map.slv @@ -1,5 +1,5 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> +<@include gpu/Config.slh@> +<$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // // skin_model_normal_map.vert @@ -12,8 +12,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include gpu/Transform.slh@> -<$declareStandardTransform()$> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> const int MAX_TEXCOORDS = 2; const int MAX_CLUSTERS = 128; @@ -54,16 +54,16 @@ void main(void) { // and the texture coordinates gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.0, 1.0); - interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); + interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); interpolatedTangent = vec4(normalize(interpolatedTangent.xyz), 0.0); - // standard transform - TransformCamera cam = getTransformCamera(); - TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, interpolatedPosition, gl_Position)$> - <$transformModelToEyeDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$> - <$transformModelToEyeDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$> - - interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, interpolatedPosition, gl_Position)$> + <$transformModelToEyeDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$> + <$transformModelToEyeDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$> + + interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); interpolatedTangent = vec4(normalize(interpolatedTangent.xyz), 0.0); } diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 7b18d01d80..68d448114c 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -43,6 +43,16 @@ inline QDebug& operator<<(QDebug& dbg, const xColor& c) { return dbg; } +inline bool operator==(const xColor& lhs, const xColor& rhs) +{ + return (lhs.red == rhs.red) && (lhs.green == rhs.green) && (lhs.blue == rhs.blue); +} + +inline bool operator!=(const xColor& lhs, const xColor& rhs) +{ + return (lhs.red != rhs.red) || (lhs.green != rhs.green) || (lhs.blue != rhs.blue); +} + // Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers. const QByteArray HIGH_FIDELITY_USER_AGENT = "Mozilla/5.0 (HighFidelityInterface)";