From 7f4e0c206cd0cce1140863168c8bb48340b89529 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 19 Nov 2014 09:13:03 -0800 Subject: [PATCH 01/24] cache indeces to toe joints --- libraries/fbx/src/FBXReader.cpp | 14 ++++++++++++-- libraries/fbx/src/FBXReader.h | 2 ++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 4ffd3f6286..e0f175d60d 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1067,6 +1067,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QString jointHeadID; QString jointLeftHandID; QString jointRightHandID; + QString jointLeftToeID; + QString jointRightToeID; QVector humanIKJointNames; for (int i = 0;; i++) { @@ -1166,11 +1168,17 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } else if (name == jointHeadName) { jointHeadID = getID(object.properties); - } else if (name == jointLeftHandName) { + } else if (name == jointLeftHandName || name == "LeftHand" || name == "joint_L_hand") { jointLeftHandID = getID(object.properties); - } else if (name == jointRightHandName) { + } else if (name == jointRightHandName || name == "RightHand" || name == "joint_R_hand") { jointRightHandID = getID(object.properties); + + } else if (name == "LeftToe" || name == "joint_L_toe" || name == "LeftToe_End") { + jointLeftToeID = getID(object.properties); + + } else if (name == "RightToe" || name == "joint_R_toe" || name == "RightToe_End") { + jointRightToeID = getID(object.properties); } int humanIKJointIndex = humanIKJointNames.indexOf(name); if (humanIKJointIndex != -1) { @@ -1595,6 +1603,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) geometry.headJointIndex = modelIDs.indexOf(jointHeadID); geometry.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID); geometry.rightHandJointIndex = modelIDs.indexOf(jointRightHandID); + geometry.leftToeJointIndex = modelIDs.indexOf(jointLeftToeID); + geometry.rightToeJointIndex = modelIDs.indexOf(jointRightToeID); foreach (const QString& id, humanIKJointIDs) { geometry.humanIKJointIndices.append(modelIDs.indexOf(id)); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 3c9e918686..4a585c2476 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -201,6 +201,8 @@ public: int headJointIndex; int leftHandJointIndex; int rightHandJointIndex; + int leftToeJointIndex; + int rightToeJointIndex; QVector humanIKJointIndices; From 6673d9d0c22bca16f792c5781f1d457094893c1a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 19 Nov 2014 09:13:24 -0800 Subject: [PATCH 02/24] avatar stands on lowest toe --- interface/src/avatar/Avatar.cpp | 5 +- interface/src/avatar/MyAvatar.cpp | 15 +++++- interface/src/avatar/MyAvatar.h | 1 + interface/src/avatar/SkeletonModel.cpp | 67 +++++++++++++++++++++++++- interface/src/avatar/SkeletonModel.h | 7 +++ 5 files changed, 91 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index e29f68e585..7c04bab633 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -775,7 +775,10 @@ void Avatar::setSkeletonOffset(const glm::vec3& offset) { } glm::vec3 Avatar::getSkeletonPosition() const { - return _position + _skeletonOffset; + // The avatar is rotated PI about the yAxis, so we have to correct for it + // to get the skeleton offset contribution in the world-frame. + const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + return _position + getOrientation() * FLIP * _skeletonOffset; } QVector Avatar::getJointRotations() const { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 292cc2fb71..af26ac95af 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -89,6 +89,7 @@ MyAvatar::MyAvatar() : _billboardValid(false), _physicsSimulation(), _voxelShapeManager(), + _feetTouchFloor(true), _isLookingAtLeftEye(true) { ShapeCollider::initDispatchTable(); @@ -115,7 +116,7 @@ QByteArray MyAvatar::toByteArray() { if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { // fake the avatar position that is sent up to the AvatarMixer glm::vec3 oldPosition = _position; - _position += _skeletonOffset; + _position = getSkeletonPosition(); QByteArray array = AvatarData::toByteArray(); // copy the correct position back _position = oldPosition; @@ -155,6 +156,9 @@ void MyAvatar::update(float deltaTime) { } simulate(deltaTime); + if (_feetTouchFloor) { + _skeletonModel.updateStandingFoot(); + } } void MyAvatar::simulate(float deltaTime) { @@ -1034,7 +1038,14 @@ void MyAvatar::setAttachmentData(const QVector& attachmentData) glm::vec3 MyAvatar::getSkeletonPosition() const { CameraMode mode = Application::getInstance()->getCamera()->getMode(); if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { - return Avatar::getSkeletonPosition(); + // The avatar is rotated PI about the yAxis, so we have to correct for it + // to get the skeleton offset contribution in the world-frame. + const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + glm::vec3 skeletonOffset = _skeletonOffset; + if (_feetTouchFloor) { + skeletonOffset += _skeletonModel.getStandingOffset(); + } + return _position + getOrientation() * FLIP * skeletonOffset; } return Avatar::getPosition(); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 01bbb556a1..0060c959e4 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -235,6 +235,7 @@ private: PhysicsSimulation _physicsSimulation; VoxelShapeManager _voxelShapeManager; + bool _feetTouchFloor; bool _isLookingAtLeftEye; RecorderPointer _recorder; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 7ca105f483..834f1c2c08 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -22,13 +22,22 @@ #include "SkeletonModel.h" #include "SkeletonRagdoll.h" +enum StandingFootState { + LEFT_FOOT, + RIGHT_FOOT, + NO_FOOT +}; + SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : Model(parent), _owningAvatar(owningAvatar), _boundingShape(), _boundingShapeLocalOffset(0.0f), _ragdoll(NULL), - _defaultEyeModelPosition(glm::vec3(0.f, 0.f, 0.f)) { + _defaultEyeModelPosition(glm::vec3(0.f, 0.f, 0.f)), + _standingFoot(NO_FOOT), + _standingOffset(0.0f), + _clampedFootPosition(0.0f) { } SkeletonModel::~SkeletonModel() { @@ -607,6 +616,62 @@ void SkeletonModel::updateVisibleJointStates() { } } +/// \return offset of hips after foot animation +void SkeletonModel::updateStandingFoot() { + glm::vec3 offset(0.0f); + int leftFootIndex = _geometry->getFBXGeometry().leftToeJointIndex; + int rightFootIndex = _geometry->getFBXGeometry().rightToeJointIndex; + + if (leftFootIndex != -1 && rightFootIndex != -1) { + glm::vec3 leftPosition, rightPosition; + getJointPosition(leftFootIndex, leftPosition); + getJointPosition(rightFootIndex, rightPosition); + + int lowestFoot = (leftPosition.y < rightPosition.y) ? LEFT_FOOT : RIGHT_FOOT; + const float MIN_STEP_HEIGHT_THRESHOLD = 0.05f; + bool oneFoot = fabsf(leftPosition.y - rightPosition.y) > MIN_STEP_HEIGHT_THRESHOLD; + int currentFoot = oneFoot ? lowestFoot : _standingFoot; + + if (_standingFoot == NO_FOOT) { + currentFoot = lowestFoot; + } + if (currentFoot != _standingFoot) { + if (_standingFoot == NO_FOOT) { + // pick the lowest foot + glm::vec3 lowestPosition = (currentFoot == LEFT_FOOT) ? leftPosition : rightPosition; + // we ignore zero length positions which can happen for a few frames until skeleton is fully loaded + if (glm::length(lowestPosition) > 0.0f) { + _standingFoot = currentFoot; + _clampedFootPosition = lowestPosition; + } + } else { + // swap feet + _standingFoot = currentFoot; + glm::vec3 nextPosition = leftPosition; + glm::vec3 prevPosition = rightPosition; + if (_standingFoot == RIGHT_FOOT) { + nextPosition = rightPosition; + prevPosition = leftPosition; + } + glm::vec3 oldOffset = _clampedFootPosition - prevPosition; + _clampedFootPosition = oldOffset + nextPosition; + offset = _clampedFootPosition - nextPosition; + } + } else { + glm::vec3 nextPosition = (_standingFoot == LEFT_FOOT) ? leftPosition : rightPosition; + offset = _clampedFootPosition - nextPosition; + } + + // clamp the offset to not exceed some max distance + const float MAX_STEP_OFFSET = 1.0f; + float stepDistance = glm::length(offset); + if (stepDistance > MAX_STEP_OFFSET) { + offset *= (MAX_STEP_OFFSET / stepDistance); + } + } + _standingOffset = offset; +} + SkeletonRagdoll* SkeletonModel::buildRagdoll() { if (!_ragdoll) { _ragdoll = new SkeletonRagdoll(this); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 81e3fcb3b6..ea732acfd5 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -101,6 +101,10 @@ public: /// \return whether or not the head was found. glm::vec3 getDefaultEyeModelPosition() const; + /// skeleton offset caused by moving feet + void updateStandingFoot(); + const glm::vec3& getStandingOffset() const { return _standingOffset; } + virtual void updateVisibleJointStates(); SkeletonRagdoll* buildRagdoll(); @@ -154,6 +158,9 @@ private: SkeletonRagdoll* _ragdoll; glm::vec3 _defaultEyeModelPosition; + int _standingFoot; + glm::vec3 _standingOffset; + glm::vec3 _clampedFootPosition; }; #endif // hifi_SkeletonModel_h From d1d561b833012af8010f6b3494f0cdb65841bdc7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 19 Nov 2014 09:18:17 -0800 Subject: [PATCH 03/24] Initialize ToolWindow _selfHidden property --- interface/src/ui/ToolWindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index 44ea44c47d..de88d75b3d 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -17,6 +17,7 @@ const int DEFAULT_WIDTH = 300; ToolWindow::ToolWindow(QWidget* parent) : QMainWindow(parent), + _selfHidden(false), _hasShown(false), _lastGeometry() { From 82bf0f62104681327163a1aa9a220acf877aeb49 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 19 Nov 2014 09:38:10 -0800 Subject: [PATCH 04/24] menu item for "idle animations shift hips" hack --- interface/src/Menu.cpp | 2 ++ interface/src/Menu.h | 1 + interface/src/avatar/MyAvatar.cpp | 1 + 3 files changed, 4 insertions(+) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f7aabc8f06..1eacfa8363 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -297,6 +297,8 @@ Menu::Menu() : avatar, SLOT(updateMotionBehavior())); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::StandOnNearbyFloors, 0, true, avatar, SLOT(updateMotionBehavior())); + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false, + avatar, SLOT(updateMotionBehavior())); QMenu* collisionsMenu = avatarMenu->addMenu("Collide With..."); addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideAsRagdoll, 0, false, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 8590d8580e..7ed8811b4f 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -484,6 +484,7 @@ namespace MenuOption { const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString SixenseLasers = "Enable Sixense UI Lasers"; const QString StandOnNearbyFloors = "Stand on nearby floors"; + const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations"; const QString Stars = "Stars"; const QString Stats = "Stats"; const QString StereoAudio = "Stereo Audio"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index af26ac95af..8b5fefc3be 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1950,6 +1950,7 @@ void MyAvatar::updateMotionBehavior() { } else { _motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED; } + _feetTouchFloor = menu->isOptionChecked(MenuOption::ShiftHipsForIdleAnimations); } void MyAvatar::onToggleRagdoll() { From 4d56c21442279d6b2bcaa1019503e76067f91016 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 19 Nov 2014 10:35:42 -0800 Subject: [PATCH 05/24] Add snapping to vertical translation --- examples/libraries/entitySelectionTool.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index ada876e9b1..e760fb0463 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -1242,7 +1242,7 @@ SelectionDisplay = (function () { Quat.getFront(lastCameraOrientation)); var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - lastPlaneIntersection = newIntersection; + vector = grid.snapToGrid(vector); // we only care about the Y axis vector.x = 0; @@ -1258,10 +1258,15 @@ SelectionDisplay = (function () { Vec3.print(" newPosition:", newPosition); } for (var i = 0; i < SelectionManager.selections.length; i++) { - var properties = Entities.getEntityProperties(SelectionManager.selections[i]); + var id = SelectionManager.selections[i]; + var properties = selectionManager.savedProperties[id.id]; + var original = properties.position; - properties.position = Vec3.sum(properties.position, vector); - Entities.editEntity(SelectionManager.selections[i], properties); + var newPosition = Vec3.sum(properties.position, vector); + + Entities.editEntity(id, { + position: newPosition, + }); } SelectionManager._update(); From 1706faf2da42db742a27e019329c0d8ed0608b86 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 19 Nov 2014 11:20:47 -0800 Subject: [PATCH 06/24] Fix animationIsPlaying not showing correctly in properties tool window --- examples/html/entityProperties.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 695879b678..a79edfb181 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -214,7 +214,7 @@ elModelSection.style.display = 'block'; elModelURL.value = properties.modelURL; elModelAnimationURL.value = properties.animationURL; - elModelAnimationPlaying.checked = properties.animationPlaying; + elModelAnimationPlaying.checked = properties.animationIsPlaying; elModelAnimationFPS.value = properties.animationFPS; } From fc54ad93b846879f05ef9db4b449ef409a35cd90 Mon Sep 17 00:00:00 2001 From: dev Date: Wed, 19 Nov 2014 11:46:59 -0800 Subject: [PATCH 07/24] refining the avatar display name rendering with different behavior if in HMD mode or not --- interface/src/Application.cpp | 8 ++++++++ interface/src/Application.h | 5 +++++ interface/src/avatar/Avatar.cpp | 35 +++++++++++++++++++++------------ 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 66b96cf132..4e8b95862a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2673,6 +2673,14 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node } } +bool Application::isHMDMode() const { + if (OculusManager::isConnected()) { + return true; + } else { + return false; + } +} + ///////////////////////////////////////////////////////////////////////////////////// // loadViewFrustum() // diff --git a/interface/src/Application.h b/interface/src/Application.h index 2c70249b32..c75202d96f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -316,6 +316,11 @@ public: void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine); + // the isHMDmode is true whenever we use the interface from an HMD and not a standard flat display + // rendering of several elements depend on that + // TODO: carry that information on the Camera as a setting + bool isHMDMode() const; + signals: /// Fired when we're simulating; allows external parties to hook in. diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 5741d6b265..9dcfb37fb7 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -655,7 +655,10 @@ void Avatar::renderDisplayName() { if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) { return; } - + + // which viewing mode? + bool inHMD = Application::getInstance()->isHMDMode(); + glDisable(GL_LIGHTING); glPushMatrix(); @@ -664,17 +667,17 @@ void Avatar::renderDisplayName() { glTranslatef(textPosition.x, textPosition.y, textPosition.z); // we need "always facing camera": we must remove the camera rotation from the stack - glm::quat rotation = Application::getInstance()->getCamera()->getRotation(); - glm::vec3 frontAxis(1.f, 0.f, 0.f); - frontAxis = glm::rotate(rotation, frontAxis); - frontAxis = glm::normalize(glm::vec3(frontAxis.x, 0.f, frontAxis.z)); - - // TODO : test this secodn solution which should be better wfor occulus - //glm::vec3 camPosition = Application::getInstance()->getCamera()->getPosition(); - //glm::vec3 frontAxis = camPosition - textPosition; - //frontAxis = glm::normalize(glm::vec3(frontAxis.z, 0.f, -frontAxis.x)); + glm::vec3 frontAxis(0.f, 0.f, 1.f); + if (inHMD) { + glm::vec3 camPosition = Application::getInstance()->getCamera()->getPosition(); + frontAxis = camPosition - textPosition; + } else { + glm::quat rotation = Application::getInstance()->getCamera()->getRotation(); + frontAxis = glm::rotate(rotation, frontAxis); + } + frontAxis = glm::normalize(glm::vec3(frontAxis.z, 0.f, -frontAxis.x)); float angle = acos(frontAxis.x) * ((frontAxis.z < 0) ? 1.f : -1.f); glRotatef(glm::degrees(angle), 0.0f, 1.0f, 0.0f); @@ -706,10 +709,16 @@ void Avatar::renderDisplayName() { if (success) { double textWindowHeight = abs(result1[1] - result0[1]); - float scaleFactor = Application::getInstance()->getRenderResolutionScale() * // Scale compensate for the resolution - QApplication::desktop()->windowHandle()->devicePixelRatio() * // And the device pixel ratio + // need to scale to compensate for the font resolution due to the device + float scaleFactor = QApplication::desktop()->windowHandle()->devicePixelRatio() * ((textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f); - glScalef(scaleFactor, scaleFactor, 1.0); + if (inHMD) { + const float HMDMODE_NAME_SCALE = 0.65f; + scaleFactor *= HMDMODE_NAME_SCALE; + } else { + scaleFactor *= Application::getInstance()->getRenderResolutionScale(); + } + glScalef(scaleFactor, scaleFactor, 1.0); glScalef(1.0f, -1.0f, 1.0f); // TextRenderer::draw paints the text upside down in y axis From 03bfaa4869821481ac29c364d305d287bef9447a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Nov 2014 12:20:12 -0800 Subject: [PATCH 08/24] handle RAW stereo audio files by using stereo.raw extension --- examples/acScripts/ambiance.js | 5 ++--- examples/radio.js | 5 ++--- libraries/audio/src/AudioInjector.cpp | 2 ++ libraries/audio/src/AudioInjectorLocalBuffer.h | 1 + libraries/audio/src/AudioInjectorOptions.cpp | 13 +++++++------ libraries/audio/src/AudioInjectorOptions.h | 1 + libraries/audio/src/AudioScriptingInterface.cpp | 6 +++++- libraries/audio/src/Sound.cpp | 17 +++++++++++++---- libraries/audio/src/Sound.h | 2 +- 9 files changed, 34 insertions(+), 18 deletions(-) diff --git a/examples/acScripts/ambiance.js b/examples/acScripts/ambiance.js index 0149d5d3ff..fcc3ba0d80 100644 --- a/examples/acScripts/ambiance.js +++ b/examples/acScripts/ambiance.js @@ -14,11 +14,10 @@ var position = { x: 700, y: 25, z: 725 }; var audioOptions = { position: position, volume: 0.4, - loop: true, - stereo: false + loop: true }; -var sound = SoundCache.getSound(soundURL, audioOptions.isStereo); +var sound = SoundCache.getSound(soundURL); var injector = null; var count = 100; diff --git a/examples/radio.js b/examples/radio.js index 0b62d78b0e..e8300c00b5 100644 --- a/examples/radio.js +++ b/examples/radio.js @@ -12,13 +12,12 @@ Script.include("libraries/globals.js"); var modelURL = HIFI_PUBLIC_BUCKET + "models/entities/radio/Speakers.fbx"; -var soundURL = HIFI_PUBLIC_BUCKET + "sounds/FamilyStereo.raw"; +var soundURL = HIFI_PUBLIC_BUCKET + "sounds/family.stereo.raw"; var AudioRotationOffset = Quat.fromPitchYawRollDegrees(0, -90, 0); var audioOptions = { volume: 0.5, - loop: true, - stereo: true + loop: true } var injector = null; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 1c9c93c00e..979bf70533 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -89,10 +89,12 @@ void AudioInjector::injectLocally() { bool success = false; if (_localAudioInterface) { if (_audioData.size() > 0) { + _localBuffer = new AudioInjectorLocalBuffer(_audioData, this); _localBuffer->open(QIODevice::ReadOnly); _localBuffer->setShouldLoop(_options.loop); + QMetaObject::invokeMethod(_localAudioInterface, "outputLocalInjector", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success), diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.h b/libraries/audio/src/AudioInjectorLocalBuffer.h index 8b32c6fbc7..92409617f6 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.h +++ b/libraries/audio/src/AudioInjectorLocalBuffer.h @@ -26,6 +26,7 @@ public: void setShouldLoop(bool shouldLoop) { _shouldLoop = shouldLoop; } + void setCurrentOffset(int currentOffset) { _currentOffset = currentOffset; } private: qint64 recursiveReadFromFront(char* data, qint64 maxSize); diff --git a/libraries/audio/src/AudioInjectorOptions.cpp b/libraries/audio/src/AudioInjectorOptions.cpp index df435cf2cc..824a816382 100644 --- a/libraries/audio/src/AudioInjectorOptions.cpp +++ b/libraries/audio/src/AudioInjectorOptions.cpp @@ -20,7 +20,8 @@ AudioInjectorOptions::AudioInjectorOptions() : orientation(glm::vec3(0.0f, 0.0f, 0.0f)), stereo(false), ignorePenumbra(false), - localOnly(false) + localOnly(false), + secondOffset(0.0) { } @@ -31,9 +32,9 @@ QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInje obj.setProperty("volume", injectorOptions.volume); obj.setProperty("loop", injectorOptions.loop); obj.setProperty("orientation", quatToScriptValue(engine, injectorOptions.orientation)); - obj.setProperty("stereo", injectorOptions.stereo); obj.setProperty("ignorePenumbra", injectorOptions.ignorePenumbra); obj.setProperty("localOnly", injectorOptions.localOnly); + obj.setProperty("secondOffset", injectorOptions.secondOffset); return obj; } @@ -54,10 +55,6 @@ void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOpt quatFromScriptValue(object.property("orientation"), injectorOptions.orientation); } - if (object.property("stereo").isValid()) { - injectorOptions.stereo = object.property("stereo").toBool(); - } - if (object.property("ignorePenumbra").isValid()) { injectorOptions.ignorePenumbra = object.property("ignorePenumbra").toBool(); } @@ -65,4 +62,8 @@ void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOpt if (object.property("localOnly").isValid()) { injectorOptions.localOnly = object.property("localOnly").toBool(); } + + if (object.property("secondOffset").isValid()) { + injectorOptions.secondOffset = object.property("secondOffset").toNumber(); + } } \ No newline at end of file diff --git a/libraries/audio/src/AudioInjectorOptions.h b/libraries/audio/src/AudioInjectorOptions.h index 4fd3a0b7ae..d06dc9eb63 100644 --- a/libraries/audio/src/AudioInjectorOptions.h +++ b/libraries/audio/src/AudioInjectorOptions.h @@ -27,6 +27,7 @@ public: bool stereo; bool ignorePenumbra; bool localOnly; + float secondOffset; }; Q_DECLARE_METATYPE(AudioInjectorOptions); diff --git a/libraries/audio/src/AudioScriptingInterface.cpp b/libraries/audio/src/AudioScriptingInterface.cpp index 35a11f4dd4..b604e2825b 100644 --- a/libraries/audio/src/AudioScriptingInterface.cpp +++ b/libraries/audio/src/AudioScriptingInterface.cpp @@ -44,7 +44,11 @@ void AudioScriptingInterface::stopAllInjectors() { AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) { if (sound) { - AudioInjector* injector = new AudioInjector(sound, injectorOptions); + // stereo option isn't set from script, this comes from sound metadata or filename + AudioInjectorOptions optionsCopy = injectorOptions; + optionsCopy.stereo = sound->isStereo(); + + AudioInjector* injector = new AudioInjector(sound, optionsCopy); injector->setLocalAudioInterface(_localAudioInterface); QThread* injectorThread = new QThread(); diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index ff54e262f8..2608c333d6 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -64,7 +64,14 @@ void Sound::downloadFinished(QNetworkReply* reply) { interpretAsWav(rawAudioByteArray, outputAudioByteArray); downSample(outputAudioByteArray); } else { - // Process as RAW file + // check if this was a stereo raw file + // since it's raw the only way for us to know that is if the file was called .stereo.raw + if (reply->url().fileName().toLower().endsWith("stereo.raw")) { + _isStereo = true; + qDebug() << "Processing sound from" << reply->url() << "as stereo audio file."; + } + + // Process as RAW file downSample(rawAudioByteArray); } trimFrames(); @@ -206,10 +213,12 @@ void Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& ou qDebug() << "Currently not supporting non PCM audio files."; return; } - if (qFromLittleEndian(fileHeader.wave.numChannels) != 1) { - qDebug() << "Currently not supporting stereo audio files."; - return; + if (qFromLittleEndian(fileHeader.wave.numChannels) == 2) { + _isStereo = true; + } else if (qFromLittleEndian(fileHeader.wave.numChannels) > 2) { + qDebug() << "Currently not support audio files with more than 2 channels."; } + if (qFromLittleEndian(fileHeader.wave.bitsPerSample) != 16) { qDebug() << "Currently not supporting non 16bit audio files."; return; diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index c78bf72ff7..02b75417e8 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -25,7 +25,7 @@ class Sound : public Resource { public: Sound(const QUrl& url, bool isStereo = false); - bool isStereo() const { return _isStereo; } + bool isStereo() const { return _isStereo; } bool isReady() const { return _isReady; } const QByteArray& getByteArray() { return _byteArray; } From 3ff44964b5da498f7cf2de52dadd28c4bf19d78e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 19 Nov 2014 13:25:38 -0800 Subject: [PATCH 09/24] Add entity list window --- examples/html/entityList.html | 125 ++++++++++++++++++ examples/html/style.css | 39 ++++++ examples/libraries/entityList.js | 65 +++++++++ examples/newEditEntities.js | 5 + .../entities/src/EntityScriptingInterface.cpp | 4 + .../entities/src/EntityScriptingInterface.h | 3 + 6 files changed, 241 insertions(+) create mode 100644 examples/html/entityList.html create mode 100644 examples/libraries/entityList.js diff --git a/examples/html/entityList.html b/examples/html/entityList.html new file mode 100644 index 0000000000..7caa45f19d --- /dev/null +++ b/examples/html/entityList.html @@ -0,0 +1,125 @@ + + + + + + + +
+ +
+ + + + + + + + + + +
TypeURL
+ + + diff --git a/examples/html/style.css b/examples/html/style.css index 1625fd094f..14d7e456a3 100644 --- a/examples/html/style.css +++ b/examples/html/style.css @@ -93,3 +93,42 @@ input.coord { width: 6em; height: 2em; } + +table#entity-table { + border-collapse: collapse; + font-family: Sans-Serif; + font-size: 12px; + width: 100%; + table-layout: fixed; +} + +#entity-table tr { + cursor: pointer; +} + +tr.selected { + background-color: #AAA; +} + +#entity-table th { + background-color: #333; + color: #fff; + border: 0px black solid; + text-align: left; + word-wrap: nowrap; + white-space: nowrap; +} + +#entity-table td { + border: 0px black solid; + word-wrap: nowrap; + white-space: nowrap; +} + +th#entity-type { + width: 10%; +} + +th#entity-url { + width: 90%; +} diff --git a/examples/libraries/entityList.js b/examples/libraries/entityList.js new file mode 100644 index 0000000000..77808adeb7 --- /dev/null +++ b/examples/libraries/entityList.js @@ -0,0 +1,65 @@ +EntityListTool = function(opts) { + var that = {}; + + var url = Script.resolvePath('html/entityList.html'); + var webView = new WebWindow('Entities', url, 200, 280); + + var visible = false; + + webView.setVisible(visible); + + that.setVisible = function(newVisible) { + visible = newVisible; + webView.setVisible(visible); + }; + + selectionManager.addEventListener(function() { + var selectedIDs = []; + + for (var i = 0; i < selectionManager.selections.length; i++) { + selectedIDs.push(selectionManager.selections[i].id); + } + + data = { + type: 'selectionUpdate', + selectedIDs: selectedIDs, + }; + webView.eventBridge.emitScriptEvent(JSON.stringify(data)); + }); + + webView.eventBridge.webEventReceived.connect(function(data) { + data = JSON.parse(data); + if (data.type == "selectionUpdate") { + var ids = data.entityIds; + var entityIDs = []; + for (var i = 0; i < ids.length; i++) { + entityIDs.push(Entities.getEntityItemID(ids[i])); + } + selectionManager.setSelections(entityIDs); + if (data.focus) { + cameraManager.focus(selectionManager.worldPosition, + selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + } + } else if (data.type == "refresh") { + var entities = []; + var ids = Entities.findEntities(MyAvatar.position, 100); + for (var i = 0; i < ids.length; i++) { + var id = ids[i]; + var properties = Entities.getEntityProperties(id); + entities.push({ + id: id.id, + type: properties.type, + url: properties.type == "Model" ? properties.modelURL : "", + }); + } + var data = { + type: "update", + entities: entities, + }; + webView.eventBridge.emitScriptEvent(JSON.stringify(data)); + } + }); + + return that; +}; diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index f866893411..27a3e8a9d2 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -39,6 +39,9 @@ Script.include("libraries/gridTool.js"); var grid = Grid(); gridTool = GridTool({ horizontalGrid: grid }); +Script.include("libraries/entityList.js"); +var entityListTool = EntityListTool(); + selectionManager.addEventListener(selectionDisplay.updateHandles); var windowDimensions = Controller.getViewportDimensions(); @@ -283,6 +286,7 @@ var toolBar = (function () { if (activeButton === toolBar.clicked(clickedOverlay)) { isActive = !isActive; if (!isActive) { + entityListTool.setVisible(false); gridTool.setVisible(false); grid.setEnabled(false); propertiesTool.setVisible(false); @@ -290,6 +294,7 @@ var toolBar = (function () { cameraManager.disable(); } else { cameraManager.enable(); + entityListTool.setVisible(true); gridTool.setVisible(true); grid.setEnabled(true); propertiesTool.setVisible(true); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 29c4a8b19a..b74c40a9cc 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -44,6 +44,10 @@ EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& pro return id; } +EntityItemID EntityScriptingInterface::getEntityItemID(const QString& uuid) { + return EntityItemID(QUuid(uuid)); +} + EntityItemID EntityScriptingInterface::identifyEntity(EntityItemID entityID) { EntityItemID actualID = entityID; diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 2150fa51da..7269579ab0 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -64,6 +64,9 @@ public slots: /// adds a model with the specific properties Q_INVOKABLE EntityItemID addEntity(const EntityItemProperties& properties); + // Get EntityItemID from uuid string + Q_INVOKABLE EntityItemID getEntityItemID(const QString& entityID); + /// identify a recently created model to determine its true ID Q_INVOKABLE EntityItemID identifyEntity(EntityItemID entityID); From e943a6167cb922f9ebe7fcc5578ab32f25896af4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 19 Nov 2014 13:27:13 -0800 Subject: [PATCH 10/24] Clear the saccade when Faceshift is active. --- interface/src/avatar/Head.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 3b6922d0d1..7c25bbd6b2 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -204,6 +204,8 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { _mouth3, _mouth4, _blendshapeCoefficients); + } else { + _saccade = glm::vec3(); } if (!isMine) { From 0242e0efcf6405af175e8b9bc02718503ac4e2fa Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 19 Nov 2014 13:35:08 -0800 Subject: [PATCH 11/24] Fix layout of entity list window --- examples/html/style.css | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/html/style.css b/examples/html/style.css index 14d7e456a3..b721c31b88 100644 --- a/examples/html/style.css +++ b/examples/html/style.css @@ -99,7 +99,6 @@ table#entity-table { font-family: Sans-Serif; font-size: 12px; width: 100%; - table-layout: fixed; } #entity-table tr { @@ -126,9 +125,8 @@ tr.selected { } th#entity-type { - width: 10%; + width: 60px; } th#entity-url { - width: 90%; } From 81c5f9ba5a1e59033984eb92dcaae2bbd444889b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Nov 2014 13:36:27 -0800 Subject: [PATCH 12/24] inject lobby music at a random starting point --- examples/lobby.js | 18 ++++++++++++++---- libraries/audio/src/AudioInjector.cpp | 13 +++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/examples/lobby.js b/examples/lobby.js index bb033971b3..9b4a81c7cd 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -39,13 +39,15 @@ var ORB_SHIFT = { x: 0, y: -1.4, z: -0.8}; var HELMET_ATTACHMENT_URL = HIFI_PUBLIC_BUCKET + "models/attachments/IronManMaskOnly.fbx" -var droneSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/drone.raw") +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.raw") -var elevatorSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/elevator.raw") +var latinSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/latin.stereo.raw") +var elevatorSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/elevator.stereo.raw") var currentMusak = null; +var firstMusakPlay = true; + function reticlePosition() { var RETICLE_DISTANCE = 1; return Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), RETICLE_DISTANCE)); @@ -137,7 +139,15 @@ function playRandomMusak() { } if (chosenSound) { - currentMusak = Audio.playSound(chosenSound, { stereo: true, localOnly: true }) + if (firstMusakPlay) { + // pick a random number of seconds from 0-10 to offset the muzak + var secondOffset = Math.random() * 10; + firstMusakPlay = false; + } else { + var secondOffset = 0.0; + } + + currentMusak = Audio.playSound(chosenSound, { localOnly: true, secondOffset: secondOffset, volume: 0.5 }) } else { currentMusak = null; } diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 979bf70533..1775099e8a 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -94,6 +94,19 @@ void AudioInjector::injectLocally() { _localBuffer->open(QIODevice::ReadOnly); _localBuffer->setShouldLoop(_options.loop); + // check if we need to offset the sound by some number of seconds + if (_options.secondOffset > 0.0f) { + + qDebug() << "injector wants a sound offset of" << _options.secondOffset; + // convert the offset into a number of bytes + int byteOffset = (int) floorf(SAMPLE_RATE * _options.secondOffset * (_options.stereo ? 2.0f : 1.0f)); + byteOffset *= sizeof(int16_t); + + qDebug() << "that gives us" << byteOffset << "bytes"; + + // give that byte offset to our local buffer + _localBuffer->setCurrentOffset(byteOffset); + } QMetaObject::invokeMethod(_localAudioInterface, "outputLocalInjector", Qt::BlockingQueuedConnection, From 45450c4fe3be73d116b661007462f4f033ee5494 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Nov 2014 13:42:24 -0800 Subject: [PATCH 13/24] handle offset passed in AudioInjectorOptions for mixer injections --- examples/lobby.js | 14 +++----------- libraries/audio/src/AudioInjector.cpp | 28 ++++++++++++++------------- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/examples/lobby.js b/examples/lobby.js index 9b4a81c7cd..3e74cb0136 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -46,8 +46,6 @@ var latinSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/latin.st var elevatorSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/elevator.stereo.raw") var currentMusak = null; -var firstMusakPlay = true; - function reticlePosition() { var RETICLE_DISTANCE = 1; return Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), RETICLE_DISTANCE)); @@ -138,15 +136,9 @@ function playRandomMusak() { chosenSound = elevatorSound; } - if (chosenSound) { - if (firstMusakPlay) { - // pick a random number of seconds from 0-10 to offset the muzak - var secondOffset = Math.random() * 10; - firstMusakPlay = false; - } else { - var secondOffset = 0.0; - } - + if (chosenSound) { + // pick a random number of seconds from 0-10 to offset the muzak + var secondOffset = Math.random() * 10; currentMusak = Audio.playSound(chosenSound, { localOnly: true, secondOffset: secondOffset, volume: 0.5 }) } else { currentMusak = null; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 1775099e8a..d5f82bbdd4 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -78,6 +78,19 @@ float AudioInjector::getLoudness() { } void AudioInjector::injectAudio() { + + // check if we need to offset the sound by some number of seconds + if (_options.secondOffset > 0.0f) { + + // convert the offset into a number of bytes + int byteOffset = (int) floorf(SAMPLE_RATE * _options.secondOffset * (_options.stereo ? 2.0f : 1.0f)); + byteOffset *= sizeof(int16_t); + + qDebug() << "Changing current send position to" << byteOffset; + + _currentSendPosition = byteOffset; + } + if (_options.localOnly) { injectLocally(); } else { @@ -94,19 +107,8 @@ void AudioInjector::injectLocally() { _localBuffer->open(QIODevice::ReadOnly); _localBuffer->setShouldLoop(_options.loop); - // check if we need to offset the sound by some number of seconds - if (_options.secondOffset > 0.0f) { - - qDebug() << "injector wants a sound offset of" << _options.secondOffset; - // convert the offset into a number of bytes - int byteOffset = (int) floorf(SAMPLE_RATE * _options.secondOffset * (_options.stereo ? 2.0f : 1.0f)); - byteOffset *= sizeof(int16_t); - - qDebug() << "that gives us" << byteOffset << "bytes"; - - // give that byte offset to our local buffer - _localBuffer->setCurrentOffset(byteOffset); - } + // give our current send position to the local buffer + _localBuffer->setCurrentOffset(_currentSendPosition); QMetaObject::invokeMethod(_localAudioInterface, "outputLocalInjector", Qt::BlockingQueuedConnection, From a183248f3856a03861958a137af3f23c2c6c82e8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 19 Nov 2014 14:12:26 -0800 Subject: [PATCH 14/24] add updateFoo() methods that accumulate flags about properties that have changed --- libraries/entities/src/EntityItem.cpp | 107 ++++++++++++++++++++++++-- libraries/entities/src/EntityItem.h | 38 ++++++++- 2 files changed, 137 insertions(+), 8 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index d0fafa06b1..1ede0b0fa2 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -90,6 +90,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) { _lastEditedFromRemoteInRemoteTime = 0; _lastUpdated = 0; _created = 0; + _updateFlags = 0; _changedOnServer = 0; initFromEntityItemID(entityItemID); _simulationState = EntityItem::Static; @@ -102,6 +103,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemPropert _lastEditedFromRemoteInRemoteTime = 0; _lastUpdated = 0; _created = properties.getCreated(); + _updateFlags = 0; _changedOnServer = 0; initFromEntityItemID(entityItemID); setProperties(properties, true); // force copy @@ -731,11 +733,6 @@ bool EntityItem::lifetimeHasExpired() const { return isMortal() && (getAge() > getLifetime()); } - -void EntityItem::copyChangedProperties(const EntityItem& other) { - *this = other; -} - EntityItemProperties EntityItem::getProperties() const { EntityItemProperties properties; properties._id = getID(); @@ -947,4 +944,104 @@ void EntityItem::recalculateCollisionShape() { _collisionShape.setScale(entityAACube.getScale()); } +void EntityItem::updatePosition(const glm::vec3& value) { + if (_position != value) { + _position = value; + recalculateCollisionShape(); + _updateFlags |= UPDATE_POSITION; + } +} + +void EntityItem::updatePositionInMeters(const glm::vec3& value) { + glm::vec3 position = glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f); + if (_position != position) { + _position = position; + recalculateCollisionShape(); + _updateFlags |= UPDATE_POSITION; + } +} + +void EntityItem::updateDimensions(const glm::vec3& value) { + if (_dimensions != value) { + _dimensions = value; + recalculateCollisionShape(); + _updateFlags |= UPDATE_SHAPE; + } +} + +void EntityItem::updateDimensionsInMeters(const glm::vec3& value) { + glm::vec3 dimensions = value / (float) TREE_SCALE; + if (_dimensions != dimensions) { + _dimensions = dimensions; + recalculateCollisionShape(); + _updateFlags |= UPDATE_SHAPE; + } +} + +void EntityItem::updateRotation(const glm::quat& rotation) { + if (_rotation != rotation) { + _rotation = rotation; + recalculateCollisionShape(); + _updateFlags |= UPDATE_POSITION; + } +} + +void EntityItem::updateMass(float value) { + if (_mass != value) { + _mass = value; + _updateFlags |= UPDATE_MASS; + } +} + +void EntityItem::updateVelocity(const glm::vec3& value) { + if (_velocity != value) { + _velocity = value; + _updateFlags |= UPDATE_VELOCITY; + } +} + +void EntityItem::updateVelocityInMeters(const glm::vec3& value) { + glm::vec3 velocity = value / (float) TREE_SCALE; + if (_velocity != velocity) { + _velocity = velocity; + _updateFlags |= UPDATE_VELOCITY; + } +} + +void EntityItem::updateGravity(const glm::vec3& value) { + if (_gravity != value) { + _gravity = value; + _updateFlags |= UPDATE_VELOCITY; + } +} + +void EntityItem::updateGravityInMeters(const glm::vec3& value) { + glm::vec3 gravity = value / (float) TREE_SCALE; + if (_gravity != gravity) { + _gravity = gravity; + _updateFlags |= UPDATE_VELOCITY; + } +} + +void EntityItem::updateAngularVelocity(const glm::vec3& value) { + if (_angularVelocity != value) { + _angularVelocity = value; + _updateFlags |= UPDATE_VELOCITY; + } +} + +void EntityItem::updateIgnoreForCollisions(bool value) { + if (_ignoreForCollisions != value) { + _ignoreForCollisions = value; + _updateFlags |= UPDATE_COLLISION; + } +} + +void EntityItem::updateCollisionsWillMove(bool value) { + if (_collisionsWillMove != value) { + _collisionsWillMove = value; + _updateFlags |= UPDATE_COLLISION; + } +} + diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index d57f547cc5..2a818217be 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -35,6 +35,14 @@ class EntityTreeElementExtraEncodeData; #define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0; #define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { }; +enum EntityUpdateFlags { + UPDATE_POSITION = 0x0001, + UPDATE_VELOCITY = 0x0002, + UPDATE_MASS = 0x0004, + UPDATE_COLLISION = 0x0008, + UPDATE_SHAPE = 0x0010, + //UPDATE_APPEARANCE = 0x8000, +}; /// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available /// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate @@ -125,9 +133,6 @@ public: virtual void debugDump() const; - // similar to assignment/copy, but it handles keeping lifetime accurate - void copyChangedProperties(const EntityItem& other); - // attributes applicable to all entity types EntityTypes::EntityType getType() const { return _type; } const glm::vec3& getPosition() const { return _position; } /// get position in domain scale units (0.0 - 1.0) @@ -266,6 +271,29 @@ public: virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; } virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); } + // updateFoo() methods to be used when changes need to be accumulated in the _updateFlags + void updatePosition(const glm::vec3& value); + void updatePositionInMeters(const glm::vec3& value); + void updateDimensions(const glm::vec3& value); + void updateDimensionsInMeters(const glm::vec3& value); + void updateRotation(const glm::quat& rotation); + void updateMass(float value); + void updateVelocity(const glm::vec3& value); + void updateVelocityInMeters(const glm::vec3& value); + void updateGravity(const glm::vec3& value); + void updateGravityInMeters(const glm::vec3& value); + void updateAngularVelocity(const glm::vec3& value); + void updateIgnoreForCollisions(bool value); + void updateCollisionsWillMove(bool value); + + void setUpdateFlags(uint32_t mask) { _updateFlags |= mask; } + void clearUpdateFlags(uint32_t mask) { _updateFlags &= ~mask; } + +#ifdef USE_BULLET_PHYSICS + EntityMotionState* getMotionState() const { return _motionState; } + virtual EntityMotionState* createMotionState() { return NULL; } + void destroyMotionState(); +#endif // USE_BULLET_PHYSICS SimulationState getSimulationState() const { return _simulationState; } protected: @@ -314,6 +342,10 @@ protected: AACubeShape _collisionShape; SimulationState _simulationState; // only set by EntityTree + + // UpdateFlags are set whenever a property changes that requires the change to be communicated to other + // data structures. It is the responsibility of the EntityTree to relay changes entity and clear flags. + uint32_t _updateFlags; }; From b5abf44aa36b83f9b1b347cc08116855233be0e6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 19 Nov 2014 14:31:49 -0800 Subject: [PATCH 15/24] Comment packt type number every 5 --- libraries/networking/src/PacketHeaders.h | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 6cca5b505d..7557ea31e1 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -21,57 +21,57 @@ // NOTE: if adding a new packet type, you can replace one marked usable or add at the end // NOTE: if you want the name of the packet type to be available for debugging or logging, update nameForPacketType() as well enum PacketType { - PacketTypeUnknown, + PacketTypeUnknown, // 0 PacketTypeStunResponse, PacketTypeDomainList, PacketTypePing, PacketTypePingReply, - PacketTypeKillAvatar, + PacketTypeKillAvatar, // 5 PacketTypeAvatarData, PacketTypeInjectAudio, PacketTypeMixedAudio, PacketTypeMicrophoneAudioNoEcho, - PacketTypeMicrophoneAudioWithEcho, + PacketTypeMicrophoneAudioWithEcho, // 10 PacketTypeBulkAvatarData, PacketTypeSilentAudioFrame, PacketTypeEnvironmentData, PacketTypeDomainListRequest, - PacketTypeRequestAssignment, + PacketTypeRequestAssignment, // 15 PacketTypeCreateAssignment, PacketTypeDomainConnectionDenied, PacketTypeMuteEnvironment, PacketTypeAudioStreamStats, - PacketTypeDataServerConfirm, + PacketTypeDataServerConfirm, // 20 PacketTypeVoxelQuery, PacketTypeVoxelData, PacketTypeVoxelSet, PacketTypeVoxelSetDestructive, - PacketTypeVoxelErase, - PacketTypeOctreeStats, // 26 + PacketTypeVoxelErase, // 25 + PacketTypeOctreeStats, PacketTypeJurisdiction, PacketTypeJurisdictionRequest, UNUSED_1, - UNUSED_2, + UNUSED_2, // 30 UNUSED_3, UNUSED_4, PacketTypeNoisyMute, PacketTypeMetavoxelData, - PacketTypeAvatarIdentity, + PacketTypeAvatarIdentity, // 35 PacketTypeAvatarBillboard, PacketTypeDomainConnectRequest, PacketTypeDomainServerRequireDTLS, PacketTypeNodeJsonStats, - PacketTypeEntityQuery, - PacketTypeEntityData, // 41 + PacketTypeEntityQuery, // 40 + PacketTypeEntityData, PacketTypeEntityAddOrEdit, PacketTypeEntityErase, PacketTypeEntityAddResponse, PacketTypeOctreeDataNack, // 45 PacketTypeVoxelEditNack, PacketTypeAudioEnvironment, - PacketTypeEntityEditNack, // 48 + PacketTypeEntityEditNack, PacketTypeSignedTransactionPayment, - PacketTypeIceServerHeartbeat, + PacketTypeIceServerHeartbeat, // 50 PacketTypeIceServerHeartbeatResponse, PacketTypeUnverifiedPing, PacketTypeUnverifiedPingReply From 0a9a6f1ef897238cf773a7baea58a008a3bf4266 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 19 Nov 2014 14:32:50 -0800 Subject: [PATCH 16/24] Fix parsing of silent packets properties --- libraries/audio/src/InboundAudioStream.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index 366659b633..666cdee6b3 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -169,9 +169,16 @@ int InboundAudioStream::parseData(const QByteArray& packet) { } int InboundAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) { - // mixed audio packets do not have any info between the seq num and the audio data. - numAudioSamples = packetAfterSeqNum.size() / sizeof(int16_t); - return 0; + if (type == PacketTypeSilentAudioFrame) { + quint16 numSilentSamples = 0; + memcpy(&numSilentSamples, packetAfterSeqNum.constData(), sizeof(quint16)); + numAudioSamples = numSilentSamples; + return sizeof(quint16); + } else { + // mixed audio packets do not have any info between the seq num and the audio data. + numAudioSamples = packetAfterSeqNum.size() / sizeof(int16_t); + return 0; + } } int InboundAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties, int numAudioSamples) { From 987b75d8f7181e6e82ff7929afb18c881d6656db Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Nov 2014 14:52:57 -0800 Subject: [PATCH 17/24] switch to next musak when finished --- examples/lobby.js | 55 ++++++++++++++----- libraries/audio/src/AudioInjector.cpp | 5 +- .../audio/src/AudioInjectorLocalBuffer.cpp | 5 ++ .../audio/src/AudioInjectorLocalBuffer.h | 2 + 4 files changed, 49 insertions(+), 18 deletions(-) diff --git a/examples/lobby.js b/examples/lobby.js index 3e74cb0136..2e7949a110 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -44,7 +44,8 @@ var currentDrone = null; var latinSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/latin.stereo.raw") var elevatorSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/elevator.stereo.raw") -var currentMusak = null; +var currentMusakInjector = null; +var currentSound = null; function reticlePosition() { var RETICLE_DISTANCE = 1; @@ -125,23 +126,42 @@ function changeLobbyTextures() { Overlays.editOverlay(panelWall, textureProp); } +var MUSAK_VOLUME = 0.5; + +function playNextMusak() { + if (panelWall) { + print("PLAYING THE NEXT MUSAK!"); + if (currentSound == latinSound) { + if (elevatorSound.downloaded) { + currentSound = elevatorSound; + } + } else if (currentSound == elevatorSound) { + if (latinSound.downloaded) { + currentSound = latinSound; + } + } + + currentMusakInjector = Audio.playSound(currentSound, { localOnly: true, volume: MUSAK_VOLUME }); + } +} + function playRandomMusak() { - chosenSound = null; + currentSound = null; if (latinSound.downloaded && elevatorSound.downloaded) { - chosenSound = Math.random < 0.5 ? latinSound : elevatorSound; + currentSound = Math.random() < 0.5 ? latinSound : elevatorSound; } else if (latinSound.downloaded) { - chosenSound = latinSound; + currentSound = latinSound; } else if (elevatorSound.downloaded) { - chosenSound = elevatorSound; + currentSound = elevatorSound; } - if (chosenSound) { - // pick a random number of seconds from 0-10 to offset the muzak + if (currentSound) { + // pick a random number of seconds from 0-10 to offset the musak var secondOffset = Math.random() * 10; - currentMusak = Audio.playSound(chosenSound, { localOnly: true, secondOffset: secondOffset, volume: 0.5 }) + currentMusakInjector = Audio.playSound(currentSound, { localOnly: true, secondOffset: secondOffset, volume: MUSAK_VOLUME }); } else { - currentMusak = null; + currentMusakInjector = null; } } @@ -150,16 +170,16 @@ function cleanupLobby() { Overlays.deleteOverlay(orbShell); Overlays.deleteOverlay(reticle); - Audio.stopInjector(currentDrone); - currentDrone = null; - - Audio.stopInjector(currentMusak); - currentMusak = null; - panelWall = false; orbShell = false; reticle = false; + Audio.stopInjector(currentDrone); + currentDrone = null; + + Audio.stopInjector(currentMusakInjector); + currentMusakInjector = null; + locations = {}; toggleEnvironmentRendering(true); @@ -224,6 +244,11 @@ function update(deltaTime) { Overlays.editOverlay(reticle, { position: reticlePosition() }); + + // if the reticle is up then we may need to play the next musak + if (!Audio.isInjectorPlaying(currentMusakInjector)) { + playNextMusak(); + } } } diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index d5f82bbdd4..d31ab83976 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -86,8 +86,6 @@ void AudioInjector::injectAudio() { int byteOffset = (int) floorf(SAMPLE_RATE * _options.secondOffset * (_options.stereo ? 2.0f : 1.0f)); byteOffset *= sizeof(int16_t); - qDebug() << "Changing current send position to" << byteOffset; - _currentSendPosition = byteOffset; } @@ -117,7 +115,8 @@ void AudioInjector::injectLocally() { Q_ARG(qreal, _options.volume), Q_ARG(AudioInjector*, this)); - + // if we're not looping and the buffer tells us it is empty then emit finished + connect(_localBuffer, &AudioInjectorLocalBuffer::bufferEmpty, this, &AudioInjector::stop); if (!success) { qDebug() << "AudioInjector::injectLocally could not output locally via _localAudioInterface"; diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.cpp b/libraries/audio/src/AudioInjectorLocalBuffer.cpp index bdf084091d..a58d686498 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.cpp +++ b/libraries/audio/src/AudioInjectorLocalBuffer.cpp @@ -47,6 +47,11 @@ qint64 AudioInjectorLocalBuffer::readData(char* data, qint64 maxSize) { _currentOffset += bytesRead; } + if (!_shouldLoop && bytesRead == bytesToEnd) { + // we hit the end of the buffer, emit a signal + emit bufferEmpty(); + } + return bytesRead; } else { return 0; diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.h b/libraries/audio/src/AudioInjectorLocalBuffer.h index 92409617f6..399c7515ec 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.h +++ b/libraries/audio/src/AudioInjectorLocalBuffer.h @@ -27,6 +27,8 @@ public: void setShouldLoop(bool shouldLoop) { _shouldLoop = shouldLoop; } void setCurrentOffset(int currentOffset) { _currentOffset = currentOffset; } +signals: + void bufferEmpty(); private: qint64 recursiveReadFromFront(char* data, qint64 maxSize); From 97d1dc4200f7fa638999b9a7608129142198ab0d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 19 Nov 2014 15:12:22 -0800 Subject: [PATCH 18/24] use updateFoo() where entity properties need relay clear EntityItem updateFlags when processed --- libraries/entities/src/EntityItem.cpp | 27 ++++++++++++------- libraries/entities/src/EntityItem.h | 6 +++-- .../entities/src/EntityItemPropertiesMacros.h | 11 ++++++++ libraries/entities/src/EntityTree.cpp | 3 ++- libraries/entities/src/EntityTreeElement.cpp | 5 ++-- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 1ede0b0fa2..8c7ef72138 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -467,7 +467,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef dataAt += propertyFlags.getEncodedLength(); bytesRead += propertyFlags.getEncodedLength(); - READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, _position); + READ_ENTITY_PROPERTY_SETTER(PROP_POSITION, glm::vec3, updatePosition); // Old bitstreams had PROP_RADIUS, new bitstreams have PROP_DIMENSIONS if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_DIMENSIONS) { @@ -486,7 +486,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef } } else { - READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, _dimensions); + READ_ENTITY_PROPERTY_SETTER(PROP_DIMENSIONS, glm::vec3, setDimensions); if (wantDebug) { qDebug() << " readEntityDataFromBuffer() NEW FORMAT... look for PROP_DIMENSIONS"; } @@ -496,19 +496,19 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef qDebug() << " readEntityDataFromBuffer() _dimensions:" << getDimensionsInMeters() << " in meters"; } - READ_ENTITY_PROPERTY_QUAT(PROP_ROTATION, _rotation); - READ_ENTITY_PROPERTY(PROP_MASS, float, _mass); - READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, _velocity); - READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, _gravity); + READ_ENTITY_PROPERTY_QUAT_SETTER(PROP_ROTATION, updateRotation); + READ_ENTITY_PROPERTY_SETTER(PROP_MASS, float, updateMass); + READ_ENTITY_PROPERTY_SETTER(PROP_VELOCITY, glm::vec3, updateVelocity); + READ_ENTITY_PROPERTY_SETTER(PROP_GRAVITY, glm::vec3, updateGravity); READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping); - READ_ENTITY_PROPERTY(PROP_LIFETIME, float, _lifetime); + READ_ENTITY_PROPERTY_SETTER(PROP_LIFETIME, float, updateLifetime); READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT,setScript); READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, _registrationPoint); - READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, _angularVelocity); + READ_ENTITY_PROPERTY_SETTER(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity); READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, _angularDamping); READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, _visible); - READ_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, bool, _ignoreForCollisions); - READ_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, bool, _collisionsWillMove); + READ_ENTITY_PROPERTY_SETTER(PROP_IGNORE_FOR_COLLISIONS, bool, updateIgnoreForCollisions); + READ_ENTITY_PROPERTY_SETTER(PROP_COLLISIONS_WILL_MOVE, bool, updateCollisionsWillMove); READ_ENTITY_PROPERTY(PROP_LOCKED, bool, _locked); READ_ENTITY_PROPERTY_STRING(PROP_USER_DATA,setUserData); @@ -1044,4 +1044,11 @@ void EntityItem::updateCollisionsWillMove(bool value) { } } +void EntityItem::updateLifetime(float value) { + if (_lifetime != value) { + _lifetime = value; + _updateFlags |= UPDATE_LIFETIME; + } +} + diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 2a818217be..5929ab73c6 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -41,6 +41,7 @@ enum EntityUpdateFlags { UPDATE_MASS = 0x0004, UPDATE_COLLISION = 0x0008, UPDATE_SHAPE = 0x0010, + UPDATE_LIFETIME = 0x0020 //UPDATE_APPEARANCE = 0x8000, }; @@ -285,9 +286,10 @@ public: void updateAngularVelocity(const glm::vec3& value); void updateIgnoreForCollisions(bool value); void updateCollisionsWillMove(bool value); + void updateLifetime(float value); - void setUpdateFlags(uint32_t mask) { _updateFlags |= mask; } - void clearUpdateFlags(uint32_t mask) { _updateFlags &= ~mask; } + uint32_t getUpdateFlags() const { return _updateFlags; } + void clearUpdateFlags() { _updateFlags = 0; } #ifdef USE_BULLET_PHYSICS EntityMotionState* getMotionState() const { return _motionState; } diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 8ad444a975..3288e56a3d 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -63,6 +63,17 @@ } \ } +#define READ_ENTITY_PROPERTY_QUAT_SETTER(P,M) \ + if (propertyFlags.getHasProperty(P)) { \ + glm::quat fromBuffer; \ + int bytes = unpackOrientationQuatFromBytes(dataAt, fromBuffer); \ + dataAt += bytes; \ + bytesRead += bytes; \ + if (overwriteLocalData) { \ + M(fromBuffer); \ + } \ + } + #define READ_ENTITY_PROPERTY_STRING(P,O) \ if (propertyFlags.getHasProperty(P)) { \ uint16_t length; \ diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index a0b2d6f9c3..b25b153f44 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -657,12 +657,13 @@ void EntityTree::updateChangedEntities(quint64 now, QSet& entities foreach (EntityItem* thisEntity, _changedEntities) { // check to see if the lifetime has expired, for immortal entities this is always false if (thisEntity->lifetimeHasExpired()) { - qDebug() << "Lifetime has expired for thisEntity:" << thisEntity->getEntityItemID(); + qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID(); entitiesToDelete << thisEntity->getEntityItemID(); clearEntityState(thisEntity); } else { updateEntityState(thisEntity); } + thisEntity->clearUpdateFlags(); } _changedEntities.clear(); } diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 7d4d95bf94..2c189846eb 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -739,8 +739,9 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int EntityTreeElement* currentContainingElement = _myTree->getContainingElement(entityItemID); bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); - // TODO: Andrew to only set changed if something has actually changed - _myTree->entityChanged(entityItem); + if (entityItem->getUpdateFlags()) { + _myTree->entityChanged(entityItem); + } bool bestFitAfter = bestFitEntityBounds(entityItem); if (bestFitBefore != bestFitAfter) { From afe4a4e432b1e23b98cad622cf407cedd9ca36f1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Nov 2014 15:26:18 -0800 Subject: [PATCH 19/24] don't show reticle if not wearing HMD, reset panel textures --- examples/lobby.js | 58 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/examples/lobby.js b/examples/lobby.js index 2e7949a110..437cfe40f3 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -47,11 +47,15 @@ var elevatorSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/eleva var currentMusakInjector = null; var currentSound = null; +var inOculusMode = Menu.isOptionChecked("EnableVRMode"); + function reticlePosition() { var RETICLE_DISTANCE = 1; return Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), RETICLE_DISTANCE)); } +var MAX_NUM_PANELS = 21; + function drawLobby() { if (!panelWall) { print("Adding overlays for the lobby panel wall and orb shell."); @@ -65,7 +69,7 @@ function drawLobby() { url: HIFI_PUBLIC_BUCKET + "models/sets/Lobby/LobbyPrototype/Lobby5_PanelsWithFrames.fbx", position: Vec3.sum(orbPosition, Vec3.multiplyQbyV(towardsMe, panelsCenterShift)), rotation: towardsMe, - dimensions: panelsDimensions + dimensions: panelsDimensions, }; var orbShellProps = { @@ -78,21 +82,23 @@ function drawLobby() { avatarStickPosition = MyAvatar.position; - panelWall = Overlays.addOverlay("model", panelWallProps); + panelWall = Overlays.addOverlay("model", panelWallProps); orbShell = Overlays.addOverlay("model", orbShellProps); // for HMD wearers, create a reticle in center of screen - var CURSOR_SCALE = 0.025; - - reticle = Overlays.addOverlay("billboard", { - url: HIFI_PUBLIC_BUCKET + "images/cursor.svg", - position: reticlePosition(), - ignoreRayIntersection: true, - isFacingAvatar: true, - alpha: 1.0, - scale: CURSOR_SCALE - }); + if (inOculusMode) { + var CURSOR_SCALE = 0.025; + reticle = Overlays.addOverlay("billboard", { + url: HIFI_PUBLIC_BUCKET + "images/cursor.svg", + position: reticlePosition(), + ignoreRayIntersection: true, + isFacingAvatar: true, + alpha: 1.0, + scale: CURSOR_SCALE + }); + } + // add an attachment on this avatar so other people see them in the lobby MyAvatar.attach(HELMET_ATTACHMENT_URL, "Neck", {x: 0, y: 0, z: 0}, Quat.fromPitchYawRollDegrees(0, 0, 0), 1.15); @@ -130,7 +136,6 @@ var MUSAK_VOLUME = 0.5; function playNextMusak() { if (panelWall) { - print("PLAYING THE NEXT MUSAK!"); if (currentSound == latinSound) { if (elevatorSound.downloaded) { currentSound = elevatorSound; @@ -166,9 +171,23 @@ function playRandomMusak() { } function cleanupLobby() { + + // 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)] = HIFI_PUBLIC_BUCKET + "models/sets/Lobby/LobbyPrototype/Texture.jpg"; + }; + + Overlays.editOverlay(panelWall, panelTexturesReset); + Overlays.deleteOverlay(panelWall); Overlays.deleteOverlay(orbShell); - Overlays.deleteOverlay(reticle); + + if (reticle) { + Overlays.deleteOverlay(reticle); + } panelWall = false; orbShell = false; @@ -240,10 +259,13 @@ function toggleEnvironmentRendering(shouldRender) { function update(deltaTime) { maybeCleanupLobby(); - if (reticle) { - Overlays.editOverlay(reticle, { - position: reticlePosition() - }); + if (panelWall) { + + if (reticle) { + Overlays.editOverlay(reticle, { + position: reticlePosition() + }); + } // if the reticle is up then we may need to play the next musak if (!Audio.isInjectorPlaying(currentMusakInjector)) { From a18a83d45c1bdc40d451f66ea7bd1a6e2acdd1e1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 19 Nov 2014 15:31:41 -0800 Subject: [PATCH 20/24] Update getEntityItemID() to check for id existence --- libraries/entities/src/EntityScriptingInterface.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index b74c40a9cc..e66da97390 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -45,7 +45,17 @@ EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& pro } EntityItemID EntityScriptingInterface::getEntityItemID(const QString& uuid) { - return EntityItemID(QUuid(uuid)); + EntityItemID entityID = EntityItemID(QUuid(uuid), UNKNOWN_ENTITY_TOKEN, false); + + _entityTree->lockForRead(); + EntityItem* entity = const_cast(_entityTree->findEntityByEntityItemID(entityID)); + _entityTree->unlock(); + + if (entity) { + return entity->getEntityItemID(); + } + + return entityID; } EntityItemID EntityScriptingInterface::identifyEntity(EntityItemID entityID) { From 4b5d67d5194061926607a8fcaa3481b402e6edc1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 19 Nov 2014 15:33:32 -0800 Subject: [PATCH 21/24] Update entityList.js to check for invalid entityIDs --- examples/libraries/entityList.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/libraries/entityList.js b/examples/libraries/entityList.js index 77808adeb7..65c06c8cea 100644 --- a/examples/libraries/entityList.js +++ b/examples/libraries/entityList.js @@ -33,7 +33,12 @@ EntityListTool = function(opts) { var ids = data.entityIds; var entityIDs = []; for (var i = 0; i < ids.length; i++) { - entityIDs.push(Entities.getEntityItemID(ids[i])); + var entityID = Entities.getEntityItemID(ids[i]); + if (entityID.isKnownID) { + entityIDs.push(entityID); + } else { + print("Tried to select invalid entity: " + ids[i]); + } } selectionManager.setSelections(entityIDs); if (data.focus) { From 11e7339e9e07093b971bf5d8235ef851067382c0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 19 Nov 2014 15:52:57 -0800 Subject: [PATCH 22/24] add UPDATE_COLLISION_GROUP and UPDATE_MOTION_TYPE --- libraries/entities/src/EntityItem.cpp | 28 +++++++++++++-------------- libraries/entities/src/EntityItem.h | 21 ++++++++++---------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 8c7ef72138..98dda6f33f 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -948,7 +948,7 @@ void EntityItem::updatePosition(const glm::vec3& value) { if (_position != value) { _position = value; recalculateCollisionShape(); - _updateFlags |= UPDATE_POSITION; + _updateFlags |= EntityItem::UPDATE_POSITION; } } @@ -957,7 +957,7 @@ void EntityItem::updatePositionInMeters(const glm::vec3& value) { if (_position != position) { _position = position; recalculateCollisionShape(); - _updateFlags |= UPDATE_POSITION; + _updateFlags |= EntityItem::UPDATE_POSITION; } } @@ -965,7 +965,7 @@ void EntityItem::updateDimensions(const glm::vec3& value) { if (_dimensions != value) { _dimensions = value; recalculateCollisionShape(); - _updateFlags |= UPDATE_SHAPE; + _updateFlags |= EntityItem::UPDATE_SHAPE; } } @@ -974,7 +974,7 @@ void EntityItem::updateDimensionsInMeters(const glm::vec3& value) { if (_dimensions != dimensions) { _dimensions = dimensions; recalculateCollisionShape(); - _updateFlags |= UPDATE_SHAPE; + _updateFlags |= EntityItem::UPDATE_SHAPE; } } @@ -982,21 +982,21 @@ void EntityItem::updateRotation(const glm::quat& rotation) { if (_rotation != rotation) { _rotation = rotation; recalculateCollisionShape(); - _updateFlags |= UPDATE_POSITION; + _updateFlags |= EntityItem::UPDATE_POSITION; } } void EntityItem::updateMass(float value) { if (_mass != value) { _mass = value; - _updateFlags |= UPDATE_MASS; + _updateFlags |= EntityItem::UPDATE_MASS; } } void EntityItem::updateVelocity(const glm::vec3& value) { if (_velocity != value) { _velocity = value; - _updateFlags |= UPDATE_VELOCITY; + _updateFlags |= EntityItem::UPDATE_VELOCITY; } } @@ -1004,14 +1004,14 @@ void EntityItem::updateVelocityInMeters(const glm::vec3& value) { glm::vec3 velocity = value / (float) TREE_SCALE; if (_velocity != velocity) { _velocity = velocity; - _updateFlags |= UPDATE_VELOCITY; + _updateFlags |= EntityItem::UPDATE_VELOCITY; } } void EntityItem::updateGravity(const glm::vec3& value) { if (_gravity != value) { _gravity = value; - _updateFlags |= UPDATE_VELOCITY; + _updateFlags |= EntityItem::UPDATE_VELOCITY; } } @@ -1019,35 +1019,35 @@ void EntityItem::updateGravityInMeters(const glm::vec3& value) { glm::vec3 gravity = value / (float) TREE_SCALE; if (_gravity != gravity) { _gravity = gravity; - _updateFlags |= UPDATE_VELOCITY; + _updateFlags |= EntityItem::UPDATE_VELOCITY; } } void EntityItem::updateAngularVelocity(const glm::vec3& value) { if (_angularVelocity != value) { _angularVelocity = value; - _updateFlags |= UPDATE_VELOCITY; + _updateFlags |= EntityItem::UPDATE_VELOCITY; } } void EntityItem::updateIgnoreForCollisions(bool value) { if (_ignoreForCollisions != value) { _ignoreForCollisions = value; - _updateFlags |= UPDATE_COLLISION; + _updateFlags |= EntityItem::UPDATE_COLLISION_GROUP; } } void EntityItem::updateCollisionsWillMove(bool value) { if (_collisionsWillMove != value) { _collisionsWillMove = value; - _updateFlags |= UPDATE_COLLISION; + _updateFlags |= EntityItem::UPDATE_MOTION_TYPE; } } void EntityItem::updateLifetime(float value) { if (_lifetime != value) { _lifetime = value; - _updateFlags |= UPDATE_LIFETIME; + _updateFlags |= EntityItem::UPDATE_LIFETIME; } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 5929ab73c6..3f63c96c4e 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -35,22 +35,23 @@ class EntityTreeElementExtraEncodeData; #define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0; #define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { }; -enum EntityUpdateFlags { - UPDATE_POSITION = 0x0001, - UPDATE_VELOCITY = 0x0002, - UPDATE_MASS = 0x0004, - UPDATE_COLLISION = 0x0008, - UPDATE_SHAPE = 0x0010, - UPDATE_LIFETIME = 0x0020 - //UPDATE_APPEARANCE = 0x8000, -}; - /// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available /// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate /// one directly, instead you must only construct one of it's derived classes with additional features. class EntityItem { public: + enum EntityUpdateFlags { + UPDATE_POSITION = 0x0001, + UPDATE_VELOCITY = 0x0002, + UPDATE_MASS = 0x0004, + UPDATE_COLLISION_GROUP = 0x0008, + UPDATE_MOTION_TYPE = 0x0010, + UPDATE_SHAPE = 0x0020, + UPDATE_LIFETIME = 0x0040 + //UPDATE_APPEARANCE = 0x8000, + }; + DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly EntityItem(const EntityItemID& entityItemID); From 2a08a6d48e1f2a7fdd2fa62d3220bb2796367be0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 19 Nov 2014 16:07:07 -0800 Subject: [PATCH 23/24] fix a bug in updateEntity() caused by the new properties not containing registrationPoint --- libraries/entities/src/UpdateEntityOperator.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/entities/src/UpdateEntityOperator.cpp b/libraries/entities/src/UpdateEntityOperator.cpp index 2f70e96c60..9cee11d73c 100644 --- a/libraries/entities/src/UpdateEntityOperator.cpp +++ b/libraries/entities/src/UpdateEntityOperator.cpp @@ -47,6 +47,13 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree, // the getMaximumAACube is the relaxed form. _oldEntityCube = _existingEntity->getMaximumAACube(); _oldEntityBox = _oldEntityCube.clamp(0.0f, 1.0f); // clamp to domain bounds + + // If the old properties doesn't contain the properties required to calculate a bounding box, + // get them from the existing entity. Registration point is required to correctly calculate + // the bounding box. + if (!_properties.registrationPointChanged()) { + _properties.setRegistrationPoint(_existingEntity->getRegistrationPoint()); + } // If the new properties has position OR dimension changes, but not both, we need to // get the old property value and set it in our properties in order for our bounds From dac980d43386902fc34161aefd6eef2a0deff1c1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Nov 2014 16:40:19 -0800 Subject: [PATCH 24/24] have AssignmentClientMonitor catch SIGTERM and cleanup --- .../src/AssignmentClientMonitor.cpp | 39 +++++++++++++++++++ .../src/AssignmentClientMonitor.h | 4 ++ 2 files changed, 43 insertions(+) diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 02a4ff04f0..358b963b4b 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include "AssignmentClientMonitor.h" @@ -17,9 +19,21 @@ const char* NUM_FORKS_PARAMETER = "-n"; const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor"; +void signalHandler(int param){ + // get the qApp and cast it to an AssignmentClientMonitor + AssignmentClientMonitor* app = qobject_cast(qApp); + + // tell it to stop the child processes and then go down + app->stopChildProcesses(); + app->quit(); +} + AssignmentClientMonitor::AssignmentClientMonitor(int &argc, char **argv, int numAssignmentClientForks) : QCoreApplication(argc, argv) { + // be a signal handler for SIGTERM so we can stop our children when we get it + signal(SIGTERM, signalHandler); + // start the Logging class with the parent's target name LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME); @@ -38,9 +52,29 @@ AssignmentClientMonitor::AssignmentClientMonitor(int &argc, char **argv, int num } } +void AssignmentClientMonitor::stopChildProcesses() { + + QList >::Iterator it = _childProcesses.begin(); + while (it != _childProcesses.end()) { + if (!it->isNull()) { + qDebug() << "Monitor is terminating child process" << it->data(); + + // don't re-spawn this child when it goes down + disconnect(it->data(), 0, this, 0); + + it->data()->terminate(); + it->data()->waitForFinished(); + } + + it = _childProcesses.erase(it); + } +} + void AssignmentClientMonitor::spawnChildClient() { QProcess *assignmentClient = new QProcess(this); + _childProcesses.append(QPointer(assignmentClient)); + // make sure that the output from the child process appears in our output assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels); @@ -55,5 +89,10 @@ void AssignmentClientMonitor::spawnChildClient() { void AssignmentClientMonitor::childProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) { qDebug("Replacing dead child assignment client with a new one"); + + // remove the old process from our list of child processes + qDebug() << "need to remove" << QPointer(qobject_cast(sender())); + _childProcesses.removeOne(QPointer(qobject_cast(sender()))); + spawnChildClient(); } diff --git a/assignment-client/src/AssignmentClientMonitor.h b/assignment-client/src/AssignmentClientMonitor.h index 1df08a345e..9dbfcc495a 100644 --- a/assignment-client/src/AssignmentClientMonitor.h +++ b/assignment-client/src/AssignmentClientMonitor.h @@ -13,6 +13,7 @@ #define hifi_AssignmentClientMonitor_h #include +#include #include #include @@ -23,10 +24,13 @@ class AssignmentClientMonitor : public QCoreApplication { Q_OBJECT public: AssignmentClientMonitor(int &argc, char **argv, int numAssignmentClientForks); + + void stopChildProcesses(); private slots: void childProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); private: void spawnChildClient(); + QList > _childProcesses; QStringList _childArguments; };